summaryrefslogtreecommitdiff
path: root/gpxe
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-03-26 16:25:35 -0700
committerH. Peter Anvin <hpa@zytor.com>2008-03-26 16:25:35 -0700
commit9eddd22a7b53b1d02fbae0d987df8af122924248 (patch)
tree882f5152880b0b1aa2d7a0619d30065acc69fb16 /gpxe
parentbbb8f15936b851e6a0ef6f7bb2c95197bff35994 (diff)
downloadsyslinux-9eddd22a7b53b1d02fbae0d987df8af122924248.tar.gz
Add gPXE into the source tree; build unified imagesyslinux-3.70-pre7
Diffstat (limited to 'gpxe')
-rw-r--r--gpxe/COPYING339
-rw-r--r--gpxe/COPYRIGHTS26
-rw-r--r--gpxe/LOG12
-rw-r--r--gpxe/README53
-rw-r--r--gpxe/VERSION1
-rw-r--r--gpxe/contrib/3c90xutil/Makefile9
-rw-r--r--gpxe/contrib/3c90xutil/README31
-rw-r--r--gpxe/contrib/3c90xutil/bromutil.c169
-rw-r--r--gpxe/contrib/3c90xutil/cromutil.c103
-rw-r--r--gpxe/contrib/3c90xutil/romutil.txt36
-rw-r--r--gpxe/contrib/Diskless-From-NT/Config.txt537
-rw-r--r--gpxe/contrib/Diskless-From-NT/Diskless-From-NT.txt565
-rw-r--r--gpxe/contrib/Diskless-From-NT/furtmayr.html82
-rw-r--r--gpxe/contrib/auto-default/mail40
-rw-r--r--gpxe/contrib/auto-default/main.c.patch55
-rw-r--r--gpxe/contrib/award_plugin_roms/README2
-rwxr-xr-xgpxe/contrib/award_plugin_roms/award_plugin_roms.pl341
-rw-r--r--gpxe/contrib/baremetal/Makefile475
-rw-r--r--gpxe/contrib/baremetal/main.c1119
-rw-r--r--gpxe/contrib/baremetal/marini.txt52
-rw-r--r--gpxe/contrib/baremetal/misc.c351
-rw-r--r--gpxe/contrib/baremetal/startmpcc.S756
-rw-r--r--gpxe/contrib/bin2intelhex/Makefile9
-rw-r--r--gpxe/contrib/bin2intelhex/bin2intelhex.c148
-rw-r--r--gpxe/contrib/bin2intelhex/bin2intelhex.c.simple74
-rw-r--r--gpxe/contrib/bochs/.gitignore7
-rw-r--r--gpxe/contrib/bochs/Makefile10
-rw-r--r--gpxe/contrib/bochs/README94
-rw-r--r--gpxe/contrib/bochs/README.qemu87
-rw-r--r--gpxe/contrib/bochs/README.windows-ris31
-rw-r--r--gpxe/contrib/bochs/bochsrc.txt750
-rw-r--r--gpxe/contrib/bochs/qemu-patch26
-rwxr-xr-xgpxe/contrib/bochs/serial-console278
-rw-r--r--gpxe/contrib/bochs/serial-console.1191
-rw-r--r--gpxe/contrib/bochs/tunctl.c113
-rwxr-xr-xgpxe/contrib/bootptodhcp/bootptodhcp.pl42
-rw-r--r--gpxe/contrib/compressor/COPYING23
-rw-r--r--gpxe/contrib/compressor/algorithm.doc58
-rw-r--r--gpxe/contrib/compressor/loader.h14
-rw-r--r--gpxe/contrib/compressor/lzhuf.c764
-rw-r--r--gpxe/contrib/dhcpdconfeg/dhcpd.conf16
-rw-r--r--gpxe/contrib/dhcpdconfeg/vendorclassid.txt140
-rw-r--r--gpxe/contrib/dhcpid/dhcpid.txt884
-rw-r--r--gpxe/contrib/eepro100notes/flash-1.txt73
-rw-r--r--gpxe/contrib/eepro100notes/flash-2.txt149
-rw-r--r--gpxe/contrib/eepro100notes/flash-3.txt57
-rw-r--r--gpxe/contrib/flashimg/Makefile29
-rw-r--r--gpxe/contrib/flashimg/flashimg.asm497
-rw-r--r--gpxe/contrib/flashimg/flashimg.imgbin0 -> 1856 bytes
-rw-r--r--gpxe/contrib/hdload/Makefile15
-rw-r--r--gpxe/contrib/hdload/hdload.S162
-rw-r--r--gpxe/contrib/hdload/petr.msg175
-rw-r--r--gpxe/contrib/initrd/ChangeLog46
-rw-r--r--gpxe/contrib/initrd/Makefile187
-rw-r--r--gpxe/contrib/initrd/Manifest15
-rw-r--r--gpxe/contrib/initrd/README37
-rw-r--r--gpxe/contrib/initrd/dhcpd.conf.etherboot.include207
-rwxr-xr-xgpxe/contrib/initrd/include-modules63
-rw-r--r--gpxe/contrib/initrd/linux-wlan.cfg7
-rw-r--r--gpxe/contrib/initrd/linuxrc76
-rwxr-xr-xgpxe/contrib/initrd/mkinitrd-net165
-rw-r--r--gpxe/contrib/initrd/mkinitrd-net.spec112
-rwxr-xr-xgpxe/contrib/initrd/mknbi-set200
-rw-r--r--gpxe/contrib/initrd/mknbi-set.conf27
-rw-r--r--gpxe/contrib/initrd/script.c.patch11
-rw-r--r--gpxe/contrib/initrd/udhcpc-post25
-rw-r--r--gpxe/contrib/linux-2.0-transname.lsm93
-rw-r--r--gpxe/contrib/linux-3c503-patch/3c503.patch24
-rw-r--r--gpxe/contrib/linux-3c503-patch/README7
-rw-r--r--gpxe/contrib/mini-slamd/COPYING339
-rw-r--r--gpxe/contrib/mini-slamd/Makefile8
-rw-r--r--gpxe/contrib/mini-slamd/mini-slamd.c521
-rw-r--r--gpxe/contrib/mkQNXnbi/Makefile10
-rw-r--r--gpxe/contrib/mkQNXnbi/README36
-rw-r--r--gpxe/contrib/mkQNXnbi/examples/bootptab29
-rw-r--r--gpxe/contrib/mkQNXnbi/examples/ws.etherboot22
-rw-r--r--gpxe/contrib/mkQNXnbi/examples/ws.etherboot.on-the-fly22
-rw-r--r--gpxe/contrib/mkQNXnbi/mkQNXnbi.c196
-rwxr-xr-xgpxe/contrib/mkffwnb/2.0.10/linuxrc76
-rw-r--r--gpxe/contrib/mkffwnb/Extendinitrd.pm43
-rw-r--r--gpxe/contrib/mkffwnb/README69
-rwxr-xr-xgpxe/contrib/mkffwnb/mkffwnb.pl226
-rw-r--r--gpxe/contrib/mklrpnb/README.txt4
-rw-r--r--gpxe/contrib/mklrpnb/extractdach.pl191
-rwxr-xr-xgpxe/contrib/mklrpnb/mklrpnb45
-rwxr-xr-xgpxe/contrib/mntnbi/mntnbi.pl97
-rw-r--r--gpxe/contrib/nfs-swap/README2
-rw-r--r--gpxe/contrib/p910nd-0.8/Makefile10
-rwxr-xr-xgpxe/contrib/p910nd-0.8/banner.pl9
-rwxr-xr-xgpxe/contrib/p910nd-0.8/client.pl58
-rw-r--r--gpxe/contrib/p910nd-0.8/p910nd.893
-rw-r--r--gpxe/contrib/p910nd-0.8/p910nd.c420
-rwxr-xr-xgpxe/contrib/p910nd-0.8/p910nd.sh39
-rw-r--r--gpxe/contrib/ppmtoansi/Makefile67
-rw-r--r--gpxe/contrib/ppmtoansi/demo/dos.ansi1
-rw-r--r--gpxe/contrib/ppmtoansi/demo/dos.ppm51
-rw-r--r--gpxe/contrib/ppmtoansi/demo/dos.xpm29
-rw-r--r--gpxe/contrib/ppmtoansi/demo/etherboot.ansibin0 -> 1005 bytes
-rw-r--r--gpxe/contrib/ppmtoansi/demo/etherboot.ppm99
-rw-r--r--gpxe/contrib/ppmtoansi/demo/etherboot.xpm27
-rw-r--r--gpxe/contrib/ppmtoansi/demo/flash.ansi1
-rw-r--r--gpxe/contrib/ppmtoansi/demo/flash.ppm35
-rw-r--r--gpxe/contrib/ppmtoansi/demo/flash.xpm25
-rw-r--r--gpxe/contrib/ppmtoansi/demo/floppy.ansi1
-rw-r--r--gpxe/contrib/ppmtoansi/demo/floppy.ppm51
-rw-r--r--gpxe/contrib/ppmtoansi/demo/floppy.xpm29
-rw-r--r--gpxe/contrib/ppmtoansi/demo/hd.ansibin0 -> 213 bytes
-rw-r--r--gpxe/contrib/ppmtoansi/demo/hd.ppm51
-rw-r--r--gpxe/contrib/ppmtoansi/demo/hd.xpm30
-rw-r--r--gpxe/contrib/ppmtoansi/demo/ibmmap.ppm11
-rw-r--r--gpxe/contrib/ppmtoansi/demo/ibmmap.xpm16
-rw-r--r--gpxe/contrib/ppmtoansi/demo/linux-logo.ansibin0 -> 1490 bytes
-rw-r--r--gpxe/contrib/ppmtoansi/demo/linux-logo.ppm552
-rw-r--r--gpxe/contrib/ppmtoansi/demo/linux-logo.xpm73
-rwxr-xr-xgpxe/contrib/ppmtoansi/demo/make-ansi.sh17
-rw-r--r--gpxe/contrib/ppmtoansi/demo/text.ansibin0 -> 200 bytes
-rw-r--r--gpxe/contrib/ppmtoansi/demo/text.ppm51
-rw-r--r--gpxe/contrib/ppmtoansi/demo/text.xpm28
-rw-r--r--gpxe/contrib/ppmtoansi/demo/x.ansi1
-rw-r--r--gpxe/contrib/ppmtoansi/demo/x.ppm51
-rw-r--r--gpxe/contrib/ppmtoansi/demo/x.xpm25
-rw-r--r--gpxe/contrib/ppmtoansi/ppmtoansi.c235
-rw-r--r--gpxe/contrib/ppmtoansi/ppmtoansi.man120
-rw-r--r--gpxe/contrib/rom-scan/Makefile64
-rw-r--r--gpxe/contrib/rom-scan/rom-scan.c115
-rw-r--r--gpxe/contrib/romid/pktdrv.bat84
-rw-r--r--gpxe/contrib/romid/readme8
-rw-r--r--gpxe/contrib/romid/romid.c124
-rw-r--r--gpxe/contrib/romid/setenvs.c200
-rw-r--r--gpxe/contrib/smc9462tx-flash/Makefile20
-rw-r--r--gpxe/contrib/smc9462tx-flash/README24
-rw-r--r--gpxe/contrib/smc9462tx-flash/dp83820_write.c310
-rw-r--r--gpxe/contrib/smc9462tx-flash/dp83820flash.c152
-rw-r--r--gpxe/contrib/t2hproxy/README95
-rw-r--r--gpxe/contrib/t2hproxy/T2hproxy.java508
-rw-r--r--gpxe/contrib/t2hproxy/build.xml19
-rwxr-xr-xgpxe/contrib/t2hproxy/runT2proxy.sh15
-rwxr-xr-xgpxe/contrib/t2hproxy/t2hproxy.pl174
-rw-r--r--gpxe/contrib/t2hproxy/t2hproxy.xinetd29
-rw-r--r--gpxe/contrib/tftp/Makefile56
-rw-r--r--gpxe/contrib/tftp/README28
-rw-r--r--gpxe/contrib/tftp/arpa/tftp.h80
-rw-r--r--gpxe/contrib/tftp/main.c684
-rw-r--r--gpxe/contrib/tftp/tftp.1159
-rw-r--r--gpxe/contrib/tftp/tftp.c536
-rw-r--r--gpxe/contrib/tftp/tftpd.875
-rw-r--r--gpxe/contrib/tftp/tftpd.c742
-rw-r--r--gpxe/contrib/tftp/tftpsubs.c260
-rw-r--r--gpxe/contrib/tomsrtbt/tomsrtbt-net.txt37
-rw-r--r--gpxe/contrib/wakeonlan/README40
-rw-r--r--gpxe/contrib/wakeonlan/wol.c108
-rw-r--r--gpxe/contrib/wakeonlan/wol.h12
-rw-r--r--gpxe/src/.gitignore4
-rw-r--r--gpxe/src/Config386
-rw-r--r--gpxe/src/Makefile191
-rw-r--r--gpxe/src/Makefile.housekeeping603
-rw-r--r--gpxe/src/README.cvs65
-rw-r--r--gpxe/src/README.pixify90
-rw-r--r--gpxe/src/arch/i386/Config148
-rw-r--r--gpxe/src/arch/i386/Makefile107
-rw-r--r--gpxe/src/arch/i386/README.i386197
-rw-r--r--gpxe/src/arch/i386/core/aout_loader.c144
-rw-r--r--gpxe/src/arch/i386/core/basemem_packet.c30
-rw-r--r--gpxe/src/arch/i386/core/cpu.c73
-rw-r--r--gpxe/src/arch/i386/core/etherboot.prefix.lds100
-rw-r--r--gpxe/src/arch/i386/core/freebsd_loader.c377
-rw-r--r--gpxe/src/arch/i386/core/gdbsym.c33
-rw-r--r--gpxe/src/arch/i386/core/i386_string.c63
-rw-r--r--gpxe/src/arch/i386/core/i386_timer.c89
-rw-r--r--gpxe/src/arch/i386/core/nap.c12
-rw-r--r--gpxe/src/arch/i386/core/nulltrap.c51
-rw-r--r--gpxe/src/arch/i386/core/pcibios.c106
-rw-r--r--gpxe/src/arch/i386/core/pcidirect.c38
-rw-r--r--gpxe/src/arch/i386/core/pic8259.c63
-rw-r--r--gpxe/src/arch/i386/core/prefixudata.lds8
-rw-r--r--gpxe/src/arch/i386/core/prefixzdata.lds8
-rw-r--r--gpxe/src/arch/i386/core/realmode.c23
-rw-r--r--gpxe/src/arch/i386/core/relocate.c163
-rw-r--r--gpxe/src/arch/i386/core/setjmp.S40
-rw-r--r--gpxe/src/arch/i386/core/stack.S13
-rw-r--r--gpxe/src/arch/i386/core/stack16.S13
-rw-r--r--gpxe/src/arch/i386/core/start16.lds8
-rw-r--r--gpxe/src/arch/i386/core/start16z.lds65
-rw-r--r--gpxe/src/arch/i386/core/start32.S325
-rw-r--r--gpxe/src/arch/i386/core/umalloc.c224
-rw-r--r--gpxe/src/arch/i386/core/video_subr.c104
-rw-r--r--gpxe/src/arch/i386/core/virtaddr.S101
-rw-r--r--gpxe/src/arch/i386/core/wince_loader.c273
-rw-r--r--gpxe/src/arch/i386/drivers/net/undi.c146
-rw-r--r--gpxe/src/arch/i386/drivers/net/undiisr.S87
-rw-r--r--gpxe/src/arch/i386/drivers/net/undiload.c170
-rw-r--r--gpxe/src/arch/i386/drivers/net/undinet.c793
-rw-r--r--gpxe/src/arch/i386/drivers/net/undionly.c109
-rw-r--r--gpxe/src/arch/i386/drivers/net/undipreload.c35
-rw-r--r--gpxe/src/arch/i386/drivers/net/undirom.c232
-rw-r--r--gpxe/src/arch/i386/drivers/timer_bios.c57
-rw-r--r--gpxe/src/arch/i386/drivers/timer_rdtsc.c69
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/basemem.c44
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/bios_console.c294
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/e820mangler.S473
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/gateA20.c170
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/hidemem.c156
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/memmap.c221
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/pnpbios.c107
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/smbios.c323
-rw-r--r--gpxe/src/arch/i386/image/bootsector.c112
-rw-r--r--gpxe/src/arch/i386/image/bzimage.c567
-rw-r--r--gpxe/src/arch/i386/image/eltorito.c334
-rw-r--r--gpxe/src/arch/i386/image/multiboot.c445
-rw-r--r--gpxe/src/arch/i386/image/nbi.c458
-rw-r--r--gpxe/src/arch/i386/image/pxe_image.c109
-rw-r--r--gpxe/src/arch/i386/include/basemem.h33
-rw-r--r--gpxe/src/arch/i386/include/basemem_packet.h13
-rw-r--r--gpxe/src/arch/i386/include/bios.h11
-rw-r--r--gpxe/src/arch/i386/include/bios_disks.h69
-rw-r--r--gpxe/src/arch/i386/include/biosint.h18
-rw-r--r--gpxe/src/arch/i386/include/bits/byteswap.h76
-rw-r--r--gpxe/src/arch/i386/include/bits/cpu.h86
-rw-r--r--gpxe/src/arch/i386/include/bits/elf.h91
-rw-r--r--gpxe/src/arch/i386/include/bits/elf_x.h5
-rw-r--r--gpxe/src/arch/i386/include/bits/eltorito.h3
-rw-r--r--gpxe/src/arch/i386/include/bits/endian.h9
-rw-r--r--gpxe/src/arch/i386/include/bits/errfile.h34
-rw-r--r--gpxe/src/arch/i386/include/bits/stdint.h21
-rw-r--r--gpxe/src/arch/i386/include/bits/string.h252
-rw-r--r--gpxe/src/arch/i386/include/bits/timer2.h8
-rw-r--r--gpxe/src/arch/i386/include/bits/uaccess.h6
-rw-r--r--gpxe/src/arch/i386/include/bits/uuid.h10
-rw-r--r--gpxe/src/arch/i386/include/bochs.h34
-rw-r--r--gpxe/src/arch/i386/include/bootsector.h12
-rw-r--r--gpxe/src/arch/i386/include/bzimage.h129
-rw-r--r--gpxe/src/arch/i386/include/callbacks_arch.h243
-rw-r--r--gpxe/src/arch/i386/include/gateA20.h7
-rw-r--r--gpxe/src/arch/i386/include/int13.h277
-rw-r--r--gpxe/src/arch/i386/include/io.h265
-rw-r--r--gpxe/src/arch/i386/include/kir.h18
-rw-r--r--gpxe/src/arch/i386/include/libkir.h233
-rw-r--r--gpxe/src/arch/i386/include/librm.h289
-rw-r--r--gpxe/src/arch/i386/include/limits.h59
-rw-r--r--gpxe/src/arch/i386/include/memsizes.h17
-rw-r--r--gpxe/src/arch/i386/include/multiboot.h147
-rw-r--r--gpxe/src/arch/i386/include/pci_io.h35
-rw-r--r--gpxe/src/arch/i386/include/pcibios.h122
-rw-r--r--gpxe/src/arch/i386/include/pcidirect.h126
-rw-r--r--gpxe/src/arch/i386/include/pic8259.h69
-rw-r--r--gpxe/src/arch/i386/include/pnpbios.h15
-rw-r--r--gpxe/src/arch/i386/include/pxe_addr.h17
-rw-r--r--gpxe/src/arch/i386/include/pxe_call.h34
-rw-r--r--gpxe/src/arch/i386/include/realmode.h128
-rw-r--r--gpxe/src/arch/i386/include/registers.h187
-rw-r--r--gpxe/src/arch/i386/include/setjmp.h12
-rw-r--r--gpxe/src/arch/i386/include/smbios.h51
-rw-r--r--gpxe/src/arch/i386/include/undi.h102
-rw-r--r--gpxe/src/arch/i386/include/undiload.h33
-rw-r--r--gpxe/src/arch/i386/include/undinet.h15
-rw-r--r--gpxe/src/arch/i386/include/undipreload.h16
-rw-r--r--gpxe/src/arch/i386/include/undirom.h51
-rw-r--r--gpxe/src/arch/i386/include/vga.h228
-rw-r--r--gpxe/src/arch/i386/include/virtaddr.h105
-rw-r--r--gpxe/src/arch/i386/interface/pcbios/biosint.c101
-rw-r--r--gpxe/src/arch/i386/interface/pcbios/int13.c653
-rw-r--r--gpxe/src/arch/i386/interface/pxe/pxe_call.c450
-rw-r--r--gpxe/src/arch/i386/interface/pxe/pxe_entry.S195
-rw-r--r--gpxe/src/arch/i386/kir-Makefile26
-rw-r--r--gpxe/src/arch/i386/prefix/bImageprefix.S611
-rw-r--r--gpxe/src/arch/i386/prefix/bootpart.S216
-rw-r--r--gpxe/src/arch/i386/prefix/comprefix.S46
-rw-r--r--gpxe/src/arch/i386/prefix/dskprefix.S375
-rw-r--r--gpxe/src/arch/i386/prefix/elf_dprefix.S94
-rw-r--r--gpxe/src/arch/i386/prefix/elfprefix.S94
-rwxr-xr-xgpxe/src/arch/i386/prefix/exeprefix.S41
-rw-r--r--gpxe/src/arch/i386/prefix/hdprefix.S103
-rw-r--r--gpxe/src/arch/i386/prefix/kpxeprefix.S7
-rw-r--r--gpxe/src/arch/i386/prefix/libprefix.S657
-rw-r--r--gpxe/src/arch/i386/prefix/lkrnprefix.S155
-rw-r--r--gpxe/src/arch/i386/prefix/lmelf_dprefix.S161
-rw-r--r--gpxe/src/arch/i386/prefix/lmelf_prefix.S161
-rw-r--r--gpxe/src/arch/i386/prefix/mbr.S13
-rw-r--r--gpxe/src/arch/i386/prefix/nbiprefix.S76
-rw-r--r--gpxe/src/arch/i386/prefix/nullprefix.S13
-rw-r--r--gpxe/src/arch/i386/prefix/pxeprefix.S666
-rw-r--r--gpxe/src/arch/i386/prefix/romprefix.S383
-rw-r--r--gpxe/src/arch/i386/prefix/unnrv2b.S182
-rw-r--r--gpxe/src/arch/i386/prefix/usbdisk.S23
-rw-r--r--gpxe/src/arch/i386/scripts/i386-kir.lds196
-rw-r--r--gpxe/src/arch/i386/scripts/i386.lds272
-rw-r--r--gpxe/src/arch/i386/transitions/libkir.S254
-rw-r--r--gpxe/src/arch/i386/transitions/libpm.S0
-rw-r--r--gpxe/src/arch/i386/transitions/librm.S559
-rw-r--r--gpxe/src/config.h169
-rw-r--r--gpxe/src/config/.gitignore2
-rw-r--r--gpxe/src/core/abft.c60
-rw-r--r--gpxe/src/core/acpi.c40
-rw-r--r--gpxe/src/core/ansiesc.c114
-rw-r--r--gpxe/src/core/asprintf.c47
-rw-r--r--gpxe/src/core/basename.c62
-rw-r--r--gpxe/src/core/bitmap.c99
-rw-r--r--gpxe/src/core/bitops.c11
-rw-r--r--gpxe/src/core/btext.c5039
-rw-r--r--gpxe/src/core/config.c203
-rw-r--r--gpxe/src/core/console.c137
-rw-r--r--gpxe/src/core/cpio.c40
-rw-r--r--gpxe/src/core/cwuri.c41
-rw-r--r--gpxe/src/core/debug.c195
-rw-r--r--gpxe/src/core/device.c102
-rw-r--r--gpxe/src/core/downloader.c270
-rw-r--r--gpxe/src/core/errno.c18
-rw-r--r--gpxe/src/core/exec.c159
-rw-r--r--gpxe/src/core/filter.c72
-rw-r--r--gpxe/src/core/getkey.c79
-rw-r--r--gpxe/src/core/getopt.c271
-rw-r--r--gpxe/src/core/hw.c74
-rw-r--r--gpxe/src/core/i82365.c656
-rw-r--r--gpxe/src/core/ibft.c341
-rw-r--r--gpxe/src/core/image.c309
-rw-r--r--gpxe/src/core/init.c117
-rw-r--r--gpxe/src/core/interface.c60
-rw-r--r--gpxe/src/core/iobuf.c94
-rw-r--r--gpxe/src/core/job.c87
-rw-r--r--gpxe/src/core/linebuf.c109
-rw-r--r--gpxe/src/core/main.c40
-rw-r--r--gpxe/src/core/malloc.c384
-rw-r--r--gpxe/src/core/misc.c91
-rw-r--r--gpxe/src/core/monojob.c91
-rw-r--r--gpxe/src/core/nvo.c261
-rw-r--r--gpxe/src/core/open.c184
-rw-r--r--gpxe/src/core/pc_kbd.c112
-rw-r--r--gpxe/src/core/pcmcia.c267
-rw-r--r--gpxe/src/core/posix_io.c353
-rw-r--r--gpxe/src/core/process.c102
-rw-r--r--gpxe/src/core/proto_eth_slow.c406
-rw-r--r--gpxe/src/core/random.c39
-rw-r--r--gpxe/src/core/refcnt.c76
-rw-r--r--gpxe/src/core/resolv.c400
-rw-r--r--gpxe/src/core/serial.c268
-rw-r--r--gpxe/src/core/settings.c1037
-rw-r--r--gpxe/src/core/string.c353
-rw-r--r--gpxe/src/core/stringextra.c273
-rw-r--r--gpxe/src/core/timer.c113
-rw-r--r--gpxe/src/core/uri.c383
-rw-r--r--gpxe/src/core/uuid.c48
-rw-r--r--gpxe/src/core/vsprintf.c421
-rw-r--r--gpxe/src/core/xfer.c405
-rw-r--r--gpxe/src/crypto/asn1.c159
-rw-r--r--gpxe/src/crypto/axtls/aes.c478
-rw-r--r--gpxe/src/crypto/axtls/axtls_asn1.c867
-rw-r--r--gpxe/src/crypto/axtls/bigint.c1496
-rw-r--r--gpxe/src/crypto/axtls/bigint.h93
-rw-r--r--gpxe/src/crypto/axtls/bigint_impl.h105
-rw-r--r--gpxe/src/crypto/axtls/crypto.h298
-rw-r--r--gpxe/src/crypto/axtls/os_port.h61
-rw-r--r--gpxe/src/crypto/axtls/rsa.c332
-rw-r--r--gpxe/src/crypto/axtls/sha1.c240
-rw-r--r--gpxe/src/crypto/axtls_aes.c54
-rw-r--r--gpxe/src/crypto/axtls_sha1.c26
-rw-r--r--gpxe/src/crypto/chap.c122
-rw-r--r--gpxe/src/crypto/cipher.c24
-rw-r--r--gpxe/src/crypto/cryptoLayer.h120
-rw-r--r--gpxe/src/crypto/crypto_null.c69
-rw-r--r--gpxe/src/crypto/framework.c86
-rw-r--r--gpxe/src/crypto/hmac.c120
-rw-r--r--gpxe/src/crypto/matrixssl/mpi.h487
-rw-r--r--gpxe/src/crypto/matrixssl/pscrypto.h661
-rw-r--r--gpxe/src/crypto/md5.c235
-rw-r--r--gpxe/src/crypto/ssl.c136
-rw-r--r--gpxe/src/crypto/ssl.h19
-rw-r--r--gpxe/src/crypto/ssl_constructs.h342
-rw-r--r--gpxe/src/doc/build_sys.dox419
-rw-r--r--gpxe/src/doc/pxe_extensions279
-rw-r--r--gpxe/src/doxygen.cfg1245
-rw-r--r--gpxe/src/drivers/bitbash/bitbash.c55
-rw-r--r--gpxe/src/drivers/bitbash/i2c_bit.c322
-rw-r--r--gpxe/src/drivers/bitbash/spi_bit.c196
-rw-r--r--gpxe/src/drivers/block/ata.c159
-rw-r--r--gpxe/src/drivers/block/ramdisk.c91
-rw-r--r--gpxe/src/drivers/block/scsi.c271
-rw-r--r--gpxe/src/drivers/bus/eisa.c185
-rw-r--r--gpxe/src/drivers/bus/isa.c175
-rw-r--r--gpxe/src/drivers/bus/isa_ids.c26
-rw-r--r--gpxe/src/drivers/bus/isapnp.c758
-rw-r--r--gpxe/src/drivers/bus/mca.c180
-rw-r--r--gpxe/src/drivers/bus/pci.c343
-rw-r--r--gpxe/src/drivers/bus/pciextra.c79
-rw-r--r--gpxe/src/drivers/infiniband/MT25218_PRM.h3460
-rw-r--r--gpxe/src/drivers/infiniband/MT25408_PRM.h3313
-rw-r--r--gpxe/src/drivers/infiniband/arbel.c1979
-rw-r--r--gpxe/src/drivers/infiniband/arbel.h468
-rw-r--r--gpxe/src/drivers/infiniband/hermon.c2016
-rw-r--r--gpxe/src/drivers/infiniband/hermon.h466
-rw-r--r--gpxe/src/drivers/infiniband/mlx_bitops.h209
-rw-r--r--gpxe/src/drivers/net/3c509-eisa.c49
-rw-r--r--gpxe/src/drivers/net/3c509.c430
-rw-r--r--gpxe/src/drivers/net/3c509.h392
-rw-r--r--gpxe/src/drivers/net/3c515.c762
-rw-r--r--gpxe/src/drivers/net/3c515.txt31
-rw-r--r--gpxe/src/drivers/net/3c529.c60
-rw-r--r--gpxe/src/drivers/net/3c595.c551
-rw-r--r--gpxe/src/drivers/net/3c595.h435
-rw-r--r--gpxe/src/drivers/net/3c5x9.c414
-rw-r--r--gpxe/src/drivers/net/3c90x.c1027
-rw-r--r--gpxe/src/drivers/net/3c90x.txt307
-rw-r--r--gpxe/src/drivers/net/amd8111e.c691
-rw-r--r--gpxe/src/drivers/net/amd8111e.h629
-rw-r--r--gpxe/src/drivers/net/bnx2.c2695
-rw-r--r--gpxe/src/drivers/net/bnx2.h4704
-rw-r--r--gpxe/src/drivers/net/bnx2_fw.h3494
-rw-r--r--gpxe/src/drivers/net/cs89x0.c737
-rw-r--r--gpxe/src/drivers/net/cs89x0.h479
-rw-r--r--gpxe/src/drivers/net/cs89x0.txt45
-rw-r--r--gpxe/src/drivers/net/davicom.c725
-rw-r--r--gpxe/src/drivers/net/depca.c803
-rw-r--r--gpxe/src/drivers/net/dmfe.c1224
-rw-r--r--gpxe/src/drivers/net/e1000/e1000.c1116
-rw-r--r--gpxe/src/drivers/net/e1000/e1000.h303
-rw-r--r--gpxe/src/drivers/net/e1000/e1000_hw.c9050
-rw-r--r--gpxe/src/drivers/net/e1000/e1000_hw.h3413
-rw-r--r--gpxe/src/drivers/net/e1000/e1000_osdep.h142
-rw-r--r--gpxe/src/drivers/net/eepro.c635
-rw-r--r--gpxe/src/drivers/net/eepro100.c851
-rw-r--r--gpxe/src/drivers/net/epic100.c537
-rw-r--r--gpxe/src/drivers/net/epic100.h188
-rw-r--r--gpxe/src/drivers/net/etherfabric.c3436
-rw-r--r--gpxe/src/drivers/net/etherfabric.h551
-rw-r--r--gpxe/src/drivers/net/forcedeth.c1436
-rw-r--r--gpxe/src/drivers/net/hfa384x.h3067
-rw-r--r--gpxe/src/drivers/net/ipoib.c929
-rw-r--r--gpxe/src/drivers/net/legacy.c152
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/MT23108_PRM.h2800
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/MT23108_PRM_append.h199
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/MT25218_PRM.h3463
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/bit_ops.h126
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/cmdif.h50
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/cmdif_comm.c564
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/cmdif_comm.h60
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/cmdif_mt23108.c193
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/cmdif_mt25218.c457
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/cmdif_priv.h50
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/doc/README.boot_over_ib176
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/ib_driver.c342
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/ib_driver.h169
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/ib_mad.c396
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/ib_mad.h110
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/ib_mt23108.c1701
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/ib_mt25218.c1929
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/ipoib.c1027
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/ipoib.h297
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/mad_attrib.h244
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/mt23108.c245
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/mt23108.h543
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/mt23108_imp.c229
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/mt25218.c245
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/mt25218.h546
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/mt25218_imp.c229
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/mt_version.c23
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/patches/dhcpd.patch23
-rw-r--r--gpxe/src/drivers/net/mlx_ipoib/samples/dhcpd.conf56
-rw-r--r--gpxe/src/drivers/net/mtd80x.c1084
-rwxr-xr-xgpxe/src/drivers/net/mtnic.c1758
-rwxr-xr-xgpxe/src/drivers/net/mtnic.h716
-rw-r--r--gpxe/src/drivers/net/natsemi.c603
-rw-r--r--gpxe/src/drivers/net/natsemi.h230
-rwxr-xr-xgpxe/src/drivers/net/ns83820.c1012
-rw-r--r--gpxe/src/drivers/net/ns8390.c1029
-rw-r--r--gpxe/src/drivers/net/ns8390.h238
-rw-r--r--gpxe/src/drivers/net/p80211hdr.h299
-rw-r--r--gpxe/src/drivers/net/pcnet32.c1025
-rw-r--r--gpxe/src/drivers/net/pnic.c276
-rw-r--r--gpxe/src/drivers/net/pnic_api.h59
-rw-r--r--gpxe/src/drivers/net/prism2.c855
-rw-r--r--gpxe/src/drivers/net/prism2_pci.c59
-rw-r--r--gpxe/src/drivers/net/prism2_plx.c121
-rw-r--r--gpxe/src/drivers/net/r8169.c1184
-rw-r--r--gpxe/src/drivers/net/rtl8139.c583
-rw-r--r--gpxe/src/drivers/net/sis900.c1296
-rw-r--r--gpxe/src/drivers/net/sis900.h373
-rw-r--r--gpxe/src/drivers/net/smc9000.c952
-rw-r--r--gpxe/src/drivers/net/smc9000.h433
-rw-r--r--gpxe/src/drivers/net/sundance.c885
-rw-r--r--gpxe/src/drivers/net/tg3.c3400
-rw-r--r--gpxe/src/drivers/net/tg3.h2211
-rw-r--r--gpxe/src/drivers/net/tlan.c1720
-rw-r--r--gpxe/src/drivers/net/tlan.h524
-rw-r--r--gpxe/src/drivers/net/tulip.c2097
-rw-r--r--gpxe/src/drivers/net/tulip.txt54
-rw-r--r--gpxe/src/drivers/net/via-rhine.c1443
-rw-r--r--gpxe/src/drivers/net/via-velocity.c1939
-rw-r--r--gpxe/src/drivers/net/via-velocity.h1930
-rw-r--r--gpxe/src/drivers/net/w89c840.c962
-rw-r--r--gpxe/src/drivers/net/wlan_compat.h575
-rw-r--r--gpxe/src/drivers/nvs/nvs.c147
-rw-r--r--gpxe/src/drivers/nvs/spi.c138
-rw-r--r--gpxe/src/drivers/nvs/threewire.c88
-rw-r--r--gpxe/src/hci/commands/autoboot_cmd.c25
-rw-r--r--gpxe/src/hci/commands/config_cmd.c37
-rw-r--r--gpxe/src/hci/commands/dhcp_cmd.c108
-rw-r--r--gpxe/src/hci/commands/ifmgmt_cmd.c177
-rw-r--r--gpxe/src/hci/commands/image_cmd.c579
-rw-r--r--gpxe/src/hci/commands/nvo_cmd.c77
-rw-r--r--gpxe/src/hci/commands/route_cmd.c85
-rw-r--r--gpxe/src/hci/commands/sanboot_cmd.c68
-rw-r--r--gpxe/src/hci/editstring.c190
-rw-r--r--gpxe/src/hci/mucurses/alert.c18
-rw-r--r--gpxe/src/hci/mucurses/ansi_screen.c72
-rw-r--r--gpxe/src/hci/mucurses/clear.c88
-rw-r--r--gpxe/src/hci/mucurses/colour.c64
-rw-r--r--gpxe/src/hci/mucurses/cursor.h35
-rw-r--r--gpxe/src/hci/mucurses/edging.c111
-rw-r--r--gpxe/src/hci/mucurses/kb.c143
-rw-r--r--gpxe/src/hci/mucurses/mucurses.c145
-rw-r--r--gpxe/src/hci/mucurses/mucurses.h21
-rw-r--r--gpxe/src/hci/mucurses/print.c84
-rw-r--r--gpxe/src/hci/mucurses/print_nadv.c26
-rw-r--r--gpxe/src/hci/mucurses/slk.c363
-rw-r--r--gpxe/src/hci/mucurses/widgets/editbox.c96
-rw-r--r--gpxe/src/hci/mucurses/winattrs.c131
-rw-r--r--gpxe/src/hci/mucurses/windows.c158
-rw-r--r--gpxe/src/hci/mucurses/wininit.c35
-rw-r--r--gpxe/src/hci/readline.c117
-rw-r--r--gpxe/src/hci/shell.c105
-rw-r--r--gpxe/src/hci/shell_banner.c75
-rw-r--r--gpxe/src/hci/strerror.c121
-rw-r--r--gpxe/src/hci/tui/settings_ui.c427
-rw-r--r--gpxe/src/image/elf.c150
-rw-r--r--gpxe/src/image/embed.S7
-rw-r--r--gpxe/src/image/embedded.c49
-rw-r--r--gpxe/src/image/initrd.c37
-rw-r--r--gpxe/src/image/script.c128
-rw-r--r--gpxe/src/image/segment.c72
-rw-r--r--gpxe/src/include/.gitignore1
-rw-r--r--gpxe/src/include/alloca.h25
-rw-r--r--gpxe/src/include/assert.h65
-rw-r--r--gpxe/src/include/big_bswap.h33
-rw-r--r--gpxe/src/include/bootp.h230
-rw-r--r--gpxe/src/include/btext.h62
-rw-r--r--gpxe/src/include/byteswap.h22
-rw-r--r--gpxe/src/include/cmdline.h8
-rw-r--r--gpxe/src/include/cmdlinelib.h99
-rw-r--r--gpxe/src/include/cmdlist.h18
-rw-r--r--gpxe/src/include/coff.h73
-rw-r--r--gpxe/src/include/compiler.h357
-rw-r--r--gpxe/src/include/console.h114
-rw-r--r--gpxe/src/include/cpu.h6
-rw-r--r--gpxe/src/include/ctype.h28
-rw-r--r--gpxe/src/include/curses.h753
-rw-r--r--gpxe/src/include/debug.h28
-rw-r--r--gpxe/src/include/dhcp.h12
-rw-r--r--gpxe/src/include/elf.h236
-rw-r--r--gpxe/src/include/endian.h19
-rw-r--r--gpxe/src/include/errno.h508
-rw-r--r--gpxe/src/include/etherboot.h42
-rw-r--r--gpxe/src/include/fs.h41
-rw-r--r--gpxe/src/include/getopt.h92
-rw-r--r--gpxe/src/include/gpxe/abft.h35
-rw-r--r--gpxe/src/include/gpxe/acpi.h41
-rw-r--r--gpxe/src/include/gpxe/aes.h8
-rw-r--r--gpxe/src/include/gpxe/ansiesc.h118
-rw-r--r--gpxe/src/include/gpxe/aoe.h126
-rw-r--r--gpxe/src/include/gpxe/arp.h41
-rw-r--r--gpxe/src/include/gpxe/asn1.h32
-rw-r--r--gpxe/src/include/gpxe/async.h228
-rw-r--r--gpxe/src/include/gpxe/ata.h205
-rw-r--r--gpxe/src/include/gpxe/bitbash.h50
-rw-r--r--gpxe/src/include/gpxe/bitmap.h83
-rw-r--r--gpxe/src/include/gpxe/bitops.h27
-rw-r--r--gpxe/src/include/gpxe/blockdev.h43
-rw-r--r--gpxe/src/include/gpxe/chap.h51
-rw-r--r--gpxe/src/include/gpxe/command.h22
-rw-r--r--gpxe/src/include/gpxe/cpio.h51
-rw-r--r--gpxe/src/include/gpxe/crypto.h116
-rw-r--r--gpxe/src/include/gpxe/device.h111
-rw-r--r--gpxe/src/include/gpxe/dhcp.h455
-rw-r--r--gpxe/src/include/gpxe/dhcpopts.h32
-rw-r--r--gpxe/src/include/gpxe/dhcppkt.h35
-rw-r--r--gpxe/src/include/gpxe/dns.h90
-rw-r--r--gpxe/src/include/gpxe/downloader.h17
-rw-r--r--gpxe/src/include/gpxe/editbox.h51
-rw-r--r--gpxe/src/include/gpxe/editstring.h31
-rw-r--r--gpxe/src/include/gpxe/eisa.h125
-rw-r--r--gpxe/src/include/gpxe/elf.h15
-rw-r--r--gpxe/src/include/gpxe/embedded.h9
-rw-r--r--gpxe/src/include/gpxe/errfile.h157
-rw-r--r--gpxe/src/include/gpxe/errortab.h19
-rw-r--r--gpxe/src/include/gpxe/ethernet.h33
-rw-r--r--gpxe/src/include/gpxe/fakedhcp.h21
-rw-r--r--gpxe/src/include/gpxe/features.h88
-rw-r--r--gpxe/src/include/gpxe/filter.h73
-rw-r--r--gpxe/src/include/gpxe/ftp.h13
-rw-r--r--gpxe/src/include/gpxe/hidemem.h23
-rw-r--r--gpxe/src/include/gpxe/hmac.h30
-rw-r--r--gpxe/src/include/gpxe/http.h21
-rw-r--r--gpxe/src/include/gpxe/i2c.h111
-rw-r--r--gpxe/src/include/gpxe/ibft.h300
-rw-r--r--gpxe/src/include/gpxe/icmp6.h57
-rw-r--r--gpxe/src/include/gpxe/if_arp.h100
-rw-r--r--gpxe/src/include/gpxe/if_ether.h33
-rw-r--r--gpxe/src/include/gpxe/image.h162
-rw-r--r--gpxe/src/include/gpxe/in.h94
-rw-r--r--gpxe/src/include/gpxe/infiniband.h751
-rw-r--r--gpxe/src/include/gpxe/init.h62
-rw-r--r--gpxe/src/include/gpxe/initrd.h14
-rw-r--r--gpxe/src/include/gpxe/interface.h56
-rw-r--r--gpxe/src/include/gpxe/iobuf.h189
-rw-r--r--gpxe/src/include/gpxe/ip.h95
-rw-r--r--gpxe/src/include/gpxe/ip6.h78
-rw-r--r--gpxe/src/include/gpxe/ipoib.h78
-rw-r--r--gpxe/src/include/gpxe/isa.h92
-rw-r--r--gpxe/src/include/gpxe/isa_ids.h49
-rw-r--r--gpxe/src/include/gpxe/isapnp.h273
-rw-r--r--gpxe/src/include/gpxe/iscsi.h654
-rw-r--r--gpxe/src/include/gpxe/job.h165
-rw-r--r--gpxe/src/include/gpxe/keys.h82
-rw-r--r--gpxe/src/include/gpxe/linebuf.h28
-rw-r--r--gpxe/src/include/gpxe/linux_compat.h25
-rw-r--r--gpxe/src/include/gpxe/list.h178
-rw-r--r--gpxe/src/include/gpxe/malloc.h57
-rw-r--r--gpxe/src/include/gpxe/mca.h103
-rw-r--r--gpxe/src/include/gpxe/md5.h22
-rw-r--r--gpxe/src/include/gpxe/memmap.h34
-rw-r--r--gpxe/src/include/gpxe/monojob.h15
-rw-r--r--gpxe/src/include/gpxe/ndp.h21
-rw-r--r--gpxe/src/include/gpxe/netdevice.h404
-rw-r--r--gpxe/src/include/gpxe/nvo.h53
-rw-r--r--gpxe/src/include/gpxe/nvs.h66
-rw-r--r--gpxe/src/include/gpxe/open.h95
-rw-r--r--gpxe/src/include/gpxe/pci.h356
-rw-r--r--gpxe/src/include/gpxe/pci_ids.h348
-rw-r--r--gpxe/src/include/gpxe/posix_io.h85
-rw-r--r--gpxe/src/include/gpxe/process.h75
-rw-r--r--gpxe/src/include/gpxe/profile.h78
-rw-r--r--gpxe/src/include/gpxe/ramdisk.h22
-rw-r--r--gpxe/src/include/gpxe/rarp.h14
-rw-r--r--gpxe/src/include/gpxe/refcnt.h44
-rw-r--r--gpxe/src/include/gpxe/resolv.h166
-rw-r--r--gpxe/src/include/gpxe/retry.h52
-rw-r--r--gpxe/src/include/gpxe/rsa.h10
-rw-r--r--gpxe/src/include/gpxe/scsi.h270
-rw-r--r--gpxe/src/include/gpxe/segment.h15
-rw-r--r--gpxe/src/include/gpxe/settings.h285
-rw-r--r--gpxe/src/include/gpxe/settings_ui.h14
-rw-r--r--gpxe/src/include/gpxe/sha1.h13
-rw-r--r--gpxe/src/include/gpxe/shell.h12
-rw-r--r--gpxe/src/include/gpxe/shell_banner.h12
-rw-r--r--gpxe/src/include/gpxe/socket.h95
-rw-r--r--gpxe/src/include/gpxe/spi.h239
-rw-r--r--gpxe/src/include/gpxe/spi_bit.h61
-rw-r--r--gpxe/src/include/gpxe/tables.h229
-rw-r--r--gpxe/src/include/gpxe/tcp.h306
-rw-r--r--gpxe/src/include/gpxe/tcpip.h118
-rw-r--r--gpxe/src/include/gpxe/tftp.h83
-rw-r--r--gpxe/src/include/gpxe/threewire.h89
-rw-r--r--gpxe/src/include/gpxe/timer.h41
-rw-r--r--gpxe/src/include/gpxe/tls.h171
-rw-r--r--gpxe/src/include/gpxe/uaccess.h27
-rw-r--r--gpxe/src/include/gpxe/udp.h47
-rw-r--r--gpxe/src/include/gpxe/umalloc.h17
-rw-r--r--gpxe/src/include/gpxe/uri.h139
-rw-r--r--gpxe/src/include/gpxe/uuid.h34
-rw-r--r--gpxe/src/include/gpxe/vsprintf.h71
-rw-r--r--gpxe/src/include/gpxe/xfer.h275
-rw-r--r--gpxe/src/include/i82365.h450
-rw-r--r--gpxe/src/include/igmp.h42
-rw-r--r--gpxe/src/include/lib.h42
-rw-r--r--gpxe/src/include/libgen.h7
-rw-r--r--gpxe/src/include/little_bswap.h33
-rw-r--r--gpxe/src/include/mii.h105
-rw-r--r--gpxe/src/include/nfs.h63
-rw-r--r--gpxe/src/include/nic.h272
-rw-r--r--gpxe/src/include/nmb.h22
-rw-r--r--gpxe/src/include/old_tcp.h37
-rw-r--r--gpxe/src/include/pc_kbd.h7
-rw-r--r--gpxe/src/include/pcmcia-opts.h23
-rw-r--r--gpxe/src/include/pcmcia.h156
-rw-r--r--gpxe/src/include/pxe.h151
-rw-r--r--gpxe/src/include/pxe_api.h1841
-rw-r--r--gpxe/src/include/pxe_types.h126
-rw-r--r--gpxe/src/include/readline/readline.h12
-rw-r--r--gpxe/src/include/stdarg.h10
-rw-r--r--gpxe/src/include/stddef.h18
-rw-r--r--gpxe/src/include/stdint.h24
-rw-r--r--gpxe/src/include/stdio.h45
-rw-r--r--gpxe/src/include/stdlib.h72
-rw-r--r--gpxe/src/include/string.h49
-rw-r--r--gpxe/src/include/strings.h63
-rw-r--r--gpxe/src/include/sys/time.h20
-rw-r--r--gpxe/src/include/sys_info.h33
-rw-r--r--gpxe/src/include/time.h22
-rw-r--r--gpxe/src/include/unistd.h31
-rw-r--r--gpxe/src/include/usr/aoeboot.h6
-rw-r--r--gpxe/src/include/usr/autoboot.h13
-rw-r--r--gpxe/src/include/usr/dhcpmgmt.h14
-rw-r--r--gpxe/src/include/usr/ifmgmt.h16
-rw-r--r--gpxe/src/include/usr/imgmgmt.h20
-rw-r--r--gpxe/src/include/usr/iscsiboot.h6
-rw-r--r--gpxe/src/include/usr/route.h12
-rw-r--r--gpxe/src/interface/pxe/pxe_errors.c103
-rw-r--r--gpxe/src/interface/pxe/pxe_file.c264
-rw-r--r--gpxe/src/interface/pxe/pxe_loader.c53
-rw-r--r--gpxe/src/interface/pxe/pxe_preboot.c353
-rw-r--r--gpxe/src/interface/pxe/pxe_tftp.c584
-rw-r--r--gpxe/src/interface/pxe/pxe_udp.c390
-rw-r--r--gpxe/src/interface/pxe/pxe_undi.c620
-rw-r--r--gpxe/src/libgcc/__divdi3.c26
-rw-r--r--gpxe/src/libgcc/__moddi3.c26
-rw-r--r--gpxe/src/libgcc/__udivdi3.c10
-rw-r--r--gpxe/src/libgcc/__udivmoddi4.c32
-rw-r--r--gpxe/src/libgcc/__umoddi3.c13
-rw-r--r--gpxe/src/libgcc/libgcc.h26
-rw-r--r--gpxe/src/libgcc/memcpy.c18
-rw-r--r--gpxe/src/net/aoe.c375
-rw-r--r--gpxe/src/net/arp.c294
-rw-r--r--gpxe/src/net/dhcpopts.c434
-rw-r--r--gpxe/src/net/dhcppkt.c172
-rw-r--r--gpxe/src/net/ethernet.c116
-rw-r--r--gpxe/src/net/fakedhcp.c205
-rw-r--r--gpxe/src/net/icmpv6.c128
-rw-r--r--gpxe/src/net/infiniband.c437
-rw-r--r--gpxe/src/net/iobpad.c66
-rw-r--r--gpxe/src/net/ipv4.c633
-rw-r--r--gpxe/src/net/ipv6.c380
-rw-r--r--gpxe/src/net/ndp.c180
-rw-r--r--gpxe/src/net/netdev_settings.c90
-rw-r--r--gpxe/src/net/netdevice.c513
-rw-r--r--gpxe/src/net/nullnet.c58
-rw-r--r--gpxe/src/net/rarp.c68
-rw-r--r--gpxe/src/net/retry.c184
-rw-r--r--gpxe/src/net/tcp.c1073
-rw-r--r--gpxe/src/net/tcp/ftp.c467
-rw-r--r--gpxe/src/net/tcp/http.c534
-rw-r--r--gpxe/src/net/tcp/https.c49
-rw-r--r--gpxe/src/net/tcp/iscsi.c1726
-rw-r--r--gpxe/src/net/tcpip.c145
-rw-r--r--gpxe/src/net/tls.c1731
-rw-r--r--gpxe/src/net/udp.c465
-rw-r--r--gpxe/src/net/udp/dhcp.c825
-rw-r--r--gpxe/src/net/udp/dns.c547
-rw-r--r--gpxe/src/net/udp/tftp.c1149
-rw-r--r--gpxe/src/proto/fsp.c243
-rw-r--r--gpxe/src/proto/igmp.c167
-rw-r--r--gpxe/src/proto/nfs.c616
-rw-r--r--gpxe/src/proto/nmb.c110
-rw-r--r--gpxe/src/proto/slam.c541
-rw-r--r--gpxe/src/tests/linebuf_test.c35
-rw-r--r--gpxe/src/tests/memcpy_test.c39
-rw-r--r--gpxe/src/tests/umalloc_test.c26
-rw-r--r--gpxe/src/tests/uri_test.c145
-rw-r--r--gpxe/src/usr/aoeboot.c73
-rw-r--r--gpxe/src/usr/autoboot.c204
-rw-r--r--gpxe/src/usr/dhcpmgmt.c48
-rw-r--r--gpxe/src/usr/ifmgmt.c69
-rw-r--r--gpxe/src/usr/imgmgmt.c127
-rw-r--r--gpxe/src/usr/iscsiboot.c69
-rw-r--r--gpxe/src/usr/route.c43
-rw-r--r--gpxe/src/util/.gitignore4
-rw-r--r--gpxe/src/util/Makefile22
-rwxr-xr-xgpxe/src/util/catrom.pl48
-rwxr-xr-xgpxe/src/util/disrom.pl114
-rwxr-xr-xgpxe/src/util/dskpad.pl12
-rwxr-xr-xgpxe/src/util/geniso56
-rwxr-xr-xgpxe/src/util/genliso85
-rwxr-xr-xgpxe/src/util/get-pci-ids135
-rw-r--r--gpxe/src/util/hijack.c628
-rwxr-xr-xgpxe/src/util/makerom.pl226
-rwxr-xr-xgpxe/src/util/mkconfig.pl188
-rwxr-xr-xgpxe/src/util/modrom.pl226
-rw-r--r--gpxe/src/util/mucurses_test.c63
-rw-r--r--gpxe/src/util/nrv2b.c1501
-rw-r--r--gpxe/src/util/parserom.pl64
-rwxr-xr-xgpxe/src/util/sortobjdump.pl40
-rwxr-xr-xgpxe/src/util/swapdevids.pl49
-rwxr-xr-xgpxe/src/util/symcheck.pl191
-rw-r--r--gpxe/src/util/zbin.c325
771 files changed, 218901 insertions, 0 deletions
diff --git a/gpxe/COPYING b/gpxe/COPYING
new file mode 100644
index 00000000..a43ea212
--- /dev/null
+++ b/gpxe/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gpxe/COPYRIGHTS b/gpxe/COPYRIGHTS
new file mode 100644
index 00000000..d686b435
--- /dev/null
+++ b/gpxe/COPYRIGHTS
@@ -0,0 +1,26 @@
+
+In general gPXE files are licensed under the GPL. GPLed files are in
+general either from Linux or have been explicitly put under GPL by the
+authors. The license for a file is usually documented at the top of
+the file.
+
+A few files are inherited from FreeBSD netboot and therefore can be
+used under BSD or GPL. Documented in this file are some of the
+non-GPL'ed files. If the internal documentation for a file disagrees
+with what is documented in this file, the internal documentation for
+the file shall be override this file.
+
+File Copyright status
+
+src/core/misc.c BSD
+src/drivers/net/3c509.c BSD
+src/drivers/net/3c509.h BSD
+src/drivers/net/3c595.c BSD
+src/drivers/net/3c595.h BSD
+src/drivers/net/3c90x.c Open Source
+src/drivers/net/epic100.c None
+src/drivers/net/epic100.h None
+src/drivers/net/ns8390.c BSD
+src/drivers/net/ns8390.h BSD
+src/arch/i386/include/bits/string.h None
+
diff --git a/gpxe/LOG b/gpxe/LOG
new file mode 100644
index 00000000..d29c8eba
--- /dev/null
+++ b/gpxe/LOG
@@ -0,0 +1,12 @@
+gPXE LOG file
+
++ Development for gPXE was moved from SourceForge CVS to a git
+ repository on git.etherboot.org in the first half of the year 2007.
+
++ The gitweb interface for gPXE is available at:
+
+ http://git.etherboot.org/?p=gpxe.git
+
++ The gitweb interface largely obsoletes the LOG file that used to be
+ distributed with Etherboot.
+
diff --git a/gpxe/README b/gpxe/README
new file mode 100644
index 00000000..b3ae2154
--- /dev/null
+++ b/gpxe/README
@@ -0,0 +1,53 @@
+gPXE README File
+
+gPXE is an implementation of the PXE specification for network
+booting, with extensions to allow additional features such as booting
+via HTTP, iSCSI, and AoE.
+
+In generally, gPXE is compatible with the industry-standard PXE
+specification, and also supports Etherboot .nbi file loading and some
+additional protocols and features.
+
+For more detailed information about gPXE, please visit our project
+website at: http://etherboot.org/
+
+BUILDING gPXE IMAGE FROM SOURCE
+
+If you don't want to install development tools, and have access to the
+Web, you can get gPXE and Etherboot ROM images made on demand from
+http://rom-o-matic.net/
+
+If you would like to compile gPXE images from source, here are some tips.
+
+We normally compile gPXE images on x86, 32-bit Linux machines. It is
+possible to also use x86-64 machines. We use gcc compiler options to
+create 32-bit output.
+
+It is important to have the necessary software packages installed. A gcc-based
+toolchain is required.
+
+The following packages (at least) are required:
+
+ - a gcc tool chain (gcc 3.x or gcc 4.x)
+ - binutils
+ - perl
+ - syslinux
+ - mtools
+
+To test your environment, cd to the "src" directory and type:
+
+ make
+
+You should see a lot of output, and when it stops, the "bin" directory
+should be populated with gPXE images and object files.
+
+To learn more about what to build and how to use gPXE, please visit our
+project website at http://etherboot.org/ , particularly the "howto" section.
+
+CONTACTING US
+
+Pointers to our project mailing lists are on http://etherboot.org/
+
+Real-time help is often available on IRC on the #etherboot channel of
+irc.freenode.net.
+
diff --git a/gpxe/VERSION b/gpxe/VERSION
new file mode 100644
index 00000000..e466de0c
--- /dev/null
+++ b/gpxe/VERSION
@@ -0,0 +1 @@
+0.9.3 2008-02-14
diff --git a/gpxe/contrib/3c90xutil/Makefile b/gpxe/contrib/3c90xutil/Makefile
new file mode 100644
index 00000000..1dd1723f
--- /dev/null
+++ b/gpxe/contrib/3c90xutil/Makefile
@@ -0,0 +1,9 @@
+FILES = cromutil bromutil
+
+INCLUDEDIR = /usr/include
+CFLAGS = -O2 -fomit-frame-pointer -Wall -I$(INCLUDEDIR)
+
+all: $(FILES)
+
+clean:
+ rm -f $(FILES) *~ core
diff --git a/gpxe/contrib/3c90xutil/README b/gpxe/contrib/3c90xutil/README
new file mode 100644
index 00000000..235530f6
--- /dev/null
+++ b/gpxe/contrib/3c90xutil/README
@@ -0,0 +1,31 @@
+This utility was apparently writen by John Finlay and came to me
+via Richard Schroeder who got it from Greg Beeley. John, if you want
+to be credited with your full address or whatever in the Etherboot
+documentation, please contact me (Etherboot maintainer).
+
+1/18/2000 Marty Connor (mdc@thinguin.org) added code for the 3C905C
+with AT49BV512 Flash memory, and created cromutil and bromutil to
+differentiate the versions. cromutil is for 3C905C and bromutil is
+for 3C905B.
+
+Be careful. You can easily erase your Flash memory using these
+utilities. Make *sure* to back them up first using the "read"
+command. You must "erase" before using "prog" to program the chip with
+Etherboot code. This code comes with NO WARRANTY, and you take sole
+responsibility and liability for whatever it does. Read the
+"romutil.txt" file for more information on commands.
+
+That being said, if you are programming a 3C905C-TXM (for example)
+you would do something like this:
+
+ $ cd etherboot-x.x.x/contrib
+ $ tar -zxvf n3c905xutil.tar.gz
+ $ cd n3c905xutil
+ $ make
+ # replace 0x6600 with whatever the IO Addr for your card is!!!!
+ $ ./cromutil 0x6600 read > 905cbackup.bin
+ $ ./cromutil 0x6600 erase
+ $ ./cromutil 0x6600 prog < 3c90x.lzrom
+
+You should now have an Etherboot-enabled 3c905C-TXM.
+
diff --git a/gpxe/contrib/3c90xutil/bromutil.c b/gpxe/contrib/3c90xutil/bromutil.c
new file mode 100644
index 00000000..a736e5af
--- /dev/null
+++ b/gpxe/contrib/3c90xutil/bromutil.c
@@ -0,0 +1,169 @@
+/*
+ * readutil.c - perform various control ops on the 3c509b bios rom
+ *
+ */
+
+#ifndef __i386__
+# error "This program can't compile or run on non-intel computers"
+#else
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef __FreeBSD__
+
+#include <fcntl.h>
+#include <machine/cpufunc.h>
+
+#define OUTB(data, port) outb(port, data)
+#define OUTW(data, port) outw(port, data)
+#define OUTL(data, port) outl(port, data)
+
+#else
+
+#include <sys/io.h>
+
+#define OUTB(data, port) outb(data, port)
+#define OUTW(data, port) outw(data, port)
+#define OUTL(data, port) outl(data, port)
+
+#endif
+
+int main(int argc, char **argv)
+{
+ unsigned int i, j, n;
+ unsigned int ioaddr;
+ unsigned long recvrstat;
+ unsigned char buf[128];
+ unsigned char b;
+
+ if (argc != 3) {
+ printf("Usage: romid ioaddr [erase|protect|unprotect|id|read >file|prog <file]\n");
+ exit(-1);
+ }
+
+#ifdef __FreeBSD__
+ /* get permissions for in/out{blw} */
+ open("/dev/io",O_RDONLY,0);
+#else
+ setuid(0); /* if we're setuid, do it really */
+ if (iopl(3)) {
+ perror("iopl()");
+ exit(1);
+ }
+#endif
+
+ sscanf(argv[1],"%x",&ioaddr);
+ /* Set the register window to 3 for the 3c905b */
+ OUTW(0x803, ioaddr+0xe);
+ recvrstat = inl(ioaddr); /* save the receiver status */
+ /* set the receiver type to MII so the full bios rom address space
+ can be accessed */
+ OUTL((recvrstat & 0xf00fffff)|0x00600000, ioaddr);
+
+ /* Set the register window to 0 for the 3c905b */
+ OUTW(0x800, ioaddr+0xe);
+
+ if (strcmp(argv[2], "erase") == 0) {
+ /* do the funky chicken to erase the rom contents */
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0xaa, ioaddr+0x8);
+ OUTL(0x2aaa, ioaddr+0x4);
+ OUTB(0x55, ioaddr+0x8);
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0x80, ioaddr+0x8);
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0xaa, ioaddr+0x8);
+ OUTL(0x2aaa, ioaddr+0x4);
+ OUTB(0x55, ioaddr+0x8);
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0x10, ioaddr+0x8);
+ printf("Bios ROM at %04x has been erased\n", ioaddr);
+ } else if (strcmp(argv[2], "protect") == 0) {
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0xaa, ioaddr+0x8);
+ OUTL(0x2aaa, ioaddr+0x4);
+ OUTB(0x55, ioaddr+0x8);
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0xa0, ioaddr+0x8);
+ printf("Software Data Protection for Bios ROM at %04x has been enabled\n",
+ ioaddr);
+ } else if (strcmp(argv[2], "unprotect") == 0) {
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0xaa, ioaddr+0x8);
+ OUTL(0x2aaa, ioaddr+0x4);
+ OUTB(0x55, ioaddr+0x8);
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0x80, ioaddr+0x8);
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0xaa, ioaddr+0x8);
+ OUTL(0x2aaa, ioaddr+0x4);
+ OUTB(0x55, ioaddr+0x8);
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0x20, ioaddr+0x8);
+ printf("Software Data Protection for Bios ROM at %04x has been disabled\n",
+ ioaddr);
+ } else if (strcmp(argv[2], "id") == 0) {
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0xaa, ioaddr+0x8);
+ OUTL(0x2aaa, ioaddr+0x4);
+ OUTB(0x55, ioaddr+0x8);
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0x90, ioaddr+0x8);
+ /* 10ms delay needed */
+ printf("Manufacturer ID - ");
+ /* manuf. id */
+ OUTL(0x0000, ioaddr+0x4);
+ printf("%02x\n", inb(ioaddr+0x8));
+ /* device id */
+ OUTL(0x0001, ioaddr+0x4);
+ printf("Device ID - %02x\n", inb(ioaddr+0x8));
+ /* undo the funky chicken */
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0xaa, ioaddr+0x8);
+ OUTL(0x2aaa, ioaddr+0x4);
+ OUTB(0x55, ioaddr+0x8);
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0xf0, ioaddr+0x8);
+ } else if (strcmp(argv[2], "read") == 0) {
+ for (i = 0; i < 65536; i++) {
+ OUTL(i, ioaddr+0x4);
+ b = inb(ioaddr+0x8);
+ write(1, &b, 1);
+ }
+ } else if (strcmp(argv[2], "prog") == 0) {
+ /* program the rom in 128 bute chunks */
+ for (i = 0, n = 0; i < 65536; i += n) {
+ n = read(0, buf, 128);
+ if (n == 0)
+ break;
+ if (n < 0) {
+ perror("File Error");
+ exit(-3);
+ }
+ /* disable SDP temporarily for programming a sector */
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0xaa, ioaddr+0x8);
+ OUTL(0x2aaa, ioaddr+0x4);
+ OUTB(0x55, ioaddr+0x8);
+ OUTL(0x5555, ioaddr+0x4);
+ OUTB(0xa0, ioaddr+0x8);
+ for (j = 0; j < n; j++) {
+ OUTL(i+j, ioaddr+0x4);
+ OUTB(buf[j], ioaddr+0x8);
+ }
+ /* wait for the programming of this sector to coomplete */
+ while (inb(ioaddr+0x8) != buf[j-1])
+ ;
+ }
+ }
+
+ /* Set the register window to 3 for the 3c905b */
+ OUTW(0x803, ioaddr+0xe);
+ /* restore the receiver status */
+ OUTL(recvrstat, ioaddr);
+ return 0;
+}
+
+#endif /* __i386__ */
diff --git a/gpxe/contrib/3c90xutil/cromutil.c b/gpxe/contrib/3c90xutil/cromutil.c
new file mode 100644
index 00000000..d4751fbf
--- /dev/null
+++ b/gpxe/contrib/3c90xutil/cromutil.c
@@ -0,0 +1,103 @@
+/*
+ * 3c905cutil.c - perform various control ops on the 3C905C bios rom
+ * which we assume to be an AT49BV512
+ *
+ */
+
+#ifndef __i386__
+# error "This program can't compile or run on non-intel computers"
+#else
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/io.h>
+
+int main(int argc, char **argv)
+{
+ unsigned int ioaddr, i, n;
+ unsigned char b;
+
+ setuid(0); /* if we're setuid, do it really */
+ if (argc != 3) {
+ printf("Usage: romid ioaddr [erase|id|read >file|prog <file]\n");
+ exit(-1);
+ }
+ if (iopl(3)) {
+ perror("iopl()");
+ exit(1);
+ }
+ sscanf(argv[1],"%x",&ioaddr);
+
+ /* Set the register window to 0 for the 3C905C */
+ outw(0x800, ioaddr+0xe);
+
+ if (strcmp(argv[2], "erase") == 0) {
+ /* do the funky chicken to erase the rom contents */
+ outl(0x5555, ioaddr+0x4);
+ outb(0xaa, ioaddr+0x8);
+ outl(0x2aaa, ioaddr+0x4);
+ outb(0x55, ioaddr+0x8);
+ outl(0x5555, ioaddr+0x4);
+ outb(0x80, ioaddr+0x8);
+ outl(0x5555, ioaddr+0x4);
+ outb(0xaa, ioaddr+0x8);
+ outl(0x2aaa, ioaddr+0x4);
+ outb(0x55, ioaddr+0x8);
+ outl(0x5555, ioaddr+0x4);
+ outb(0x10, ioaddr+0x8);
+ sleep (1);
+ printf("Bios ROM at %04x has been erased\n", ioaddr);
+ } else if (strcmp(argv[2], "id") == 0) {
+ outl(0x5555, ioaddr+0x4);
+ outb(0xaa, ioaddr+0x8);
+ outl(0x2aaa, ioaddr+0x4);
+ outb(0x55, ioaddr+0x8);
+ outl(0x5555, ioaddr+0x4);
+ outb(0x90, ioaddr+0x8);
+ /* 10ms delay needed */
+ printf("Manufacturer ID - ");
+ /* manuf. id */
+ outl(0x0000, ioaddr+0x4);
+ printf("%02x\n", inb(ioaddr+0x8));
+ /* device id */
+ outl(0x0001, ioaddr+0x4);
+ printf("Device ID - %02x\n", inb(ioaddr+0x8));
+ /* undo the funky chicken */
+ outl(0x5555, ioaddr+0x4);
+ outb(0xaa, ioaddr+0x8);
+ outl(0x2aaa, ioaddr+0x4);
+ outb(0x55, ioaddr+0x8);
+ outl(0x5555, ioaddr+0x4);
+ outb(0xf0, ioaddr+0x8);
+ } else if (strcmp(argv[2], "read") == 0) {
+ for (i = 0; i < 65536; i++) {
+ outl(i, ioaddr+0x4);
+ b = inb(ioaddr+0x8);
+ write(1, &b, 1);
+ }
+ } else if (strcmp(argv[2], "prog") == 0) {
+ for (i = 0; i < 65536; i++) {
+ n = read(0, &b, 1);
+ if (n == 0)
+ break;
+ if (n < 0) {
+ perror("File Error");
+ exit(-3);
+ }
+ outl(0x5555, ioaddr+0x4);
+ outb(0xaa, ioaddr+0x8);
+ outl(0x2aaa, ioaddr+0x4);
+ outb(0x55, ioaddr+0x8);
+ outl(0x5555, ioaddr+0x4);
+ outb(0xA0, ioaddr+0x8);
+ outl(i, ioaddr+0x4);
+ outb(b, ioaddr+0x8);
+ while (inb(ioaddr+0x8) != b)
+ ;
+ }
+ }
+ return 0;
+}
+
+#endif /* __i386__ */
diff --git a/gpxe/contrib/3c90xutil/romutil.txt b/gpxe/contrib/3c90xutil/romutil.txt
new file mode 100644
index 00000000..58074b9b
--- /dev/null
+++ b/gpxe/contrib/3c90xutil/romutil.txt
@@ -0,0 +1,36 @@
+I wrote the attached little util program to try out the basic approach
+and thought that you might find it useful as well as providing some
+simple testing. It isn't a final solution so the interface is rough. The
+program must be run as root on an Intel based machine.
+
+The key point is that the IO address needs to be entered - I grab it
+from the dmesg output:
+
+eth0: 3Com 3c905B Cyclone 100baseTx at 0xe400, 00:10:4b:d2:5e:0d, IRQ
+11
+
+or "cat /proc/pci" to find the "I/O at XXXXXX" for your 3Com Card.
+
+Some example commands are:
+
+romutil 0xe400 erase - erases the ROM contents
+romutil 0xe400 protect - enables the Software Data Protection
+on the ROM [3c905B only]
+romutil 0xe400 unprotect - disables the Software Data Protection
+on the ROM [3c905B only]
+romutil 0xe400 id - displays the manufacturer and
+device IDs
+romutil 0xe400 read >file - writes the contents of the ROM to stdout
+romutil 0xe400 prog <file - writes the contents of the stdin into the
+ROM (<64k)
+
+I tried reading and writing the ROM while doing large ftp transfers and
+experienced no problems. I didn't spend much time worrying about the
+possible race conditions. My system has lots of resources (450MHx P2,
+128MB RAM) so it might not provide the best test candidate.
+
+Let me know what results you get if you try it out.
+
+Thanks
+
+John
diff --git a/gpxe/contrib/Diskless-From-NT/Config.txt b/gpxe/contrib/Diskless-From-NT/Config.txt
new file mode 100644
index 00000000..60385fb7
--- /dev/null
+++ b/gpxe/contrib/Diskless-From-NT/Config.txt
@@ -0,0 +1,537 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_X86=y
+CONFIG_ISA=y
+# CONFIG_SBUS is not set
+CONFIG_UID16=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_KMOD is not set
+
+#
+# Processor type and features
+#
+CONFIG_M386=y
+# CONFIG_M486 is not set
+# CONFIG_M586 is not set
+# CONFIG_M586TSC is not set
+# CONFIG_M586MMX is not set
+# CONFIG_M686 is not set
+# CONFIG_M686FXSR is not set
+# CONFIG_MPENTIUM4 is not set
+# CONFIG_MK6 is not set
+# CONFIG_MK7 is not set
+# CONFIG_MCRUSOE is not set
+# CONFIG_MWINCHIPC6 is not set
+# CONFIG_MWINCHIP2 is not set
+# CONFIG_MWINCHIP3D is not set
+# CONFIG_X86_CMPXCHG is not set
+CONFIG_X86_L1_CACHE_SHIFT=4
+# CONFIG_TOSHIBA is not set
+# CONFIG_MICROCODE is not set
+# CONFIG_X86_MSR is not set
+# CONFIG_X86_CPUID is not set
+CONFIG_NOHIGHMEM=y
+# CONFIG_HIGHMEM4G is not set
+# CONFIG_HIGHMEM64G is not set
+CONFIG_MATH_EMULATION=y
+CONFIG_MTRR=y
+# CONFIG_SMP is not set
+# CONFIG_X86_UP_IOAPIC is not set
+
+#
+# General setup
+#
+CONFIG_NET=y
+# CONFIG_VISWS is not set
+CONFIG_PCI=y
+# CONFIG_PCI_GOBIOS is not set
+# CONFIG_PCI_GODIRECT is not set
+CONFIG_PCI_GOANY=y
+CONFIG_PCI_BIOS=y
+CONFIG_PCI_DIRECT=y
+# CONFIG_PCI_NAMES is not set
+CONFIG_EISA=y
+# CONFIG_MCA is not set
+CONFIG_HOTPLUG=y
+
+#
+# PCMCIA/CardBus support
+#
+# CONFIG_PCMCIA is not set
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+CONFIG_KCORE_ELF=y
+# CONFIG_KCORE_AOUT is not set
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=m
+CONFIG_PM=y
+# CONFIG_ACPI is not set
+# CONFIG_APM is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+CONFIG_PARPORT_PC_FIFO=y
+# CONFIG_PARPORT_PC_SUPERIO is not set
+# CONFIG_PARPORT_AMIGA is not set
+# CONFIG_PARPORT_MFC3 is not set
+# CONFIG_PARPORT_ATARI is not set
+# CONFIG_PARPORT_SUNBPP is not set
+# CONFIG_PARPORT_OTHER is not set
+# CONFIG_PARPORT_1284 is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+CONFIG_BLK_DEV_FD=m
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
+# CONFIG_BLK_DEV_INITRD is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
+
+#
+#
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_HP100 is not set
+CONFIG_NET_ISA=y
+# CONFIG_E2100 is not set
+# CONFIG_EEXPRESS is not set
+# CONFIG_EEXPRESS_PRO is not set
+# CONFIG_HPLAN_PLUS is not set
+# CONFIG_HPLAN is not set
+# CONFIG_ETH16I is not set
+CONFIG_NE2000=y
+# CONFIG_SK_G16 is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+# CONFIG_IRDA is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Input core support
+#
+# CONFIG_INPUT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+CONFIG_PRINTER=m
+# CONFIG_LP_CONSOLE is not set
+# CONFIG_PPDEV is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+CONFIG_MOUSE=y
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+
+#
+# Joysticks
+#
+
+#
+# Game port support
+#
+
+#
+# Gameport joysticks
+#
+
+#
+# Serial port support
+#
+
+#
+# Serial port joysticks
+#
+
+#
+# Parallel port joysticks
+#
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_INTEL_RNG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+CONFIG_AGP=m
+CONFIG_AGP_INTEL=y
+CONFIG_AGP_I810=y
+CONFIG_AGP_VIA=y
+# CONFIG_AGP_AMD is not set
+# CONFIG_AGP_SIS is not set
+# CONFIG_AGP_ALI is not set
+# CONFIG_DRM is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=m
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS_FS_VERBOSE=0
+# CONFIG_CRAMFS is not set
+# CONFIG_RAMFS is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+CONFIG_DEVFS_FS=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SMB_NLS is not set
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+CONFIG_NLS_ISO8859_1=m
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Console drivers
+#
+CONFIG_VGA_CONSOLE=y
+# CONFIG_VIDEO_SELECT is not set
+# CONFIG_MDA_CONSOLE is not set
+
+#
+# Frame-buffer support
+#
+# CONFIG_FB is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+# CONFIG_SOUND_CMPCI is not set
+# CONFIG_SOUND_EMU10K1 is not set
+# CONFIG_SOUND_FUSION is not set
+# CONFIG_SOUND_CS4281 is not set
+# CONFIG_SOUND_ES1370 is not set
+CONFIG_SOUND_ES1371=m
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_MAESTRO is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_TRIDENT is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+CONFIG_SOUND_VIA82CXXX=m
+CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_TRACEINIT is not set
+# CONFIG_SOUND_DMAP is not set
+# CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_ADLIB is not set
+# CONFIG_SOUND_ACI_MIXER is not set
+# CONFIG_SOUND_CS4232 is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_ICH is not set
+# CONFIG_SOUND_VMIDI is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_NM256 is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_PAS is not set
+# CONFIG_SOUND_PSS is not set
+CONFIG_SOUND_SB=m
+# CONFIG_SOUND_AWE32_SYNTH is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+# CONFIG_SOUND_MAUI is not set
+CONFIG_SOUND_YM3812=m
+# CONFIG_SOUND_OPL3SA1 is not set
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_YMPCI is not set
+# CONFIG_SOUND_YMFPCI is not set
+# CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_AEDSP16 is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
diff --git a/gpxe/contrib/Diskless-From-NT/Diskless-From-NT.txt b/gpxe/contrib/Diskless-From-NT/Diskless-From-NT.txt
new file mode 100644
index 00000000..54c2b746
--- /dev/null
+++ b/gpxe/contrib/Diskless-From-NT/Diskless-From-NT.txt
@@ -0,0 +1,565 @@
+
+
+The Diskless Terminal running from NT server Mini-HOWTO
+Pavel Tkatchouk, ptkatcho@portal.ca
+v0.1, June 19th 1999
+
+Table of Contents
+
+1. Introduction.
+
+ 1.1 What is it for?
+ 1.2 Do we need this HOWTO?
+ 1.3 A bit of History.
+
+2. Project description.
+
+ 2.1 Packaging.
+ 2.2 Image.
+ 2.2.1 Kernel.
+ 2.2.2 MRFS.
+ 2.2.3 Building MRFS.
+ 2.3 Remotefs.
+ 2.4 Booting sequence.
+ 2.4.1 BOOTP, TFTP.
+ 2.5 Bootprom.
+
+3. Resources.
+
+4. Copyright.
+
+5. Feedback and credits.
+
+
+1. Introduction.
+
+
+1.1. What is it for?
+
+This document describes how to build software distribution to run Java client on diskless
+terminal booted from Microsoft Windows 95/98/NT workstation. Package can also be easily
+modified to be used as Linux terminal or X Windows terminal's software. I found it also
+convenient for setup over the Ethernet of floppyless PS's, hard disk of which for some
+reason can not be accessed (sealed case under warranty, etc.).
+
+
+1.2. Do we need this HOWTO?
+
+To be honest, I'm not sure. There are few excellent HOWTO's (see 3. Recources) that up until
+recently I considered quite sufficient to build what I've done two years ago. But since my
+project uses MS Windows as a file server vs. traditional NFS there were some know-how's
+involved which number of people wanted to see in some formal document.
+
+
+1.3. A bit of history.
+
+My project at that time (1996) was to find OS/JVM that will allow to run Java application
+on hardware we manufacture. Hardware is practically generic x86 PC except it has no keyboard,
+hard drive, floppy drive, mouse, but touchscreen over LCD, plus some POS specific peripherals
+(badge reader, credit card reader, etc.). Due to cost consideration it had no any significant
+storage, so OS and Java client along with support binaries, libraries etc. had to be loaded
+remotely. Because our clients are exclusively Windows shops, Server had to be Windows as well.
+During evaluation of different commercial OS'es along with JVM's available it become apparent
+to my surprise that most promising solution was GPL one - Linux.
+
+
+2. Project description.
+
+2.1. Packaging.
+
+The whole distribution consists of remote file system (RemoteFS) residing on MS Windows
+server (NT Workstation, NT Server or Windows9x) and tagged bootable image.
+
+
+2.2. Image.
+
+Image (~1.5MB) is generated by mknbi utility that comes with Etherboot package
+<http://etherboot.sourceforge.net>. It can include minimal root file system (MRFS)
+like in my case (since I had to boot client from MS Windows server and Linux kernel doesn't
+support SMBFS-Root, only NFS-Root. So I had to keep rootfs in the ramdisk). To generate
+image the following script can be used.
+
+#!/bin/sh
+# mkrootnet: makes tagged netbootable image
+# This image includes kernel and minimal root filesystem
+# to do initial boot.
+#
+# Copyright (c) Pavel Tkatchouk 1996. All rights reserved.
+# Permission is granted for this material to be freely
+# used and distributed, provided the source is acknowledged.
+# No warranty of any kind is provided. You use this material
+# at your own risk.
+#
+DEVICEFILENAME="/tmp/file" # temporary file to be used as device
+FSBLOCKS=4096 # uncompressed filesystem size in K
+BOOTDISKDIR="/usr/BOOT/ROOTFS" # root filesystem model
+MOUNT="/mnt2" # temporary mount point
+ROOTFS="/tmp/rootfs" # root filesystem image
+ROOTFSGZ="/tmp/rootfs.gz" # compressed root filesystem image
+KERNEL="/usr/KERNELS/vmlinuz-nt" # kernel image
+KERNELTMP="/tmp/vmlinuz" # temporary copy of kernel image
+BOOTIMAGE="/tmp/img" # tagged image to be booted by client
+# if you want ramisk more than default 4096 set CMDLINE, don't forget to
+# adjust $FSBLOCKS
+# CMDLINE="ramdisk_size=8192" # parameters to pass to the kernel
+#
+echo "check:"
+echo "- if tftp server's download dir mounted to /mnt"
+echo "- loopback device is built-in or loaded"
+echo "\n press Enter when done"
+read tmp
+UPLOAD="/mnt/tmp" # tftp server's dir to upload bootimage
+echo -e "\nZeroing $DEVICEFILENAME of $FSBLOCKS k"
+echo "to be used as device for root filesystem model"
+dd if=/dev/zero of=$DEVICEFILENAME bs=1k count=$FSBLOCKS
+echo -e "\nMaking file system on $DEVICEFILENAME"
+mke2fs -m 0 $DEVICEFILENAME
+echo "Mounting $DEVICEFILENAME as a loopback device"
+mount -o loop -t ext2 $DEVICEFILENAME $MOUNT
+curdir=`pwd`
+cd $BOOTDISKDIR
+echo -e "Copying files from $BOOTDISKDIR to $DEVICEFILENAME, please wait"
+find . -print|cpio -pmd $MOUNT
+echo "Unmounting $MOUNT"
+umount $MOUNT
+cd $curdir
+echo "Copying $DEVICEFILENAME to $ROOTFS"
+dd if=$DEVICEFILENAME of=$ROOTFS bs=1k
+echo "Compressing $ROOTFS, it may take a while"
+echo "Please wait..."
+if [ -f $ROOTFSGZ ];then
+ rm -f $ROOTFSGZ
+fi
+gzip -c $ROOTFS>$ROOTFSGZ
+rm -f $ROOTFS
+echo -e "\nCreating netbootable image"
+cp $KERNEL $KERNELTMP
+mknbi -d ram -i rom -r $ROOTFSGZ -k $KERNELTMP -a $CMDLINE -o $BOOTIMAGE
+echo "Uploading $BOOTIMAGE to $UPLOAD"
+cp $BOOTIMAGE $UPLOAD
+echo "Cleaning after ourselves"
+rm -f $KERNELTMP $DEVICEFILENAME $BOOTIMAGE
+echo "All done"
+
+
+In the above script actual image is generated by the following comand
+
+#mknbi -d ram -i rom -r rootfs.gz -k vmlinuz-nt -o img
+
+where:
+ rootfs.gz - minimal root file system (MRFS);
+ vmlinuz-nt - kernel;
+ img - resulting image.
+
+
+Note:
+Default ramdisk size is 4096. It was enough for RedHat4.1 based minimal file system, but
+apparently not enough for 5.2 based. When this happens "end request:I/O error, dev 01:00 ..."
+error shows up. To fix that either use "mknbi -a ramdisk_size=8192" to pass parameter to the
+kernel (doesn't require kernel recompilation), or change /usr/src/linux/drivers/block/rd.c:
+int rd_size= from 4096 to 8192 or whatever and rebuild the kernel.
+
+
+2.2.1. Kernel.
+
+Kernels 2.0.30 and 2.0.36 have been used by author, although nothing is preventing you from
+experimenting with others. Kernel should include ramdisk support. The following
+<link to .config> configuration has been used to build <link to binary (kernel 2.0.30)>.
+You may find some components unnecessary, just exclude them and rebuild.
+
+Don't forget to change root device after you built the kernel (rdev vmlinuz /dev/rd).
+
+Gotcha's: apparently smbfs is broken in 2.2.x kernels. Symptoms: remote share is mounted
+just fine but after a while fails with "smb_request: result = -32" errmsg. I've heard
+SuSe has fix for that.
+
+2.2.2. MRFS.
+
+Minimal root file system is required to get Linux up and running along with networking until
+it can mount remote file system to run X/Java from there. After image gets loaded from the
+server MRFS is decompressed into ramdisk. If you can afford a lot of ram on your terminal the
+entire remote file system can be moved to rootfs.gz. That will make your terminal more
+responsive.
+
+
+2.2.3. Building MRFS.
+
+Some folks found it easier to start from scratch, others use known "minimal" Linux distributions
+(Linux Router, tomsrtbt, etc.), yet others prefer to start from "big" Linuces like I did. Every
+path has it's pro and contras.
+
+Pruning standard distribution (RedHat, Debian, etc.) to your needs might be very time consuming.
+To ease that painful process I have used remotely booted diskless client with NFS-Root (see
+Etherboot's Readme, NFS-Root and NFS-Root-Client mini-HOWTO's, Diskless-HOWTO):
+
+- setup minimal RedHat4.1 install (networked workstation, X, no development, mail, etc., ~117MB);
+- find . -print|cpio -pmd /usr/NFS/ROOTFS - copy entire fs tree to NFS exported dir;
+- mknod /usr/NFS/ROOTFS/dev/nfsroot b 0 255;
+- build vmlinuz-nfs kernel according to NFS-Howto (built-in bootp,rarp,NFS,NFS root,NIC
+ driver,RAM disk);
+- rdev vmlinuz-nfs /dev/nfsroot - to set NFS root device;
+- build image for NFS-Root fs:
+ #mknbi -d rom -i rom -k vmlinuz-nfs -o nfsImage;
+- boot client while monitoring NFS file requests (by Solaris snoop);
+- copy files from /usr/NFS/ROOTFS to /usr/BOOT/ROOTFS (MRFS model) according to snoop's
+ filelist;
+- generate image by mkrootnet script (don't forget to point to the right kernel vmlinuz-nt).
+
+The above trick not only allows to determine the sought files set but also debug boot process
+analyzing NFS messages. I found it convenient to put "read tmp" statements into init scripts
+for debugging. Tracking files up until issuing login gives you <link to rootfs.gz> MRFS (~1MB)
+that can be used to boot Linux from ROM (flash, eprom, DiskOnChip, SanDisk, etc.) as well. All
+the other files requested by client (during starting X, Java, Java client) were put into (link
+to remotefs.zip, ~9MB).
+
+
+To restore MRFS model on your PC from the above rootfs.gz:
+- #cd /tmp
+- #gunzip rootfs.gz
+- #mount -o loop -t ext2 /tmp/rootfs /mnt
+- #cd /mnt
+- #find . -print|cpio -pmd /usr/BOOT/ROOTFS
+- #umount /mnt
+
+Note:
+
+You will have to change attributes of some dirs, files (/etc/mtab, /etc/mtab~, /var/lock/subsys/*,
+/var/run/*, /dev/tty*, etc.) against standard. This is because with standard attribs diskless
+client refused to work. For example I had to change /dev/tty* ownerships to 99:99 from original
+0:0 or 0:5, to get rid of errmsg "INIT: Id "1" respawning too fast: disabled for 5 minutes".
+Being admin illiterate I just chmod them to 777 and chown to 99:99 to make life easier.
+THIS IS SERIOUS SECURITY VIOLATION!!! Using keyboardless terminal with no daemons running in
+my case reduces the risk, yet I would appreciate very much those more experienced who will help
+to restore the right attribs while keeping the distribution working.
+
+Some "gotcha's" to watch for during MRFS building:
+- standard attributes/ownership of some files don't work;
+- rdev must be set (non-tagged image didn't work, so couldn't use config file to pass parrs
+ to the kernel);
+- diskless client writes 99:99 ownership on generated files;
+- "password incorrect" for root, but any other OK and su OK too.
+
+
+2.3. RemoteFS.
+
+Remotefs.zip file includes everything required by the system that can be located on
+remote file system, i.e after booting has been complete and remote file system mounted.
+In my case it is X Windows System and Java binaries, libraries etc. To use that file on
+MS Windows NT:
+- unzip remotefs.zip to some directory;
+- share this directory read-only as "usr" (or share as some other name and pass this name to
+ the client through bootptab configuration file for BOOTP server;
+- create an account username=root, password=linux on NT (can be set in bootptab).
+
+Note:
+There's no symbolic links on NTFS, so UNIX links must be replaced by copies on NTFS.
+To determine potential troublmakers one could use the following:
+- first copy required subset (according to snoop's intercept) from /usr/NFS/ROOTFS to
+ /usr/BOOT/REMOTEFS;
+- mount some share from NTFS to /mnt;
+- /usr/BOOT/REMOTEFS#find . -print|cpio -pmd /mnt 2>links;
+In the links file you will find names to work with.
+
+
+2.4. Booting sequence.
+
+Boot occurs in the following sequence:
+- bootprom sends bootp request,
+- bootp server responds with subnet mask, client's name, client's IP, TFTP server's IP,
+ bootfile name and some optional parameters (like NT's username/password to use it's share,
+ you could pass some other share name here as say T104="somedir");
+- bootprom downloads image from TFTP server;
+- kernel starts;
+- kernel decompresses MRFS in RAM;
+- system starts init using ramdisk root,
+- mounts remote file system from NT via SMBFS;
+- automatically logins;
+- starts xstart script located on remotefs (/usr/sbin) where you can start any of your
+ programs, change parameters, etc. without rebuilding the image.
+
+Below are some config/init sample files from <rootfs.gz>, <remotefs.zip>:
+
+<bootptab, change to link>
+t1:sm=255.255.255.0:sa=192.168.33.150:bf=img:T100="pavelnt4":T101="root":T102="linux"
+touch1:hn=touch1:tc=t1:ha=00A0F00035CD:ip=192.168.33.127
+
+</etc/fstab, change to link>:
+/dev/ram / ext2 defaults 1 1
+/proc /proc proc defaults 0 0
+
+</etc/rc.d/rc.bootp, change to link later>:
+#!/bin/sh
+# Written to simply set the IP stuff up from the
+# bootpc data.
+# Last updated : Mon Mar 10 15:17:01 1997
+#
+# Variables
+
+BOOTPC=/sbin/bootpc
+IFCONFIG=/sbin/ifconfig
+ROUTE=/sbin/route
+BINHOST=/bin/hostname
+DEV=eth0
+ASKSERVER="255.255.255.255"
+TW="--timeoutwait 320"
+RIF="--returniffail"
+RIFMESSAGE="Bootp failed -- disabling network."
+RCONF=/etc/resolv.conf
+EHOSTS=/etc/hosts
+LHOSTS=/etc/hosts.local
+TMPFILE=/tmp/bootp
+# Functions
+# Remove the networking by taking down the interface
+netdown() {
+ ${ROUTE} del default
+ ${IFCONFIG} ${DEV} down
+}
+## End of the functions
+
+## Start of the actual work
+# Bring up minimal networking use 0.0.0.0 as our address as we don't
+# know it yet (Means "Me but I don't know my address or network")
+${IFCONFIG} ${DEV} up 0.0.0.0
+${ROUTE} add default dev ${DEV}
+
+# Perform the bootp -- doesn't return unless it gets an answer
+if ${BOOTPC} --dev ${DEV} --server ${ASKSERVER} ${RIF} ${TW} > ${TMPFILE}
+then
+# Take down networking (use the 0.0.0.0 for as short a time as possible)
+ netdown
+# Read in the values
+ . ${TMPFILE}
+
+# To use in mountsmb script later
+SMBSERVER=${T100}
+# And delete the temporary file
+# rm ${TMPFILE}
+else
+# Take down networking (use the 0.0.0.0 for as short a time as possible)
+ netdown
+# give message and quit
+ echo ${RIFMESSAGE}
+ exit 1
+fi
+
+# Start the loopback interface and add a route to it
+# It's already set by standard init?
+${IFCONFIG} lo 127.0.0.1
+${ROUTE} add -net 127.0.0.0
+
+# Setup of IP stuff needs doing first
+#
+if [ -z "${NETMASK}" ] ; then
+# No netmask info, all this is guessed from the IP number
+# If this is wrong for your network FIX the bootpd to know
+# what it should send in the RFC1497 cookie! 11/02/94 JSP
+#
+ ${IFCONFIG} ${DEV} up ${IPADDR} broadcast ${BROADCAST}
+ ${ROUTE} -n add -net ${NETWORK} dev ${DEV}
+else
+# We will have NETMASK, BROADCAST, and NETWORK defined
+ ${IFCONFIG} ${DEV} up ${IPADDR} broadcast ${BROADCAST} netmask ${NETMASK}
+ ${ROUTE} -n add -net ${NETWORK} dev ${DEV}
+fi
+
+# Set the hostname from what we got via bootp or reverse lookup
+
+echo "127.0.0.1 loopback localhost">${EHOSTS}
+${BINHOST} "${HOSTNAME}"
+echo "${IPADDR} ${HOSTNAME}" >>${EHOSTS}
+echo "${SERVER} ${SMBSERVER}" >>${EHOSTS}
+
+
+</etc/rc.d/rc.local, change to link>:
+#!/bin/sh
+# This script will be executed *after* all the other init scripts.
+# You can put your own initialization stuff in here if you don't
+# want to do the full Sys V style init stuff.
+#
+# 07/02/97 Pavel Tkatchouk
+#
+echo "Start networking"
+insmod /lib/8390.o
+insmod /lib/ne.o io=0x300 irq=9
+echo "Install serial"
+insmod /lib/serial.o
+echo "Install touch"
+insmod /lib/touch.o
+echo "Install smbfs"
+insmod /lib/smbfs.o
+echo "Getting TCP/IP parameters from bootp server"
+echo "and start networking"
+/etc/rc.d/rc.bootp
+if [ -f /etc/squirrel-release ]; then
+ R=$(cat /etc/squirrel-release)
+else
+ R="release 0.02"
+fi
+echo "Mounting remote fs"
+/sbin/mountsmb
+echo "XYZ Inc. Diskless Linux $R"
+echo "Starting X and Java client without login"
+su -c /sbin/xstart root
+
+
+</usr/sbin/xstart, change to link>:
+#!/bin/bash
+#
+# Script to start X and Java client
+# 08/07/97 Pavel Tkatchouk
+#
+# Read bootps response first
+. /tmp/bootp
+# -s 0 to disable screen-saver
+/usr/X11R6/bin/X -s 0 &
+export DISPLAY=:0.0
+# /usr is share mounted from Windows workstation
+cd /usr/program/
+java SomeJavaApp
+
+
+</sbin/mountsmb, change to link>:
+#!/bin/bash
+# mountsmb: mounts remote filesystems from NT workstation
+# using Microsoft's SMB protocol
+#
+# Copyright (c) Pavel Tkatchouk 1997. All rights reserved.
+# Permission is granted for this material to be freely
+# used and distributed, provided the source is acknowledged.
+# No warranty of any kind is provided. You use this material
+# at your own risk.
+#
+# Last edit June 29 8:30 1997
+#
+MOUNTDIR="usr"
+SHRDIR="usr"
+BOOTPRES="/tmp/bootp"
+# Read botpc response
+. ${BOOTPRES}
+# Sharename from NT server, uncomment if you want to use
+# non-hardcoded "usr" but from bootptab
+#SHRDIR=${T104}
+SMBSRV="//${T100}"
+CLIENT="${HOSTNAME}"
+USER="${T101}"
+PASSWORD="${T102}"
+echo -e "\nMounting $SMBSRV/$SHRDIR to /$MOUNTDIR"
+smbmount $SMBSRV/$SHRDIR $MOUNTDIR -c $CLIENT -U $USER -P $PASSWORD
+echo -e "\nDone"
+
+Gotcha's:
+Looks like smbmount client from smbfs package used to mount remote Windows shares to local
+Linux dirs in pre 2.2.x era isn't maintained anymore so you should use one coming with
+Samba package. Also binary smbmount won't work with 2.2.x, so you have to recompile with
+2.2.x headers following Samba's readme. Yet even that won't guarantee reliable work until
+somebody fixes kernel's smbfs module.
+
+2.4.1. BOOTP, TFTP.
+
+There are number of BOOTP, TFTP servers for Windows on the market. You could find them
+here:
+
+- www.walusoft.co.uk (Walusoft's tftp);
+- ftp.coast.net/simtel/nt/internet/tftpds12.zip (Millwood AB's tftp);
+- ftp.cabletron.com/pub/snmp/bootftp/boottft2.zip (Cabletron's bootp/tftp combo);
+- www.tellurian.au.com (Tellurian's bootp, tftp, dhcp servers).
+- www.metainfo.com (Metainfo's DHCP server)
+- www.nts.com (Network Telesystems's DHCP server in IPserver package)
+
+My choice was Tellurian's products - very reliable, simple to install, attractively priced
+(fully capable evaluation versions are available).
+
+2.5. Bootprom.
+
+Ken Yap's Etherboot <etherboot.sourceforge.net> will tell you everything about bootprom.
+Here I just want to mention that normally you would have to put bootprom's code into network
+adapter's PROM. But if your hardware like mine has BIOS programmed in flash you could
+re-program it to add bootprom (some BIOS requires special programmer to do that, others don't)
+as BIOS extension.
+
+This is what I did to add ne.rom (bootprom generated by Etherboot's makerom for NE2000 clone)
+to AMI BIOS on my flash:
+
+- read flash content by programmer into bios.bin binary file;
+- use one of available binary editors (say www.simtel.net/Win95/editors/hxp3005.zip to add
+ ne.rom to bios.bin (and to edit ne.rom if necessary);
+- write new bios.bin back to flash.
+
+Notes:
+- makerom generates bootprom for standard EPROM sizes (8k, 16k, 32k, etc.), so if you tight on
+ space use -s flag to adjust size (or cut it manually to multiple of 512 bytes blocks, just
+ don't forget to adjust extension's length which is coded in Byte 2 and checksum to 8 bits
+ of zero;
+- valid absolute addresses for BIOS extensions are from 0xC8000 to 0xF4000 (check with
+ motherboard's manufacturer how flash is mapped onto system memory space);
+- Byte 0 must be 0x55, Byte 1 must be 0xAA, Byte 2 must be extension's length in 512 bytes
+ blocks;
+- extension BIOS has to start at a 2k boundary;
+
+
+3. Resources.
+
+FAQ's:
+- tomsrtbt.FAQ (www.toms.net);
+
+HOWTO's:
+- Paul Moody's miniHOWTO (www.linuxembedded.com/pmhowto.html)
+- Diskless;
+- Diskless-HOWTO;
+- NFS-Root;
+- NFS-Root-Client;
+- Bootdisk-HOWTO;
+- BootPrompt-HOWTO;
+- NCD-X-Terminal;
+- Remote-Boot;
+- Remote-X-Apps;
+
+Web:
+- etherboot.sourceforge.net/
+- www.waste.org/~zanshin
+- www.tellurian.com.au.
+- www.toms.net
+- www.trinux.org
+- www.linux.org.uk/ELKS-Home
+- www.embedded.com
+- www.linuxembedded.com
+- www.thinlinux.org
+- www.linuxrouter.org
+- linux-mandrake.com
+- www.disklessworkstations.com
+
+Newsgroups:
+- comp.arch.embedded
+
+Lists:
+- netboot-owner@baghira.han.de
+- linux-embedded@waste.org
+
+Magazines:
+- Circuit Cellar #100 - 105
+
+
+4. Copyright.
+
+Copyright (c) Pavel Tkatchouk 1999.
+Permission is granted for this material to be freely used and distributed, provided the source
+is acknowledged. Copyright policy is GPL as published by the Free Software Foundation.
+
+No warranty of any kind is provided. You use this material at your own risk.
+
+
+
+5. Feedback and credits.
+
+Since I am neither have a lot of Linux experience nor native English speaker, there would be
+errors in this document. I would accept any help with gratitude whether in form of proof-reading,
+techical corrections or otherwise. Please send your comments, suggestions and questions to Pavel
+Tkatchouk (ptkatcho@portal.ca)
+
+I wish to thank Pierre Mondie who convinced me to start this document. I'm also very much in
+debt to all those who's work made this project possible:
+
+Ken Yap <ken_yap@users.sourceforge.net> (Etherboot)
+David Newall <www.tellurian.com.au> (Bootpdnt/Ftpdnt)
+(to be continued)
+
diff --git a/gpxe/contrib/Diskless-From-NT/furtmayr.html b/gpxe/contrib/Diskless-From-NT/furtmayr.html
new file mode 100644
index 00000000..224632d5
--- /dev/null
+++ b/gpxe/contrib/Diskless-From-NT/furtmayr.html
@@ -0,0 +1,82 @@
+<html>
+
+<head>
+<title>Free TFTP / Syslog / NFS Servers for Windows</title>
+</head>
+
+<body>
+
+<h3 align="center">Free TFTP / Syslog / NFS Servers for Windows</h3>
+<div align="center"><center>
+
+<address>
+ Stefan Furtmayr (<a href="mailto:sf@paf.net">sf@paf.net</a>)
+</address>
+</center></div>
+
+<p>Feel free to send me your comments about these programs or some additions.</p>
+
+<p>Also have a look at the <a
+href="http://www.ltsp.org/contrib/diskless-windows-howto.htm">&quot;Diskless Windows
+Cookbook &quot;</a> in the <a href="http://www.ltsp.org/contrib/">LTSP Contrib Area</a>.</p>
+
+<p><small>From this list I have only used the W2K-TFTP once for a customer with the <a
+href="http://support.3com.com/infodeli/tools/nic/mba.htm">3Com MBA Utility Disk</a>, while
+it can be used as well with <a href="http://etherboot.sourceforge.net">Etherboot</a>
+tagged images for other NIC brands.</small><br>
+<small>The solution used a netbooted DOS with MS Client 3.0 to easily restore disk images
+with <a href="http://www.ghost.com/">Symantec Ghost</a> (see <a
+href="http://appdeploy.com/tools/imaging.shtml">appdeploy.com</a> for similar tools).</small><br>
+<small>Sure there are several possibilities to do this with Linux but for cloning NT4/W2K
+the NTFS support is rather experimental and automatically changing the SID is another
+issue.</small></p>
+
+<h4>TFTP Servers:</h4>
+
+<p>In Autumn 2000 i tested some different TFTP servers and found out that most of them do
+not install/run as a service, especially under W2K.
+
+<ul>
+ <li>TFTP server included with Windows 2000 (remote installation services need to be
+ installed)<br>
+ The path for the images has to be specified via registry:<br>
+ Q232115 - Changing the Drive or Volume on Which Remote Installation Images Reside:<br>
+ <a href="http://support.microsoft.com/support/kb/articles/Q232/1/15.ASP">http://support.microsoft.com/support/kb/articles/Q232/1/15.ASP</a></li>
+ <li>3CDaemon version 2.0 revision 10. Freeware. Integrated TFTP/FTP/Syslog Daemon for
+ Windows 95/98/NT<br>
+ <a href="ftp://ftp.3com.com/pub/utilbin/win32/3cdv2r10.zip">ftp://ftp.3com.com/pub/utilbin/win32/3cdv2r10.zip</a><br>
+ - Windows 2000 Server: does not run as service!<br>
+ - Windows NT Server 4.0: not tested</li>
+ <li>The TFTP Server portion of 3CServer, altered to run as a system Service under Windows NT<br>
+ <a href="ftp://ftp.3com.com/pub/utilbin/win32/3CTftpSvc.zip">ftp://ftp.3com.com/pub/utilbin/win32/3CTftpSvc.zip</a><br>
+ - Windows 2000 Server: only working in debug mode, does not run as service!<br>
+ - Windows NT Server 4.0: not tested</li>
+ <li>Cisco TFTP Server v.1.1<br>
+ <a href="http://www.cisco.com/pcgi-bin/tablebuild.pl/tftp">http://www.cisco.com/pcgi-bin/tablebuild.pl/tftp</a><br>
+ Does not provide an option itself to install as service.</li>
+ <li><a href="http://solarwinds.net/Tools/Free_Tools/TFTP_Server/">http://solarwinds.net/Tools/Free_Tools/TFTP_Server/</a><br>
+ Does not provide an option itself to install as service.</li>
+</ul>
+
+<p><em>Untested:</em></p>
+
+<p>found on <a href="http://www.nonags.com/nonags/servd32.html">http://www.nonags.com/nonags/servd32.html</a><br>
+- <a href="http://www.klever.net/kin/pumpkin.html">http://www.klever.net/kin/pumpkin.html</a><br>
+- <a href="http://membres.tripod.fr/phjounin//P_tftpd32.htm">http://membres.tripod.fr/phjounin//P_tftpd32.htm</a></p>
+
+<h4>Syslog Servers:</h4>
+
+<ul>
+ <li>3Com Software Library - Utilities for 32 bit Windows<br>
+ <a href="http://support.3com.com/software/utilities_for_windows_32_bit.htm">http://support.3com.com/software/utilities_for_windows_32_bit.htm</a></li>
+ <li><a href="http://www.netal.com/download.htm#SL4NT03">http://www.netal.com/download.htm#SL4NT03</a>
+ (works as service) </li>
+</ul>
+
+<h4>NFS Servers:</h4>
+
+<ul>
+ <li>War NFS Daemon: <a href="http://www.jgaa.com">http://www.jgaa.com</a> (untested)</li>
+</ul>
+</body>
+</html>
diff --git a/gpxe/contrib/auto-default/mail b/gpxe/contrib/auto-default/mail
new file mode 100644
index 00000000..015c789a
--- /dev/null
+++ b/gpxe/contrib/auto-default/mail
@@ -0,0 +1,40 @@
+Date: 11/9/2001 3:56 PM
+Received: 11/9/2001 4:05 PM
+From: Steve Tilden, stilden@sicom-sys.com
+
+...
+
+2) I have added conditional code to main.c from Etherboot 5.0.4 to add
+a new default boot option and I have included the modified main.c as an
+attachment to this message.
+
+As I received Etherboot 5.0.4, in the Config file, if you select
+ASK_BOOT with a non zero time-out option, then you also get to set
+ANS_DEFAULT = ANS_NETWORK or ANS_DEFAULT = ANS_LOCAL to determine what
+will happen if the operator does not respond to the prompt. I have now
+added conditional code in main.c such that if you set ANS_DEFAULT =
+ANS_AUTO, the default answer will be set according to whether or not
+there is a hard disk in the system (as detected by the BIOS). If a hard
+disk is present, then if the operator does nothing, the system will boot
+from it. If a hard disk does not exist, then again if the operator does
+nothing, the system will boot via the network. Either way, for our
+particular environment, the operator has to do nothing to get it to boot
+correctly. Yet the operator can still override the default selection
+to, for example, allow a unit without a hard disk, to boot directly from
+a floppy rather than the network, or to allow a unit with a hard disk,
+to boot from the network.
+
+I don't know it the code I have added might be correct for a future
+production version of Etherboot, but I thought I'd send it to you and
+let you get it into the system if you feel it might be appropriate.
+
+Thanks,
+
+Steve Tilden
+Sicom Systems Inc.
+stilden@sicom-sys.com
+
+[Ed: On a compliant BIOS, it will actually boot the next device in the
+BIOS list if local is selected, either explicitly or by timeout, which
+may or may not be the hard disk, which is why it's less than general and
+not included in the distribution by default.]
diff --git a/gpxe/contrib/auto-default/main.c.patch b/gpxe/contrib/auto-default/main.c.patch
new file mode 100644
index 00000000..e707b63a
--- /dev/null
+++ b/gpxe/contrib/auto-default/main.c.patch
@@ -0,0 +1,55 @@
+--- main.c Mon Nov 5 18:58:30 2001
++++ main.c.new Thu Nov 15 01:45:12 2001
+@@ -149,21 +151,49 @@
+ static unsigned short ipchksum(unsigned short *ip, int len);
+ static unsigned short udpchksum(struct iphdr *packet);
+
++
++#if defined(ASK_BOOT) && ASK_BOOT > 0 && (ANS_DEFAULT == ANS_AUTO)
++/*
++ * Read Installed Hard Disk Count from BIOS memory at 0:0475
++ */
++static int hdsk_cnt(void)
++{
++ int retv;
++ __asm__ __volatile__(
++ "xorw %%ax,%%ax\n\t"
++ "movb 0x475,%%al\n"
++ : "=a" (retv)
++ : /* no inputs */
++ : "ax", "cc", "memory"
++ );
++ return(retv);
++}
++#endif /* ASK_BOOT && ANS_AUTO */
++
++
+ static inline void ask_boot(void)
+ {
+ #if defined(ASK_BOOT) && ASK_BOOT > 0
+ while(1) {
+- int c;
++ int c, deflt;
+ unsigned long time;
++#if defined(ASK_BOOT) && ASK_BOOT > 0 && (ANS_DEFAULT == ANS_AUTO)
++ if (hdsk_cnt() != 0)
++ deflt = ANS_LOCAL;
++ else
++ deflt = ANS_NETWORK;
++#else
++ deflt = ANS_DEFAULT;
++#endif
+ printf(ASK_PROMPT);
+ for (time = currticks() + ASK_BOOT*TICKS_PER_SEC; !iskey(); )
+ if (currticks() > time) {
+- c = ANS_DEFAULT;
++ c = deflt;
+ goto done;
+ }
+ c = getchar();
+ if ((c >= 'a') && (c <= 'z')) c &= 0x5F;
+- if (c == '\n') c = ANS_DEFAULT;
++ if (c == '\n') c = deflt;
+ done:
+ if ((c >= ' ') && (c <= '~')) putchar(c);
+ putchar('\n');
diff --git a/gpxe/contrib/award_plugin_roms/README b/gpxe/contrib/award_plugin_roms/README
new file mode 100644
index 00000000..5f657cf5
--- /dev/null
+++ b/gpxe/contrib/award_plugin_roms/README
@@ -0,0 +1,2 @@
+An Award BIOS ROM lister in Perl contributed by Eric W. Biederman
+<ebiederman@lnxi.com>.
diff --git a/gpxe/contrib/award_plugin_roms/award_plugin_roms.pl b/gpxe/contrib/award_plugin_roms/award_plugin_roms.pl
new file mode 100755
index 00000000..2b95eed1
--- /dev/null
+++ b/gpxe/contrib/award_plugin_roms/award_plugin_roms.pl
@@ -0,0 +1,341 @@
+#!/usr/bin/perl -w
+use strict;
+use FileHandle;
+use integer;
+
+sub unsigned_little_endian_to_value
+{
+ # Assumes the data is initially little endian
+ my ($buffer) = @_;
+ my $bytes = length($buffer);
+ my $value = 0;
+ my $i;
+ for($i = $bytes -1; $i >= 0; $i--) {
+ my $byte = unpack('C', substr($buffer, $i, 1));
+ $value = ($value * 256) + $byte;
+ }
+ return $value;
+}
+
+sub decode_fixed_string
+{
+ my ($data, $bytes) = @_;
+ return $data;
+}
+
+sub decode_pstring
+{
+ my ($buf_ref, $offset_ref) = @_;
+ # Decode a pascal string
+ my $offset = ${$offset_ref};
+ my $len = unpack('C',substr(${$buf_ref}, $offset, 1));
+ my $data = substr(${$buf_ref}, $offset +1, $len);
+ ${$offset_ref} = $offset + $len +1;
+ return $data;
+}
+
+sub decode_cstring
+{
+ # Decode a c string
+ my ($buf_ref, $offset_ref) = @_;
+ my ($data, $byte);
+ my $index = ${$offset_ref};
+ while(1) {
+ $byte = substr(${$buf_ref}, $index, 1);
+ if (!defined($byte) || ($byte eq "\0")) {
+ last;
+ }
+ $data .= $byte;
+ $index++;
+ }
+ ${$offset_ref} = $index;
+ return $data;
+}
+
+sub type_size
+{
+ my ($entry) = @_;
+ my %type_length = (
+ byte => 1,
+ half => 2,
+ word => 4,
+ xword => 8,
+ 'fixed-string' => $entry->[2],
+ pstring => 0,
+ cstring => 0,
+ );
+ my $type = $entry->[0];
+ if (!exists($type_length{$type})) {
+ die "unknown type $type";
+ }
+ my $length = $type_length{$type};
+ return $length;
+}
+
+sub decode_fixed_type
+{
+ my ($type, $data, $bytes) = @_;
+ my %decoders = (
+ 'byte' => \&unsigned_little_endian_to_value,
+ 'half' => \&unsigned_little_endian_to_value,
+ 'word' => \&unsigned_little_endian_to_value,
+ 'xword' => \&unsigned_little_endian_to_value,
+ 'fixed-string' => \&decode_fixed_string,
+ );
+ my $decoder = $decoders{$type} or die "unknow fixed type $type";
+ return $decoder->($data, $bytes);
+}
+
+sub decode_variable_type
+{
+ my ($type, $buf_ref, $offset_ref) = @_;
+ my %decoders = (
+ 'pstring' => \&decode_pstring,
+ 'cstring' => \&decode_cstring,
+ );
+ my $decoder = $decoders{$type} or die "unknow variable type $type";
+ return $decoder->($buf_ref, $offset_ref);
+}
+
+sub decode_struct
+{
+ my ($buf_ref, $offset, $layout) = @_;
+ my $initial_offset = $offset;
+ my ($entry, %results);
+ foreach $entry (@$layout) {
+ my ($type, $name) = @$entry;
+ my $bytes = type_size($entry);
+ if ($bytes > 0) {
+ my $data = substr(${$buf_ref}, $offset, $bytes);
+ $results{$name} = decode_fixed_type($type, $data, $bytes);
+ $offset += $bytes;
+ } else {
+ $results{$name} = decode_variable_type($type, $buf_ref, \$offset);
+ }
+ }
+ return (\%results, $offset - $initial_offset);
+}
+
+sub print_big_hex
+{
+ my ($min_digits, $value) = @_;
+ my @digits;
+ while($min_digits > 0 || ($value > 0)) {
+ my $digit = $value%16;
+ $value /= 16;
+ unshift(@digits, $digit);
+ $min_digits--;
+ }
+ my $digit;
+ foreach $digit (@digits) {
+ printf("%01x", $digit);
+ }
+}
+
+
+
+my %lha_signatures = (
+ '-com-' => 1,
+ '-lhd-' => 1,
+ '-lh0-' => 1,
+ '-lh1-' => 1,
+ '-lh2-' => 1,
+ '-lh3-' => 1,
+ '-lh4-' => 1,
+ '-lh5-' => 1,
+ '-lzs-' => 1,
+ '-lz4-' => 1,
+ '-lz5-' => 1,
+ '-afx-' => 1,
+ '-lzf-' => 1,
+);
+
+my %lha_os = (
+ 'M' => 'MS-DOS',
+ '2' => 'OS/2',
+ '9' => 'OS9',
+ 'K' => 'OS/68K',
+ '3' => 'OS/386',
+ 'H' => 'HUMAN',
+ 'U' => 'UNIX',
+ 'C' => 'CP/M',
+ 'F' => 'FLEX',
+ 'm' => 'Mac',
+ 'R' => 'Runser',
+ 'T' => 'TownOS',
+ 'X' => 'XOSK',
+ 'A' => 'Amiga',
+ 'a' => 'atari',
+ ' ' => 'Award ROM',
+);
+
+
+my @lha_level_1_header = (
+ [ 'byte', 'header_size' ], # 1
+ [ 'byte', 'header_sum', ], # 2
+ [ 'fixed-string', 'method_id', 5 ], # 7
+ [ 'word', 'skip_size', ], # 11
+ [ 'word', 'original_size' ], # 15
+ [ 'half', 'dos_time' ], # 17
+ [ 'half', 'dos_date' ], # 19
+ [ 'byte', 'fixed' ], # 20
+ [ 'byte', 'level' ], # 21
+ [ 'pstring', 'filename' ], # 22
+ [ 'half', 'crc' ],
+ [ 'fixed-string', 'os_id', 1 ],
+ [ 'half', 'ext_size' ],
+);
+
+# General lha_header
+my @lha_header = (
+ [ 'byte', 'header_size' ],
+ [ 'byte', 'header_sum', ],
+ [ 'fixed-string', 'method_id', 5 ],
+ [ 'word', 'skip_size', ],
+ [ 'word', 'original_size' ],
+ [ 'half', 'dos_time' ],
+ [ 'half', 'dos_date' ],
+ [ 'half', 'rom_addr' ],
+ [ 'half', 'rom_flags' ],
+ [ 'byte', 'fixed' ],
+ [ 'byte', 'level' ],
+ [ 'pstring', 'filename' ],
+ [ 'half', 'crc' ],
+ [ 'lha_os', 'os_id', 1 ],
+ [ 'half', 'ext_size' ],
+ [ 'byte', 'zero' ],
+ [ 'byte', 'total_checksum' ],
+ [ 'half', 'total_size' ],
+);
+
+sub print_struct
+{
+ my ($layout, $self) = @_;
+ my $entry;
+ my $width = 0;
+ foreach $entry(@$layout) {
+ my ($type, $name) = @$entry;
+ if (length($name) > $width) {
+ $width = length($name);
+ }
+ }
+ foreach $entry (@$layout) {
+ my ($type, $name) = @$entry;
+ printf("%*s = ", $width, $name);
+ my $value = $self->{$name};
+ if (!defined($value)) {
+ print "undefined";
+ }
+ elsif ($type eq "lha_os") {
+ print "$lha_os{$value}";
+ }
+ elsif ($type =~ m/string/) {
+ print "$value";
+ }
+ else {
+ my $len = type_size($entry);
+ print "0x";
+ print_big_hex($len *2, $value);
+ }
+ print "\n";
+ }
+}
+
+sub checksum
+{
+ my ($buf_ref, $offset, $length) = @_;
+ my ($i, $sum);
+ $sum = 0;
+ for($i = 0; $i < $length; $i++) {
+ my $byte = unpack('C', substr($$buf_ref, $offset + $i, 1));
+ $sum = ($sum + $byte) %256;
+ }
+ return $sum;
+}
+
+sub decode_lha_header
+{
+ my ($buf_ref, $offset) = @_;
+ my $level = unpack('C',substr(${$buf_ref}, $offset + 20, 1));
+
+ my %self;
+ my ($struct, $bytes);
+ if ($level == 1) {
+ ($struct, $bytes)
+ = decode_struct($buf_ref, $offset, \@lha_level_1_header);
+ %self = %$struct;
+ if ($self{fixed} != 0x20) {
+ die "bad fixed value";
+ }
+ $self{total_size} = $self{header_size} + 2 + $self{skip_size};
+ if ($bytes != $self{header_size} +2) {
+ die "$bytes != $self{header_size} +2";
+ }
+ my $checksum = checksum($buf_ref, $offset +2, $self{header_size});
+ if ($checksum != $self{header_sum}) {
+ printf("WARN: Header bytes checksum to %02lx\n",
+ $checksum);
+ }
+ # If we are an award rom...
+ if ($self{os_id} eq ' ') {
+ @self{qw(zero total_checksum)} =
+ unpack('CC', substr($$buf_ref,
+ $offset + $self{total_size}, 2));
+ if ($self{zero} != 0) {
+ warn "Award ROM without trailing zero";
+ }
+ else {
+ $self{total_size}++;
+ }
+ my $checksum =
+ checksum($buf_ref, $offset, $self{total_size});
+ if ($self{total_checksum} != $checksum) {
+ printf("WARN: Image bytes checksum to %02lx\n",
+ $checksum);
+ }
+ else {
+ $self{total_size}++;
+ }
+ $self{rom_addr} = $self{dos_time};
+ $self{rom_flags} = $self{dos_date};
+ delete @self{qw(dos_time dos_date)};
+ }
+ }
+ else {
+ die "Unknown header type";
+ }
+ return \%self;
+}
+
+sub main
+{
+ my ($filename, $rom_length) = @_;
+ my $fd = new FileHandle;
+ if (!defined($rom_length)) {
+ my ($dev, $ino, $mode, $nlink, $uid, $gid,$rdev,$size,
+ $atime, $mtime, $ctime, $blksize, $blocks)
+ = stat($filename);
+ $rom_length = $size;
+ }
+ $fd->open("<$filename") or die "Cannot ope $filename";
+ my $data;
+ $fd->read($data, $rom_length);
+ $fd->close();
+
+ my $i;
+ for($i = 0; $i < $rom_length; $i++) {
+ my $sig = substr($data, $i, 5);
+ if (exists($lha_signatures{$sig})) {
+ my $start = $i -2;
+ my $header = decode_lha_header(\$data, $start);
+
+ my $length = $header->{total_size};
+ print "AT: $start - @{[$start + $length -1]}, $length bytes\n";
+ print_struct(\@lha_header, $header);
+ print "\n";
+
+ }
+ }
+}
+
+main(@ARGV);
diff --git a/gpxe/contrib/baremetal/Makefile b/gpxe/contrib/baremetal/Makefile
new file mode 100644
index 00000000..df4de762
--- /dev/null
+++ b/gpxe/contrib/baremetal/Makefile
@@ -0,0 +1,475 @@
+#
+# Makefile for Etherboot
+#
+# Most of the time you should edit Config
+#
+# Common options:
+# VERSION=v - Set the version string
+#
+# NS8390 options:
+# -DINCLUDE_NE - Include NE1000/NE2000 support
+# -DNE_SCAN=list - Probe for NE base address using list of
+# comma separated hex addresses
+# -DINCLUDE_3C503 - Include 3c503 support
+# -DT503_SHMEM - Use 3c503 shared memory mode (off by default)
+# -DINCLUDE_WD - Include Western Digital/SMC support
+# -DWD_DEFAULT_MEM- Default memory location for WD/SMC cards
+# -DCOMPEX_RL2000_FIX
+#
+# If you have a Compex RL2000 PCI 32-bit (11F6:1401),
+# and the bootrom hangs in "Probing...[NE*000/PCI]",
+# try enabling this fix... it worked for me :).
+# In the first packet write somehow it somehow doesn't
+# get back the expected data so it is stuck in a loop.
+# I didn't bother to investigate what or why because it works
+# when I interrupt the loop if it takes more then COMPEX_RL2000_TRIES.
+# The code will notify if it does a abort.
+# SomniOne - somnione@gmx.net
+#
+# 3C509 option:
+# -DINCLUDE_3C509 - Include 3c509 support
+#
+# 3C90X options:
+# -DINCLUDE_3C90X - Include 3c90x support
+# -DCFG_3C90X_PRESERVE_XCVR - Reset the transceiver type to the value it
+# had initially just before the loaded code is started.
+# -DCFG_3C90X_XCVR - Hardcode the tranceiver type Etherboot uses.
+# -DCFG_3C90X_BOOTROM_FIX - If you have a 3c905B with buggy ROM
+# interface, setting this option might "fix" it. Use
+# with caution and read the docs in 3c90x.txt!
+#
+# See the documentation file 3c90x.txt for more details.
+#
+# CS89X0 (optional) options:
+# -DINCLUDE_CS89X0- Include CS89x0 support
+# -DCS_SCAN=list - Probe for CS89x0 base address using list of
+# comma separated hex addresses; increasing the
+# address by one (0x300 -> 0x301) will force a
+# more aggressive probing algorithm. This might
+# be neccessary after a soft-reset of the NIC.
+#
+# LANCE options:
+# -DINCLUDE_NE2100- Include NE2100 support
+# -DINCLUDE_NI6510- Include NI6510 support
+#
+# SK_G16 options:
+# -DINCLUDE_SK_G16- Include SK_G16 support
+#
+# I82586 options:
+# -DINCLUDE_3C507 - Include 3c507 support
+# -DINCLUDE_NI5210- Include NI5210 support
+# -DINCLUDE_EXOS205-Include EXOS205 support
+#
+# SMC9000 options:
+# -DINCLUDE_SMC9000 - Include SMC9000 driver
+# -DSMC9000_SCAN=list - List of I/O addresses to probe
+#
+# TIARA (Fujitsu Etherstar) options:
+# -DINCLUDE_TIARA - Include Tiara support
+#
+# NI5010 options:
+# -DINCLUDE_NI5010 - Include NI5010 support
+#
+# TULIP options:
+# -DINCLUDE_TULIP - Include Tulip support
+# -DUSE_INTERNAL_BUFFER - receuve and transmit buffers within program
+# space, not below 0x10000, in case that region is used
+#
+# RTL8139 options:
+# -DINCLUDE_RTL8139 - Include RTL8139 support
+# -DUSE_INTERNAL_BUFFER - 8 kB receive buffer within program space,
+# not at 0x10000 - 8kB, in case that region is used
+#
+
+include Config
+
+GCC= gcc
+CPP= gcc -E
+VERSION= 4.6.12
+CFLAGS16+= -DVERSION=\"$(VERSION)\" -DRELOC=$(RELOCADDR)
+CFLAGS32+= -DVERSION=\"$(VERSION)\" -DRELOC=$(RELOCADDR) $(OLDGAS)
+LCONFIG+= -DRELOC=$(RELOCADDR)
+
+IDENT16= 'Etherboot/16 $(VERSION) (GPL) $(@F)'
+IDENT32= 'Etherboot/32 $(VERSION) (GPL) $(@F)'
+
+# Find out if we're using binutils 2.9.1 which uses a different syntax in some
+# places (most prominently in the opcode prefix area).
+OLDGAS:= $(shell $(AS) --version | grep -q '2\.9\.1' && echo -DGAS291)
+
+# Check the requested type of build (32, 16 or both families)
+ifeq ($(ETHERBOOT),16)
+BUILD_LIBS= $(BLIB16)
+BUILD_BINS= $(BINS16)
+endif
+ifeq ($(ETHERBOOT),32)
+BUILD_LIBS= $(BLIB32)
+BUILD_BINS= $(BINS32)
+endif
+ifeq ($(ETHERBOOT),both)
+BUILD_LIBS= $(BLIB16) $(BLIB32)
+BUILD_BINS= $(BINS16) $(BINS32)
+endif
+
+3C503FLAGS= -DINCLUDE_3C503 # -DT503_SHMEM
+# Note that the suffix to MAKEROM_ is the (mixed case) basename of the ROM file
+MAKEROM_3c503= -3
+3C507FLAGS= -DINCLUDE_3C507
+3C509FLAGS= -DINCLUDE_3C509
+3C529FLAGS= -DINCLUDE_3C529
+3C595FLAGS= -DINCLUDE_3C595
+3C90XFLAGS= -DINCLUDE_3C90X
+CS89X0FLAGS= -DINCLUDE_CS89X0
+EEPROFLAGS= -DINCLUDE_EEPRO
+EEPRO100FLAGS= -DINCLUDE_EEPRO100
+EPIC100FLAGS= -DINCLUDE_EPIC100
+EXOS205FLAGS= -DINCLUDE_EXOS205
+LANCEFLAGS= -DINCLUDE_LANCE # Lance/PCI!
+NE2100FLAGS= -DINCLUDE_NE2100
+NEFLAGS= -DINCLUDE_NE -DNE_SCAN=0x300,0x280,0x320,0x340,0x380
+NS8390FLAGS= -DINCLUDE_NS8390 # NE2000/PCI!
+NI5010FLAGS= -DINCLUDE_NI5010
+NI5210FLAGS= -DINCLUDE_NI5210
+NI6510FLAGS= -DINCLUDE_NI6510
+RTL8139FLAGS= -DINCLUDE_RTL8139
+SK_G16FLAGS= -DINCLUDE_SK_G16
+SMC9000FLAGS= -DINCLUDE_SMC9000
+TIARAFLAGS= -DINCLUDE_TIARA
+DEPCAFLAGS= -DINCLUDE_DEPCA # -DDEPCA_MODEL=DEPCA -DDEPCA_RAM_BASE=0xd0000
+TULIPFLAGS= -DINCLUDE_TULIP
+OTULIPFLAGS= -DINCLUDE_OTULIP
+VIA_RHINEFLAGS= -DINCLUDE_VIA_RHINE
+WDFLAGS= -DINCLUDE_WD -DWD_DEFAULT_MEM=0xCC000
+W89C840FLAGS= -DINCLUDE_W89C840
+
+# If you have not made any changes to the *.S files, AS86 need not be set.
+# (most people)
+# If you have made changes to the *.S files and you want to rebuild *loader.bin
+# and {floppy,com}load.bin and you have as86 from the ELKS Dev86 package (not
+# the one that normally comes with Linux) (not most people)
+#AS86= as86
+# If you have made changes to the *.S files and you want to rebuild *loader.bin
+# and {floppy,com}load.bin and you have nasm (not most people)
+#AS86= nasm
+
+# if your as has trouble with the data32 directive, uncomment this
+# but note that the premade start*.o will be larger than necessary because it
+# contains some routines which may not be used
+#AS_PSEUDOS= n
+
+SRCS= floppyload.S comload.S liloprefix.S loader.S start16.S start32.S serial.S startmpcc.S
+SRCS+= main.c pci.c osloader.c nfs.c misc.c ansiesc.c bootmenu.c config.c
+SRCS+= md5.c floppy.c
+
+# ROM loaders: LZ version (prefix Z), PCI header version (prefix P)
+ifndef AS86
+RLOADER= rloader.bin.pre
+PRLOADER= prloader.bin.pre
+RZLOADER= rzloader.bin.pre
+PRZLOADER= przloader.bin.pre
+FLOPPYLOAD= floppyload.bin.pre
+COMLOAD= comload.bin.pre
+LILOPREFIX= liloprefix.bin.pre
+else
+RLOADER= bin/rloader.bin
+PRLOADER= bin/prloader.bin
+RZLOADER= bin/rzloader.bin
+PRZLOADER= bin/przloader.bin
+FLOPPYLOAD= bin/floppyload.bin
+COMLOAD= bin/comload.bin
+LILOPREFIX= bin/liloprefix.bin
+endif
+
+ifeq ($(AS86),as86)
+LCPPFLAGS+= -DUSE_AS86
+LASFLAGS+= $(AS86FLAGS) -0
+LASBINARY:= -b
+endif
+ifeq ($(AS86),nasm)
+LCPPFLAGS+= -DUSE_NASM
+LASFLAGS+= $(NASMFLAGS) -fbin
+LASBINARY:= -o
+endif
+
+ifeq ($(AS_PSEUDOS),n)
+START16= start16.o.pre
+START32= start32.o.pre
+else
+START16= bin16/start16.o
+START32= bin32/startmpcc.o
+endif
+
+BOBJS16= bin16/main.o bin16/osloader.o bin16/misc.o bin16/bootmenu.o
+BOBJS16+= bin16/floppy.o bin16/timer.o
+BOBJS32= bin32/main.o bin32/osloader.o bin32/nfs.o bin32/misc.o
+BOBJS32+= bin32/ansiesc.o bin32/bootmenu.o bin32/md5.o bin32/floppy.o
+BOBJS32+= bin32/serial.o bin32/timer.o
+BLIB16= bin16/bootlib.a
+BLIB32= bin32/bootlib.a
+LIBS16= $(BLIB16) $(LIBC16)
+LIBS32= $(BLIB32) $(LIBC32) /usr/lib/gcc-lib/i386-redhat-linux/2.96/libgcc.a
+UTIL_LZHUF:= $(shell if [ -d ../contrib/compressor ]; then echo bin/lzhuf; fi)
+UTILS+= bin/makerom $(UTIL_LZHUF) bin/organon
+STDDEPS16= $(START16) $(BLIB16) $(UTILS)
+STDDEPS32= $(START32) $(BLIB32) $(UTILS)
+MAKEDEPS= Makefile Config Roms
+
+CHECKSIZE= { read d1; read d1 d2 d3 size d4; [ $$size -gt $(ROMLIMIT) ] &&\
+ { $(RM) $@; echo "ERROR: code size exceeds limit!"; exit 1; }; exit 0; }
+
+# Make sure that the relocation address is acceptable for all ROM sizes.
+# Setting it to 0x98000 leaves about 29kB of space for the Etherboot program.
+# The check is done based running 'size' on the binary, not ROM size, but
+# roughly this means a ROM of 16kB or a partially used ROM of 32kB,
+# remembering to compressed ROM images into account.
+# You may also set RELOCADDR to 0x88000 to avoid using 0x98000
+# because of other drivers (e.g. Disk On Chip). In that case, you may
+# only load 512kB of OS, or load in memory above 1MB.
+# Don't forget to choose an assembler because the loaders have to be rebuilt.
+ifndef RELOCADDR
+RELOCADDR=0x98000
+#RELOCADDR=0xe0000
+endif
+
+# Evaluate ROMLIMIT only once - it is constant during the make run.
+# Note that the 3K safety margin below is for the 1K extended BIOS data area
+# and for the Etherboot runtime stack. Under normal situations, 2K of stack
+# are rarely needed. If you experience strange behaviour in functions that use
+# many local variables or that call functions that do, check for stack overrun!
+# Make sure that the normal case needs no perl interpreter - if someone uses a
+# different RELOCADDR, then he has perl installed anyways (the shell cannot
+# deal with hex numbers, as test/eval don't support non-decimal integers).
+ifeq ($(RELOCADDR),0x98000)
+ROMLIMIT=29696
+else
+ROMLIMIT:=$(shell perl -e 'print 0x10000 - 3072 - ($(RELOCADDR) & 0xFFFF), "\n";')
+endif
+
+# Start of targets
+
+all: $(UTILS) $(BUILD_LIBS) allbins
+
+include Roms
+
+# We need allbins because $(BINS16) and $(BINS32) are not defined until
+# the Makefile fragment "Roms" is read.
+
+allbins: $(BUILD_BINS)
+
+# Common files
+
+$(BLIB16): $(BOBJS16)
+ $(AR16) rv $@ $(BOBJS16)
+ $(RANLIB16) $@
+
+$(BLIB32): $(BOBJS32)
+ $(AR32) rv $@ $(BOBJS32)
+ $(RANLIB32) $@
+
+bin16/main.o: main.c etherboot.h osdep.h nic.h
+bin32/main.o: main.c etherboot.h osdep.h nic.h
+
+bin16/osloader.o: osloader.c etherboot.h osdep.h
+bin32/osloader.o: osloader.c etherboot.h osdep.h
+
+# NFS currently makes no sense for Etherboot/16
+bin32/nfs.o: nfs.c etherboot.h osdep.h nic.h
+
+bin16/misc.o: misc.c etherboot.h osdep.h
+bin32/misc.o: misc.c etherboot.h osdep.h
+
+# ANSIESC is not supported for Etherboot/16
+bin32/ansiesc.o: ansiesc.c etherboot.h osdep.h
+
+bin16/bootmenu.o: bootmenu.c etherboot.h osdep.h
+bin32/bootmenu.o: bootmenu.c etherboot.h osdep.h
+
+# Password support is not available for Etherboot/16
+bin32/md5.o: md5.c etherboot.h osdep.h
+
+bin16/floppy.o: floppy.c etherboot.h osdep.h
+bin32/floppy.o: floppy.c etherboot.h osdep.h
+
+bin16/timer.o: timer.c timer.h etherboot.h osdep.h
+bin32/timer.o: timer.c timer.h etherboot.h osdep.h
+
+bin32/inthw.o: inthw.c
+
+# PCI support code (common to all PCI drivers)
+
+bin32/pci.o: pci.c pci.h
+
+# Do not add driver specific dependencies here unless it's something the
+# genrules.pl script *can't* deal with, i.e. if it is not C code.
+
+# Prepended loaders
+
+#ifndef AS86
+#$(RLOADER) $(RZLOADER) $(PRLOADER) $(PRZLOADER): $(MAKEDEPS)
+# @if [ $(RELOCADDR) != 0x98000 ]; then echo Non-standard RELOCADDR, must assemble $@; exit 1; fi
+# $(TOUCH) $@
+#else
+#bin/rloader.s: loader.S $(MAKEDEPS)
+# $(CPP) $(LCPPFLAGS) $(LCONFIG) -o $@ $<
+#
+#bin/rzloader.s: loader.S $(MAKEDEPS)
+# $(CPP) $(LCPPFLAGS) $(LCONFIG) -DZLOADER -o $@ $<
+#
+#bin/prloader.s: loader.S $(MAKEDEPS)
+# $(CPP) $(LCPPFLAGS) $(LCONFIG) -DPCI_PNP_HEADER -o $@ $<
+#
+#bin/przloader.s: loader.S $(MAKEDEPS)
+# $(CPP) $(LCPPFLAGS) $(LCONFIG) -DPCI_PNP_HEADER -DZLOADER -o $@ $<
+#endif
+
+# Floppy loader
+
+ifdef AS86
+bin/floppyload.s: floppyload.S $(MAKEDEPS)
+ $(CPP) $(LCPPFLAGS) -o $@ $<
+endif
+
+# COM loader
+
+ifdef AS86
+bin/comload.s: comload.S $(MAKEDEPS)
+ $(CPP) $(LCPPFLAGS) -o $@ $<
+endif
+
+# LILO prefix:
+
+ifdef AS86
+bin/liloprefix.s: liloprefix.S $(MAKEDEPS)
+ $(CPP) $(LCPPFLAGS) -o $@ $<
+endif
+
+# Utilities
+
+bin/makerom: makerom.c
+ $(GCC) -O2 -o $@ makerom.c
+
+bin/organon: organon.c
+ $(GCC) -o $@ organon.c
+
+bin/lzhuf: ../contrib/compressor/lzhuf.c
+ $(GCC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -o $@ $<
+
+# Roms file
+
+Roms: NIC genrules.pl
+ @chmod +x genrules.pl
+ ./genrules.pl NIC > $@
+
+# Pattern Rules
+
+# general rules for compiling/assembling source files
+bin16/%.o: %.c $(MAKEDEPS)
+ $(CC16) $(CFLAGS16) -o $@ -c $<
+
+bin32/%.o: %.c $(MAKEDEPS)
+ $(CC32) $(CFLAGS32) -o $@ -c $<
+
+bin16/%.o: %.S $(MAKEDEPS)
+ $(CC16) $(CFLAGS16) $(ASFLAGS16) -c -o $@ $<
+
+bin32/%.o: %.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS32) $< | $(AS) $(ASFLAGS32) -o $@
+
+# general rule for .bin (plain binary loader code), may be overridden
+ifdef AS86
+bin/%.bin: bin/%.s
+ $(AS86) $(LASFLAGS) $(LASBINARY) $@ $<
+endif
+
+# general rule for .huf (compressed binary code), may be overridden
+%.huf: %.img
+ bin/lzhuf e $< $@
+
+# general rules for normal/compressed ROM images, may be overridden
+bin16/%.rom: bin16/%.img $(RLOADER)
+ cat $(RLOADER) $< > $@
+ bin/makerom $(MAKEROM_$*) -i$(IDENT16) $@
+
+bin32/%.rom: bin32/%.img $(RLOADER)
+ cat $(RLOADER) $< > $@
+ bin/makerom $(MAKEROM_$*) -i$(IDENT32) $@
+
+bin16/%.lzrom: bin16/%.huf $(RZLOADER)
+ cat $(RZLOADER) $< > $@
+ bin/makerom $(MAKEROM_$*) -i$(IDENT16) $@
+
+bin32/%.lzrom: bin32/%.huf $(RZLOADER)
+ cat $(RZLOADER) $< > $@
+ bin/makerom $(MAKEROM_$*) -i$(IDENT32) $@
+
+# rules to write the .rom/.lzrom image onto a blank floppy
+# You must give the directory name, e.g. use bin32/rtl8139.lzfd0 as the target.
+%.fd0: %.rom $(FLOPPYLOAD)
+ cat $(FLOPPYLOAD) $< > /dev/fd0
+
+%.lzfd0: %.lzrom $(FLOPPYLOAD)
+ cat $(FLOPPYLOAD) $< > /dev/fd0
+
+# rules to generate a .com executable
+# You must give the directory name, e.g. use bin32/rtl8139.com as the target.
+%.com: %.lzrom $(COMLOAD)
+ cat $(COMLOAD) $< > $@
+
+# rules to make a floppy image (padding to fill an even number of cylinders).
+# VMware reports floppy image read errors if it cannot read ahead 36 sectors,
+# probably because the floppyload.S code reads up to that number of sectors in
+# a single request. Not that 18k matters much these days...
+# You must give the directory name, e.g. use bin32/rtl8139.fdimg as the target.
+%.fdimg: %.rom $(FLOPPYLOAD)
+ cat $(FLOPPYLOAD) $< > $@.x
+ dd if=$@.x of=$@ bs=36k conv=sync 2> /dev/null
+ $(RM) $@.x
+
+%.lzfdimg: %.lzrom $(FLOPPYLOAD)
+ cat $(FLOPPYLOAD) $< > $@.x
+ dd if=$@.x of=$@ bs=36k conv=sync 2> /dev/null
+ $(RM) $@.x
+
+# rules to make a LILO-bootable image
+%.lilo: %.rom $(LILOPREFIX)
+ cat $(LILOPREFIX) $< /dev/zero | head -c 64k > $@
+
+%.lzlilo: %.lzrom $(LILOPREFIX)
+ cat $(LILOPREFIX) $< /dev/zero | head -c 64k > $@
+
+# Housekeeping
+
+# To make sure that this actually builds a start32.o.pre with all options set,
+# you have to make sure that -DFLOPPY -DANSIESC -DCONSOLE_DUAL are in CFLAGS32.
+precompiled: bin/rloader.bin bin/rzloader.bin bin/prloader.bin bin/przloader.bin bin/floppyload.bin bin/comload.bin bin16/start16.o bin32/start32.o bin/liloprefix.bin
+ cp -p bin/rloader.bin rloader.bin.pre
+ cp -p bin/rzloader.bin rzloader.bin.pre
+ cp -p bin/prloader.bin prloader.bin.pre
+ cp -p bin/przloader.bin przloader.bin.pre
+ cp -p bin/floppyload.bin floppyload.bin.pre
+ cp -p bin/comload.bin comload.bin.pre
+ cp -p bin16/start16.o start16.o.pre
+ cp -p bin32/start32.o start32.o.pre
+ cp -p bin/liloprefix.bin liloprefix.bin.pre
+
+clean:
+ $(RM) $(UTILS) bin/*.s bin/*.bin
+ $(RM) $(BLIB16) $(BLIB32)
+ $(RM) bin16/*.o bin32/*.o bin16/*.tmp bin32/*.tmp
+ $(RM) bin16/*.img bin32/*.img bin16/*.huf bin32/*.huf
+ $(RM) bin16/*.rom bin32/*.rom bin16/*.lzrom bin32/*.lzrom
+ $(RM) bin16/*.com bin32/*.com
+ $(RM) bin16/*.fdimg bin32/*.fdimg bin16/*.lzfdimg bin32/*.lzfdimg
+ $(RM) bin16/*.lilo bin32/*.lilo bin16/*.lzlilo bin32/*.lzlilo
+ $(RM) bin32/*.hex
+ $(RM) bin32/*.asm
+ $(RM) bin32/*.map
+
+tarball:
+ (echo -n $(VERSION) ''; date -u +'%Y-%m-%d') > ../VERSION
+ (cd ..; tar cf /tmp/mpccboot-$(VERSION).tar --exclude CVS mpccboot)
+ bzip2 -9 < /tmp/mpccboot-$(VERSION).tar > /tmp/mpccboot-$(VERSION).tar.bz2
+ gzip -9 < /tmp/mpccboot-$(VERSION).tar > /tmp/mpccboot-$(VERSION).tar.gz
+
+version:
+ @echo $(VERSION)
diff --git a/gpxe/contrib/baremetal/main.c b/gpxe/contrib/baremetal/main.c
new file mode 100644
index 00000000..7b0de44c
--- /dev/null
+++ b/gpxe/contrib/baremetal/main.c
@@ -0,0 +1,1119 @@
+/**************************************************************************
+ETHERBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: Dec/93
+
+Literature dealing with the network protocols:
+ ARP - RFC826
+ RARP - RFC903
+ UDP - RFC768
+ BOOTP - RFC951, RFC2132 (vendor extensions)
+ DHCP - RFC2131, RFC2132 (options)
+ TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize)
+ RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper)
+ NFS - RFC1094, RFC1813 (v3, useful for clarifications, not implemented)
+
+**************************************************************************/
+
+/* #define MDEBUG */
+
+#include "etherboot.h"
+#include "nic.h"
+
+int jmp_bootmenu[10];
+
+struct arptable_t arptable[MAX_ARP];
+
+const char *kernel;
+char kernel_buf[128];
+struct rom_info rom;
+
+#ifdef IMAGE_MENU
+static char *imagelist[RFC1533_VENDOR_NUMOFIMG];
+static int useimagemenu;
+int menutmo,menudefault;
+unsigned char *defparams = NULL;
+int defparams_max = 0;
+#endif
+#ifdef MOTD
+char *motd[RFC1533_VENDOR_NUMOFMOTD];
+#endif
+#ifdef IMAGE_FREEBSD
+int freebsd_howto = 0;
+#endif
+int vendorext_isvalid;
+char config_buffer[TFTP_MAX_PACKET+1]; /* +1 for null byte */
+unsigned long netmask;
+char *hostname = "";
+int hostnamelen = 0;
+#if defined(ETHERBOOT16) || defined(INTERNAL_BOOTP_DATA)
+struct bootpd_t bootp_data;
+#endif
+unsigned long xid;
+unsigned char *end_of_rfc1533 = NULL;
+#ifndef NO_DHCP_SUPPORT
+int dhcp_reply;
+in_addr dhcp_server = { 0L };
+in_addr dhcp_addr = { 0L };
+#endif /* NO_DHCP_SUPPORT */
+
+unsigned char vendorext_magic[] = {0xE4,0x45,0x74,0x68}; /* äEth */
+#ifdef NO_DHCP_SUPPORT
+char rfc1533_cookie[5] = { RFC1533_COOKIE, RFC1533_END };
+#else
+char rfc1533_cookie[] = { RFC1533_COOKIE};
+char rfc1533_end[]={RFC1533_END };
+static const char dhcpdiscover[]={
+ RFC2132_MSG_TYPE,1,DHCPDISCOVER,
+ RFC2132_MAX_SIZE,2, /* request as much as we can */
+ sizeof(struct bootpd_t) / 256, sizeof(struct bootpd_t) % 256,
+ RFC2132_PARAM_LIST,4,RFC1533_NETMASK,RFC1533_GATEWAY,
+ RFC1533_HOSTNAME
+ };
+static const char dhcprequest []={
+ RFC2132_MSG_TYPE,1,DHCPREQUEST,
+ RFC2132_SRV_ID,4,0,0,0,0,
+ RFC2132_REQ_ADDR,4,0,0,0,0,
+ RFC2132_MAX_SIZE,2, /* request as much as we can */
+ sizeof(struct bootpd_t) / 256, sizeof(struct bootpd_t) % 256,
+ /* request parameters */
+ RFC2132_PARAM_LIST,
+#ifdef IMAGE_FREEBSD
+ /* 4 standard + 6 vendortags + 8 motd + 16 menu items */
+ 4 + 6 + 8 + 16,
+#else
+ /* 4 standard + 5 vendortags + 8 motd + 16 menu items */
+ 4 + 5 + 8 + 16,
+#endif
+ /* Standard parameters */
+ RFC1533_NETMASK, RFC1533_GATEWAY,
+ RFC1533_HOSTNAME,
+ RFC1533_ROOTPATH, /* only passed to the booted image */
+ /* Etherboot vendortags */
+ RFC1533_VENDOR_MAGIC,
+ RFC1533_VENDOR_ADDPARM,
+ RFC1533_VENDOR_ETHDEV,
+#ifdef IMAGE_FREEBSD
+ RFC1533_VENDOR_HOWTO,
+#endif
+ RFC1533_VENDOR_MNUOPTS, RFC1533_VENDOR_SELECTION,
+ /* 8 MOTD entries */
+ RFC1533_VENDOR_MOTD,
+ RFC1533_VENDOR_MOTD+1,
+ RFC1533_VENDOR_MOTD+2,
+ RFC1533_VENDOR_MOTD+3,
+ RFC1533_VENDOR_MOTD+4,
+ RFC1533_VENDOR_MOTD+5,
+ RFC1533_VENDOR_MOTD+6,
+ RFC1533_VENDOR_MOTD+7,
+ /* 16 image entries */
+ RFC1533_VENDOR_IMG,
+ RFC1533_VENDOR_IMG+1,
+ RFC1533_VENDOR_IMG+2,
+ RFC1533_VENDOR_IMG+3,
+ RFC1533_VENDOR_IMG+4,
+ RFC1533_VENDOR_IMG+5,
+ RFC1533_VENDOR_IMG+6,
+ RFC1533_VENDOR_IMG+7,
+ RFC1533_VENDOR_IMG+8,
+ RFC1533_VENDOR_IMG+9,
+ RFC1533_VENDOR_IMG+10,
+ RFC1533_VENDOR_IMG+11,
+ RFC1533_VENDOR_IMG+12,
+ RFC1533_VENDOR_IMG+13,
+ RFC1533_VENDOR_IMG+14,
+ RFC1533_VENDOR_IMG+15,
+ };
+
+#endif /* NO_DHCP_SUPPORT */
+static const char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+/**************************************************************************
+MAIN - Kick off routine
+**************************************************************************/
+int main(void)
+{
+ char *p;
+ static int card_retries = 0;
+ int i;
+
+ for (p=_edata; p<_end; p++)
+ *p = 0; /* Zero BSS */
+
+#ifdef CONSOLE_SERIAL
+ (void)serial_init();
+#endif
+
+#ifdef DELIMITERLINES
+ for (i=0; i<80; i++) putchar('=');
+#endif
+
+#ifdef ETHERBOOT32
+ rom = *(struct rom_info *)ROM_INFO_LOCATION;
+ printf("ROM segment %#x length %#x reloc %#x\n", rom.rom_segment,
+ rom.rom_length << 1, ((unsigned long)_start) >> 4);
+#endif
+#ifdef ETHERBOOT16
+ fmemcpy(&rom, (Address)ROM_INFO_LOCATION, sizeof(rom));
+ printf("ROM segment %#x length %#x\n", rom.rom_segment,
+ rom.rom_length << 1);
+#endif
+#ifdef ASK_BOOT
+ while (1) {
+ int c;
+ unsigned long time;
+ printf(ASK_PROMPT);
+#if ASK_BOOT > 0
+ for (time = currticks() + ASK_BOOT*TICKS_PER_SEC; !iskey(); )
+ if (currticks() > time) {
+ c = ANS_DEFAULT;
+ goto done;
+ }
+#endif
+ c = getchar();
+ if ((c >= 'a') && (c <= 'z')) c &= 0x5F;
+ if (c == '\n') c = ANS_DEFAULT;
+done:
+ if ((c >= ' ') && (c <= '~')) putchar(c);
+ putchar('\n');
+ if (c == ANS_LOCAL)
+ exit(0);
+ if (c == ANS_NETWORK)
+ break;
+ }
+#endif
+#if (TRY_FLOPPY_FIRST > 0) && defined(FLOPPY)
+ disk_init();
+ printf("Trying floppy");
+ for (i = TRY_FLOPPY_FIRST; i-- > 0; ) {
+ putchar('.');
+ if (disk_read(0, 0, 0, 0, ((char *) FLOPPY_BOOT_LOCATION)) != 0x8000) {
+ printf("using floppy\n");
+ exit(0);
+ }
+ }
+ printf("no floppy\n");
+#endif /* TRY_FLOPPY_FIRST && FLOPPY */
+ print_config();
+ gateA20_set();
+#ifdef EMERGENCYDISKBOOT
+ if (!eth_probe()) {
+ printf("No adapter found\n");
+ exit(0);
+ }
+#else
+ while (!eth_probe()) {
+ printf("No adapter found");
+ if (!setjmp(jmp_bootmenu))
+ rfc951_sleep(++card_retries);
+ }
+#endif
+ kernel = DEFAULT_BOOTFILE;
+ while (1) {
+ if ((i = setjmp(jmp_bootmenu)) != 0) {
+#if defined(ANSIESC) && defined(CONSOLE_CRT)
+ ansi_reset();
+#endif
+ bootmenu(--i);
+ } else {
+ load();
+ }
+#if defined(ANSIESC) && defined(CONSOLE_CRT)
+ ansi_reset();
+#endif
+ }
+}
+
+/**************************************************************************
+LOADKERNEL - Try to load kernel image
+**************************************************************************/
+#ifndef FLOPPY
+#define loadkernel(s) download((s),downloadkernel)
+#else
+static int loadkernel(const char *fname)
+{
+ if (!memcmp(fname,"/dev/",5) && fname[6] == 'd') {
+ int dev, part = 0;
+ if (fname[5] == 'f') {
+ if ((dev = fname[7] - '0') < 0 || dev > 3)
+ goto nodisk; }
+ else if (fname[5] == 'h' || fname[5] == 's') {
+ if ((dev = 0x80 + fname[7] - 'a') < 0x80 || dev > 0x83)
+ goto nodisk;
+ if (fname[8]) {
+ part = fname[8] - '0';
+ if (fname[9])
+ part = 10*part + fname[9] - '0'; }
+ /* bootdisk cannot cope with more than eight partitions */
+ if (part < 0 || part > 8)
+ goto nodisk; }
+ else
+ goto nodisk;
+ return(bootdisk(dev,part)); }
+nodisk:
+ return download(fname, downloadkernel);
+}
+#endif
+
+/**************************************************************************
+LOAD - Try to get booted
+**************************************************************************/
+void load()
+{
+ static int bootp_completed = 0;
+
+ /* Find a server to get BOOTP reply from */
+ if (!bootp_completed ||
+ !arptable[ARP_CLIENT].ipaddr.s_addr || !arptable[ARP_SERVER].ipaddr.s_addr) {
+retry:
+ bootp_completed = 0;
+#ifdef RARP_NOT_BOOTP
+ printf("Searching for server (RARP)...\n");
+#else
+#ifndef NO_DHCP_SUPPORT
+ printf("Searching for server (DHCP)...\n");
+#else
+ printf("Searching for server (BOOTP)...\n");
+#endif
+#endif
+
+#ifdef RARP_NOT_BOOTP
+ if (!rarp()) {
+#else
+ if (!bootp()) {
+#endif
+ printf("No Server found\n");
+#ifdef EMERGENCYDISKBOOT
+ exit(0);
+#else
+ goto retry;
+#endif
+ }
+ bootp_completed++;
+ }
+ printf("Me: %I, Server: %I",
+ arptable[ARP_CLIENT].ipaddr.s_addr,
+ arptable[ARP_SERVER].ipaddr.s_addr);
+ if (BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr)
+ printf(", Relay: %I",
+ BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr);
+ if (arptable[ARP_GATEWAY].ipaddr.s_addr)
+ printf(", Gateway %I", arptable[ARP_GATEWAY].ipaddr.s_addr);
+ putchar('\n');
+
+#ifdef MDEBUG
+ printf("\n=>>"); getchar();
+#endif
+
+#ifdef MOTD
+ if (vendorext_isvalid)
+ show_motd();
+#endif
+ /* Now use TFTP to load file */
+#ifdef IMAGE_MENU
+ if (vendorext_isvalid && useimagemenu) {
+ selectImage(imagelist);
+ bootp_completed = 0;
+ }
+#endif
+#ifdef DOWNLOAD_PROTO_NFS
+ rpc_init();
+#endif
+ for (;;) {
+ printf("Loading %s ",kernel);
+ while (!loadkernel(kernel)) {
+ printf("Unable to load file.\n");
+ sleep(2); /* lay off server for a while */
+ }
+ }
+}
+
+/**************************************************************************
+DEFAULT_NETMASK - Return default netmask for IP address
+**************************************************************************/
+static inline unsigned long default_netmask(void)
+{
+ int net = ntohl(arptable[ARP_CLIENT].ipaddr.s_addr) >> 24;
+ if (net <= 127)
+ return(htonl(0xff000000));
+ else if (net < 192)
+ return(htonl(0xffff0000));
+ else
+ return(htonl(0xffffff00));
+}
+
+/**************************************************************************
+UDP_TRANSMIT - Send a UDP datagram
+**************************************************************************/
+int udp_transmit(unsigned long destip, unsigned int srcsock,
+ unsigned int destsock, int len, const void *buf)
+{
+ struct iphdr *ip;
+ struct udphdr *udp;
+ struct arprequest arpreq;
+ int arpentry, i;
+ int retry;
+
+ ip = (struct iphdr *)buf;
+ udp = (struct udphdr *)((long)buf + sizeof(struct iphdr));
+ ip->verhdrlen = 0x45;
+ ip->service = 0;
+ ip->len = htons(len);
+ ip->ident = 0;
+ ip->frags = 0;
+ ip->ttl = 60;
+ ip->protocol = IP_UDP;
+ ip->chksum = 0;
+ ip->src.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
+ ip->dest.s_addr = destip;
+ ip->chksum = ipchksum((unsigned short *)buf, sizeof(struct iphdr));
+ udp->src = htons(srcsock);
+ udp->dest = htons(destsock);
+ udp->len = htons(len - sizeof(struct iphdr));
+ udp->chksum = 0;
+ if (destip == IP_BROADCAST) {
+ eth_transmit(broadcast, IP, len, buf);
+ } else {
+ if (((destip & netmask) !=
+ (arptable[ARP_CLIENT].ipaddr.s_addr & netmask)) &&
+ arptable[ARP_GATEWAY].ipaddr.s_addr)
+ destip = arptable[ARP_GATEWAY].ipaddr.s_addr;
+ for(arpentry = 0; arpentry<MAX_ARP; arpentry++)
+ if (arptable[arpentry].ipaddr.s_addr == destip) break;
+ if (arpentry == MAX_ARP) {
+ printf("%I is not in my arp table!\n", destip);
+ return(0);
+ }
+ for (i = 0; i<ETHER_ADDR_SIZE; i++)
+ if (arptable[arpentry].node[i]) break;
+ if (i == ETHER_ADDR_SIZE) { /* Need to do arp request */
+ arpreq.hwtype = htons(1);
+ arpreq.protocol = htons(IP);
+ arpreq.hwlen = ETHER_ADDR_SIZE;
+ arpreq.protolen = 4;
+ arpreq.opcode = htons(ARP_REQUEST);
+ memcpy(arpreq.shwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+ memcpy(arpreq.sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
+ memset(arpreq.thwaddr, 0, ETHER_ADDR_SIZE);
+ memcpy(arpreq.tipaddr, &destip, sizeof(in_addr));
+ for (retry = 1; retry <= MAX_ARP_RETRIES; retry++) {
+ eth_transmit(broadcast, ARP, sizeof(arpreq),
+ &arpreq);
+ if (await_reply(AWAIT_ARP, arpentry,
+ arpreq.tipaddr, TIMEOUT)) goto xmit;
+ rfc951_sleep(retry);
+ /* We have slept for a while - the packet may
+ * have arrived by now. If not, we have at
+ * least some room in the Rx buffer for the
+ * next reply. */
+ if (await_reply(AWAIT_ARP, arpentry,
+ arpreq.tipaddr, 0)) goto xmit;
+ }
+ return(0);
+ }
+xmit:
+ eth_transmit(arptable[arpentry].node, IP, len, buf);
+ }
+ return(1);
+}
+
+/**************************************************************************
+DOWNLOADKERNEL - Try to load file
+**************************************************************************/
+int downloadkernel(data, block, len, eof)
+ unsigned char *data;
+ int block, len, eof;
+{
+#ifdef SIZEINDICATOR
+ static int rlen = 0;
+
+ if (!(block % 4) || eof) {
+ int size;
+ size = ((block-1) * rlen + len) / 1024;
+
+ putchar('\b');
+ putchar('\b');
+ putchar('\b');
+ putchar('\b');
+
+ putchar('0' + (size/1000)%10);
+ putchar('0' + (size/100)%10);
+ putchar('0' + (size/10)%10);
+ putchar('0' + (size/1)%10);
+ }
+#endif
+ if (block == 1)
+ {
+#ifdef SIZEINDICATOR
+ rlen=len;
+#endif
+ if (!eof && (
+#ifdef TAGGED_IMAGE
+ *((unsigned long *)data) == 0x1B031336L ||
+#endif
+#ifdef ELF_IMAGE
+ *((unsigned long *)data) == 0x464C457FL ||
+#endif
+#ifdef AOUT_IMAGE
+ *((unsigned short *)data) == 0x010BL ||
+#endif
+ ((unsigned short *)data)[255] == 0xAA55))
+ {
+ ;
+ }
+ else if (eof)
+ {
+ memcpy(config_buffer, data, len);
+ config_buffer[len] = 0;
+ return (1); /* done */
+ }
+ else
+ {
+ printf("error: not a tagged image\n");
+ return(0); /* error */
+ }
+ }
+ if (len != 0) {
+ if (!os_download(block, data, len))
+ return(0); /* error */
+ }
+ if (eof) {
+ os_download(block+1, data, 0); /* does not return */
+ return(0); /* error */
+ }
+ return(-1); /* there is more data */
+}
+
+#ifdef DOWNLOAD_PROTO_TFTP
+/**************************************************************************
+TFTP - Download extended BOOTP data, or kernel image
+**************************************************************************/
+int tftp(const char *name, int (*fnc)(unsigned char *, int, int, int))
+{
+ int retry = 0;
+ static unsigned short iport = 2000;
+ unsigned short oport;
+ unsigned short len, block = 0, prevblock = 0;
+ int bcounter = 0;
+ struct tftp_t *tr;
+ struct tftp_t tp;
+ int rc;
+ int packetsize = TFTP_DEFAULTSIZE_PACKET;
+
+ /* Clear out the Rx queue first. It contains nothing of interest,
+ * except possibly ARP requests from the DHCP/TFTP server. We use
+ * polling throughout Etherboot, so some time may have passed since we
+ * last polled the receive queue, which may now be filled with
+ * broadcast packets. This will cause the reply to the packets we are
+ * about to send to be lost immediately. Not very clever. */
+ await_reply(AWAIT_QDRAIN, 0, NULL, 0);
+
+ tp.opcode = htons(TFTP_RRQ);
+ len = (sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
+ name, 0, 0, 0, TFTP_MAX_PACKET) - ((char *)&tp)) + 1;
+ if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
+ TFTP_PORT, len, &tp))
+ return (0);
+ for (;;)
+ {
+#ifdef CONGESTED
+ if (!await_reply(AWAIT_TFTP, iport, NULL, (block ? TFTP_REXMT : TIMEOUT)))
+#else
+ if (!await_reply(AWAIT_TFTP, iport, NULL, TIMEOUT))
+#endif
+ {
+ if (!block && retry++ < MAX_TFTP_RETRIES)
+ { /* maybe initial request was lost */
+ rfc951_sleep(retry);
+ if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
+ ++iport, TFTP_PORT, len, &tp))
+ return (0);
+ continue;
+ }
+#ifdef CONGESTED
+ if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
+ { /* we resend our last ack */
+#ifdef MDEBUG
+ printf("<REXMT>\n");
+#endif
+ udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
+ iport, oport,
+ TFTP_MIN_PACKET, &tp);
+ continue;
+ }
+#endif
+ break; /* timeout */
+ }
+ tr = (struct tftp_t *)&nic.packet[ETHER_HDR_SIZE];
+ if (tr->opcode == ntohs(TFTP_ERROR))
+ {
+ printf("TFTP error %d (%s)\n",
+ ntohs(tr->u.err.errcode),
+ tr->u.err.errmsg);
+ break;
+ }
+
+ if (tr->opcode == ntohs(TFTP_OACK)) {
+ char *p = tr->u.oack.data, *e;
+
+ if (prevblock) /* shouldn't happen */
+ continue; /* ignore it */
+ len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
+ if (len > TFTP_MAX_PACKET)
+ goto noak;
+ e = p + len;
+ while (*p != '\000' && p < e) {
+ if (!strcasecmp("blksize", p)) {
+ p += 8;
+ if ((packetsize = getdec(&p)) <
+ TFTP_DEFAULTSIZE_PACKET)
+ goto noak;
+ while (p < e && *p) p++;
+ if (p < e)
+ p++;
+ }
+ else {
+ noak:
+ tp.opcode = htons(TFTP_ERROR);
+ tp.u.err.errcode = 8;
+ len = (sprintf((char *)tp.u.err.errmsg,
+ "RFC1782 error")
+ - ((char *)&tp)) + 1;
+ udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
+ iport, ntohs(tr->udp.src),
+ len, &tp);
+ return (0);
+ }
+ }
+ if (p > e)
+ goto noak;
+ block = tp.u.ack.block = 0; /* this ensures, that */
+ /* the packet does not get */
+ /* processed as data! */
+ }
+ else if (tr->opcode == ntohs(TFTP_DATA)) {
+ len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
+ if (len > packetsize) /* shouldn't happen */
+ continue; /* ignore it */
+ block = ntohs(tp.u.ack.block = tr->u.data.block); }
+ else /* neither TFTP_OACK nor TFTP_DATA */
+ break;
+
+ if ((block || bcounter) && (block != prevblock+1)) {
+ /* Block order should be continuous */
+ tp.u.ack.block = htons(block = prevblock);
+ }
+ tp.opcode = htons(TFTP_ACK);
+ oport = ntohs(tr->udp.src);
+ udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport,
+ oport, TFTP_MIN_PACKET, &tp); /* ack */
+ if ((unsigned short)(block-prevblock) != 1) {
+ /* Retransmission or OACK, don't process via callback
+ * and don't change the value of prevblock. */
+ continue;
+ }
+ prevblock = block;
+ retry = 0; /* It's the right place to zero the timer? */
+ if ((rc = fnc(tr->u.data.download,
+ ++bcounter, len, len < packetsize)) >= 0)
+ return(rc);
+ if (len < packetsize) /* End of data */
+ return (1);
+ }
+ return (0);
+}
+#endif /* DOWNLOAD_PROTO_TFTP */
+
+#ifdef RARP_NOT_BOOTP
+/**************************************************************************
+RARP - Get my IP address and load information
+**************************************************************************/
+int rarp()
+{
+ int retry;
+
+ /* arp and rarp requests share the same packet structure. */
+ struct arprequest rarpreq;
+
+ memset(&rarpreq, 0, sizeof(rarpreq));
+
+ rarpreq.hwtype = htons(1);
+ rarpreq.protocol = htons(IP);
+ rarpreq.hwlen = ETHER_ADDR_SIZE;
+ rarpreq.protolen = 4;
+ rarpreq.opcode = htons(RARP_REQUEST);
+ memcpy(&rarpreq.shwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+ /* sipaddr is already zeroed out */
+ memcpy(&rarpreq.thwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+ /* tipaddr is already zeroed out */
+
+ for (retry = 0; retry < MAX_ARP_RETRIES; rfc951_sleep(++retry)) {
+ eth_transmit(broadcast, RARP, sizeof(rarpreq), &rarpreq);
+
+ if (await_reply(AWAIT_RARP, 0, rarpreq.shwaddr, TIMEOUT))
+ break;
+ }
+
+ if (retry < MAX_ARP_RETRIES) {
+ sprintf(kernel = kernel_buf, "/tftpboot/kernel.%I", arptable[ARP_CLIENT].ipaddr);
+
+ return (1);
+ }
+ return (0);
+}
+
+#else
+
+/**************************************************************************
+BOOTP - Get my IP address and load information
+**************************************************************************/
+int bootp()
+{
+ int retry;
+#ifndef NO_DHCP_SUPPORT
+ int retry1;
+#endif /* NO_DHCP_SUPPORT */
+ struct bootp_t bp;
+ unsigned long starttime;
+#ifdef T509HACK
+ int flag;
+
+ flag = 1;
+#endif
+ memset(&bp, 0, sizeof(struct bootp_t));
+ bp.bp_op = BOOTP_REQUEST;
+ bp.bp_htype = 1;
+ bp.bp_hlen = ETHER_ADDR_SIZE;
+ bp.bp_xid = xid = starttime = currticks();
+ memcpy(bp.bp_hwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+#ifdef NO_DHCP_SUPPORT
+ memcpy(bp.bp_vend, rfc1533_cookie, 5); /* request RFC-style options */
+#else
+ memcpy(bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); /* request RFC-style options */
+ memcpy(bp.bp_vend+sizeof rfc1533_cookie, dhcpdiscover, sizeof dhcpdiscover);
+ memcpy(bp.bp_vend+sizeof rfc1533_cookie +sizeof dhcpdiscover, rfc1533_end, sizeof rfc1533_end);
+#endif /* NO_DHCP_SUPPORT */
+
+ for (retry = 0; retry < MAX_BOOTP_RETRIES; ) {
+
+ /* Clear out the Rx queue first. It contains nothing of
+ * interest, except possibly ARP requests from the DHCP/TFTP
+ * server. We use polling throughout Etherboot, so some time
+ * may have passed since we last polled the receive queue,
+ * which may now be filled with broadcast packets. This will
+ * cause the reply to the packets we are about to send to be
+ * lost immediately. Not very clever. */
+ await_reply(AWAIT_QDRAIN, 0, NULL, 0);
+
+ udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER,
+ sizeof(struct bootp_t), &bp);
+#ifdef T509HACK
+ if (flag) {
+ flag--;
+ } else {
+ if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT))
+ return(1);
+ rfc951_sleep(++retry);
+
+ }
+#else
+#ifdef NO_DHCP_SUPPORT
+ if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT))
+#else
+ if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT)){
+ if (dhcp_reply==DHCPOFFER){
+ dhcp_reply=0;
+ memcpy(bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie);
+ memcpy(bp.bp_vend+sizeof rfc1533_cookie, dhcprequest, sizeof dhcprequest);
+ memcpy(bp.bp_vend+sizeof rfc1533_cookie +sizeof dhcprequest, rfc1533_end, sizeof rfc1533_end);
+ memcpy(bp.bp_vend+9, &dhcp_server, sizeof(in_addr));
+ memcpy(bp.bp_vend+15, &dhcp_addr, sizeof(in_addr));
+ for (retry1 = 0; retry1 < MAX_BOOTP_RETRIES;) {
+ udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER,
+ sizeof(struct bootp_t), &bp);
+ dhcp_reply=0;
+ if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT))
+ if (dhcp_reply==DHCPACK)
+ return(1);
+ rfc951_sleep(++retry1);
+ }
+ } else
+#endif /* NO_DHCP_SUPPORT */
+ return(1);
+#ifndef NO_DHCP_SUPPORT
+ }
+ rfc951_sleep(++retry);
+
+#endif /* NO_DHCP_SUPPORT */
+#endif
+ bp.bp_secs = htons((currticks()-starttime)/20);
+ }
+ return(0);
+}
+#endif /* RARP_NOT_BOOTP */
+
+/**************************************************************************
+AWAIT_REPLY - Wait until we get a response for our request
+**************************************************************************/
+int await_reply(int type, int ival, void *ptr, int timeout)
+{
+ unsigned long time;
+ struct iphdr *ip;
+ struct udphdr *udp;
+ struct arprequest *arpreply;
+ struct bootp_t *bootpreply;
+ struct rpc_t *rpc;
+ unsigned short ptype;
+
+ unsigned int protohdrlen = ETHER_HDR_SIZE + sizeof(struct iphdr) +
+ sizeof(struct udphdr);
+ time = timeout + currticks();
+ /* The timeout check is done below. The timeout is only checked if
+ * there is no packet in the Rx queue. This assumes that eth_poll()
+ * needs a negligible amount of time. */
+ for (;;) {
+ if (eth_poll()) { /* We have something! */
+ /* Check for ARP - No IP hdr */
+ if (nic.packetlen >= ETHER_HDR_SIZE) {
+ ptype = ((unsigned short) nic.packet[12]) << 8
+ | ((unsigned short) nic.packet[13]);
+ } else continue; /* what else could we do with it? */
+ if ((nic.packetlen >= ETHER_HDR_SIZE +
+ sizeof(struct arprequest)) &&
+ (ptype == ARP) ) {
+ unsigned long tmp;
+
+ arpreply = (struct arprequest *)
+ &nic.packet[ETHER_HDR_SIZE];
+ if ((arpreply->opcode == ntohs(ARP_REPLY)) &&
+ !memcmp(arpreply->sipaddr, ptr, sizeof(in_addr)) &&
+ (type == AWAIT_ARP)) {
+ memcpy(arptable[ival].node, arpreply->shwaddr, ETHER_ADDR_SIZE);
+ return(1);
+ }
+ memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
+ if ((arpreply->opcode == ntohs(ARP_REQUEST)) &&
+ (tmp == arptable[ARP_CLIENT].ipaddr.s_addr)) {
+ arpreply->opcode = htons(ARP_REPLY);
+ memcpy(arpreply->tipaddr, arpreply->sipaddr, sizeof(in_addr));
+ memcpy(arpreply->thwaddr, arpreply->shwaddr, ETHER_ADDR_SIZE);
+ memcpy(arpreply->sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
+ memcpy(arpreply->shwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+ eth_transmit(arpreply->thwaddr, ARP,
+ sizeof(struct arprequest),
+ arpreply);
+#ifdef MDEBUG
+ memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
+ printf("Sent ARP reply to: %I\n",tmp);
+#endif MDEBUG
+ }
+ continue;
+ }
+
+ if (type == AWAIT_QDRAIN) {
+ continue;
+ }
+
+ /* Check for RARP - No IP hdr */
+ if ((type == AWAIT_RARP) &&
+ (nic.packetlen >= ETHER_HDR_SIZE +
+ sizeof(struct arprequest)) &&
+ (ptype == RARP)) {
+ arpreply = (struct arprequest *)
+ &nic.packet[ETHER_HDR_SIZE];
+ if ((arpreply->opcode == ntohs(RARP_REPLY)) &&
+ !memcmp(arpreply->thwaddr, ptr, ETHER_ADDR_SIZE)) {
+ memcpy(arptable[ARP_SERVER].node, arpreply->shwaddr, ETHER_ADDR_SIZE);
+ memcpy(& arptable[ARP_SERVER].ipaddr, arpreply->sipaddr, sizeof(in_addr));
+ memcpy(& arptable[ARP_CLIENT].ipaddr, arpreply->tipaddr, sizeof(in_addr));
+ return(1);
+ }
+ continue;
+ }
+
+ /* Anything else has IP header */
+ if ((nic.packetlen < protohdrlen) ||
+ (ptype != IP) ) continue;
+ ip = (struct iphdr *)&nic.packet[ETHER_HDR_SIZE];
+ if ((ip->verhdrlen != 0x45) ||
+ ipchksum((unsigned short *)ip, sizeof(struct iphdr)) ||
+ (ip->protocol != IP_UDP)) continue;
+ udp = (struct udphdr *)&nic.packet[ETHER_HDR_SIZE +
+ sizeof(struct iphdr)];
+
+ /* BOOTP ? */
+ bootpreply = (struct bootp_t *)&nic.packet[ETHER_HDR_SIZE];
+ if ((type == AWAIT_BOOTP) &&
+ (nic.packetlen >= (ETHER_HDR_SIZE +
+#ifdef NO_DHCP_SUPPORT
+ sizeof(struct bootp_t))) &&
+#else
+ sizeof(struct bootp_t))-DHCP_OPT_LEN) &&
+#endif /* NO_DHCP_SUPPORT */
+ (ntohs(udp->dest) == BOOTP_CLIENT) &&
+ (bootpreply->bp_op == BOOTP_REPLY) &&
+ (bootpreply->bp_xid == xid)) {
+ arptable[ARP_CLIENT].ipaddr.s_addr =
+ bootpreply->bp_yiaddr.s_addr;
+#ifndef NO_DHCP_SUPPORT
+ dhcp_addr.s_addr = bootpreply->bp_yiaddr.s_addr;
+#endif /* NO_DHCP_SUPPORT */
+ netmask = default_netmask();
+ arptable[ARP_SERVER].ipaddr.s_addr =
+ bootpreply->bp_siaddr.s_addr;
+ memset(arptable[ARP_SERVER].node, 0, ETHER_ADDR_SIZE); /* Kill arp */
+ arptable[ARP_GATEWAY].ipaddr.s_addr =
+ bootpreply->bp_giaddr.s_addr;
+ memset(arptable[ARP_GATEWAY].node, 0, ETHER_ADDR_SIZE); /* Kill arp */
+ if (bootpreply->bp_file[0]) {
+ memcpy(kernel_buf, bootpreply->bp_file, 128);
+ kernel = kernel_buf;
+ }
+ memcpy((char *)BOOTP_DATA_ADDR, (char *)bootpreply, sizeof(struct bootpd_t));
+ decode_rfc1533(BOOTP_DATA_ADDR->bootp_reply.bp_vend,
+#ifdef NO_DHCP_SUPPORT
+ 0, BOOTP_VENDOR_LEN + MAX_BOOTP_EXTLEN, 1);
+#else
+ 0, DHCP_OPT_LEN + MAX_BOOTP_EXTLEN, 1);
+#endif /* NO_DHCP_SUPPORT */
+ return(1);
+ }
+
+#ifdef DOWNLOAD_PROTO_TFTP
+ /* TFTP ? */
+ if ((type == AWAIT_TFTP) &&
+ (ntohs(udp->dest) == ival)) return(1);
+#endif /* DOWNLOAD_PROTO_TFTP */
+
+#ifdef DOWNLOAD_PROTO_NFS
+ /* RPC ? */
+ rpc = (struct rpc_t *)&nic.packet[ETHER_HDR_SIZE];
+ if ((type == AWAIT_RPC) &&
+ (ntohs(udp->dest) == ival) &&
+ (*(unsigned long *)ptr == ntohl(rpc->u.reply.id)) &&
+ (ntohl(rpc->u.reply.type) == MSG_REPLY)) {
+ return (1);
+ }
+#endif /* DOWNLOAD_PROTO_NFS */
+
+ } else {
+ /* Check for abort key only if the Rx queue is empty -
+ * as long as we have something to process, don't
+ * assume that something failed. It is unlikely that
+ * we have no processing time left between packets. */
+ if (iskey() && (getchar() == ESC))
+#ifdef EMERGENCYDISKBOOT
+ exit(0);
+#else
+ longjmp(jmp_bootmenu,1);
+#endif
+ /* Do the timeout after at least a full queue walk. */
+ if ((timeout == 0) || (currticks() > time)) {
+ break;
+ }
+ }
+ }
+ return(0);
+}
+
+/**************************************************************************
+DECODE_RFC1533 - Decodes RFC1533 header
+**************************************************************************/
+int decode_rfc1533(p, block, len, eof)
+ register unsigned char *p;
+ int block, len, eof;
+{
+ static unsigned char *extdata = NULL, *extend = NULL;
+ unsigned char *extpath = NULL;
+ unsigned char *endp;
+
+ if (block == 0) {
+#ifdef IMAGE_MENU
+ memset(imagelist, 0, sizeof(imagelist));
+ menudefault = useimagemenu = 0;
+ menutmo = -1;
+#endif
+#ifdef MOTD
+ memset(motd, 0, sizeof(motd));
+#endif
+ end_of_rfc1533 = NULL;
+ vendorext_isvalid = 0;
+ if (memcmp(p, rfc1533_cookie, 4))
+ return(0); /* no RFC 1533 header found */
+ p += 4;
+ endp = p + len; }
+ else {
+ if (block == 1) {
+ if (memcmp(p, rfc1533_cookie, 4))
+ return(0); /* no RFC 1533 header found */
+ p += 4;
+ len -= 4; }
+ if (extend + len <= (unsigned char *)&(BOOTP_DATA_ADDR->bootp_extension[MAX_BOOTP_EXTLEN])) {
+ memcpy(extend, p, len);
+ extend += len;
+ } else {
+ printf("Overflow in vendor data buffer! Aborting...\n");
+ *extdata = RFC1533_END;
+ return(0);
+ }
+ p = extdata; endp = extend;
+ }
+ if (eof) {
+ while(p < endp) {
+ unsigned char c = *p;
+ if (c == RFC1533_PAD) {p++; continue;}
+ else if (c == RFC1533_END) {
+ end_of_rfc1533 = endp = p; continue; }
+ else if (c == RFC1533_NETMASK) {memcpy(&netmask, p+2, sizeof(in_addr));}
+
+ else if (c == RFC1533_GATEWAY) {
+ /* This is a little simplistic, but it will
+ usually be sufficient.
+ Take only the first entry */
+ if (TAG_LEN(p) >= sizeof(in_addr))
+ memcpy(&arptable[ARP_GATEWAY].ipaddr, p+2, sizeof(in_addr));
+ }
+ else if (c == RFC1533_EXTENSIONPATH)
+ extpath = p;
+#ifndef NO_DHCP_SUPPORT
+ else if (c == RFC2132_MSG_TYPE)
+ { dhcp_reply=*(p+2);
+ }
+ else if (c == RFC2132_SRV_ID)
+ {
+ memcpy(&dhcp_server, p+2, sizeof(in_addr));
+ }
+#endif /* NO_DHCP_SUPPORT */
+ else if (c == RFC1533_HOSTNAME)
+ {
+ hostname = p + 2;
+ hostnamelen = *(p + 1);
+ }
+ else if (c == RFC1533_VENDOR_MAGIC
+#ifndef IMAGE_FREEBSD /* since FreeBSD uses tag 128 for swap definition */
+ && TAG_LEN(p) >= 6 &&
+ !memcmp(p+2,vendorext_magic,4) &&
+ p[6] == RFC1533_VENDOR_MAJOR
+#endif
+ )
+ vendorext_isvalid++;
+#ifdef IMAGE_FREEBSD
+ else if (c == RFC1533_VENDOR_HOWTO) {
+ freebsd_howto = ((p[2]*256+p[3])*256+p[4])*256+p[5];
+ }
+#endif
+#ifdef IMAGE_MENU
+ else if (c == RFC1533_VENDOR_MNUOPTS) {
+ parse_menuopts(p+2, TAG_LEN(p));
+ }
+ else if (c >= RFC1533_VENDOR_IMG &&
+ c<RFC1533_VENDOR_IMG+RFC1533_VENDOR_NUMOFIMG){
+ imagelist[c - RFC1533_VENDOR_IMG] = p;
+ useimagemenu++;
+ }
+#endif
+#ifdef MOTD
+ else if (c >= RFC1533_VENDOR_MOTD &&
+ c < RFC1533_VENDOR_MOTD +
+ RFC1533_VENDOR_NUMOFMOTD)
+ motd[c - RFC1533_VENDOR_MOTD] = p;
+#endif
+ else {
+#if 0
+ unsigned char *q;
+ printf("Unknown RFC1533-tag ");
+ for(q=p;q<p+2+TAG_LEN(p);q++)
+ printf("%x ",*q);
+ putchar('\n');
+#endif
+ }
+ p += TAG_LEN(p) + 2;
+ }
+ extdata = extend = endp;
+ if (block == 0 && extpath != NULL) {
+ char fname[64];
+ memcpy(fname, extpath+2, TAG_LEN(extpath));
+ fname[(int)TAG_LEN(extpath)] = '\000';
+ printf("Loading BOOTP-extension file: %s\n",fname);
+ download(fname,decode_rfc1533);
+ }
+ }
+ return(-1); /* proceed with next block */
+}
+
+/**************************************************************************
+IPCHKSUM - Checksum IP Header
+**************************************************************************/
+unsigned short ipchksum(ip, len)
+ register unsigned short *ip;
+ register int len;
+{
+ unsigned long sum = 0;
+ len >>= 1;
+ while (len--) {
+ sum += *(ip++);
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+ return((~sum) & 0x0000FFFF);
+}
+
+/**************************************************************************
+RFC951_SLEEP - sleep for expotentially longer times
+**************************************************************************/
+void rfc951_sleep(exp)
+ int exp;
+{
+ static long seed = 0;
+ long q;
+ unsigned long tmo;
+
+#ifdef BACKOFF_LIMIT
+ if (exp > BACKOFF_LIMIT)
+ exp = BACKOFF_LIMIT;
+#endif
+ if (!seed) /* Initialize linear congruential generator */
+ seed = currticks() + *(long *)&arptable[ARP_CLIENT].node
+ + ((short *)arptable[ARP_CLIENT].node)[2];
+ /* simplified version of the LCG given in Bruce Scheier's
+ "Applied Cryptography" */
+ q = seed/53668;
+ if ((seed = 40014*(seed-53668*q) - 12211*q) < 0) seed += 2147483563l;
+ /* compute mask */
+ for (tmo = 63; tmo <= 60*TICKS_PER_SEC && --exp > 0; tmo = 2*tmo+1);
+ /* sleep */
+ printf("<sleep>\n");
+
+ for (tmo = (tmo&seed)+currticks(); currticks() < tmo; )
+ if (iskey() && (getchar() == ESC)) longjmp(jmp_bootmenu,1);
+ return;
+}
+
+/**************************************************************************
+CLEANUP_NET - shut down networking
+**************************************************************************/
+void cleanup_net(void)
+{
+#ifdef DOWNLOAD_PROTO_NFS
+ nfs_umountall(ARP_SERVER);
+#endif
+ eth_disable();
+ eth_reset();
+}
+
+/**************************************************************************
+CLEANUP - shut down etherboot so that the OS may be called right away
+**************************************************************************/
+void cleanup(void)
+{
+#if defined(ANSIESC) && defined(CONSOLE_CRT)
+ ansi_reset();
+#endif
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/gpxe/contrib/baremetal/marini.txt b/gpxe/contrib/baremetal/marini.txt
new file mode 100644
index 00000000..464f1488
--- /dev/null
+++ b/gpxe/contrib/baremetal/marini.txt
@@ -0,0 +1,52 @@
+From: "Paolo Marini" <paolom@prisma-eng.it>
+Subject: Etherboot on bare metal
+Date: Tue, 10 Apr 2001 23:19:19 +0200
+Organization: Prisma Engineering srl
+
+Hi Ken,
+I have ported Etherboot on an embedded, biosless platform and would like
+to contribute the code.
+
+Essentially, the hardware I was running Etherboot is a Pentium based
+embedded system, with an Intel Chipset, *but* without serial, VGA,
+keyboard etc., only an 82559 Intel (custom) Ethernet controller (I debug
+it with the etheral Ethernet packet analyser and an emulator).
+
+What I did was:
+
+ a.. integrate the init.s file within the firmware, with GDT
+(re)initialisation (a simple and single entry point taking control of
+the boot process)
+ b.. provide some stupid BIOS stubs in order to let the OS boot and
+still belive that an INT10 call goes to the BIOS
+ c.. provide some basic functions to Etherboot, like timer (I used the
+Pentium TSC internal counter)
+ d.. hardwire in the code information about the RAM size
+The BIOS stubs are enough to boot Linux, pSOS and QNX with bootp. QNX is
+somewhat difficult to load, because the i82559 driver tries to find the
+component using the BIOS32 calls, so I had to patch it.
+
+what i I got from the original firmware is the PCI initialisation and
+resource (I/O, interrupts, memory) allocation.
+
+I send you what I changed, that is, the initialisation code and the
+misc.c file containing the timer, and the makefile (I don't remember
+exactly the options I used to compile all).
+
+Of course, it is only a good starting point for anyone wanting to
+implement a bootp client on a biosless platform; some integration work
+still needs to be done.
+
+Ciao
+Paolo
+
+And in a subsequent email:
+
+I worked with version 4.6.12, but the real modifications involve the
+init.S file, which I think is quite sstable between releases. I forgot
+to say that my entry point (symbol _start in init.s) assumes the
+processor is already in protected mode.
+
+[The only difference between main.c and misc.c from those in Etherboot
+4.6.12 seems to be the deletion of eth_reset(). This may be of use to
+others trying to make these changes work on more recent releases. Ken]
diff --git a/gpxe/contrib/baremetal/misc.c b/gpxe/contrib/baremetal/misc.c
new file mode 100644
index 00000000..924ccd6d
--- /dev/null
+++ b/gpxe/contrib/baremetal/misc.c
@@ -0,0 +1,351 @@
+/**************************************************************************
+MISC Support Routines
+**************************************************************************/
+
+#include "etherboot.h"
+
+/**************************************************************************
+SLEEP
+**************************************************************************/
+void sleep(int secs)
+{
+ unsigned long tmo;
+
+ for (tmo = currticks()+secs*TICKS_PER_SEC; currticks() < tmo; )
+ /* Nothing */;
+}
+
+/**************************************************************************
+TWIDDLE
+**************************************************************************/
+void twiddle()
+{
+ static unsigned long lastticks = 0;
+ static int count=0;
+ static const char tiddles[]="-\\|/";
+ unsigned long ticks;
+ if ((ticks = currticks()) == lastticks)
+ return;
+ lastticks = ticks;
+ putchar(tiddles[(count++)&3]);
+ putchar('\b');
+}
+
+/**************************************************************************
+STRCASECMP (not entirely correct, but this will do for our purposes)
+**************************************************************************/
+int strcasecmp(a,b)
+ char *a, *b;
+{
+ while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; }
+ return((*a & ~0x20) - (*b & ~0x20));
+}
+
+/**************************************************************************
+PRINTF and friends
+
+ Formats:
+ %[#]X - 4 bytes long (8 hex digits)
+ %[#]x - 2 bytes int (4 hex digits)
+ - optional # prefixes 0x
+ %b - 1 byte int (2 hex digits)
+ %d - decimal int
+ %c - char
+ %s - string
+ %I - Internet address in x.x.x.x notation
+ Note: width specification not supported
+**************************************************************************/
+static char *do_printf(char *buf, const char *fmt, const int *dp)
+{
+ register char *p;
+ int alt;
+ char tmp[16];
+ static const char hex[]="0123456789ABCDEF";
+
+ while (*fmt) {
+ if (*fmt == '%') { /* switch() uses more space */
+ alt = 0;
+ fmt++;
+ if (*fmt == '#') {
+ alt = 1;
+ fmt++;
+ }
+ if (*fmt == 'X') {
+ const long *lp = (const long *)dp;
+ register long h = *lp++;
+ dp = (const int *)lp;
+ if (alt) {
+ *buf++ = '0';
+ *buf++ = 'x';
+ }
+ *(buf++) = hex[(h>>28)& 0x0F];
+ *(buf++) = hex[(h>>24)& 0x0F];
+ *(buf++) = hex[(h>>20)& 0x0F];
+ *(buf++) = hex[(h>>16)& 0x0F];
+ *(buf++) = hex[(h>>12)& 0x0F];
+ *(buf++) = hex[(h>>8)& 0x0F];
+ *(buf++) = hex[(h>>4)& 0x0F];
+ *(buf++) = hex[h& 0x0F];
+ }
+ if (*fmt == 'x') {
+ register int h = *(dp++);
+ if (alt) {
+ *buf++ = '0';
+ *buf++ = 'x';
+ }
+ *(buf++) = hex[(h>>12)& 0x0F];
+ *(buf++) = hex[(h>>8)& 0x0F];
+ *(buf++) = hex[(h>>4)& 0x0F];
+ *(buf++) = hex[h& 0x0F];
+ }
+ if (*fmt == 'b') {
+ register int h = *(dp++);
+ *(buf++) = hex[(h>>4)& 0x0F];
+ *(buf++) = hex[h& 0x0F];
+ }
+ if (*fmt == 'd') {
+ register int dec = *(dp++);
+ p = tmp;
+ if (dec < 0) {
+ *(buf++) = '-';
+ dec = -dec;
+ }
+ do {
+ *(p++) = '0' + (dec%10);
+ dec = dec/10;
+ } while(dec);
+ while ((--p) >= tmp) *(buf++) = *p;
+ }
+ if (*fmt == 'I') {
+ union {
+ long l;
+ unsigned char c[4];
+ } u;
+ const long *lp = (const long *)dp;
+ u.l = *lp++;
+ dp = (const int *)lp;
+ buf = sprintf(buf,"%d.%d.%d.%d",
+ u.c[0], u.c[1], u.c[2], u.c[3]);
+ }
+ if (*fmt == 'c')
+ *(buf++) = *(dp++);
+ if (*fmt == 's') {
+ p = (char *)*dp++;
+ while (*p) *(buf++) = *p++;
+ }
+ } else *(buf++) = *fmt;
+ fmt++;
+ }
+ *buf = '\0';
+ return(buf);
+}
+
+char *sprintf(char *buf, const char *fmt, ...)
+{
+ return do_printf(buf, fmt, ((const int *)&fmt)+1);
+}
+
+void printf(const char *fmt, ...)
+{
+ char buf[120], *p;
+
+ p = buf;
+ do_printf(buf, fmt, ((const int *)&fmt)+1);
+ while (*p) putchar(*p++);
+}
+
+#ifdef IMAGE_MENU
+/**************************************************************************
+INET_ATON - Convert an ascii x.x.x.x to binary form
+**************************************************************************/
+int inet_aton(char *p, in_addr *i)
+{
+ unsigned long ip = 0;
+ int val;
+ if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
+ if (*p != '.') return(0);
+ p++;
+ ip = val;
+ if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
+ if (*p != '.') return(0);
+ p++;
+ ip = (ip << 8) | val;
+ if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
+ if (*p != '.') return(0);
+ p++;
+ ip = (ip << 8) | val;
+ if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
+ i->s_addr = htonl((ip << 8) | val);
+ return(1);
+}
+
+#endif /* IMAGE_MENU */
+
+int getdec(char **ptr)
+{
+ char *p = *ptr;
+ int ret=0;
+ if ((*p < '0') || (*p > '9')) return(-1);
+ while ((*p >= '0') && (*p <= '9')) {
+ ret = ret*10 + (*p - '0');
+ p++;
+ }
+ *ptr = p;
+ return(ret);
+}
+
+#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
+#define K_STATUS 0x64 /* keyboard status */
+#define K_CMD 0x64 /* keybd ctlr command (write-only) */
+
+#define K_OBUF_FUL 0x01 /* output buffer full */
+#define K_IBUF_FUL 0x02 /* input buffer full */
+
+#define KC_CMD_WIN 0xd0 /* read output port */
+#define KC_CMD_WOUT 0xd1 /* write output port */
+#define KB_SET_A20 0xdf /* enable A20,
+ enable output buffer full interrupt
+ enable data line
+ disable clock line */
+#define KB_UNSET_A20 0xdd /* enable A20,
+ enable output buffer full interrupt
+ enable data line
+ disable clock line */
+#ifndef IBM_L40
+static void empty_8042(void)
+{
+ unsigned long time;
+ char st;
+
+ time = currticks() + TICKS_PER_SEC; /* max wait of 1 second */
+ while ((((st = inb(K_CMD)) & K_OBUF_FUL) ||
+ (st & K_IBUF_FUL)) &&
+ currticks() < time)
+ inb(K_RDWR);
+}
+#endif IBM_L40
+
+/*
+ * Gate A20 for high memory
+ */
+void gateA20_set(void)
+{
+#ifdef IBM_L40
+ outb(0x2, 0x92);
+#else /* IBM_L40 */
+ empty_8042();
+ outb(KC_CMD_WOUT, K_CMD);
+ empty_8042();
+ outb(KB_SET_A20, K_RDWR);
+ empty_8042();
+#endif /* IBM_L40 */
+}
+
+#ifdef TAGGED_IMAGE
+/*
+ * Unset Gate A20 for high memory - some operating systems (mainly old 16 bit
+ * ones) don't expect it to be set by the boot loader.
+ */
+void gateA20_unset(void)
+{
+#ifdef IBM_L40
+ outb(0x0, 0x92);
+#else /* IBM_L40 */
+ empty_8042();
+ outb(KC_CMD_WOUT, K_CMD);
+ empty_8042();
+ outb(KB_UNSET_A20, K_RDWR);
+ empty_8042();
+#endif /* IBM_L40 */
+}
+#endif
+
+#ifdef ETHERBOOT32
+/* Serial console is only implemented in ETHERBOOT32 for now */
+void
+putchar(int c)
+{
+#ifndef ANSIESC
+ if (c == '\n')
+ putchar('\r');
+#endif
+
+#ifdef CONSOLE_CRT
+#ifdef ANSIESC
+ handleansi(c);
+#else
+ putc(c);
+#endif
+#endif
+#ifdef CONSOLE_SERIAL
+#ifdef ANSIESC
+ if (c == '\n')
+ serial_putc('\r');
+#endif
+ serial_putc(c);
+#endif
+}
+
+/**************************************************************************
+GETCHAR - Read the next character from the console WITHOUT ECHO
+**************************************************************************/
+int
+getchar(void)
+{
+ int c = 256;
+
+#if defined CONSOLE_CRT || defined CONSOLE_SERIAL
+ do {
+#ifdef CONSOLE_CRT
+ if (ischar())
+ c = getc();
+#endif
+#ifdef CONSOLE_SERIAL
+ if (serial_ischar())
+ c = serial_getc();
+#endif
+ } while (c==256);
+ if (c == '\r')
+ c = '\n';
+#endif
+ return c;
+}
+
+int
+iskey(void)
+{
+#ifdef CONSOLE_CRT
+ if (ischar())
+ return 1;
+#endif
+#ifdef CONSOLE_SERIAL
+ if (serial_ischar())
+ return 1;
+#endif
+ return 0;
+}
+#endif /* ETHERBOOT32 */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
+#include <asm/msr.h>
+
+#define CPUCLOCK 166
+
+unsigned long currticks(void)
+{
+ register unsigned long l, h;
+ long long unsigned p;
+ long long unsigned hh,ll;
+
+ rdtsc(l, h);
+ ll = l, hh = h;
+
+ p = (ll + hh * 0x100000000LL) * 182 / (CPUCLOCK * 100000LL);
+ return (unsigned)p;
+}
+
diff --git a/gpxe/contrib/baremetal/startmpcc.S b/gpxe/contrib/baremetal/startmpcc.S
new file mode 100644
index 00000000..07486ce5
--- /dev/null
+++ b/gpxe/contrib/baremetal/startmpcc.S
@@ -0,0 +1,756 @@
+/* #defines because ljmp wants a number, probably gas bug */
+/* .equ KERN_CODE_SEG,_pmcs-_gdt */
+#define KERN_CODE_SEG 0x08
+ .equ KERN_DATA_SEG,_pmds-_gdt
+/* .equ REAL_CODE_SEG,_rmcs-_gdt */
+#define REAL_CODE_SEG 0x18
+ .equ REAL_DATA_SEG,_rmds-_gdt
+ .equ CR0_PE,1
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+#define PIC1_VBS 0x08 /* PIC1 interrupts start at vector 64 */
+#define PIC2_VBS 0x70 /* PIC1 interrupts start at vector 112 */
+
+/*
+ * NOTE: if you write a subroutine that is called from C code (gcc/egcs),
+ * then you only have to take care of %ebx, %esi, %edi and %ebp. These
+ * registers must not be altered under any circumstance. All other registers
+ * may be clobbered without any negative side effects. If you don't follow
+ * this rule then you'll run into strange effects that only occur on some
+ * gcc versions (because the register allocator may use different registers).
+ *
+ * All the data32 prefixes for the ljmp instructions are necessary, because
+ * the assembler emits code with a relocation address of 0. This means that
+ * all destinations are initially negative, which the assembler doesn't grok,
+ * because for some reason negative numbers don't fit into 16 bits. The addr32
+ * prefixes are there for the same reasons, because otherwise the memory
+ * references are only 16 bit wide. Theoretically they are all superfluous.
+ * One last note about prefixes: the data32 prefixes on all call _real_to_prot
+ * instructions could be removed if the _real_to_prot function is changed to
+ * deal correctly with 16 bit return addresses. I tried it, but failed.
+ */
+
+/**************************************************************************
+START - Where all the fun begins....
+**************************************************************************/
+/* this must be the first thing in the file because we enter from the top */
+ .global _start
+ .code32
+_start:
+ cli
+
+ /* load new IDT and GDT */
+ lgdt gdtarg
+ lidt Idt_Reg
+ /* flush prefetch queue, and reload %cs:%eip */
+ ljmp $KERN_CODE_SEG,$1f
+1:
+
+ /* reload other segment registers */
+ movl $KERN_DATA_SEG,%eax
+ movl %eax,%ds
+ movl %eax,%es
+ movl %eax,%ss
+ movl $stktop,%esp
+
+ /* program the PITs in order to stop them */
+ mov $0x30,%al
+ out %al,$0x43
+ out %al,$0x40
+ mov $0x70,%al
+ out %al,$0x43
+ out %al,$0x41
+ mov $0xf0,%al
+ out %al,$0x43
+ out %al,$0x42
+
+ call main
+ /* fall through */
+
+ .globl exit
+exit:
+2:
+ ljmp $KERN_CODE_SEG,$2b
+
+/**************************************************************************
+MEMSIZE - Determine size of extended memory
+**************************************************************************/
+ .globl memsize
+memsize:
+#if 0
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ call _prot_to_real
+ .code16
+ movw $0xe801,%ax
+ stc
+ int $0x15
+ jc 1f
+ andl $0xffff,%eax
+ andl $0xffff,%ebx
+ shll $6,%ebx
+ addl %ebx,%eax
+ jmp 2f
+1:
+ movw $0x8800,%ax
+ int $0x15
+ andl $0xffff,%eax
+2:
+ movl %eax,%esi
+ DATA32 call _real_to_prot
+ .code32
+ movl %esi,%eax
+ popl %edi
+ popl %esi
+ popl %ebx
+#else
+ mov $32768,%eax
+#endif
+ ret
+
+/**************************************************************************
+XSTART - Transfer control to the kernel just loaded
+**************************************************************************/
+ .code16
+
+ .globl _int08_handler
+_int08_handler:
+ movb $0x20, %al
+ outb %al, $0x20
+ iret
+
+ .globl _int10_handler
+_int10_handler:
+ cmp $0x3, %ah
+ jnz _int10_04
+ mov $0x0, %dx
+ mov $0x0, %cx
+ iret
+_int10_04:
+ cmp $0x4, %ah
+ jnz _int10_05
+ mov $0x0, %ah
+ iret
+_int10_05:
+ cmp $0x5, %ah
+ jnz _int10_08
+ mov $0x0, %al
+ iret
+_int10_08:
+ cmp $0x8, %ah
+ jnz _int10_0D
+ mov $0x20, %al
+ mov $0x7, %ah
+ iret
+_int10_0D:
+ cmp $0xD, %ah
+ jnz _int10_0F
+ mov $0x0, %al
+ iret
+_int10_0F:
+ cmp $0xF, %ah
+ jnz _int10_XX
+ mov $0xb, %al
+ mov $80, %ah
+ mov $0, %bh
+_int10_XX:
+ iret
+
+ .globl _int11_handler
+_int11_handler:
+ mov $0x22, %ax
+ iret
+
+ .globl _int12_handler
+_int12_handler:
+ mov $640, %ax
+ iret
+
+ .globl _int13_handler
+_int13_handler:
+ clc
+ mov $0, %ah
+ iret
+
+ .globl _int14_handler
+_int14_handler:
+ iret
+
+ .globl _int15_handler
+_int15_handler:
+ cmp $0xe801,%ax
+ jz _int15_008
+ cmp $0x0, %ah
+ jz _int15_000
+ cmp $0x1, %ah
+ jz _int15_000
+ cmp $0x2, %ah
+ jz _int15_000
+ cmp $0x3, %ah
+ jz _int15_000
+ cmp $0xf, %ah
+ jz _int15_000
+ cmp $0x21, %ah
+ jz _int15_000
+ cmp $0x40, %ah
+ jz _int15_000
+ cmp $0x41, %ah
+ jz _int15_000
+ cmp $0x42, %ah
+ jz _int15_000
+ cmp $0x43, %ah
+ jz _int15_000
+ cmp $0x44, %ah
+ jz _int15_000
+ cmp $0x80, %ah
+ jz _int15_001
+ cmp $0x81, %ah
+ jz _int15_001
+ cmp $0x82, %ah
+ jz _int15_002
+ cmp $0x83, %ah
+ jz _int15_003
+ cmp $0x84, %ah
+ jz _int15_000
+ cmp $0x85, %ah
+ jz _int15_004
+ cmp $0x86, %ah
+ jz _int15_003
+ cmp $0x87, %ah
+ jz _int15_005
+ cmp $0x88, %ah
+ jz _int15_006
+ cmp $0x89, %ah
+ jz _int15_005
+ cmp $0x90, %ah
+ jz _int15_007
+ cmp $0xc0, %ah
+ jz _int15_000
+ cmp $0xc1, %ah
+ jz _int15_000
+ cmp $0xc2, %ah
+ jz _int15_000
+ cmp $0xc3, %ah
+ jz _int15_000
+ cmp $0xc4, %ah
+ jz _int15_000
+ iret
+
+_int15_000:
+ mov $0x86, %ah
+ stc
+ iret
+
+_int15_001:
+ mov $0, %bx
+ mov $0, %cx
+ iret
+
+_int15_002:
+ mov $0, %bx
+ iret
+
+_int15_003:
+ clc
+ iret
+
+_int15_004:
+ mov $0, %al
+ iret
+
+_int15_005:
+ mov $0, %ah
+ clc
+ cmp $0, %ah
+ iret
+
+_int15_006:
+ mov $0xf000, %ax
+ iret
+
+_int15_007:
+ stc
+ iret
+
+_int15_008:
+ clc
+ mov $1024, %dx /* dx -> extended memory size (in 64K chuncks) */
+ mov $640, %cx /* cx -> conventional memory size (in 1 Kbytes chuncks) */
+ iret
+
+ .globl _int16_handler
+_int16_handler:
+ cmp $0x0, %ah
+ jnz _int16_01
+ mov $0x20, %al
+ mov $0x39, %ah
+ iret
+_int16_01:
+ cmp $0x1, %ah
+ jnz _int16_02
+ iret
+_int16_02:
+ cmp $0x2, %ah
+ jnz _int16_05
+ mov $0, %al
+ iret
+_int16_05:
+ cmp $0x5, %ah
+ jnz _int16_10
+ mov $0, %al
+ iret
+_int16_10:
+ cmp $0x10, %ah
+ jnz _int16_11
+ mov $0x20, %al
+ mov $0x39, %ah
+ iret
+_int16_11:
+ cmp $0x11, %ah
+ jnz _int16_12
+ iret
+_int16_12:
+ cmp $0x12, %ah
+ jnz _int16_XX
+ mov $0, %ax
+ iret
+_int16_XX:
+ iret
+
+ .globl _int17_handler
+_int17_handler:
+ mov $0xd0, %ah
+ iret
+
+ .globl _int19_handler
+_int19_handler:
+ hlt
+ iret
+
+ .globl _int1A_handler
+_int1A_handler:
+ stc
+ iret
+
+ .code32
+ .globl xstart
+xstart:
+ /* reprogram the PICs so that interrupt are masked */
+ movb $0x11,%al /* ICW1 [ICW4 NEEDED, EDGE TRIGGERED]*/
+ outb %al,$0x20
+ movb $PIC1_VBS, %al
+ outb %al,$0x21
+ movb $0x4,%al
+ outb %al,$0x21
+ movb $0x1,%al
+ outb %al,$0x21
+ movb $0xff,%al
+ outb %al,$0x21
+
+ movb $0x11,%al /* ICW1 [ICW4 NEEDED, EDGE TRIGGERED]*/
+ outb %al,$0xa0
+ movb $PIC2_VBS, %al
+ outb %al,$0xa1
+ movb $0x2,%al
+ outb %al,$0xa1
+ movb $0x1,%al
+ outb %al,$0xa1
+ movb $0xff,%al
+ outb %al,$0xa1
+
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ movl 8(%ebp),%eax
+ movl %eax,_execaddr
+ movl 12(%ebp),%ebx
+ movl 16(%ebp),%ecx /* bootp record (32bit pointer) */
+ addl $28,%ecx /* ip, udp header */
+ shll $12,%ecx
+ shrw $12,%cx
+ call _prot_to_real
+ .code16
+/* MP: add int10 handler */
+ push %eax
+ push %ebx
+ push %es
+ mov $0,%ax
+ mov %ax,%es
+ mov %cs,%ax
+ shl $16,%eax
+
+ ADDR32 mov $(_int08_handler-_start),%ax
+ mov $0x20,%ebx
+ mov %eax,%es:(%bx)
+
+ ADDR32 mov $(_int10_handler-_start),%ax
+ mov $0x40,%ebx
+ mov %eax,%es:(%bx)
+
+ ADDR32 mov $(_int11_handler-_start),%ax
+ mov $0x44,%ebx
+ mov %eax,%es:(%bx)
+
+ ADDR32 mov $(_int12_handler-_start),%ax
+ mov $0x48,%ebx
+ mov %eax,%es:(%bx)
+
+ ADDR32 mov $(_int13_handler-_start),%ax
+ mov $0x4c,%ebx
+ mov %eax,%es:(%bx)
+
+ ADDR32 mov $(_int14_handler-_start),%ax
+ mov $0x50,%ebx
+ mov %eax,%es:(%bx)
+
+ ADDR32 mov $(_int15_handler-_start),%ax
+ mov $0x54,%ebx
+ mov %eax,%es:(%bx)
+
+ ADDR32 mov $(_int16_handler-_start),%ax
+ mov $0x58,%ebx
+ mov %eax,%es:(%bx)
+
+ ADDR32 mov $(_int17_handler-_start),%ax
+ mov $0x5c,%ebx
+ mov %eax,%es:(%bx)
+
+ ADDR32 mov $(_int19_handler-_start),%ax
+ mov $0x64,%ebx
+ mov %eax,%es:(%bx)
+
+ ADDR32 mov $(_int1A_handler-_start),%ax
+ mov $0x68,%ebx
+ mov %eax,%es:(%bx)
+
+ pop %es
+ pop %ebx
+ pop %eax
+/* */
+ pushl %ecx /* bootp record */
+ pushl %ebx /* file header */
+ movl $((RELOC<<12)+(1f-RELOC)),%eax
+ pushl %eax
+ ADDR32 LJMPI(_execaddr-_start)
+1:
+ addw $8,%sp /* XXX or is this 10 in case of a 16bit "ret" */
+ DATA32 call _real_to_prot
+ .code32
+ popl %edi
+ popl %esi
+ popl %ebx
+ popl %ebp
+ ret
+
+_execaddr:
+ .long 0
+
+#ifdef IMAGE_MULTIBOOT
+/**************************************************************************
+XEND - Restart Etherboot from the beginning (from protected mode)
+**************************************************************************/
+
+ .globl xend
+xend:
+ cs
+ lidt idtarg_realmode-_start+RELOC
+ cs
+ lgdt gdtarg-_start+RELOC
+#ifdef GAS291
+ ljmp $REAL_CODE_SEG,$1f-RELOC /* jump to a 16 bit segment */
+#else
+ ljmp $REAL_CODE_SEG,$1f-_start /* jump to a 16 bit segment */
+#endif /* GAS291 */
+1:
+ .code16
+ movw $REAL_DATA_SEG,%ax
+ movw %ax,%ds
+ movw %ax,%ss
+ movw %ax,%es
+
+ /* clear the PE bit of CR0 */
+ movl %cr0,%eax
+ andl $0!CR0_PE,%eax
+ movl %eax,%cr0
+
+ /* make intersegment jmp to flush the processor pipeline
+ * and reload %cs:%eip (to clear upper 16 bits of %eip).
+ */
+ DATA32 ljmp $(RELOC)>>4,$2f-_start
+2:
+ /* we are in real mode now
+ * set up the real mode segment registers : %ds, %ss, %es
+ */
+ movw %cs,%ax
+ movw %ax,%ds
+ movw %ax,%es
+ movw %ax,%ss
+ xorl %esp,%esp
+ ADDR32 movw initsp-RELOC,%sp
+
+ movw $0,%ax
+ movw %ax,%fs
+ movw %ax,%gs
+
+ sti
+ jmp _start
+
+ .code32
+#endif /* IMAGE_MULTIBOOT */
+
+.global get_cs
+get_cs:
+ xorl %eax,%eax
+ movw %cs,%ax
+ ret
+
+.global get_ds
+get_ds:
+ xorl %eax,%eax
+ movw %ds,%ax
+ ret
+
+.global getsp
+getsp:
+ movl %esp,%eax /* GET STACK POINTER */
+ subl $4, %eax /* ACCOUNT FOR RETURN ADDRESS ON */
+ ret
+
+.global get_gdtbase
+get_gdtbase:
+ sub $8,%esp /* ALLOCATE ROOM ON THE STACK */
+ sgdt (%esp,1) /*STORE IGDT REGISTER ON STACK */
+ mov 2(%esp),%eax /* READ GDT BASE ADDRESS */
+ mov $KERN_DATA_SEG,%dx /* ASSUME UNIVERSAL DS. */
+ add $8,%esp /* RESTORE STACK */
+ ret /* DONE */
+
+.global get_gdtsize
+get_gdtsize:
+ sub $8,%esp /* ALLOCATE ROOM ON THE STACK */
+ sgdt (%esp,1) /*STORE IGDT REGISTER ON STACK */
+ xor %eax,%eax
+ mov 2(%esp),%eax /* READ GDT BASE ADDRESS */
+ mov (%ESP),%ax
+ shr $3,%ax
+ add $8,%esp /* RESTORE STACK */
+ ret /* DONE */
+
+.global get_idtbase
+get_idtbase:
+ sub $8,%esp
+ sidt (%esp,1) /* STORE IIDT REGISTER ON STACK */
+ mov 2(%esp),%eax
+ mov $KERN_DATA_SEG,%dx
+ add $8,%esp
+ ret
+
+.global get_lw
+get_lw:
+ xor %edx,%edx
+ mov 8(%esp),%eax
+ mov 4(%esp),%dx
+ ret
+
+/**************************************************************************
+SETJMP - Save stack context for non-local goto
+**************************************************************************/
+ .globl setjmp
+setjmp:
+ mov 4(%esp),%ecx
+ mov 0(%esp),%edx
+ mov %edx,0(%ecx)
+ mov %ebx,4(%ecx)
+ mov %esp,8(%ecx)
+ mov %ebp,12(%ecx)
+ mov %esi,16(%ecx)
+ mov %edi,20(%ecx)
+ mov %eax,24(%ecx)
+ mov $0,%eax
+ ret
+
+/**************************************************************************
+LONGJMP - Non-local jump to a saved stack context
+**************************************************************************/
+ .globl longjmp
+longjmp:
+ mov 4(%esp),%edx
+ mov 8(%esp),%eax
+ mov 0(%edx),%ecx
+ mov 4(%edx),%ebx
+ mov 8(%edx),%esp
+ mov 12(%edx),%ebp
+ mov 16(%edx),%esi
+ mov 20(%edx),%edi
+ cmp $0,%eax
+ jne 1f
+ mov $1,%eax
+1: mov %ecx,0(%esp)
+ ret
+
+/**************************************************************************
+_REAL_TO_PROT - Go from REAL mode to Protected Mode
+**************************************************************************/
+ .globl _real_to_prot
+_real_to_prot:
+ .code16
+ cli
+ cs
+ ADDR32 lgdt gdtarg-_start
+ movl %cr0,%eax
+ orl $CR0_PE,%eax
+ movl %eax,%cr0 /* turn on protected mode */
+
+ /* flush prefetch queue, and reload %cs:%eip */
+ DATA32 ljmp $KERN_CODE_SEG,$1f
+1:
+ .code32
+ /* reload other segment registers */
+ movl $KERN_DATA_SEG,%eax
+ movl %eax,%ds
+ movl %eax,%es
+ movl %eax,%ss
+ addl $RELOC,%esp /* Fix up stack pointer */
+ xorl %eax,%eax
+ movl %eax,%fs
+ movl %eax,%gs
+ popl %eax /* Fix up return address */
+ addl $RELOC,%eax
+ pushl %eax
+ ret
+
+/**************************************************************************
+_PROT_TO_REAL - Go from Protected Mode to REAL Mode
+**************************************************************************/
+ .globl _prot_to_real
+_prot_to_real:
+ .code32
+ popl %eax
+ subl $RELOC,%eax /* Adjust return address */
+ pushl %eax
+ subl $RELOC,%esp /* Adjust stack pointer */
+#ifdef GAS291
+ ljmp $REAL_CODE_SEG,$1f-RELOC /* jump to a 16 bit segment */
+#else
+ ljmp $REAL_CODE_SEG,$1f-_start /* jump to a 16 bit segment */
+#endif /* GAS291 */
+1:
+ .code16
+ movw $REAL_DATA_SEG,%ax
+ movw %ax,%ds
+ movw %ax,%ss
+ movw %ax,%es
+ movw %ax,%fs
+ movw %ax,%gs
+ cli
+
+ /* clear the PE bit of CR0 */
+ movl %cr0,%eax
+ andl $0!CR0_PE,%eax
+ movl %eax,%cr0
+
+ /* make intersegment jmp to flush the processor pipeline
+ * and reload %cs:%eip (to clear upper 16 bits of %eip).
+ */
+ DATA32 ljmp $(RELOC)>>4,$2f-_start
+2:
+ /* we are in real mode now
+ * set up the real mode segment registers : %ds, $ss, %es
+ */
+ movw %cs,%ax
+ movw %ax,%ds
+ movw %ax,%es
+ movw %ax,%ss
+#if 0
+ sti
+#endif
+ DATA32 ret /* There is a 32 bit return address on the stack */
+ .code32
+
+/**************************************************************************
+GLOBAL DESCRIPTOR TABLE
+**************************************************************************/
+ .align 4
+Idt_Reg:
+ .word 0x3ff
+ .long 0
+
+ .align 4
+_gdt:
+gdtarg:
+Gdt_Table:
+ .word 0x27 /* limit */
+ .long _gdt /* addr */
+ .word 0
+_pmcs:
+ /* 32 bit protected mode code segment */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds:
+ /* 32 bit protected mode data segment */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+
+_rmcs:
+ /* 16 bit real mode code segment */
+ .word 0xffff,(RELOC&0xffff)
+ .byte (RELOC>>16),0x9b,0x00,(RELOC>>24)
+
+_rmds:
+ /* 16 bit real mode data segment */
+ .word 0xffff,(RELOC&0xffff)
+ .byte (RELOC>>16),0x93,0x00,(RELOC>>24)
+
+ .align 4
+RUN_GDT: /* POINTER TO GDT IN RAM */
+ .byte 0x7f,0 /* [BSP_GDT_NUM*8]-1 */
+ .long Gdt_Table
+
+ .align 4
+
+ .section ".rodata"
+err_not386:
+ .ascii "Etherboot/32 requires 386+"
+ .byte 0x0d, 0x0a
+err_not386_end:
+
+days: .long 0
+irq_num: .long
+
+ .data
+ .align 4
+ .org 2048
+.global stktop
+stktop:
+ .long
+
+.section ".armando"
+/*                1:::::::::2:::::::::3:::::::3 */
+/*        12345678901234567890123456789012345678 */
+/*       v----+----v----+----v----+----v----+--- */
+
+.global EtherbootString
+EtherbootString:
+.ascii "EtherBoot MPCC " /* fw identifier */
+
+.byte 0, 0 /* mandatory hole */
+
+.long _start /* entry point */
+.word 0
+.byte 'E' /* type */
+.byte 0 /* selector */
+.word 0 /* CRC */
diff --git a/gpxe/contrib/bin2intelhex/Makefile b/gpxe/contrib/bin2intelhex/Makefile
new file mode 100644
index 00000000..74069688
--- /dev/null
+++ b/gpxe/contrib/bin2intelhex/Makefile
@@ -0,0 +1,9 @@
+
+CC=gcc
+CFLAGS=-Wall -O2
+
+bin2intelhex:
+
+
+clean:
+ rm -f bin2intelhex core *.o
diff --git a/gpxe/contrib/bin2intelhex/bin2intelhex.c b/gpxe/contrib/bin2intelhex/bin2intelhex.c
new file mode 100644
index 00000000..75b88c15
--- /dev/null
+++ b/gpxe/contrib/bin2intelhex/bin2intelhex.c
@@ -0,0 +1,148 @@
+/* name : bin2intelhex.c
+ * from : Jean Marc Lacroix <jeanmarc.lacroix@free.fr>
+ * date : 06/12/1997.
+ * abstract : Y have rewrite this program from ????? with some modifications
+ * to add :
+ * - the Intel specification.
+ * - correct a bug because my prom programmer don't understand the
+ * initial format. Y suspect a bug in the calcul of the lrc
+ * in the original program.
+ * - correct the format of printf . In the original program, it was
+ * %x, and it is in fact %X, because in the Intel Format, all the
+ * char are in upper case.
+ * - correct the lrc calculation.
+ * usage:
+ *-------
+ * this program read the standard input and put to the standard output
+ * the result of the conversion.
+ * an example of use :
+ * cat my_bin | bin2intelhex > my_bin.hex or.....
+ * bin2intelhex < my_bin > my_bin.hex
+ */
+
+
+/*
+ * $Id$
+ * $Log$
+ * Revision 1.1 2005/05/17 16:45:06 mcb30
+ * Initial revision
+ *
+ * Revision 1.9 1997/12/14 05:14:54 install
+ * - some documentation....
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+/* Intel Hex format specifications
+
+The 8-bit Intel Hex File Format is a printable ASCII format consisting of one
+ or more data records followed by an end of file record. Each
+record consists of one line of information. Data records may appear in any
+ order. Address and data values are represented as 2 or 4 hexadecimal
+digit values.
+
+Record Format
+:LLAAAARRDDDD......DDDDCC
+
+
+LL
+AAAA
+RR
+DD
+CC
+Length field. Number of data bytes.
+Address field. Address of first byte.
+Record type field. 00 for data and 01 for end of record.
+Data field.
+Checksum field. One's complement of length, address, record type and data
+ fields modulo 256.
+CC = LL + AAAA + RR + all DD = 0
+
+Example:
+:06010000010203040506E4
+:00000001FF
+
+The first line in the above example Intel Hex file is a data record addressed
+ at location 100H with data values 1 to 6. The second line is the end
+of file record, so that the LL field is 0
+
+*/
+
+
+typedef unsigned char t_u8;
+typedef unsigned short t_u16;
+/*
+ * the choice for the total length (16) of a line, but the specification
+ * can support an another value
+ */
+#define LL_MAX_LINE 16
+typedef struct
+{
+ t_u8 intel_lg_data;
+ t_u16 intel_adr;
+ t_u8 intel_type;
+ t_u8 intel_data [LL_MAX_LINE];
+ t_u8 intel_lrc;
+} t_one_line;
+#define INTEL_DATA_TYPE 0
+#define EXIT_OK 0
+int main (const int argc, const char ** const argv)
+{
+ t_one_line line;
+ /*
+ * init for the adress, please note that it is assume that the program begin at 0
+ */
+ line.intel_adr = 0;
+ line.intel_type = INTEL_DATA_TYPE;
+ /*
+ * read the data on the standard input
+ */
+ while ((line.intel_lg_data = read (0, &line.intel_data [0] ,LL_MAX_LINE )) > 0)
+ {
+ t_u8 i;
+ /*
+ * and now for this line, calculate the lrc.
+ */
+ line.intel_lrc = line.intel_lg_data;
+ line.intel_lrc += ((line.intel_adr >> 8) & 0xff);
+ line.intel_lrc += (line.intel_adr &0xff);
+ line.intel_lrc += line.intel_type;
+ /*
+ * the structure is ready, print it to stdout in the
+ * right format
+ */
+ (void) printf (":%02X%04X%02X",
+ line.intel_lg_data,
+ line.intel_adr,
+ line.intel_type);
+ /*
+ * edit all the data read
+ */
+ for (i=0; i<line.intel_lg_data; i++)
+ {
+ (void) printf ("%02X",
+ (line.intel_data [i] & 0xff));
+ /*
+ * add to the lrc the data print
+ */
+ line.intel_lrc +=line.intel_data [i];
+ }
+ /*
+ * edit the value of the lrc and new line for the next
+ */
+ (void) printf ("%02X\n",
+ (0x100 - line.intel_lrc) & 0xff);
+ /*
+ * prepare the new adress for the next line
+ */
+ line.intel_adr+=line.intel_lg_data;
+ }
+ /*
+ * print the last line with a length of 0 data, so that the lrc is easy to
+ * calculate (ff+01 =0)
+ */
+ printf (":00000001FF\n");
+ exit (EXIT_OK);
+}
diff --git a/gpxe/contrib/bin2intelhex/bin2intelhex.c.simple b/gpxe/contrib/bin2intelhex/bin2intelhex.c.simple
new file mode 100644
index 00000000..3cb279a7
--- /dev/null
+++ b/gpxe/contrib/bin2intelhex/bin2intelhex.c.simple
@@ -0,0 +1,74 @@
+/*
+
+ Quick and dirty program to make intel-hex from a binary.
+
+ Written by R.E.Wolff@BitWizard.nl
+ This file is in the public domain
+
+ Typing started:
+
+ Mon Jun 16 00:24:15 MET DST 1997
+
+ programming stopped:
+
+ Mon Jun 16 00:31:27 MET DST 1997
+
+ debugging finished (2 bugs found):
+ Mon Jun 16 00:32:52 MET DST 1997
+
+---------------------------------------------------------
+
+ Doc written in timeout. Everything else in this file was done while
+ the timer was running.
+
+ I promised "Mark Kopecki" that writing the bin-to-intel-hex
+ converter would cost less than 15 minutes, and that it would be more
+ trouble to find a converter on the net than to write the converter
+ myself. I ended up spending over half an hour searching for
+ spec/converter/docs because of unreachable hosts on the internet. I
+ got a file with docs, after that it was 8 minutes.....
+
+---------------------------------------------------------
+
+*/
+
+
+#include <stdio.h>
+#include <unistd.h>
+
+/* Intel Hex format:
+
+ ll aaaa tt dd....dd cc
+
+ ll = length
+ aaaa = address
+ tt = type
+ dd....dd = data
+ cc = checksum.
+*/
+
+
+int main (int argc, char **argv)
+{
+ unsigned char buf[32];
+ int addr = 0;
+ int n,i;
+
+ while ((n = read (0, buf+4, 16)) > 0) {
+ buf[0] = n;
+ buf[1] = addr >> 8;
+ buf[2] = addr & 0xff;
+ buf[3] = 0x00;
+ buf[4+n] = 0x00;
+
+ for (i=0;i<4+n;i++)
+ buf[4+n] -= buf[i];
+ printf (":");
+ for (i=0;i<= 4+n;i++)
+ printf ("%02x", buf[i]);
+ printf ("\n");
+ addr += n;
+ }
+ printf (":0000000001ff\n");
+ exit (0);
+}
diff --git a/gpxe/contrib/bochs/.gitignore b/gpxe/contrib/bochs/.gitignore
new file mode 100644
index 00000000..4e8f81ab
--- /dev/null
+++ b/gpxe/contrib/bochs/.gitignore
@@ -0,0 +1,7 @@
+bochsout.txt
+parport.out
+ne2k-tx.log
+ne2k-txdump.txt
+tunctl
+bochs
+qemu
diff --git a/gpxe/contrib/bochs/Makefile b/gpxe/contrib/bochs/Makefile
new file mode 100644
index 00000000..4a5b14ce
--- /dev/null
+++ b/gpxe/contrib/bochs/Makefile
@@ -0,0 +1,10 @@
+all : tunctl serial-console.1
+
+%.1 : %
+ pod2man $< > $@
+
+tunctl : tunctl.c
+ $(CC) -o $@ $<
+
+clean :
+ rm -f serial-console.1 tunctl
diff --git a/gpxe/contrib/bochs/README b/gpxe/contrib/bochs/README
new file mode 100644
index 00000000..57c6f052
--- /dev/null
+++ b/gpxe/contrib/bochs/README
@@ -0,0 +1,94 @@
+Running Etherboot within Bochs
+==============================
+
+Michael Brown <mbrown@fensystems.co.uk>
+Based on an idea suggested by H. Peter Anvin <hpa@zytor.com>.
+
+$Id$
+
+Bochs is a program that simulates a complete Intel x86 computer,
+including hardware. It can be used to test Etherboot. There is a
+special pseudo NIC ("pnic") implemented in Bochs, with a corresponding
+driver in Etherboot. (There is also an NE2000 ISA driver in Bochs,
+but it doesn't seem to quite work.)
+
+To get bochs running is fairly simple:
+
+1. Build the utilities in this directory:
+ make
+
+2. Get the bochs source code:
+ cvs -d:pserver:anonymous@bochs.cvs.sourceforge.net:/cvsroot/bochs \
+ login
+ cvs -d:pserver:anonymous@bochs.cvs.sourceforge.net:/cvsroot/bochs \
+ co bochs
+
+3. Configure bochs with
+ pushd bochs
+ ./configure --enable-all-optimisations --enable-pci --enable-pnic \
+ --enable-debugger --enable-magic-breakpoints \
+ --enable-disasm --enable-show-ips --enable-ne2000
+ popd
+
+4. Build bochs:
+ make -C bochs
+
+5. As root, set up a TAP virtual network device:
+ /sbin/modprobe tun
+ chmod o+rw /dev/net/tun
+ ./tunctl -u <username> -t tap0
+ /sbin/ifconfig tap0 up 10.254.254.2 netmask 255.255.255.0
+
+6. As root, add the following fragment to /etc/dhcpd.conf:
+ subnet 10.254.254.0 netmask 255.255.255.252 {
+ range dynamic-bootp 10.254.254.1 10.254.254.1;
+ }
+ You will also need to add in any of your usual declarations for
+ Etherboot, e.g. 'filename "vmlinuz.ltsp";'. Note that this setup
+ assumes that your DHCP server, TFTP server etc. all live on the
+ machine you are using for running Bochs. If not, then you're on
+ your own.
+
+7. As root, restart dhcpd
+ /etc/init.d/dhcpd restart
+
+8. Build Etherboot images
+ pushd ../../src
+ make bin/pnic.dsk
+ popd
+
+9. Start Bochs
+ ./bochs/bochs -q
+ You should get to the debugger prompt "<bochs:1>". Type "c" to
+ start running Bochs.
+
+You should see Bochs start up, load up Etherboot and attempt to boot
+from the network.
+
+
+
+Serial console
+==============
+
+You can use the program "serial-console" to obtain a virtual serial
+console for Etherboot running within Bochs. Simply run
+"./serial-console" on a spare tty (e.g. a separate xterm window)
+before starting Bochs, and ensure that you have enabled CONSOLE_SERIAL
+in config.h.
+
+There is a manual page for "serial-console"; use
+"man ./serial-console.1" to view it.
+
+
+
+TODO
+====
+
+Packet forwarding/masquerading - document what must be set up.
+
+Mention possibility of using RFB as the display device - in
+conjunction with the serial console, gives you a test facility that
+can be accessed remotely.
+
+Mention use of BOCHSBP instruction (xchgw %bx,%bx) to avoid need to
+calculate breakpoints.
diff --git a/gpxe/contrib/bochs/README.qemu b/gpxe/contrib/bochs/README.qemu
new file mode 100644
index 00000000..ee20eec4
--- /dev/null
+++ b/gpxe/contrib/bochs/README.qemu
@@ -0,0 +1,87 @@
+Running Etherboot within qemu
+=============================
+
+Michael Brown <mbrown@fensystems.co.uk>
+
+To get qemu running is fairly simple:
+
+1. Build the utilities in this directory:
+ make
+
+2. Get the qemu source code:
+ cvs -d:pserver:anonymous@cvs.savannah.nongnu.org:/sources/qemu \
+ login
+ cvs -d:pserver:anonymous@cvs.savannah.nongnu.org:/sources/qemu \
+ co qemu
+
+2a. Patch the qemu code. There is currently a bug that causes qemu to
+ execute Etherboot incredibly slowly. The bug seems to be related
+ to the relative prioritisation of CPU and I/O operations within
+ qemu. This patch (which I found via Google) isn't a proper fix,
+ but it does work around the problem:
+ patch -p0 < qemu-patch
+
+3. Configure qemu with
+ pushd qemu
+ ./configure --enable-system
+ popd
+
+ Note that qemu will not compile with gcc4; if your system's
+ default compiler is gcc4 then specify the path to gcc3 using
+ e.g. --cc=gcc-3.3.6
+
+4. Build qemu:
+ make -C qemu
+
+5. As root, set up a TAP virtual network device:
+ /sbin/modprobe tun
+ chmod o+rw /dev/net/tun
+ ./tunctl -u <username> -t tap0
+ /sbin/ifconfig tap0 up 10.254.254.2 netmask 255.255.255.0
+
+6. As root, add the following fragment to /etc/dhcpd.conf:
+ subnet 10.254.254.0 netmask 255.255.255.252 {
+ range dynamic-bootp 10.254.254.1 10.254.254.1;
+ }
+ You will also need to add in any of your usual declarations for
+ Etherboot, e.g. 'filename "vmlinuz.ltsp";'. Note that this setup
+ assumes that your DHCP server, TFTP server etc. all live on the
+ machine you are using for running qemu. If not, then you're on
+ your own.
+
+7. As root, restart dhcpd
+ /etc/init.d/dhcpd restart
+
+8. Build Etherboot floppy disk images and pad to 1.44MB
+ pushd ../../src
+ make bin/rtl8139.pdsk
+ popd
+
+9. Start qemu
+ export SDL_VIDEO_X11_DGAMOUSE=0
+ ./qemu/i386-softmmu/qemu -L qemu/pc-bios \
+ -net nic,model=rtl8139 -net tap,ifname=tap0 \
+ -boot a -fda ../../src/bin/rtl8139.pdsk
+
+You should see qemu start up, load up Etherboot and attempt to boot
+from the network.
+
+
+
+Serial console
+==============
+
+You can use the program "serial-console" to obtain a virtual serial
+console for Etherboot running within qemu. Run "./serial-console" on
+a spare tty (e.g. a separate xterm window) before starting qemu, and
+ensure that you have enabled CONSOLE_SERIAL in config.h.
+
+When serial-console starts, it will print out the message "Slave pty
+is /dev/pts/XX", where XX is a number. You need to append the option
+
+ -serial /dev/pts/XX
+
+to the qemu command line.
+
+There is a manual page for "serial-console"; use "man
+./serial-console.1" to view it.
diff --git a/gpxe/contrib/bochs/README.windows-ris b/gpxe/contrib/bochs/README.windows-ris
new file mode 100644
index 00000000..830db6a8
--- /dev/null
+++ b/gpxe/contrib/bochs/README.windows-ris
@@ -0,0 +1,31 @@
+Debugging Windows Remote Installation Services (RIS) can be
+problematic. Here are some assorted notes on the process:
+
+
+Getting hold of the files
+=========================
+
+Add/Remove Windows Components -> Remote Installation Services
+
+Files will be placed in \windows\system32\reminst. Copy them out to a
+TFTP server. Configure DHCP to hand out startrom.com.
+
+
+Getting past the "Press F12" message
+====================================
+
+Passing F12 through to the guest machine is difficult. It's easier to
+patch the startrom.com binary to accept a different key. Open
+startrom.com in a hex editor, search for the hex string
+6681fb00860000, and replace it with 6681fb66210000. startrom.com will
+now accept the "F" key instead of "F12".
+
+
+
+DHCP filename
+=============
+
+Must use Windows backslash separator e.g. 'filename
+"OSChooser\\i386\\startrom.com";', otherwise startrom.com fails to
+correctly identify the path to NTLDR.
+
diff --git a/gpxe/contrib/bochs/bochsrc.txt b/gpxe/contrib/bochs/bochsrc.txt
new file mode 100644
index 00000000..bf99f4cd
--- /dev/null
+++ b/gpxe/contrib/bochs/bochsrc.txt
@@ -0,0 +1,750 @@
+# You may now use double quotes around pathnames, in case
+# your pathname includes spaces.
+
+#=======================================================================
+# CONFIG_INTERFACE
+#
+# The configuration interface is a series of menus or dialog boxes that
+# allows you to change all the settings that control Bochs's behavior.
+# There are two choices of configuration interface: a text mode version
+# called "textconfig" and a graphical version called "wx". The text
+# mode version uses stdin/stdout and is always compiled in. The graphical
+# version is only available when you use "--with-wx" on the configure
+# command. If you do not write a config_interface line, Bochs will
+# choose a default for you.
+#
+# NOTE: if you use the "wx" configuration interface, you must also use
+# the "wx" display library.
+#=======================================================================
+#config_interface: textconfig
+#config_interface: wx
+
+#=======================================================================
+# DISPLAY_LIBRARY
+#
+# The display library is the code that displays the Bochs VGA screen. Bochs
+# has a selection of about 10 different display library implementations for
+# different platforms. If you run configure with multiple --with-* options,
+# the display_library command lets you choose which one you want to run with.
+# If you do not write a display_library line, Bochs will choose a default for
+# you.
+#
+# The choices are:
+# x use X windows interface, cross platform
+# win32 use native win32 libraries
+# carbon use Carbon library (for MacOS X)
+# beos use native BeOS libraries
+# macintosh use MacOS pre-10
+# amigaos use native AmigaOS libraries
+# sdl use SDL library, cross platform
+# svga use SVGALIB library for Linux, allows graphics without X11
+# term text only, uses curses/ncurses library, cross platform
+# rfb provides an interface to AT&T's VNC viewer, cross platform
+# wx use wxWidgets library, cross platform
+# nogui no display at all
+#
+# NOTE: if you use the "wx" configuration interface, you must also use
+# the "wx" display library.
+#
+# Specific options:
+# Some display libraries now support specific option to control their
+# behaviour. See the examples below for currently supported options.
+#=======================================================================
+#display_library: amigaos
+#display_library: beos
+#display_library: carbon
+#display_library: macintosh
+#display_library: nogui
+#display_library: rfb, options="timeout=60" # time to wait for client
+#display_library: sdl, options="fullscreen" # startup in fullscreen mode
+#display_library: term
+#display_library: win32, options="legacyF12" # use F12 to toggle mouse
+#display_library: wx
+#display_library: x
+
+#=======================================================================
+# ROMIMAGE:
+# The ROM BIOS controls what the PC does when it first powers on.
+# Normally, you can use a precompiled BIOS in the source or binary
+# distribution called BIOS-bochs-latest. The ROM BIOS is usually loaded
+# starting at address 0xf0000, and it is exactly 64k long.
+# You can also use the environment variable $BXSHARE to specify the
+# location of the BIOS.
+# The usage of external large BIOS images (up to 512k) at memory top is
+# now supported, but we still recommend to use the BIOS distributed with
+# Bochs. Now the start address can be calculated from image size.
+#=======================================================================
+romimage: file=bochs/bios/BIOS-bochs-latest, address=0xe0000
+#romimage: file=mybios.bin, address=0xfff80000 # 512k at memory top
+#romimage: file=mybios.bin # calculate start address from image size
+
+#=======================================================================
+# CPU:
+# This defines cpu-related parameters inside Bochs:
+#
+# COUNT:
+# Set the number of processors:cores per processor:threads per core
+# when Bochs is compiled for SMP emulation.
+# Bochs currently supports up to 8 threads running simultaniosly.
+# If Bochs is compiled without SMP support, it won't accept values
+# different from 1.
+#
+# RESET_ON_TRIPLE_FAULT:
+# Reset the CPU when triple fault occur (highly recommended) rather than
+# PANIC. Remember that if you trying to continue after triple fault the
+# simulation will be completely bogus !
+#
+# IPS:
+# Emulated Instructions Per Second. This is the number of IPS that bochs
+# is capable of running on your machine. You can recompile Bochs with
+# --enable-show-ips option enabled, to find your workstation's capability.
+# Measured IPS value will then be logged into your log file or status bar
+# (if supported by the gui).
+#
+# IPS is used to calibrate many time-dependent events within the bochs
+# simulation. For example, changing IPS affects the frequency of VGA
+# updates, the duration of time before a key starts to autorepeat, and
+# the measurement of BogoMips and other benchmarks.
+#
+# Examples:
+# Machine Mips
+# ________________________________________________________________
+# 2.1Ghz Athlon XP with Linux 2.6/g++ 3.4 12 to 15 Mips
+# 1.6Ghz Intel P4 with Win2000/g++ 3.3 5 to 7 Mips
+# 650Mhz Athlon K-7 with Linux 2.4.4/egcs-2.91.66 2 to 2.5 Mips
+# 400Mhz Pentium II with Linux 2.0.36/egcs-1.0.3 1 to 1.8 Mips
+#=======================================================================
+cpu: count=1, ips=10000000, reset_on_triple_fault=1
+
+#=======================================================================
+# MEGS
+# Set the number of Megabytes of physical memory you want to emulate.
+# The default is 32MB, most OS's won't need more than that.
+# The maximum amount of memory supported is 2048Mb.
+#=======================================================================
+#megs: 256
+#megs: 128
+#megs: 64
+megs: 32
+#megs: 16
+#megs: 8
+
+#=======================================================================
+# OPTROMIMAGE[1-4]:
+# You may now load up to 4 optional ROM images. Be sure to use a
+# read-only area, typically between C8000 and EFFFF. These optional
+# ROM images should not overwrite the rombios (located at
+# F0000-FFFFF) and the videobios (located at C0000-C7FFF).
+# Those ROM images will be initialized by the bios if they contain
+# the right signature (0x55AA) and a valid checksum.
+# It can also be a convenient way to upload some arbitrary code/data
+# in the simulation, that can be retrieved by the boot loader
+#=======================================================================
+#optromimage1: file=optionalrom.bin, address=0xd0000
+#optromimage2: file=optionalrom.bin, address=0xd1000
+#optromimage3: file=optionalrom.bin, address=0xd2000
+#optromimage4: file=optionalrom.bin, address=0xd3000
+optromimage1: file=../../src/bin/pnic.rom, address=0xd0000
+#optromimage1: file=../../src/bin/rtl8029.rom, address=0xd0000
+
+#optramimage1: file=/path/file1.img, address=0x0010000
+#optramimage2: file=/path/file2.img, address=0x0020000
+#optramimage3: file=/path/file3.img, address=0x0030000
+#optramimage4: file=/path/file4.img, address=0x0040000
+
+#=======================================================================
+# VGAROMIMAGE
+# You now need to load a VGA ROM BIOS into C0000.
+#=======================================================================
+#vgaromimage: file=bios/VGABIOS-elpin-2.40
+vgaromimage: file=bochs/bios/VGABIOS-lgpl-latest
+#vgaromimage: file=bios/VGABIOS-lgpl-latest-cirrus
+
+#=======================================================================
+# VGA:
+# Here you can specify the display extension to be used. With the value
+# 'none' you can use standard VGA with no extension. Other supported
+# values are 'vbe' for Bochs VBE and 'cirrus' for Cirrus SVGA support.
+#=======================================================================
+#vga: extension=cirrus
+#vga: extension=vbe
+vga: extension=none
+
+#=======================================================================
+# FLOPPYA:
+# Point this to pathname of floppy image file or device
+# This should be of a bootable floppy(image/device) if you're
+# booting from 'a' (or 'floppy').
+#
+# You can set the initial status of the media to 'ejected' or 'inserted'.
+# floppya: 2_88=path, status=ejected (2.88M 3.5" floppy)
+# floppya: 1_44=path, status=inserted (1.44M 3.5" floppy)
+# floppya: 1_2=path, status=ejected (1.2M 5.25" floppy)
+# floppya: 720k=path, status=inserted (720K 3.5" floppy)
+# floppya: 360k=path, status=inserted (360K 5.25" floppy)
+# floppya: 320k=path, status=inserted (320K 5.25" floppy)
+# floppya: 180k=path, status=inserted (180K 5.25" floppy)
+# floppya: 160k=path, status=inserted (160K 5.25" floppy)
+# floppya: image=path, status=inserted (guess type from image size)
+#
+# The path should be the name of a disk image file. On Unix, you can use a raw
+# device name such as /dev/fd0 on Linux. On win32 platforms, use drive letters
+# such as a: or b: as the path. The parameter 'image' works with image files
+# only. In that case the size must match one of the supported types.
+#=======================================================================
+#floppya: 1_44=/dev/fd0, status=inserted
+#floppya: image=../1.44, status=inserted
+#floppya: 1_44=/dev/fd0H1440, status=inserted
+#floppya: 1_2=../1_2, status=inserted
+#floppya: 1_44=a:, status=inserted
+#floppya: 1_44=a.img, status=inserted
+#floppya: 1_44=/dev/rfd0a, status=inserted
+floppya: 1_44=../../src/bin/pnic.dsk, status=inserted
+
+#=======================================================================
+# FLOPPYB:
+# See FLOPPYA above for syntax
+#=======================================================================
+#floppyb: 1_44=b:, status=inserted
+floppyb: 1_44=b.img, status=inserted
+
+#=======================================================================
+# ATA0, ATA1, ATA2, ATA3
+# ATA controller for hard disks and cdroms
+#
+# ata[0-3]: enabled=[0|1], ioaddr1=addr, ioaddr2=addr, irq=number
+#
+# These options enables up to 4 ata channels. For each channel
+# the two base io addresses and the irq must be specified.
+#
+# ata0 and ata1 are enabled by default with the values shown below
+#
+# Examples:
+# ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
+# ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
+# ata2: enabled=1, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
+# ata3: enabled=1, ioaddr1=0x168, ioaddr2=0x360, irq=9
+#=======================================================================
+ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
+ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
+ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
+ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9
+
+#=======================================================================
+# ATA[0-3]-MASTER, ATA[0-3]-SLAVE
+#
+# This defines the type and characteristics of all attached ata devices:
+# type= type of attached device [disk|cdrom]
+# mode= only valid for disks [flat|concat|external|dll|sparse|vmware3]
+# mode= only valid for disks [undoable|growing|volatile]
+# path= path of the image
+# cylinders= only valid for disks
+# heads= only valid for disks
+# spt= only valid for disks
+# status= only valid for cdroms [inserted|ejected]
+# biosdetect= type of biosdetection [none|auto], only for disks on ata0 [cmos]
+# translation=type of translation of the bios, only for disks [none|lba|large|rechs|auto]
+# model= string returned by identify device command
+# journal= optional filename of the redolog for undoable and volatile disks
+#
+# Point this at a hard disk image file, cdrom iso file, or physical cdrom
+# device. To create a hard disk image, try running bximage. It will help you
+# choose the size and then suggest a line that works with it.
+#
+# In UNIX it may be possible to use a raw device as a Bochs hard disk,
+# but WE DON'T RECOMMEND IT. In Windows there is no easy way.
+#
+# In windows, the drive letter + colon notation should be used for cdroms.
+# Depending on versions of windows and drivers, you may only be able to
+# access the "first" cdrom in the system. On MacOSX, use path="drive"
+# to access the physical drive.
+#
+# The path is always mandatory. For flat hard disk images created with
+# bximage geometry autodetection can be used (cylinders=0 -> cylinders are
+# calculated using heads=16 and spt=63). For other hard disk images and modes
+# the cylinders, heads, and spt are mandatory.
+#
+# Default values are:
+# mode=flat, biosdetect=auto, translation=auto, model="Generic 1234"
+#
+# The biosdetect option has currently no effect on the bios
+#
+# Examples:
+# ata0-master: type=disk, mode=flat, path=10M.sample, cylinders=306, heads=4, spt=17
+# ata0-slave: type=disk, mode=flat, path=20M.sample, cylinders=615, heads=4, spt=17
+# ata1-master: type=disk, mode=flat, path=30M.sample, cylinders=615, heads=6, spt=17
+# ata1-slave: type=disk, mode=flat, path=46M.sample, cylinders=940, heads=6, spt=17
+# ata2-master: type=disk, mode=flat, path=62M.sample, cylinders=940, heads=8, spt=17
+# ata2-slave: type=disk, mode=flat, path=112M.sample, cylinders=900, heads=15, spt=17
+# ata3-master: type=disk, mode=flat, path=483M.sample, cylinders=1024, heads=15, spt=63
+# ata3-slave: type=cdrom, path=iso.sample, status=inserted
+#=======================================================================
+#ata0-master: type=disk, mode=flat, path="30M.sample", cylinders=615, heads=6, spt=17
+#ata0-slave: type=cdrom, path=D:, status=inserted
+#ata0-slave: type=cdrom, path=/dev/cdrom, status=inserted
+#ata0-slave: type=cdrom, path="drive", status=inserted
+#ata0-slave: type=cdrom, path=/dev/rcd0d, status=inserted
+
+#=======================================================================
+# BOOT:
+# This defines the boot sequence. Now you can specify up to 3 boot drives.
+# You can either boot from 'floppy', 'disk' or 'cdrom'
+# legacy 'a' and 'c' are also supported
+# Examples:
+# boot: floppy
+# boot: disk
+# boot: cdrom
+# boot: c
+# boot: a
+# boot: cdrom, floppy, disk
+#=======================================================================
+#boot: floppy
+#boot: disk
+
+#=======================================================================
+# CLOCK:
+# This defines the parameters of the clock inside Bochs:
+#
+# SYNC:
+# TO BE COMPLETED (see Greg explanation in feature request #536329)
+#
+# TIME0:
+# Specifies the start (boot) time of the virtual machine. Use a time
+# value as returned by the time(2) system call. If no time0 value is
+# set or if time0 equal to 1 (special case) or if time0 equal 'local',
+# the simulation will be started at the current local host time.
+# If time0 equal to 2 (special case) or if time0 equal 'utc',
+# the simulation will be started at the current utc time.
+#
+# Syntax:
+# clock: sync=[none|slowdown|realtime|both], time0=[timeValue|local|utc]
+#
+# Example:
+# clock: sync=none, time0=local # Now (localtime)
+# clock: sync=slowdown, time0=315529200 # Tue Jan 1 00:00:00 1980
+# clock: sync=none, time0=631148400 # Mon Jan 1 00:00:00 1990
+# clock: sync=realtime, time0=938581955 # Wed Sep 29 07:12:35 1999
+# clock: sync=realtime, time0=946681200 # Sat Jan 1 00:00:00 2000
+# clock: sync=none, time0=1 # Now (localtime)
+# clock: sync=none, time0=utc # Now (utc/gmt)
+#
+# Default value are sync=none, time0=local
+#=======================================================================
+#clock: sync=none, time0=local
+
+
+#=======================================================================
+# FLOPPY_BOOTSIG_CHECK: disabled=[0|1]
+# Enables or disables the 0xaa55 signature check on boot floppies
+# Defaults to disabled=0
+# Examples:
+# floppy_bootsig_check: disabled=0
+# floppy_bootsig_check: disabled=1
+#=======================================================================
+#floppy_bootsig_check: disabled=1
+floppy_bootsig_check: disabled=0
+
+#=======================================================================
+# LOG:
+# Give the path of the log file you'd like Bochs debug and misc. verbiage
+# to be written to. If you don't use this option or set the filename to
+# '-' the output is written to the console. If you really don't want it,
+# make it "/dev/null" (Unix) or "nul" (win32). :^(
+#
+# Examples:
+# log: ./bochs.out
+# log: /dev/tty
+#=======================================================================
+#log: /dev/null
+log: bochsout.txt
+
+#=======================================================================
+# LOGPREFIX:
+# This handles the format of the string prepended to each log line.
+# You may use those special tokens :
+# %t : 11 decimal digits timer tick
+# %i : 8 hexadecimal digits of cpu current eip (ignored in SMP configuration)
+# %e : 1 character event type ('i'nfo, 'd'ebug, 'p'anic, 'e'rror)
+# %d : 5 characters string of the device, between brackets
+#
+# Default : %t%e%d
+# Examples:
+# logprefix: %t-%e-@%i-%d
+# logprefix: %i%e%d
+#=======================================================================
+#logprefix: %t%e%d
+
+#=======================================================================
+# LOG CONTROLS
+#
+# Bochs now has four severity levels for event logging.
+# panic: cannot proceed. If you choose to continue after a panic,
+# don't be surprised if you get strange behavior or crashes.
+# error: something went wrong, but it is probably safe to continue the
+# simulation.
+# info: interesting or useful messages.
+# debug: messages useful only when debugging the code. This may
+# spit out thousands per second.
+#
+# For events of each level, you can choose to crash, report, or ignore.
+# TODO: allow choice based on the facility: e.g. crash on panics from
+# everything except the cdrom, and only report those.
+#
+# If you are experiencing many panics, it can be helpful to change
+# the panic action to report instead of fatal. However, be aware
+# that anything executed after a panic is uncharted territory and can
+# cause bochs to become unstable. The panic is a "graceful exit," so
+# if you disable it you may get a spectacular disaster instead.
+#=======================================================================
+panic: action=ask
+error: action=report
+info: action=report
+debug: action=ignore
+#pass: action=fatal
+
+#=======================================================================
+# DEBUGGER_LOG:
+# Give the path of the log file you'd like Bochs to log debugger output.
+# If you really don't want it, make it /dev/null or '-'. :^(
+#
+# Examples:
+# debugger_log: ./debugger.out
+#=======================================================================
+#debugger_log: /dev/null
+#debugger_log: debugger.out
+debugger_log: -
+
+#=======================================================================
+# COM1, COM2, COM3, COM4:
+# This defines a serial port (UART type 16550A). In the 'term' you can specify
+# a device to use as com1. This can be a real serial line, or a pty. To use
+# a pty (under X/Unix), create two windows (xterms, usually). One of them will
+# run bochs, and the other will act as com1. Find out the tty the com1
+# window using the `tty' command, and use that as the `dev' parameter.
+# Then do `sleep 1000000' in the com1 window to keep the shell from
+# messing with things, and run bochs in the other window. Serial I/O to
+# com1 (port 0x3f8) will all go to the other window.
+# Other serial modes are 'null' (no input/output), 'file' (output to a file
+# specified as the 'dev' parameter), 'raw' (use the real serial port - under
+# construction for win32), 'mouse' (standard serial mouse - requires
+# mouse option setting 'type=serial' or 'type=serial_wheel') and 'socket'
+# (connect a networking socket).
+#
+# Examples:
+# com1: enabled=1, mode=null
+# com1: enabled=1, mode=mouse
+# com2: enabled=1, mode=file, dev=serial.out
+# com3: enabled=1, mode=raw, dev=com1
+# com3: enabled=1, mode=socket, dev=localhost:8888
+#=======================================================================
+#com1: enabled=1, mode=term, dev=/dev/ttyp9
+
+
+#=======================================================================
+# PARPORT1, PARPORT2:
+# This defines a parallel (printer) port. When turned on and an output file is
+# defined the emulated printer port sends characters printed by the guest OS
+# into the output file. On some platforms a device filename can be used to
+# send the data to the real parallel port (e.g. "/dev/lp0" on Linux, "lpt1" on
+# win32 platforms).
+#
+# Examples:
+# parport1: enabled=1, file="parport.out"
+# parport2: enabled=1, file="/dev/lp0"
+# parport1: enabled=0
+#=======================================================================
+parport1: enabled=1, file="parport.out"
+
+#=======================================================================
+# SB16:
+# This defines the SB16 sound emulation. It can have several of the
+# following properties.
+# All properties are in the format sb16: property=value
+# midi: The filename is where the midi data is sent. This can be a
+# device or just a file if you want to record the midi data.
+# midimode:
+# 0=no data
+# 1=output to device (system dependent. midi denotes the device driver)
+# 2=SMF file output, including headers
+# 3=output the midi data stream to the file (no midi headers and no
+# delta times, just command and data bytes)
+# wave: This is the device/file where wave output is stored
+# wavemode:
+# 0=no data
+# 1=output to device (system dependent. wave denotes the device driver)
+# 2=VOC file output, incl. headers
+# 3=output the raw wave stream to the file
+# log: The file to write the sb16 emulator messages to.
+# loglevel:
+# 0=no log
+# 1=resource changes, midi program and bank changes
+# 2=severe errors
+# 3=all errors
+# 4=all errors plus all port accesses
+# 5=all errors and port accesses plus a lot of extra info
+# dmatimer:
+# microseconds per second for a DMA cycle. Make it smaller to fix
+# non-continuous sound. 750000 is usually a good value. This needs a
+# reasonably correct setting for the IPS parameter of the CPU option.
+#
+# For an example look at the next line:
+#=======================================================================
+
+#sb16: midimode=1, midi=/dev/midi00, wavemode=1, wave=/dev/dsp, loglevel=2, log=sb16.log, dmatimer=600000
+
+#=======================================================================
+# VGA_UPDATE_INTERVAL:
+# Video memory is scanned for updates and screen updated every so many
+# virtual seconds. The default is 40000, about 25Hz. Keep in mind that
+# you must tweak the 'cpu: ips=N' directive to be as close to the number
+# of emulated instructions-per-second your workstation can do, for this
+# to be accurate.
+#
+# Examples:
+# vga_update_interval: 250000
+#=======================================================================
+vga_update_interval: 300000
+
+# using for Winstone '98 tests
+#vga_update_interval: 100000
+
+#=======================================================================
+# KEYBOARD_SERIAL_DELAY:
+# Approximate time in microseconds that it takes one character to
+# be transfered from the keyboard to controller over the serial path.
+# Examples:
+# keyboard_serial_delay: 200
+#=======================================================================
+keyboard_serial_delay: 250
+
+#=======================================================================
+# KEYBOARD_PASTE_DELAY:
+# Approximate time in microseconds between attempts to paste
+# characters to the keyboard controller. This leaves time for the
+# guest os to deal with the flow of characters. The ideal setting
+# depends on how your operating system processes characters. The
+# default of 100000 usec (.1 seconds) was chosen because it works
+# consistently in Windows.
+#
+# If your OS is losing characters during a paste, increase the paste
+# delay until it stops losing characters.
+#
+# Examples:
+# keyboard_paste_delay: 100000
+#=======================================================================
+keyboard_paste_delay: 100000
+
+#=======================================================================
+# MOUSE:
+# This option prevents Bochs from creating mouse "events" unless a mouse
+# is enabled. The hardware emulation itself is not disabled by this.
+# You can turn the mouse on by setting enabled to 1, or turn it off by
+# setting enabled to 0. Unless you have a particular reason for enabling
+# the mouse by default, it is recommended that you leave it off.
+# You can also toggle the mouse usage at runtime (control key + middle
+# mouse button on X11, SDL, wxWidgets and Win32).
+# With the mouse type option you can select the type of mouse to emulate.
+# The default value is 'ps2'. The other choices are 'imps2' (wheel mouse
+# on PS/2), 'serial', 'serial_wheel' (one com port requires setting
+# 'mode=mouse') and 'usb' (3-button mouse - one of the USB ports must be
+# connected with the 'mouse' device - requires PCI and USB support).
+#
+# Examples:
+# mouse: enabled=1
+# mouse: enabled=1, type=imps2
+# mouse: enabled=1, type=serial
+# mouse: enabled=0
+#=======================================================================
+mouse: enabled=0
+
+#=======================================================================
+# private_colormap: Request that the GUI create and use it's own
+# non-shared colormap. This colormap will be used
+# when in the bochs window. If not enabled, a
+# shared colormap scheme may be used. Not implemented
+# on all GUI's.
+#
+# Examples:
+# private_colormap: enabled=1
+# private_colormap: enabled=0
+#=======================================================================
+private_colormap: enabled=0
+
+#=======================================================================
+# fullscreen: ONLY IMPLEMENTED ON AMIGA
+# Request that Bochs occupy the entire screen instead of a
+# window.
+#
+# Examples:
+# fullscreen: enabled=0
+# fullscreen: enabled=1
+#=======================================================================
+#fullscreen: enabled=0
+#screenmode: name="sample"
+
+#=======================================================================
+# ne2k: NE2000 compatible ethernet adapter
+#
+# Examples:
+# ne2k: ioaddr=IOADDR, irq=IRQ, mac=MACADDR, ethmod=MODULE, ethdev=DEVICE, script=SCRIPT
+#
+# ioaddr, irq: You probably won't need to change ioaddr and irq, unless there
+# are IRQ conflicts.
+#
+# mac: The MAC address MUST NOT match the address of any machine on the net.
+# Also, the first byte must be an even number (bit 0 set means a multicast
+# address), and you cannot use ff:ff:ff:ff:ff:ff because that's the broadcast
+# address. For the ethertap module, you must use fe:fd:00:00:00:01. There may
+# be other restrictions too. To be safe, just use the b0:c4... address.
+#
+# ethdev: The ethdev value is the name of the network interface on your host
+# platform. On UNIX machines, you can get the name by running ifconfig. On
+# Windows machines, you must run niclist to get the name of the ethdev.
+# Niclist source code is in misc/niclist.c and it is included in Windows
+# binary releases.
+#
+# script: The script value is optional, and is the name of a script that
+# is executed after bochs initialize the network interface. You can use
+# this script to configure this network interface, or enable masquerading.
+# This is mainly useful for the tun/tap devices that only exist during
+# Bochs execution. The network interface name is supplied to the script
+# as first parameter
+#
+# If you don't want to make connections to any physical networks,
+# you can use the following 'ethmod's to simulate a virtual network.
+# null: All packets are discarded, but logged to a few files.
+# arpback: ARP is simulated. Disabled by default.
+# vde: Virtual Distributed Ethernet
+# vnet: ARP, ICMP-echo(ping), DHCP and read/write TFTP are simulated.
+# The virtual host uses 192.168.10.1.
+# DHCP assigns 192.168.10.2 to the guest.
+# TFTP uses the ethdev value for the root directory and doesn't
+# overwrite files.
+#
+#=======================================================================
+# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=fbsd, ethdev=en0 #macosx
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=fbsd, ethdev=xl0
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=win32, ethdev=MYCARD
+# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0
+# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=tuntap, ethdev=/dev/net/tun0, script=./tunconfig
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=null, ethdev=eth0
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vde, ethdev="/tmp/vde.ctl"
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vnet, ethdev="c:/temp"
+pnic: mac=fe:fd:00:00:00:01, ethmod=tuntap, ethdev=/dev/net/tun:tap0
+#ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=tuntap, ethdev=/dev/net/tun:tap0
+
+#=======================================================================
+# KEYBOARD_MAPPING:
+# This enables a remap of a physical localized keyboard to a
+# virtualized us keyboard, as the PC architecture expects.
+# If enabled, the keymap file must be specified.
+#
+# Examples:
+# keyboard_mapping: enabled=1, map=gui/keymaps/x11-pc-de.map
+#=======================================================================
+keyboard_mapping: enabled=0, map=
+
+#=======================================================================
+# KEYBOARD_TYPE:
+# Type of keyboard return by a "identify keyboard" command to the
+# keyboard controler. It must be one of "xt", "at" or "mf".
+# Defaults to "mf". It should be ok for almost everybody. A known
+# exception is french macs, that do have a "at"-like keyboard.
+#
+# Examples:
+# keyboard_type: mf
+#=======================================================================
+#keyboard_type: mf
+
+#=======================================================================
+# USER_SHORTCUT:
+# This defines the keyboard shortcut to be sent when you press the "user"
+# button in the headerbar. The shortcut string is a combination of maximum
+# 3 key names (listed below) separated with a '-' character. The old-style
+# syntax (without the '-') still works for the key combinations supported
+# in Bochs 2.2.1.
+# Valid key names:
+# "alt", "bksl", "bksp", "ctrl", "del", "down", "end", "enter", "esc",
+# "f1", ... "f12", "home", "ins", "left", "menu", "minus", "pgdwn", "pgup",
+# "plus", "right", "shift", "space", "tab", "up", and "win".
+#
+# Example:
+# user_shortcut: keys=ctrl-alt-del
+#=======================================================================
+user_shortcut: keys=ctrl-alt-del
+
+#=======================================================================
+# I440FXSUPPORT:
+# This option controls the presence of the i440FX PCI chipset. You can
+# also specify the devices connected to PCI slots. Up to 5 slots are
+# available now. These devices are currently supported: ne2k, pcivga,
+# pcidev and pcipnic. If Bochs is compiled with Cirrus SVGA support
+# you'll have the additional choice 'cirrus'.
+#
+# Example:
+# i440fxsupport: enabled=1, slot1=pcivga, slot2=ne2k
+#=======================================================================
+i440fxsupport: enabled=1, slot1=pcipnic
+#i440fxsupport: enabled=1, slot1=ne2k
+
+#=======================================================================
+# USB1:
+# This option controls the presence of the USB root hub which is a part
+# of the i440FX PCI chipset. With the portX option you can connect devices
+# to the hub (currently supported: 'mouse' and 'keypad'). If you connect
+# the mouse to one of the ports and use the mouse option 'type=usb' you'll
+# have a 3-button USB mouse.
+#
+# Example:
+# usb1: enabled=1, port1=mouse, port2=keypad
+#=======================================================================
+#usb1: enabled=1
+
+#=======================================================================
+# CMOSIMAGE:
+# This defines image file that can be loaded into the CMOS RAM at startup.
+# The rtc_init parameter controls whether initialize the RTC with values stored
+# in the image. By default the time0 argument given to the clock option is used.
+# With 'rtc_init=image' the image is the source for the initial time.
+#
+# Example:
+# cmosimage: file=cmos.img, rtc_init=image
+#=======================================================================
+#cmosimage: file=cmos.img, rtc_init=time0
+
+#=======================================================================
+# other stuff
+#=======================================================================
+magic_break: enabled=1
+#load32bitOSImage: os=nullkernel, path=../kernel.img, iolog=../vga_io.log
+#load32bitOSImage: os=linux, path=../linux.img, iolog=../vga_io.log, initrd=../initrd.img
+#text_snapshot_check: enable
+
+#-------------------------
+# PCI host device mapping
+#-------------------------
+#pcidev: vendor=0x1234, device=0x5678
+
+#=======================================================================
+# GDBSTUB:
+# Enable GDB stub. See user documentation for details.
+# Default value is enabled=0.
+#=======================================================================
+#gdbstub: enabled=0, port=1234, text_base=0, data_base=0, bss_base=0
+
+#=======================================================================
+# IPS:
+# The IPS directive is DEPRECATED. Use the parameter IPS of the CPU
+# directive instead.
+#=======================================================================
+#ips: 10000000
+
+#=======================================================================
+# for Macintosh, use the style of pathnames in the following
+# examples.
+#
+# vgaromimage: :bios:VGABIOS-elpin-2.40
+# romimage: file=:bios:BIOS-bochs-latest, address=0xf0000
+# floppya: 1_44=[fd:], status=inserted
+#=======================================================================
diff --git a/gpxe/contrib/bochs/qemu-patch b/gpxe/contrib/bochs/qemu-patch
new file mode 100644
index 00000000..5fb6c09e
--- /dev/null
+++ b/gpxe/contrib/bochs/qemu-patch
@@ -0,0 +1,26 @@
+Index: qemu/cpu-exec.c
+===================================================================
+RCS file: /sources/qemu/qemu/cpu-exec.c,v
+retrieving revision 1.84
+diff -u -r1.84 cpu-exec.c
+--- qemu/cpu-exec.c 29 Jul 2006 19:09:31 -0000 1.84
++++ qemu/cpu-exec.c 28 Aug 2006 01:54:15 -0000
+@@ -788,6 +788,18 @@
+ cpu_loop_exit();
+ }
+ #endif
++#if 1
++#define MIN_CYCLE_COUNT 100
++ {
++ static int cycle_count;
++
++ if (++cycle_count > MIN_CYCLE_COUNT) {
++ cycle_count = 0;
++ env->exception_index = EXCP_INTERRUPT;
++ cpu_loop_exit();
++ }
++ }
++#endif
+ }
+ } else {
+ env_to_regs();
diff --git a/gpxe/contrib/bochs/serial-console b/gpxe/contrib/bochs/serial-console
new file mode 100755
index 00000000..cc4fd005
--- /dev/null
+++ b/gpxe/contrib/bochs/serial-console
@@ -0,0 +1,278 @@
+#!/usr/bin/perl -w
+
+=head1 NAME
+
+serial-console
+
+=head1 SYNOPSIS
+
+serial-console [options]
+
+Options:
+
+ -h,--help Display brief help message
+ -v,--verbose Increase verbosity
+ -q,--quiet Decrease verbosity
+ -l,--log FILE Log output to file
+ -r,--rcfile FILE Modify specified bochsrc file
+
+=head1 DESCRIPTION
+
+C<serial-console> provides a virtual serial console for use with
+Bochs. Running C<serial-console> creates a pseudo-tty. The master
+side of this pty is made available to the user for interaction; the
+slave device is written to the Bochs configuration file
+(C<bochsrc.txt>) for use by a subsequent Bochs session.
+
+=head1 EXAMPLES
+
+=over 4
+
+=item C<serial-console>
+
+Create a virtual serial console for Bochs, modify C<bochsrc.txt>
+appropriately.
+
+=item C<serial-console -r ../.bochsrc -l serial.log>
+
+Create a virtual serial console for Bochs, modify C<../.bochsrc>
+appropriately, log output to C<serial.log>.
+
+=back
+
+=head1 INVOCATION
+
+Before starting Bochs, run C<serial-console> in a different session
+(e.g. a different xterm window). When you subsequently start Bochs,
+anything that the emulated machine writes to its serial port will
+appear in the window running C<serial-console>, and anything typed in
+the C<serial-console> window will arrive on the emulated machine's
+serial port.
+
+You do B<not> need to rerun C<serial-console> afresh for each Bochs
+session.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-l,--log FILE>
+
+Log all output (i.e. everything that is printed in the
+C<serial-console> window) to the specified file.
+
+=item B<-r,--rcfile FILE>
+
+Modify the specified bochsrc file. The file will be updated to
+contain the path to the slave side of the psuedo tty that we create.
+The original file will be restored when C<serial-console> exits. The
+default is to modify the file C<bochsrc.txt> in the current directory.
+
+To avoid modifying any bochsrc file, use C<--norcfile>.
+
+=back
+
+=cut
+
+use IO::Pty;
+use IO::Select;
+use File::Spec::Functions qw ( :ALL );
+use Getopt::Long;
+use Pod::Usage;
+use POSIX qw ( :termios_h );
+use strict;
+use warnings;
+
+my $o;
+my $restore_file = {};
+my $restore_termios;
+use constant BLOCKSIZE => 8192;
+
+##############################################################################
+#
+# Parse command line options into options hash ($o)
+#
+# $o = parse_opts();
+
+sub parse_opts {
+ # $o is the hash that will hold the options
+ my $o = {
+ verbosity => 1,
+ rcfile => 'bochsrc.txt',
+ };
+ # Special handlers for some options
+ my $opt_handlers = {
+ verbose => sub { $o->{verbosity}++; },
+ quiet => sub { $o->{verbosity}--; },
+ help => sub { pod2usage(1); },
+ norcfile => sub { delete $o->{rcfile}; },
+ };
+ # Merge handlers into main options hash (so that Getopt::Long can find them)
+ $o->{$_} = $opt_handlers->{$_} foreach keys %$opt_handlers;
+ # Option specifiers for Getopt::Long
+ my @optspec = ( 'help|h|?',
+ 'quiet|q+',
+ 'verbose|v+',
+ 'log|l=s',
+ 'rcfile|r=s',
+ 'norcfile',
+ );
+ # Do option parsing
+ Getopt::Long::Configure ( 'bundling' );
+ pod2usage("Error parsing command-line options") unless GetOptions (
+ $o, @optspec );
+ # Clean up $o by removing the handlers
+ delete $o->{$_} foreach keys %$opt_handlers;
+ return $o;
+}
+
+##############################################################################
+#
+# Modify bochsrc file
+
+sub patch_bochsrc {
+ my $active = shift;
+ my $pty = shift;
+
+ # Rename active file to backup file
+ ( my $vol, my $dir, my $file ) = splitpath ( $active );
+ $file = '.'.$file.".serial-console";
+ my $backup = catpath ( $vol, $dir, $file );
+ rename $active, $backup
+ or die "Could not back up $active to $backup: $!\n";
+
+ # Derive line to be inserted
+ my $patch = "com1: enabled=1, mode=term, dev=$pty\n";
+
+ # Modify file
+ open my $old, "<$backup" or die "Could not open $backup: $!\n";
+ open my $new, ">$active" or die "Could not open $active: $!\n";
+ print $new <<"EOF";
+##################################################
+#
+# This file has been modified by serial-console.
+#
+# Do not modify this file; it will be erased when
+# serial-console (pid $$) exits and will be
+# replaced with the backup copy held in
+# $backup.
+#
+##################################################
+
+
+EOF
+ my $patched;
+ while ( my $line = <$old> ) {
+ if ( $line =~ /^\s*\#?\s*com1:\s*\S/ ) {
+ if ( ! $patched ) {
+ $line = $patch;
+ $patched = 1;
+ } else {
+ $line = '# '.$line unless $line =~ /^\s*\#/;
+ }
+ }
+ print $new $line;
+ }
+ print $new $patch unless $patched;
+ close $old;
+ close $new;
+
+ return $backup;
+}
+
+##############################################################################
+#
+# Attach/detach message printing and terminal settings
+
+sub bochs_attached {
+ print STDERR "Bochs attached.\n\n\n"
+ if $o->{verbosity} >= 1;
+}
+
+sub bochs_detached {
+ print STDERR "\n\nWaiting for bochs to attach...\n"
+ if $o->{verbosity} >= 1;
+}
+
+##############################################################################
+#
+# Main program
+
+$o = parse_opts();
+pod2usage(1) if @ARGV;
+
+# Catch signals
+my $sigdie = sub { die "Exiting via signal\n"; };
+$SIG{INT} = $sigdie;
+
+# Create Pty, close slave side
+my $pty = IO::Pty->new();
+$pty->close_slave();
+$pty->set_raw();
+print STDERR "Slave pty is ".$pty->ttyname."\n" if $o->{verbosity} >= 1;
+
+# Open logfile
+my $log;
+if ( $o->{log} ) {
+ open $log, ">$o->{log}" or die "Could not open $o->{log}: $!\n";
+}
+
+# Set up terminal
+my $termios;
+if ( -t STDIN ) {
+ $termios = POSIX::Termios->new;
+ $restore_termios = POSIX::Termios->new;
+ $termios->getattr ( fileno(STDIN) );
+ $restore_termios->getattr ( fileno(STDIN) );
+ $termios->setlflag ( $termios->getlflag &
+ ~(ICANON) & ~(ECHO) );
+ $termios->setattr ( fileno(STDIN), TCSANOW );
+}
+
+# Modify bochsrc file
+$restore_file = { $o->{rcfile} =>
+ patch_bochsrc ( $o->{rcfile}, $pty->ttyname ) }
+ if $o->{rcfile};
+
+# Start character shunt
+my $attached = 1;
+my $select = IO::Select->new ( \*STDIN, $pty );
+while ( 1 ) {
+ my %can_read = map { $_ => 1 }
+ $select->can_read ( $attached ? undef : 1 );
+ if ( $can_read{\*STDIN} ) {
+ sysread ( STDIN, my $data, BLOCKSIZE )
+ or die "Cannot read from STDIN: $!\n";
+ $pty->syswrite ( $data );
+ }
+ if ( $can_read{$pty} ) {
+ if ( $pty->sysread ( my $data, BLOCKSIZE ) ) {
+ # Actual data available
+ bochs_attached() if $attached == 0;
+ $attached = 1;
+ syswrite ( STDOUT, $data );
+ $log->syswrite ( $data ) if $log;
+ } else {
+ # No data available but select() says we can read. This almost
+ # certainly indicates that nothing is attached to the slave.
+ bochs_detached() if $attached == 1;
+ $attached = 0;
+ sleep ( 1 );
+ }
+ } else {
+ bochs_attached() if $attached == 0;
+ $attached = 1;
+ }
+}
+
+END {
+ # Restore bochsrc file if applicable
+ if ( ( my $orig_file, my $backup_file ) = %$restore_file ) {
+ unlink $orig_file;
+ rename $backup_file, $orig_file;
+ }
+ # Restore terminal settings if applicable
+ if ( $restore_termios ) {
+ $restore_termios->setattr ( fileno(STDIN), TCSANOW );
+ }
+}
diff --git a/gpxe/contrib/bochs/serial-console.1 b/gpxe/contrib/bochs/serial-console.1
new file mode 100644
index 00000000..210de550
--- /dev/null
+++ b/gpxe/contrib/bochs/serial-console.1
@@ -0,0 +1,191 @@
+.\" Automatically generated by Pod::Man v1.34, Pod::Parser v1.13
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. | will give a
+.\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to
+.\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C'
+.\" expand to `' in nroff, nothing in troff, for use with C<>.
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.if \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. nr % 0
+. rr F
+.\}
+.\"
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "SERIAL-CONSOLE 1"
+.TH SERIAL-CONSOLE 1 "2004-03-10" "perl v5.8.0" "User Contributed Perl Documentation"
+.SH "NAME"
+serial\-console
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+serial-console [options]
+.PP
+Options:
+.PP
+.Vb 5
+\& -h,--help Display brief help message
+\& -v,--verbose Increase verbosity
+\& -q,--quiet Decrease verbosity
+\& -l,--log FILE Log output to file
+\& -r,--rcfile FILE Modify specified bochsrc file
+.Ve
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+\&\f(CW\*(C`serial\-console\*(C'\fR provides a virtual serial console for use with
+Bochs. Running \f(CW\*(C`serial\-console\*(C'\fR creates a pseudo\-tty. The master
+side of this pty is made available to the user for interaction; the
+slave device is written to the Bochs configuration file
+(\f(CW\*(C`bochsrc.txt\*(C'\fR) for use by a subsequent Bochs session.
+.SH "EXAMPLES"
+.IX Header "EXAMPLES"
+.ie n .IP """serial\-console""" 4
+.el .IP "\f(CWserial\-console\fR" 4
+.IX Item "serial-console"
+Create a virtual serial console for Bochs, modify \f(CW\*(C`bochsrc.txt\*(C'\fR
+appropriately.
+.ie n .IP """serial\-console \-r ../.bochsrc \-l serial.log""" 4
+.el .IP "\f(CWserial\-console \-r ../.bochsrc \-l serial.log\fR" 4
+.IX Item "serial-console -r ../.bochsrc -l serial.log"
+Create a virtual serial console for Bochs, modify \f(CW\*(C`../.bochsrc\*(C'\fR
+appropriately, log output to \f(CW\*(C`serial.log\*(C'\fR.
+.SH "INVOCATION"
+.IX Header "INVOCATION"
+Before starting Bochs, run \f(CW\*(C`serial\-console\*(C'\fR in a different session
+(e.g. a different xterm window). When you subsequently start Bochs,
+anything that the emulated machine writes to its serial port will
+appear in the window running \f(CW\*(C`serial\-console\*(C'\fR, and anything typed in
+the \f(CW\*(C`serial\-console\*(C'\fR window will arrive on the emulated machine's
+serial port.
+.PP
+You do \fBnot\fR need to rerun \f(CW\*(C`serial\-console\*(C'\fR afresh for each Bochs
+session.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.IP "\fB\-l,\-\-log \s-1FILE\s0\fR" 4
+.IX Item "-l,--log FILE"
+Log all output (i.e. everything that is printed in the
+\&\f(CW\*(C`serial\-console\*(C'\fR window) to the specified file.
+.IP "\fB\-r,\-\-rcfile \s-1FILE\s0\fR" 4
+.IX Item "-r,--rcfile FILE"
+Modify the specified bochsrc file. The file will be updated to
+contain the path to the slave side of the psuedo tty that we create.
+The original file will be restored when \f(CW\*(C`serial\-console\*(C'\fR exits. The
+default is to modify the file \f(CW\*(C`bochsrc.txt\*(C'\fR in the current directory.
+.Sp
+To avoid modifying any bochsrc file, use \f(CW\*(C`\-\-norcfile\*(C'\fR.
diff --git a/gpxe/contrib/bochs/tunctl.c b/gpxe/contrib/bochs/tunctl.c
new file mode 100644
index 00000000..6e439060
--- /dev/null
+++ b/gpxe/contrib/bochs/tunctl.c
@@ -0,0 +1,113 @@
+/* Copyright 2002 Jeff Dike
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <linux/if_tun.h>
+
+static void Usage(char *name)
+{
+ fprintf(stderr, "Create: %s [-b] [-u owner] [-t device-name] "
+ "[-f tun-clone-device]\n", name);
+ fprintf(stderr, "Delete: %s -d device-name [-f tun-clone-device]\n\n",
+ name);
+ fprintf(stderr, "The default tun clone device is /dev/net/tun - some systems"
+ " use\n/dev/misc/net/tun instead\n\n");
+ fprintf(stderr, "-b will result in brief output (just the device name)\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ struct ifreq ifr;
+ struct passwd *pw;
+ long owner = geteuid();
+ int tap_fd, opt, delete = 0, brief = 0;
+ char *tun = "", *file = "/dev/net/tun", *name = argv[0], *end;
+
+ while((opt = getopt(argc, argv, "bd:f:t:u:")) > 0){
+ switch(opt) {
+ case 'b':
+ brief = 1;
+ break;
+ case 'd':
+ delete = 1;
+ tun = optarg;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case 'u':
+ pw = getpwnam(optarg);
+ if(pw != NULL){
+ owner = pw->pw_uid;
+ break;
+ }
+ owner = strtol(optarg, &end, 0);
+ if(*end != '\0'){
+ fprintf(stderr, "'%s' is neither a username nor a numeric uid.\n",
+ optarg);
+ Usage(name);
+ }
+ break;
+ case 't':
+ tun = optarg;
+ break;
+ case 'h':
+ default:
+ Usage(name);
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if(argc > 0)
+ Usage(name);
+
+ if((tap_fd = open(file, O_RDWR)) < 0){
+ fprintf(stderr, "Failed to open '%s' : ", file);
+ perror("");
+ exit(1);
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ strncpy(ifr.ifr_name, tun, sizeof(ifr.ifr_name) - 1);
+ if(ioctl(tap_fd, TUNSETIFF, (void *) &ifr) < 0){
+ perror("TUNSETIFF");
+ exit(1);
+ }
+
+ if(delete){
+ if(ioctl(tap_fd, TUNSETPERSIST, 0) < 0){
+ perror("TUNSETPERSIST");
+ exit(1);
+ }
+ printf("Set '%s' nonpersistent\n", ifr.ifr_name);
+ }
+ else {
+ if(ioctl(tap_fd, TUNSETPERSIST, 1) < 0){
+ perror("TUNSETPERSIST");
+ exit(1);
+ }
+ if(ioctl(tap_fd, TUNSETOWNER, owner) < 0){
+ perror("TUNSETPERSIST");
+ exit(1);
+ }
+ if(brief)
+ printf("%s\n", ifr.ifr_name);
+ else printf("Set '%s' persistent and owned by uid %ld\n", ifr.ifr_name,
+ owner);
+ }
+ return(0);
+}
diff --git a/gpxe/contrib/bootptodhcp/bootptodhcp.pl b/gpxe/contrib/bootptodhcp/bootptodhcp.pl
new file mode 100755
index 00000000..c8d6465a
--- /dev/null
+++ b/gpxe/contrib/bootptodhcp/bootptodhcp.pl
@@ -0,0 +1,42 @@
+#!/usr/bin/perl -w
+#
+# Quick hack to convert /etc/bootptab to format required by ISC DHCPD
+# This only outputs the fixed hosts portion of the config file
+# You still have to provide the global options and the subnet scoping
+#
+# Turn $useipaddr on if you prefer to use IP addresses in the config file
+# I run DNS so I prefer domain names
+$useipaddr = 0;
+# This will be appended to get the FQDN unless the hostname is already FQDN
+$domainname = "ken.com.au";
+$tftpdir = "/tftpdir/";
+open(B, "/etc/bootptab") or die "/etc/bootptab: $!\n";
+while(<B>) {
+ if (/^[^a-z]/) {
+ $prevline = $_;
+ next;
+ }
+ chomp($_);
+ ($hostname, @tags) = split(/:/, $_, 5);
+ ($fqdn = $hostname) .= ".$domainname" unless($hostname =~ /\./);
+ ($macaddr) = grep(/^ha=/, @tags);
+ $macaddr =~ s/ha=//;
+ $macaddr =~ s/(..)(..)(..)(..)(..)(..)/$1:$2:$3:$4:$5:$6/g;
+ ($ipaddr) = grep(/^ip=/, @tags);
+ $ipaddr =~ s/ip=//;
+ ($bootfile) = grep(/^bf=/, @tags);
+ $bootfile =~ s/bf=//;
+ $bootfile = $tftpdir . $bootfile;
+# I have a comment line above most entries and I like to carry this over
+ print $prevline if ($prevline =~ /^#/);
+ $address = $useipaddr ? $ipaddr : $fqdn;
+ print <<EOF
+ host $hostname {
+ hardware ethernet $macaddr;
+ fixed-address $address;
+ filename "$bootfile";
+ }
+EOF
+;
+ $prevline = $_;
+}
diff --git a/gpxe/contrib/compressor/COPYING b/gpxe/contrib/compressor/COPYING
new file mode 100644
index 00000000..e574f7cd
--- /dev/null
+++ b/gpxe/contrib/compressor/COPYING
@@ -0,0 +1,23 @@
+The compression code as implemented in "lzhuf.c" was taken from a BBS
+program written by Joachim Schurig <jschurig@zedat.fu-berlin.de>. He
+states that the code can be used freely for programs that are covered
+by a "freeware" license. This probably includes both BSD style
+licenses and the GPL.
+
+The code in "loader.asm" is a reimplementation of the uncompressor. It
+has been written from scratch and is hereby placed under the
+conditions of the GNU General Public License (GPL). The algorithm is
+outlined in "algorithm.doc".
+
+Thus, there are no copyright problems with using this code, but there
+still might be difficulties with software patents. These patents are
+not legal in most parts of the world, but if you live in a country
+that honors software patents then you should verify that using these
+algorithms is legally permitted. Unless you are absolutely sure, that
+there are no legal obstacles, you should use the code for educational
+purposes only (this assumes that your educational institution is
+exempted from patent laws). The author cannot be held responsible for
+using the program code in violation of applicable local laws.
+
+If you are aware of patents that might affect the legality of using
+the code in some parts of the world, please let me know.
diff --git a/gpxe/contrib/compressor/algorithm.doc b/gpxe/contrib/compressor/algorithm.doc
new file mode 100644
index 00000000..74a7646c
--- /dev/null
+++ b/gpxe/contrib/compressor/algorithm.doc
@@ -0,0 +1,58 @@
+The compressor achieves an average compression rate of 60% of the
+original size which is on par with "gzip". It seems that you cannot do
+much better for compressing compiled binaries. This means that the
+break even point for using compressed images is reached, once the
+uncompressed size approaches 1.5kB. We can stuff more than 12kB into
+an 8kB EPROM and more than 25kB into an 16kB EPROM. As there is only
+32kB of RAM for both the uncompressed image and its BSS area, this
+means that 32kB EPROMs will hardly ever be required.
+
+The compression algorithm uses a 4kB ring buffer for buffering the
+uncompressed data. Before compression starts, the ring buffer is
+filled with spaces (ASCII character 0x20). The algorithm tries to
+find repeated input sequences of a maximum length of 60 bytes. All
+256 different input bytes plus the 58 (60 minus a threshold of 2)
+possible repeat lengths form a set of 314 symbols. These symbols are
+adaptively Huffman encoded. The algorithm starts out with a Huffmann
+tree that assigns equal code lengths to each of the 314 symbols
+(slightly favoring the repeat symbols over symbols for regular input
+characters), but it will be changed whenever the frequency of any of
+the symbols changes. Frequency counts are kept in 16bit words until
+the total number of compressed codes totals 2^15. Then, all frequency
+counts will be halfed (rounding to the bigger number). For unrepeated
+characters (symbols 0..255) the Huffman code is written to the output
+stream. For repeated characters the Huffmann code, which denotes the
+length of the repeated character sequence, is written out and then the
+index in the ring buffer is computed. From this index, the algorithm
+computes the offset relative to the current index into the ring
+buffer. Thus, for typical input data, one would expect that short to
+medium range offsets are more frequent than extremely short or medium
+range to long range offsets. Thus the 12bit (for a 4kB buffer) offset
+value is statically Huffman encoded using a precomputed Huffman tree
+that favors those offset values that are deemed to be more
+frequent. The Huffman encoded offset is written to the output data
+stream, directly following the code that determines the length of
+repeated characters.
+
+This algorithm, as implemented in the C example code, looks very good
+and its operating parameters are already well optimized. This also
+explains why it achieves compression ratios comparable with
+"gzip". Depending on the input data, it sometimes excells considerably
+beyond what "gzip -9" does, but this phenomenon does not appear to be
+typical. There are some flaws with the algorithm, such as the limited
+buffer sizes, the adaptive Huffman tree which takes very long to
+change, if the input characters experience a sudden change in
+distribution, and the static Huffman tree for encoding offsets into
+the buffer. The slow changes of the adaptive Huffman tree are
+partially counteracted by artifically keeping a 16bit precision for
+the frequency counts, but this does not come into play until 32kB of
+compressed data is output, so it does not have any impact on our use
+for "etherboot", because the BOOT Prom does not support uncompressed
+data of more then 32kB (c.f. doc/spec.doc).
+
+Nonetheless, these problems do not seem to affect compression of
+compiled programs very much. Mixing object code with English text,
+would not work too well though, and the algorithm should be reset in
+between. Actually, we might gain a little improvement, if text and
+data segments were compressed individually, but I have not
+experimented with this option, yet.
diff --git a/gpxe/contrib/compressor/loader.h b/gpxe/contrib/compressor/loader.h
new file mode 100644
index 00000000..20fa9af3
--- /dev/null
+++ b/gpxe/contrib/compressor/loader.h
@@ -0,0 +1,14 @@
+/* Do not change these values unless you really know what you are doing;
+ the pre-computed lookup tables rely on the buffer size being 4kB or
+ smaller. The buffer size must be a power of two. The lookahead size has
+ to fit into 6 bits. If you change any of these numbers, you will also
+ have to adjust the decompressor accordingly.
+ */
+
+#define BUFSZ 4096
+#define LOOKAHEAD 60
+#define THRESHOLD 2
+#define NCHAR (256+LOOKAHEAD-THRESHOLD)
+#define TABLESZ (NCHAR+NCHAR-1)
+#define NIL ((unsigned short)-1)
+
diff --git a/gpxe/contrib/compressor/lzhuf.c b/gpxe/contrib/compressor/lzhuf.c
new file mode 100644
index 00000000..ea65b5e0
--- /dev/null
+++ b/gpxe/contrib/compressor/lzhuf.c
@@ -0,0 +1,764 @@
+/*
+----------------------------------------------------------------------------
+
+M. LZHuf Compression
+
+This is the LZHuf compression algorithm as used in DPBOX and F6FBB.
+
+----------------------------------------------------------------------------
+*/
+/**************************************************************
+ lzhuf.c
+ written by Haruyasu Yoshizaki 11/20/1988
+ some minor changes 4/6/1989
+ comments translated by Haruhiko Okumura 4/7/1989
+
+ minor beautifications and adjustments for compiling under Linux
+ by Markus Gutschke <gutschk@math.uni-muenster.de>
+ 1997-01-27
+
+ Modifications to allow use as a filter by Ken Yap <ken_yap@users.sourceforge.net>.
+ 1997-07-01
+
+ Small mod to cope with running on big-endian machines
+ by Jim Hague <jim.hague@acm.org)
+ 1998-02-06
+
+ Make compression statistics report shorter
+ by Ken Yap <ken_yap@users.sourceforge.net>.
+ 2001-04-25
+**************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#ifndef VERBOSE
+#define Fprintf(x)
+#define wterr 0
+#else
+#define Fprintf(x) fprintf x
+#if defined(ENCODE) || defined(DECODE)
+static char wterr[] = "Can't write.";
+#ifdef ENCODE
+static unsigned long int codesize = 0;
+#endif
+static unsigned long int printcount = 0;
+#endif
+#endif
+
+#ifndef MAIN
+extern
+#endif
+FILE *infile, *outfile;
+
+#if defined(ENCODE) || defined(DECODE)
+static unsigned long int textsize = 0;
+
+static __inline__ void Error(char *message)
+{
+ Fprintf((stderr, "\n%s\n", message));
+ exit(EXIT_FAILURE);
+}
+
+/* These will be a complete waste of time on a lo-endian */
+/* system, but it only gets done once so WTF. */
+static unsigned long i86ul_to_host(unsigned long ul)
+{
+ unsigned long res = 0;
+ int i;
+ union
+ {
+ unsigned char c[4];
+ unsigned long ul;
+ } u;
+
+ u.ul = ul;
+ for (i = 3; i >= 0; i--)
+ res = (res << 8) + u.c[i];
+ return res;
+}
+
+static unsigned long host_to_i86ul(unsigned long ul)
+{
+ int i;
+ union
+ {
+ unsigned char c[4];
+ unsigned long ul;
+ } u;
+
+ for (i = 0; i < 4; i++)
+ {
+ u.c[i] = ul & 0xff;
+ ul >>= 8;
+ }
+ return u.ul;
+}
+#endif
+
+/********** LZSS compression **********/
+
+#define N 4096 /* buffer size */
+/* Attention: When using this file for f6fbb-type compressed data exchange,
+ set N to 2048 ! (DL8HBS) */
+#define F 60 /* lookahead buffer size */
+#define THRESHOLD 2
+#define NIL N /* leaf of tree */
+
+#if defined(ENCODE) || defined(DECODE)
+static unsigned char
+ text_buf[N + F - 1];
+#endif
+
+#ifdef ENCODE
+static int match_position, match_length,
+ lson[N + 1], rson[N + 257], dad[N + 1];
+
+static void InitTree(void) /* initialize trees */
+{
+ int i;
+
+ for (i = N + 1; i <= N + 256; i++)
+ rson[i] = NIL; /* root */
+ for (i = 0; i < N; i++)
+ dad[i] = NIL; /* node */
+}
+
+static void InsertNode(int r) /* insert to tree */
+{
+ int i, p, cmp;
+ unsigned char *key;
+ unsigned c;
+
+ cmp = 1;
+ key = &text_buf[r];
+ p = N + 1 + key[0];
+ rson[r] = lson[r] = NIL;
+ match_length = 0;
+ for ( ; ; ) {
+ if (cmp >= 0) {
+ if (rson[p] != NIL)
+ p = rson[p];
+ else {
+ rson[p] = r;
+ dad[r] = p;
+ return;
+ }
+ } else {
+ if (lson[p] != NIL)
+ p = lson[p];
+ else {
+ lson[p] = r;
+ dad[r] = p;
+ return;
+ }
+ }
+ for (i = 1; i < F; i++)
+ if ((cmp = key[i] - text_buf[p + i]) != 0)
+ break;
+ if (i > THRESHOLD) {
+ if (i > match_length) {
+ match_position = ((r - p) & (N - 1)) - 1;
+ if ((match_length = i) >= F)
+ break;
+ }
+ if (i == match_length) {
+ if ((c = ((r - p) & (N - 1)) - 1) < match_position) {
+ match_position = c;
+ }
+ }
+ }
+ }
+ dad[r] = dad[p];
+ lson[r] = lson[p];
+ rson[r] = rson[p];
+ dad[lson[p]] = r;
+ dad[rson[p]] = r;
+ if (rson[dad[p]] == p)
+ rson[dad[p]] = r;
+ else
+ lson[dad[p]] = r;
+ dad[p] = NIL; /* remove p */
+}
+
+static void DeleteNode(int p) /* remove from tree */
+{
+ int q;
+
+ if (dad[p] == NIL)
+ return; /* not registered */
+ if (rson[p] == NIL)
+ q = lson[p];
+ else
+ if (lson[p] == NIL)
+ q = rson[p];
+ else {
+ q = lson[p];
+ if (rson[q] != NIL) {
+ do {
+ q = rson[q];
+ } while (rson[q] != NIL);
+ rson[dad[q]] = lson[q];
+ dad[lson[q]] = dad[q];
+ lson[q] = lson[p];
+ dad[lson[p]] = q;
+ }
+ rson[q] = rson[p];
+ dad[rson[p]] = q;
+ }
+ dad[q] = dad[p];
+ if (rson[dad[p]] == p)
+ rson[dad[p]] = q;
+ else
+ lson[dad[p]] = q;
+ dad[p] = NIL;
+}
+#endif
+
+/* Huffman coding */
+
+#define N_CHAR (256 - THRESHOLD + F)
+ /* kinds of characters (character code = 0..N_CHAR-1) */
+#define T (N_CHAR * 2 - 1) /* size of table */
+#define R (T - 1) /* position of root */
+#define MAX_FREQ 0x8000 /* updates tree when the */
+ /* root frequency comes to this value. */
+typedef unsigned char uchar;
+
+/* table for encoding and decoding the upper 6 bits of position */
+
+/* for encoding */
+
+#ifdef ENCODE
+static uchar p_len[64] = {
+ 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+};
+
+static uchar p_code[64] = {
+ 0x00, 0x20, 0x30, 0x40, 0x50, 0x58, 0x60, 0x68,
+ 0x70, 0x78, 0x80, 0x88, 0x90, 0x94, 0x98, 0x9C,
+ 0xA0, 0xA4, 0xA8, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC,
+ 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE,
+ 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE,
+ 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
+};
+#endif
+
+#ifdef DECODE
+/* for decoding */
+static uchar d_code[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,
+ 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
+ 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
+ 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
+ 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B,
+ 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F,
+ 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,
+ 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
+ 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B,
+ 0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+};
+
+static uchar d_len[256] = {
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+};
+#endif
+
+#if defined(ENCODE) || defined(DECODE)
+static unsigned freq[T + 1]; /* frequency table */
+
+static int prnt[T + N_CHAR]; /* pointers to parent nodes, except for the */
+ /* elements [T..T + N_CHAR - 1] which are used to get */
+ /* the positions of leaves corresponding to the codes. */
+
+static int son[T]; /* pointers to child nodes (son[], son[] + 1) */
+#endif
+
+#ifdef DECODE
+static unsigned getbuf = 0;
+static uchar getlen = 0;
+
+static int GetBit(void) /* get one bit */
+{
+ int i;
+
+ while (getlen <= 8) {
+ if ((i = getc(infile)) < 0) i = 0;
+ getbuf |= i << (8 - getlen);
+ getlen += 8;
+ }
+ i = getbuf;
+ getbuf <<= 1;
+ getlen--;
+ return ((signed short)i < 0);
+}
+
+static int GetByte(void) /* get one byte */
+{
+ unsigned short i;
+
+ while (getlen <= 8) {
+ if ((signed short)(i = getc(infile)) < 0) i = 0;
+ getbuf |= i << (8 - getlen);
+ getlen += 8;
+ }
+ i = getbuf;
+ getbuf <<= 8;
+ getlen -= 8;
+ return i >> 8;
+}
+#endif
+
+#ifdef ENCODE
+static unsigned putbuf = 0;
+static uchar putlen = 0;
+
+static void Putcode(int l, unsigned c) /* output c bits of code */
+{
+ putbuf |= c >> putlen;
+ if ((putlen += l) >= 8) {
+ if (putc(putbuf >> 8, outfile) == EOF) {
+ Error(wterr);
+ }
+ if ((putlen -= 8) >= 8) {
+ if (putc(putbuf, outfile) == EOF) {
+ Error(wterr);
+ }
+#ifdef VERBOSE
+ codesize += 2;
+#endif
+ putlen -= 8;
+ putbuf = c << (l - putlen);
+ } else {
+ putbuf <<= 8;
+#ifdef VERBOSE
+ codesize++;
+#endif
+ }
+ }
+}
+#endif
+
+/* initialization of tree */
+
+#if defined(ENCODE) || defined(DECODE)
+static void StartHuff(void)
+{
+ int i, j;
+
+ for (i = 0; i < N_CHAR; i++) {
+ freq[i] = 1;
+ son[i] = i + T;
+ prnt[i + T] = i;
+ }
+ i = 0; j = N_CHAR;
+ while (j <= R) {
+ freq[j] = freq[i] + freq[i + 1];
+ son[j] = i;
+ prnt[i] = prnt[i + 1] = j;
+ i += 2; j++;
+ }
+ freq[T] = 0xffff;
+ prnt[R] = 0;
+}
+
+/* reconstruction of tree */
+
+static void reconst(void)
+{
+ int i, j, k;
+ unsigned f, l;
+
+ /* collect leaf nodes in the first half of the table */
+ /* and replace the freq by (freq + 1) / 2. */
+ j = 0;
+ for (i = 0; i < T; i++) {
+ if (son[i] >= T) {
+ freq[j] = (freq[i] + 1) / 2;
+ son[j] = son[i];
+ j++;
+ }
+ }
+ /* begin constructing tree by connecting sons */
+ for (i = 0, j = N_CHAR; j < T; i += 2, j++) {
+ k = i + 1;
+ f = freq[j] = freq[i] + freq[k];
+ for (k = j - 1; f < freq[k]; k--);
+ k++;
+ l = (j - k) * 2;
+ memmove(&freq[k + 1], &freq[k], l);
+ freq[k] = f;
+ memmove(&son[k + 1], &son[k], l);
+ son[k] = i;
+ }
+ /* connect prnt */
+ for (i = 0; i < T; i++) {
+ if ((k = son[i]) >= T) {
+ prnt[k] = i;
+ } else {
+ prnt[k] = prnt[k + 1] = i;
+ }
+ }
+}
+
+/* increment frequency of given code by one, and update tree */
+
+static void update(int c)
+{
+ int i, j, k, l;
+
+ if (freq[R] == MAX_FREQ) {
+ reconst();
+ }
+ c = prnt[c + T];
+ do {
+ k = ++freq[c];
+
+ /* if the order is disturbed, exchange nodes */
+ if (k > freq[l = c + 1]) {
+ while (k > freq[++l]);
+ l--;
+ freq[c] = freq[l];
+ freq[l] = k;
+
+ i = son[c];
+ prnt[i] = l;
+ if (i < T) prnt[i + 1] = l;
+
+ j = son[l];
+ son[l] = i;
+
+ prnt[j] = c;
+ if (j < T) prnt[j + 1] = c;
+ son[c] = j;
+
+ c = l;
+ }
+ } while ((c = prnt[c]) != 0); /* repeat up to root */
+}
+#endif
+
+#ifdef ENCODE
+#if 0
+static unsigned code, len;
+#endif
+
+static void EncodeChar(unsigned c)
+{
+ unsigned i;
+ int j, k;
+
+ i = 0;
+ j = 0;
+ k = prnt[c + T];
+
+ /* travel from leaf to root */
+ do {
+ i >>= 1;
+
+ /* if node's address is odd-numbered, choose bigger brother node */
+ if (k & 1) i += 0x8000;
+
+ j++;
+ } while ((k = prnt[k]) != R);
+ Putcode(j, i);
+#if 0
+ code = i;
+ len = j;
+#endif
+ update(c);
+}
+
+static void EncodePosition(unsigned c)
+{
+ unsigned i;
+
+ /* output upper 6 bits by table lookup */
+ i = c >> 6;
+ Putcode(p_len[i], (unsigned)p_code[i] << 8);
+
+ /* output lower 6 bits verbatim */
+ Putcode(6, (c & 0x3f) << 10);
+}
+
+static void EncodeEnd(void)
+{
+ if (putlen) {
+ if (putc(putbuf >> 8, outfile) == EOF) {
+ Error(wterr);
+ }
+#ifdef VERBOSE
+ codesize++;
+#endif
+ }
+}
+#endif
+
+#ifdef DECODE
+static int DecodeChar(void)
+{
+ unsigned c;
+
+ c = son[R];
+
+ /* travel from root to leaf, */
+ /* choosing the smaller child node (son[]) if the read bit is 0, */
+ /* the bigger (son[]+1} if 1 */
+ while (c < T) {
+ c += GetBit();
+ c = son[c];
+ }
+ c -= T;
+ update(c);
+ return c;
+}
+
+static int DecodePosition(void)
+{
+ unsigned i, j, c;
+
+ /* recover upper 6 bits from table */
+ i = GetByte();
+ c = (unsigned)d_code[i] << 6;
+ j = d_len[i];
+
+ /* read lower 6 bits verbatim */
+ j -= 2;
+ while (j--) {
+ i = (i << 1) + GetBit();
+ }
+ return c | (i & 0x3f);
+}
+#endif
+
+#ifdef ENCODE
+/* compression */
+
+void Encode(void) /* compression */
+{
+ int i, c, len, r, s, last_match_length;
+ unsigned long tw;
+
+ fseek(infile, 0L, 2);
+ textsize = ftell(infile);
+#ifdef VERBOSE
+ if ((signed long)textsize < 0)
+ Fprintf((stderr, "Errno: %d", errno));
+#endif
+ tw = host_to_i86ul(textsize);
+ if (fwrite(&tw, sizeof tw, 1, outfile) < 1)
+ Error(wterr); /* output size of text */
+ if (textsize == 0)
+ return;
+ rewind(infile);
+ textsize = 0; /* rewind and re-read */
+ StartHuff();
+ InitTree();
+ s = 0;
+ r = N - F;
+ for (i = s; i < r; i++)
+ text_buf[i] = ' ';
+ for (len = 0; len < F && (c = getc(infile)) != EOF; len++)
+ text_buf[r + len] = c;
+ textsize = len;
+ for (i = 1; i <= F; i++)
+ InsertNode(r - i);
+ InsertNode(r);
+ do {
+ if (match_length > len)
+ match_length = len;
+ if (match_length <= THRESHOLD) {
+ match_length = 1;
+ EncodeChar(text_buf[r]);
+ } else {
+ EncodeChar(255 - THRESHOLD + match_length);
+ EncodePosition(match_position);
+ }
+ last_match_length = match_length;
+ for (i = 0; i < last_match_length &&
+ (c = getc(infile)) != EOF; i++) {
+ DeleteNode(s);
+ text_buf[s] = c;
+ if (s < F - 1)
+ text_buf[s + N] = c;
+ s = (s + 1) & (N - 1);
+ r = (r + 1) & (N - 1);
+ InsertNode(r);
+ }
+ if ((textsize += i) > printcount) {
+#if defined(VERBOSE) && defined(EXTRAVERBOSE)
+ Fprintf((stderr, "%12ld\r", textsize));
+#endif
+ printcount += 1024;
+ }
+ while (i++ < last_match_length) {
+ DeleteNode(s);
+ s = (s + 1) & (N - 1);
+ r = (r + 1) & (N - 1);
+ if (--len) InsertNode(r);
+ }
+ } while (len > 0);
+ EncodeEnd();
+#ifdef LONG_REPORT
+ Fprintf((stderr, "input size %ld bytes\n", codesize));
+ Fprintf((stderr, "output size %ld bytes\n", textsize));
+ Fprintf((stderr, "input/output %.3f\n", (double)codesize / textsize));
+#else
+ Fprintf((stderr, "input/output = %ld/%ld = %.3f\n", codesize, textsize,
+ (double)codesize / textsize));
+#endif
+}
+#endif
+
+#ifdef DECODE
+void Decode(void) /* recover */
+{
+ int i, j, k, r, c;
+ unsigned long int count;
+ unsigned long tw;
+
+ if (fread(&tw, sizeof tw, 1, infile) < 1)
+ Error("Can't read"); /* read size of text */
+ textsize = i86ul_to_host(tw);
+ if (textsize == 0)
+ return;
+ StartHuff();
+ for (i = 0; i < N - F; i++)
+ text_buf[i] = ' ';
+ r = N - F;
+ for (count = 0; count < textsize; ) {
+ c = DecodeChar();
+ if (c < 256) {
+ if (putc(c, outfile) == EOF) {
+ Error(wterr);
+ }
+ text_buf[r++] = c;
+ r &= (N - 1);
+ count++;
+ } else {
+ i = (r - DecodePosition() - 1) & (N - 1);
+ j = c - 255 + THRESHOLD;
+ for (k = 0; k < j; k++) {
+ c = text_buf[(i + k) & (N - 1)];
+ if (putc(c, outfile) == EOF) {
+ Error(wterr);
+ }
+ text_buf[r++] = c;
+ r &= (N - 1);
+ count++;
+ }
+ }
+ if (count > printcount) {
+#if defined(VERBOSE) && defined(EXTRAVERBOSE)
+ Fprintf((stderr, "%12ld\r", count));
+#endif
+ printcount += 1024;
+ }
+ }
+ Fprintf((stderr, "%12ld\n", count));
+}
+#endif
+
+#ifdef MAIN
+int main(int argc, char *argv[])
+{
+ char *s;
+ FILE *f;
+ int c;
+
+ if (argc == 2) {
+ outfile = stdout;
+ if ((f = tmpfile()) == NULL) {
+ perror("tmpfile");
+ return EXIT_FAILURE;
+ }
+ while ((c = getchar()) != EOF)
+ fputc(c, f);
+ rewind(infile = f);
+ }
+ else if (argc != 4) {
+ Fprintf((stderr, "'lzhuf e file1 file2' encodes file1 into file2.\n"
+ "'lzhuf d file2 file1' decodes file2 into file1.\n"));
+ return EXIT_FAILURE;
+ }
+ if (argc == 4) {
+ if ((s = argv[1], s[1] || strpbrk(s, "DEde") == NULL)
+ || (s = argv[2], (infile = fopen(s, "rb")) == NULL)
+ || (s = argv[3], (outfile = fopen(s, "wb")) == NULL)) {
+ Fprintf((stderr, "??? %s\n", s));
+ return EXIT_FAILURE;
+ }
+ }
+ if (toupper(*argv[1]) == 'E')
+ Encode();
+ else
+ Decode();
+ fclose(infile);
+ fclose(outfile);
+ return EXIT_SUCCESS;
+}
+#endif
diff --git a/gpxe/contrib/dhcpdconfeg/dhcpd.conf b/gpxe/contrib/dhcpdconfeg/dhcpd.conf
new file mode 100644
index 00000000..4d13e0f9
--- /dev/null
+++ b/gpxe/contrib/dhcpdconfeg/dhcpd.conf
@@ -0,0 +1,16 @@
+This is an example of using vendor tags in DHCPD config, supplied by
+Bernd Wiebelt.
+
+
+subnet 10.97.0.0 netmask 255.255.0.0 {
+ range 10.97.0.2 10.97.0.254;
+ option option-128 e4:45:74:68:0:0;
+ option option-160 "default=193";
+ option option-184 "HALLO";
+ option option-192 "Linux:::linux.tagged:";
+ option option-193 "DOS Bootdisk:::dosboot.tagged";
+ option option-194 "RH61 Bootdisk:::boot.tagged";
+ option option-195 "Local Disk:::/dev/hda:85b103482a20682da703aa388933a6d8";
+}
+
+
diff --git a/gpxe/contrib/dhcpdconfeg/vendorclassid.txt b/gpxe/contrib/dhcpdconfeg/vendorclassid.txt
new file mode 100644
index 00000000..7b1f3910
--- /dev/null
+++ b/gpxe/contrib/dhcpdconfeg/vendorclassid.txt
@@ -0,0 +1,140 @@
+From: Dax Kelson
+To: Etherboot users list
+Subject: [Etherboot-users] Example ISC DHCP v3 dhcpd.conf using conditional operations
+Date: Wed, 13 Jun 2001 20:22:21 -0600
+
+Hopefully someone will find this useful. I spent a long time tracking
+down and figuring out all the pieces. To the powers that be, feel free to
+stick this in contrib if you like it.
+
+Goal: Use the vendor-class-identifier and ISC DHCP v3 "match" option to
+conditionally send proper options only when the DHCP discover/request from
+etherboot comes in. We use static-MAC-to-IP mappings for classroom
+computers, and dynamic dhcp ranges for other clients (student laptops,
+etc).
+
+I used Etherboot 5.0.1 and the patch (required) in this email:
+
+http://www.geocrawler.com/lists/3/SourceForge/5299/0/5952625/
+
+Furture versions of Etherboot will likely already have this patch
+included.
+
+Dax Kelson
+Guru Labs
+
+######### Begin ISC DHCP v3 dhcpd.conf #############
+
+ddns-update-style ad-hoc;
+
+# Global default, can be overridden
+filename "/exports/kickstart/class1-rh7.1.ks";
+
+# Define options for Etherboot
+# There are more, these are just the ones I'm using
+option ebootmagic code 128 = string;
+option cmdline code 129 = string;
+option menudflts code 160 = string;
+option menuline1 code 192 = string;
+option menuline2 code 193 = string;
+option menuline3 code 194 = string;
+option menuline4 code 195 = string;
+option menuline5 code 196 = string;
+option menuline6 code 197 = string;
+option menuline7 code 198 = string;
+option menuline8 code 199 = string;
+option menuline9 code 200 = string;
+option menuline10 code 201 = string;
+option menuline11 code 202 = string;
+option menuline12 code 203 = string;
+option menuline13 code 204 = string;
+option menuline14 code 205 = string;
+option menuline15 code 206 = string;
+option menuline16 code 207 = string;
+option motdline1 code 184 = string;
+
+class "Etherboot" {
+ match if substring (option vendor-class-identifier, 0, 9) = "Etherboot";
+
+ option ebootmagic = E4:45:74:68:00:00;
+
+# We don't use this here, because different menu items require
+# different cmdlines. In our ".nbi" files we specify the cmdlines
+
+# option cmdline = "ks initrd=initrd.img lang= devfs=nomount";
+
+ option motdline1 = "Welcome to Guru Labs classroom";
+
+ option menudflts = "timeout=30:default=192";
+
+ option menuline1 = "Boot from Hard Drive (Default):::/dev/hda:::";
+ option menuline2 = "Boot from Floppy:::/dev/fd0:::";
+ option menuline3 = "Boot from CDROM::::::";
+ option menuline4 = "Kickstart install Red Hat 7.1:::rh71-ks-etherboot.nbi:::";
+ option menuline5 = "Red Hat 7.1 network rescue:::rh71-rescue-etherboot.nbi:::";
+ option menuline6 = "Boot Win98SE startup floppy:::win98se-startupdisk.nbi:::";
+ option menuline7 = "Jumpstart install Solaris 8 (not working yet):::/dev/hda:::";
+ option menuline8 = "Install Windows 98 SE (not working yet):::/dev/hda:::";
+ option menuline9 = "Install Windows 2000 (not working yet):::/dev/hda:::";
+ option menuline10 = "Install FreeBSD 4.3 (not working yet):::/dev/hda:::";
+ option menuline11 = "Install OpenBSD 2.9 (not working yet):::/dev/hda:::";
+
+ # This is a hidden menu item, it should be password protected too
+ option menuline12 = "^[[3D^[[K^[[1A^M:::/dev/hda:::";
+
+# We are using the menu, with different bootfiles. So we don't use this.
+# If you weren't using a menu, you could use this override the global
+# default "filename" setting.
+
+# filename "rh71-ks-etherboot";
+
+# Use the following if etherboot compiled with -DREQUIRE_VCI_ETHERBOOT
+
+ option vendor-encapsulated-options 3c:09:45:74:68:65:72:62:6f:6f:74:ff;
+
+}
+
+subnet 10.100.0.0 netmask 255.255.255.0 {
+ authoritative;
+ option routers 10.100.0.254;
+ option subnet-mask 255.255.255.0;
+ option domain-name "example.com";
+ option domain-name-servers 10.100.0.254;
+ option time-offset -7; # US/Mountain
+ option ntp-servers 10.100.0.254;
+ range dynamic-bootp 10.100.0.175 10.100.0.250;
+ default-lease-time 21600;
+ max-lease-time 43200;
+ option netbios-name-servers 10.100.0.254;
+ option netbios-node-type 2;
+ use-host-decl-names on;
+ next-server server1.example.com;
+
+}
+
+host station1 {
+ hardware ethernet 00:01:03:de:57:e2;
+ fixed-address 10.100.0.1;
+}
+host station2 {
+ hardware ethernet 00:01:03:de:57:e7;
+ fixed-address 10.100.0.2;
+}
+host station3 {
+ hardware ethernet 00:01:03:de:57:b4;
+ fixed-address 10.100.0.3;
+}
+host station4 {
+ hardware ethernet 00:01:03:de:57:38;
+ fixed-address 10.100.0.4;
+}
+host station5 {
+ hardware ethernet 00:01:03:de:58:3d;
+ fixed-address 10.100.0.5;
+}
+
+#
+# Etc, etc
+#
+
+############## End ISC DHCP v3 dhcpd.conf #############
diff --git a/gpxe/contrib/dhcpid/dhcpid.txt b/gpxe/contrib/dhcpid/dhcpid.txt
new file mode 100644
index 00000000..e6b5d277
--- /dev/null
+++ b/gpxe/contrib/dhcpid/dhcpid.txt
@@ -0,0 +1,884 @@
+From daniel@insu.com Thu Apr 27 14:14:55 2000
+Sender: root@iNsu.COM
+Message-ID: <39075669.FAEB20F2@insu.com>
+Date: Wed, 26 Apr 2000 16:49:45 -0400
+From: Daniel Shane <daniel@insu.com>
+X-Mailer: Mozilla 4.72 [en] (X11; U; Linux 2.2.14-5.0 i686)
+X-Accept-Language: en
+MIME-Version: 1.0
+Subject: Re: New feature added to etherboot
+References: <20000425170804.6677127D8A@Goffman.iNsu.COM>
+Content-Type: multipart/mixed;
+ boundary="------------4734FDA0BF2F2FBDF8EB8DF6"
+
+This is a multi-part message in MIME format.
+--------------4734FDA0BF2F2FBDF8EB8DF6
+Content-Type: text/plain; charset=us-ascii
+Content-Transfer-Encoding: 7bit
+
+Ok, here is a diff for etherboot 4.6.0 that adds identifiers.
+
+To test this you need to use a class in the dhcpd.conf file and
+also send back a string in option 208.
+
+These identifiers prevent a client from booting from other DHCP
+servers when you have more than 1 in your network.
+
+In will also prevent any client, except the valid ones, to use this
+DHCP server.
+
+Here is a subset of my dhcpd.conf :
+
+option iNdiskless-state code 208 = text;
+
+class "iNdiskless-boot" {
+ match if substring(option iNdiskless-state,0,4) = "BOOT";
+}
+class "iNdiskless-setup" {
+ match if substring(option iNdiskless-state,0,5) = "SETUP";
+}
+
+subnet 10.4.1.0 netmask 255.255.255.0 {
+pool {
+ allow members of "iNdiskless-boot";
+ deny unknown clients;
+ range 10.4.1.2 10.4.1.200;
+ next-server 10.4.1.1;
+
+# Identify ourselves to the etherboot/DHCP client
+ option iNdiskless-state "BOOT";
+
+ host labo01 {
+ hardware ethernet 00:80:c8:ec:04:1b;
+ }
+ host labo02 {
+ hardware ethernet 00:4f:4c:04:45:d6;
+ }
+ host labo03 {
+ hardware ethernet 00:50:ba:c8:db:d6;
+ }
+}
+pool {
+ allow members of "iNdiskless-setup";
+ range 10.4.1.201 10.4.1.254;
+ option iNdiskless-state "SETUP";
+
+# send another kernel to setup the diskless workstation
+ }
+}
+
+Daniel Shane.
+--------------4734FDA0BF2F2FBDF8EB8DF6
+Content-Type: text/plain; charset=us-ascii;
+ name="main.c.diff"
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline;
+ filename="main.c.diff"
+
+--- etherboot-4.6.0/src/main.c Tue Apr 25 08:30:01 2000
++++ etherboot-4.5.6-new/src/main.c Wed Apr 26 16:17:09 2000
+@@ -42,6 +42,23 @@ char *motd[RFC1533_VENDOR_NUMOFMOTD];
+ #ifdef IMAGE_FREEBSD
+ int freebsd_howto = 0;
+ #endif
++
++#ifdef SERVER_IDENT
++#ifdef DEFAULT_SERVER_IDENT
++char server_ident[9] = DEFAULT_SERVER_IDENT;
++#else
++char server_ident[9] = {};
++#endif
++#endif
++
++#ifdef CLIENT_IDENT
++#ifdef DEFAULT_CLIENT_IDENT
++char client_ident[9] = DEFAULT_CLIENT_IDENT;
++#else
++char client_ident[9] = {};
++#endif
++#endif
++
+ int vendorext_isvalid;
+ char config_buffer[TFTP_MAX_PACKET+1]; /* +1 for null byte */
+ unsigned long netmask;
+@@ -63,61 +80,85 @@ char rfc1533_cookie[5] = { RFC1533_CO
+ char rfc1533_cookie[] = { RFC1533_COOKIE};
+ char rfc1533_end[]={RFC1533_END };
+ static const char dhcpdiscover[]={
+- RFC2132_MSG_TYPE,1,DHCPDISCOVER,
+- RFC2132_MAX_SIZE,2,2,64,
+- RFC2132_PARAM_LIST,4,RFC1533_NETMASK,RFC1533_GATEWAY,
+- RFC1533_HOSTNAME,RFC1533_EXTENSIONPATH
+- };
+-static const char dhcprequest []={
+- RFC2132_MSG_TYPE,1,DHCPREQUEST,
+- RFC2132_SRV_ID,4,0,0,0,0,
+- RFC2132_REQ_ADDR,4,0,0,0,0,
+- RFC2132_MAX_SIZE,2,2,64,
+- /* request parameters */
+- RFC2132_PARAM_LIST,
+-#ifdef IMAGE_FREEBSD
+- /* 4 standard + 4 vendortags + 8 motd + 16 menu items */
+- 4 + 4 + 8 + 16,
++ RFC2132_MSG_TYPE,1,DHCPDISCOVER,
++ RFC2132_MAX_SIZE,2,2,64,
++#ifdef CLIENT_IDENT
++ RFC1533_VENDOR_CLIENT_IDENT,8,0,0,0,0,0,0,0,0,
++#endif
++ RFC2132_PARAM_LIST,
++#ifdef SERVER_IDENT
++ 5,
+ #else
+- /* 4 standard + 3 vendortags + 8 motd + 16 menu items */
+- 4 + 3 + 8 + 16,
++ 4,
+ #endif
+- /* Standard parameters */
+- RFC1533_NETMASK, RFC1533_GATEWAY,
+- RFC1533_HOSTNAME, RFC1533_EXTENSIONPATH,
+- /* Etherboot vendortags */
+- RFC1533_VENDOR_MAGIC,
++#ifdef SERVER_IDENT
++ RFC1533_VENDOR_SERVER_IDENT,
++#endif
++ RFC1533_NETMASK,
++ RFC1533_GATEWAY,
++ RFC1533_HOSTNAME,
++ RFC1533_EXTENSIONPATH
++};
++static const char dhcprequest []={
++ RFC2132_MSG_TYPE,1,DHCPREQUEST,
++ RFC2132_SRV_ID,4,0,0,0,0,
++ RFC2132_REQ_ADDR,4,0,0,0,0,
++#ifdef CLIENT_IDENT
++ RFC1533_VENDOR_CLIENT_IDENT,8,0,0,0,0,0,0,0,0,
++#endif
++ RFC2132_MAX_SIZE,2,2,64,
++ /* request parameters */
++ RFC2132_PARAM_LIST,
++ /* 4 standard + 3 vendortags + 8 motd + 16 menu items */
++ 4 +
++ 3 +
++#ifdef IMAGE_FREEBSD
++ 1 + /* One more vendortags for VENDOR_HOWTO */
++#endif
++#ifdef SERVER_IDENT
++ 1 + /* One more vendortags for VENDOR_SERVER_IDENT */
++#endif
++ 8 +
++ 16,
++ /* Standard parameters */
++ RFC1533_NETMASK, RFC1533_GATEWAY,
++ RFC1533_HOSTNAME, RFC1533_EXTENSIONPATH,
++ /* Etherboot vendortags */
++ RFC1533_VENDOR_MAGIC,
+ #ifdef IMAGE_FREEBSD
+- RFC1533_VENDOR_HOWTO,
++ RFC1533_VENDOR_HOWTO,
+ #endif
+- RFC1533_VENDOR_MNUOPTS, RFC1533_VENDOR_SELECTION,
+- /* 8 MOTD entries */
+- RFC1533_VENDOR_MOTD,
+- RFC1533_VENDOR_MOTD+1,
+- RFC1533_VENDOR_MOTD+2,
+- RFC1533_VENDOR_MOTD+3,
+- RFC1533_VENDOR_MOTD+4,
+- RFC1533_VENDOR_MOTD+5,
+- RFC1533_VENDOR_MOTD+6,
+- RFC1533_VENDOR_MOTD+7,
+- /* 16 image entries */
+- RFC1533_VENDOR_IMG,
+- RFC1533_VENDOR_IMG+1,
+- RFC1533_VENDOR_IMG+2,
+- RFC1533_VENDOR_IMG+3,
+- RFC1533_VENDOR_IMG+4,
+- RFC1533_VENDOR_IMG+5,
+- RFC1533_VENDOR_IMG+6,
+- RFC1533_VENDOR_IMG+7,
+- RFC1533_VENDOR_IMG+8,
+- RFC1533_VENDOR_IMG+9,
+- RFC1533_VENDOR_IMG+10,
+- RFC1533_VENDOR_IMG+11,
+- RFC1533_VENDOR_IMG+12,
+- RFC1533_VENDOR_IMG+13,
+- RFC1533_VENDOR_IMG+14,
+- RFC1533_VENDOR_IMG+15,
+- };
++#ifdef SERVER_IDENT
++ RFC1533_VENDOR_SERVER_IDENT,
++#endif
++ RFC1533_VENDOR_MNUOPTS, RFC1533_VENDOR_SELECTION,
++ /* 8 MOTD entries */
++ RFC1533_VENDOR_MOTD,
++ RFC1533_VENDOR_MOTD+1,
++ RFC1533_VENDOR_MOTD+2,
++ RFC1533_VENDOR_MOTD+3,
++ RFC1533_VENDOR_MOTD+4,
++ RFC1533_VENDOR_MOTD+5,
++ RFC1533_VENDOR_MOTD+6,
++ RFC1533_VENDOR_MOTD+7,
++ /* 16 image entries */
++ RFC1533_VENDOR_IMG,
++ RFC1533_VENDOR_IMG+1,
++ RFC1533_VENDOR_IMG+2,
++ RFC1533_VENDOR_IMG+3,
++ RFC1533_VENDOR_IMG+4,
++ RFC1533_VENDOR_IMG+5,
++ RFC1533_VENDOR_IMG+6,
++ RFC1533_VENDOR_IMG+7,
++ RFC1533_VENDOR_IMG+8,
++ RFC1533_VENDOR_IMG+9,
++ RFC1533_VENDOR_IMG+10,
++ RFC1533_VENDOR_IMG+11,
++ RFC1533_VENDOR_IMG+12,
++ RFC1533_VENDOR_IMG+13,
++ RFC1533_VENDOR_IMG+14,
++ RFC1533_VENDOR_IMG+15,
++};
+
+ #endif /* NO_DHCP_SUPPORT */
+ static const char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+@@ -176,6 +217,55 @@ done:
+ break;
+ }
+ #endif
++
++#ifdef SHIFTED_IDENT_INPUT
++ if (getshift() & 3)
++ {
++#endif
++
++#ifdef CLIENT_IDENT
++# ifdef ASK_CLIENT_IDENT
++ {
++ char tmp_ident[9] = {};
++# ifdef DEFAULT_CLIENT_IDENT
++ printf("Enter the client identifier (8 char max.) default [%s] : ",client_ident);
++# else
++ printf("Enter the client identifier (8 char max.) : ");
++# endif
++ getstr(tmp_ident,8);
++ if (strlen(tmp_ident) != 0)
++ memcpy(client_ident,tmp_ident,8);
++ else
++ printf("%s",client_ident);
++ putchar('\n');
++ }
++# endif
++#endif
++
++#ifdef SERVER_IDENT
++# ifdef ASK_SERVER_IDENT
++ {
++ char tmp_ident[9] = {};
++# ifdef DEFAULT_SERVER_IDENT
++ printf("Enter the server identifier (8 char max.) default [%s] : ",server_ident);
++# else
++ printf("Enter the server identifier (8 char max.) : ");
++# endif
++ getstr(tmp_ident,8);
++ if (strlen(tmp_ident) != 0)
++ memcpy(server_ident,tmp_ident,8);
++ else
++ printf("%s",server_ident);
++ putchar('\n');
++ }
++# endif
++#endif
++
++#ifdef SHIFTED_IDENT_INPUT
++ }
++#endif
++
++ print_config();
+ #if (TRY_FLOPPY_FIRST > 0) && defined(FLOPPY)
+ disk_init();
+ printf("Trying floppy");
+@@ -188,7 +278,7 @@ done:
+ }
+ printf("no floppy\n");
+ #endif /* TRY_FLOPPY_FIRST && FLOPPY */
+- print_config();
++ print_config();
+ gateA20_set();
+ #ifdef EMERGENCYDISKBOOT
+ if (!eth_probe()) {
+@@ -663,6 +753,8 @@ BOOTP - Get my IP address and load infor
+ int bootp()
+ {
+ int retry;
++ int offset = 0;
++
+ #ifndef NO_DHCP_SUPPORT
+ int retry1;
+ #endif /* NO_DHCP_SUPPORT */
+@@ -680,11 +772,18 @@ int bootp()
+ bp.bp_xid = xid = starttime = currticks();
+ memcpy(bp.bp_hwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+ #ifdef NO_DHCP_SUPPORT
+- memcpy(bp.bp_vend, rfc1533_cookie, 5); /* request RFC-style options */
++ memcpy(bp.bp_vend+offset, rfc1533_cookie, 5); /* request RFC-style options */
++ offset += sizeof rfc1533_cookie;
+ #else
+- memcpy(bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); /* request RFC-style options */
+- memcpy(bp.bp_vend+sizeof rfc1533_cookie, dhcpdiscover, sizeof dhcpdiscover);
+- memcpy(bp.bp_vend+sizeof rfc1533_cookie +sizeof dhcpdiscover, rfc1533_end, sizeof rfc1533_end);
++ memcpy(bp.bp_vend+offset, rfc1533_cookie, sizeof rfc1533_cookie); /* request RFC-style options */
++ offset += sizeof rfc1533_cookie;
++ memcpy(bp.bp_vend+offset, dhcpdiscover, sizeof dhcpdiscover);
++ offset += sizeof dhcpdiscover;
++#ifdef CLIENT_IDENT
++ memcpy(bp.bp_vend+13, client_ident, strlen(client_ident));
++#endif
++ memcpy(bp.bp_vend+offset, rfc1533_end, sizeof rfc1533_end);
++ offset += sizeof rfc1533_end;
+ #endif /* NO_DHCP_SUPPORT */
+
+ for (retry = 0; retry < MAX_BOOTP_RETRIES; ) {
+@@ -715,19 +814,22 @@ int bootp()
+ #else
+ if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT)){
+ if (dhcp_reply==DHCPOFFER){
+- dhcp_reply=0;
+- memcpy(bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie);
+- memcpy(bp.bp_vend+sizeof rfc1533_cookie, dhcprequest, sizeof dhcprequest);
+- memcpy(bp.bp_vend+sizeof rfc1533_cookie +sizeof dhcprequest, rfc1533_end, sizeof rfc1533_end);
+- memcpy(bp.bp_vend+9, &dhcp_server, sizeof(in_addr));
+- memcpy(bp.bp_vend+15, &dhcp_addr, sizeof(in_addr));
+- for (retry1 = 0; retry1 < MAX_BOOTP_RETRIES;) {
+- udp_transmit(IP_BROADCAST, 0, BOOTP_SERVER,
+- sizeof(struct bootp_t), &bp);
+ dhcp_reply=0;
+- if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT))
+- if (dhcp_reply==DHCPACK)
+- return(1);
++ memcpy(bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie);
++ memcpy(bp.bp_vend+sizeof rfc1533_cookie, dhcprequest, sizeof dhcprequest);
++ memcpy(bp.bp_vend+sizeof rfc1533_cookie +sizeof dhcprequest, rfc1533_end, sizeof rfc1533_end);
++ memcpy(bp.bp_vend+9, &dhcp_server, sizeof(in_addr));
++ memcpy(bp.bp_vend+15, &dhcp_addr, sizeof(in_addr));
++#ifdef CLIENT_IDENT
++ memcpy(bp.bp_vend+21, client_ident, strlen(client_ident));
++#endif
++ for (retry1 = 0; retry1 < MAX_BOOTP_RETRIES;) {
++ udp_transmit(IP_BROADCAST, 0, BOOTP_SERVER,
++ sizeof(struct bootp_t), &bp);
++ dhcp_reply=0;
++ if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT))
++ if (dhcp_reply==DHCPACK)
++ return(1);
+ rfc951_sleep(++retry1);
+ }
+ } else
+@@ -750,6 +852,7 @@ AWAIT_REPLY - Wait until we get a respon
+ **************************************************************************/
+ int await_reply(int type, int ival, void *ptr, int timeout)
+ {
++ int result;
+ unsigned long time;
+ struct iphdr *ip;
+ struct udphdr *udp;
+@@ -757,6 +860,7 @@ int await_reply(int type, int ival, void
+ struct bootp_t *bootpreply;
+ struct rpc_t *rpc;
+ unsigned short ptype;
++ unsigned int min_packetlen;
+
+ unsigned int protohdrlen = ETHER_HDR_SIZE + sizeof(struct iphdr) +
+ sizeof(struct udphdr);
+@@ -766,35 +870,35 @@ int await_reply(int type, int ival, void
+ * needs a negligible amount of time. */
+ for (;;) {
+ if (eth_poll()) { /* We have something! */
+- /* Check for ARP - No IP hdr */
++ /* Check for ARP - No IP hdr */
+ if (nic.packetlen >= ETHER_HDR_SIZE) {
+ ptype = ((unsigned short) nic.packet[12]) << 8
+ | ((unsigned short) nic.packet[13]);
+ } else continue; /* what else could we do with it? */
+ if ((nic.packetlen >= ETHER_HDR_SIZE +
+- sizeof(struct arprequest)) &&
+- (ptype == ARP) ) {
++ sizeof(struct arprequest)) &&
++ (ptype == ARP) ) {
+ unsigned long tmp;
+-
++
+ arpreply = (struct arprequest *)
+ &nic.packet[ETHER_HDR_SIZE];
+ if ((arpreply->opcode == ntohs(ARP_REPLY)) &&
+- !memcmp(arpreply->sipaddr, ptr, sizeof(in_addr)) &&
+- (type == AWAIT_ARP)) {
++ !memcmp(arpreply->sipaddr, ptr, sizeof(in_addr)) &&
++ (type == AWAIT_ARP)) {
+ memcpy(arptable[ival].node, arpreply->shwaddr, ETHER_ADDR_SIZE);
+ return(1);
+ }
+ memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
+ if ((arpreply->opcode == ntohs(ARP_REQUEST)) &&
+- (tmp == arptable[ARP_CLIENT].ipaddr.s_addr)) {
++ (tmp == arptable[ARP_CLIENT].ipaddr.s_addr)) {
+ arpreply->opcode = htons(ARP_REPLY);
+ memcpy(arpreply->tipaddr, arpreply->sipaddr, sizeof(in_addr));
+ memcpy(arpreply->thwaddr, arpreply->shwaddr, ETHER_ADDR_SIZE);
+ memcpy(arpreply->sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
+ memcpy(arpreply->shwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+ eth_transmit(arpreply->thwaddr, ARP,
+- sizeof(struct arprequest),
+- arpreply);
++ sizeof(struct arprequest),
++ arpreply);
+ #ifdef MDEBUG
+ memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
+ printf("Sent ARP reply to: %I\n",tmp);
+@@ -802,20 +906,20 @@ int await_reply(int type, int ival, void
+ }
+ continue;
+ }
+-
++
+ if (type == AWAIT_QDRAIN) {
+ continue;
+ }
+-
+- /* Check for RARP - No IP hdr */
++
++ /* Check for RARP - No IP hdr */
+ if ((type == AWAIT_RARP) &&
+- (nic.packetlen >= ETHER_HDR_SIZE +
+- sizeof(struct arprequest)) &&
+- (ptype == RARP)) {
++ (nic.packetlen >= ETHER_HDR_SIZE +
++ sizeof(struct arprequest)) &&
++ (ptype == RARP)) {
+ arpreply = (struct arprequest *)
+ &nic.packet[ETHER_HDR_SIZE];
+ if ((arpreply->opcode == ntohs(RARP_REPLY)) &&
+- !memcmp(arpreply->thwaddr, ptr, ETHER_ADDR_SIZE)) {
++ !memcmp(arpreply->thwaddr, ptr, ETHER_ADDR_SIZE)) {
+ memcpy(arptable[ARP_SERVER].node, arpreply->shwaddr, ETHER_ADDR_SIZE);
+ memcpy(& arptable[ARP_SERVER].ipaddr, arpreply->sipaddr, sizeof(in_addr));
+ memcpy(& arptable[ARP_CLIENT].ipaddr, arpreply->tipaddr, sizeof(in_addr));
+@@ -823,64 +927,72 @@ int await_reply(int type, int ival, void
+ }
+ continue;
+ }
+-
+- /* Anything else has IP header */
++
++ /* Anything else has IP header */
+ if ((nic.packetlen < protohdrlen) ||
+- (ptype != IP) ) continue;
++ (ptype != IP) ) continue;
+ ip = (struct iphdr *)&nic.packet[ETHER_HDR_SIZE];
+ if ((ip->verhdrlen != 0x45) ||
+- ipchksum((unsigned short *)ip, sizeof(struct iphdr)) ||
+- (ip->protocol != IP_UDP)) continue;
++ ipchksum((unsigned short *)ip, sizeof(struct iphdr)) ||
++ (ip->protocol != IP_UDP)) continue;
+ udp = (struct udphdr *)&nic.packet[ETHER_HDR_SIZE +
+- sizeof(struct iphdr)];
+-
+- /* BOOTP ? */
++ sizeof(struct iphdr)];
++
++ /* BOOTP ? */
+ bootpreply = (struct bootp_t *)&nic.packet[ETHER_HDR_SIZE];
+- if ((type == AWAIT_BOOTP) &&
+- (nic.packetlen >= (ETHER_HDR_SIZE +
+-#ifdef NO_DHCP_SUPPORT
+- sizeof(struct bootp_t))) &&
++#ifdef NO_DHCP_SUPPORT
++ min_packetlen = ETHER_HDR_SIZE + sizeof(struct bootp_t);
+ #else
+- sizeof(struct bootp_t))-DHCP_OPT_LEN) &&
+-#endif /* NO_DHCP_SUPPORT */
+- (ntohs(udp->dest) == BOOTP_CLIENT) &&
+- (bootpreply->bp_op == BOOTP_REPLY) &&
+- (bootpreply->bp_xid == xid)) {
+- arptable[ARP_CLIENT].ipaddr.s_addr =
+- bootpreply->bp_yiaddr.s_addr;
++ min_packetlen = ETHER_HDR_SIZE + sizeof(struct bootp_t) - DHCP_OPT_LEN;
++#endif
++ if (
++ (type == AWAIT_BOOTP) &&
++ (nic.packetlen >= min_packetlen) &&
++ (ntohs(udp->dest) == BOOTP_CLIENT) &&
++ (bootpreply->bp_op == BOOTP_REPLY) &&
++ (bootpreply->bp_xid == xid)
++ ) {
++ arptable[ARP_CLIENT].ipaddr.s_addr = bootpreply->bp_yiaddr.s_addr;
+ #ifndef NO_DHCP_SUPPORT
+ dhcp_addr.s_addr = bootpreply->bp_yiaddr.s_addr;
+ #endif /* NO_DHCP_SUPPORT */
+ netmask = default_netmask();
+- arptable[ARP_SERVER].ipaddr.s_addr =
+- bootpreply->bp_siaddr.s_addr;
++ arptable[ARP_SERVER].ipaddr.s_addr = bootpreply->bp_siaddr.s_addr;
+ memset(arptable[ARP_SERVER].node, 0, ETHER_ADDR_SIZE); /* Kill arp */
+- arptable[ARP_GATEWAY].ipaddr.s_addr =
+- bootpreply->bp_giaddr.s_addr;
++ arptable[ARP_GATEWAY].ipaddr.s_addr = bootpreply->bp_giaddr.s_addr;
+ memset(arptable[ARP_GATEWAY].node, 0, ETHER_ADDR_SIZE); /* Kill arp */
+ if (bootpreply->bp_file[0]) {
+ memcpy(kernel_buf, bootpreply->bp_file, 128);
+ kernel = kernel_buf;
+ }
+ memcpy((char *)BOOTP_DATA_ADDR, (char *)bootpreply, sizeof(struct bootpd_t));
+- decode_rfc1533(BOOTP_DATA_ADDR->bootp_reply.bp_vend,
+-#ifdef NO_DHCP_SUPPORT
+- 0, BOOTP_VENDOR_LEN +
+- MAX_BOOTP_EXTLEN, 1);
+-#else
+- 0, DHCP_OPT_LEN, 1);
+-#endif /* NO_DHCP_SUPPORT */
+- return(1);
++#ifdef NO_DHCP_SUPPORT
++ if (decode_rfc1533(BOOTP_DATA_ADDR->bootp_reply.bp_vend,
++ 0, BOOTP_VENDOR_LEN +
++ MAX_BOOTP_EXTLEN, 1)) {
++ return(1);
++ }
++ else {
++ continue;
++ }
++#else
++ if (decode_rfc1533(BOOTP_DATA_ADDR->bootp_reply.bp_vend,
++ 0, DHCP_OPT_LEN, 1)) {
++ return(1);
++ }
++ else {
++ continue;
++ }
+ }
+-
++#endif /* NO_DHCP_SUPPORT */
+ #ifdef DOWNLOAD_PROTO_TFTP
+- /* TFTP ? */
++ /* TFTP ? */
+ if ((type == AWAIT_TFTP) &&
+- (ntohs(udp->dest) == ival)) return(1);
++ (ntohs(udp->dest) == ival)) return(1);
+ #endif /* DOWNLOAD_PROTO_TFTP */
+-
++
+ #ifdef DOWNLOAD_PROTO_NFS
+- /* RPC ? */
++ /* RPC ? */
+ rpc = (struct rpc_t *)&nic.packet[ETHER_HDR_SIZE];
+ if ((type == AWAIT_RPC) &&
+ (ntohs(udp->dest) == ival) &&
+@@ -889,19 +1001,19 @@ int await_reply(int type, int ival, void
+ return (1);
+ }
+ #endif /* DOWNLOAD_PROTO_NFS */
+-
++
+ } else {
+- /* Check for abort key only if the Rx queue is empty -
+- * as long as we have something to process, don't
+- * assume that something failed. It is unlikely that
+- * we have no processing time left between packets. */
++ /* Check for abort key only if the Rx queue is empty -
++ * as long as we have something to process, don't
++ * assume that something failed. It is unlikely that
++ * we have no processing time left between packets. */
+ if (iskey() && (getchar() == ESC))
+ #ifdef EMERGENCYDISKBOOT
+ exit(0);
+ #else
+- longjmp(jmp_bootmenu,1);
++ longjmp(jmp_bootmenu,1);
+ #endif
+- /* Do the timeout after at least a full queue walk. */
++ /* Do the timeout after at least a full queue walk. */
+ if ((timeout == 0) || (currticks() > time)) {
+ break;
+ }
+@@ -914,13 +1026,15 @@ int await_reply(int type, int ival, void
+ DECODE_RFC1533 - Decodes RFC1533 header
+ **************************************************************************/
+ int decode_rfc1533(p, block, len, eof)
+- register unsigned char *p;
+- int block, len, eof;
++ register unsigned char *p;
++ int block, len, eof;
+ {
+ static unsigned char *extdata = NULL, *extend = NULL;
+ unsigned char *extpath = NULL;
+ unsigned char *endp;
+-
++#ifdef SERVER_IDENT
++ char rcvd_server_ident[9] = {};
++#endif
+ if (block == 0) {
+ #ifdef IMAGE_MENU
+ memset(imagelist, 0, sizeof(imagelist));
+@@ -1002,11 +1116,16 @@ int decode_rfc1533(p, block, len, eof)
+ }
+ #endif
+ #ifdef MOTD
+- else if (c >= RFC1533_VENDOR_MOTD &&
++ else if (c >= RFC1533_VENDOR_MOTD &&
+ c < RFC1533_VENDOR_MOTD +
+ RFC1533_VENDOR_NUMOFMOTD)
+ motd[c - RFC1533_VENDOR_MOTD] = p;
+ #endif
++#ifdef SERVER_IDENT
++ else if (c == RFC1533_VENDOR_SERVER_IDENT) {
++ memcpy(rcvd_server_ident,p+2,TAG_LEN(p));
++ }
++#endif
+ else {
+ #if 0
+ unsigned char *q;
+@@ -1018,6 +1137,30 @@ int decode_rfc1533(p, block, len, eof)
+ }
+ p += TAG_LEN(p) + 2;
+ }
++#if defined(SERVER_IDENT) && defined(DBG_IDENT)
++ if (strcasecmp(rcvd_server_ident,server_ident)) {
++ char ip[16];
++
++ inet_ntoa(dhcp_server,ip);
++ printf("[%s]: Option %d (%s), invalid response. Wanted (%s).\n",
++ ip,
++ RFC1533_VENDOR_SERVER_IDENT,
++ rcvd_server_ident,
++ server_ident);
++ strcpy(rcvd_server_ident,"");
++ return(0);
++ }
++ else {
++ char ip[16];
++
++ inet_ntoa(dhcp_server,ip);
++ printf("[%s]: Option %d (%s), valid response.\n",
++ ip,
++ RFC1533_VENDOR_SERVER_IDENT,
++ rcvd_server_ident);
++ strcpy(rcvd_server_ident,"");
++ }
++#endif
+ extdata = extend = endp;
+ if (block == 0 && extpath != NULL) {
+ char fname[64];
+@@ -1103,3 +1246,4 @@ void cleanup(void)
+ * c-basic-offset: 8
+ * End:
+ */
++
+
+--------------4734FDA0BF2F2FBDF8EB8DF6
+Content-Type: text/plain; charset=us-ascii;
+ name="misc.c.diff"
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline;
+ filename="misc.c.diff"
+
+--- etherboot-4.6.0/src/misc.c Tue Apr 25 08:30:25 2000
++++ etherboot-4.5.6-new/src/misc.c Wed Apr 26 16:26:38 2000
+@@ -140,9 +140,11 @@ void printf(const char *fmt, ...)
+
+ #ifdef IMAGE_MENU
+ /**************************************************************************
+-INET_ATON - Convert an ascii x.x.x.x to binary form
++INET_NTOA - Convert an ascii x.x.x.x to binary form
+ **************************************************************************/
+-int inet_aton(char *p, in_addr *i)
++int inet_aton(p, i)
++ char *p;
++ in_addr *i;
+ {
+ unsigned long ip = 0;
+ int val;
+@@ -165,7 +167,19 @@ int inet_aton(char *p, in_addr *i)
+
+ #endif /* IMAGE_MENU */
+
+-int getdec(char **ptr)
++#if defined(CLIENT_IDENT) || defined (SERVER_IDENT)
++/**************************************************************************
++INET_NTOA - Convert a binary form to an ascii x.x.x.x form
++**************************************************************************/
++char *inet_ntoa(in_addr i, char *p)
++{
++ sprintf(p,"%d.%d.%d.%d",i.s_addr>>24,i.s_addr<<8>>24,i.s_addr<<16>>24,i.s_addr<<24>>24);
++ return p;
++}
++#endif
++
++int getdec(ptr)
++ char **ptr;
+ {
+ char *p = *ptr;
+ int ret=0;
+@@ -308,6 +322,45 @@ iskey(void)
+ return 0;
+ }
+ #endif /* ETHERBOOT32 */
++
++/**************************************************************************
++GETSTR - Read a string of size bytes from the keyboard
++(without echoing the final return)
++**************************************************************************/
++void getstr(char *s, int size)
++{
++ int i=0;
++ char c;
++
++ while(1) {
++ c = getc();
++
++
++ if (c == 13)
++ {
++ s[i]='\0';
++ break;
++ }
++ else if (
++ ((c >= 'a') && (c <='z')) ||
++ ((c >= 'A') && (c <='Z')) ||
++ ((c >= '0') && (c <='9'))
++ ) {
++ if (i==8) {
++ putchar(8);
++ putchar(s[i-1]=c);
++ }
++ else
++ putchar(s[i++]=c);
++ }
++ else if ( c == 8 ) {
++ if (i != 0) {
++ --i;
++ s[i]='\0';
++ putchar(8);
++ putchar(32);
++ putchar(8);
++ }
++ }
++ }
++}
+
+ /*
+ * Local variables:
+
+--------------4734FDA0BF2F2FBDF8EB8DF6
+Content-Type: text/plain; charset=us-ascii;
+ name="Config.diff"
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline;
+ filename="Config.diff"
+
+--- etherboot-4.6.0/src/Config Tue Apr 25 08:30:57 2000
++++ etherboot-4.5.6-new/src/Config Wed Apr 26 15:55:57 2000
+@@ -59,6 +59,27 @@
+ # may no longer be appropriate. You might need to set
+ # MAX_ARP_RETRIES, MAX_BOOTP_RETRIES, MAX_TFTP_RETRIES
+ # and MAX_RPC_RETRIES to a larger value.
++# -DDEFAULT_CLIENT_IDENT
++# The default client identifier that is sent to the
++# DHCP server to identify itself.
++# -DDEFAULT_SERVER_IDENT
++# The expected response that the client will wait
++# for when a DHCP server responds to the the initial
++# client discovery.
++# -DASK_CLIENT_IDENT
++# -DASK_SERVER_IDENT
++# If these are set, the boot process will include
++# a question period where you can manualy specify
++# the client and/or server identifiers.
++# -DSHIFTED_IDENT_INPUT
++# If this is set then the boot process will only
++# ask for the identifiers if one of the shift keys
++# is pressed. Else it will send the default identifiers
++# automatically
++# -DDBG_IDENT
++# This will give show all the DHCP responses with
++# their identifiers.
++#
+ #
+ # Etherboot/32 only options:
+ # -DAOUT_IMAGE - Add a.out kernel boot support (generic)
+@@ -147,6 +168,14 @@ CFLAGS32+= -DASK_BOOT=3 -DANS_DEFAULT=AN
+
+ # Change download protocol to NFS. Only available for Etherboot/32 for now.
+ # CFLAGS32+= -DDOWNLOAD_PROTO_NFS
++
++# If you have more than one DHCP server you might want to
++# enable these to be able to sort out which one you want to
++# respond to.
++CFLAGS32+= -DDEFAULT_CLIENT_IDENT=\"BOOT\" -DDEFAULT_SERVER_IDENT=\"BOOT\"
++CFLAGS32+= -DASK_CLIENT_IDENT -DASK_SERVER_IDENT
++CFLAGS32+= -DSHIFTED_IDENT_INPUT
++CFLAGS32+= -DDBG_IDENT
+
+ # These flags affect the loader that is prepended to the Etherboot image
+ LCONFIG+= -DMOVEROM
+
+--------------4734FDA0BF2F2FBDF8EB8DF6
+Content-Type: text/plain; charset=us-ascii;
+ name="etherboot.h.diff"
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline;
+ filename="etherboot.h.diff"
+
+--- etherboot-4.6.0/src/etherboot.h Tue Apr 25 08:30:55 2000
++++ etherboot-4.5.6-new/src/etherboot.h Wed Apr 26 16:07:16 2000
+@@ -8,6 +8,14 @@ Author: Martin Renters
+
+ #include "osdep.h"
+
++#if (! defined(NO_DHCP_SUPPORT)) && (defined(ASK_CLIENT_IDENT) || defined(DEFAULT_CLIENT_IDENT))
++# define CLIENT_IDENT
++#endif
++
++#if (! defined(NO_DHCP_SUPPORT)) && (defined(ASK_SERVER_IDENT) || defined(DEFAULT_SERVER_IDENT))
++# define SERVER_IDENT
++#endif
++
+ /* These could be customised for different languages perhaps */
+ #define ASK_PROMPT "Boot from (N)etwork or from (L)ocal? "
+ #define ANS_NETWORK 'N'
+@@ -224,6 +232,12 @@ Author: Martin Renters
+ #ifdef IMAGE_FREEBSD
+ #define RFC1533_VENDOR_HOWTO 132
+ #endif
++#ifdef CLIENT_IDENT
++#define RFC1533_VENDOR_CLIENT_IDENT 208
++#endif
++#ifdef SERVER_IDENT
++#define RFC1533_VENDOR_SERVER_IDENT 208
++#endif
+ #define RFC1533_VENDOR_MNUOPTS 160
+ #define RFC1533_VENDOR_SELECTION 176
+ #define RFC1533_VENDOR_MOTD 184
+@@ -477,11 +491,13 @@ extern int getdec P((char **));
+ extern void printf P((const char *, ...));
+ extern char *sprintf P((char *, const char *, ...));
+ extern int inet_aton P((char *p, in_addr *i));
++extern char *inet_ntoa P((in_addr i, char *p));
+ extern void gateA20_set P((void));
+ extern void gateA20_unset P((void));
+ extern void putchar P((int));
+ extern int getchar P((void));
+ extern int iskey P((void));
++extern void getstr P((char *s, int size));
+
+ /* start*.S */
+ extern int getc P((void));
+@@ -528,8 +544,10 @@ extern int hostnamelen;
+ extern unsigned long netmask;
+ extern int jmp_bootmenu[10];
+ extern struct arptable_t arptable[MAX_ARP];
+-#ifdef IMAGE_MENU
++#ifdef MOTD
+ extern char *motd[RFC1533_VENDOR_NUMOFMOTD];
++#endif
++#ifdef IMAGE_MENU
+ extern int menutmo,menudefault;
+ extern unsigned char *defparams;
+ extern int defparams_max;
+
+--------------4734FDA0BF2F2FBDF8EB8DF6--
+
diff --git a/gpxe/contrib/eepro100notes/flash-1.txt b/gpxe/contrib/eepro100notes/flash-1.txt
new file mode 100644
index 00000000..61579b4c
--- /dev/null
+++ b/gpxe/contrib/eepro100notes/flash-1.txt
@@ -0,0 +1,73 @@
+Date: Tue, 18 May 1999 15:45:55 +0200 (MEST)
+From: Erik Starback <erik@math.uu.se>
+To: netboot@baghira.han.de
+Subject: Netboot with Intel EEPRO100+ Management
+Message-ID: <Pine.LNX.3.96.990518154313.3875A-100000@anarchy.math.uu.se>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=iso-8859-1
+Content-Transfer-Encoding: 8BIT
+Sender: owner-netboot@baghira.han.de
+Precedence: bulk
+Reply-To: netboot@baghira.han.de
+X-Moderator: netboot-owner@baghira.han.de
+X-UIDL: 6ca8453c19c46d622813e9be8ada9517
+Status: O
+X-Status:
+
+Hello!
+
+When Intel eepro100+ NIC disappeared from the market, I didn't know
+what to do. I didn't find any information if anyone has used the
+new eepro100+ Management Adapter to netboot linux.
+
+I thought that the card should netboot with the same configuration as
+the old card when I read Donald Beckers comment:
+> The driver should "just work" with the '559. It's not supposed to be
+> substantially different than the '558. (I don't have a datasheet
+> or sample card to confirm this statement.)
+
+The problem was now only to put the netboot-program to the built in
+flash memory on the NIC. With the old card I used a flash memory (Intel
+N28F020 [N28010 didn't work])) and the program FUTIL.EXE from Intel to
+flash it. FUTIL did't recognize the memory on the management card
+and did not work therefore.
+
+I found the intel program FBOOT.EXE that was made to upgrade the built
+in Intel BOOT agent. I did: Boot dos from floppy, Run FBOOT (choose
+adapter), choose (u)pdate, choose Create restore image, rename the
+backup file (in my case 2743BE52.FLS [the eight last hex digits from
+the MAC address]), rename your netboot code (in my case netboot 0.8.1)
+to the backup files original name (in my case 2743BE52.FLS), run
+FBOOT, choose (r)estore.
+
+Voila!
+
+A shorter way (if you don't need the backup of the old Intel BOOT
+agent code) is of course: rename netboot file to [the eight last hex
+digits from the MAC address].FLS, run FBOOT, choose restore.
+
+Caution: I think it is possible to make a NIC unusable if you have
+made the netboot (or etherboot) file with "wrong" parameters. A couple
+of month ago I did a etherboot boot file and put it on an old
+EEPRO100+ card. It worked fine, but it was impossible to boot local
+with it. So I could not boot dos and with FUTIL or FBOOT erase the
+flash memory! To erase the chip I had to take out the memory chip,
+boot dos and then put in the memory chip. This isn't possible when the
+memory chip is build in.
+
+Links:
+<http://support.intel.com/support/landesk/configmgr/LSA1_193.HTM>
+FUTIL.EXE is a part of LSA1_193.ZIP
+
+<http://support.intel.com/support/etherexpress/pro100/100pboot.htm>
+FBOOT.EXE is a part of 100pboot.exe
+
+/Erik S
+
+-------------------------------------------------------------------------
+Erik Starbäck, System administrator E-mail address: erik@math.uu.se
+Uppsala University Telephone (o): +46 18 4713277
+Department of Mathematics Cellular phone: +46 70 4250260
+P. O. Box 480 Fax (o): +46 18 4713201
+SE-751 06 UPPSALA
+Sweden
diff --git a/gpxe/contrib/eepro100notes/flash-2.txt b/gpxe/contrib/eepro100notes/flash-2.txt
new file mode 100644
index 00000000..1128c30e
--- /dev/null
+++ b/gpxe/contrib/eepro100notes/flash-2.txt
@@ -0,0 +1,149 @@
+Subject: Look Mom, no PROM burner! (eepro100b flashing instructions) :-)
+Date: Sun, 23 Jan 2000 01:53:08 -0500
+x-sender: mdc%thinguin.org@cdi.entity.com
+x-mailer: Claris Emailer 2.0v3, January 22, 1998
+From: Marty Connor <mdc@thinguin.org>
+To: "Netboot List" <netboot@baghira.han.de>
+Mime-Version: 1.0
+Content-Type: text/plain; charset="US-ASCII"
+Message-ID: <1263512144-341319205@entity.com>
+
+Continuing the Etherboot World Domination theme, I noticed that there was
+a PCI ethernet card on my bookshelf that still contained the original
+vendor's code in its flash memory. The card virtually cried out to be
+flashed with Etherboot 4.4.1. :-)
+
+After having figured out how to flash the 3C905C last week, and owing to
+the fact that the temperature here in Cambridge, Massachusetts (USA) has
+dropped well below freezing, I decided to explore the possibility of
+flashing the Intel eepro100b that was sitting on my bookcase.
+
+After determining that it was unlikely that one could flash the chip in
+user mode under linux like the 3C509C, I turned to other options. (the
+reason is that the flash is memory mapped to a place that causes a core
+dump if accessed. i suppose one could to patch the kernel to flash the
+card, or add a linux device driver, but... :-)
+
+By the way, If you are ever looking for Linux utilities for Ethernet
+cards, you may want to check out:
+
+ http://cesdis.gsfc.nasa.gov/linux/diag/
+
+which is a treasure trove of tools for manipulating and testing Ethernet
+cards, all with source, courtesy of Donald Becker.
+
+At this point, I felt it was time to make a virtual trip to the Intel
+site (http://www.intel.com/), and search for utilities that might work
+with the eepro100B. I found two candidates: FUTIL and FBOOT. I
+downloaded, decompressed, and transferred them to a DOS formatted floppy.
+Next I determined (after a few tries) that F8 will let me get to DOS
+instead of booting windows. (I tend to avoid Windows when I can).
+
+I first tried FUTIL.EXE. No good. It told me it didn't recognize the
+flash on my eepro100B. how unfortunate. and I had such hopes :-)
+
+Next I tested FBOOT.EXE (available at
+http://support.intel.com/support/network/adapter/pro100/100PBOOT.htm)
+This program did in fact recognize my eepro100b card.
+
+The thing about FBOOT however, is that it thinks it only can load certain
+files. I of course needed to load an Etherboot image. It appeared to
+have no option for doing that. Things looked grim.
+
+Then I noticed that FBOOT was kind enough to do the following dialog:
+
+ Select Option (U)pdate or (R)estore: U
+
+I chose Update and it then offered to back up my flash rom for later
+restore:
+
+ Create Restore Image (Y)es or (N)o: Y
+
+I chose "Y" and it proceeded to write a file of my flash memory, which
+contained the Intel code.
+
+ Writing FLASH image to file... 100%
+
+It then erased the device:
+
+ Erasing FLASH Device... 100%
+
+and then programmed it with fresh code (stored inside the program, no
+doubt):
+
+ Programming FLASH Device... 100%
+
+So now I had a backup of the Intel boot code in a file strangely called:
+
+ 2794FC60.FLS
+
+Hmmmm, interesting name. The MAC address of the card is 09902794FC60.
+They just name the file with the last 4 octets of the MAC address and
+.FLS. The file is exactly 65536 bytes, which would make sense for a 64K
+Flash Memory device.
+
+Then I got to thinking, I wonder how carefully the "restore" part of
+FBOOT looks at what it is loading? What if I took an Etherboot .rom
+file, padded it with 48K of 0xFFs and named it 2794FC60.FLS. What if I
+then told FBOOT.EXE to "restore" that?
+
+Well, I guess by now, you know it worked :-)
+
+The card came up with the delightful Etherboot banner, Did DHCP, tftp,
+and started a kernel.
+
+The only unfortunate part is that you need to do this under DOS because
+you seem to need to be in real mode to program the card. Oh well,
+sacrifices have to be made :-)
+
+So, in summary, to prepare Etherboot image for flashing into the Intel
+EEPRO100B card with FBOOT, you need to first make an eepro100.rom file,
+as usual.
+
+Then, see how large it is, with an "ls -l eepro100.rom". the answer will
+probably be 16,384. You need to pad it with hex FFs to make it 64K for
+FBOOT. I used the following two lines to create the flash image file.
+
+ $ perl -e 'print "\xFF" x 49152' > 48kpad.bin
+ $ cat eepro100.rom 48kpad.bin > 2794FC60.FLS
+
+Next write it to a DOS Floppy:
+
+ $ mount -t msdos /dev/fd0 /mnt/floppy
+ $ cp 2794FC60.FLS /mnt/floppy
+ $ umount /mnt/floppy
+
+Now you need to get to DOS. You could actually use a bootable DOS floppy
+with FBOOT.EXE and 2794FC60.FLS on it. I started a Windows box and hit
+F8 right before Windows started, and chose option 5, "Command Prompt
+Only", which gives you DOS. This program can't run in a DOS window under
+Windows or anything like that. You need to be in real DOS.
+
+Next it's time to run FBOOT. It will detect your ethernet card(s), ask
+you which one you want to program, and let you choose it from a menu.
+
+now the fun part:
+
+ Select Option (U)pdate or (R)estore: R
+ Erasing FLASH Device... 100%
+ Writing FLASH image from file... 100%
+
+Time to reboot and let Etherboot take over.
+
+So there you go, a way to make Intel EEPRO100Bs play nicely with
+Etherboot. Maybe we should put these instructions in the Etherboot
+contrib directory so people who have eepro100b cards will be able to
+avoid 3C905C envy :-)
+
+I hope this helps a few people out.
+
+Regards,
+
+Marty
+
+---
+ Name: Martin D. Connor
+US Mail: Entity Cyber, Inc.; P.O. Box 391827; Cambridge, MA 02139; USA
+ Voice: (617) 491-6935, Fax: (617) 491-7046
+ Email: mdc@thinguin.org
+ Web: http://www.thinguin.org/
diff --git a/gpxe/contrib/eepro100notes/flash-3.txt b/gpxe/contrib/eepro100notes/flash-3.txt
new file mode 100644
index 00000000..1a865a44
--- /dev/null
+++ b/gpxe/contrib/eepro100notes/flash-3.txt
@@ -0,0 +1,57 @@
+Date: Sun, 23 Jan 2000 09:47:15 +0100 (MET)
+From: Erik Starbäck <erik@math.uu.se>
+To: Netboot List <netboot@baghira.han.de>
+Subject: Re: Look Mom, no PROM burner! (eepro100b flashing instructions) :-)
+In-Reply-To: <1263512144-341319205@entity.com>
+Message-ID: <Pine.LNX.3.96.1000123094505.28562A-100000@anarchy.math.uu.se>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=iso-8859-1
+Content-Transfer-Encoding: 8BIT
+Sender: owner-netboot@baghira.han.de
+Precedence: bulk
+Reply-To: netboot@baghira.han.de
+X-Moderator: netboot-owner@baghira.han.de
+
+
+Hello!
+
+In <http://www.han.de/~gero/netboot/archive/msg01718.html> I wrote what I
+did know about futil and fboot then. It is about the same as Martys
+instructions, but I have a few comments now.
+
+> Then, see how large it is, with an "ls -l eepro100.rom". the answer will
+> probably be 16,384. You need to pad it with hex FFs to make it 64K for
+> FBOOT. I used the following two lines to create the flash image file.
+
+> $ perl -e 'print "\xFF" x 49152' > 48kpad.bin
+> $ cat eepro100.rom 48kpad.bin > 2794FC60.FLS
+
+It worked for me without any padding. When I burned a smaller image
+the program printed 50% instead of 100% and then it
+stopped. Everything worked anyway.
+
+
+I also did a brutal way of install etherboot or netboot on a
+EEPRO100+Mng without creating a file of type "2794FC60.FLS" for
+every card. It was necessary for me when I installed 70 clients...
+
+I chopped the binary file fboot.exe (my version was 99811 bytes, I
+don't remember the version name) in three parts:
+
+fboot1 30096 bytes
+fboot2 65536 bytes
+fboot3 4179 bytes
+
+Then you cat put them together again, but with a different part 2 and
+save it as fbootown.exe and execute it. It worked for me anyway. Of
+course you have to use padding to get a 64k part instead of fboot2.
+
+/Erik S
+
+-------------------------------------------------------------------------
+Erik Starbäck, System administrator E-mail address: erik@math.uu.se
+Uppsala University Telephone (o): +46 18 4713277
+Department of Mathematics Cellular phone: +46 70 4250260
+P. O. Box 480 Fax (o): +46 18 4713201
+SE-751 06 UPPSALA
+Sweden
diff --git a/gpxe/contrib/flashimg/Makefile b/gpxe/contrib/flashimg/Makefile
new file mode 100644
index 00000000..39f58e23
--- /dev/null
+++ b/gpxe/contrib/flashimg/Makefile
@@ -0,0 +1,29 @@
+CPPFLAGS = -x assembler-with-cpp
+AS86 = as86
+LD86 = ld86
+OBJDUMP = objdump
+
+.SUFFIXES: .s86 .asm .aout .img
+
+all: flashimg.img
+
+clean:
+ rm -rf *.o *.s86 *.aout *.img
+
+realclean: clean
+ rm -rf *.img
+
+.asm.s86: $*.asm $*.inc
+ $(CPP) $(CPPFLAGS) -o $@ $*.asm
+
+.s86.img: $*.s86
+ $(AS86) -0 -b $@ $*.s86
+
+# .s86.o: $*.s86
+# $(AS86) -0 -a -o $@ $*.s86
+#
+# .o.aout: $*.o
+# $(LD86) -0 -s -o $@ $*.o
+#
+# .aout.img:
+# dd if=$*.aout of=$@ bs=32 skip=1
diff --git a/gpxe/contrib/flashimg/flashimg.asm b/gpxe/contrib/flashimg/flashimg.asm
new file mode 100644
index 00000000..7a37ed54
--- /dev/null
+++ b/gpxe/contrib/flashimg/flashimg.asm
@@ -0,0 +1,497 @@
+; Copyright (C) 1997 Markus Gutschke <gutschk@uni-muenster.de>
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 2 of the License, or
+; any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, write to the Free Software
+; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+; Prepend this image file to an arbitrary ROM image. The resulting binary
+; can be loaded from any BOOT-Prom that supports the "nbi" file format.
+; When started, the image will reprogram the flash EPROM on the FlashCard
+; ISA card. The flash EPROM has to be an AMD 29F010, and the programming
+; algorithm is the same as that suggested by AMD in the appropriate data
+; sheets.
+
+
+#define SEGLOW 0xC800 /* lower range for EPROM segment */
+#define SEGHIGH 0xE800 /* upper range for EPROM segment */
+#define AMD_ID 0x2001 /* flash EPROM ID, only support AMD */
+#define ERASE1_CMD 0x80 /* first cmd for erasing full chip */
+#define ERASE2_CMD 0x10 /* second cmd for erasing full chip */
+#define READID_CMD 0x90 /* cmd to read chip ID */
+#define PROG_CMD 0xA0 /* cmd to program a byte */
+#define RESET_CMD 0xF0 /* cmd to reset chip state machine */
+
+;----------------------------------------------------------------------------
+
+
+ .text
+ .org 0
+
+; .globl _main
+_main: mov ax,#0x0FE0
+ mov ds,ax
+ mov ax,magic ; verify that we have been loaded by
+ cmp ax,#0xE4E4 ; boot prom
+ jnz lderr
+ jmpi 0x200,0x0FE0 ; adjust code segment
+lderr: mov si,#loaderr
+ cld
+lderrlp:seg cs
+ lodsb ; loop over all characters of
+ or al,al ; string
+ jnz lderrnx
+ xor ah,ah
+ int 0x16 ; wait for keypress
+ jmpi 0x0000,0xFFFF ; reboot!
+lderrnx:mov ah,#0x0E ; print it
+ mov bl,#0x07
+ xor bh,bh
+ int 0x10
+ jmp lderrlp
+
+loaderr:.ascii "The flash EPROM utility has to be loaded from a BOOT-Prom"
+ .byte 0xa,0xd
+ .ascii "that knows about the 'nbi' file format!"
+ .byte 0xa,0xd
+ .ascii "Reboot to proceed..."
+ .byte 0
+
+ .org 510
+ .byte 0x55,0xAA
+
+!----------------------------------------------------------------------------
+
+start: mov ax,cs
+ mov ds,ax
+ mov ax,romdata ; verify that there is an Prom image
+ cmp ax,#0xAA55 ; attached to the utility
+ jnz resmag
+ mov al,romdata+2
+ or al,al ; non-zero size is required
+ jnz magicok
+resmag: mov si,#badmagic ; print error message
+reset: call prnstr
+ xor ah,ah
+ int 0x16 ; wait for keypress
+ jmpi 0x0000,0xFFFF ; reboot!
+magicok:mov di,#clrline1
+ mov si,#welcome ; print welcome message
+inpnew: call prnstr
+inprest:xor bx,bx
+ mov cl,#0xC ; expect 4 nibbles input data
+inploop:xor ah,ah
+ int 0x16
+ cmp al,#0x8 ; <Backspace>
+ jnz inpnobs
+ or bx,bx ; there has to be at least one input ch
+ jz inperr
+ mov si,#delchar ; wipe out char from screen
+ call prnstr
+ add cl,#4 ; compute bitmask for removing input
+ mov ch,cl
+ mov cl,#0xC
+ sub cl,ch
+ mov ax,#0xFFFF
+ shr ax,cl
+ not ax
+ and bx,ax
+ mov cl,ch
+inploop1:jmp inploop
+inpnobs:cmp al,#0x0D ; <Return>
+ jnz inpnocr
+ or bx,bx ; zero input -> autoprobing
+ jz inpdone
+ cmp cl,#-4 ; otherwise there have to be 4 nibbles
+ jz inpdone
+inperr: mov al,#7 ; ring the console bell
+ jmp inpecho
+inpnocr:cmp al,#0x15 ; <CTRL-U>
+ jnz inpnokl
+ mov si,di
+ call prnstr ; clear entire input and restart
+ jmp inprest
+inpnokl:cmp cl,#-4 ; cannot input more than 4 nibbles
+ jz inperr
+ cmp al,#0x30 ; '0'
+ jb inperr
+ ja inpdig
+ or bx,bx ; leading '0' is not allowed
+ jz inperr
+inpdig: cmp al,#0x39 ; '9'
+ ja inpnodg
+ mov ch,al
+ sub al,#0x30
+inpnum: xor ah,ah ; compute new input value
+ shl ax,cl
+ add ax,bx
+ test ax,#0x1FF ; test for 8kB boundary
+ jnz inperr
+ cmp ax,#SEGHIGH ; input has to be below E800
+ jae inperr
+ cmp ax,#SEGLOW ; and above/equal C800
+ jae inpok
+ cmp cl,#0xC ; if there is just one nibble, yet,
+ jnz inperr ; then the lower limit ix C000
+ cmp ax,#0xC000
+ jb inperr
+inpok: mov bx,ax ; adjust bitmask
+ sub cl,#4
+ mov al,ch
+inpecho:call prnchr ; output new character
+ jmp inploop1
+inpnodg:and al,#0xDF ; lower case -> upper case
+ cmp al,#0x41 ; 'A'
+ jb inperr
+ cmp al,#0x46 ; 'F'
+ ja inperr
+ mov ch,al
+ sub al,#0x37
+ jmp inpnum
+inpdone:or bx,bx ; zero -> autoprobing
+ jnz probe
+ mov si,#automsg
+ call prnstr
+ mov cx,#0x10
+ mov bx,#SEGHIGH ; scan from E800 to C800
+autoprb:sub bx,#0x0200 ; stepping down in 8kB increments
+ mov di,bx
+ call readid
+ cmp ax,#AMD_ID
+ jz prbfnd
+ loop autoprb
+ mov si,#failmsg
+nofnd: mov di,#clrline2
+ jmp near inpnew ; failure -> ask user for new input
+probe: mov di,bx
+ test bx,#0x07FF ; EPROM might have to be aligned to
+ jz noalign ; 32kB boundary
+ call readid
+ cmp ax,#AMD_ID ; check for AMDs id
+ jz prbfnd
+ mov si,#alignmsg
+ call prnstr
+ and bx,#0xF800 ; enforce alignment of hardware addr
+noalign:call readid ; check for AMDs id
+ cmp ax,#AMD_ID
+ jz prbfnd
+ mov si,#nofndmsg ; could not find any EPROM at speci-
+ call prnstr ; fied location --- even tried
+ mov si,#basemsg ; aligning to 32kB boundary
+ jmp nofnd ; failure -> ask user for new input
+prbfnd: mov si,#fndmsg
+ call prnstr ; we found a flash EPROM
+ mov ax,bx
+ call prnwrd
+ mov si,#ersmsg
+ call prnstr
+ call erase ; erase old contents
+ jnc ersdone
+ mov si,#failresmsg ; failure -> reboot machine
+ jmp near reset
+ersdone:mov si,#prg1msg ; tell user that we are about
+ call prnstr ; to program the new data into
+ mov ax,di ; the specified range
+ call prnwrd
+ mov si,#prg2msg
+ call prnstr
+ xor dh,dh
+ mov dl,romdata+2
+ shl dx,#1
+ mov ah,dh
+ mov cl,#4
+ shl ah,cl
+ xor al,al
+ add ax,di
+ call prnwrd
+ mov al,#0x3A ; ':'
+ call prnchr
+ mov ah,dl
+ xor al,al
+ dec ax
+ call prnwrd
+ mov al,#0x20
+ call prnchr
+ mov dh,romdata+2 ; number of 512 byte blocks
+ push ds
+ mov ax,ds
+ add ax,#romdata>>4 ; adjust segment descriptor, so that
+ mov ds,ax ; we can handle images which are
+prgloop:mov cx,#0x200 ; larger than 64kB
+ xor si,si
+ xor bp,bp
+ call program ; program 512 data bytes
+ jc prgerr ; check error condition
+ mov ax,ds
+ add ax,#0x20 ; increment segment descriptors
+ mov ds,ax
+ add di,#0x20
+ dec dh ; decrement counter
+ jnz prgloop
+ pop ds
+ mov si,#donemsg ; success -> reboot
+prgdone:call prnstr
+ mov si,#resetmsg
+ jmp near reset
+prgerr: pop ds ; failure -> reboot
+ mov si,#failresmsg
+ jmp prgdone
+
+
+;----------------------------------------------------------------------------
+
+; READID -- read EPROM id number, base address is passed in BX
+; ======
+;
+; changes: AX, DL, ES
+
+readid: mov dl,#RESET_CMD ; reset chip
+ call sendop
+ mov dl,#READID_CMD
+ call sendop ; send READID command
+ mov es,bx
+ seg es
+ mov ax,0x00 ; read manufacturer ID
+ mov dl,#RESET_CMD
+ jmp sendop ; reset chip
+
+
+;----------------------------------------------------------------------------
+
+; ERASE -- erase entire EPROM, base address is passed in BX
+; =====
+;
+; changes: AL, CX, DL, ES, CF
+
+erase: mov dl,#ERASE1_CMD
+ call sendop ; send ERASE1 command
+ mov dl,#ERASE2_CMD
+ call sendop ; send ERASE2 command
+ xor bp,bp
+ mov al,#0xFF
+ push di
+ mov di,bx
+ call waitop ; wait until operation finished
+ pop di
+ jnc erfail
+ mov dl,#RESET_CMD
+ call sendop ; reset chip
+ stc
+erfail: ret
+
+
+;----------------------------------------------------------------------------
+
+; PROGRAM -- write data block at DS:SI of length CX into EPROM at DI:BP
+; =======
+;
+; changes: AX, CX, DL, BP, ES, CF
+
+program:mov dl,#PROG_CMD
+ call sendop ; send programming command
+ lodsb ; get next byte from buffer
+ mov es,di
+ seg es
+ mov byte ptr [bp],al ; write next byte into flash EPROM
+ call waitop ; wait until programming operation is
+ jc progdn ; completed
+ inc bp
+ loop program ; continue with next byte
+ clc ; return without error
+progdn: ret
+
+
+;----------------------------------------------------------------------------
+
+; SENDOP -- send command in DL to EPROM, base address is passed in BX
+; ======
+;
+; changes: ES
+
+sendop: mov es,bx
+ seg es
+ mov byte ptr 0x5555,#0xAA ; write magic data bytes into
+ jcxz so1 ; magic locations. This unlocks
+so1: jcxz so2 ; the flash EPROM. N.B. that the
+so2: seg es ; magic locations are mirrored
+ mov byte ptr 0x2AAA,#0x55 ; every 32kB; the hardware address
+ jcxz so3 ; might have to be adjusted to a
+so3: jcxz so4 ; 32kB boundary
+so4: seg es
+ mov byte ptr 0x5555,dl
+ ret
+
+
+;----------------------------------------------------------------------------
+
+; WAITOP -- wait for command to complete, address is passed in DI:BP
+; ======
+;
+; for details on the programming algorithm, c.f. http://www.amd.com
+;
+; changes: AX, DL, ES, CF
+
+waitop: and al,#0x80 ; monitor bit 7
+ mov es,di
+wait1: seg es ; read contents of EPROM cell that is
+ mov ah,byte ptr [bp] ; being programmed
+ mov dl,ah
+ and ah,#0x80
+ cmp al,ah ; bit 7 indicates sucess
+ je waitok
+ test dl,#0x20 ; bit 5 indicates timeout/error
+ jz wait1 ; otherwise wait for cmd to complete
+ seg es
+ mov ah,byte ptr [bp] ; check error condition once again,
+ and ah,#0x80 ; because bits 7 and 5 can change
+ cmp al,ah ; simultaneously
+ je waitok
+ stc
+ ret
+waitok: clc
+ ret
+
+;----------------------------------------------------------------------------
+
+; PRNSTR -- prints a string in DS:SI onto the console
+; ======
+;
+; changes: AL
+
+prnstr: push si
+ cld
+prns1: lodsb ; loop over all characters of
+ or al,al ; string
+ jz prns2
+ call prnchr ; print character
+ jmp prns1
+prns2: pop si
+ ret
+
+
+;----------------------------------------------------------------------------
+
+; PRNWRD, PRNBYT, PRNNIB, PRNCHR -- prints hexadezimal values, or ASCII chars
+; ====== ====== ====== ======
+;
+; changes: AX
+
+prnwrd: push ax
+ mov al,ah
+ call prnbyt ; print the upper byte
+ pop ax
+prnbyt: push ax
+ shr al,1 ; prepare upper nibble
+ shr al,1
+ shr al,1
+ shr al,1
+ call prnnib ; print it
+ pop ax
+prnnib: and al,#0x0F ; prepare lower nibble
+ add al,#0x30
+ cmp al,#0x39 ; convert it into hex
+ jle prnchr
+ add al,#7
+prnchr: push bx
+ mov ah,#0x0E ; print it
+ mov bl,#0x07
+ xor bh,bh
+ int 0x10
+ pop bx
+ ret
+
+
+;----------------------------------------------------------------------------
+
+magic: .byte 0xE4,0xE4
+
+badmagic:.byte 0xa,0xd
+ .ascii "There does not appear to be a ROM image attached to the"
+ .ascii "flash EPROM utility;"
+ .byte 0xa,0xd
+resetmsg:.ascii "Reboot to proceed..."
+ .byte 0
+
+welcome:.byte 0xa,0xd
+ .ascii "Flash EPROM programming utility V1.0"
+ .byte 0xa,0xd
+ .ascii "Copyright (c) 1997 by M. Gutschke <gutschk@uni-muenster.de>"
+ .byte 0xa,0xd
+ .ascii "==========================================================="
+ .byte 0xa,0xd
+prompt: .byte 0xa,0xd
+ .ascii "Enter base address for AMD29F010 flash EPROM on FlashCard or"
+ .byte 0xa,0xd
+ .ascii "press <RETURN> to start autoprobing; the base address has"
+ .byte 0xa
+clrline1:.byte 0xd
+ .ascii "to be in the range C800..E600: "
+ .ascii " "
+ .byte 0x8,0x8,0x8,0x8
+ .byte 0
+
+delchar:.byte 0x8,0x20,0x8
+ .byte 0
+
+automsg:.ascii "autoprobing... "
+ .byte 0
+
+failmsg:.ascii "failed!"
+basemsg:.byte 0xa
+clrline2:.byte 0xd
+ .ascii "Enter base address: "
+ .ascii " "
+ .byte 0x8,0x8,0x8,0x8
+ .byte 0
+
+fndmsg: .byte 0xa,0xd
+ .ascii "Found flash EPROM at: "
+ .byte 0
+
+alignmsg:.byte 0xa,0xd
+ .ascii "FlashCard requires the hardware address to be aligned to a"
+ .byte 0xa,0xd
+ .ascii "32kB boundary; automatically adjusting..."
+ .byte 0
+
+nofndmsg:.byte 0xa,0xd
+ .ascii "No AMD29F010 flash EPROM found"
+ .byte 0
+
+ersmsg: .byte 0xa,0xd
+ .ascii "Erasing old contents... "
+ .byte 0
+
+prg1msg:.ascii "done"
+ .byte 0xa,0xd
+ .ascii "Programming from "
+ .byte 0
+
+prg2msg:.ascii ":0000 to "
+ .byte 0
+
+donemsg:.ascii "done!"
+ .byte 0xa,0xd
+ .byte 0
+
+failresmsg:
+ .ascii "failed!"
+ .byte 0xa,0xd
+ .byte 0
+
+
+;----------------------------------------------------------------------------
+
+ .align 16
+ .org *-1
+ .byte 0x00
+romdata:
diff --git a/gpxe/contrib/flashimg/flashimg.img b/gpxe/contrib/flashimg/flashimg.img
new file mode 100644
index 00000000..263d3392
--- /dev/null
+++ b/gpxe/contrib/flashimg/flashimg.img
Binary files differ
diff --git a/gpxe/contrib/hdload/Makefile b/gpxe/contrib/hdload/Makefile
new file mode 100644
index 00000000..9ed750de
--- /dev/null
+++ b/gpxe/contrib/hdload/Makefile
@@ -0,0 +1,15 @@
+# Use nasm or as86
+ASM=nasm
+# ASM=as86
+
+hdload.bin: hdload.S
+ifeq ($(ASM),as86)
+ gcc $(CFLAGS) -DUSE_AS86 -E -traditional -o hdload.s hdload.S
+ as86 -0 -b hdload.bin hdload.s
+else
+ gcc $(CFLAGS) -DUSE_NASM -E -traditional -o hdload.s hdload.S
+ nasm -f bin hdload.s -o hdload.bin
+endif
+
+clean:
+ $(RM) -f hdload.s hdload.bin
diff --git a/gpxe/contrib/hdload/hdload.S b/gpxe/contrib/hdload/hdload.S
new file mode 100644
index 00000000..3bb5649b
--- /dev/null
+++ b/gpxe/contrib/hdload/hdload.S
@@ -0,0 +1,162 @@
+#if !defined(USE_NASM) && !defined(USE_AS86)
+#define USE_AS86
+#endif
+
+#ifdef USE_AS86
+#define CON(x) *x
+#define BCON(x) *x
+#define WCON(x) *x
+#define LOC(x) x
+#define BLOC(x) byte ptr x
+#define WLOC(x) word ptr x
+#define JMP(x) jmp x
+#define STRDECL(s) .ascii s
+#define SEGCS seg cs
+#define SEGES seg es
+#define ALIGN(x) .align x
+#define SPACE(x) .space x
+#endif
+
+#ifdef USE_NASM
+#define CON(x) x
+#define BCON(x) byte x
+#define WCON(x) word x
+#define LOC(x) [x]
+#define BLOC(x) byte [x]
+#define WLOC(x) word [x]
+#define JMP(x) jmp short x
+#define STRDECL(s) db s
+#define SEGCS cs
+#define SEGES es
+#define ALIGN(x) align x, db 0
+#define SPACE(x) times x db 0
+#endif
+
+ROMLOAD equ 0x5000
+
+start:
+ cli
+ xor ax, ax
+ mov ss, ax
+ mov sp, CON(0x7C00)
+ mov si, sp
+ mov es, ax
+ mov ds, ax
+ sti
+ cld
+ mov di, CON(0x600)
+ mov cx, CON(0x100)
+ rep
+ movsw
+ db 0xEA
+ dw jump
+ dw 0
+jump:
+ mov si, CON(Hlaska)
+ call print
+
+ ; rozmery prvniho HD
+ mov ah, CON(8)
+ mov dl, CON(0x80)
+ int 0x13
+ jc chyba
+ ; dh - H, cx - CS
+
+ ; prvi stopa obsahuje bootrom, tak ji natahneme do RAM
+ mov ah, CON(2)
+ mov al, cl
+ and al, CON(0x3F)
+ dec al
+ mov dx, CON(0x80)
+ mov cx, CON(2)
+ mov bx, CON(ROMLOAD)
+ mov es, bx
+ xor bx, bx
+ int 0x13
+ jc chyba
+
+ ; hromada kodu podle zdrojaku netboot
+ xor di, di
+ mov es, di
+ mov di, CON(0x380)
+ push di
+ mov cx, CON(10)
+ cld
+ rep
+ stosw
+ pop di
+#ifdef USE_AS86
+ mov word ptr [ di ], CON(0x5a5a)
+ mov byte ptr [ di + 2 ], CON(0x50)
+ mov word ptr [ di + 0x10 ], CON(0xFFFF)
+ mov word ptr [ di + 0x12 ], CON(0xFFFF)
+#endif
+#ifdef USE_NASM
+ mov word [ di ], CON(0x5a5a)
+ mov byte [ di + 2 ], CON(0x50)
+ mov word [ di + 10h ], CON(0xFFFF)
+ mov word [ di + 12h ], CON(0xFFFF)
+#endif
+
+ ; navratova adresa, kdyby nezabrala ROM
+ SEGCS
+ mov WLOC(OfsErr), CON(RomErr)
+ push cs
+ push WCON(chyba)
+ mov ax, CON(ROMLOAD)
+ mov es, ax
+ push es
+ ; kouzelny jump....
+ SEGES
+ mov si, [ 0x1a ]
+ SEGES
+#ifdef USE_AS86
+ push word ptr [ si + 0x1a ] ; ...do bootrom v RAM
+#endif
+#ifdef USE_NASM
+ push word [ si + 0x1a ] ; ...do bootrom v RAM
+#endif
+ retf
+
+chyba:
+ SEGCS
+ mov si, LOC(OfsErr)
+ call print
+ mov si, CON(CRLF)
+ call print
+ JMP(chyba)
+
+print:
+ lodsb
+ cmp al,CON(0)
+ je navrat
+ push si
+ mov bx,CON(7)
+ mov ah,CON(0x0E)
+ int 0x10
+ pop si
+ JMP(print)
+
+navrat:
+ ret
+
+Hlaska: db 13, 10
+ STRDECL('HD Net Loader v1.0 (c) poli 1999')
+ db 13, 10, 0
+CRLF: db 13, 10, 0
+OfsErr: dw Error
+Error: STRDECL('Error load from HD !')
+ db 0
+RomErr: STRDECL('ROM Error !')
+ db 0
+
+mbrend:
+ ret
+
+#ifdef USE_AS86
+ org 510
+#endif
+#ifdef USE_NASM
+ times 510-($-$$) db 0
+#endif
+ dw 0xAA55
diff --git a/gpxe/contrib/hdload/petr.msg b/gpxe/contrib/hdload/petr.msg
new file mode 100644
index 00000000..a3134d04
--- /dev/null
+++ b/gpxe/contrib/hdload/petr.msg
@@ -0,0 +1,175 @@
+From netboot-owner@baghira.han.de Thu Sep 16 12:08:44 1999
+Return-Path: <netboot-owner@baghira.han.de>
+Received: (from factotum@localhost)
+ by baghira.han.de (8.9.3/8.9.3) id NAA23838
+ for netboot-outgoing; Wed, 15 Sep 1999 13:12:44 +0200
+X-Authentication-Warning: baghira.han.de: factotum set sender to owner-netboot using -f
+Received: from hathi.han.de (root@hathi.han.de [192.109.225.1])
+ by baghira.han.de (8.9.3/8.9.3) with ESMTP id NAA23785
+ for <netboot@baghira.han.de>; Wed, 15 Sep 1999 13:11:02 +0200
+Received: from vsb.cz (root@decsys.vsb.cz [158.196.149.9])
+ by hathi.han.de (8.9.3/8.9.3) with ESMTP id NAA04707
+ for <netboot@baghira.han.de>; Wed, 15 Sep 1999 13:11:00 +0200
+Received: from nwfei1.vsb.cz (nwfei1.vsb.cz [158.196.146.13])
+ by vsb.cz (8.9.3/8.9.1) with ESMTP id NAA22363
+ for <netboot@baghira.han.de>; Wed, 15 Sep 1999 13:10:52 +0200 (MET DST)
+Received: from FEI1/SpoolDir by nwfei1.vsb.cz (Mercury 1.44);
+ 15 Sep 99 13:10:50 +0100
+Received: from SpoolDir by FEI1 (Mercury 1.44); 15 Sep 99 13:10:27 +0100
+Received: from pcd403z.vsb.cz (158.196.146.9) by nwfei1.vsb.cz (Mercury 1.44) with ESMTP;
+ 15 Sep 99 13:10:25 +0100
+Received: from oli10 by pcd403z.vsb.cz with local-esmtp (Exim 2.05 #1 (Debian))
+ id 11RCxI-0000oT-00; Wed, 15 Sep 1999 13:10:28 +0200
+Date: Wed, 15 Sep 1999 13:10:28 +0200 (CEST)
+From: Petr Olivka <Petr.Olivka@vsb.cz>
+To: netboot@baghira.han.de
+Subject: netboot image on hard disk - it is easy
+In-Reply-To: <37DF4BD4.E8FFF8FC@gsmbox.com>
+Message-ID: <Pine.LNX.4.10.9909151247430.2936-100000@pcd403z.vsb.cz>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; charset=US-ASCII
+Sender: owner-netboot@baghira.han.de
+Precedence: bulk
+Reply-To: netboot@baghira.han.de
+X-Moderator: netboot-owner@baghira.han.de
+
+It is good joke, at this moment I have only simple version of MBR to load
+image from HD, but only from track 0. HD have to have enough sectors per
+track for rom image.
+And small program in turbo-pascal to download image to HD.
+
+below is assembler code for MBR. Is writen for tasm and tlink.
+If you have 512 bytes binary file with MBR code, then concat it with
+rom-image and download to hda. BUT NOT DIRECTLY !!!! You have to copy
+partition table ( and NT signature ) to MBR and then download. BUT ONLY tO
+TRACK 0.
+
+Everything in your own risk.
+
+If I will have some free time, I will write some code directly to netboot.
+
+poli
+
+.model large, pascal
+
+.code
+.386
+ public mbrasm, mbrend
+
+ROMLOAD equ 5000h
+
+ org 600h
+
+mbrasm proc
+
+ cli
+ xor ax, ax
+ mov ss, ax
+ mov sp, 7C00h
+ mov si, sp
+ mov es, ax
+ mov ds, ax
+ sti
+ cld
+ mov di, 600h
+ mov cx, 100h
+ rep movsw
+ db 0EAh
+ dw offset @@jump
+ dw 0
+@@jump:
+ mov si, offset Hlaska
+ call @@print
+
+ ; rozmery prvniho HD
+ mov ah, 8
+ mov dl, 80h
+ int 13h
+ jc @@chyba
+ ; dh - H, cx - CS
+
+ ; prvi stopa obsahuje bootrom, tak ji natahneme do RAM
+ mov ah, 2
+ mov al, cl
+ and al, 3Fh
+ dec al
+ mov dx, 80h
+ mov cx, 2
+ mov bx, ROMLOAD
+ mov es, bx
+ xor bx, bx
+ int 13h
+ jc @@chyba
+
+ ; hromada kodu podle zdrojaku netboot
+ xor di, di
+ mov es, di
+ mov di, 380h
+ push di
+ mov cx, 10
+ cld
+ rep stosw
+ pop di
+ mov word ptr [ di ], 5a5ah
+ mov byte ptr [ di + 2 ], 50h
+ mov word ptr [ di + 10h ], 0FFFFh
+ mov word ptr [ di + 12h ], 0FFFFh
+
+ ; navratova adresa, kdyby nezabrala ROM
+ mov OfsErr, offset RomErr
+ push cs
+ push offset @@chyba
+
+ mov ax, ROMLOAD
+ mov es, ax
+ push es
+ ; kouzelny jump....
+ mov si, es:[ 1ah ]
+ push word ptr es:[ si + 1ah ] ; ...do bootrom v RAM
+ retf
+
+@@chyba:
+ mov si, OfsErr
+ call @@print
+ mov si, offset CRLF
+ call @@print
+ jmp @@chyba
+
+@@print:
+ lodsb
+ cmp al,0
+ je @@navrat
+ push si
+ mov bx,7
+ mov ah,0Eh
+ int 10h
+ pop si
+ jmp @@print
+
+@@navrat:
+ retn
+
+Hlaska db 13, 10, 'HD Net Loader v1.0 (c) poli 1999', 13, 10, 0
+CRLF db 13, 10, 0
+OfsErr dw offset Error
+Error db 'Error load from HD !', 0
+RomErr db 'ROM Error !', 0
+
+mbrasm endp
+
+mbrend proc
+ ret
+mbrend endp
+
+ org 800h - 2
+ dw 0AA55h
+
+end
+
+===========================================================================
+This Mail was sent to netboot mailing list by:
+Petr Olivka <Petr.Olivka@vsb.cz>
+To get help about this list, send a mail with 'help' as the only string in
+it's body to majordomo@baghira.han.de. If you have problems with this list,
+send a mail to netboot-owner@baghira.han.de.
+
diff --git a/gpxe/contrib/initrd/ChangeLog b/gpxe/contrib/initrd/ChangeLog
new file mode 100644
index 00000000..94f8f6aa
--- /dev/null
+++ b/gpxe/contrib/initrd/ChangeLog
@@ -0,0 +1,46 @@
+mkinitrd-net ChangeLog
+
+Last Modified: Fri Jul 26 23:08:28 2002
+
+$Log$
+Revision 1.1 2005/05/17 16:45:02 mcb30
+Initial revision
+
+Revision 1.1 2002/11/06 06:31:06 ken_yap
+Contributed by Michael Brown.
+
+Revision 1.10 2002/07/26 23:09:13 mcb30
+Support for new binary etherboot.nic-dev-id structure
+Added --kernel option patch from Stew Benedict at MandrakeSoft
+Only try to use sudo if we are not already root
+
+Revision 1.9 2002/06/05 13:31:50 mcb30
+Modifications to allow DHCP, TFTP and NFS servers to be separate machines.
+
+Revision 1.8 2002/05/30 11:41:18 mcb30
+/tftpboot symlinked to /var/lib/tftpboot
+Has ability to be quiet if "quiet" specified on kernel cmdline
+
+Revision 1.7 2002/05/26 11:15:04 mcb30
+PCI-ID auto-mapping via dhcpd.conf.etherboot-pcimap.include
+
+Revision 1.6 2002/05/24 02:05:11 mcb30
+Bugfixes, migrated /tftpboot to /var/lib/tftpboot
+
+Revision 1.5 2002/05/23 21:29:58 mcb30
+Now includes dhcpd.conf.etherboot.include
+Automatically scans for all network modules in the pcimap file
+
+Revision 1.4 2002/05/08 09:04:31 mcb30
+Bugfixes: tmpdir selection, linuxrc typos, ifconfig peculiarities
+
+Revision 1.3 2002/05/04 21:44:13 mcb30
+During %make, LIBDIR must be set for mknbi
+Added %post scriptlet since %trigger seems not to be being triggered...
+
+Revision 1.2 2002/05/04 21:20:32 mcb30
+Added extra sources instead of requiring "make" to download them
+
+Revision 1.1 2002/05/04 13:19:40 mcb30
+First attempt at an RPM package
+
diff --git a/gpxe/contrib/initrd/Makefile b/gpxe/contrib/initrd/Makefile
new file mode 100644
index 00000000..f18b73f6
--- /dev/null
+++ b/gpxe/contrib/initrd/Makefile
@@ -0,0 +1,187 @@
+UCLIBC_VERSION = 0.9.11
+UCLIBC = uClibc-$(UCLIBC_VERSION)
+$(UCLIBC)_SOURCE = http://www.uclibc.org/downloads/$(UCLIBC).tar.bz2
+UCLIBC_INSTALL = $(CURDIR)/uClibc
+
+UDHCP_VERSION = 0.9.7
+UDHCP = udhcp-$(UDHCP_VERSION)
+$(UDHCP)_SOURCE = http://udhcp.busybox.net/source/$(UDHCP).tar.gz
+
+BUSYBOX_VERSION = 0.60.3
+BUSYBOX = busybox-$(BUSYBOX_VERSION)
+$(BUSYBOX)_SOURCE = http://www.busybox.net/downloads/$(BUSYBOX).tar.bz2
+
+LINUX_WLAN_VERSION = 0.1.13
+LINUX_WLAN = linux-wlan-ng-$(LINUX_WLAN_VERSION)
+$(LINUX_WLAN)_SOURCE = ftp://ftp.linux-wlan.org/pub/linux-wlan-ng/$(LINUX_WLAN).tar.gz
+
+MKNBI_VERSION = 1.2
+MKNBI = mknbi-$(MKNBI_VERSION)
+$(MKNBI)_SOURCE = http://belnet.dl.sourceforge.net/sourceforge/etherboot/$(MKNBI).tar.gz
+
+export PATH := $(UCLIBC_INSTALL)/bin:$(PATH)
+
+all : utils initrd-skel mknbi mknbi-linux
+ # Run "make tftpboot/initrd-kernel_module.img" to generate a suitable initrd
+ # Run "make tftpboot/boot-kernel_module.nbi" to generate a suitable NBI
+ # Run "make all-nbi" to generate a complete set of NBIs
+
+%.tar.bz2 :
+ [ -d $* ] || wget $($*_SOURCE)
+ [ -f $*.t*gz ] && ( gunzip $*.t*gz ; bzip2 -9 $*.tar ) || true
+
+UTILS = udhcpc busybox wlanctl
+
+utils : $(UTILS)
+
+clean : partlyclean
+ rm -rf uClibc
+ rm -rf $(UCLIBC)
+ rm -rf tftpboot/*
+
+partlyclean :
+ rm -rf $(UDHCP)
+ rm -rf $(BUSYBOX)
+ rm -rf $(LINUX_WLAN)
+ rm -rf $(MKNBI)
+ rm -rf initrd-skel
+ rm -f *.img *.ird *.nbi insert-modules
+ rm -f $(UTILS) mknbi-linux
+ rm -f *.uClibc *.busybox *.udhcpc *.wlanctl
+
+.PHONY : all utils clean partlyclean
+
+uClibc : $(UCLIBC)
+ rm -rf $@
+ $(MAKE) -C $(UCLIBC) install
+
+$(UCLIBC) : $(UCLIBC).tar.bz2
+ [ -d $@ ] || tar xvjf $<
+ [ -f $(UCLIBC)/Config ] || perl -pe 's/^(INCLUDE_RPC).*/$$1 = true/ ;' \
+ -e 's{^(DEVEL_PREFIX).*}{$$1 = $(UCLIBC_INSTALL)} ;' \
+ -e 's{^(SHARED_LIB_LOADER_PATH).*}{$$1 = /lib} ;' \
+ $(UCLIBC)/extra/Configs/Config.i386 > $(UCLIBC)/Config
+ # Stripping out spurious CVS directories (screws up local cvs update)
+ rm -rf `find $(UCLIBC) -name CVS`
+ $(MAKE) -C $(UCLIBC)
+ install -m 644 $(UCLIBC)/COPYING.LIB COPYING.uClibc
+
+udhcpc : $(UDHCP)
+ install -m 755 -s $(UDHCP)/$@ $@
+
+$(UDHCP) : $(UDHCP).tar.bz2 uClibc
+ [ -d $@ ] || tar xvjf $<
+ if [ ! -f $@/.script.c.patch ]; then \
+ patch -d $@ -b -z .orig < script.c.patch ; \
+ touch $@/.script.c.patch ; \
+ fi
+ $(MAKE) LDFLAGS+=-static -C $(UDHCP)
+ install -m 644 $(UDHCP)/AUTHORS AUTHORS.udhcpc
+ install -m 644 $(UDHCP)/COPYING COPYING.udhcpc
+
+busybox : $(BUSYBOX)
+ install -m 755 -s $(BUSYBOX)/$@ $@
+
+$(BUSYBOX) : $(BUSYBOX).tar.bz2 uClibc
+ [ -d $@ ] || tar xvjf $<
+ perl -pi.orig -e \
+ 's/^.*(#define BB_(FEATURE_NFSMOUNT|INSMOD|PIVOT_ROOT|IFCONFIG|ROUTE)).*/$$1/' \
+ $(BUSYBOX)/Config.h
+ perl -pi.orig -e \
+ 's/^(DOSTATIC).*$$/$$1 = true/' \
+ $(BUSYBOX)/Makefile
+ $(MAKE) -C $(BUSYBOX)
+ install -m 644 $(BUSYBOX)/AUTHORS AUTHORS.busybox
+ install -m 644 $(BUSYBOX)/LICENSE LICENSE.busybox
+
+wlanctl : $(LINUX_WLAN)
+ install -m 755 -s $(LINUX_WLAN)/src/wlanctl/$@ $@
+
+$(LINUX_WLAN) : $(LINUX_WLAN).tar.bz2 uClibc linux-wlan.cfg
+ [ -d $@ ] || tar xvjf $<
+ cd $(LINUX_WLAN) ; ./Configure -d ../linux-wlan.cfg
+ perl -pi.orig -e \
+ 's/(-o wlanctl)/-static $$1/' \
+ $(LINUX_WLAN)/src/wlanctl/Makefile
+ $(MAKE) -C $(LINUX_WLAN)/src/wlanctl
+ install -m 644 $(LINUX_WLAN)/COPYING COPYING.wlanctl
+ install -m 644 $(LINUX_WLAN)/LICENSE LICENSE.wlanctl
+ install -m 644 $(LINUX_WLAN)/THANKS THANKS.wlanctl
+
+mknbi-linux : $(MKNBI)
+
+mknbi : $(MKNBI)
+
+$(MKNBI) : $(MKNBI).tar.bz2
+ [ -d $@ ] || tar xvjf $<
+ if [ ! -f $@/.mknbi-encap.patch ]; then \
+ patch -d $@ -b -z .orig < mknbi-encap.patch ; \
+ touch $@/.mknbi-encap.patch ; \
+ fi
+ make -C $(MKNBI) LIBDIR=`pwd`/$(MKNBI) mknbi
+ install -m 755 $(MKNBI)/mknbi mknbi-linux
+ make -C $(MKNBI) clean
+ make -C $(MKNBI)
+
+initrd-skel : $(UTILS) linuxrc udhcpc-post include-modules
+ rm -rf $@
+ mkdir -p $@
+ mkdir -p $@/dev
+ mkdir -p $@/etc
+ mkdir -p $@/bin
+ mkdir -p $@/lib
+ mkdir -p $@/lib/modules
+ mkdir -p $@/proc
+ mkdir -p $@/sysroot
+ ln -s bin $@/sbin
+ install -m 755 busybox $@/bin/
+ install -m 755 udhcpc $@/bin/
+ install -m 755 wlanctl $@/bin/
+ ln -s busybox $@/bin/sh
+ ln -s busybox $@/bin/echo
+ ln -s busybox $@/bin/mknod
+ ln -s busybox $@/bin/chmod
+ ln -s busybox $@/bin/insmod
+ ln -s busybox $@/bin/ifconfig
+ ln -s busybox $@/bin/route
+ ln -s busybox $@/bin/mount
+ ln -s busybox $@/bin/pivot_root
+ ln -s busybox $@/bin/umount
+ ln -s busybox $@/bin/[
+ ln -s busybox $@/bin/sleep
+ ln -s busybox $@/bin/grep
+
+ install -m 755 linuxrc $@/linuxrc
+ install -m 755 udhcpc-post $@/bin/udhcpc-post
+
+tftpboot/initrd-%.img : initrd-skel
+ ./mkinitrd-net -l `echo $* | tr . " "`
+
+tftpboot/boot-%.nbi : tftpboot/initrd-%.img mknbi-linux
+ ./mknbi-linux --format=nbi --target=linux /boot/vmlinuz $< > $@
+ sudo cp $@ $(tftpbootdir)
+
+all-nbi : all
+ ./mknbi-set -l -v
+ ls tftpboot
+
+prefix = /usr
+sysconfdir = /etc
+bindir = $(prefix)/bin
+libdir = $(prefix)/lib
+mandir = $(prefix)/share/man
+docdir = $(prefix)/share/doc
+tftpbootdir = /var/lib/tftpboot
+initrdskeldir = $(prefix)/lib/mkinitrd-net/initrd-skel
+
+install :
+ mkdir -p $(libdir)/mknbi
+ mkdir -p $(bindir)
+ mkdir -p $(sysconfdir)
+ mkdir -p $(tftpbootdir)
+ mkdir -p $(initrdskeldir)
+ install -m 755 mkinitrd-net include-modules mknbi-set $(bindir)/
+ cp -a initrd-skel/* $(initrdskeldir)/
+ install -m 644 mknbi-set.conf dhcpd.conf.etherboot.include $(sysconfdir)
+ make -C $(MKNBI) INSTPREFIX=$(prefix) MANDIR=$(mandir)/man1 \
+ DOCDIR=$(docdir)/$(MKNBI) install
diff --git a/gpxe/contrib/initrd/Manifest b/gpxe/contrib/initrd/Manifest
new file mode 100644
index 00000000..b41e7255
--- /dev/null
+++ b/gpxe/contrib/initrd/Manifest
@@ -0,0 +1,15 @@
+initrd/ChangeLog
+initrd/Makefile
+initrd/Manifest
+initrd/README
+initrd/dhcpd.conf.etherboot.include
+initrd/include-modules
+initrd/linux-wlan.cfg
+initrd/linuxrc
+initrd/mkinitrd-net
+initrd/mkinitrd-net.spec
+initrd/mknbi-encap.patch
+initrd/mknbi-set
+initrd/mknbi-set.conf
+initrd/script.c.patch
+initrd/udhcpc-post
diff --git a/gpxe/contrib/initrd/README b/gpxe/contrib/initrd/README
new file mode 100644
index 00000000..5152425a
--- /dev/null
+++ b/gpxe/contrib/initrd/README
@@ -0,0 +1,37 @@
+README for mkinitrd-net
+
+mkinitrd-net enables you to use your distribution's stock kernel for
+diskless workstations, without having to compile in support for the
+relevant network card(s). It creates an initial ramdisk image containing
+the required network-card kernel modules and bootstrap scripts to load the
+module, obtain an IP address via DHCP and mount the root filesystem via
+NFS.
+
+mkinitrd-net also generates a dhcpd.conf file fragment that can be used to
+automate the process of mapping NBI files to clients, based on the PCI IDs
+of their network cards. Etherboot will send the PCI ID of the network
+card to the DHCP server in the etherboot-encapsulated-options field
+(Etherboot 5.0.7 and newer) and the DHCP server can use this to identify
+the correct NBI to point the client towards.
+
+The end result is that:
+
+a) You can avoid the hassle of compiling custom kernels for diskless
+ workstations.
+
+b) Diskless workstations will automatically download the correct
+ kernel+initrd.
+
+c) You have an easier life! :-)
+
+
+
+mkinitrd-net is Copyright Fen Systems Ltd. 2001. mkinitrd-net itself is
+licensed under the GNU GPL. It incorporates code from the uClibc,
+busybox, udhcpc and Etherboot projects, each of which has its own licence
+terms. Standard disclaimers apply.
+
+The copy of mkinitrd-net in the Etherboot contribs is not the
+authoritative copy of mkinitrd-net; please do not make modifications to
+this copy. Patches should be sent to Michael Brown
+<mbrown@fensystems.co.uk>.
diff --git a/gpxe/contrib/initrd/dhcpd.conf.etherboot.include b/gpxe/contrib/initrd/dhcpd.conf.etherboot.include
new file mode 100644
index 00000000..9cec1dc1
--- /dev/null
+++ b/gpxe/contrib/initrd/dhcpd.conf.etherboot.include
@@ -0,0 +1,207 @@
+# dhcpd.conf include file for Etherboot
+#
+# Include this file from your /etc/dhcpd.conf
+# $Id$
+
+# Definition of Etherboot options
+# (taken from vendortags.html)
+
+# We use an encapsulated option space to avoid polluting the site-local DHCP option space
+#
+option space etherboot;
+option etherboot-encapsulated-options code 150 = encapsulate etherboot;
+
+# Definition of option codes within the etherboot-encapsulated-options space
+#
+option etherboot.extensions-path code 18 = string;
+option etherboot.magic code 128 = string;
+option etherboot.kernel-cmdline code 129 = string;
+option etherboot.menu-opts code 160 = string;
+option etherboot.nic-dev-id code 175 = string;
+option etherboot.menu-selection code 176 = unsigned integer 8;
+option etherboot.motd-1 code 184 = string;
+option etherboot.motd-2 code 185 = string;
+option etherboot.motd-3 code 186 = string;
+option etherboot.motd-4 code 187 = string;
+option etherboot.motd-5 code 188 = string;
+option etherboot.motd-6 code 189 = string;
+option etherboot.motd-7 code 190 = string;
+option etherboot.motd-8 code 191 = string;
+option etherboot.image-1 code 192 = string;
+option etherboot.image-2 code 193 = string;
+option etherboot.image-3 code 194 = string;
+option etherboot.image-4 code 195 = string;
+option etherboot.image-5 code 196 = string;
+option etherboot.image-6 code 197 = string;
+option etherboot.image-7 code 198 = string;
+option etherboot.image-8 code 199 = string;
+option etherboot.image-9 code 200 = string;
+option etherboot.image-10 code 201 = string;
+option etherboot.image-11 code 202 = string;
+option etherboot.image-12 code 203 = string;
+option etherboot.image-13 code 204 = string;
+option etherboot.image-14 code 205 = string;
+option etherboot.image-15 code 206 = string;
+option etherboot.image-16 code 207 = string;
+option etherboot.kmod code 254 = string;
+
+# Legacy support for Etherboot options as site-local options (i.e. non-encapsulated)
+# Note: options defined after the switch to encapsulated options should not be defined here
+#
+option legacy-etherboot-magic code 128 = string;
+option legacy-etherboot-kernel-cmdline code 129 = string;
+option legacy-etherboot-menu-opts code 160 = string;
+option legacy-etherboot-menu-selection code 176 = unsigned integer 8;
+option legacy-etherboot-motd-1 code 184 = string;
+option legacy-etherboot-motd-2 code 185 = string;
+option legacy-etherboot-motd-3 code 186 = string;
+option legacy-etherboot-motd-4 code 187 = string;
+option legacy-etherboot-motd-5 code 188 = string;
+option legacy-etherboot-motd-6 code 189 = string;
+option legacy-etherboot-motd-7 code 190 = string;
+option legacy-etherboot-motd-8 code 191 = string;
+option legacy-etherboot-image-1 code 192 = string;
+option legacy-etherboot-image-2 code 193 = string;
+option legacy-etherboot-image-3 code 194 = string;
+option legacy-etherboot-image-4 code 195 = string;
+option legacy-etherboot-image-5 code 196 = string;
+option legacy-etherboot-image-6 code 197 = string;
+option legacy-etherboot-image-7 code 198 = string;
+option legacy-etherboot-image-8 code 199 = string;
+option legacy-etherboot-image-9 code 200 = string;
+option legacy-etherboot-image-10 code 201 = string;
+option legacy-etherboot-image-11 code 202 = string;
+option legacy-etherboot-image-12 code 203 = string;
+option legacy-etherboot-image-13 code 204 = string;
+option legacy-etherboot-image-14 code 205 = string;
+option legacy-etherboot-image-15 code 206 = string;
+option legacy-etherboot-image-16 code 207 = string;
+
+# Apply Etherboot options only for Etherboot clients
+#
+if substring ( option vendor-class-identifier, 0, 9 ) = "Etherboot" {
+
+ # We must specify this value for etherboot-magic, or Etherboot will
+ # ignore all other options.
+ #
+ option etherboot.magic E4:45:74:68:00:00;
+
+ # Bootfile name: derive from etherboot.kmod (calculated below)
+ # Use boot.nbi if no NIC_DEV_ID option present
+ # (i.e. if etherboot.kmod doesn't get set)
+ # Also pass filename back in filename field
+ #
+ option bootfile-name = pick-first-value ( concat ( "boot-",
+ config-option etherboot.kmod,
+ ".nbi" ),
+ "boot.nbi" ) ;
+ filename = config-option bootfile-name;
+
+ # "Sensible" default values for some options
+
+ # Mount devfs (will probably be needed for a network-boot)
+ option etherboot.kernel-cmdline " devfs=mount";
+
+ # Info message (includes client IP address, MAC address, hardware ID string,
+ # server IP address and name of boot file)
+ option etherboot.motd-4 = concat ( "Using Etherboot to boot ",
+ binary-to-ascii ( 10, 8, ".", leased-address ),
+ " [",
+ binary-to-ascii ( 16, 8, ":", suffix ( hardware, 6 ) ),
+ "] [",
+ pick-first-value ( option etherboot.nic-dev-id, "unknown card" ),
+ "]", 0d:0a, " from ",
+ binary-to-ascii ( 10, 8, ".", option dhcp-server-identifier ),
+ " with file ",
+ config-option tftp-server-name,
+ ":",
+ config-option bootfile-name,
+ " [",
+ pick-first-value ( config-option etherboot.kmod, "unknown module" ),
+ "]", 0d:0a );
+
+ # Legacy site-local option support
+ # If client does not include an etherboot-encapsulated-options field in its DHCPREQUEST, then
+ # it will not understand etherboot-encapsulated-options in the DHCPACK and so we must send
+ # back the options as site-local options (i.e. not encapsulated).
+ # Note: we need do this only for options that existed prior to the switch to encapsulation.
+ #
+ if not exists etherboot-encapsulated-options {
+ option legacy-etherboot-magic = config-option etherboot.magic;
+ option legacy-etherboot-kernel-cmdline = config-option etherboot.kernel-cmdline;
+ option legacy-etherboot-menu-opts = config-option etherboot.menu-opts;
+ option legacy-etherboot-menu-selection = config-option etherboot.menu-selection;
+ option legacy-etherboot-motd-1 = config-option etherboot.motd-1;
+ option legacy-etherboot-motd-2 = config-option etherboot.motd-2;
+ option legacy-etherboot-motd-3 = config-option etherboot.motd-3;
+ option legacy-etherboot-motd-4 = config-option etherboot.motd-4;
+ option legacy-etherboot-motd-5 = config-option etherboot.motd-5;
+ option legacy-etherboot-motd-6 = config-option etherboot.motd-6;
+ option legacy-etherboot-motd-7 = config-option etherboot.motd-7;
+ option legacy-etherboot-motd-8 = config-option etherboot.motd-8;
+ option legacy-etherboot-image-1 = config-option etherboot.image-1;
+ option legacy-etherboot-image-2 = config-option etherboot.image-2;
+ option legacy-etherboot-image-3 = config-option etherboot.image-3;
+ option legacy-etherboot-image-4 = config-option etherboot.image-4;
+ option legacy-etherboot-image-5 = config-option etherboot.image-5;
+ option legacy-etherboot-image-6 = config-option etherboot.image-6;
+ option legacy-etherboot-image-7 = config-option etherboot.image-7;
+ option legacy-etherboot-image-8 = config-option etherboot.image-8;
+ option legacy-etherboot-image-9 = config-option etherboot.image-9;
+ option legacy-etherboot-image-10 = config-option etherboot.image-10;
+ option legacy-etherboot-image-11 = config-option etherboot.image-11;
+ option legacy-etherboot-image-12 = config-option etherboot.image-12;
+ option legacy-etherboot-image-13 = config-option etherboot.image-13;
+ option legacy-etherboot-image-14 = config-option etherboot.image-14;
+ option legacy-etherboot-image-15 = config-option etherboot.image-15;
+ option legacy-etherboot-image-16 = config-option etherboot.image-16;
+ }
+}
+
+# Some options should be set for both Etherboot and the udhcpc client
+#
+if ( ( substring ( option vendor-class-identifier, 0, 9 ) = "Etherboot" )
+ or ( substring ( option vendor-class-identifier, 0, 5 ) = "udhcp" ) ) {
+
+ # TFTP server defaults to DHCP server and is specified in both
+ # next-server field and tftp-server-name option field
+ #
+ option tftp-server-name = binary-to-ascii ( 10, 8, ".", config-option dhcp-server-identifier );
+ server-name = config-option tftp-server-name;
+ next-server = config-option dhcp-server-identifier;
+
+ # Root path defaults to root of TFTP server
+ option root-path = concat ( config-option tftp-server-name, ":/" );
+
+ # A fallback hostname, generated from the IP address
+ option host-name = concat ( "client_", binary-to-ascii ( 10, 8, "_", leased-address ) );
+}
+
+# Force some items onto parameter request list for udhcp
+#
+if substring ( option vendor-class-identifier, 0, 5 ) = "udhcp" {
+ # Forcibly add root-path to list
+ option dhcp-parameter-request-list = concat ( option dhcp-parameter-request-list, 11 );
+}
+
+# Etherboot sends a string to identify the NIC in etherboot.nic-dev-id.
+# For PCI NICs, this string is of the form "PCI:vvvv:dddd" where vvvv is the
+# vendor identifier and dddd the device identifier, in lower-case ASCII hex.
+# For ISA NICs, the format of the string is "ISA:..." where ... is not yet
+# decided upon.
+#
+# We use the identifier to select the NBI image that will be specified via
+# the "bootfile-name" option.
+#
+# PCI NICs - use PCI vendor and device IDs
+# Listed in file generated by mknbi-set
+#
+include "/etc/dhcpd.conf.etherboot-pcimap.include";
+
+# ISA NICs
+#
+if substring ( option vendor-class-identifier, 0, 9 ) = "Etherboot" {
+ if exists etherboot.nic-dev-id {
+
+ }
+}
diff --git a/gpxe/contrib/initrd/include-modules b/gpxe/contrib/initrd/include-modules
new file mode 100755
index 00000000..60e76fc6
--- /dev/null
+++ b/gpxe/contrib/initrd/include-modules
@@ -0,0 +1,63 @@
+#!/usr/bin/perl -w
+#
+# Retrieve modules required for an initrd image
+# $Id$
+
+unless ( @ARGV ) {
+ die "Syntax: $0 [ -d target_directory ] module_1 module_2 module_3\n"
+}
+
+# Parse command line arguments
+my @requested_modules = ();
+my $target_dir = "";
+my $kernel_ver;
+my $quiet;
+chomp ( my $current_kernel_ver = `uname -r` );
+while ( $_ = shift ) {
+ if ( /-d/ ) { $target_dir = shift }
+ elsif ( /-k/ ) { $kernel_ver = shift }
+ elsif ( /-q/ ) { $quiet = 1 }
+ else { push @requested_modules, $_ };
+}
+
+# Create target directory if required
+if ( $target_dir ) {
+ print STDERR "Target directory is $target_dir\n" unless $quiet;
+ system ( "mkdir -p $target_dir" );
+ chdir $target_dir;
+}
+
+# Use modprobe -nav to retrieve locations of modules and their dependencies
+print STDERR "Requested modules ". join (' ', @requested_modules)."\n" unless $quiet;
+my @modules_dups;
+foreach my $module ( @requested_modules ) {
+ my @module_list = map { /^\S+\s+(.*)$/ ; $1 } `/sbin/modprobe -nva $module`;
+ die "Cannot find any modules matching $module\n" unless @module_list;
+ push @modules_dups, @module_list;
+}
+
+# Remove duplicates from list
+my %module_basenames = ();
+my @modules = ();
+foreach my $module ( @modules_dups ) {
+ # Ugly hack : assume that dependencies are independent of kernel version
+ # This seems to be necessary because we can't run modprobe and specify
+ # an alternate modules.dep file; it refuses to understand lines of the
+ # form "depfile=XXX" as documented in modules.conf(5)
+ $module =~ s/$current_kernel_ver/$kernel_ver/ if $kernel_ver;
+ push @modules, $module unless $module_basenames{$module};
+ ( my $basename ) = ( $module =~ /([^\/]+)\.o/ );
+ $module_basenames{$module} = $basename;
+}
+
+# Process module list
+print "#!/bin/sh\n";
+foreach my $module ( @modules ) {
+ my $basename = $module_basenames{$module};
+ # Report via stdout
+ print STDERR "Using module $basename from $module\n" unless $quiet;
+ # Copy uncompressed module to current directory
+ system ("gunzip -c $module > $basename.o");
+ # Print insmod line to stdout
+ print "insmod $basename\n";
+}
diff --git a/gpxe/contrib/initrd/linux-wlan.cfg b/gpxe/contrib/initrd/linux-wlan.cfg
new file mode 100644
index 00000000..7df4a059
--- /dev/null
+++ b/gpxe/contrib/initrd/linux-wlan.cfg
@@ -0,0 +1,7 @@
+# Dummy config file for building only wlanctl
+# $Id$
+
+PRISM2_PLX=n
+PRISM2_PCMCIA=n
+PRISM2_PCI=n
+PRISM2_USB=n
diff --git a/gpxe/contrib/initrd/linuxrc b/gpxe/contrib/initrd/linuxrc
new file mode 100644
index 00000000..24bdb0df
--- /dev/null
+++ b/gpxe/contrib/initrd/linuxrc
@@ -0,0 +1,76 @@
+#!/bin/sh
+# $Id$
+
+PATH=/sbin:/bin
+
+echo Busybox /linuxrc starting
+
+echo Mounting /proc filesystem
+mount -t proc none /proc
+
+echo=echo
+if grep '\bquiet\b' /proc/cmdline > /dev/null; then
+ echo=true
+ quiet=1
+fi
+
+$echo Creating root device
+mknod /dev/root b 1 0 2>/dev/null
+chmod 700 /dev/root
+echo 0x100 > /proc/sys/kernel/real-root-dev
+
+$echo Inserting modules
+if [ -z "$quiet" ]; then
+ /bin/insert-modules
+else
+ /bin/insert-modules >/dev/null
+fi
+
+$echo Bringing up loopback interface
+ifconfig lo 127.0.0.1 up
+route add -net 127.0.0.0 netmask 255.0.0.0 lo
+
+# Hack required for prism2 cards
+# It is not yet possible to use iwconfig to configure these cards,
+# so we need wlanctl.
+if ifconfig wlan0 down 2> /dev/null; then
+ $echo Setting up wireless link
+ wlanctl wlan0 lnxreq_ifstate ifstate=enable
+ wlanctl wlan0 lnxreq_autojoin ssid= authtype=opensystem
+fi
+
+$echo Obtaining IP address via DHCP
+$echo Trying to obtain IP address via wired link [eth0]
+if udhcpc -i eth0 -f -n -q -s /bin/udhcpc-post; then
+ $echo Successfully obtained IP address via wired link [eth0]
+else
+ $echo Failed to obtain IP address via wired link [eth0]
+ $echo Trying to obtain IP address via wireless link [wlan0]
+ udhcpc -i wlan0 -f -n -q -s /bin/udhcpc-post
+fi
+
+if [ -d /sysroot/initrd ]; then
+ $echo Unmounting /proc prior to pivot_root
+ umount /proc
+
+ $echo Pivoting root to /sysroot
+ pivot_root /sysroot /sysroot/initrd
+ cd /
+
+ $echo Remounting devfs at correct place
+ mount -t devfs none /dev
+
+ $echo Releasing locks on old devfs
+ exec 0</dev/null
+ exec 1>/dev/console
+ exec 2>/dev/console
+
+ $echo Unmounting old devfs
+ umount /initrd/dev
+else
+ # Failed to mount root: report error and hang
+ echo FATAL ERROR: Failed to mount root filesystem
+ echo Press Alt-SysRq-B or hit the reset switch to reboot
+ while : ; do sleep 6000 ; done
+fi
+
diff --git a/gpxe/contrib/initrd/mkinitrd-net b/gpxe/contrib/initrd/mkinitrd-net
new file mode 100755
index 00000000..0c95ebd1
--- /dev/null
+++ b/gpxe/contrib/initrd/mkinitrd-net
@@ -0,0 +1,165 @@
+#!/bin/sh
+#
+# $Id$
+# initrd builder for network booting
+
+# Utility function to determine whether or not a filesystem is usable for
+# loopback mounts. Lifted verbatim from Erik Troan's mkinitrd script.
+#
+is_good_fs() {
+ local parttype= tmpname=
+ local dir=$1
+ [[ -d $dir ]] || return 1
+ [[ -w $dir ]] || return 1
+ [[ $dir == */ ]] && dir=${dir%/}
+ parttype=$(awk "{if (\$2 == \""$dir"\") print \$3 }" /proc/mounts)
+
+ while tmpname=${dir%/*} && [[ -z $parttype ]];do
+ [[ -z $tmpname ]] && tmpname=/
+ parttype=$(awk "{if (\$2 == \""$tmpname"\") print \$3 }" /proc/mounts)
+ dir=$tmpname
+ done
+
+ case $parttype in
+ nfs|tmpfs) return 1;;
+ *) return 0;
+ esac
+}
+
+# Find a suitable temporary directory (i.e. not tmpfs or nfs)
+if is_good_fs $TMPDIR; then
+ tmpdir=$TMPDIR
+elif is_good_fs /tmp; then
+ tmpdir=/tmp
+elif is_good_fs /var/tmp; then
+ tmpdir=/var/tmp
+elif is_good_fs /root/tmp; then
+ tmpdir=/root/tmp
+else
+ echo "Cannot use a tmp directory" >&2
+ exit 1
+fi
+
+# Default settings (some can be overridden by command-line options)
+include_modules=include-modules
+initrd_skel=/usr/lib/mkinitrd-net/initrd-skel
+kernel_ver=`uname -r`
+use_sudo=y
+keep=n
+output_dir=/var/lib/tftpboot
+make_link=y
+quiet=
+
+# No need to use sudo if we are root
+if [ $UID -eq 0 ]; then
+ use_sudo=n
+fi
+
+USAGE="Usage: $0 [-k|--kernel <kernel_ver>] [-n|--nolink] [-q|--quiet] [-l|--local] [--nosudo] [--keep] [--help] module_list ..."
+
+# Parse command-line options
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -l|--local)
+ shift
+ use_local=y ;;
+ -k|--kernel)
+ shift
+ kernel_ver=$1
+ shift ;;
+ --nosudo) shift ; use_sudo=n ;;
+ --keep) shift ; keep=y ;;
+ --n|--nolink)
+ shift ; make_link=n ;;
+ -q|--quiet) shift ; quiet=-q ;;
+ --help) shift ; do_help=y ;;
+ --) shift ; break ;;
+ -*) echo "${0}: ${1}: invalid option" >&2
+ echo $USAGE >& 2
+ exit 2 ;;
+ *) break ;;
+ esac
+done
+
+# Build list of requested modules
+modules="$*"
+requested_modules="$modules"
+modules="$modules nfs" # Always require nfs for nfs mount
+modules="$modules af_packet" # Always require af_packet for udhcpc
+
+# --help => Print help message
+if [ "$do_help" == "y" ]; then
+ echo $USAGE
+ echo " -k, --kernel Specify kernel version"
+ echo " -n, --nolink Do not create a matching symbolic link"
+ echo " -l, --local Run locally from CVS (for developers only)"
+ echo " --nosudo Do not use sudo (i.e. must run as root instead)"
+ echo " --keep Keep temporary files instead of deleting them"
+ exit 0;
+fi
+
+# --local => we are running directly from CVS, rather than
+# from an installed copy, so use local files and directories
+if [ "$use_local" == "y" ]; then
+ include_modules=./include-modules
+ initrd_skel=initrd-skel
+ output_dir=tftpboot
+fi
+
+# If use_sudo is set, check that sudo exists
+sudo=/usr/bin/sudo
+if [ "$use_sudo" == "y" ]; then
+ if [ ! -x $sudo ]; then
+ use_sudo=n
+ echo "WARNING: --nosudo not specified but $sudo not found"
+ fi
+fi
+if [ "$use_sudo" == "n" ]; then
+ sudo=
+fi
+
+# Create temporary working files
+initrd=`mktemp -d ${tmpdir}/initrd.XXXXXX`
+initrdimg=`mktemp ${tmpdir}/initrd.img.XXXXXX`
+initrdmnt=`mktemp -d ${tmpdir}/initrd.mnt.XXXXXX`
+
+# Copy skeleton into temporary area
+cp -a $initrd_skel/* $initrd/
+mkdir -p $initrd/lib/modules/$kernel_ver
+$include_modules $quiet -k $kernel_ver -d $initrd/lib/modules/$kernel_ver $modules > $initrd/bin/insert-modules || exit 1
+chmod 755 $initrd/bin/insert-modules
+
+# Create empty ext2fs image file
+dd if=/dev/zero bs=1k of=$initrdimg count=$((`du -sk $initrd | cut -f1` * 7 / 6)) 2> /dev/null
+/sbin/mke2fs -q -F $initrdimg 2> /dev/null
+
+# Mount image file, copy files on, create /dev entries, display free space, umount
+$sudo mount -o loop $initrdimg $initrdmnt
+cp -a $initrd/* $initrdmnt/
+$sudo mknod $initrdmnt/dev/console c 5 1
+$sudo mknod $initrdmnt/dev/null c 1 3
+$sudo mknod $initrdmnt/dev/ram b 1 1
+$sudo mknod $initrdmnt/dev/systty c 4 0
+for i in 1 2 3 4; do $sudo mknod $initrdmnt/dev/tty$i c 4 $i; done
+if [ "$quiet" == "n" ]; then
+ df -h $initrdmnt
+fi
+$sudo umount $initrdmnt
+
+# Create output file
+initrd_suffix=`echo $requested_modules | tr " " .`
+gzip -9 -n -c $initrdimg > $output_dir/initrd-$initrd_suffix.$kernel_ver.img
+
+# Create symlink
+if [ "$make_link" == "y" ]; then
+ link=$output_dir/initrd-$initrd_suffix.img
+ [ -L $link ] && rm -f $link
+ ln -s initrd-$initrd_suffix.$kernel_ver.img $link
+fi
+
+# Remove temporary files
+if [ "$keep" == "n" ]; then
+ rm -rf $initrd
+ rm -f $initrdimg
+ rmdir $initrdmnt
+fi
diff --git a/gpxe/contrib/initrd/mkinitrd-net.spec b/gpxe/contrib/initrd/mkinitrd-net.spec
new file mode 100644
index 00000000..94f5d9db
--- /dev/null
+++ b/gpxe/contrib/initrd/mkinitrd-net.spec
@@ -0,0 +1,112 @@
+%define name mkinitrd-net
+%define version 1.10
+%define release 1fs
+
+Summary: Network-booting initrd builder
+Name: %{name}
+Version: %{version}
+Release: %{release}
+Source0: %{name}-%{version}.tar.bz2
+Source1: http://belnet.dl.sourceforge.net/sourceforge/etherboot/mknbi-1.2.tar.bz2
+Source2: http://www.busybox.net/downloads/busybox-0.60.3.tar.bz2
+Source3: http://www.uclibc.org/downloads/uClibc-0.9.11.tar.bz2
+Source4: ftp://ftp.linux-wlan.org/pub/linux-wlan-ng/linux-wlan-ng-0.1.13.tar.bz2
+Source5: http://udhcp.busybox.net/source/udhcp-0.9.7.tar.bz2
+Copyright: GPL/LGPL/MPL
+Group: System/Kernel and hardware
+BuildRoot: %{_tmppath}/%{name}-buildroot
+Prefix: %{_prefix}
+Requires: tftp-server
+
+%description
+mkinitrd-net allows you to build initial ramdisk images (initrds) suitable
+for use with Etherboot and other network-booting software. This package
+contains two main utilities: mkinitrd-net (to build an initrd containing a
+specified set of network-card modules) and mknbi (to generate
+Etherboot-usable NBI images from a given kernel and initrd). It also
+contains a helper script mknbi-set which will maintain sets of initrds to
+match all your currently-installed kernels.
+
+mkinitrd-net uses code from the uClibc, busybox, udhcp and Etherboot
+projects.
+
+%prep
+%setup -n initrd -a1 -a2 -a3 -a4 -a5
+
+%build
+%make LIBDIR=%{_libdir}/mknbi
+
+%install
+rm -rf $RPM_BUILD_ROOT
+%makeinstall tftpbootdir=$RPM_BUILD_ROOT%{_localstatedir}/tftpboot
+touch $RPM_BUILD_ROOT%{_sysconfdir}/dhcpd.conf.etherboot-pcimap.include
+ln -s %{_localstatedir}/tftpboot $RPM_BUILD_ROOT/tftpboot
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+%{_bindir}/mknbi-set
+
+%triggerin -- kernel kernel-smp kernel-secure kernel-enterprise
+%{_bindir}/mknbi-set
+
+%files
+%defattr(-,root,root)
+%config(noreplace) %{_sysconfdir}/mknbi-set.conf
+%config(noreplace) %{_sysconfdir}/dhcpd.conf.etherboot.include
+%ghost %{_sysconfdir}/dhcpd.conf.etherboot-pcimap.include
+%{_bindir}/mknbi-*
+%{_bindir}/mkelf-*
+%{_bindir}/dis*
+%{_bindir}/mkinitrd-net
+%{_bindir}/include-modules
+%{_libdir}/mknbi
+%{_libdir}/mkinitrd-net
+%{_mandir}/man*/*
+/tftpboot
+%{_localstatedir}/tftpboot
+%doc README
+%doc AUTHORS.busybox LICENSE.busybox
+%doc AUTHORS.udhcpc COPYING.udhcpc
+%doc COPYING.wlanctl LICENSE.wlanctl THANKS.wlanctl
+%doc COPYING.uClibc
+%docdir %{_docdir}/mknbi*
+%{_docdir}/mknbi*
+
+%changelog
+* Fri Jul 26 2002 Michael Brown <mbrown@fensystems.co.uk> 1.10-1fs
+- Support for new binary etherboot.nic-dev-id structure
+- Added --kernel option patch from Stew Benedict at MandrakeSoft
+- Only try to use sudo if we are not already root
+
+* Wed Jun 05 2002 Michael Brown <mbrown@fensystems.co.uk> 1.9-1fs
+- Modifications to allow DHCP, TFTP and NFS servers to be separate machines.
+
+* Thu May 30 2002 Michael Brown <mbrown@fensystems.co.uk> 1.8-1fs
+- /tftpboot symlinked to /var/lib/tftpboot
+- Has ability to be quiet if "quiet" specified on kernel cmdline
+
+* Sun May 26 2002 Michael Brown <mbrown@fensystems.co.uk> 1.7-1fs
+- PCI-ID auto-mapping via dhcpd.conf.etherboot-pcimap.include
+
+* Fri May 24 2002 Michael Brown <mbrown@fensystems.co.uk> 1.6-1fs
+- Bugfixes, migrated /tftpboot to /var/lib/tftpboot
+
+* Thu May 23 2002 Michael Brown <mbrown@fensystems.co.uk> 1.5-1fs
+- Now includes dhcpd.conf.etherboot.include
+- Automatically scans for all network modules in the pcimap file
+
+* Wed May 08 2002 Michael Brown <mbrown@fensystems.co.uk> 1.4-1fs
+- Bugfixes: tmpdir selection, linuxrc typos, ifconfig peculiarities
+
+* Sat May 04 2002 Michael Brown <mbrown@fensystems.co.uk> 1.3-1fs
+- During %make, LIBDIR must be set for mknbi
+- Added %post scriptlet since %trigger seems not to be being triggered...
+
+* Sat May 04 2002 Michael Brown <mbrown@fensystems.co.uk> 1.2-1fs
+- Added extra sources instead of requiring "make" to download them
+
+* Sat May 04 2002 Michael Brown <mbrown@fensystems.co.uk> 1.1-1fs
+- First attempt at an RPM package
+
diff --git a/gpxe/contrib/initrd/mknbi-set b/gpxe/contrib/initrd/mknbi-set
new file mode 100755
index 00000000..e61acac9
--- /dev/null
+++ b/gpxe/contrib/initrd/mknbi-set
@@ -0,0 +1,200 @@
+#!/usr/bin/perl -w
+#
+# $Id$
+# Maintains set of NBIs based on currently-installed kernels
+# Network card module sets are taken from /etc/mknbi-set.conf
+
+use strict;
+use vars qw($verbosity);
+
+use constant EB_PCI_DEVICE => 1;
+
+# Utility function: calculate output id given a kernel file name and
+# space-separated list of modules
+sub calc_output_id ($$) {
+ my $kernel = shift;
+ my $moduleset = shift;
+ my $kernel_ver = "";
+ ( $kernel_ver ) = ( $kernel =~ /vmlinuz-(.*)$/ );
+ ( my $output_id = "$moduleset".( $kernel_ver ? ".$kernel_ver" : "" ) ) =~ tr/,/./;
+ return ( $kernel_ver, $output_id );
+}
+
+# Utility function: read modules.pcimap-style file
+# Add modules to modulesets hash, write out dhcpd.conf fragment
+sub read_config_file ($$$$) {
+ my $configfile = shift;
+ my $modulesets = shift;
+ my $dhcpfh = shift;
+ my $alwaysuse = shift;
+
+ print "Scanning through $configfile for network modules...\n" if $verbosity >= 1;
+ open CF, $configfile or die "Could not open $configfile: $!\n";
+ chomp ( my $tempmodule = `mktemp /tmp/mknbi-set.XXXXXX` );
+ chomp ( my $cwd = `pwd` ); chdir '/'; # Modprobe searches the current directory...
+ print $dhcpfh " \# Generated from $configfile\n";
+ while (<CF>) {
+ chomp;
+ next if /^[\#;]/ or /^\s*$/;
+ ( my $module, undef, my $vendor, my $device ) = /^(\S+)(\s+(\S+)\s+(\S+))?/ ;
+ $modulesets->{$module} = 1 if $alwaysuse;
+ if ( ! exists $modulesets->{$module} ) {
+ # Check to see if module is a network module
+ # Only do this the first time we encounter a module
+ my @modulepaths = `/sbin/modprobe -l $module.o*` ;
+ chomp ( my $modulepath = $modulepaths[0] );
+ if ( $modulepath ) {
+ if ( $modulepath =~ /.o.gz$/ ) {
+ system ( "zcat $modulepath > $tempmodule" );
+ } else {
+ system ( "cp $modulepath $tempmodule" );
+ }
+ $modulesets->{$module} = 0;
+ foreach ( `nm $tempmodule` ) {
+ chomp;
+ $modulesets->{$module} = 1 if /(ether|wlan)/ ;
+ }
+ unlink $tempmodule;
+ } else {
+ print STDERR "Cannot locate module $module specified in $configfile\n";
+ }
+ }
+ if ( $modulesets->{$module} ) {
+ if ( $vendor ) {
+ print "$module ($vendor,$device) listed in $configfile\n" if $verbosity >= 2;
+ printf $dhcpfh ( " if option etherboot.nic-dev-id = %02x:%02x:%02x:%02x:%02x { option etherboot.kmod \"%s\"; }\n",
+ EB_PCI_DEVICE,
+ ( hex($vendor) >> 8 ) & 0xff, hex($vendor) & 0xff,
+ ( hex($device) >> 8 ) & 0xff, hex($device) & 0xff,
+ $module );
+ } else {
+ print "$module (without PCI IDs) listed in $configfile\n" if $verbosity >= 2;
+ }
+ }
+ }
+ close CF;
+ print $dhcpfh "\n";
+ chdir $cwd;
+}
+
+my $conffile = '/etc/mknbi-set.conf';
+my $mkinitrd_net = 'mkinitrd-net';
+my $mknbi = 'mknbi-linux';
+my $output_dir = '/var/lib/tftpboot';
+my $dhcpfile = '/etc/dhcpd.conf.etherboot-pcimap.include';
+my $use_local;
+our $verbosity = 1;
+my $modulesets = {};
+my $kernel = '';
+my @kernels = ();
+
+my $usage="Usage: $0 [-l|--local] [-q] [-v] [-r|--refresh module[,module...]] [--help]";
+
+# Parse command-line options
+while ( $_ = shift ) {
+ if ( /-l|--local/ ) {
+ $conffile = 'mknbi-set.conf';
+ $mkinitrd_net = './mkinitrd-net';
+ $mknbi = './mknbi-linux --format=nbi --target=linux';
+ $output_dir = 'tftpboot';
+ $dhcpfile = 'tftpboot/dhcpd.conf.etherboot-pcimap.include';
+ $use_local = 1;
+ } elsif ( /-r|--refresh/ ) {
+ my $moduleset = shift;
+ $modulesets->{$moduleset} = 1;
+ } elsif ( /-k|--kernel/ ) {
+ $kernel = shift;
+ } elsif ( /-v|--verbose/ ) {
+ $verbosity++;
+ } elsif ( /-q|--quiet/ ) {
+ $verbosity--;
+ } elsif ( /--help/ ) {
+ die "$usage\n".
+ " -k, --kernel Build NBIs for a particular kernel\n".
+ " -l, --local Run locally from CVS (for developers only)\n".
+ " -r, --refresh Refresh NBI for a particular module\n".
+ " -v, --verbose Be more verbose\n".
+ " -q, --quiet Be less verbose\n";
+ } else {
+ die "$usage\n";
+ }
+}
+
+# Get set of current kernels
+if ($kernel) {
+ @kernels = ( $kernel );
+} else {
+ @kernels = glob('/boot/vmlinuz*');
+}
+die "Could not find any kernels in /boot\n" unless @kernels;
+
+# If modules have been specified via --refresh, do not scan for modules or rewrite the
+# dhcpd.conf fragment file
+unless ( %$modulesets ) {
+ # Write dhcpd.conf fragment file
+ open my $dhcpfh, ">$dhcpfile" or die "Could not open $dhcpfile for writing: $!\n";
+ print $dhcpfh "# Etherboot PCI ID -> Linux kernel module mapping file\n";
+ print $dhcpfh "# Generated by mknbi-set on ".(scalar localtime)."\n";
+ print $dhcpfh "#\n";
+ print $dhcpfh "if substring ( option vendor-class-identifier, 0, 9 ) = \"Etherboot\" {\n";
+ print $dhcpfh " if exists etherboot.nic-dev-id {\n";
+ print $dhcpfh " \# Legacy nic-dev-id mechanism: there are some DLink DFE538 cards in circulation that\n";
+ print $dhcpfh " \# predated the change to the new nic-dev-id binary structure\n";
+ print $dhcpfh " if option etherboot.nic-dev-id = \"PCI:1186:1300\" { option etherboot.kmod \"8139too\"; }\n";
+ print $dhcpfh "\n";
+
+ # Get set of network modules to build NBIs for
+ # Read explicitly-specified module sets from $conffile
+ read_config_file($conffile, $modulesets, $dhcpfh, 1);
+ # Obtain list of all network modules from pcimap file
+ my $pcimap;
+ foreach ( `/sbin/modprobe -c` ) {
+ $pcimap = $1 if /^pcimap.*?=(.*)$/;
+ }
+ if ( $pcimap ) {
+ read_config_file($pcimap, $modulesets, $dhcpfh, 0);
+ } else {
+ print STDERR "Could not identify pcimap file\n";
+ }
+ # Finish off dhcpd.conf fragment file
+ print $dhcpfh " }\n}\n";
+ close $dhcpfh;
+}
+
+# Build initrd and nbi for each kernel-moduleset combination
+foreach my $moduleset ( sort keys %$modulesets ) {
+ next unless $modulesets->{$moduleset}; # Ignore if value is 0
+ print "Building NBIs for module set $moduleset\n" if $verbosity >= 1;
+ foreach my $kernel ( @kernels ) {
+ ( my $kernel_ver, my $output_id ) = calc_output_id ( $kernel, $moduleset );
+ if ( -l $kernel ) {
+ # Symbolic link; create matching symlink
+ my $real_kernel = readlink ( $kernel );
+ ( my $real_kernel_ver, my $real_output_id ) = calc_output_id ( $real_kernel, $moduleset );
+ print "Symlinking $output_id to $real_output_id\n" if $verbosity >= 2;
+ my $initrd_file = "$output_dir/initrd-$output_id.img";
+ unlink ( $initrd_file ) if -l $initrd_file;
+ system ( "ln -s initrd-$real_output_id.img $initrd_file" ) == 0 or print STDERR "Could not symlink $initrd_file to initrd-$real_output_id.img: $!\n";
+ my $nbi_file = "$output_dir/boot-$output_id.nbi";
+ unlink ( $nbi_file ) if -l $nbi_file;
+ system ( "ln -s boot-$real_output_id.nbi $nbi_file" ) == 0 or print STDERR "Could not symlink $nbi_file to boot-$real_output_id.nbi: $!\n";
+ } else {
+ # Real file: create initrd and nbi
+ print "Creating initrd and nbi for $output_id\n" if $verbosity >= 2;
+ ( my $moduleset_spaces = $moduleset ) =~ tr/,/ /;
+ my $initrd_cmd = "$mkinitrd_net --nolink ".
+ ( $use_local ? "--local " : "" ).
+ ( $kernel_ver ? "--kernel $kernel_ver " : "" ).
+ ( $verbosity >= 2 ? "" : "-q " ).
+ $moduleset_spaces;
+ print "$initrd_cmd\n" if $verbosity >= 3;
+ if ( system ( $initrd_cmd ) == 0 ) {
+ my $mknbi_cmd = "$mknbi $kernel $output_dir/initrd-$output_id.img > $output_dir/boot-$output_id.nbi";
+ print "$mknbi_cmd\n" if $verbosity >= 3;
+ system ( $mknbi_cmd ) == 0 or print STDERR "mknbi failed: $!\n";
+ } else {
+ print STDERR "$initrd_cmd failed: $!\n";
+ }
+ }
+ }
+}
diff --git a/gpxe/contrib/initrd/mknbi-set.conf b/gpxe/contrib/initrd/mknbi-set.conf
new file mode 100644
index 00000000..f24846ca
--- /dev/null
+++ b/gpxe/contrib/initrd/mknbi-set.conf
@@ -0,0 +1,27 @@
+# This file specifies the network cards for which NBI images should be built
+# Each line contains a list of kernel modules to be used, separated by commas
+# You can optionally specify PCI vendor and device IDs that should be automatically
+# mapped to this module.
+#
+# The format is similar to modutils' modules.pcimap file.
+#
+# Examples:
+# RTL8139-based ethernet card
+; 8139too
+# RTL8139-based ethernet card with PCI IDs vendor=0x1186, device=0x1300
+; 8139too 0x1186 0x1300
+# RTL8139 and Prism2_pci in same image
+; 8139too,prism2_pci
+
+# Some modules do not include the MODULE_DEVICE_TABLE macro, and so end up not
+# being listed in the pcimap file. These modules are included here to force
+# the creation of corresponding initrds.
+prism2_pci 0x1260 0x3873
+prism2_plx 0x1638 0x1100
+prism2_plx 0x16ab 0x1101
+prism2_plx 0x16ab 0x1102
+prism2_plx 0x1385 0x4100
+prism2_plx 0x15e8 0x0130
+prism2_plx 0x16ec 0x3685
+prism2_plx 0x16ab 0x1102
+prism2_plx 0x15e8 0x0131
diff --git a/gpxe/contrib/initrd/script.c.patch b/gpxe/contrib/initrd/script.c.patch
new file mode 100644
index 00000000..127b881c
--- /dev/null
+++ b/gpxe/contrib/initrd/script.c.patch
@@ -0,0 +1,11 @@
+--- script.c.orig Tue Apr 2 23:49:33 2002
++++ script.c Wed Jun 5 14:17:22 2002
+@@ -179,7 +179,7 @@
+ }
+ if (packet->siaddr) {
+ envp[j] = malloc(sizeof("siaddr=255.255.255.255"));
+- sprintip(envp[j++], "siaddr=", (unsigned char *) &packet->yiaddr);
++ sprintip(envp[j++], "siaddr=", (unsigned char *) &packet->siaddr);
+ }
+ if (!(over & FILE_FIELD) && packet->file[0]) {
+ /* watch out for invalid packets */
diff --git a/gpxe/contrib/initrd/udhcpc-post b/gpxe/contrib/initrd/udhcpc-post
new file mode 100644
index 00000000..395d6c59
--- /dev/null
+++ b/gpxe/contrib/initrd/udhcpc-post
@@ -0,0 +1,25 @@
+#!/bin/sh
+# $Id$
+
+if [ "$1" = "deconfig" ]; then
+ ifconfig $interface 0.0.0.0 up
+else if [ "$1" = "bound" ] ; then
+ echo UDHCPC: I am $ip [$hostname], booting from $serverid
+ [ -n "$hostname" ] && echo $hostname > /proc/sys/kernel/hostname
+ [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
+ [ -n "$subnet" ] && NETMASK="netmask $subnet"
+ ifconfig $interface $ip $BROADCAST $NETMASK
+ route add default gw $router dev $interface
+ echo -n > /etc/resolv.conf
+ for i in $dns; do
+ echo nameserver $i >> /etc/resolv.conf
+ done
+ [ -n "$siaddr" ] || siaddr=$serverid
+ [ -n "$rootpath" ] || rootpath=$siaddr:/
+ echo Mounting root filesystem $rootpath at /sysroot
+ echo If this appears to hang, check that the server of $rootpath is able to
+ echo reverse-map my IP address $ip to obtain my hostname $hostname
+ mount -t nfs -o nolock,rsize=8192,wsize=8192 $rootpath /sysroot
+ fi
+fi
+
diff --git a/gpxe/contrib/linux-2.0-transname.lsm b/gpxe/contrib/linux-2.0-transname.lsm
new file mode 100644
index 00000000..4a590968
--- /dev/null
+++ b/gpxe/contrib/linux-2.0-transname.lsm
@@ -0,0 +1,93 @@
+Xref: news.nsw.CSIRO.AU comp.os.linux.announce:2827
+Path: news.nsw.CSIRO.AU!metro!metro!munnari.OZ.AU!news.ecn.uoknor.edu!news.wildstar.net!news.ececs.uc.edu!newsrelay.netins.net!newsfeed.dacom.co.kr!arclight.uoregon.edu!feed1.news.erols.com!howland.erols.net!newsfeed.internetmci.com!in3.uu.net!liw.clinet.fi!usenet
+From: schoebel@informatik.uni-stuttgart.de (Thomas Schoebel-Theuer)
+Newsgroups: comp.os.linux.announce
+Subject: linux-2.0.21-transname - Patch for easier pool administration
+Followup-To: comp.os.linux.misc
+Date: 30 Oct 1996 10:53:38 GMT
+Organization: Informatik, Uni Stuttgart, Germany
+Lines: 76
+Approved: linux-announce@news.ornl.gov (Lars Wirzenius)
+Message-ID: <pgpmoose.199610301253.4416@liw>
+NNTP-Posting-Host: liw
+X-Auth: PGPMoose V1.1 PGP comp.os.linux.announce
+ iQBVAwUBMnczrjiesvPHtqnBAQEO6gH/WRtFpTPyVtwi0cFVPZ1Xhn8cvfb6i3mk
+ LQY2kgpAPquP2TeXYWb5Ta3HxqK15pR1AgaEy5BmPS6+66ixZFvKRQ==
+ =hhea
+
+-----BEGIN PGP SIGNED MESSAGE-----
+
+linux-2.0.21-transname.patch enables diskless clients, X-terminals etc to
+mount the *root filesystem* of the server. This makes administration of
+large pools *a lot* easier.
+
+Currently different diskless clients must have their root "/" on different
+directories on the server, beause each client has _some_ different
+configuration files. However, most administration files (typically about 99%)
+have the same contents on the clients and on the server, but have to be
+(and maintained separately) just because of the 1% differences.
+
+This duplication causes very large efforts in practice, since at least
+the /etc directory has to be duplicated for every client. Even in /etc
+many files are identical, for example sendmail.cf, initrc scripts and
+others. Maintaining a large pool means to ensure coherence amoung the
+duplicates. Classical methods like symlinks are unconvenient
+for this task because they have to be valid in the view of mounted
+filesystems at the client, not at the server.
+
+linux-2.0-transname.patch overcomes this problem by allowing filenames
+to be context-dependend. For example, if you have a file "/etc/config"
+that should differ on the hosts "myserver" and "myclient", you just
+create two different files named "/etc/config#host=myserver#" and
+"/etc/config#host=myclient#". On host "myserver", the file
+"/etc/config#host=myserver#" will appear as if it were hardlinked to
+file "/etc/config" (without the #...=...# suffix). On host "myclient",
+the corresponding other file will appear as "/etc/config". So you
+can access the right file contents under the same name, depending
+on which host you are.
+
+As a result, you may use different contexts for e.g. /etc/fstab, but
+have one shared /etc/passwd for all pool machines. So you don't need
+yp or NYS any more.
+
+The kernel patch was developped for and is used at our Linux pool at the
+University of Stuttgart with good results. Maintainance of the pool is
+at a minimum, and adding new clients is a child's play. No worry with
+keeping up mail configurations, newly installed tools, changed /etc/services,
+/etc/shells, /etc/resolv.conf and many, many others. In contrast to a
+sophisticated symlink solution, adding a new file to the /etc directory
+is seen immediately by all clients. I never had less trouble with
+administration before.
+
+I just uploaded the patch to
+ ftp://ftp.lmh.ox.ac.uk
+ where it should appear in /pub/linux-kernel-patch-archive/
+and also to
+ ftp://sunsite.unc.edu/pub/Linux/Incoming/
+ where it should be moved to /pub/Linux/kernel/patches/misc/ soon.
+
+More details can be found in the README there, and also in the
+configure-help.
+
+Enjoy,
+
+- -- Thomas
+
+
+-----BEGIN PGP SIGNATURE-----
+Version: 2.6.3i
+Charset: noconv
+
+iQCVAwUBMnczhYQRll5MupLRAQHzuwP9HGYa4I3bZpt22Y3oQIwEKZGfvnaS5AaD
+fVG8wOQ/T7Nrant9JtTktsTVlxGVlYVnziRY4c0ew2qExapK9FqY/ItN0NJXy5aT
+a4eSkn86rp6Un7m90RK1xVY5AyVAq49Rdw2StGxr7uj+davnmg3Np+U0MiAILq91
+52jKGaR3fvc=
+=LSD6
+-----END PGP SIGNATURE-----
+
+--
+This article has been digitally signed by the moderator, using PGP.
+http://www.iki.fi/liw/lars-public-key.asc has PGP key for validating signature.
+Send submissions for comp.os.linux.announce to: linux-announce@news.ornl.gov
+PLEASE remember a short description of the software and the LOCATION.
+This group is archived at http://www.iki.fi/liw/linux/cola.html
diff --git a/gpxe/contrib/linux-3c503-patch/3c503.patch b/gpxe/contrib/linux-3c503-patch/3c503.patch
new file mode 100644
index 00000000..b27ff1fc
--- /dev/null
+++ b/gpxe/contrib/linux-3c503-patch/3c503.patch
@@ -0,0 +1,24 @@
+diff -Naur linux.orig/drivers/net/3c503.c linux/drivers/net/3c503.c
+--- linux.orig/drivers/net/3c503.c Thu Feb 19 23:14:04 1998
++++ linux/drivers/net/3c503.c Thu Feb 19 23:16:24 1998
+@@ -179,7 +179,8 @@
+ for both the old and new 3Com prefix */
+ outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
+ vendor_id = inb(ioaddr)*0x10000 + inb(ioaddr + 1)*0x100 + inb(ioaddr + 2);
+- if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID)) {
++ if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID) &&
++ (vendor_id != BULL_3COM_ID)) {
+ /* Restore the register we frobbed. */
+ outb(saved_406, ioaddr + 0x406);
+ return ENODEV;
+diff -Naur linux.orig/drivers/net/3c503.h linux/drivers/net/3c503.h
+--- linux.orig/drivers/net/3c503.h Thu Feb 19 23:14:05 1998
++++ linux/drivers/net/3c503.h Mon Feb 16 11:41:56 1998
+@@ -11,6 +11,7 @@
+
+ #define OLD_3COM_ID 0x02608c
+ #define NEW_3COM_ID 0x0020af
++#define BULL_3COM_ID 0x000062
+
+ /* Shared memory management parameters. NB: The 8 bit cards have only
+ one bank (MB1) which serves both Tx and Rx packet space. The 16bit
diff --git a/gpxe/contrib/linux-3c503-patch/README b/gpxe/contrib/linux-3c503-patch/README
new file mode 100644
index 00000000..109094ff
--- /dev/null
+++ b/gpxe/contrib/linux-3c503-patch/README
@@ -0,0 +1,7 @@
+As part of determining whether a 3c503 is present, the Linux driver
+examines the first 3 bytes of the ethernet address (the vendor ID)
+to see if it corresponds to a known 3Com vendor ID.
+
+The Bull discless 386 workstation I have (don't laugh) has an
+unknown vendor ID 0x000062. This trivial patch adds it to those
+known to the driver.
diff --git a/gpxe/contrib/mini-slamd/COPYING b/gpxe/contrib/mini-slamd/COPYING
new file mode 100644
index 00000000..a43ea212
--- /dev/null
+++ b/gpxe/contrib/mini-slamd/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gpxe/contrib/mini-slamd/Makefile b/gpxe/contrib/mini-slamd/Makefile
new file mode 100644
index 00000000..05f1a366
--- /dev/null
+++ b/gpxe/contrib/mini-slamd/Makefile
@@ -0,0 +1,8 @@
+CC=gcc
+CFLAGS=-Wall -O2
+
+mini-slamd: mini-slamd.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+clean:
+ rm -f mini-slamd \ No newline at end of file
diff --git a/gpxe/contrib/mini-slamd/mini-slamd.c b/gpxe/contrib/mini-slamd/mini-slamd.c
new file mode 100644
index 00000000..7c33e22b
--- /dev/null
+++ b/gpxe/contrib/mini-slamd/mini-slamd.c
@@ -0,0 +1,521 @@
+/*
+ * mini-slamd
+ * (c) 2002 Eric Biederman
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/ip.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+/*
+ * To specify the default interface for multicast packets use:
+ * route add -net 224.0.0.0 netmask 240.0.0.0 dev eth1
+ * This server is stupid and does not override the default.
+ */
+
+/* Sever states.
+ *
+ * Waiting for clients.
+ * Sending data to clients.
+ * Pinging clients for data.
+ *
+ */
+#define SLAM_PORT 10000
+#define SLAM_MULTICAST_IP ((239<<24)|(255<<16)|(1<<8)|(1<<0))
+#define SLAM_MULTICAST_PORT 10000
+#define SLAM_MULTICAST_TTL 1
+#define SLAM_MULTICAST_LOOPBACK 1
+#define SLAM_MAX_CLIENTS 10
+
+#define SLAM_PING_TIMEOUT 100 /* ms */
+
+/*** Packets Formats ***
+ * Data Packet:
+ * transaction
+ * total bytes
+ * block size
+ * packet #
+ * data
+ *
+ * Status Request Packet
+ * transaction
+ * total bytes
+ * block packets
+ *
+ * Status Packet
+ * received packets
+ * requested packets
+ * received packets
+ * requested packets
+ * ...
+ * received packets
+ * requested packtes
+ * 0
+ */
+
+#define MAX_HDR (7 + 7 + 7) /* transaction, total size, block size */
+#define MIN_HDR (1 + 1 + 1) /* transaction, total size, block size */
+
+#define MAX_DATA_HDR (MAX_HDR + 7) /* header, packet # */
+#define MIN_DATA_HDR (MAX_HDR + 1) /* header, packet # */
+
+/* ETH_MAX_MTU 1500 - sizeof(iphdr) 20 - sizeof(udphdr) 8 = 1472 */
+#define SLAM_MAX_NACK (1500 - (20 + 8))
+/* ETH_MAX_MTU 1500 - sizeof(iphdr) 20 - sizeof(udphdr) 8 - MAX_HDR = 1451 */
+#define SLAM_BLOCK_SIZE (1500 - (20 + 8 + MAX_HDR))
+
+
+/* Define how many debug messages you want
+ * 1 - sparse but useful
+ * 2 - everything
+ */
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
+static int slam_encode(
+ unsigned char **ptr, unsigned char *end, unsigned long value)
+{
+ unsigned char *data = *ptr;
+ int bytes;
+ bytes = sizeof(value);
+ while ((bytes > 0) && ((0xff & (value >> ((bytes -1)<<3))) == 0)) {
+ bytes--;
+ }
+ if (bytes <= 0) {
+ bytes = 1;
+ }
+ if (data + bytes >= end) {
+ return -1;
+ }
+ if ((0xe0 & (value >> ((bytes -1)<<3))) == 0) {
+ /* packed together */
+ *data = (bytes << 5) | (value >> ((bytes -1)<<3));
+ } else {
+ bytes++;
+ *data = (bytes << 5);
+ }
+ bytes--;
+ data++;
+ while(bytes) {
+ *(data++) = 0xff & (value >> ((bytes -1)<<3));
+ bytes--;
+ }
+ *ptr = data;
+ return 0;
+}
+
+static unsigned long slam_decode(unsigned char **ptr, unsigned char *end, int *err)
+{
+ unsigned long value;
+ unsigned bytes;
+ if (*ptr >= end) {
+ *err = -1;
+ }
+ bytes = ((**ptr) >> 5) & 7;
+ if ((bytes == 0) || (bytes > sizeof(unsigned long))) {
+ *err = -1;
+ return 0;
+ }
+ if ((*ptr) + bytes >= end) {
+ *err = -1;
+ }
+ value = (**ptr) & 0x1f;
+ bytes--;
+ (*ptr)++;
+ while(bytes) {
+ value <<= 8;
+ value |= **ptr;
+ (*ptr)++;
+ bytes--;
+ }
+ return value;
+}
+
+
+static struct sockaddr_in client[SLAM_MAX_CLIENTS];
+static int clients;
+
+
+void del_client(struct sockaddr_in *old)
+{
+ int i;
+ for(i = 0; i < clients; i++) {
+ if ((client[i].sin_family == old->sin_family) &&
+ (client[i].sin_addr.s_addr == old->sin_addr.s_addr) &&
+ (client[i].sin_port == old->sin_port)) {
+ memmove(&client[i], &client[i+1],
+ (clients - (i+1))*sizeof(client[0]));
+ clients--;
+ }
+ }
+}
+
+void add_client(struct sockaddr_in *new)
+{
+ del_client(new);
+ if (clients >= SLAM_MAX_CLIENTS)
+ return;
+ memcpy(&client[clients], new, sizeof(*new));
+ clients++;
+}
+
+void push_client(struct sockaddr_in *new)
+{
+ del_client(new);
+ if (clients >= SLAM_MAX_CLIENTS) {
+ clients--;
+ }
+ memmove(&client[1], &client[0], clients*sizeof(*new));
+ memcpy(&client[0], new, sizeof(*new));
+ clients++;
+}
+
+
+void next_client(struct sockaddr_in *next)
+{
+ /* Find the next client we want to ping next */
+ if (!clients) {
+ next->sin_family = AF_UNSPEC;
+ return;
+ }
+ /* Return the first client */
+ memcpy(next, &client[0], sizeof(*next));
+}
+
+int main(int argc, char **argv)
+{
+ char *filename;
+ uint8_t nack_packet[SLAM_MAX_NACK];
+ int nack_len;
+ uint8_t request_packet[MAX_HDR];
+ int request_len;
+ uint8_t data_packet[MAX_DATA_HDR + SLAM_BLOCK_SIZE];
+ int data_len;
+ uint8_t *ptr, *end;
+ struct sockaddr_in master_client;
+ struct sockaddr_in sa_src;
+ struct sockaddr_in sa_mcast;
+ uint8_t mcast_ttl;
+ uint8_t mcast_loop;
+ int sockfd, filefd;
+ int result;
+ struct pollfd fds[1];
+ int state;
+#define STATE_PINGING 1
+#define STATE_WAITING 2
+#define STATE_RECEIVING 3
+#define STATE_TRANSMITTING 4
+ off_t size;
+ struct stat st;
+ uint64_t transaction;
+ unsigned long packet;
+ unsigned long packet_count;
+ unsigned slam_port, slam_multicast_port;
+ struct in_addr slam_multicast_ip;
+
+ slam_port = SLAM_PORT;
+ slam_multicast_port = SLAM_MULTICAST_PORT;
+ slam_multicast_ip.s_addr = htonl(SLAM_MULTICAST_IP);
+
+ if (argc != 2) {
+ fprintf(stderr, "Bad argument count\n");
+ fprintf(stderr, "Usage: mini-slamd filename\n");
+ exit(EXIT_FAILURE);
+ }
+ filename = argv[1];
+ filefd = -1;
+ size = 0;
+ transaction = 0;
+
+ /* Setup the udp socket */
+ sockfd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ fprintf(stderr, "Cannot create socket\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(&sa_src, 0, sizeof(sa_src));
+ sa_src.sin_family = AF_INET;
+ sa_src.sin_port = htons(slam_port);
+ sa_src.sin_addr.s_addr = INADDR_ANY;
+
+ result = bind(sockfd, &sa_src, sizeof(sa_src));
+ if (result < 0) {
+ fprintf(stderr, "Cannot bind socket to port %d\n",
+ ntohs(sa_src.sin_port));
+ exit(EXIT_FAILURE);
+ }
+
+ /* Setup the multicast transmission address */
+ memset(&sa_mcast, 0, sizeof(sa_mcast));
+ sa_mcast.sin_family = AF_INET;
+ sa_mcast.sin_port = htons(slam_multicast_port);
+ sa_mcast.sin_addr.s_addr = slam_multicast_ip.s_addr;
+ if (!IN_MULTICAST(ntohl(sa_mcast.sin_addr.s_addr))) {
+ fprintf(stderr, "Not a multicast ip\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Set the multicast ttl */
+ mcast_ttl = SLAM_MULTICAST_TTL;
+ setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
+ &mcast_ttl, sizeof(mcast_ttl));
+
+ /* Set the multicast loopback status */
+ mcast_loop = SLAM_MULTICAST_LOOPBACK;
+ setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &mcast_loop, sizeof(mcast_loop));
+
+
+ state = STATE_WAITING;
+ packet = 0;
+ packet_count = 0;
+ fds[0].fd = sockfd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ for(;;) {
+ switch(state) {
+ case STATE_PINGING:
+ state = STATE_WAITING;
+ next_client(&master_client);
+ if (master_client.sin_family == AF_UNSPEC) {
+ break;
+ }
+#if DEBUG
+ printf("Pinging %s:%d\n",
+ inet_ntoa(master_client.sin_addr),
+ ntohs(master_client.sin_port));
+ fflush(stdout);
+#endif
+
+ /* Prepare the request packet, it is all header */
+ ptr = request_packet;
+ end = &request_packet[sizeof(request_packet) -1];
+ slam_encode(&ptr, end, transaction);
+ slam_encode(&ptr, end, size);
+ slam_encode(&ptr, end, SLAM_BLOCK_SIZE);
+ request_len = ptr - request_packet;
+
+ result = sendto(sockfd, request_packet, request_len, 0,
+ &master_client, sizeof(master_client));
+ /* Forget the client I just asked, when the reply
+ * comes in we will remember it again.
+ */
+ del_client(&master_client);
+ break;
+ case STATE_WAITING:
+ {
+ int timeout;
+ int from_len;
+ timeout = -1;
+ if (master_client.sin_family != AF_UNSPEC) {
+ timeout = SLAM_PING_TIMEOUT;
+ }
+ result = poll(fds, sizeof(fds)/sizeof(fds[0]), timeout);
+ if (result == 0) {
+ /* On a timeout try the next client */
+ state = STATE_PINGING;
+ break;
+ }
+ if (result > 0) {
+ from_len = sizeof(master_client);
+ result = recvfrom(sockfd,
+ nack_packet, sizeof(nack_packet), 0,
+ &master_client, &from_len);
+ if (result < 0)
+ break;
+ nack_len = result;
+#if DEBUG
+ printf("Received Nack from %s:%d\n",
+ inet_ntoa(master_client.sin_addr),
+ ntohs(master_client.sin_port));
+ fflush(stdout);
+#endif
+#if DEBUG
+ {
+ ptr = nack_packet;
+ end = ptr + result;
+ packet = 0;
+ result = 0;
+ while(ptr < end) {
+ packet += slam_decode(&ptr, end, &result);
+ if (result < 0) break;
+ packet_count = slam_decode(&ptr, end, &result);
+ if (result < 0) break;
+ printf("%d-%d ",
+ packet, packet + packet_count -1);
+ }
+ printf("\n");
+ fflush(stdout);
+ }
+#endif
+ /* Forget this client temporarily.
+ * If the packet appears good they will be
+ * readded.
+ */
+ del_client(&master_client);
+ ptr = nack_packet;
+ end = ptr + nack_len;
+ result = 0;
+ packet = slam_decode(&ptr, end, &result);
+ if (result < 0)
+ break;
+ packet_count = slam_decode(&ptr, end, &result);
+ if (result < 0)
+ break;
+ /* We appear to have a good packet, keep
+ * this client.
+ */
+ push_client(&master_client);
+
+ /* Reopen the file to transmit */
+ if (filefd != -1) {
+ close(filefd);
+ }
+ filefd = open(filename, O_RDONLY);
+ if (filefd < 0) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ filename, strerror(errno));
+ break;
+ }
+ size = lseek(filefd, 0, SEEK_END);
+ if (size < 0) {
+ fprintf(stderr, "Seek failed on %s: %s\n",
+ filename, strerror(errno));
+ break;
+ }
+ result = fstat(filefd, &st);
+ if (result < 0) {
+ fprintf(stderr, "Stat failed on %s: %s\n",
+ filename, strerror(errno));
+ break;
+ }
+ transaction = st.st_mtime;
+
+ state = STATE_TRANSMITTING;
+ break;
+ }
+ break;
+ }
+ case STATE_RECEIVING:
+ /* Now clear the queue of received packets */
+ {
+ struct sockaddr_in from;
+ int from_len;
+ uint8_t dummy_packet[SLAM_MAX_NACK];
+ state = STATE_TRANSMITTING;
+ result = poll(fds, sizeof(fds)/sizeof(fds[0]), 0);
+ if (result < 1)
+ break;
+ from_len = sizeof(from);
+ result = recvfrom(sockfd,
+ dummy_packet, sizeof(dummy_packet), 0,
+ &from, &from_len);
+ if (result <= 0)
+ break;
+#if DEBUG
+ printf("Received Nack from %s:%d\n",
+ inet_ntoa(from.sin_addr),
+ ntohs(from.sin_port));
+ fflush(stdout);
+#endif
+ /* Receive packets until I don't get any more */
+ state = STATE_RECEIVING;
+ /* Process a packet */
+ if (dummy_packet[0] == '\0') {
+ /* If the first byte is null it is a disconnect
+ * packet.
+ */
+ del_client(&from);
+ }
+ else {
+ /* Otherwise attempt to add the client. */
+ add_client(&from);
+ }
+ break;
+ }
+ case STATE_TRANSMITTING:
+ {
+ off_t off;
+ off_t offset;
+ ssize_t bytes;
+ uint8_t *ptr2, *end2;
+
+ /* After I transmit a packet check for packets to receive. */
+ state = STATE_RECEIVING;
+
+ /* Find the packet to transmit */
+ offset = packet * SLAM_BLOCK_SIZE;
+
+ /* Seek to the desired packet */
+ off = lseek(filefd, offset, SEEK_SET);
+ if ((off < 0) || (off != offset)) {
+ fprintf(stderr, "Seek failed on %s:%s\n",
+ filename, strerror(errno));
+ break;
+ }
+ /* Encode the packet header */
+ ptr2 = data_packet;
+ end2 = data_packet + sizeof(data_packet);
+ slam_encode(&ptr2, end2, transaction);
+ slam_encode(&ptr2, end2, size);
+ slam_encode(&ptr2, end2, SLAM_BLOCK_SIZE);
+ slam_encode(&ptr2, end2, packet);
+ data_len = ptr2 - data_packet;
+
+ /* Read in the data */
+ bytes = read(filefd, &data_packet[data_len],
+ SLAM_BLOCK_SIZE);
+ if (bytes <= 0) {
+ fprintf(stderr, "Read failed on %s:%s\n",
+ filename, strerror(errno));
+ break;
+ }
+ data_len += bytes;
+ /* Write out the data */
+ result = sendto(sockfd, data_packet, data_len, 0,
+ &sa_mcast, sizeof(sa_mcast));
+ if (result != data_len) {
+ fprintf(stderr, "Send failed %s\n",
+ strerror(errno));
+ break;
+ }
+#if DEBUG > 1
+ printf("Transmitted: %d\n", packet);
+ fflush(stdout);
+#endif
+ /* Compute the next packet */
+ packet++;
+ packet_count--;
+ if (packet_count == 0) {
+ packet += slam_decode(&ptr, end, &result);
+ if (result >= 0)
+ packet_count = slam_decode(&ptr, end, &result);
+ if (result < 0) {
+ /* When a transmission is done close the file,
+ * so it may be updated. And then ping then start
+ * pinging clients to get the transmission started
+ * again.
+ */
+ state = STATE_PINGING;
+ close(filefd);
+ filefd = -1;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/gpxe/contrib/mkQNXnbi/Makefile b/gpxe/contrib/mkQNXnbi/Makefile
new file mode 100644
index 00000000..4f6c0f2a
--- /dev/null
+++ b/gpxe/contrib/mkQNXnbi/Makefile
@@ -0,0 +1,10 @@
+# Makefile for the mkQNXnbi filter
+
+all: mkQNXnbi
+
+mkQNXnbi: mkQNXnbi.o
+
+mkQNXnbi.o: mkQNXnbi.c
+
+clean:
+ rm -f mkQNXnbi *.o
diff --git a/gpxe/contrib/mkQNXnbi/README b/gpxe/contrib/mkQNXnbi/README
new file mode 100644
index 00000000..1522df31
--- /dev/null
+++ b/gpxe/contrib/mkQNXnbi/README
@@ -0,0 +1,36 @@
+mkQNXnbi is a quick hack to generate tagged images from QNX boot images.
+
+To boot a QNX client with Etherboot you have to consider the following:
+1. You MUST have another QNX box running in the network to provide the
+ root filesystem and the license info to the client. QNX cannot use
+ e.g. NFS for its root filesystem, as it needs to load a valid license
+ from a file on the root fs before being able to start TCP/IP. This
+ would lead to a chicken-and-egg problem.
+2. The Net task normally determines the size of its internal tables from
+ the actual number of licensed nodes. Since this information is not
+ available at boot time when booting from the network, you will have
+ to set the max. number of nodes as well as a valid netmap entry for
+ the node providing the root filesystem as an option to Net in the
+ build file.
+ See examples/ws.etherboot and fill in the <blanks>.
+3. The client does not need a TCP/IP license in order to boot.
+4. You can use the boot-server OS of your choice. If you choose to use
+ QNX as boot server, the server of course needs a TCP/IP run-time
+ license. In this case you have the option of creating the boot image
+ on-the-fly and use the macro $(netmap) instead of a hard-coded MAC
+ address.
+ See examples/ws.etherboot.on-the-fly and fill in the <blanks>.
+ A template bootptab for the QNX bootp server is placed in the
+ examples directory.
+5. mkQNXnbi expects the QNX image to be supplied on stdin and generates
+ the tagged image to stdout. This can be overridden on the command line
+ using the options -i <input-file> and -o <output-file>.
+
+mkQNXnbi can be compiled using e.g. Linux/gcc or on QNX using Watcom C
+(or gcc, if you have it - see http://w3c.teaser.fr/~jcmichot/)
+
+Bug-reports to <al@alarsen.net>
+
+2002-01-25
+Anders Larsen
+<al@alarsen.net>
diff --git a/gpxe/contrib/mkQNXnbi/examples/bootptab b/gpxe/contrib/mkQNXnbi/examples/bootptab
new file mode 100644
index 00000000..2077fa94
--- /dev/null
+++ b/gpxe/contrib/mkQNXnbi/examples/bootptab
@@ -0,0 +1,29 @@
+# /etc/bootptab: database for QNX bootp server (/etc/bootpd)
+
+# First, we define a global entry which specifies the stuff every host uses.
+global:\
+ :hd=/boot:\
+ :ht=ethernet:\
+ :sm=<your netmask here>:\
+ :bf=|cd /boot; buildqnx -b 0x10000 build/<your build-file here> | mkQNXnbi:\
+ :hn:
+
+# node 2 uses the default build-file
+node2:\
+ :tc=global:\
+ :ha=<your MAC here>:\
+ :ip=<your IP address here>:
+
+# node 3 uses its own build-file
+node3:\
+ :tc=global:\
+ :ha=<your MAC here>:\
+ :ip=<your IP address here>:\
+ :bf=|cd /boot; buildqnx -b 0x10000 build/<your build-file here> | mkQNXnbi:
+
+# node 4 uses a pre-built boot image
+node3:\
+ :tc=global:\
+ :ha=<your MAC here>:\
+ :ip=<your IP address here>:\
+ :bf=images/<your image-file here>:
diff --git a/gpxe/contrib/mkQNXnbi/examples/ws.etherboot b/gpxe/contrib/mkQNXnbi/examples/ws.etherboot
new file mode 100644
index 00000000..d8120bc8
--- /dev/null
+++ b/gpxe/contrib/mkQNXnbi/examples/ws.etherboot
@@ -0,0 +1,22 @@
+# /boot/build/ws.etherboot
+
+sys/boot
+$ boot -v
+
+sys/Proc32
+$ Proc32 -l <target node number>
+
+sys/Slib32
+$ Slib32
+
+sys/Slib16
+$ Slib16
+
+/bin/Net
+$ Net -n <highest QNX node number in network> -m "<node number of boot server> 1 <MAC of boot server node here>"
+
+/bin/Net.<network driver>
+$ Net.<network driver>
+
+/bin/sinit
+$ sinit -r //<node number of boot server>/ TERM=<your terminal emulation {QNX|qansi}>
diff --git a/gpxe/contrib/mkQNXnbi/examples/ws.etherboot.on-the-fly b/gpxe/contrib/mkQNXnbi/examples/ws.etherboot.on-the-fly
new file mode 100644
index 00000000..3058c450
--- /dev/null
+++ b/gpxe/contrib/mkQNXnbi/examples/ws.etherboot.on-the-fly
@@ -0,0 +1,22 @@
+# /boot/build/ws.etherboot.on-the-fly
+
+sys/boot
+$ boot -v
+
+sys/Proc32
+$ Proc32 -l $(lnode)
+
+sys/Slib32
+$ Slib32
+
+sys/Slib16
+$ Slib16
+
+/bin/Net
+$ Net -n <highest QNX node number in network> -m $(netmap)
+
+/bin/Net.<network driver>
+$ Net.<network driver>
+
+/bin/sinit
+$ sinit -r //$(bnode)/ TERM=<your terminal emulation {QNX|qansi}>
diff --git a/gpxe/contrib/mkQNXnbi/mkQNXnbi.c b/gpxe/contrib/mkQNXnbi/mkQNXnbi.c
new file mode 100644
index 00000000..2ec2dc4b
--- /dev/null
+++ b/gpxe/contrib/mkQNXnbi/mkQNXnbi.c
@@ -0,0 +1,196 @@
+//*****************************************************************************
+//
+// Purpose: Make a boot-image for EtherBoot
+//
+//
+// Compiler: This source can be compiled with gcc and Watcom C
+//
+//
+// Note: The QNX boot image can be build with any reasonable
+// start address, e.g. 0x1000 (default) or 0x10000
+// (widespread Boot-Rom address)
+//
+//
+// Author: Anders Larsen
+//
+//
+// Copyright: (C) 1999 by
+//
+// Anders Larsen
+// systems engineer
+// Gutleuthausstr. 3
+// D-69469 Weinheim
+// Germany
+// phone: +49-6201-961717
+// fax: +49-6201-961718
+// e-mail: al@alarsen.net
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Change Log:
+// V0.2: Sun 1999-12-13 Anders Larsen <al@alarsen.net>
+//*****************************************************************************
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+
+// max. size of QNX OS boot image is 512K
+#define MAXSIZE (512*1024)
+
+typedef unsigned short ushort_t;
+typedef unsigned long ulong_t;
+
+
+// global header of tagged image:
+struct initial_t
+{
+ ulong_t magic;
+ ulong_t length;
+ ulong_t location;
+ ulong_t start;
+};
+
+
+// header of each image:
+struct header_t
+{
+ ulong_t flags;
+ ulong_t loadaddr;
+ ulong_t imgsize;
+ ulong_t memsize;
+};
+
+
+// global header of the QNX EtherBoot image:
+struct qnx_loader_t
+{
+ struct initial_t setup;
+ struct header_t qnx;
+};
+
+
+// global header:
+union
+{
+ struct qnx_loader_t h;
+ char filler[512];
+} header;
+
+
+char buffer[MAXSIZE];
+
+
+int usage( char* const* argv )
+{
+ fprintf( stderr, "%s - make a tagged boot image for EtherBoot\n", *argv );
+ fprintf( stderr, "\nuse:\n" );
+ fprintf( stderr, "%s [ -<option> ]*\n", *argv );
+ fprintf( stderr, "\noptions:\n" );
+ fprintf( stderr, " i <input file> : QNX boot file (default: stdin)\n" );
+ fprintf( stderr, " o <output file> : tagged image file (default: stdout)\n" );
+ fprintf( stderr, " v : be verbose\n" );
+ return EXIT_FAILURE;
+}
+
+#ifdef __USAGE
+%C - make a tagged boot image for EtherBoot
+
+use:
+%C [ -<option> ]*
+
+options:
+ i <input file> : QNX boot file (default: stdin)
+ o <output file> : tagged image file (default: stdout)
+ v : be verbose
+#endif
+
+
+int main( int argc, char* const* argv )
+{
+ int ch, l;
+ int verbose = 0;
+
+ while ( ( ch = getopt( argc, argv, "hi:o:v" ) ) != EOF )
+ switch ( ch )
+ {
+ case 'i':
+ if ( !freopen( optarg, "r", stdin ) )
+ {
+ perror( "can't open input file" );
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 'o':
+ if ( !freopen( optarg, "w", stdout ) )
+ {
+ perror( "can't create output file" );
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ case 'h':
+ default:
+ return usage( argv );
+ }
+ if ( optind != argc )
+ return usage( argv );
+
+ memset( &header, 0, sizeof header );
+ header.h.setup.magic = 0x1b031336; // magic number
+ header.h.setup.length = 4;
+ header.h.setup.location = 0x93e00000; // just below the EtherBoot rom
+ header.h.setup.start = 0; // filled in dynamically
+ header.h.qnx.flags = 0x04000004; // single image only
+ header.h.qnx.loadaddr = 0; // filled in dynamically
+ header.h.qnx.imgsize = 0; // filled in dynamically
+ header.h.qnx.memsize = 0; // filled in dynamically
+
+ // read the QNX image from stdin:
+ for ( ; ( l = fread( buffer + header.h.qnx.imgsize, 1, 1024, stdin ) ) > 0;
+ header.h.qnx.imgsize += l
+ )
+ ;
+ header.h.qnx.memsize = header.h.qnx.imgsize;
+
+ // fill in the real load-address of the QNX boot image:
+ header.h.setup.start = *(ushort_t*)&buffer[10] << 16;
+ header.h.qnx.loadaddr = *(ushort_t*)&buffer[10] << 4;
+
+ // write the tagged image file to stdout:
+ fwrite( &header, 1, 512, stdout );
+ fwrite( buffer, 1, header.h.qnx.imgsize, stdout );
+
+ if ( verbose )
+ {
+ // print diagnostic information:
+ fprintf( stderr, "QNX image size: %d bytes (%dK), load addr: 0x%05X\n",
+ header.h.qnx.imgsize,
+ header.h.qnx.imgsize / 1024,
+ header.h.qnx.loadaddr
+ );
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/gpxe/contrib/mkffwnb/2.0.10/linuxrc b/gpxe/contrib/mkffwnb/2.0.10/linuxrc
new file mode 100755
index 00000000..351679c7
--- /dev/null
+++ b/gpxe/contrib/mkffwnb/2.0.10/linuxrc
@@ -0,0 +1,76 @@
+#!/bin/sh
+#
+# floppyfw initfile
+#
+# nicked from:
+# hal91's initfile (/linuxrc), the bootup script of the system
+#
+
+VERSION=2.1.6
+
+load_fsmod () {
+ case $1 in
+ /dev/hd*)
+ insmod ide-cd
+ insmod cdrom
+ ;;
+ esac
+ case $2 in
+ vfat)
+ echo vfat support is builtin
+ ;;
+ iso9660)
+ insmod isofs
+ ;;
+ esac
+}
+
+#
+/bin/busybox echo "Booting floppyfw"
+
+PATH="/bin:/sbin:/usr/bin:/usr/sbin"
+#PATH="/bin"
+TERM=linux
+ignoreeof=10
+no_exit_on_failed_exec=yes
+export PATH TERM ignoreeof
+umask 022
+
+/bin/busybox echo "mounting: proc"
+/bin/busybox mount -t proc /proc /proc
+
+/bin/busybox echo "Generating links. (Thanks to busybox.lineo.com)"
+/bin/busybox --install -s
+
+echo "Generated"
+
+# Modified by Gem, based on coyote distro, changes by Ken Yap
+ROOTDEV=`sed -e 's/$/ /' -e 's/.*root=\([^ ]*\) .*/\1/' -e 's/,/ /g' -e 's:/dev/nfs:/dev/fd0:' /proc/cmdline`
+set -- $ROOTDEV
+# Backward compatibility with a single device argument
+if [ $# -eq 1 ]
+then
+ set -- $1 vfat
+fi
+while [ "$1" -a "$2" ]
+do
+ echo "attempting to mount $1 ($2)"
+ load_fsmod $1 $2
+ if mount -t $2 $1 /mnt/tmp
+ then
+ echo "mounted $1 on /mnt/tmp"
+ break
+ fi
+ shift; shift
+done
+
+[ -f /mnt/tmp/floppyfw/floppyfw.ini ] && cat /mnt/tmp/floppyfw/floppyfw.ini \
+| tr -d '\015' >/floppyfw.ini
+
+[ -f /floppyfw.ini ] && chmod 777 /floppyfw.ini
+[ -f /floppyfw.ini ] && exec /floppyfw.ini
+
+echo
+echo "** floppyfw.ini failed.. starting a shell"
+echo
+exec sh
diff --git a/gpxe/contrib/mkffwnb/Extendinitrd.pm b/gpxe/contrib/mkffwnb/Extendinitrd.pm
new file mode 100644
index 00000000..3b919ad1
--- /dev/null
+++ b/gpxe/contrib/mkffwnb/Extendinitrd.pm
@@ -0,0 +1,43 @@
+#!/usr/bin/perl -w
+
+sub status_system ($$) {
+ my ($command, $message) = @_;
+
+ $status = system($command);
+ $status <<= 8;
+ if ($status < 0) {
+ print STDERR "$!\n";
+ }
+ if ($status != 0) {
+ print STDERR "$message\n";
+ }
+}
+
+sub extendinitrd ($$) {
+ my ($initrd, $nblocks) = @_;
+
+ if ($nblocks <= 1440) {
+ print STDERR "nblocks must be >= 1440\n";
+ return (1);
+ }
+ (undef, $type, undef, $fnlen, undef) = split(' ', `file $initrd`, 5);
+ print "$type $fnlen\n";
+ if ($type ne 'Minix' || $fnlen != 30) {
+ die "Can only handle Minix initrds with 30 char filenames\n";
+ return (1);
+ }
+ status_system("dd if=/dev/zero of=newinitrd bs=1k count=$nblocks", "Cannot create new initrd\n");
+ status_system("mkfs.minix -n 30 newinitrd $nblocks", "Cannot mkfs.minix new initrd\n");
+ mkdir("initrd.from") || print STDERR "Cannot make temp mount point initrd.from\n";
+ mkdir("initrd.to") || print STDERR "Cannot make temp mount point initrd.to\n";
+ status_system("mount -o ro,loop $initrd initrd.from", "Cannot mount $initrd on initrd.from");
+ status_system("mount -o loop newinitrd initrd.to", "Cannot mount newinitrd on initrd.to");
+ status_system("cp -a initrd.from/* initrd.to/", "Cannot copy initrd to newinitrd");
+ status_system("umount initrd.from", "Cannot umount initrd.from");
+ status_system("umount initrd.to", "Cannot umount initrd.to");
+ rmdir("initrd.from") || print STDERR "Cannot remove temp mount point initrd.from\n";
+ rmdir("initrd.to") || print STDERR "Cannot remove temp mount point initrd.to\n";
+ return (0);
+}
+
+1;
diff --git a/gpxe/contrib/mkffwnb/README b/gpxe/contrib/mkffwnb/README
new file mode 100644
index 00000000..38df4769
--- /dev/null
+++ b/gpxe/contrib/mkffwnb/README
@@ -0,0 +1,69 @@
+This is a quick and dirty script to convert a floppyfw floppy
+(http://www.zelow.no/floppyfw/) to a tagged image for booting with
+Etherboot (http://etherboot.sourceforge.net/). The advantages of network
+booting include: it's much faster loading from the network than from a
+floppy disk, you can boot from any size floppy, and you are not limited
+to the maximum of 1.44 MB of the physical floppy. If you have enough RAM
+and use a virtual floppy to build the initial boot image, you can put as
+much on it as will fit the ramdisk.
+
+See further down under -nonet if you want to boot from HD or CDROM.
+
+This program requires mtools, tar, bzip2, loopback mount in the kernel,
+and root privileges to execute. Hope you have them.
+
+This script works for any of the releases for which a subdirectory of
+that name is provided, but it should not be too hard to make it work for
+other releases, all that is done here is to substitute some scripts for
+the distributed ones.
+
+First of all you should make the floppy work the way you want before
+converting it to a tagged image. This involves editing the various
+config files on the floppy. Instructions on this are distributed from
+the floppyfw web page mentioned above.
+
+Edit the $tftpdir assignment for the directory where you put your tagged
+images. Edit the $libdir assignment and the use lib directive near the
+top if you decide to put this package somewhere other than
+/usr/local/lib/mkffwnb/. Adjust the instructions below as necessary.
+
+Copy everything to $libdir.
+
+ mkdir -p /usr/local/lib/mkffwnb/
+ cp -a . /usr/local/lib/mkffwnb/
+
+Make a link from /usr/local/lib/mkffwnb/mkffwnb.pl to
+/usr/local/bin/mkffwnb so that it's in your path.
+
+ ln -s /usr/local/lib/mkffwnb/mkffwnb.pl /usr/local/bin/mkffwnb
+
+Then run it as:
+
+ mkffwnb
+
+You can also provide a floppy drive as an argument, e.g.
+
+ mkffwnb x:
+
+where x: could be mapped to a disk file. This allows you to build an
+image without a real floppy drive. Remember that for virtual drives root
+must have the mapping for the drive in question in ~root/.mtoolsrc.
+
+You can use the option --localtime=/etc/localtime to specify that the
+file /etc/localtime is to be copied to /etc/localtime on the initrd.
+Instead of /etc/localtime, you can use any of the timezone files under
+/usr/share/zoneinfo/, it's just that /etc/localtime will usually be the
+correct one for your timezone.
+
+If you use the option -nonet, it leaves the intermediate files in
+$tempdir, /tmp/mkffwnb by default. This is useful if you want the
+vmlinuz and initrd.gz files for use with LILO or isolinux to boot from
+HD or CDROM. Actually you can also use these with a floppy, it loads
+faster if you fold all the scripts and modules into the initrd ahead
+of time.
+
+mkffwnb has to be run as root because it uses loopback mounts and also
+because the files inside the initrd are owned by root.
+
+Ken Yap
+2003-04-20
diff --git a/gpxe/contrib/mkffwnb/mkffwnb.pl b/gpxe/contrib/mkffwnb/mkffwnb.pl
new file mode 100755
index 00000000..555ec3ca
--- /dev/null
+++ b/gpxe/contrib/mkffwnb/mkffwnb.pl
@@ -0,0 +1,226 @@
+#!/usr/bin/perl -w
+#
+# Perl script to make a bootable image from a floppyfw floppy
+# The basic idea is to unpack and replace or convert all
+# the necessary config files into the initrd
+# and then make a bootable image out of it
+#
+# The --format= option overrides the default of nbi or elf hardcoded
+# in the source. Valid arguments are nbi or elf.
+#
+# The --output= options specifies an output file instead of stdout
+# The --nonet option specifies that a netbootable image is not to
+# be built but the vmlinuz and initrd.gz files left behind in $tempdir
+# The --localtime=f option specifies a timezone file that's to be
+# copied to /etc/localtime in the initrd, allowing a different timezone.
+# The --ffw29 option is intended for 2.9.x and above and extends
+# the size of the initrd by making a bigger one and copying the original over.
+#
+# The first non-option argument is taken to be the letter of a floppy to
+# convert, e.g. a:, b: or even x: where x: is mapped to a file using
+# mtools mapping in $HOME/.mtoolsrc. See the mtools documentation.
+# Thus you can work on a floppy image in a disk file and only write
+# to a floppy with dd or cp when you need to test the image.
+
+use Getopt::Long;
+
+use lib '/usr/local/lib/mkffwnb/';
+use Extendinitrd;
+
+use strict;
+
+use vars qw($testing $verbose $localtime $nonet $format $ffw29 $imagefile
+ $floppy $libdir $tftpdir $output $tempdir $tempmount);
+
+sub findversion () {
+ my ($version) = grep(/FloppyFW/, `mtype $imagefile ${floppy}floppyfw.msg`);
+ return '' unless defined($version) and $version ne '';
+ chomp($version);
+ $version =~ s/.*FloppyFW (\d+\.\d+\.\d+(\.\d+)?).*/$1/;
+ return ($version);
+}
+
+sub getappendargs () {
+ my ($append) = join(' ', grep(/^\s*(append\s|console=)/, `mtype $imagefile ${floppy}syslinux.cfg`));
+ chomp ($append);
+ my @args = split(/\s+/, $append);
+ my @result = ();
+ foreach $_ (@args) {
+ next if (/^$/ or /^append/ or /^initrd=/);
+ next if (!$ffw29 and /^root=/);
+ push (@result, $_);
+ }
+ return (join(' ', @result));
+}
+
+# Copy whole floppy to the current directory
+# m preserves timestamps, n overwrites without warning and / means recursive
+sub mcopy ($) {
+ my ($tempdir) = @_;
+
+ print "mcopy $imagefile -mn/ ${floppy}* $tempdir\n";
+ my $status = system("mcopy -mn/ $imagefile ${floppy}* $tempdir");
+ return ($status / 256);
+}
+
+# Gunzip file, -f forces overwriting of uncompressed file
+sub gunzip ($) {
+ my ($file) = @_;
+
+ print "Gunzipping $file\n" if ($verbose);
+ my $status = system('gunzip', '-f', $file);
+ return ($status / 256);
+}
+
+# Gzip file, -f forces overwriting of compressed file
+sub gzip ($) {
+ my ($file) = @_;
+
+ print "Gzipping $file\n" if ($verbose);
+ my $status = system('gzip', '-9', '-f', $file);
+ return ($status / 256);
+}
+
+sub loopbackmount ($$) {
+ my ($file, $point) = @_;
+
+ print "Mounting $file on $point loopback\n" if ($verbose);
+ my $status = system('mount', '-o', 'loop', $file, $point);
+ return ($testing ? 0 : $status / 256);
+}
+
+sub loopbackumount ($) {
+ my ($point) = @_;
+
+ print "Umounting $point\n" if ($verbose);
+ my $status = system('umount', $point);
+ return ($testing ? 0 : $status / 256);
+}
+
+# Convert DOS CR-NL to Unix NL. $dst has implied prefix of $tempmount
+# Use @output for temporary storage in case we write back to the same file
+sub dostounix ($$) {
+ my ($src, $dst) = @_;
+ my @output = ();
+
+ $dst = "$tempmount/$dst";
+ print "Converting $src to $dst\n" if ($verbose);
+ unless (open(S, $src)) {
+ print "$src: $!\n";
+ return (0);
+ }
+ while (<S>) {
+ chomp;
+ tr /\015//d;
+ push(@output, $_);
+ }
+ close(S);
+ open(D, ">$dst") or return;
+ for $_ (@output) {
+ print D "$_\n";
+ }
+ close(D);
+ chmod(0755, $dst);
+ return (1);
+}
+
+sub bunzip2untar ($$) {
+ my ($file, $dir) = @_;
+
+ print "Unpacking $file into $dir\n" if ($verbose);
+ system("bunzip2 < $file | (cd $dir; tar xf -)");
+}
+
+$testing = $< != 0;
+$verbose = 1;
+$format = '';
+$imagefile = '';
+GetOptions('output=s' => \$output,
+ 'nonet!' => \$nonet,
+ 'localtime=s' => \$localtime,
+ 'format=s' => \$format,
+ 'ffw29!' => \$ffw29,
+ 'ffw30!' => \$ffw29,
+ 'i=s' => \$imagefile);
+if (defined($output) and $output !~ m(^/)) {
+ my $d = `pwd`;
+ chomp($d);
+ $output = "$d/$output";
+}
+if ($imagefile) {
+ $imagefile = "-i $imagefile";
+}
+$libdir = '/usr/local/lib/mkffwnb';
+$tftpdir = '/usr/local/var/tftpboot';
+# default can also be 'elf'
+$format = 'nbi' if ($format ne 'elf' and $format ne 'nbi');
+$floppy = $#ARGV >= 0 ? $ARGV[0] : 'a:';
+print <<EOF;
+This program requires mtools, tar, bzip2, loopback mount in the kernel,
+and root privileges to execute. Hope you have them.
+EOF
+my $version = &findversion();
+$version ne '' or die "Cannot determine version\n";
+print "Version $version\n";
+my $append = &getappendargs();
+$append = "--append='$append'" if $append ne '';
+print "$append\n";
+$libdir .= '/' . $version;
+-d $libdir or die "Cannot find files for $version\n";
+$tempdir = $nonet ? '/tmp/mkffwnb' : "/tmp/mkffwnb$$";
+$tempmount = 'tmpmount';
+mkdir($tempdir, 0755);
+print "Copying files off floppy, please be patient...\n";
+&mcopy($tempdir) == 0 or die "Mcopy failed, diskette problem?\n";
+chdir($tempdir);
+&gunzip('initrd.gz') == 0 or die "Gunzip of initrd.gz failed\n";
+if ($ffw29) {
+ extendinitrd("initrd", 5760);
+ system("mv newinitrd initrd");
+}
+mkdir($tempmount, 0755);
+&loopbackmount('initrd', $tempmount) == 0 or die "Loopback mount failed\n";
+&dostounix("$libdir/linuxrc", "linuxrc") if (-r "$libdir/linuxrc");
+unless (&dostounix("$libdir/floppyfw.ini", "floppyfw.ini")) {
+ &dostounix("floppyfw/floppyfw.ini", $ffw29 ? "etc/floppyfw.ini" : "floppyfw.ini");
+}
+&dostounix("config", $ffw29 ? "etc/config.prelogin" : "etc/config");
+for my $i (glob('*.bz2 floppyfw/add.bz2 modules/*.bz2 packages/*.bz2')) {
+ &bunzip2untar($i, $tempmount);
+}
+for my $i (glob('packages/*.ini')) {
+ my $file = $i;
+ $file =~ s:packages/::;
+ &dostounix($i, "etc/$file");
+}
+&dostounix("hosts", "etc/hosts");
+&dostounix("modules.lst", "etc/modules.lst");
+&dostounix("network.ini", "etc/network.init");
+&dostounix("firewall.ini", "etc/firewall.init");
+&dostounix("syslog.cfg", "etc/syslog.conf");
+&dostounix("packages/timeinfo", "etc/localtime");
+system("cp -p licenses/* $tempmount/licenses/");
+# This conditional code is for 1.1.2 and below
+unless (glob('modules/*.bz2')) {
+ print "Copying additional modules\n" if ($verbose);
+ system("cp -p modules/* $tempmount/lib/modules/");
+}
+# If a timezone file has been specified, copy that onto initrd
+if (defined($localtime)) {
+ if (-r $localtime) {
+ print "Copying $localtime to $tempmount/etc/localtime\n";
+ system("cp -p $localtime $tempmount/etc/localtime");
+ } else {
+ print "$localtime: $!\n";
+ }
+}
+&loopbackumount($tempmount) == 0 or die "Loopback umount failed\n";
+&gzip('initrd') == 0 or die "Gzip of initrd failed\n";
+if ($nonet) {
+ print "Floppyfw directory in $tempdir\n";
+} else {
+ print "Calling mk$format-linux to make the netbootable image\n" if ($verbose);
+ $output = "$tftpdir/floppyfw-$version.nb" if (!defined($output));
+ system("mk$format-linux $append --output=$output vmlinuz initrd.gz");
+ system("rm -fr $tempdir");
+}
diff --git a/gpxe/contrib/mklrpnb/README.txt b/gpxe/contrib/mklrpnb/README.txt
new file mode 100644
index 00000000..c99c55c6
--- /dev/null
+++ b/gpxe/contrib/mklrpnb/README.txt
@@ -0,0 +1,4 @@
+This is a quick and dirty Perl program to make a netbootable
+image from a Linux Router floppy. It was tested with a Coyote Linux
+(http://www.coyotelinux.com) floppy which is based on LRP. You need tar,
+mtools, mknbi-1.0, and of course, perl, to run this script.
diff --git a/gpxe/contrib/mklrpnb/extractdach.pl b/gpxe/contrib/mklrpnb/extractdach.pl
new file mode 100644
index 00000000..6c81da38
--- /dev/null
+++ b/gpxe/contrib/mklrpnb/extractdach.pl
@@ -0,0 +1,191 @@
+#!/usr/bin/perl -w
+#
+# A program to make a netbootable image from a LRP firewall floppy
+#
+# Tested on a Dachstein Linux floppy image available from
+# http://lrp1.steinkuehler.net/ or via http://leaf.sourceforge.net/
+
+# The most recent version of this script and a companion HowTo is available at
+# http://members.optushome.com.au/graybeard/linux/netboot.html
+#
+# Modified from the mklrpnb file found in the contrib/mklrpnb directory of the
+# Etherboot source at http://etherboot.sourceforge.net/
+#
+# Modifications by Glenn McK <graybeard@users.sourceforge.net>
+# $Id$
+#####################################
+
+# this entry will need changing
+$image = "/home/graybeard/etherboot/dachstein-v1.0.2-1680.bin";
+
+# these can remain, but change them if desired
+#
+# the next argument defaults to firewall if no other name is passed via the
+# command line, this will be the directory where distribution will be expanded
+# under $base and also the directory in /tftpboot for lrp.nb
+
+my $uniqdir = shift || 'firewall';
+
+$mntdir = "/mnt/floppy"; # where the above image file can be mounted
+$tftpbase = "/tftpboot";
+$tftpboot = "$tftpbase/$uniqdir"; # where the netboot images will be available
+$base = "/usr/src/LRP";
+$dachorg = "$base/dach-org-$uniqdir"; # a copy required to make the distribution
+$dachnew = "$base/lrp-$uniqdir"; # the base files for the new distribution
+$packages = "$dachnew/var/lib/lrpkg"; # list to allow lrcfg to display Packages
+
+# everything below should be okay
+######################################
+
+if ( !-e $image ) {
+ print
+"\n\tA valid LRP file and directory are required\n\tdownload one then edit $0\n\n";
+ exit 1;
+}
+if ( !-d $base ) {
+ mkdir( $base, 0700 );
+}
+
+if ( !-d $dachorg ) {
+ mkdir( $dachorg, 0700 );
+}
+
+if ( !-d $dachnew ) {
+ mkdir( $dachnew, 0700 );
+ `umount $mntdir`;
+ `mount -o ro,loop $image $mntdir`;
+
+ `cp -vr $mntdir/* $dachorg/`;
+
+ @cfg = `cat $mntdir/syslinux.cfg`;
+
+ unless ( defined(@cfg) ) {
+ print "Cannot find syslinux.cfg on $mntdir\n";
+ exit 1;
+ }
+ print "cfg = @cfg\n";
+ ($append) = grep( /append/, @cfg ); # find the append= line
+ print "append = \n$append\n";
+ chomp($append); # remove trailing newline
+ $append =~ s/append=//; # remove the append= at beginning
+ print "strip append = \n$append\n\n";
+ @args = split ( / /, $append ); # split into arguments at whitespace
+ ($root) = grep( /^initrd=/, @args ); # find the initrd= argument
+ $root =~ s/^initrd=//; # remove the initrd= at beginning
+ $root =~ s/\.lrp$//; # cleanup for paclages list
+ print "strip initrd = \n$root\n\n";
+ ($lrp) = grep( /^LRP=/, @args ); # find the LRP= argument
+ $lrp =~ s/^LRP=//; # remove the LRP= at beginning
+ print "strip LRP =\n$lrp\n\n";
+ @lrp = split ( /,/, $lrp ); # split into filenames at ,
+ unshift ( @lrp, $root ); # prepend the root LRP filename
+ @pack = @lrp;
+ print "LRP =\n@lrp\n\n";
+ $append = '';
+
+ foreach $i (@args) { # rebuild the append string
+ next if ( $i =~ /^initrd=/ ); # minus the unneeded parameters
+ next if ( $i =~ /^LRP=/ );
+ next if ( $i =~ /^boot=/ );
+ next if ( $i =~ /^PKGPATH=/ );
+ print "$i = i\n";
+ $append .= "$i ";
+ }
+
+ print "final append = \n$append\n";
+
+ chdir($dachnew) or die "$dachnew: $!\n";
+ foreach $i (@lrp) {
+ $i .= '.lrp' if $i !~ /\.lrp$/;
+ print "\n\n\nUnpacking $i\n";
+ system("ln -svf $dachorg/$i ${dachorg}/${i}.tar.gz");
+ chmod 0600, "$dachorg/$i";
+ system("cat $mntdir/$i | tar zxvf -");
+ }
+
+ # create file for lrcfg to display packages
+ open( PACKAGES, ">$packages/packages" )
+ || print "unable to modify $packages:$!\n";
+ foreach $line (@pack) {
+ print PACKAGES "$line\n";
+ }
+ close PACKAGES;
+
+ # prevent previous file from being overwritten during installation
+ # and also mess with some values in /linuxrc to hide non errors
+ open( LINUXRC, "$packages/root.linuxrc" );
+ @text = <LINUXRC>;
+ close LINUXRC;
+ open( LINUXRC, ">$packages/root.linuxrc" );
+ foreach $line (@text) {
+ $line =~ s/PFX\/packages/PFX\/packages-old \
+\t\t\t\t# packages changed to packages-old for netboot setup/;
+ $line =~
+s/^rc=1/# rc=1 changed to rc=0 to suppress error messages for netboot setup \
+rc=0/;
+ $line =~
+s/echo -n \" \(nf\!\)\"/#echo -n \" \(nf\!\)\" changed to reflect ToDo list \
+\t\t\techo -n \" netboot setup - No backups possible from this machine - ToFix ?"/;
+ print LINUXRC $line;
+ }
+ close LINUXRC;
+
+ # swap interfaces around in network config file
+ # eth1 is the new external eth0 is OUR internal server access
+ open( NETWORK, "$dachnew/etc/network.conf" )
+ || print "Unable to modify NETWORK:$!\n";
+ @text = <NETWORK>;
+ close NETWORK;
+ open( NETWORK, ">$dachnew/etc/network.conf" )
+ || print "Unable to modify NETWORK:$!\n";
+ foreach $line (@text) {
+ $line =~ s/eth0/eth00/;
+ $line =~ s/eth1/eth0/;
+ $line =~ s/eth00/eth1/;
+ print NETWORK $line;
+ }
+ close NETWORK;
+
+ `echo $append > $dachorg/appendstr`;
+
+ `umount /mnt/floppy`;
+ print "\nThe files have been extracted to $dachnew\n";
+ system("ls -al $dachnew");
+}
+else {
+ print "\n\n\t$image \n \thas already been extracted to $dachnew \
+\tNow skipping to the next step where the netboot file\
+\twill be created.\n";
+
+ $append = `cat $dachorg/appendstr`;
+ print "\nThe new append string will be...\n$append\n";
+
+ chdir($dachnew);
+ if ( !-d $tftpbase ) {
+ mkdir( $tftpbase, 0710 );
+ system("chgrp nobody $tftpbase");
+ }
+
+ unlink($tftpboot);
+
+ # these permissions really need changing to something secure
+ mkdir( $tftpboot, 0710 );
+ system("chgrp nobody $tftpboot");
+ print "\tRepacking to $tftpboot/lrp.lrp\n";
+ system("tar zcf $tftpboot/lrp.lrp *");
+ print "\tExtracting kernel image from $dachorg\n";
+ system("cat $dachorg/linux > $tftpboot/lrp.ker");
+ print "\tCreating netboot image $tftpboot/lrp.nb\n";
+ system(
+"mknbi-linux --append='$append' --output=$tftpboot/lrp.nb $tftpboot/lrp.ker $tftpboot/lrp.lrp"
+ );
+ chmod 0604, "$tftpboot/lrp.nb", "$tftpboot/lrp.ker", "$tftpboot/lrp.lrp";
+ print "\nThese netboot files are in $tftpboot\n";
+ system("ls -al $tftpboot");
+ print "\n The owner and permissions for $tftpboot \
+ and files should be checked for security. The above\
+permissions assume that tftp is running chroot (nobody)
+ drwx--r--- root:nobody /tftpboot\n\n";
+}
+
+exit 0;
diff --git a/gpxe/contrib/mklrpnb/mklrpnb b/gpxe/contrib/mklrpnb/mklrpnb
new file mode 100755
index 00000000..47590f74
--- /dev/null
+++ b/gpxe/contrib/mklrpnb/mklrpnb
@@ -0,0 +1,45 @@
+#!/usr/bin/perl -w
+#
+# A program to make a netbootable image from a LRP firewall floppy
+# Tested on a Coyote Linux floppy
+#
+@cfg = `mtype a:syslinux.cfg`;
+unless (defined(@cfg)) {
+ print "Cannot find syslinux.cfg on floppy\n";
+ exit 1;
+}
+($append) = grep(/^append/, @cfg); # find the append= line
+chomp($append); # remove trailing newline
+$append =~ s/append=//; # remove the append= at beginning
+@args = split(/ /, $append); # split into arguments at whitespace
+($root) = grep(/^initrd=/, @args); # find the initrd= argument
+$root =~ s/^initrd=//; # remove the initrd= at beginning
+($lrp) = grep(/^LRP=/, @args); # find the LRP= argument
+$lrp =~ s/^LRP=//; # remove the LRP= at beginning
+@lrp = split(/,/, $lrp); # split into filenames at ,
+unshift(@lrp, $root); # prepend the root LRP filename
+$append = '';
+foreach $i (@args) { # rebuild the append string
+ next if ($i =~ /^initrd=/); # minus the unneeded parameters
+ next if ($i =~ /^LRP=/);
+ next if ($i =~ /^boot=/);
+ $append .= "$i ";
+}
+# print "$append\n";
+$tempdir = "/tmp/lrp$$";
+mkdir($tempdir, 0777) or die "$tempdir: $!\n";
+chdir($tempdir) or die "$tempdir: $!\n";
+foreach $i (@lrp) {
+ $i .= '.lrp' if $i !~ /\.lrp$/;
+ print "Unpacking $i\n";
+ system("mtype a:$i | tar zxvf -");
+}
+print "Repacking to /tmp/lrp.lrp\n";
+system("tar zcf /tmp/lrp.lrp *");
+chdir('/tmp') or die "/tmp: $!\n";
+system("rm -fr $tempdir");
+print "Extracting kernel image from floppy\n";
+system("mtype a:linux > /tmp/lrp.ker");
+print "Creating netboot image in /tmp/lrp.nb\n";
+system("mkelf-linux --append='$append' --output=/tmp/lrp.nb /tmp/lrp.ker /tmp/lrp.lrp");
+exit 0;
diff --git a/gpxe/contrib/mntnbi/mntnbi.pl b/gpxe/contrib/mntnbi/mntnbi.pl
new file mode 100755
index 00000000..55b81484
--- /dev/null
+++ b/gpxe/contrib/mntnbi/mntnbi.pl
@@ -0,0 +1,97 @@
+#!/usr/bin/perl -w
+#
+# Quick Perl program to decode and display details about
+# tagged images created by mknbi, and then mount the contained
+# DOS filesystem using a loop-back mount
+#
+# Martin Atkins, November 1998
+# by hacking disnbi by
+# Ken Yap, September 1998
+#
+#
+
+sub getvendordata {
+ my ($flags) = @_;
+
+ my $vendordata = '';
+ my $vendorlen = ($flags & 0xff) >> 4;
+ if ($vendorlen > 0) {
+ $vendorlen *= 4;
+ $vendordata = unpack("A$vendorlen", substr($imageheader, $curoffset));
+ $curoffset += $vendorlen;
+ }
+ return ($vendordata);
+}
+
+sub decodesegmentflags {
+ my ($flags) = @_;
+
+ $flags >>= 24;
+ $flags &= 0x3;
+ ($flags == 0) and $type = "Absolute";
+ ($flags == 1) and $type = "Follows last segment";
+ ($flags == 2) and $type = "Below end of memory";
+ ($flags == 3) and $type = "Below last segment loaded";
+ return ($type);
+}
+
+sub onesegment
+{
+ my ($segnum) = @_;
+ my ($type, $vendordata);
+
+ my ($flags, $loadaddr, $imagelen, $memlength) = unpack("V4", substr($imageheader, $curoffset));
+ $curoffset += 16;
+ print "Segment number $segnum\n";
+ printf "Load address:\t\t%08x\n", $loadaddr;
+ printf "Image length:\t\t%d\n", $imagelen;
+ printf "Memory length:\t\t%d\n", $memlength;
+ $type = &decodesegmentflags($flags);
+ printf "Position:\t\t$type\n";
+ printf "Vendor tag:\t\t%d\n", ($flags >> 8) & 0xff;
+ if (($vendordata = &getvendordata($flags)) ne '') {
+ print "Vendor data:\t\t", $vendordata, "\n";
+ }
+ print "\n";
+ push (@seglengths, $imagelen);
+ return (($flags >> 26) & 1);
+}
+
+@seglengths = ();
+$#ARGV == 1 or die "Usage: mntnbi tagged-image-file dir\n";
+$imagefile= $ARGV[0];
+open(I, $ARGV[0]) or die "$imagefile: $!\n";
+(defined($status = sysread(I, $imageheader, 512)) and $status == 512)
+ or die "$imagefile: Cannot read header\n";
+$headerrecord = substr($imageheader, 0, 16);
+($magic, $flags, $bx, $ds, $ip, $cs) = unpack("a4Vv4", $headerrecord);
+$magic eq "\x36\x13\x03\x1B" or die "$imagefile: Not a tagged image file\n";
+$curoffset = 16;
+
+# Now decode the header
+
+printf "Header location:\t%04x:%04x\n", $ds, $bx;
+printf "Start address:\t\t%04x:%04x\n", $cs, $ip;
+printf "Flags:\n";
+ print "Return to loader after execution (extension)\n" if (($flags >> 8) & 1);
+if (($vendordata = &getvendordata($flags)) ne '') {
+ print "Vendor data:\t\t", $vendordata, "\n";
+}
+print "\n";
+
+# Now decode each segment record
+
+$segnum = 1;
+do {
+ $lastrecord = &onesegment($segnum);
+ ++$segnum;
+} while (!$lastrecord);
+
+if ($#seglengths != 1) {
+ die "This is not a DOS image $#seglengths\n";
+}
+$offset = 512 + $seglengths[0];
+print "mounting filesystem at offset $offset in $ARGV[0] on $ARGV[1]\n";
+$rc = system "mount $ARGV[0] $ARGV[1] -t msdos -o loop,offset=$offset";
+print "Done\n" if ($rc == 0);
+exit(0);
diff --git a/gpxe/contrib/nfs-swap/README b/gpxe/contrib/nfs-swap/README
new file mode 100644
index 00000000..b95e5d7b
--- /dev/null
+++ b/gpxe/contrib/nfs-swap/README
@@ -0,0 +1,2 @@
+For more information please check
+http://nfs-swap.dot-heine.de
diff --git a/gpxe/contrib/p910nd-0.8/Makefile b/gpxe/contrib/p910nd-0.8/Makefile
new file mode 100644
index 00000000..c2827d27
--- /dev/null
+++ b/gpxe/contrib/p910nd-0.8/Makefile
@@ -0,0 +1,10 @@
+# Comment out the second command and uncomment the first command
+# below if you don't want to use libwrap (hosts.{allow,deny} access control)
+
+# If you don't have it in /var/log/subsys, uncomment and define
+#CFLAGS+=-DLOCKFILE_DIR=\"/var/log\"
+LIBWRAP=-lwrap
+
+p910nd: p910nd.c
+# $(CC) -Wall $(CFLAGS) -o $@ p910nd.c
+ $(CC) -Wall $(CFLAGS) -DUSE_LIBWRAP -o $@ p910nd.c $(LIBWRAP)
diff --git a/gpxe/contrib/p910nd-0.8/banner.pl b/gpxe/contrib/p910nd-0.8/banner.pl
new file mode 100755
index 00000000..5a238c80
--- /dev/null
+++ b/gpxe/contrib/p910nd-0.8/banner.pl
@@ -0,0 +1,9 @@
+#!/usr/bin/perl
+while (1)
+{
+ exit 0 if read(STDIN,$c,1) == 0;
+ last if ($cl eq "\031" && $c eq "\001");
+ $cl = $c;
+}
+kill 'STOP',$$;
+exit 0
diff --git a/gpxe/contrib/p910nd-0.8/client.pl b/gpxe/contrib/p910nd-0.8/client.pl
new file mode 100755
index 00000000..0b0e4c36
--- /dev/null
+++ b/gpxe/contrib/p910nd-0.8/client.pl
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+# edit this to the printer hostname
+$them = 'ken';
+$port = 9101;
+
+open(STDIN, "$ARGV[0]") if $#ARGV >= 0;
+
+use Socket;
+#use Sys::Hostname;
+
+#$hostname = hostname;
+
+($name, $aliases, $proto) = getprotobyname('tcp');
+($name, $aliases, $port) = getservbyname($port, 'tcp')
+ unless $port =~ /^\d+$/;
+
+#$thisaddr = inet_aton($hostname);
+#defined($thisaddr) or &errexit("inet_aton: cannot resolve $hostname\n");
+
+$thataddr = inet_aton($them);
+defined($thataddr) or &errexit("inet_aton: cannot resolve $them\n");
+
+socket(S, PF_INET, SOCK_STREAM, $proto) or &errexit("socket: $!\n");
+
+#$this = sockaddr_in(0, $thisaddr);
+#bind(S, $this) || &errexit("bind: $!\n");
+
+$that = sockaddr_in($port, $thataddr);
+connect(S, $that) || &errexit("connect: $!\n");
+
+select(S); $| = 1; select(STDOUT);
+
+$buffer = '';
+while (1)
+{
+ $rin = '';
+ vec($rin, fileno(S), 1) = 1;
+ $nfound = select($rout=$rin, $wout=$rin, undef, undef);
+ if (vec($rout, fileno(S), 1)) {
+ print STDERR "$buffer\n" if
+ defined($nread = sysread(S, $buffer, 8192));
+ }
+ if (vec($wout, fileno(S), 1)) {
+ $nread = read(STDIN, $buffer, 8192);
+ last if $nread == 0;
+ &errexit("write: $!\n") unless
+ defined($written = syswrite(S,$buffer,$nread));
+ }
+}
+close(S);
+exit 0;
+
+sub errexit
+{
+ print STDERR @_;
+ exit 2;
+}
diff --git a/gpxe/contrib/p910nd-0.8/p910nd.8 b/gpxe/contrib/p910nd-0.8/p910nd.8
new file mode 100644
index 00000000..24572c47
--- /dev/null
+++ b/gpxe/contrib/p910nd-0.8/p910nd.8
@@ -0,0 +1,93 @@
+.TH P910ND 8 "1 August 2004"
+.SH NAME
+p910nd \- port 9100+n printer daemon
+.SH SYNOPSIS
+.B p910nd
+[\fB-f device\fR]
+[\fB-i bindaddr\fR]
+[\fB-bv\fR]
+[\fB0|1|2\fR]
+.SH DESCRIPTION
+.I p910nd
+is a small daemon that copies any data received on the port
+it is listening on to the corresponding printer port.
+It is primarily intended for diskless Linux hosts running as printer drivers
+but there is no reason why it could not be used on diskful hosts.
+Port 9100 is copied to /dev/lp0, 9101 to /dev/lp1 and 9102 to /dev/lp2.
+The default is port 9100 to /dev/lp0.
+.LP
+The \fB-f\fR option can be used to specify a different printer device,
+e.g. /dev/usblp0.
+.LP
+The \fB-i\fR option can be used to specify binding to one address instead
+of all interfaces which is the default.
+.LP
+The \fB-b\fR option turns on bidirectional copying.
+.LP
+The \fB-v\fR option shows the version number.
+.SH INSTALLATION
+.I p910nd
+can be run as a standalone daemon or from inetd.
+It will automatically detect if it is running under inetd.
+.LP
+A sample SysVinit script,
+.IR p910nd.sh ,
+is provided for operation as a daemon.
+.I p910nd
+will change its name under ps to match the printer port, i.e.
+.I p9100d, p9101d
+and
+.IR p9102d .
+.LP
+When running under inetd, the
+.I /etc/inetd.conf
+entry should look something like this (with tcpwrappers protection):
+.sp
+.nf
+p9101 stream tcp nowait root /usr/sbin/tcpd /sbin/p910nd
+.fi
+.sp
+Don't forget to add an entry in
+.I /etc/services
+for the corresponding port.
+.LP
+If operating with lprng, use the host%port syntax for the
+printer device to send jobs to it.
+.LP
+If operating with CUPS, this is supported as the AppSocket
+protocol, also known as the JetDirect (probably TM) protocol.
+.LP
+If operating with classic Berkeley lpd, a sample client,
+.IR client.pl ,
+is provided.
+This should be installed as the ifilter (if=) in /etc/printcap.
+.I banner.pl
+should be installed as the ofilter (of=) in /etc/printcap.
+It may be necessary to create a dummy spool file for lpd (lp=).
+This file will be opened but not written to.
+The corresponding C versions are left as an exercise for the reader.
+.LP
+When running under inetd, more than one instance could be started.
+To avoid problems with multiple instances attempting to access the
+printer at the same time, make sure that only one client is active
+at any one time. This can be done by designating one host as the
+spooler and sending all jobs to this host. You will probably
+need to set up an intermediate queue anyway to provide print job filtering.
+.LP
+If compiled with USE_LIBWRAP and linked with -lwrap, it uses the libwrap
+library (tcpwrappers). Access control can be done with /etc/hosts.allow
+and /etc/hosts.deny. The service name is p910nd.
+.SH DIAGNOSTICS
+.I p910nd
+logs error messages to syslog.
+.SH "SEE ALSO"
+printcap(5), hosts_access(5)
+.SH FILES
+/var/run/p9100d.pid, /var/lock/subsys/p9100d, /etc/hosts.allow, /etc/hosts.deny
+.SH COPYRIGHT
+.I p910nd
+is under the GNU Public License
+.SH AUTHOR
+Ken Yap (ken_yap@users.sourceforge.net)
+.SH DATE
+Version 0.8 October 2004
diff --git a/gpxe/contrib/p910nd-0.8/p910nd.c b/gpxe/contrib/p910nd-0.8/p910nd.c
new file mode 100644
index 00000000..c2473d8d
--- /dev/null
+++ b/gpxe/contrib/p910nd-0.8/p910nd.c
@@ -0,0 +1,420 @@
+/*
+ * Port 9100+n daemon
+ * Accepts a connection from port 9100+n and copy stream to
+ * /dev/lpn, where n = 0,1,2.
+ *
+ * Run standalone as: p910nd [0|1|2]
+ *
+ * Run under inetd as:
+ * p910n stream tcp nowait root /usr/sbin/tcpd p910nd [0|1|2]
+ * where p910n is an /etc/services entry for
+ * port 9100, 9101 or 9102 as the case may be.
+ * root can be replaced by any uid with rw permission on /dev/lpn
+ *
+ * Port 9100+n will then be passively opened
+ * n defaults to 0
+ *
+ * Version 0.8
+ * Allow specifying address to bind to
+ *
+ * Version 0.7
+ * Bidirectional data transfer
+ *
+ * Version 0.6
+ * Arne Bernin fixed some cast warnings, corrected the version number
+ * and added a -v option to print the version.
+ *
+ * Version 0.5
+ * -DUSE_LIBWRAP and -lwrap enables hosts_access (tcpwrappers) checking.
+ *
+ * Version 0.4
+ * Ken Yap (ken_yap@users.sourceforge.net), April 2001
+ * Placed under GPL.
+ *
+ * Added -f switch to specify device which overrides /dev/lpn.
+ * But number is still required get distinct ports and locks.
+ *
+ * Added locking so that two invocations of the daemon under inetd
+ * don't try to open the printer at the same time. This can happen
+ * even if there is one host running clients because the previous
+ * client can exit after it has sent all data but the printer has not
+ * finished printing and inetd starts up a new daemon when the next
+ * request comes in too soon.
+ *
+ * Various things could be Linux specific. I don't
+ * think there is much demand for this program outside of PCs,
+ * but if you port it to other distributions or platforms,
+ * I'd be happy to receive your patches.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef USE_LIBWRAP
+#include "tcpd.h"
+int allow_severity, deny_severity;
+extern int hosts_ctl(char *daemon, char *client_name,
+ char *client_addr, char *client_user);
+#endif
+
+#define BASEPORT 9100
+#define PIDFILE "/var/run/p910%cd.pid"
+#ifdef LOCKFILE_DIR
+#define LOCKFILE LOCKFILE_DIR "/p910%cd"
+#else
+#define LOCKFILE "/var/lock/subsys/p910%cd"
+#endif
+#define PRINTERFILE "/dev/lp%c"
+#define LOGOPTS LOG_ERR
+
+static char *progname;
+static char version[] = "p910nd Version 0.8";
+static int lockfd = -1;
+static char *device = 0;
+static int bidir = 0;
+static char *bindaddr = 0;
+
+void usage(void)
+{
+ fprintf(stderr, "Usage: %s [-f device] [-i bindaddr] [-bv] [0|1|2]\n", progname);
+ exit(1);
+}
+
+void show_version (void)
+{
+ fprintf(stdout, "%s \n", version);
+}
+
+FILE *open_printer(int lpnumber)
+{
+ FILE *f;
+ char lpname[sizeof(PRINTERFILE)];
+
+#ifdef TESTING
+ (void)snprintf(lpname, sizeof(lpname), "/dev/tty");
+#else
+ (void)snprintf(lpname, sizeof(lpname), PRINTERFILE, lpnumber);
+#endif
+ if (device == 0)
+ device = lpname;
+ if ((f = fopen(device, bidir ? "w+" : "w")) == NULL)
+ {
+ syslog(LOGOPTS, "%s: %m\n", device);
+ exit(1);
+ }
+ return (f);
+}
+
+int get_lock(int lpnumber)
+{
+ char lockname[sizeof(LOCKFILE)];
+ struct flock lplock;
+
+ (void)snprintf(lockname, sizeof(lockname), LOCKFILE, lpnumber);
+ if ((lockfd = open(lockname, O_CREAT|O_RDWR)) < 0)
+ {
+ syslog(LOGOPTS, "%s: %m\n", lockname);
+ return (0);
+ }
+ memset(&lplock, 0, sizeof(lplock));
+ lplock.l_type = F_WRLCK;
+ lplock.l_pid = getpid();
+ if (fcntl(lockfd, F_SETLKW, &lplock) < 0)
+ {
+ syslog(LOGOPTS, "%s: %m\n", lockname);
+ return (0);
+ }
+ return (1);
+}
+
+void free_lock(void)
+{
+ if (lockfd >= 0)
+ (void)close(lockfd);
+}
+
+/* Copy network socket to FILE f until EOS */
+int copy_stream(int fd, FILE *f)
+{
+ int nread;
+ char buffer[8192];
+
+ if (bidir) {
+ FILE *nf;
+
+ if ((nf = fdopen(fd, "w")) == NULL) {
+ syslog(LOGOPTS, "fdopen: %m\n");
+ }
+ for (;;) {
+ fd_set readfds;
+ int result;
+ int maxfd = fileno(f) > fd ? fileno(f) : fd;
+ FD_ZERO(&readfds);
+ FD_SET(fileno(f), &readfds);
+ FD_SET(fd, &readfds);
+ result = select(maxfd + 1, &readfds, 0, 0, 0);
+ if (result < 0)
+ return (result);
+ if (result == 0)
+ continue;
+ if (FD_ISSET(fd, &readfds)) {
+ nread = read(fd, buffer, sizeof(buffer));
+ if (nread <= 0)
+ break;
+ (void)fwrite(buffer, sizeof(char), nread, f);
+ }
+ if (FD_ISSET(fileno(f), &readfds)) {
+ nread = read(fileno(f), buffer, sizeof(buffer));
+ if (nread > 0 && nf != NULL) {
+ (void)fwrite(buffer, sizeof(char), nread, nf);
+ (void)fflush(nf);
+ }
+ }
+ }
+ (void)fflush(f);
+ (void)fclose(nf);
+ return (0);
+ } else {
+ while ((nread = read(fd, buffer, sizeof(buffer))) > 0)
+ (void)fwrite(buffer, sizeof(char), nread, f);
+ (void)fflush(f);
+ return (nread);
+ }
+}
+
+void one_job(int lpnumber)
+{
+ FILE *f;
+ struct sockaddr_in client;
+ socklen_t clientlen = sizeof(client);
+
+ if (getpeername(0, (struct sockaddr*) &client, &clientlen) >= 0)
+ syslog(LOGOPTS, "Connection from %s port %hu\n",
+ inet_ntoa(client.sin_addr),
+ ntohs(client.sin_port));
+ if (get_lock(lpnumber) == 0)
+ return;
+ f = open_printer(lpnumber);
+ if (copy_stream(0, f) < 0)
+ syslog(LOGOPTS, "copy_stream: %m\n");
+ fclose(f);
+ free_lock();
+}
+
+void server(int lpnumber)
+{
+ struct rlimit resourcelimit;
+#ifdef USE_GETPROTOBYNAME
+ struct protoent *proto;
+#endif
+ int netfd, fd, one = 1;
+ socklen_t clientlen;
+ struct sockaddr_in netaddr, client;
+ char pidfilename[sizeof(PIDFILE)];
+ FILE *f;
+ int ipret;
+
+#ifndef TESTING
+ switch (fork())
+ {
+ case -1:
+ syslog(LOGOPTS, "fork: %m\n");
+ exit (1);
+ case 0: /* child */
+ break;
+ default: /* parent */
+ exit(0);
+ }
+ /* Now in child process */
+ resourcelimit.rlim_max = 0;
+ if (getrlimit(RLIMIT_NOFILE, &resourcelimit) < 0)
+ {
+ syslog(LOGOPTS, "getrlimit: %m\n");
+ exit(1);
+ }
+ for (fd = 0; fd < resourcelimit.rlim_max; ++fd)
+ (void)close(fd);
+ if (setsid() < 0)
+ {
+ syslog(LOGOPTS, "setsid: %m\n");
+ exit(1);
+ }
+ (void)chdir("/");
+ (void)umask(022);
+ fd = open("/dev/null", O_RDWR); /* stdin */
+ (void)dup(fd); /* stdout */
+ (void)dup(fd); /* stderr */
+ (void)snprintf(pidfilename, sizeof(pidfilename), PIDFILE, lpnumber);
+ if ((f = fopen(pidfilename, "w")) == NULL)
+ {
+ syslog(LOGOPTS, "%s: %m\n", pidfilename);
+ exit(1);
+ }
+ (void)fprintf(f, "%d\n", getpid());
+ (void)fclose(f);
+ if (get_lock(lpnumber) == 0)
+ exit(1);
+#endif
+ f = open_printer(lpnumber);
+#ifdef USE_GETPROTOBYNAME
+ if ((proto = getprotobyname("tcp")) == NULL)
+ {
+ syslog(LOGOPTS, "Cannot find protocol for TCP!\n");
+ exit(1);
+ }
+ if ((netfd = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0)
+#else
+ if ((netfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
+#endif
+ {
+ syslog(LOGOPTS, "socket: %m\n");
+ exit(1);
+ }
+ if (setsockopt(netfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
+ {
+ syslog(LOGOPTS, "setsocketopt: %m\n");
+ exit(1);
+ }
+ netaddr.sin_port = htons(BASEPORT + lpnumber - '0');
+ if (bindaddr == 0) {
+ netaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ } else {
+ ipret = inet_pton(AF_INET, bindaddr, &netaddr.sin_addr.s_addr);
+ if (ipret < 0) {
+ syslog(LOGOPTS, "inet_pton: %m\n");
+ exit(1);
+ } else if (ipret == 0) {
+ syslog(LOGOPTS, "inet_pton: invalid bind IP address\n");
+ exit(1);
+ }
+ }
+ memset(netaddr.sin_zero, 0, sizeof(netaddr.sin_zero));
+ if (bind(netfd, (struct sockaddr*) &netaddr, sizeof(netaddr)) < 0)
+ {
+ syslog(LOGOPTS, "bind: %m\n");
+ exit(1);
+ }
+ if (listen(netfd, 5) < 0)
+ {
+ syslog(LOGOPTS, "listen: %m\n");
+ exit(1);
+ }
+ clientlen = sizeof(client);
+ memset(&client, 0, sizeof(client));
+ while ((fd = accept(netfd, (struct sockaddr*) &client, &clientlen)) >= 0)
+ {
+#ifdef USE_LIBWRAP
+ if (hosts_ctl("p910nd", STRING_UNKNOWN,
+ inet_ntoa(client.sin_addr), STRING_UNKNOWN) == 0) {
+ syslog(LOGOPTS, "Connection from %s port %hd rejected\n",
+ inet_ntoa(client.sin_addr),
+ ntohs(client.sin_port));
+ close(fd);
+ continue;
+ }
+#endif
+ syslog(LOGOPTS, "Connection from %s port %hd accepted\n",
+ inet_ntoa(client.sin_addr),
+ ntohs(client.sin_port));
+ /*write(fd, "Printing", 8);*/
+ if (copy_stream(fd, f) < 0)
+ syslog(LOGOPTS, "copy_stream: %m\n");
+ (void)close(fd);
+ }
+ syslog(LOGOPTS, "accept: %m\n");
+ free_lock();
+ exit(1);
+}
+
+int is_standalone(void)
+{
+ struct sockaddr_in bind_addr;
+ socklen_t ba_len;
+
+ /*
+ * Check to see if a socket was passed to us from inetd.
+ *
+ * Use getsockname() to determine if descriptor 0 is indeed a socket
+ * (and thus we are probably a child of inetd) or if it is instead
+ * something else and we are running standalone.
+ */
+ ba_len = sizeof(bind_addr);
+ if (getsockname(0, (struct sockaddr*) &bind_addr, &ba_len) == 0)
+ return (0); /* under inetd */
+ if (errno != ENOTSOCK) /* strange... */
+ syslog(LOGOPTS, "getsockname: %m\n");
+ return (1);
+}
+
+int main(int argc, char *argv[])
+{
+ int c, lpnumber;
+ char *p;
+
+ if (argc <= 0) /* in case not provided in inetd.conf */
+ progname = "p910nd";
+ else
+ {
+ progname = argv[0];
+ if ((p = strrchr(progname, '/')) != 0)
+ progname = p + 1;
+ }
+ lpnumber = '0';
+ while ((c = getopt(argc, argv, "bi:f:v")) != EOF)
+ {
+ switch (c)
+ {
+ case 'b':
+ bidir = 1;
+ break;
+ case 'f':
+ device = optarg;
+ break;
+ case 'i':
+ bindaddr = optarg;
+ break;
+ case 'v':
+ show_version();
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc > 0)
+ {
+ if (isdigit(argv[0][0]))
+ lpnumber = argv[0][0];
+ }
+ /* change the n in argv[0] to match the port so ps will show that */
+ if ((p = strstr(progname, "p910n")) != NULL)
+ p[4] = lpnumber;
+
+ /* We used to pass (LOG_PERROR|LOG_PID|LOG_LPR|LOG_ERR) to syslog, but
+ * syslog ignored the LOG_PID and LOG_PERROR option. I.e. the intention
+ * was to add both options but the effect was to have neither.
+ * I disagree with the intention to add PERROR. --Stef */
+ openlog (p, LOG_PID, LOG_LPR);
+ if (is_standalone())
+ server(lpnumber);
+ else
+ one_job(lpnumber);
+ return (0);
+}
diff --git a/gpxe/contrib/p910nd-0.8/p910nd.sh b/gpxe/contrib/p910nd-0.8/p910nd.sh
new file mode 100755
index 00000000..291a836a
--- /dev/null
+++ b/gpxe/contrib/p910nd-0.8/p910nd.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# p910nd.sh This shell script takes care of starting and stopping
+# p910nd (port 9100+n printer daemon)
+# This script only controls the one on port 9101.
+# You can start others if you wish.
+#
+
+# Todo: Make it fully LSB
+
+# See how we were called.
+case "$1" in
+ start)
+ # Start daemons.
+ echo -n "Starting p910nd: "
+ # default port is 1 so it will appear as p9101d on a ps
+ start_daemon p910nd
+ echo
+ ;;
+ stop)
+ # Stop daemons.
+ echo -n "Shutting down p910nd: "
+ killproc p9101d
+ echo
+ rm -f /var/run/p9101.pid
+ ;;
+ status)
+ status p9101d
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ echo "Usage: p910nd {start|stop|restart|status}"
+ exit 1
+esac
+
+exit 0
diff --git a/gpxe/contrib/ppmtoansi/Makefile b/gpxe/contrib/ppmtoansi/Makefile
new file mode 100644
index 00000000..bc0ca0a6
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/Makefile
@@ -0,0 +1,67 @@
+CPPFLAGS =
+LDLIBS =
+CFLAGS = -pipe -g -O2 -Wall
+LDFLAGS = -pipe
+CC = gcc
+LD = gcc
+# Some "black" magic to determine optimal compiler flags for target
+# architecture
+TARGET_ARCH:= $(shell if [ \! -r .compile-options ] ; then ( \
+ cpu=`grep cpu /proc/cpuinfo 2>&1 |head -1| \
+ cut -d : -f 2-| sed -e 's/ //g'`; \
+ if [ x"$$cpu" = x"" ] ; then \
+ echo -fno-strength-reduce; \
+ else if [ "$$cpu" = "386" ] ; then \
+ echo -m386 -fno-strength-reduce; \
+ else if [ "$$cpu" = "486" ] ; then \
+ echo -m486 -fno-strength-reduce; \
+ else if [ "$$cpu" = "Alpha" ] ; then \
+ echo -fno-strength-reduce; \
+ else echo main\(\)\{\} >.compile-options.c; \
+ if gcc -mpentium -o .compile-options.o -c \
+ .compile-options.c &>/dev/null; then \
+ echo -mpentium -fstrength-reduce; \
+ else if gcc -m486 -malign-functions=2 -malign-jumps=2 \
+ -malign-loops=2 -o .compile-options.o -c \
+ .compile-options.c &>/dev/null; then \
+ echo -n -m486 -malign-functions=2 -malign-jumps=2; \
+ echo ' '-malign-loops=2 -fno-strength-reduce; \
+ else echo -m486; \
+ fi;fi;fi;fi;fi;fi) > .compile-options; \
+ rm -f .compile-options.c .compile-options.o; \
+ fi; cat .compile-options)
+ASFLAGS = $(TARGET_ARCH)
+
+OBJS = ppmtoansi.o
+
+##############################################################################
+
+ifeq (.depend,$(wildcard .depend))
+all: ppmtoansi
+include .depend
+else
+all: depend
+ @$(MAKE) all
+endif
+
+##############################################################################
+
+ppmtoansi: $(OBJS)
+
+##############################################################################
+
+clean:
+ $(RM) *~ *.o *.dvi *.log *.aux *yacc.tab.[ch] *yacc.output *lex.[co] \
+ *.dat .depend .tmp_depend .compile-options*
+ strip ppmtoansi >&/dev/null || true
+
+distclean: clean
+ $(RM) -rf ppmtoansi
+
+##############################################################################
+
+depend:
+ for i in *.c;do $(CPP) $(CPPFLAGS) -MM $$i;done >.tmp_depend
+ mv .tmp_depend .depend
+
+##############################################################################
diff --git a/gpxe/contrib/ppmtoansi/demo/dos.ansi b/gpxe/contrib/ppmtoansi/demo/dos.ansi
new file mode 100644
index 00000000..2ce5cda4
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/dos.ansi
@@ -0,0 +1 @@
+[9;;1- [15;;1- [18;;5-Ûl[9;1;3-$€[13;1;3-$€[17;1;1-À[9;2;1- [12;2;1- [15;2;1- [17;2;1-À[9;3;1- [15;3;1- [18;3;5-Ûl[9;4;1- [15;4;1- [23;4;1-À[9;5;1- [15;5;1- [23;5;1-À[9;6;1- [15;6;1- [18;6;5-Ûl[8;8;4-I [14;8;4-m°[19;8;5-¶Ú[8;9;1-@[12;9;2-L[18;9;2-¬[8;10;1-@[11;10;1-`[13;10;1-@[18;10;1- [20;10;1-`[8;11;1-@[11;11;1-`[13;11;1-@[19;11;5-¶Ú[8;12;1-@[11;12;1-`[13;12;1-@[20;12;1-`[24;12;1- [8;13;1-@[12;13;2-L[18;13;2-l[24;13;1- [8;14;4-I [14;14;4-m°[19;14;5-¶Ú \ No newline at end of file
diff --git a/gpxe/contrib/ppmtoansi/demo/dos.ppm b/gpxe/contrib/ppmtoansi/demo/dos.ppm
new file mode 100644
index 00000000..6efde7fa
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/dos.ppm
@@ -0,0 +1,51 @@
+P3
+17 16
+65535
+0 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 0 65535 65535 0 0 0 0 0 0
+0 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 65535 0 0
+65535 0 0 65535 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 65535 0 0 0 0 0
+0 0 0 65535 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 0 65535 65535 0 0 0 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 65535 65535 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 65535 65535 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 0 65535 65535 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 0 0 0 65535 0 65535
+65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 0 0
+0 65535 0 0 0 0 0 0 0 0 0 0 0 65535 0 65535 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 0 65535 65535 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 65535 0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 0 65535 0 0 0
+65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 65535 0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 65535
+65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 0 0
+0 65535 0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 0 0 0 0 0 0 0 0 0 65535 0 65535
+0 65535 0 0 0 0 0 0 0 0 0 0 0 65535 0 65535 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 0 65535 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 0 65535
+0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 0 0 0 65535 0 65535
+65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/gpxe/contrib/ppmtoansi/demo/dos.xpm b/gpxe/contrib/ppmtoansi/demo/dos.xpm
new file mode 100644
index 00000000..471c4edf
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/dos.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"17 16 6 1",
+/* colors */
+"` c #000000",
+"a c #00FFFF",
+"b c #00FF00",
+"c c #FF00FF",
+"d c #FF0000",
+"e c #FFFF00",
+/* pixels */
+"`d`````d``aaaaa``",
+"`ddd`ddd`a```````",
+"`d``d``d`a```````",
+"`d`````d``aaaaa``",
+"`d`````d```````a`",
+"`d`````d```````a`",
+"`d`````d``aaaaa``",
+"`````````````````",
+"bbbb``eeee`ccccc`",
+"b```be````ce`````",
+"b``e`b````c`e````",
+"b``e`b`````ccccc`",
+"b``e`b``````e```c",
+"b```be````ee````c",
+"bbbb``eeee`ccccc`",
+"`````````````````"
+};
diff --git a/gpxe/contrib/ppmtoansi/demo/etherboot.ansi b/gpxe/contrib/ppmtoansi/demo/etherboot.ansi
new file mode 100644
index 00000000..7975f36c
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/etherboot.ansi
Binary files differ
diff --git a/gpxe/contrib/ppmtoansi/demo/etherboot.ppm b/gpxe/contrib/ppmtoansi/demo/etherboot.ppm
new file mode 100644
index 00000000..da5b2458
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/etherboot.ppm
@@ -0,0 +1,99 @@
+P3
+32 16
+65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0
+65535 0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0
+0 65535 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0
+0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0
+0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0
+0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0
+0 0 0 65535 0 0
+65535 0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0
+0 65535 0 0 0 0 0 65535 0 0 0 0 0 0 0 0 65535 0
+0 0 0 0 65535 0 0 0 0 0 0 0 0 65535 0 0 0 0
+0 0 0 0 0 0 0 65535 0 0 0 0 0 0 0 0 0 0
+0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0
+0 0 0 65535 0 0
+65535 0 0 0 0 0 0 0 0 0 65535 0 0 0 0 0 0 0
+0 65535 0 0 0 0 0 65535 0 0 0 0 0 0 0 0 65535 0
+0 0 0 0 65535 0 0 0 0 0 0 0 0 65535 0 0 0 0
+0 0 0 0 0 0 0 65535 0 0 0 0 0 0 0 0 0 0
+0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0
+0 0 0 65535 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0
+0 65535 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0
+0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0
+0 0 0 0 0 0 0 65535 0 0 0 0 0 0 0 0 0 0
+0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0
+0 0 0 0 0 0 65535 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0
+0 0 0 65535 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0
+0 65535 0 0 65535 0 0 0 0 0 65535 0 0 65535 0 0 65535 0
+0 65535 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0
+0 65535 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0
+65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0
+0 0 0 0 0 0 0 0 0 0 65535 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 0 0
+0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0
+65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0
+0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 0 0
+0 0 0 0 0 0 0 65535 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0
+65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0
+0 0 0 0 0 0 0 0 0 0 65535 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 0 0
+0 0 0 0 0 0 0 65535 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0 65535 65535
+0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0
diff --git a/gpxe/contrib/ppmtoansi/demo/etherboot.xpm b/gpxe/contrib/ppmtoansi/demo/etherboot.xpm
new file mode 100644
index 00000000..d4217146
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/etherboot.xpm
@@ -0,0 +1,27 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"32 16 4 1",
+/* colors */
+"` c #000000",
+"a c #00FFFF",
+"b c #00FF00",
+"c c #FF0000",
+/* pixels */
+"``a``a``a``a``a``a``a``a``a``a``",
+"``a``a``a``a``a``a``a``a``a``a``",
+"cccccccccccccccccccccccccccccccc",
+"c``bbbb`bbbb`bbbb`bbbbb`bbbb```c",
+"c``bbbb`b``b`b``b```b```bbbb```c",
+"c``b``b`b``b`b``b```b```b``````c",
+"`c`bbbb`bbbb`bbbb```b```b``````c",
+"``c````````````````````````````c",
+"`c`bbbbb`bbbb`bbbbb`bbbb```````c",
+"c````b```b``````b```bbbb```````c",
+"c````b```bbb````b```b``````````c",
+"c````b```b``````b```b``````````c",
+"cccccccccccccccccccccccccccccccc",
+"``a``a``a``a``a``a``a``a``a``a``",
+"``a``a``a``a``a``a``a``a``a``a``",
+"````````````````````````````````"
+};
diff --git a/gpxe/contrib/ppmtoansi/demo/flash.ansi b/gpxe/contrib/ppmtoansi/demo/flash.ansi
new file mode 100644
index 00000000..43837a78
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/flash.ansi
@@ -0,0 +1 @@
+[11;;2-l[11;1;2-l[17;1;1-`[11;2;2-l[16;2;2-l[11;3;2-l[15;3;3-m€[11;4;2-l[14;4;4-m°[11;5;2-l[14;5;4-m°[11;6;4-m°[16;6;2-l[11;7;4-m°[16;7;2-l[11;8;3-m€[16;8;2-l[11;9;2-l[16;9;2-l[11;10;1-`[16;10;2-l[16;11;2-l[13;12;2-l[16;12;2-l[19;12;2-l[14;13;6-m¶À[15;14;4-m°[16;15;2-l \ No newline at end of file
diff --git a/gpxe/contrib/ppmtoansi/demo/flash.ppm b/gpxe/contrib/ppmtoansi/demo/flash.ppm
new file mode 100644
index 00000000..1464af6e
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/flash.ppm
@@ -0,0 +1,35 @@
+P3
+10 16
+65535
+65535 65535 0 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 65535 65535 0 0 0 0 0 0 0 0 0 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 65535 65535 0 0 0 0 0 0 0 65535 65535 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 65535 65535 0 0 0 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 65535 65535 0 0 0 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 0 0 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 0 0 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 0 0 0 0 0 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 65535 65535 0 0 0 0 0 0 0 0 0 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
+65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 65535 65535 0 65535 65535 0 0 0 0 65535 65535 0
+65535 65535 0 0 0 0 65535 65535 0 65535 65535 0
+0 0 0 0 0 0 0 0 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0
diff --git a/gpxe/contrib/ppmtoansi/demo/flash.xpm b/gpxe/contrib/ppmtoansi/demo/flash.xpm
new file mode 100644
index 00000000..25419d70
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/flash.xpm
@@ -0,0 +1,25 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"10 16 2 1",
+/* colors */
+" c #000000",
+"x c #FFFF00",
+/* pixels */
+"xx ",
+"xx x ",
+"xx xx ",
+"xx xxx ",
+"xx xxxx ",
+"xx xxxx ",
+"xxxx xx ",
+"xxxx xx ",
+"xxx xx ",
+"xx xx ",
+"x xx ",
+" xx ",
+" xx xx xx",
+" xxxxxx ",
+" xxxx ",
+" xx ",
+};
diff --git a/gpxe/contrib/ppmtoansi/demo/floppy.ansi b/gpxe/contrib/ppmtoansi/demo/floppy.ansi
new file mode 100644
index 00000000..f4267f40
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/floppy.ansi
@@ -0,0 +1 @@
+[9;;14-%’Ûm¶@[8;1;16-$²[l’É[8;2;17-$²[l’É [8;3;17-$²[l’É [8;4;17-$¶Ûm¶É [8;5;17;1+[8;6;17;1+[8;7;17-$í¶Ûm‰ [8;8;17-$ëm¶Û‰ [8;9;17-$í¶Ûm‰ [8;10;17-$ëm¶Û‰ [8;11;17-$í¶Ûm‰ [8;12;17-$ëm¶Û‰ [8;13;17-2m¶Ûm¤ [8;14;17-2km¶Û¤ [9;15;15-'m¶ÛlH \ No newline at end of file
diff --git a/gpxe/contrib/ppmtoansi/demo/floppy.ppm b/gpxe/contrib/ppmtoansi/demo/floppy.ppm
new file mode 100644
index 00000000..2b0b2863
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/floppy.ppm
@@ -0,0 +1,51 @@
+P3
+17 16
+65535
+0 0 0 65535 0 0 65535 0 0 65535 65535 0 65535 0 0 65535 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 0 0 0 0 0 0 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 65535 0 65535 0 0 65535 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 0 65535 0 0
+65535 0 0 65535 65535 0 65535 0 0 65535 0 0 0 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 65535 0 65535 0 0 65535 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 0 65535 0 0
+65535 0 0 65535 65535 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 65535 0 65535 0 0 65535 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 0 65535 0 0
+65535 0 0 65535 65535 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 0 65535 65535 65535 0 65535 65535 0 65535
+65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535
+65535 0 65535 0 65535 65535 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 0 65535 65535 65535 0 65535 65535 0 65535
+65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535
+65535 0 65535 0 65535 65535 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 0 65535 65535 65535 0 65535 65535 0 65535
+65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535
+65535 0 65535 0 65535 65535 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 0 0 65535 0 0 65535 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 0 0 65535 0 0 65535 65535 0 0
+65535 0 0 0 0 65535 0 0 65535 0 65535 65535 65535 0 65535 65535 0 65535
+65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535
+65535 0 65535 0 65535 65535 0 0 65535 0 0 65535 65535 0 0
+0 0 0 65535 0 0 65535 0 0 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 65535 0 0 65535 0 0 0 0 0
diff --git a/gpxe/contrib/ppmtoansi/demo/floppy.xpm b/gpxe/contrib/ppmtoansi/demo/floppy.xpm
new file mode 100644
index 00000000..8bb60d29
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/floppy.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"17 16 6 1",
+/* colors */
+"` c #000000",
+"a c #00FFFF",
+"b c #FF00FF",
+"c c #FF0000",
+"d c #FFFF00",
+"e c #0000FF",
+/* pixels */
+"`ccdccddddddddc``",
+"cccdccddddcccdcc`",
+"cccdccddddcccdccc",
+"cccdccddddcccdccc",
+"cccdddddddddddccc",
+"ccccccccccccccccc",
+"ccccccccccccccccc",
+"cccaaaaaaaaaaaccc",
+"cccabbbbbbbbbaccc",
+"cccaaaaaaaaaaaccc",
+"cccabbbbbbbbbaccc",
+"cccaaaaaaaaaaaccc",
+"cccabbbbbbbbbaccc",
+"ceeaaaaaaaaaaaeec",
+"ceeabbbbbbbbbaeec",
+"`ccaaaaaaaaaaacc`"
+};
diff --git a/gpxe/contrib/ppmtoansi/demo/hd.ansi b/gpxe/contrib/ppmtoansi/demo/hd.ansi
new file mode 100644
index 00000000..33bda53c
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/hd.ansi
Binary files differ
diff --git a/gpxe/contrib/ppmtoansi/demo/hd.ppm b/gpxe/contrib/ppmtoansi/demo/hd.ppm
new file mode 100644
index 00000000..34878bf9
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/hd.ppm
@@ -0,0 +1,51 @@
+P3
+17 16
+65535
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0
+0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0
+65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0 0
+65535 0 0 0 65535 0 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535
+0 65535 0 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 65535 0
+65535 0 0 65535 65535 0 65535 65535 0 65535 0 0 0 0 0
+65535 0 0 0 65535 0 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535
+0 65535 0 65535 0 65535 0 65535 0 0 65535 0 0 65535 0 0 65535 0
+65535 0 0 65535 65535 0 65535 65535 0 65535 0 0 0 0 0
+65535 0 0 0 65535 0 65535 0 65535 0 65535 0 0 65535 0 0 65535 0
+0 65535 0 65535 0 65535 0 65535 0 0 65535 0 0 65535 0 0 65535 0
+65535 0 0 65535 65535 0 65535 65535 0 65535 0 0 0 0 0
+65535 0 0 0 65535 0 65535 0 65535 0 65535 0 0 65535 0 0 65535 0
+0 65535 0 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 65535 0
+65535 0 0 65535 65535 0 65535 0 0 0 0 0 0 0 0
+65535 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0
+0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0
+65535 0 0 65535 0 0 0 0 65535 0 0 65535 0 0 65535
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 0 65535 65535 0 65535 65535 0 0 65535 0 0 65535
+0 0 0 0 0 65535 0 65535 65535 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 65535 65535
+0 65535 65535 0 65535 65535 0 0 65535 0 65535 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 65535 65535 0 65535 65535 0 0 65535
+0 0 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 0 65535 0 65535 65535 0 65535 65535 0 0 65535
+0 0 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 65535 65535 0 0 65535 0 65535 65535 0 0 65535 0 0 0
+0 0 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535
+0 65535 65535 0 0 65535 0 0 65535 0 0 0 0 0 0
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/gpxe/contrib/ppmtoansi/demo/hd.xpm b/gpxe/contrib/ppmtoansi/demo/hd.xpm
new file mode 100644
index 00000000..280c7abc
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/hd.xpm
@@ -0,0 +1,30 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"17 16 7 1",
+/* colors */
+"` c #000000",
+"a c #00FFFF",
+"b c #00FF00",
+"c c #FF00FF",
+"d c #FF0000",
+"e c #FFFF00",
+"f c #0000FF",
+/* pixels */
+"`````````````````",
+"ddddddddddddd````",
+"dbbbbbbbbbbbddd``",
+"dbccccbccccbdeed`",
+"dbccccbcbbbbdeed`",
+"dbcbbbbcbbbbdeed`",
+"dbcbbbbccccbded``",
+"dbbbbbbbbbbbddfff",
+"dddddddddddddaaff",
+"`faddddddddaaafaf",
+"ffffffffffffffaaf",
+"faaaaaaaaaaaafaaf",
+"faaaaaafffffafaf`",
+"faaaaaaaaaaaaff``",
+"ffffffffffffff```",
+"`````````````````"
+};
diff --git a/gpxe/contrib/ppmtoansi/demo/ibmmap.ppm b/gpxe/contrib/ppmtoansi/demo/ibmmap.ppm
new file mode 100644
index 00000000..648676e4
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/ibmmap.ppm
@@ -0,0 +1,11 @@
+P3
+8 1
+255
+ 0 0 0
+255 0 0
+ 0 255 0
+ 0 0 255
+255 255 0
+255 0 255
+ 0 255 255
+255 255 255
diff --git a/gpxe/contrib/ppmtoansi/demo/ibmmap.xpm b/gpxe/contrib/ppmtoansi/demo/ibmmap.xpm
new file mode 100644
index 00000000..06d4d990
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/ibmmap.xpm
@@ -0,0 +1,16 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"8 1 8 1",
+/* colors */
+"` c #000000",
+"a c #00FFFF",
+"b c #00FF00",
+"c c #FF00FF",
+"d c #FF0000",
+"e c #FFFFFF",
+"f c #FFFF00",
+"g c #0000FF",
+/* pixels */
+"`dbgfcae"
+};
diff --git a/gpxe/contrib/ppmtoansi/demo/linux-logo.ansi b/gpxe/contrib/ppmtoansi/demo/linux-logo.ansi
new file mode 100644
index 00000000..165f834b
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/linux-logo.ansi
Binary files differ
diff --git a/gpxe/contrib/ppmtoansi/demo/linux-logo.ppm b/gpxe/contrib/ppmtoansi/demo/linux-logo.ppm
new file mode 100644
index 00000000..18604b44
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/linux-logo.ppm
@@ -0,0 +1,552 @@
+P3
+52 61
+65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 0 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+65535 65535 65535 0 0 0 0 0 0 65535 65535 65535 0 0 0 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 0 0 0 0 0 0 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 65535 65535 65535 0 0 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 0 0 0 0 0 65535 65535 65535
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 0 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 65535 65535 65535 65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 0 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 0 0 65535 65535 0 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 65535 0 0
+65535 65535 0 65535 65535 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0
+0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 0 0 0 0 65535 65535 0 0 0 0 0 0 0
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 65535 65535 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 65535 65535 0
+65535 65535 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 65535 65535 0 65535 65535 0 65535 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 0 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 65535 65535 0
+65535 65535 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 0 0 0 0 0 0 0 0 0 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 0 0 65535 0 0 0 0 0 65535 0 0
+65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 0
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 0 0 0 0 0 0
+0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 0 0 0 0 65535 0 0 65535
+0 0 65535 65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 0 0 0
+0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 0 0 0 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 0 0 65535
+0 0 65535 65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 0 0 0 0 0 0 65535 0 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 65535 0 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 0 0 65535
+65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 0 0 0 0 65535 0 0 65535
+0 0 65535 65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 0 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 65535 0 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 65535 0 0 65535 0 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 0 0 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 65535 0 65535 65535 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 0 0
+0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 65535 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 65535 0 0
+65535 0 0 65535 0 0 65535 65535 0 65535 0 0 65535 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 65535 0 0 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 65535
diff --git a/gpxe/contrib/ppmtoansi/demo/linux-logo.xpm b/gpxe/contrib/ppmtoansi/demo/linux-logo.xpm
new file mode 100644
index 00000000..fd2dd671
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/linux-logo.xpm
@@ -0,0 +1,73 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"52 61 5 1",
+/* colors */
+"` c #000000",
+"a c #FF0000",
+"b c #FFFFFF",
+"c c #FFFF00",
+"d c #0000FF",
+/* pixels */
+"dddddddddddddddddddddd```````ddddddddddddddddddddddd",
+"dddddddddddddddddddd```````````ddddddddddddddddddddd",
+"ddddddddddddddddddd`````````````dddddddddddddddddddd",
+"dddddddddddddddddd```````````````ddddddddddddddddddd",
+"ddddddddddddddddd`````````````````dddddddddddddddddd",
+"ddddddddddddddddd`````````````````dddddddddddddddddd",
+"ddddddddddddddddd``````````````````ddddddddddddddddd",
+"ddddddddddddddddd``````````````````ddddddddddddddddd",
+"ddddddddddddddddd``````````````````ddddddddddddddddd",
+"ddddddddddddddddd``bb`````bbbb`````ddddddddddddddddd",
+"ddddddddddddddddd``bbb```bbbbb`````ddddddddddddddddd",
+"ddddddddddddddddd`b``b```bb``bb````ddddddddddddddddd",
+"ddddddddddddddddd`b``````b````b````ddddddddddddddddd",
+"ddddddddddddddddd`````cccc````b````ddddddddddddddddd",
+"ddddddddddddddddd``b`cccccca`b``````dddddddddddddddd",
+"ddddddddddddddddd``acccccccccc``````dddddddddddddddd",
+"ddddddddddddddddd``cccccccccccc`````dddddddddddddddd",
+"ddddddddddddddddd``ccccccccccca`````dddddddddddddddd",
+"ddddddddddddddddd```ccccccaccc``````dddddddddddddddd",
+"ddddddddddddddddd``bacccaccccbb``````ddddddddddddddd",
+"ddddddddddddddddd``bbacccacbbbb```````dddddddddddddd",
+"ddddddddddddddddd``bbbaaccbbbbbb``````dddddddddddddd",
+"dddddddddddddddd```bbbbbbbbbbbbb```````ddddddddddddd",
+"ddddddddddddddd```bbbbbbbbbbbbbbb``````ddddddddddddd",
+"dddddddddddddd```bbbbbbbbbbbbbbbb```````dddddddddddd",
+"dddddddddddddd```bbbbbbbbbbbbbbbbb```````ddddddddddd",
+"ddddddddddddd```bbbbbbbbbbbbbbbbbb````````dddddddddd",
+"dddddddddddd````bbbbbbbbbbbbbbbbbb````````dddddddddd",
+"dddddddddddd````bbbbbbbbbbbbbbbbbbb````````ddddddddd",
+"ddddddddddd````bbbbbbbbbbbbbbbbbbbb`````````dddddddd",
+"ddddddddddd````bbbbbbbbbbbbbbbbbbbbb````````dddddddd",
+"ddddddddddd```bbbbbbbbbbbbbbbbbbbbbb````````dddddddd",
+"dddddddddd````bbbbbbbbbbbbbbbbbbbbbbb````````ddddddd",
+"dddddddddd```bbbbbbbbbbbbbbbbbbbbbbbb````````ddddddd",
+"ddddddddd````bbbbbbbbbbbbbbbbbbbbbbbb````````ddddddd",
+"ddddddddd````bbbbbbbbbbbbbbbbbbbbbbbb`````````dddddd",
+"ddddddddd```bbbbbbbbbbbbbbbbbbbbbbbbb`````````dddddd",
+"dddddddd````bbbbbbbbbbbbbbbbbbbbbbbbb`````````dddddd",
+"ddddddd`````bbbbbbbbbbbbbbbbbbbbbbbbb`````````dddddd",
+"ddddddd`````bbbbbbbbbbbbbbbbbbbbbbbbb`````````dddddd",
+"ddddddd`````bbbbbbbbbbbbbbbbbbbbbbbbb`````````dddddd",
+"ddddddd``c``bbbbbbbbbbbbbbbbbbbbbbbbb`````````dddddd",
+"dddddddcccc``bbbbbbbbbbbbbbbbbbbbbbcc`````````dddddd",
+"dddddddccccc``bbbbbbbbbbbbbbbbbbbbcccc```````cdddddd",
+"ddddddccccccc``bbbbbbbbbbbbbbbbbbbccca``````ccaddddd",
+"dddddcccccccca``bbbbbbbbbbbbbbbbbbccca`````cccaddddd",
+"dacccccccccccc```bbbbbbbbbbbbbbbbbccccaa`aaccccddddd",
+"acccccccccccccc````bbbbbbbbbbbbbbbcccccccccccccadddd",
+"acccccccccccccc````bbbbbbbbbbbbbbbccccccccccccccdddd",
+"accccccccccccccc````bbbbbbbbbbbbbbaccccccccccccccadd",
+"daccccccccccccccc``bbbbbbbbbbbbbb``ccccccccccccccccd",
+"daccccccccccccccccbbbbbbbbbbbbbb``accccccccccccccccc",
+"acccccccccccccccccbbbbbbbbbbbbb```accccccccccccccccd",
+"acccccccccccccccccabbbbbbbbbb`````accccccccccccccadd",
+"dacccccccccccccccca``bbbb`````````acccccccccccccdddd",
+"dacccccccccccccccca```````````````acccccccccccaddddd",
+"ddaacccccccccccccca```````````````acccccccccaddddddd",
+"ddddddaaaacccccccaddd```````````dddaccccccaddddddddd",
+"ddddddddddaaaaaaaddddddddddddddddddaaacaaddddddddddd",
+"ddddddddddddddadddddddddddddddddddddaaaadddddddddddd",
+"dddddddddddddddddddddddddddddddddddddddddddddddddddd"
+};
diff --git a/gpxe/contrib/ppmtoansi/demo/make-ansi.sh b/gpxe/contrib/ppmtoansi/demo/make-ansi.sh
new file mode 100755
index 00000000..16d7b98d
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/make-ansi.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+xpmtoppm <linux-logo.xpm >linux-logo.ppm
+../ppmtoansi -b 0/0/255 -y 10 -t 0/0/0:4 linux-logo.ppm >linux-logo.ansi
+xpmtoppm <etherboot.xpm >etherboot.ppm
+../ppmtoansi -b 0/0/0 etherboot.ppm >etherboot.ansi
+xpmtoppm <text.xpm >text.ppm
+../ppmtoansi -b 0/0/0 -x 10 text.ppm >text.ansi
+xpmtoppm <x.xpm >x.ppm
+../ppmtoansi -b 0/0/0 -x 8 x.ppm >x.ansi
+xpmtoppm <dos.xpm >dos.ppm
+../ppmtoansi -b 0/0/0 -x 8 dos.ppm >dos.ansi
+xpmtoppm <hd.xpm >hd.ppm
+../ppmtoansi -b 0/0/0 -x 8 hd.ppm >hd.ansi
+xpmtoppm <floppy.xpm >floppy.ppm
+../ppmtoansi -b 0/0/0 -x 8 floppy.ppm >floppy.ansi
+xpmtoppm <flash.xpm >flash.ppm
+../ppmtoansi -b 0/0/0 -x 11 flash.ppm >flash.ansi
diff --git a/gpxe/contrib/ppmtoansi/demo/text.ansi b/gpxe/contrib/ppmtoansi/demo/text.ansi
new file mode 100644
index 00000000..bbe7998a
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/text.ansi
Binary files differ
diff --git a/gpxe/contrib/ppmtoansi/demo/text.ppm b/gpxe/contrib/ppmtoansi/demo/text.ppm
new file mode 100644
index 00000000..c6f87610
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/text.ppm
@@ -0,0 +1,51 @@
+P3
+13 16
+65535
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535
+0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535
+0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0
+0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 65535 65535
+0 0 65535 65535 65535 65535 0 0 65535 0 0 0 0 0 0 0 0 0
+0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 65535 0
+65535 65535 0 0 0 65535 0 0 65535 0 0 0 0 0 0 0 0 0
+0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 65535 0
+65535 65535 0 65535 0 0 0 0 65535 0 0 0 0 0 0 0 0 0
+0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 65535 65535
+65535 65535 65535 65535 65535 65535 0 0 65535 0 0 65535 0 0 0 0 0 0
+0 0 0
+0 0 0 0 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 0 0 65535 0 0 65535 0 0 0 0 0 0
+0 0 0
+0 0 0 0 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0
+0 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0
+0 0 0 0 0 0 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 65535 0 0 65535 0 0 65535
+0 0 0
+0 0 0 0 0 0 65535 0 0 65535 65535 65535 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 65535 0 0 65535 0 0 0
+0 0 0
+0 0 0 65535 65535 0 65535 65535 0 65535 0 0 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 65535 65535 0 0 0 0 65535 65535 65535 0
+0 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 65535 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+65535 0 0
+65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 65535
+65535 65535 65535 65535 65535 65535 65535 65535 0 65535 65535 0 65535 65535 0 65535 65535 0
+0 0 0
+0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 0 0 0
+0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0
diff --git a/gpxe/contrib/ppmtoansi/demo/text.xpm b/gpxe/contrib/ppmtoansi/demo/text.xpm
new file mode 100644
index 00000000..5261b962
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/text.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"13 16 5 1",
+/* colors */
+"` c #000000",
+"a c #FF0000",
+"b c #FFFFFF",
+"c c #FFFF00",
+"d c #0000FF",
+/* pixels */
+"`````ddd`````",
+"````ddddd````",
+"````dbdbd````",
+"````dccdd````",
+"````dccad````",
+"`````bbbdd```",
+"```dbbbbdd```",
+"```dbbbbbdd``",
+"``dbbbbbbdd``",
+"``dbbbbbbddd`",
+"``abbbbbbdd``",
+"`ccabbbbbadc`",
+"ccccbbbbcccca",
+"cccccbbbcccc`",
+"```aa````aa``",
+"`````````````"
+};
diff --git a/gpxe/contrib/ppmtoansi/demo/x.ansi b/gpxe/contrib/ppmtoansi/demo/x.ansi
new file mode 100644
index 00000000..e2844b34
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/x.ansi
@@ -0,0 +1 @@
+[8;1;4-$[23;1;1- [9;2;4-$[22;2;1- [10;3;4-$[21;3;1- [11;4;4-$[20;4;1- [12;5;4-$[19;5;1- [13;6;4-$[18;6;1- [14;7;2-$[17;7;2-$[14;8;1- [16;8;4-$[13;9;1- [17;9;4-$[12;10;1- [18;10;4-$[11;11;1- [19;11;4-$[10;12;1- [20;12;4-$[9;13;1- [21;13;4-$ \ No newline at end of file
diff --git a/gpxe/contrib/ppmtoansi/demo/x.ppm b/gpxe/contrib/ppmtoansi/demo/x.ppm
new file mode 100644
index 00000000..53e5bcfc
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/x.ppm
@@ -0,0 +1,51 @@
+P3
+17 16
+65535
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 65535 0 0 0 0 0
+0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 65535 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 65535 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 0 0 0 65535 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 0 0 65535 0 0 0 0 0 65535 0 0 65535 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0
+0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0 65535 0 0
+65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0 65535 0 0
+65535 0 0 65535 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 65535 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65535 0 0
+65535 0 0 65535 0 0 65535 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 65535 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+65535 0 0 65535 0 0 65535 0 0 65535 0 0 0 0 0
+0 0 0 65535 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 65535 0 0 65535 0 0 65535 0 0 65535 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/gpxe/contrib/ppmtoansi/demo/x.xpm b/gpxe/contrib/ppmtoansi/demo/x.xpm
new file mode 100644
index 00000000..d0695270
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/demo/x.xpm
@@ -0,0 +1,25 @@
+/* XPM */
+static char *noname[] = {
+/* width height ncolors chars_per_pixel */
+"17 16 2 1",
+/* colors */
+"` c #000000",
+"a c #FF0000",
+/* pixels */
+"`````````````````",
+"aaaa```````````a`",
+"`aaaa`````````a``",
+"``aaaa```````a```",
+"```aaaa`````a````",
+"````aaaa```a`````",
+"`````aaaa`a``````",
+"``````aa`aa``````",
+"``````a`aaaa`````",
+"`````a```aaaa````",
+"````a`````aaaa```",
+"```a```````aaaa``",
+"``a`````````aaaa`",
+"`a```````````aaaa",
+"`````````````````",
+"`````````````````"
+};
diff --git a/gpxe/contrib/ppmtoansi/ppmtoansi.c b/gpxe/contrib/ppmtoansi/ppmtoansi.c
new file mode 100644
index 00000000..71d95cba
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/ppmtoansi.c
@@ -0,0 +1,235 @@
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static int palette[8][3] = {
+/* black, red, green, yellow, */
+ { 0, 0, 0},{255, 0, 0},{ 0,255, 0},{255,255, 0},
+ { 0, 0,255},{255, 0,255},{ 0,255,255},{255,255,255}};
+/* blue, magenta, cyan, white */
+
+static struct trans {
+ struct trans *next;
+ int idx,r,g,b;
+} *trans = NULL;
+
+static int skipcomment(FILE *fp)
+{
+ int ch;
+
+ for (;;) {
+ ch = getc(fp);
+ if (ch != '#')
+ return(ch);
+ while (ch != '\n' && ch != EOF)
+ ch = getc(fp); }
+}
+
+static int readentry(FILE *fp,int format,int depth)
+{
+ int ch,i = 0;
+
+ if (format == '3') {
+ while ((ch = getc(fp)) == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
+ if (ch < '0' || ch > '9') {
+ error:
+ fprintf(stderr,"Format error in input file\n");
+ exit(1); }
+ for (; ch >= '0' && ch <= '9'; ch = getc(fp))
+ i = 10*i + ch - '0'; }
+ else {
+ if ((i = getc(fp)) > depth || i < 0)
+ goto error; }
+ return((i*256)/(depth+1));
+}
+
+static void packpixel(char *data,int c)
+{
+ int i = 0, n = 0;
+
+ while (c--) {
+ i = (i << 3) | (*data++ & 0x7);
+ if ((n += 3) >= 8)
+ putchar((i >> (n -= 8)) & 0xFF); }
+ if (n)
+ putchar(i << (8 - n));
+ return;
+}
+
+static int dg(int i)
+{
+ int d;
+
+ for (d = 0; i; d++, i /= 10);
+ return(d);
+}
+
+static char *i2s(char *buf,int i)
+{
+/* if (!i)
+ *buf = '\000';
+ else*/
+ sprintf(buf,"%d",i);
+ return(buf);
+}
+
+static void flushdata(int x,int y,int c,char *data)
+{
+ char b1[10],b2[10],b3[10],b4[10];
+ int i,j,rle,v;
+
+ for (i = j = v = 0; i < c; ) {
+ for (rle = 0; i+rle < c && data[i] == data[i+rle]; rle++);
+ if (rle > (i != j ? (v ? 4 : 6) : 0) +
+ ((v || (i != j)) ? 4+dg(rle)+dg(data[i])
+ : 6+dg(x+i)+dg(y)+dg(rle)+dg(data[i]))) {
+ if (i != j) {
+ if (v)
+ printf("[%s-",i2s(b1,i-j));
+ else
+ printf("[%s;%s;%s-",i2s(b1,x+j),i2s(b2,y),i2s(b3,i-j));
+ packpixel(data+j,i-j); }
+ if (v++ || (i != j))
+ printf("[%s;%s+",i2s(b1,rle),i2s(b2,data[i]));
+ else
+ printf("[%s;%s;%s;%s+",i2s(b1,x+i),i2s(b2,y),
+ i2s(b3,rle),i2s(b4,data[i]));
+ j = i += rle; }
+ else
+ i++; }
+ if (j != c) {
+ if (v)
+ printf("[%s-",i2s(b1,c-j));
+ else
+ printf("[%s;%s;%s-",i2s(b1,x+j),i2s(b2,y),i2s(b3,c-j));
+ packpixel(data+j,c-j); }
+ return;
+}
+
+int main(int argc,char *argv[])
+{
+ extern int optind;
+ extern char *optarg;
+ FILE *infile = NULL;
+ int ch,i,j,dist,idx;
+ int format,width,height,depth;
+ int bg = 0,bgred = 0,bggreen = 0,bgblue = 0;
+ int xoffset = 0,yoffset = 0;
+ int w,h,r,g,b,c;
+ struct trans *tp;
+ char *buf;
+
+ while ((i = getopt(argc,argv,"b:t:x:y:")) >= 0) switch(i) {
+ case 'b':
+ bg++;
+ for (i = bgred = 0; optarg[i] >= '0' && optarg[i] <= '9';
+ bgred = 10*bgred + optarg[i++] - '0');
+ if (optarg[i++] != '/')
+ goto usage;
+ for (bggreen = 0; optarg[i] >= '0' && optarg[i] <= '9';
+ bggreen = 10*bggreen + optarg[i++] - '0');
+ if (optarg[i++] != '/')
+ goto usage;
+ for (bgblue = 0; optarg[i] >= '0' && optarg[i] <= '9';
+ bgblue = 10*bgblue + optarg[i++] - '0');
+ if (optarg[i])
+ goto usage;
+ break;
+ case 't':
+ if ((tp = malloc(sizeof(struct trans))) == NULL)
+ goto usage;
+ for (i = tp->r = 0; optarg[i] >= '0' && optarg[i] <= '9';
+ tp->r = 10*tp->r + optarg[i++] - '0');
+ if (optarg[i++] != '/')
+ goto usage;
+ for (tp->g = 0; optarg[i] >= '0' && optarg[i] <= '9';
+ tp->g = 10*tp->g + optarg[i++] - '0');
+ if (optarg[i++] != '/')
+ goto usage;
+ for (tp->b = 0; optarg[i] >= '0' && optarg[i] <= '9';
+ tp->b = 10*tp->b + optarg[i++] - '0');
+ if (optarg[i++] != ':')
+ goto usage;
+ if (optarg[i] == '-') {
+ j = -1; i++; }
+ else j = 1;
+ for (tp->idx = 0; optarg[i] >= '0' && optarg[i] <= '9';
+ tp->idx = 10*tp->idx + optarg[i++] - '0');
+ tp->idx *= j;
+ if (tp->idx < -1 || tp->idx >= 8)
+ goto usage;
+ if (optarg[i])
+ goto usage;
+ tp->next = trans;
+ trans = tp;
+ break;
+ case 'x':
+ for (i = xoffset = 0; optarg[i] >= '0' && optarg[i] <= '9';
+ xoffset = 10*xoffset + optarg[i++] - '0');
+ if (optarg[i])
+ goto usage;
+ break;
+ case 'y':
+ for (i = yoffset = 0; optarg[i] >= '0' && optarg[i] <= '9';
+ yoffset = 10*yoffset + optarg[i++] - '0');
+ if (optarg[i])
+ goto usage;
+ break;
+ default:
+ usage:
+ fprintf(stderr,"Usage: %s [-b r/g/b] [-t r/g/b:idx] "
+ "[-x offset] [-y offset] [ppmfile]\n",argv[0]);
+ exit(1); }
+ if (argc-optind == 0)
+ infile = stdin;
+ else if (argc-optind == 1)
+ infile = fopen(argv[optind],"r");
+ if (!infile)
+ goto usage;
+ if ((ch = skipcomment(infile)) != 'P' ||
+ ((format = getc(infile)) != '3' && format != '6') ||
+ ((ch = getc(infile)) != '\n' && ch != '\r' && getc(infile) != '\n'))
+ goto usage;
+ for (width = 0; (ch = skipcomment(infile)) >= '0' && ch <= '9';
+ width = 10*width + ch - '0');
+ while (ch == ' ') ch = getc(infile);
+ for (height = 0; ch >= '0' && ch <= '9'; ch = getc(infile))
+ height = 10*height + ch - '0';
+ if (ch != '\n' && ch != '\r' && getc(infile) != '\n')
+ goto usage;
+ for (depth = 0; (ch = skipcomment(infile)) >= '0' && ch <= '9';
+ depth = 10*depth + ch - '0');
+ if (ch != '\n' && ch != '\r' && getc(infile) != '\n')
+ goto usage;
+ if (!width || !height || !depth /* || depth > 255 */)
+ goto usage;
+ if ((buf = malloc(width)) == NULL)
+ goto usage;
+ for (h = 0; h < height; h++) {
+ for (w = c = 0; w < width; w++) {
+ r = readentry(infile,format,depth);
+ g = readentry(infile,format,depth);
+ b = readentry(infile,format,depth);
+ idx = 255;
+ if (bg && bgred == r &&
+ bggreen == g && bgblue == b)
+ idx = -1;
+ else for (tp = trans; tp; tp = tp->next)
+ if (tp->r == r && tp->g == g && tp->b == b) {
+ idx = tp->idx;
+ break; }
+ if (idx == 255)
+ for (idx = -1, dist = 3*255*255, i = 8; i--;)
+ if ((j = (r-palette[i][0])*(r-palette[i][0]) +
+ (g-palette[i][1])*(g-palette[i][1]) +
+ (b-palette[i][2])*(b-palette[i][2])) < dist) {
+ dist = j; idx = i; }
+ if (idx >= 0)
+ buf[c++] = idx;
+ else if (c) {
+ flushdata(w-c+xoffset,h+yoffset,c,buf);
+ c = 0; } }
+ if (c)
+ flushdata(w-c+xoffset,h+yoffset,c,buf); }
+ exit(0);
+}
diff --git a/gpxe/contrib/ppmtoansi/ppmtoansi.man b/gpxe/contrib/ppmtoansi/ppmtoansi.man
new file mode 100644
index 00000000..b19f9795
--- /dev/null
+++ b/gpxe/contrib/ppmtoansi/ppmtoansi.man
@@ -0,0 +1,120 @@
+.TH PPMTOANSI 1 "January 12, 1997"
+.SH NAME
+ppmtoansi \- Graphics Conversion Program For Extended ANSI Escape Codes
+.SH SYNOPSIS
+.B ppmtoansi
+[
+.SM \-b
+r/g/b ] [
+.SM \-t
+r/g/b:idx] [
+.SM \-x
+<xoffset> ] [
+.SM \-y
+<yoffset> ] [
+<filename> ]
+.SH DESCRIPTION
+.I ppmtoansi
+converts input files in
+.IR ppm (5)
+format to escape sequences that are understood by
+.IR etherboot .
+This allows for displaying icons and logos on the BOOT-Prom's splash
+screen.
+.PP
+If no input file is specified, data is read from
+.IR standard
+.IR input .
+All output is send to
+.IR standard
+.IR output ,
+error messages go to
+.IR standard
+.IR error .
+.I ppmtoansi
+tries to do some compression and minimizes the output filesize.
+.SH OPTIONS
+.TP
+.B \-b
+Specifies the
+.IR red ,
+.IR green , and
+.IR blue
+intensities for the background color. This information is
+used for providing a shape mask. All values are in the range from 0 to
+255.
+.TP
+.B \-t
+Allows for explicitly providing a translation between color values and
+color indices. The indices are in the range 0 to 7 and conform to the
+standard
+.IR ANSI
+.IR colorcodes .
+.RS
+.PP
+0 black 4 blue
+.PP
+1 red 5 magenta
+.PP
+2 green 6 cyan
+.PP
+3 yellow 7 white
+.PP
+If no translations apply,
+.IR ppmtoansi
+will automatically select the color that is closest.
+.RE
+.TP
+.B \-x
+Specify an X offset relative to the current text cursor position.
+.TP
+.B \-y
+Specify an Y offset relative to the current text cursor position.
+.SH FILE FORMAT
+.IR ppmtoansi
+uses these extensions to the standard
+.B ANSI escape
+.BR codes .
+.TP
+.B <esc>[a;b;c;d+<data>
+Draw pixel data. Use one byte per pixel. Colors are encoded as shown
+above. In text mode, graphics is approximated by outputting suitable
+characters. Parameters differ depending on the number of parameters
+passed:
+.RS
+.TP
+.B cnt
+.IR cnt
+data bytes follow. They will be drawn to the right of the last
+graphics position.
+.TP
+.B rle;col
+The next
+.IR rle
+pixels have the value
+.IR col .
+They will be drawn to the right of the last graphics position. No data
+bytes follow.
+.TP
+.B x;y;cnt
+.IR cnt
+data bytes follow. They will be drawn relative to the top left corner
+of the text cursor with an offset of
+.RI ( x / y ).
+.TP
+.B x;y;rle;col
+the next
+.IR rle
+pixels have the value
+.IR col .
+They will be drawn relative to the top left corner of the text cursor
+with an offset of
+.RI ( x / y ).
+No data bytes follow.
+.RE
+.TP
+.B <esc>[a;b;c;d\-<data>
+same as above, but pack pixels into three bits. The first pixel is
+stored in the three most significant bits of the first data byte.
+.SH "SEE ALSO"
+ppm(5), mknbi-linux(8), mknbi-dos(8).
diff --git a/gpxe/contrib/rom-scan/Makefile b/gpxe/contrib/rom-scan/Makefile
new file mode 100644
index 00000000..637404ad
--- /dev/null
+++ b/gpxe/contrib/rom-scan/Makefile
@@ -0,0 +1,64 @@
+CPPFLAGS =
+LDLIBS =
+CFLAGS = -pipe -g -O2 -Wall
+LDFLAGS = -pipe
+CC = gcc
+LD = gcc
+# Some "black" magic to determine optimal compiler flags for target
+# architecture
+TARGET_ARCH:= $(shell if [ \! -r .compile-options ] ; then ( \
+ cpu=`grep cpu /proc/cpuinfo 2>&1 |head -1| \
+ cut -d : -f 2-| sed -e 's/ //g'`; \
+ if [ x"$$cpu" = x"" ] ; then \
+ echo -fno-strength-reduce; \
+ else if [ "$$cpu" = "386" ] ; then \
+ echo -m386 -fno-strength-reduce; \
+ else if [ "$$cpu" = "486" ] ; then \
+ echo -m486 -fno-strength-reduce; \
+ else if [ "$$cpu" = "Alpha" ] ; then \
+ echo -fno-strength-reduce; \
+ else echo main\(\)\{\} >.compile-options.c; \
+ if gcc -mpentium -o .compile-options.o -c \
+ .compile-options.c &>/dev/null; then \
+ echo -mpentium -fstrength-reduce; \
+ else if gcc -m486 -malign-functions=2 -malign-jumps=2 \
+ -malign-loops=2 -o .compile-options.o -c \
+ .compile-options.c &>/dev/null; then \
+ echo -n -m486 -malign-functions=2 -malign-jumps=2; \
+ echo ' '-malign-loops=2 -fno-strength-reduce; \
+ else echo -m486; \
+ fi;fi;fi;fi;fi;fi) > .compile-options; \
+ rm -f .compile-options.c .compile-options.o; \
+ fi; cat .compile-options)
+ASFLAGS = $(TARGET_ARCH)
+
+OBJS = rom-scan.o
+
+##############################################################################
+
+ifeq (.depend,$(wildcard .depend))
+all: rom-scan
+include .depend
+else
+all: depend
+ @$(MAKE) all
+endif
+
+##############################################################################
+
+rom-scan: $(OBJS)
+
+##############################################################################
+
+clean:
+ $(RM) *~ *.o *.dvi *.log *.aux *yacc.tab.[ch] *yacc.output *lex.[co] \
+ *.dat .depend .tmp_depend .compile-options*
+ strip rom-scan >&/dev/null || true
+
+##############################################################################
+
+depend:
+ for i in *.c;do $(CPP) $(CPPFLAGS) -MM $$i;done >.tmp_depend
+ mv .tmp_depend .depend
+
+
diff --git a/gpxe/contrib/rom-scan/rom-scan.c b/gpxe/contrib/rom-scan/rom-scan.c
new file mode 100644
index 00000000..c5e48299
--- /dev/null
+++ b/gpxe/contrib/rom-scan/rom-scan.c
@@ -0,0 +1,115 @@
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#ifndef __TURBOC__
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#ifdef __TURBOC__
+#define HUGE huge
+#else
+#define HUGE
+#endif
+
+#define ROMSTART 0xC8000
+#define ROMEND 0xE8000
+#define ROMINCREMENT 0x00800
+#define ROMMASK 0x03FFF
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)(long long)-1)
+#endif
+
+typedef struct Images {
+ struct Images *next;
+ long start;
+ long size;
+} Images;
+
+static void rom_scan(const unsigned char HUGE *rom,long offset,long len)
+{
+ static Images *images = NULL;
+ Images *ptr;
+/* The assignments to dummy are to overcome a bug in TurboC */
+ long dummy, size;
+ int chksum;
+ long i;
+
+ if (rom[offset] != 0x55 || rom[dummy = offset+1] != 0xAA)
+ return;
+ size = (long)rom[dummy = offset+2]*512L;
+ printf("Found ROM header at %04lX:0000; "
+ "announces %ldk image (27C%02d EPROM)\n",
+ offset/16,(size+512)/1024,
+ size <= 1024 ? 8 :
+ size <= 2048 ? 16 :
+ size <= 4096 ? 32 :
+ size <= 8192 ? 64 :
+ size <= 16384 ? 128 :
+ size <= 32768 ? 256 :
+ size <= 65536 ? 512 : 11);
+ if (offset & ROMMASK)
+ printf(" This is a unusual position; not all BIOSs might find it.\n"
+ " Try to move to a 16kB boundary.\n");
+ if (size > len) {
+ printf(" This image extends beyond %04X:0000. "
+ "It clashes with the system BIOS\n",
+ ROMEND/16);
+ size = len; }
+ for (chksum=0, i = size; i--; chksum += rom[dummy = offset+i]);
+ if (chksum % 256)
+ printf(" Checksum does not match. This image is not active\n");
+ ptr = malloc(sizeof(Images));
+ ptr->next = images;
+ ptr->start = offset;
+ ptr->size = size;
+ images = ptr;
+ for (ptr = ptr->next; ptr != NULL; ptr = ptr->next) {
+ for (i = 0; i < size && i < ptr->size; i++)
+ if (rom[dummy = ptr->start+i] != rom[dummy = offset+i])
+ break;
+ if (i > 32) {
+ printf(" Image is identical with image at %04lX:0000 "
+ "for the first %ld bytes\n",
+ ptr->start/16,i);
+ if (i >= 1024)
+ if (i == size)
+ printf(" this means that you misconfigured the EPROM size!\n");
+ else
+ printf(" this could suggest that you misconfigured the "
+ "EPROM size\n");
+ else
+ printf(" this is probably harmless. Just ignore it...\n"); } }
+ return;
+}
+
+int main(void)
+{
+ long i;
+ unsigned char HUGE *rom;
+
+#ifndef __TURBOC__
+ int fh;
+ if ((fh = open("/dev/kmem",O_RDONLY|O_SYNC)) < 0) {
+ fprintf(stderr,"Could not open \"/dev/kmem\": %s\n",0 );//strerror(errno));
+ return(1); }
+ if ((rom = mmap(NULL,ROMEND-ROMSTART,PROT_READ,MAP_SHARED,fh,
+ ROMSTART)) == MAP_FAILED) {
+ fprintf(stderr,"Could not mmap \"/dev/kmem\": %s\n",0); //strerror(errno));
+ close(fh);
+ return(1); }
+ close(fh);
+#endif
+ for (i = ROMEND; (i -= ROMINCREMENT) >= ROMSTART; )
+#ifdef __TURBOC__
+ rom_scan(0,i,ROMEND-i);
+#else
+ rom_scan(rom-ROMSTART,i,ROMEND-i);
+ munmap(rom,ROMEND-ROMSTART);
+#endif
+ return(0);
+}
diff --git a/gpxe/contrib/romid/pktdrv.bat b/gpxe/contrib/romid/pktdrv.bat
new file mode 100644
index 00000000..058fd4e9
--- /dev/null
+++ b/gpxe/contrib/romid/pktdrv.bat
@@ -0,0 +1,84 @@
+@echo off
+if (%PKTDRV%)==(P) goto end
+A:\PKT\ROMID
+if (%ROMID%)==() goto select
+if (%ROMID%)==(NE) goto NE
+if (%ROMID%)==(WD) goto WD
+if (%ROMID%)==(3C509) goto 3C509
+if (%ROMID%)==(3C905B) goto 3C905B
+if (%ROMID%)==(EEPRO100) goto EEPRO100
+if (%PCIID%)==(10EC:8029) goto RTL8029
+if (%PCIID%)==(10EC:8139) goto RTL8139
+if (%PCIID%)==(11AD:0002) goto LITEON
+if (%PCIID%)==(1011:0009) goto DEC
+echo No Driver installed!
+goto end
+:select
+cls
+echo *** Select your network card.... ***
+echo.
+echo 1 -- Intel EtherExpress 100B PCI Adapter
+echo 2 -- 3Com 3C90X Etherlink III PCI Adapter
+echo 3 -- 3Com 3C5X9 Etherlink III ISA Adapter
+echo 4 -- NE2000 Novell ISA Adapter
+echo 5 -- Realtek 8029 PCI Adapter
+echo 6 -- Realtek 8139 PCI Adapter
+echo 7 -- SMC EliteUltra 8216 ISA Adapter
+echo 8 -- DEC21x4 Busmaster PCI Adapter
+echo 9 -- LiteOn PNIC Busmaster PCI Adapter
+echo.
+echo *** **************************** ***
+echo.
+choice /c123456789 /n Select:
+if errorlevel 9 goto LITEON
+if errorlevel 8 goto DEC
+if errorlevel 7 goto WD
+if errorlevel 6 goto RTL8139
+if errorlevel 5 goto RTL8029
+if errorlevel 4 goto NE
+if errorlevel 3 goto 3C509
+if errorlevel 2 goto 3C905B
+if errorlevel 1 goto EEPRO100
+goto end
+
+:EEPRO100
+A:\PKT\E100BPKT 0x60
+if errorlevel 0 goto ok
+goto end
+:NE
+A:\PKT\NE2000 0x60 11 0x0300
+if errorlevel 0 goto ok
+goto end
+:RTL8029
+A:\PKT\PCIPKT 0x60
+if errorlevel 0 goto ok
+goto end
+:RTL8139
+A:\PKT\RTSPKT 0x60
+if ERRORLEVEL 0 goto OK
+goto end
+:WD
+A:\PKT\SMC_WD 0x60
+if errorlevel 0 goto ok
+goto end
+:3C509
+A:\PKT\3C5X9PD 0x60
+if errorlevel 0 goto ok
+goto end
+:3C905B
+A:\PKT\3C90XPD 0x60
+if errorlevel 0 goto ok
+goto end
+:LITEON
+A:\PKT\FEPD 0x60
+if ERRORLEVEL 0 goto ok
+goto end
+:DEC
+A:\PKT\ETHPCI 0x60
+if ERRORLEVEL 0 goto ok
+goto end
+
+:ok
+SET PKTDRV=P
+:end
+
diff --git a/gpxe/contrib/romid/readme b/gpxe/contrib/romid/readme
new file mode 100644
index 00000000..6c6c500a
--- /dev/null
+++ b/gpxe/contrib/romid/readme
@@ -0,0 +1,8 @@
+This simple utility has currently no correct error checking.
+If your environment space couldn't hold up the variables, the
+program breaks without an error message, so be sure to add a
+line similar to the following to your config.sys:
+SHELL=A:\COMMAND.COM A:\ /P/E:2048
+
+The batch is a sample which shows how it could be used for those
+one not familar with batch files and environment variables. \ No newline at end of file
diff --git a/gpxe/contrib/romid/romid.c b/gpxe/contrib/romid/romid.c
new file mode 100644
index 00000000..f6049bfd
--- /dev/null
+++ b/gpxe/contrib/romid/romid.c
@@ -0,0 +1,124 @@
+/* This little program is my try to provide information about the
+ EtherBoot rom via environment variables to a batch file. This
+ could be done better, I think, but it works...
+ The program compiles with Borland C 3.1; other versions not tested.
+ The C code for setting the environment variables I got from an
+ archive, it was written by Richard Marks <rmarks@KSP.unisys.COM>.
+ ROMID is written by Guenter Knauf <eflash@gmx.net>
+*/
+#define VERSION "0.6"
+#define VDATE "2003-08-24"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ROMSTART 0xC8000
+#define ROMEND 0xE8000
+#define ROMINCREMENT 0x00800
+#define ROMMASK 0x03FFF
+
+int verbose = 0;
+
+int settheenv(char *symbol, char *val);
+
+static int rom_scan(const unsigned char huge *rom,long offset,long len) {
+ long size,i,j;
+ char symbol[16];
+ char val[64];
+ char romid[64];
+ char *rptr;
+
+ if (rom[offset] != 0x55 || rom[offset+1] != 0xAA)
+ return 0;
+
+ size = (long)rom[offset+2]*512L;
+ if (verbose) {
+ printf("Found ROM header at %04lX:0000; announces %ldk image\n", offset/16,(size+512)/1024);
+ if (offset & ROMMASK)
+ printf(" This is a unusual position; not all BIOSs might find it.\n"
+ " Try to move to a 16kB boundary.\n");
+ if (size > len) {
+ printf(" This image extends beyond %04X:0000. It clashes with the system BIOS\n", ROMEND/16);
+ size = len;
+ }
+ }
+
+ for (i=0; i<64; i++) {
+ if (rom[offset+size-3-i] == 0xff)
+ break;
+ }
+ if (20<i && i<63) {
+ i--;
+ for (j=0; j<i; j++)
+ val[j] = rom[offset+size-3-i+j];
+ val[i] = 0;
+ } else
+ return 0;
+
+ if (strstr(val, "therboot") == NULL)
+ return 0;
+
+ if (verbose)
+ printf("ROM Signature '%s'\n", val);
+ if ((rptr = strstr(val, "rom")) != NULL) {
+ for (i=1; i<4; i++) {
+ rptr--;
+ if (rptr[0] == 0x2E)
+ break;
+ }
+ i = 0;
+ while (!(rptr[0] == 0x20 || rptr < val)) {
+ i++;
+ rptr--;
+ }
+ rptr++;
+ i--;
+ strncpy(romid, rptr, i);
+ romid[i] = 0;
+ if (verbose)
+ printf("ROM Driver ID '%s'\n", romid);
+ strcpy(symbol, "ROMID");
+ if (settheenv(symbol, romid))
+ printf("Error setting evironment var %s with value %s\n", symbol, romid);
+ } else {
+ if (verbose)
+ printf("Couldnt find driver name!\n");
+ return 0;
+ }
+ if (rom[offset+0x1C] == 'P' && rom[offset+0x1D] == 'C' && rom[offset+0x1E] == 'I') {
+ sprintf(val, "%02X%02X:%02X%02X", rom[offset+0x21], rom[offset+0x20],
+ rom[offset+0x23], rom[offset+0x22]);
+ if (verbose)
+ printf("ROM Vendor ID '%s'\n", val);
+ strcpy(symbol, "PCIID");
+ if (settheenv(symbol, val))
+ printf("Error setting evironment var %s with value %s\n", symbol, val);
+ }
+ return 1;
+}
+
+/* **************** main stuff **************** */
+int main (int argc, char *argv[]) {
+ long i;
+
+ printf("\nROM-ID for Etherboot v%s (c) G. Knauf %s\n", VERSION, VDATE);
+ if (argc > 1) {
+ /* parse input parameters */
+ for (argc--, argv++; *argv; argc--, argv++) {
+ if ((strnicmp (*argv, "-", 1) == 0) || (strnicmp (*argv, "/", 1) == 0)) {
+ if ((strnicmp (*argv, "-V", 2) == 0) || (strnicmp (*argv, "/V", 2) == 0)) {
+ verbose = 1;
+ } else {
+ printf("Usage: %s [-v]\n");
+ }
+ }
+ }
+ }
+ for (i = ROMEND; (i -= ROMINCREMENT) >= ROMSTART;)
+ if (rom_scan(0,i,ROMEND-i))
+ break;
+
+ return 0;
+}
diff --git a/gpxe/contrib/romid/setenvs.c b/gpxe/contrib/romid/setenvs.c
new file mode 100644
index 00000000..e18e399e
--- /dev/null
+++ b/gpxe/contrib/romid/setenvs.c
@@ -0,0 +1,200 @@
+/* subroutine to put a value string into an environment symbol.
+ Uses the controling command.com environment, not the programs.
+ This means that the env variable is set so other routines in
+ a .BAT file may use it.
+
+ call: settheenv (char * symbol, char * val);
+ symbol is an asciiz string containing the env variable name,
+ val is an asciiz string containing the value to assign to this vbl.
+
+ returns: 0 = OK,
+ 1 = failure.
+ failure is not unlikely. The env block may be full. Or on some
+ systems the env block might not be found
+
+ SETENVS.C was written by Richard Marks <rmarks@KSP.unisys.COM>.
+*/
+
+
+#include <stdio.h>
+#include <dos.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct {
+ char fill1[0x0A];
+ int *prev_term_handler;
+ int *prev_ctrl_c;
+ int *prev_crit_error;
+ char fill2[0x16];
+ int envir_seg;
+} psp;
+
+typedef struct {
+ char type;
+ int psp_segment;
+ int num_segments;
+ char fill[11];
+ char arena_data;
+} arena;
+
+
+#define NORMAL_ATYPE 0x4D
+#define LAST_ATYPE 0x5A
+
+
+static arena * get_next_arena (arena * ap) {
+ return( MK_FP( FP_SEG(ap)+1+ap->num_segments, 0) );
+}
+
+/* returns 0 if passed pointer is to an arena, else returns 1 */
+static int is_valid_arena (arena * ap) {
+ arena * ap1;
+ if (ap->type == NORMAL_ATYPE &&
+ (ap1=get_next_arena(ap))->type == NORMAL_ATYPE &&
+ ( (ap1=get_next_arena(ap1))->type == NORMAL_ATYPE ||
+ ap1->type == LAST_ATYPE) )
+ return(0);
+ return (1);
+}
+
+
+static arena * get_first_arena () {
+/* return pointer to the first arena.
+ * scan memory for a 0x4D on a segment start,
+ * see if this points to another two levels of arena
+ */
+ arena * ap, * ap1;
+ int * temp;
+ int segment;
+
+ for (segment=0; segment<_CS; segment++) {
+ ap = MK_FP(segment, 0);
+ if ( is_valid_arena (ap) == 0) return (ap);
+ }
+ return(NULL);
+} /* end get_first_arena */
+
+
+static int is_valid_env (char * ad, int num_segs) {
+ char * base_ad;
+ base_ad = ad;
+ while ( (*ad) && (((ad-base_ad)>>4) < num_segs) ) {
+ if (strnicmp(ad, "COMSPEC=", 8)==0) return(0);
+ ad += strlen(ad) + 1;
+ }
+ return (1);
+}
+
+
+static arena * get_arena_of_environment () {
+/* to get the arena of first environment block:
+ First get segment of COMMAND.COM from segment of previous critical err code.
+ Then scan all the arenas for an environment block with a matching PSP
+ segment */
+
+arena * ap;
+psp * pspp, * pspc;
+unsigned int i, ccseg;
+
+/* set pspp to psp of this program */
+pspp = MK_FP(_psp,0);
+
+#ifdef DEBUG
+printf("prog psp=%p\n",pspp);
+#endif
+
+/* set pspc to psp of COMMAND.COM, back up a bit to get it if needed */
+ccseg = FP_SEG (pspp->prev_crit_error);
+if ( (i=ccseg-32) < 60) i=60;
+
+while (ccseg>i) {
+ pspc = MK_FP (ccseg, 0);
+ if ( is_valid_arena((arena *) pspc) == 0) goto L1;
+ ccseg--;
+}
+return (NULL);
+
+L1: pspc = MK_FP (++ccseg, 0);
+#ifdef DEBUG
+printf("comm.com=%p\n",pspc);
+#endif
+
+/* first see if env seg in command.com points to valid env block
+ if env seg is in a valid arena, then arena must point to this command.com
+ else assume env block is fabricated like for 4DOS, use 128 bytes */
+
+ap = MK_FP (pspc->envir_seg-1, 0);
+i = ap->num_segments;
+
+if (is_valid_arena (ap) == 0) {
+ if (ap->psp_segment != FP_SEG(pspc)) goto L2;
+} else {
+ i = 9;
+}
+
+if ( is_valid_env (&ap->arena_data, i) == 0 )
+ return (ap);
+
+/* command.com did not so point, search thru all env blocks */
+
+L2:
+if ( (ap=get_first_arena()) != NULL ) {
+ while (ap->type != LAST_ATYPE) {
+#ifdef DEBUG
+ printf("%p\n",ap);
+#endif
+ if (ap->psp_segment == FP_SEG(pspc) &&
+ is_valid_env (&ap->arena_data, ap->num_segments)==0 )
+ return (ap);
+
+ ap = get_next_arena(ap);
+ }
+} return(NULL);
+} /* end get_arena_of_environment */
+
+/*****************************************************************************/
+
+int settheenv(char * symbol, char * val) {
+int total_size,
+ needed_size=0,
+ strlength;
+char * sp, *op, *envir;
+char symb_len=strlen(symbol);
+char found=0;
+arena * ap;
+
+strupr(symbol);
+
+/* first, can COMMAND.COM's envir block be found ? */
+if ( (ap=get_arena_of_environment()) == NULL)
+ return(1);
+
+/* search to end of the envir block, get sizes */
+total_size = 16 * ap->num_segments;
+envir = &ap->arena_data;
+op=sp=envir;
+while (*sp) {
+ strlength = strlen(sp)+1;
+ if ( *(sp+symb_len)=='=' &&
+ strnicmp(sp,symbol,symb_len)==0 )
+ found=1;
+ else {
+ needed_size += strlength;
+ if (found) strcpy(op,sp);
+ op = &op[strlength];
+ }
+ sp += strlength;
+}
+*op=0;
+if (strlen(val) > 0) {
+ needed_size += 3 + strlen(symbol) + strlen(val);
+ if (needed_size > total_size)
+ return(1); /* could mess with environment expansion here */
+
+ strcpy(op, symbol); strcat(op, "="); strcat(op, val);
+ op += strlen(op)+1;
+ *op = 0;
+}
+return(0);
+} /* end setheenv subroutine */
diff --git a/gpxe/contrib/smc9462tx-flash/Makefile b/gpxe/contrib/smc9462tx-flash/Makefile
new file mode 100644
index 00000000..dc5b3ff8
--- /dev/null
+++ b/gpxe/contrib/smc9462tx-flash/Makefile
@@ -0,0 +1,20 @@
+GCC = gcc
+KERNELDIR = ../linux-2.4.20
+KERNELSTYLE=-D__KERNEL__ -DCPU=__i386__ -DMODULE
+INCLUDE_DIR=-I$(KERNELDIR)/include -I../include -I$(ROOTDIR)/include
+
+all: dp83820flash.o dp83820_write
+
+CFLAGS+=-O2 -Wall -fomit-frame-pointer -fno-strength-reduce
+CFLAGS+=$(KERNELSTYLE) $(CDEBUG) $(INCLUDE_DIR)
+
+install:
+
+dp83820flash.o: dp83820flash.c
+ $(GCC) dp83820flash.c -o dp83820flash.o -c $(CFLAGS)
+
+dp83820_write: dp83820_write.c
+ $(GCC) $< -o $@ -Wall -O2
+
+clean:
+ $(RM) *.o dp83820_write
diff --git a/gpxe/contrib/smc9462tx-flash/README b/gpxe/contrib/smc9462tx-flash/README
new file mode 100644
index 00000000..53501cb3
--- /dev/null
+++ b/gpxe/contrib/smc9462tx-flash/README
@@ -0,0 +1,24 @@
+This code was written by Dave Ashley for NXTV, Inc. It is released under
+the terms of the GPL. The purpose is to let you write to the bootrom of
+the SMC9462TX card. The assumption is that you've stuck an AT29C512 in the
+socket. Note that the board has pins D5 + D6 reversed on the socket. Also
+the socket only supplies 3.3V to the rom. Good luck trying to locate a
+DIP programmable flash device that operates at 3.3V. What I do is to bend
+pin 32 back and solder a wire directly from the 5V side of the 3.3V regulator
+over to it. The dp83820's bootrom interface pins are all 5V logic tolerant.
+However mod your board at your own risk, no warranty or guarantees are implied
+or given!!! If you don't wire the 5V to the AT29C512, you can still read
+the rom contents (it operates ok at 3.3V evidently) but you can't write to it
+because the AT29C512 has a safety protection, it disables writes if the
+power supply voltage drops below 3.8V.
+
+See the comments at the top of the 2 C files for more information.
+
+The Makefile needs to be hacked to build for your system. If you can't
+figure it out you shouldn't be messing with this stuff anyway.
+
+-Dave Ashley
+Email address intentionally left out to avoid spam.
+http://www.xdr.com/dash
+
+Mon Mar 8 13:55:34 PST 2004
diff --git a/gpxe/contrib/smc9462tx-flash/dp83820_write.c b/gpxe/contrib/smc9462tx-flash/dp83820_write.c
new file mode 100644
index 00000000..ab6e5662
--- /dev/null
+++ b/gpxe/contrib/smc9462tx-flash/dp83820_write.c
@@ -0,0 +1,310 @@
+/*
+ DP83820 flash utility written by Dave Ashley for NXTV, Inc.
+ Copyright (C) 2004 by NXTV, Inc.
+ Written 20040219 by Dave Ashley.
+
+ Currently only supports the AT29C512
+
+ This code is released under the terms of the GPL. No warranty.
+
+
+ THEORY:
+ This code uses the /proc/dp83820 file which is created by the
+ dp83820flash.o module. That file allows single byte reads + writes
+ to the bootrom.
+
+*/
+
+#include <unistd.h>
+#include <sys/io.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <signal.h>
+
+
+// SMC9462TX card has D5 + D6 on the bootrom socket reversed
+int fixb(int val)
+{
+ return (val&~0x60) | ((val&0x20)<<1) | ((val&0x40)>>1);
+}
+int openit(void)
+{
+int fd;
+ fd=open("/proc/dp83820",O_RDWR);
+ if(fd<0)
+ {
+ printf("Failed to open the /proc/dp83820 file to access the flashrom.\n");
+ printf("Make sure you've done:\n");
+ printf(" modprobe dp83820flash\n");
+ exit(-1);
+ }
+ return fd;
+}
+void set(int addr, unsigned char val)
+{
+unsigned char msg[3];
+int fd;
+ fd=openit();
+ msg[0]=addr;
+ msg[1]=addr>>8;
+ msg[2]=val;
+ write(fd,msg,3);
+ close(fd);
+}
+int get(int addr)
+{
+unsigned char msg[2];
+int fd;
+ fd=openit();
+ msg[0]=addr;
+ msg[1]=addr>>8;
+ write(fd,msg,2);
+ read(fd,msg,1);
+ close(fd);
+ return msg[0];
+}
+
+
+int getromsize(unsigned char *id)
+{
+ if(id[0]==0xbf && id[1]==0xb6) return 0x40000;
+ if(id[0]==0xc2 && id[1]==0xb0) return 0x40000;
+ if(id[0]==0x1f && id[1]==0x3d) return 0x10000;
+ return -1;
+}
+
+#define MAXROMSIZE 0x200000
+unsigned char *buffer;
+
+int loadfile(char *name)
+{
+int filefd;
+int filesize;
+ filefd=open(name,O_RDONLY);
+ if(filefd<0)
+ {
+ printf("Couldn't open file %s\n",name);
+ return -1;
+ }
+ filesize=read(filefd,buffer,MAXROMSIZE);
+ close(filefd);
+ if(filesize<0)
+ {
+ printf("Error trying to read from file %s\n",name);
+ }
+ return filesize;
+}
+
+void readbios(char *name,int len)
+{
+int filefd;
+int filesize=0;
+unsigned char block[256];
+int i,j;
+
+ filefd=open(name,O_WRONLY|O_TRUNC|O_CREAT,0644);
+ if(filefd<0)
+ {
+ printf("Couldn't create file %s for writing\n",name);
+ return;
+ }
+ for(i=j=0;i<len;++i)
+ {
+ block[j++]=get(i);
+ if(j<sizeof(block)) continue;
+ filesize+=write(filefd,block,j);
+ j=0;
+ }
+ close(filefd);
+ if(filesize!=len)
+ {
+ printf("Error during write of %s file\n",name);
+ return;
+ }
+ printf("BIOS contents saved to %s, $%x bytes\n",name,len);
+}
+
+int verifybios(char *name,int len, int print)
+{
+int filelen;
+int i;
+int same=0;
+
+ filelen=loadfile(name);
+ for(i=0;i<filelen;++i)
+ if(get(i)!=buffer[i]) break;
+ if(i<filelen)
+ {
+ if(print)
+ printf("BIOS contents does not match file %s, from byte $%x\n",
+ name,i);
+ } else
+ {
+ if(print)
+ printf("BIOS contents match file %s for all of its $%x bytes\n",
+ name,i);
+ same=1;
+ }
+ return same;
+}
+
+void writebios(char *name,int len,unsigned char *id)
+{
+int i;
+int p1,p2;
+int sectorsize=128;
+
+ if(len!=loadfile(name))
+ {
+ printf("File size does not match expected ROM size\n");
+ return;
+ }
+ if(0 && (id[0]!=0xbf || id[1]!=0xb6))
+ {
+ printf("Don't know how to write this kind of flash device\n");
+ return;
+ }
+
+ printf("Erasing device\n");
+ set(0x5555,fixb(0xaa));
+ set(0x2aaa,fixb(0x55));
+ set(0x5555,fixb(0x80));
+ set(0x5555,fixb(0xaa));
+ set(0x2aaa,fixb(0x55));
+ set(0x5555,fixb(0x10));
+
+ for(;;)
+ {
+ printf(".");fflush(stdout);
+ usleep(250000);
+ if(get(0)==get(0) && get(0)==get(0))
+ break;
+ }
+ printf("BIOS erased\n");
+
+ printf("Writing to BIOS\n");
+ p1=-1;
+ for(i=0;i<len;++i)
+ {
+ p2=100*i/(len-1);
+ if(p2!=p1)
+ {
+ printf("\r%d%%",p1=p2);
+ fflush(stdout);
+ }
+ if(i%sectorsize==0)
+ {
+ set(0x5555,fixb(0xaa));
+ set(0x2aaa,fixb(0x55));
+ set(0x5555,fixb(0xa0));
+ }
+ set(i,buffer[i]);
+ if(i%sectorsize==sectorsize-1)
+ while(get(0)!=get(0) || get(0)!=get(0));
+ }
+ printf("\n");
+}
+
+void helptext(char *name)
+{
+ printf("USE: %s <options>\n",name);
+ printf(" -v <filename> = verify bios rom contents with file\n");
+ printf(" -w <filename> = write to bios rom contents from file\n");
+ printf(" -r <filename> = read from bios rom contents to file\n");
+ printf(" -f = force erase/write even if contents already match\n");
+ exit(0);
+}
+
+int main(int argc,char **argv)
+{
+int i;
+int vals;
+unsigned char id[4];
+char *filename=0;
+char action=0;
+int romsize;
+int force=0;
+int same;
+
+ vals=0;
+
+ if(argc<2) helptext(argv[0]);
+ for(i=1;i<argc;++i)
+ {
+ if(argv[i][0]!='-')
+ helptext(argv[0]);
+ switch(argv[i][1])
+ {
+ case 'f':
+ force=1;
+ break;
+ case 'v':
+ case 'w':
+ case 'r':
+ action=argv[i][1];
+ if(i+1<argc)
+ filename=argv[++i];
+ else helptext(argv[0]);
+ break;
+ default:
+ helptext(argv[0]);
+ }
+
+ }
+
+ buffer=malloc(MAXROMSIZE);
+ if(!buffer)
+ {
+ printf("No memory available!\n");
+ exit(-1);
+ }
+
+ set(0x5555,fixb(0xaa)); // get into flash ID mode
+ set(0x2aaa,fixb(0x55));
+ set(0x5555,fixb(0x90));
+
+ for(i=0;i<4;++i) id[i]=get(i);
+
+ set(0x5555,fixb(0xaa)); // get out of flash ID mode
+ set(0x2aaa,fixb(0x55));
+ set(0x5555,fixb(0xf0));
+ usleep(10000);
+
+ for(i=0;i<4;++i)
+ if(id[i]!=get(i)) break;
+ if(i==4)
+ {
+ printf("Could not read BIOS flashrom ID.\n");
+ goto biosdone;
+ }
+ printf("ID %02x %02x\n",id[0],id[1]);
+ romsize=getromsize(id);
+ if(romsize<0)
+ {
+ printf("Unknown rom type\n");
+ goto biosdone;
+ }
+ printf("romsize=$%x bytes\n",romsize);
+ if(action=='r')
+ readbios(filename,romsize);
+ if(action=='w')
+ {
+ if(!force)
+ same=verifybios(filename,romsize,0);
+ else
+ same=0;
+ if(!same)
+ writebios(filename,romsize,id);
+ }
+ if(action=='v' || action=='w')
+ verifybios(filename,romsize,1);
+
+biosdone:
+
+ return 0;
+}
diff --git a/gpxe/contrib/smc9462tx-flash/dp83820flash.c b/gpxe/contrib/smc9462tx-flash/dp83820flash.c
new file mode 100644
index 00000000..661c4291
--- /dev/null
+++ b/gpxe/contrib/smc9462tx-flash/dp83820flash.c
@@ -0,0 +1,152 @@
+/*
+ Kernel module for the dp83820 flash write utility. This code was written
+ by Dave Ashley for NXTV, Inc.
+ Copyright 2004 by NXTV, Inc.
+ Written 20040219 by Dave Ashley.
+
+ This code is released under the terms of the GPL. No warranty.
+
+ THEORY: The dp83820 bootrom interface is flawed in that you can't
+ read or write a single byte at a time, and this is required in order
+ to write to flash devices like the AT29C512. So the workaround is
+ to use the chips ability to map into memory the bootrom, then the cpu
+ can directly do byte accesses.
+
+ The problem is that a "feature" of the dp83820 is that when you map
+ in the bootrom, you conveniently lose access to the PCI registers.
+ So we need to do this in kernel space and wrap every access to the
+ bootrom within interrupt_disable/restore, in case a network interrupt
+ were to come in.
+
+ This kernel module is very simple, it just creates a proc file
+ /proc/dp83820
+ If you write 3 bytes to this file you are doing a write to the flashrom:
+
+Byte 1 2 3
+ ALOW AHIGH DATA
+
+ If you write 2 bytes to this file you are doing a read from the flashrom:
+Byte 1 2
+ ALOW AHIGH
+ Then the next read from the file will return a single byte of what
+ was at that location.
+
+ You only get one shot at accessing the proc file, you need to then
+ close/open if you want to do another access. This could probably be
+ cleaned up pretty easily so more accesses can be done without having
+ to close/open the file.
+
+*/
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+
+
+#define PROCNAME "dp83820"
+
+struct pci_dev *mydev=0;
+unsigned long loc;
+unsigned char *addr=0;
+
+unsigned char lastread;
+
+
+int my_read_proc(char *buf, char **start,off_t offset,int count, int *eof,void *data)
+{
+int retval=0;
+
+ if(count>0)
+ {
+ buf[0]=lastread;
+ retval=1;
+ }
+
+ *eof=1;
+
+ return retval;
+}
+
+int my_write_proc(struct file *file, const char *buffer, unsigned long count,
+ void *data)
+{
+unsigned char *msg;
+
+unsigned long flags;
+
+ msg=(void *)buffer;
+ save_flags(flags);
+ cli();
+ pci_write_config_dword(mydev, 0x30, loc | 1);
+
+ switch(count)
+ {
+ case 2:
+ lastread=addr[msg[0] | (msg[1]<<8)];
+ break;
+ case 3:
+ addr[msg[0] | (msg[1]<<8)] = msg[2];
+ break;
+ }
+ pci_write_config_dword(mydev, 0x30, loc);
+ restore_flags(flags);
+ return count;
+}
+
+
+struct proc_dir_entry *de=0;
+
+int __init init_module(void)
+{
+int found=0;
+ mydev=0;
+ pci_for_each_dev(mydev)
+ {
+ if(mydev->vendor==0x100b && mydev->device==0x0022)
+ {
+ found=1;
+ break;
+ }
+ }
+ if(!found)
+ {
+ printk("Could not find DP83820 network device\n");
+ return ENODEV;
+ }
+
+ de=create_proc_entry(PROCNAME,0,0);
+ if(!de)
+ return -1;
+ de->data=0;
+ de->read_proc=my_read_proc;
+ de->write_proc=my_write_proc;
+
+ loc=mydev->resource[PCI_ROM_RESOURCE].start;
+ addr=ioremap_nocache(loc,0x10000);
+
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if(de)
+ {
+ remove_proc_entry(PROCNAME,0);
+ de=0;
+ }
+ if(addr)
+ {
+ iounmap(addr);
+ addr=0;
+ }
+}
+
diff --git a/gpxe/contrib/t2hproxy/README b/gpxe/contrib/t2hproxy/README
new file mode 100644
index 00000000..ce5e1e9b
--- /dev/null
+++ b/gpxe/contrib/t2hproxy/README
@@ -0,0 +1,95 @@
+T2hproxy
+
+This is a TFTP to HTTP proxy. To the TFTP client it looks like a TFTP
+server. To the HTTP server it looks like a HTTP client. So you can store
+your boot files on the HTTP server. Or even create them with a CGI
+program. E.g. if you can get dhcpd to send a filename which has strings
+representing attributes of the client, as determined from the DHCP
+request, then you can get the CGI program to parse this and send the
+appropriate image, which might even be synthesised.
+
+There are two versions of the proxy, in Perl and in Java.
+
+1. The Perl version.
+
+This is the original quick Perl hack conceived in a moment of madness.
+:-) Perl is great for prototyping.
+
+To run it, you need Perl 5.8.0 or later and all the Perl modules listed
+at the top of the program installed. Edit and install the xinetd config
+file as /etc/xinetd.d/t2hproxy and restart xinetd. The prefix is the
+string that is prepended to all filenames to form the URL requested from
+the HTTP server. Remember you need the trailing / if the filenames don't
+start with /.
+
+This is only a proof-of concept. It has these drawbacks at the moment:
+
++ (I don't consider this a draback, but some may.) It's started from
+xinetd because xinetd handles all the socket listening, IP address
+checking, rate limiting, etc.
+
++ It has no cache. Use a proxy to do the caching (there's a --proxy
+option). This also takes care of fetching from outside a firewall.
+
++ It reads the entire HTTP content into memory before serving. Ideally
+it should stream it from the HTTP server to minimise memory usage. This
+is a serious drawback for booting lots of clients. Each instance of the
+server will consume an amount of memory equal to the size of image
+loaded.
+
++ If the HTTP server is at the end of a slow link there is a delay
+before the first data block is sent. The client may timeout before
+then. Another reason for streaming, as this allows the first block to
+be sent sooner. A local cache primed with the images in advance may
+help. Using the blocksize option helps here because this causes the
+server to send the OACK to the client immediately before the data is
+fetched and this prevents it from starting up another connection.
+
++ The transfer size may not be obtainable from the HTTP headers in all
+cases, e.g. a CGI constructed image. This matters for clients that need
+the tsize extension, which is not supported at the moment.
+
+If I'm feeling masochistic I may write a Java version, which should take
+care of the multi-threading and streaming.
+
+2. The Java version
+
+The main problem with the Perl version is that it does not stream the
+HTTP input but sucks it all in at once. As mentioned, this causes a
+delay as well as requiring memory to hold the image. I could fix this by
+doing the polling on the HTTP socket myself instead of letting LWP do
+it, but that's for later. Java has streaming facilities as well as
+threading and is also somewhat portable. So I decided to be masochistic
+and give it a go. But boy is Java bureaucratic.
+
+You will need a Java 1.4 JRE, because I use the java.nio classes; and
+the commons-httpclient and commons-logging jars from the
+jakarta.apache.org project. As I understand it, there are several ways
+to get those jars on your classpath. One is to put it in the directory
+where your java extensions jars are kept, normally
+$JAVA_HOME/jre/lib/ext. But it may not be writable to you. Another is to
+set your $CLASSPATH variable to have those jars in the path. A third is
+to use the -cp option of the java interpreter, see the shell script
+runT2hproxy for details.
+
+All the source is in one Java file. build.xml is a "Makefile" for ant to
+compile and jar it. You should then edit runT2proxy.sh as required, then
+start it. As with the Perl version, the prefix is what's prepended to
+the filenames requested by the TFTP client, and the proxy is the
+host:port string for the proxy if you are using one. On *ix you will
+need root permission to listen on ports below 1024 (TFTP is at 69 UDP by
+default).
+
+Currently it logs to stderr, but you can change this by downloading and
+installing the log4j jar from jakarta.apache.org and instructing
+commons-logging to use that, with a command line property setting and a
+property file. Destinations could be syslog, or a file, or an event
+logger, or...; it's supposedly very flexible.
+
+3. Licensing
+
+All this code is GPLed. For details read the file COPYING found in the
+Etherboot top directory since it currently bundled with Etherboot. I
+don't see the point of including COPYING in every directory.
+
+Ken Yap, October 2003
diff --git a/gpxe/contrib/t2hproxy/T2hproxy.java b/gpxe/contrib/t2hproxy/T2hproxy.java
new file mode 100644
index 00000000..cfe1d1a7
--- /dev/null
+++ b/gpxe/contrib/t2hproxy/T2hproxy.java
@@ -0,0 +1,508 @@
+/*
+ * TFTP to HTTP proxy in Java
+ *
+ * Copyright Ken Yap 2003
+ * Released under GPL2
+ */
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.String;
+import java.lang.StringBuffer;
+import java.lang.Thread;
+import java.lang.NumberFormatException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.BufferUnderflowException;
+import java.util.HashMap;
+import java.util.Properties;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HostConfiguration;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.UsernamePasswordCredentials;
+import org.apache.commons.httpclient.methods.GetMethod;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Description of the Class
+ *
+ *@author ken
+ *@created 24 September 2003
+ */
+public class T2hproxy implements Runnable {
+ /**
+ * Description of the Field
+ */
+ public final static String NAME = T2hproxy.class.getName();
+ /**
+ * Description of the Field
+ */
+ public final static String VERSION = "0.1";
+ /**
+ * Description of the Field
+ */
+ public final static int MTU = 1500;
+ /**
+ * Description of the Field
+ */
+ public final static short TFTP_RRQ = 1;
+ /**
+ * Description of the Field
+ */
+ public final static short TFTP_DATA = 3;
+ /**
+ * Description of the Field
+ */
+ public final static short TFTP_ACK = 4;
+ /**
+ * Description of the Field
+ */
+ public final static short TFTP_ERROR = 5;
+ /**
+ * Description of the Field
+ */
+ public final static short TFTP_OACK = 6;
+ /**
+ * Description of the Field
+ */
+ public final static short ERR_NOFILE = 1;
+ /**
+ * Description of the Field
+ */
+ public final static short ERR_ILLOP = 4;
+ /**
+ * Description of the Field
+ */
+ public final static int MAX_RETRIES = 5;
+ /**
+ * TFTP timeout in milliseconds
+ */
+ public final static int TFTP_ACK_TIMEOUT = 2000;
+ /**
+ * Description of the Field
+ */
+ public final static int DEFAULT_PROXY_PORT = 3128;
+
+ private static Log log = LogFactory.getLog(T2hproxy.class);
+ /**
+ * The members below must be per thread and must not share any storage with
+ * the main thread
+ */
+ private DatagramSocket responsesocket;
+ private DatagramPacket response;
+ private InetAddress iaddr;
+ private int port;
+ private byte[] req;
+ private String prefix;
+ private String proxy = null;
+ private int timeout;
+ private HashMap options = new HashMap();
+ private int blocksize = 512;
+ private HttpClient client = new HttpClient();
+ private HttpMethod method;
+ private BufferedInputStream bstream = null;
+ private String message;
+
+
+ /**
+ * Constructor for the T2hproxy object
+ *
+ *@param i Description of the Parameter
+ *@param p Description of the Parameter
+ *@param b Description of the Parameter
+ *@param pf Description of the Parameter
+ *@param pr Description of the Parameter
+ *@param t Timeout for HTTP GET
+ */
+ public T2hproxy(InetAddress i, int p, byte[] b, String pf, String pr, int t) {
+ iaddr = i;
+ port = p;
+ // make a copy of the request buffer
+ req = new byte[b.length];
+ System.arraycopy(b, 0, req, 0, b.length);
+ prefix = pf;
+ // proxy can be null
+ proxy = pr;
+ timeout = t;
+ }
+
+
+ /**
+ * Extract an asciz string from bufer
+ *
+ *@param buffer Description of the Parameter
+ *@return The asciz value
+ */
+ private String getAsciz(ByteBuffer buffer) {
+ StringBuffer s = new StringBuffer();
+ try {
+ byte b;
+ while ((b = buffer.get()) != 0) {
+ s.append((char) b);
+ }
+ } catch (BufferUnderflowException e) {
+ } finally {
+ return (s.toString());
+ }
+ }
+
+
+ /**
+ * Convert a string of digits to a number, invalid => 0
+ *
+ *@param s Description of the Parameter
+ *@return Description of the Return Value
+ */
+ private int atoi(String s) {
+ if (s == null) {
+ return (0);
+ }
+ int value = 0;
+ try {
+ value = (new Integer(s)).intValue();
+ } catch (NumberFormatException e) {
+ }
+ return (value);
+ }
+
+
+ /**
+ * Wait for ack packet with timeout
+ *
+ *@return Return block number acked
+ */
+ private int waitForAck() {
+ DatagramPacket ack = new DatagramPacket(new byte[MTU], MTU);
+ try {
+ do {
+ responsesocket.setSoTimeout(TFTP_ACK_TIMEOUT);
+ responsesocket.receive(ack);
+ } while (!ack.getAddress().equals(iaddr) || ack.getPort() != port);
+ } catch (SocketTimeoutException e) {
+ return (-1);
+ } catch (Exception e) {
+ log.info(e.toString(), e);
+ }
+ ByteBuffer buffer = ByteBuffer.wrap(ack.getData(), ack.getOffset(), ack.getLength() - ack.getOffset());
+ short op;
+ if ((op = buffer.getShort()) == TFTP_ACK) {
+ return ((int) buffer.getShort());
+ } else if (op == TFTP_ERROR) {
+ return (-2);
+ }
+ return (-3);
+ }
+
+
+ /**
+ * Description of the Method
+ *
+ *@param error Description of the Parameter
+ *@param message Description of the Parameter
+ */
+ private void sendError(short error, String message) {
+ ByteBuffer buffer = ByteBuffer.wrap(response.getData());
+ buffer.putShort(TFTP_ERROR).putShort(error).put(message.getBytes());
+ response.setLength(buffer.position());
+ try {
+ responsesocket.send(response);
+ } catch (Exception e) {
+ log.info(e.toString(), e);
+ }
+ }
+
+
+ /**
+ * Description of the Method
+ *
+ *@return Description of the Return Value
+ */
+ private boolean sendOackRecvAck() {
+ ByteBuffer buffer = ByteBuffer.wrap(response.getData());
+ buffer.putShort(TFTP_OACK).put("blksize".getBytes()).put((byte) 0).put(String.valueOf(blocksize).getBytes()).put((byte) 0);
+ response.setLength(buffer.position());
+ int retry;
+ for (retry = 0; retry < MAX_RETRIES; retry++) {
+ try {
+ responsesocket.send(response);
+ } catch (Exception e) {
+ log.info(e.toString(), e);
+ }
+ if (waitForAck() == 0) {
+ log.debug("Ack received");
+ break;
+ }
+ }
+ return (retry < MAX_RETRIES);
+ }
+
+
+ /**
+ * Description of the Method
+ *
+ *@param block Description of the Parameter
+ *@return Description of the Return Value
+ */
+ private boolean sendDataBlock(int block) {
+ int retry;
+ for (retry = 0; retry < MAX_RETRIES; retry++) {
+ try {
+ responsesocket.send(response);
+ } catch (Exception e) {
+ log.info(e.toString(), e);
+ }
+ int ablock;
+ if ((ablock = waitForAck()) == block) {
+ log.debug("Ack received for " + ablock);
+ break;
+ } else if (ablock == -1) {
+ log.info("Timeout waiting for ack");
+ } else if (ablock == -2) {
+ return (false);
+ } else {
+ log.info("Unknown opcode from ack");
+ }
+ }
+ return (retry < MAX_RETRIES);
+ }
+
+
+ /**
+ * Description of the Method
+ *
+ *@param buffer Description of the Parameter
+ *@return Description of the Return Value
+ */
+ private boolean handleOptions(ByteBuffer buffer) {
+ for (; ; ) {
+ String option = getAsciz(buffer);
+ String value = getAsciz(buffer);
+ if (option.equals("") || value.equals("")) {
+ break;
+ }
+ log.info(option + " " + value);
+ options.put(option, value);
+ }
+ blocksize = atoi((String) options.get("blksize"));
+ if (blocksize < 512) {
+ blocksize = 512;
+ }
+ if (blocksize > 1432) {
+ blocksize = 1432;
+ }
+ return (sendOackRecvAck());
+ }
+
+
+ /**
+ * Description of the Method
+ *
+ *@param url Description of the Parameter
+ */
+ private void makeStream(String url) {
+ // establish a connection within timeout milliseconds
+ client.setConnectionTimeout(timeout);
+ if (proxy != null) {
+ String[] hostport = proxy.split(":");
+ int port = DEFAULT_PROXY_PORT;
+ if (hostport.length > 1) {
+ port = atoi(hostport[1]);
+ if (port == 0) {
+ port = DEFAULT_PROXY_PORT;
+ }
+ }
+ log.info("Proxy is " + hostport[0] + ":" + port);
+ client.getHostConfiguration().setProxy(hostport[0], port);
+ }
+ // create a method object
+ method = new GetMethod(url);
+ method.setFollowRedirects(true);
+ method.setStrictMode(false);
+ try {
+ int status;
+ if ((status = client.executeMethod(method)) != 200) {
+ log.info(message = method.getStatusText());
+ return;
+ }
+ bstream = new BufferedInputStream(method.getResponseBodyAsStream());
+ } catch (HttpException he) {
+ message = he.getMessage();
+ } catch (IOException ioe) {
+ message = "Unable to get " + url;
+ }
+ }
+
+
+ /**
+ * Reads a block of data from URL stream
+ *
+ *@param stream Description of the Parameter
+ *@param data Description of the Parameter
+ *@param blocksize Description of the Parameter
+ *@param offset Description of the Parameter
+ *@return Number of bytes read
+ */
+ private int readBlock(BufferedInputStream stream, byte[] data, int offset, int blocksize) {
+ int status;
+ int nread = 0;
+ while (nread < blocksize) {
+ try {
+ status = stream.read(data, offset + nread, blocksize - nread);
+ } catch (Exception e) {
+ return (-1);
+ }
+ if (status < 0) {
+ return (nread);
+ }
+ nread += status;
+ }
+ return (nread);
+ }
+
+
+ /**
+ * Description of the Method
+ *
+ *@param filename Description of the Parameter
+ */
+ private void doRrq(String filename) {
+ String url = prefix + filename;
+ log.info("GET " + url);
+ makeStream(url);
+ if (bstream == null) {
+ log.info(message);
+ sendError(ERR_NOFILE, message);
+ return;
+ }
+ // read directly into send buffer to avoid buffer copying
+ byte[] data;
+ ByteBuffer buffer = ByteBuffer.wrap(data = response.getData());
+ // dummy puts to get start position of data
+ buffer.putShort(TFTP_DATA).putShort((short) 0);
+ int start = buffer.position();
+ int length;
+ int block = 1;
+ do {
+ length = readBlock(bstream, data, start, blocksize);
+ block &= 0xffff;
+ log.debug("Block " + block + " " + length);
+ // fill in the block number
+ buffer.position(0);
+ buffer.putShort(TFTP_DATA).putShort((short) block);
+ response.setLength(start + length);
+ if (!sendDataBlock(block)) {
+ break;
+ }
+ buffer.position(start);
+ block++;
+ } while (length >= blocksize);
+ log.info("Closing TFTP session");
+ // clean up the connection resources
+ method.releaseConnection();
+ method.recycle();
+ }
+
+
+ /**
+ * Main processing method for the T2hproxy object
+ */
+ public void run() {
+ ByteBuffer buffer = ByteBuffer.wrap(req);
+ buffer.getShort();
+ String filename = getAsciz(buffer);
+ String mode = getAsciz(buffer);
+ log.info(filename + " " + mode);
+ response = new DatagramPacket(new byte[MTU], MTU, iaddr, port);
+ try {
+ responsesocket = new DatagramSocket();
+ } catch (SocketException e) {
+ log.info(e.toString(), e);
+ return;
+ }
+ if (!handleOptions(buffer)) {
+ return;
+ }
+ doRrq(filename);
+ }
+
+
+ /**
+ * Description of the Method
+ *
+ *@param s Description of the Parameter
+ *@param r Description of the Parameter
+ *@param prefix Description of the Parameter
+ *@param proxy Description of the Parameter
+ *@param timeout Description of the Parameter
+ */
+ public static void handleRequest(DatagramSocket s, DatagramPacket r, String prefix, String proxy, int timeout) {
+ log.info("Connection from " + r.getAddress().getCanonicalHostName() + ":" + r.getPort());
+ ByteBuffer buffer = ByteBuffer.wrap(r.getData(), r.getOffset(), r.getLength() - r.getOffset());
+ if (buffer.getShort() != TFTP_RRQ) {
+ DatagramPacket error = new DatagramPacket(new byte[MTU], MTU);
+ ByteBuffer rbuf = ByteBuffer.wrap(error.getData());
+ rbuf.putShort(TFTP_ERROR).putShort(ERR_ILLOP).put("Illegal operation".getBytes());
+ error.setLength(rbuf.position());
+ try {
+ s.send(error);
+ } catch (Exception e) {
+ log.info(e.toString(), e);
+ }
+ return;
+ }
+ // fork thread
+ new Thread(new T2hproxy(r.getAddress(), r.getPort(), r.getData(), prefix, proxy, timeout)).start();
+ }
+
+
+ /**
+ * The main program for the T2hproxy class
+ *
+ *@param argv The command line arguments
+ *@exception IOException Description of the Exception
+ */
+ public static void main(String[] argv) throws IOException {
+ log.info(T2hproxy.NAME + "." + T2hproxy.VERSION);
+ int port = Integer.getInteger(T2hproxy.NAME + ".port", 69).intValue();
+ String prefix = System.getProperty(T2hproxy.NAME + ".prefix", "http://localhost/");
+ String proxy = System.getProperty(T2hproxy.NAME + ".proxy");
+ int timeout = Integer.getInteger(T2hproxy.NAME + ".timeout", 5000).intValue();
+ String propfile = System.getProperty(T2hproxy.NAME + ".properties");
+ if (propfile != null) {
+ FileInputStream pf = new FileInputStream(propfile);
+ Properties p = new Properties(System.getProperties());
+ p.load(pf);
+ // set the system properties
+ System.setProperties(p);
+ }
+ DatagramSocket requestsocket;
+ try {
+ requestsocket = new DatagramSocket(port);
+ } catch (SocketException e) {
+ log.info(e.toString(), e);
+ return;
+ }
+ DatagramPacket request = new DatagramPacket(new byte[MTU], MTU);
+ for (; ; ) {
+ try {
+ requestsocket.receive(request);
+ handleRequest(requestsocket, request, prefix, proxy, timeout);
+ } catch (Exception e) {
+ log.info(e.toString(), e);
+ }
+ }
+ }
+}
diff --git a/gpxe/contrib/t2hproxy/build.xml b/gpxe/contrib/t2hproxy/build.xml
new file mode 100644
index 00000000..5494ab96
--- /dev/null
+++ b/gpxe/contrib/t2hproxy/build.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!--
+ Build file for T2hproxy
+-->
+<project name="T2hproxy" default="jar" basedir=".">
+ <target name="compile">
+ <javac fork="true" srcdir="." destdir="." />
+ </target>
+
+ <target name="jar" depends="compile">
+ <jar jarfile="T2hproxy.jar" basedir="."
+ includes="T2hproxy.class">
+ <manifest>
+ <attribute name="Main-Class" value="T2hproxy" />
+ </manifest>
+ </jar>
+ </target>
+</project>
+
diff --git a/gpxe/contrib/t2hproxy/runT2proxy.sh b/gpxe/contrib/t2hproxy/runT2proxy.sh
new file mode 100755
index 00000000..d7fc0d2d
--- /dev/null
+++ b/gpxe/contrib/t2hproxy/runT2proxy.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# If the httpclient and logging jars are not in the standard directories
+# edit and uncomment
+# CP='-cp /usr/local/lib/commons-httpclient-2.0-rc1.jar:/usr/local/lib/commons-logging-api.jar:/usr/local/lib/commons-logging.jar'
+
+# Edit and uncomment to use an alternate port
+# PORT='-DT2hproxy.port=1069'
+PREFIX='-DT2hproxy.prefix=http://localhost/'
+# Edit and uncomment to use a proxy
+# PROXY='-DT2hproxy.proxy=localhost:3128'
+# These T2hproxy properties can be put in a file and read in all at once
+# PROPERTIES='-DT2hproxy.properties=t2hproxy.prop
+
+exec java -jar $CP $PORT $PREFIX $PROXY $PROPERTIES T2hproxy.jar
diff --git a/gpxe/contrib/t2hproxy/t2hproxy.pl b/gpxe/contrib/t2hproxy/t2hproxy.pl
new file mode 100755
index 00000000..4fc01781
--- /dev/null
+++ b/gpxe/contrib/t2hproxy/t2hproxy.pl
@@ -0,0 +1,174 @@
+#!/usr/bin/perl -w
+#
+# tftp to http proxy
+# Copyright 2003 Ken Yap
+# Released under GPL2
+#
+
+require 5.8.0; # needs constant and the pack Z format behaviour
+
+use bytes; # to forestall Unicode interpretation of strings
+use strict;
+
+use Getopt::Long;
+use Socket;
+use Sys::Hostname;
+use Sys::Syslog;
+use LWP;
+use POSIX 'setsid';
+
+use constant PROGNAME => 't2hproxy';
+use constant VERSION => '0.1';
+
+use constant ETH_DATA_LEN => 1500;
+use constant {
+ TFTP_RRQ => 1, TFTP_WRQ => 2, TFTP_DATA => 3, TFTP_ACK => 4,
+ TFTP_ERROR => 5, TFTP_OACK => 6
+};
+use constant {
+ E_UNDEF => 0, E_FNF => 1, E_ACC => 2, E_DISK => 3, E_ILLOP => 4,
+ E_UTID => 5, E_FEXIST => 6, E_NOUSER => 7
+};
+
+use vars qw($prefix $proxy $sockh $timeout %options $tsize $bsize);
+
+# We can't use die because xinetd will think something's wrong
+sub log_and_exit ($) {
+ syslog('info', $_[0]);
+ exit;
+}
+
+sub what_source ($) {
+ my ($port, $saddr) = sockaddr_in($_[0]);
+ my $host = gethostbyaddr($saddr, AF_INET);
+ return ($host, $port);
+}
+
+sub send_error ($$$) {
+ my ($iaddr, $error, $message) = @_;
+ # error packets don't get acked
+ send(STDOUT, pack('nna*', TFTP_ERROR, $error, $message), 0, $iaddr);
+}
+
+sub send_ack_retry ($$$$$) {
+ my ($iaddr, $udptimeout, $maxretries, $blockno, $sendfunc) = @_;
+RETRY:
+ while ($maxretries-- > 0) {
+ &$sendfunc;
+ my $rin = '';
+ my $rout = '';
+ vec($rin, fileno($sockh), 1) = 1;
+ do {
+ my ($fds, $timeleft) = select($rout = $rin, undef, undef, $udptimeout);
+ last if ($fds <= 0);
+ my $ack;
+ my $theiripaddr = recv($sockh, $ack, 256, 0);
+ # check it's for us
+ if ($theiripaddr eq $iaddr) {
+ my ($opcode, $ackblock) = unpack('nn', $ack);
+ return (0) if ($opcode == TFTP_ERROR);
+ # check that the right block was acked
+ if ($ackblock == $blockno) {
+ return (1);
+ } else {
+ syslog('info', "Resending block $blockno");
+ next RETRY;
+ }
+ }
+ # stray packet for some other server instance
+ send_error($theiripaddr, E_UTID, 'Wrong TID');
+ } while (1);
+ }
+ return (0);
+}
+
+sub handle_options ($$) {
+ my ($iaddr, $operand) = @_;
+ while ($operand ne '') {
+ my ($key, $value) = unpack('Z*Z*', $operand);
+ $options{$key} = $value;
+ syslog('info', "$key=$value");
+ $operand = substr($operand, length($key) + length($value) + 2);
+ }
+ my $optstr = '';
+ if (exists($options{blksize})) {
+ $bsize = $options{blksize};
+ $bsize = 512 if ($bsize < 512);
+ $bsize = 1432 if ($bsize > 1432);
+ $optstr .= pack('Z*Z*', 'blksize', $bsize . '');
+ }
+ # OACK expects an ack for block 0
+ log_and_exit('Abort received or retransmit limit reached, exiting')
+ unless send_ack_retry($iaddr, 2, 5, 0,
+ sub { send($sockh, pack('na*', TFTP_OACK, $optstr), 0, $iaddr); });
+}
+
+sub http_get ($) {
+ my ($url) = @_;
+ syslog('info', "GET $url");
+ my $ua = LWP::UserAgent->new;
+ $ua->timeout($timeout);
+ $ua->proxy(['http', 'ftp'], $proxy) if (defined($proxy) and $proxy);
+ my $req = HTTP::Request->new(GET => $url);
+ my $res = $ua->request($req);
+ return ($res->is_success, $res->status_line, $res->content_ref);
+}
+
+sub send_file ($$) {
+ my ($iaddr, $contentref) = @_;
+ my $blockno = 1;
+ my $data;
+ do {
+ $blockno &= 0xffff;
+ $data = substr($$contentref, ($blockno - 1) * $bsize, $bsize);
+ # syslog('info', "Block $blockno length " . length($data));
+ log_and_exit('Abort received or retransmit limit reached, exiting')
+ unless send_ack_retry($iaddr, 2, 5, $blockno,
+ sub { send($sockh, pack('nna*', TFTP_DATA, $blockno, $data), 0, $iaddr); });
+ $blockno++;
+ } while (length($data) >= $bsize);
+}
+
+sub do_rrq ($$) {
+ my ($iaddr, $packetref) = @_;
+ # fork and handle request in child so that *inetd can continue
+ # to serve incoming requests
+ defined(my $pid = fork) or log_and_exit("Can't fork: $!");
+ exit if $pid; # parent exits
+ setsid or log_and_exit("Can't start a new session: $!");
+ socket(SOCK, PF_INET, SOCK_DGRAM, getprotobyname('udp')) or log_and_exit('Cannot create UDP socket');
+ $sockh = *SOCK{IO};
+ my ($opcode, $operand) = unpack('na*', $$packetref);
+ my ($filename, $mode) = unpack('Z*Z*', $operand);
+ syslog('info', "RRQ $filename $mode");
+ my $length = length($filename) + length($mode) + 2;
+ $operand = substr($operand, $length);
+ handle_options($iaddr, $operand) if ($operand ne '');
+ my ($success, $status_line, $result) = http_get($prefix . $filename);
+ syslog('info', $status_line);
+ if ($success) {
+ send_file($iaddr, $result);
+ } else {
+ send_error($iaddr, E_FNF, $status_line);
+ }
+}
+
+$prefix = 'http://localhost/';
+$timeout = 60;
+GetOptions('prefix=s' => \$prefix,
+ 'proxy=s' => \$proxy,
+ 'timeout=i' => \$timeout);
+$bsize = 512;
+openlog(PROGNAME, 'cons,pid', 'user');
+syslog('info', PROGNAME . ' version ' . VERSION);
+my $packet;
+my $theiriaddr = recv(STDIN, $packet, ETH_DATA_LEN, 0);
+my ($host, $port) = what_source($theiriaddr);
+syslog('info', "Connection from $host:$port");
+my $opcode = unpack('n', $packet);
+if ($opcode == TFTP_RRQ) {
+ do_rrq($theiriaddr, \$packet);
+} else { # anything else is an error
+ send_error($theiriaddr, E_ILLOP, 'Illegal operation');
+}
+exit 0;
diff --git a/gpxe/contrib/t2hproxy/t2hproxy.xinetd b/gpxe/contrib/t2hproxy/t2hproxy.xinetd
new file mode 100644
index 00000000..ea6a03f1
--- /dev/null
+++ b/gpxe/contrib/t2hproxy/t2hproxy.xinetd
@@ -0,0 +1,29 @@
+# Description: tftp to http proxy
+# A sample config file for xinetd, edit and put in /etc/xinetd.d
+# then killall -HUP xinetd, or restart xinetd
+
+service t2hproxy
+{
+ type = UNLISTED
+ id = t2hproxy
+ socket_type = dgram
+ protocol = udp
+#
+# The pathname to where you have installed it
+#
+ server = /usr/local/sbin/t2hproxy.pl
+#
+# If your filenames don't start with /, then the trailing
+# slash is needed
+#
+ server_args = --prefix http://localhost/
+#
+# --proxy http://proxyhost:3128/ can also be appended
+#
+ log_type = FILE /var/log/t2hproxy.log
+ user = nobody
+ wait = yes
+ instances = 10
+ disable = no
+ port = 69
+}
diff --git a/gpxe/contrib/tftp/Makefile b/gpxe/contrib/tftp/Makefile
new file mode 100644
index 00000000..bd427cd0
--- /dev/null
+++ b/gpxe/contrib/tftp/Makefile
@@ -0,0 +1,56 @@
+#
+# Copyright (c) 1987 Regents of the University of California.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are permitted
+# provided that the above copyright notice and this paragraph are
+# duplicated in all such forms and that any documentation,
+# advertising materials, and other materials related to such
+# distribution and use acknowledge that the software was developed
+# by the University of California, Berkeley. The name of the
+# University may not be used to endorse or promote products derived
+# from this software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+#
+# @(#)Makefile 5.8 (Berkeley) 9/20/88
+#
+# We override /usr/include/arpa/tftp.h with our own because
+# we want tu_block to be unsigned short, not short as on most platforms
+#
+CFLAGS= -I. -O2 -Dsin=sin_x
+SRCS= main.c tftp.c tftpsubs.c tftpd.c
+OBJS= main.o tftp.o tftpsubs.o
+DOBJS= tftpd.o tftpsubs.o
+CC= gcc
+LIBS= # -linet
+
+all: tftp tftpd
+
+tftp: ${OBJS}
+ ${CC} -o $@ ${CFLAGS} ${OBJS} # -linet
+
+tftpd: ${DOBJS}
+ ${CC} -o $@ ${CFLAGS} ${DOBJS} ${LIBS}
+
+clean:
+ rm -f ${OBJS} ${DOBJS} core tftp tftpd
+
+cleandir: clean
+ rm -f tags .depend
+
+depend: ${SRCS}
+ mkdep ${CFLAGS} ${SRCS}
+
+install:
+ install -s -o root -g root -m 755 tftp /usr/bin/tftp
+ install -c -o root -g root -m 444 tftp.1 /usr/man/man1
+ install -s -o root -g root -m 755 tftpd /usr/sbin/in.tftpd
+ install -c -o root -g root -m 444 tftpd.8 /usr/man/man8
+
+lint: ${SRCS}
+ lint ${CFLAGS} ${SRCS}
+
+tags: ${SRCS}
+ ctags ${SRCS}
diff --git a/gpxe/contrib/tftp/README b/gpxe/contrib/tftp/README
new file mode 100644
index 00000000..e4957202
--- /dev/null
+++ b/gpxe/contrib/tftp/README
@@ -0,0 +1,28 @@
+This is a copy of the TFTP client as available from
+ftp://sunsite.unc.edu/pub/linux/system/Network/file-transfer; I
+modified the code, so that it understands RFC1782 and RFC1783
+extensions to the TFTP protocol. This allows for negotating an
+extended transfer block size of up to 1432 bytes (as oppossed to the
+standard 512 bytes). On busy networks, this will result in
+considerably improved throughput and less load on the network.
+
+For further information and for licensing conditions, please have a
+look at the header of the source files.
+
+Markus Gutschke <gutschk@math.uni-muenster.de>
+
+This is a copy of the TFTP server as available from
+ftp://sunsite.unc.edu/pub/linux/system/Network/file-transfer; I
+modified the code, so that it understands RFC1782 and RFC1783
+extensions to the TFTP protocol. This allows for negotating an
+extended transfer block size of up to 1432 bytes (as oppossed to the
+standard 512 bytes). On busy networks, this will result in
+considerably improved throughput and less load on the network.
+
+I also added two command line options for changing the root directory
+and for enabling debugging output.
+
+For further information and for licensing conditions, please have a
+look at the header of the source files.
+
+Markus Gutschke <gutschk@math.uni-muenster.de>
diff --git a/gpxe/contrib/tftp/arpa/tftp.h b/gpxe/contrib/tftp/arpa/tftp.h
new file mode 100644
index 00000000..0904407c
--- /dev/null
+++ b/gpxe/contrib/tftp/arpa/tftp.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tftp.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _ARPA_TFTP_H
+#define _ARPA_TFTP_H 1
+
+/*
+ * Trivial File Transfer Protocol (IEN-133)
+ */
+#define SEGSIZE 512 /* data segment size */
+
+/*
+ * Packet types.
+ */
+#define RRQ 01 /* read request */
+#define WRQ 02 /* write request */
+#define DATA 03 /* data packet */
+#define ACK 04 /* acknowledgement */
+#define ERROR 05 /* error code */
+
+struct tftphdr {
+ short th_opcode; /* packet type */
+ union {
+ unsigned short tu_block; /* block # */
+ short tu_code; /* error code */
+ char tu_stuff[1]; /* request packet stuff */
+ } th_u;
+ char th_data[1]; /* data or error string */
+};
+
+#define th_block th_u.tu_block
+#define th_code th_u.tu_code
+#define th_stuff th_u.tu_stuff
+#define th_msg th_data
+
+/*
+ * Error codes.
+ */
+#define EUNDEF 0 /* not defined */
+#define ENOTFOUND 1 /* file not found */
+#define EACCESS 2 /* access violation */
+#define ENOSPACE 3 /* disk full or allocation exceeded */
+#define EBADOP 4 /* illegal TFTP operation */
+#define EBADID 5 /* unknown transfer ID */
+#define EEXISTS 6 /* file already exists */
+#define ENOUSER 7 /* no such user */
+
+#endif /* arpa/tftp.h */
diff --git a/gpxe/contrib/tftp/main.c b/gpxe/contrib/tftp/main.c
new file mode 100644
index 00000000..ca4427a1
--- /dev/null
+++ b/gpxe/contrib/tftp/main.c
@@ -0,0 +1,684 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 5.8 (Berkeley) 10/11/88";
+#endif /* not lint */
+
+/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
+
+/*
+ * TFTP User Program -- Command Interface.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <ctype.h>
+#include <netdb.h>
+
+#define TIMEOUT 5 /* secs between rexmt's */
+
+struct sockaddr_in sin;
+int f;
+short port;
+int trace;
+int verbose;
+int connected;
+char mode[32];
+char line[200];
+int margc;
+char *margv[20];
+char *prompt = "tftp";
+jmp_buf toplevel;
+void intr(int);
+struct servent *sp;
+
+int segsize = 512;
+
+int quit(), help(), setverbose(), settrace(), status();
+int get(), put(), setpeer(), modecmd(), setrexmt(), settimeout();
+int setbinary(), setascii(), setblocksize();
+
+#define HELPINDENT (sizeof("connect"))
+
+struct cmd {
+ char *name;
+ char *help;
+ int (*handler)();
+};
+
+char vhelp[] = "toggle verbose mode";
+char thelp[] = "toggle packet tracing";
+char chelp[] = "connect to remote tftp";
+char qhelp[] = "exit tftp";
+char hhelp[] = "print help information";
+char shelp[] = "send file";
+char rhelp[] = "receive file";
+char mhelp[] = "set file transfer mode";
+char sthelp[] = "show current status";
+char xhelp[] = "set per-packet retransmission timeout";
+char ihelp[] = "set total retransmission timeout";
+char ashelp[] = "set mode to netascii";
+char bnhelp[] = "set mode to octet";
+char bshelp[] = "set blocksize for next transfer";
+
+struct cmd cmdtab[] = {
+ { "connect", chelp, setpeer },
+ { "mode", mhelp, modecmd },
+ { "put", shelp, put },
+ { "get", rhelp, get },
+ { "quit", qhelp, quit },
+ { "verbose", vhelp, setverbose },
+ { "trace", thelp, settrace },
+ { "status", sthelp, status },
+ { "binary", bnhelp, setbinary },
+ { "ascii", ashelp, setascii },
+ { "rexmt", xhelp, setrexmt },
+ { "timeout", ihelp, settimeout },
+ { "blocksize", bshelp, setblocksize },
+ { "?", hhelp, help },
+ 0
+};
+
+struct cmd *getcmd();
+char *tail();
+char *index();
+char *rindex();
+
+main(argc, argv)
+ char *argv[];
+{
+ struct sockaddr_in sin;
+ int top;
+
+ sp = getservbyname("tftp", "udp");
+ if (sp == 0) {
+ fprintf(stderr, "tftp: udp/tftp: unknown service\n");
+ exit(1);
+ }
+ f = socket(AF_INET, SOCK_DGRAM, 0);
+ if (f < 0) {
+ perror("tftp: socket");
+ exit(3);
+ }
+ bzero((char *)&sin, sizeof (sin));
+ sin.sin_family = AF_INET;
+ if (bind(f, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ perror("tftp: bind");
+ exit(1);
+ }
+ strcpy(mode, "netascii");
+ signal(SIGINT, intr);
+ if (argc > 1) {
+ if (setjmp(toplevel) != 0)
+ exit(0);
+ setpeer(argc, argv);
+ }
+ top = setjmp(toplevel) == 0;
+ for (;;)
+ command(top);
+}
+
+char hostname[100];
+
+setpeer(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct hostent *host;
+
+ if (argc < 2) {
+ strcpy(line, "Connect ");
+ printf("(to) ");
+ fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, stdin);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc > 3) {
+ printf("usage: %s host-name [port]\n", argv[0]);
+ return;
+ }
+ host = gethostbyname(argv[1]);
+ if (host) {
+ sin.sin_family = host->h_addrtype;
+ bcopy(host->h_addr, &sin.sin_addr, host->h_length);
+ strcpy(hostname, host->h_name);
+ } else {
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = inet_addr(argv[1]);
+ if (sin.sin_addr.s_addr == -1) {
+ connected = 0;
+ printf("%s: unknown host\n", argv[1]);
+ return;
+ }
+ strcpy(hostname, argv[1]);
+ }
+ port = sp->s_port;
+ if (argc == 3) {
+ port = atoi(argv[2]);
+ if (port < 0) {
+ printf("%s: bad port number\n", argv[2]);
+ connected = 0;
+ return;
+ }
+ port = htons(port);
+ }
+ connected = 1;
+}
+
+struct modes {
+ char *m_name;
+ char *m_mode;
+} modes[] = {
+ { "ascii", "netascii" },
+ { "netascii", "netascii" },
+ { "binary", "octet" },
+ { "image", "octet" },
+ { "octet", "octet" },
+/* { "mail", "mail" }, */
+ { 0, 0 }
+};
+
+modecmd(argc, argv)
+ char *argv[];
+{
+ register struct modes *p;
+ char *sep;
+
+ if (argc < 2) {
+ printf("Using %s mode to transfer files.\n", mode);
+ return;
+ }
+ if (argc == 2) {
+ for (p = modes; p->m_name; p++)
+ if (strcmp(argv[1], p->m_name) == 0)
+ break;
+ if (p->m_name) {
+ setmode(p->m_mode);
+ return;
+ }
+ printf("%s: unknown mode\n", argv[1]);
+ /* drop through and print usage message */
+ }
+
+ printf("usage: %s [", argv[0]);
+ sep = " ";
+ for (p = modes; p->m_name; p++) {
+ printf("%s%s", sep, p->m_name);
+ if (*sep == ' ')
+ sep = " | ";
+ }
+ printf(" ]\n");
+ return;
+}
+
+setbinary(argc, argv)
+char *argv[];
+{ setmode("octet");
+}
+
+setascii(argc, argv)
+char *argv[];
+{ setmode("netascii");
+}
+
+setmode(newmode)
+char *newmode;
+{
+ strcpy(mode, newmode);
+ if (verbose)
+ printf("mode set to %s\n", mode);
+}
+
+
+/*
+ * Send file(s).
+ */
+put(argc, argv)
+ char *argv[];
+{
+ int fd;
+ register int n;
+ register char *cp, *targ;
+
+ if (argc < 2) {
+ strcpy(line, "send ");
+ printf("(file) ");
+ fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, stdin);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ putusage(argv[0]);
+ return;
+ }
+ targ = argv[argc - 1];
+ if (index(argv[argc - 1], ':')) {
+ char *cp;
+ struct hostent *hp;
+
+ for (n = 1; n < argc - 1; n++)
+ if (index(argv[n], ':')) {
+ putusage(argv[0]);
+ return;
+ }
+ cp = argv[argc - 1];
+ targ = index(cp, ':');
+ *targ++ = 0;
+ hp = gethostbyname(cp);
+ if (hp == NULL) {
+ fprintf(stderr, "tftp: %s: ", cp);
+ herror((char *)NULL);
+ return;
+ }
+ bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
+ sin.sin_family = hp->h_addrtype;
+ connected = 1;
+ strcpy(hostname, hp->h_name);
+ }
+ if (!connected) {
+ printf("No target machine specified.\n");
+ return;
+ }
+ if (argc < 4) {
+ cp = argc == 2 ? tail(targ) : argv[1];
+ fd = open(cp, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: "); perror(cp);
+ return;
+ }
+ if (verbose)
+ printf("putting %s to %s:%s [%s]\n",
+ cp, hostname, targ, mode);
+ sin.sin_port = port;
+ sendfile(fd, targ, mode);
+ return;
+ }
+ /* this assumes the target is a directory */
+ /* on a remote unix system. hmmmm. */
+ cp = index(targ, '\0');
+ *cp++ = '/';
+ for (n = 1; n < argc - 1; n++) {
+ strcpy(cp, tail(argv[n]));
+ fd = open(argv[n], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: "); perror(argv[n]);
+ continue;
+ }
+ if (verbose)
+ printf("putting %s to %s:%s [%s]\n",
+ argv[n], hostname, targ, mode);
+ sin.sin_port = port;
+ sendfile(fd, targ, mode);
+ }
+}
+
+putusage(s)
+ char *s;
+{
+ printf("usage: %s file ... host:target, or\n", s);
+ printf(" %s file ... target (when already connected)\n", s);
+}
+
+/*
+ * Receive file(s).
+ */
+get(argc, argv)
+ char *argv[];
+{
+ int fd;
+ register int n;
+ register char *cp;
+ char *src;
+
+ if (argc < 2) {
+ strcpy(line, "get ");
+ printf("(files) ");
+ fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, stdin);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ getusage(argv[0]);
+ return;
+ }
+ if (!connected) {
+ for (n = 1; n < argc ; n++)
+ if (index(argv[n], ':') == 0) {
+ getusage(argv[0]);
+ return;
+ }
+ }
+ for (n = 1; n < argc ; n++) {
+ src = index(argv[n], ':');
+ if (src == NULL)
+ src = argv[n];
+ else {
+ struct hostent *hp;
+
+ *src++ = 0;
+ hp = gethostbyname(argv[n]);
+ if (hp == NULL) {
+ fprintf(stderr, "tftp: %s: ", argv[n]);
+ herror((char *)NULL);
+ continue;
+ }
+ bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
+ sin.sin_family = hp->h_addrtype;
+ connected = 1;
+ strcpy(hostname, hp->h_name);
+ }
+ if (argc < 4) {
+ cp = argc == 3 ? argv[2] : tail(src);
+ fd = creat(cp, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: "); perror(cp);
+ return;
+ }
+ if (verbose)
+ printf("getting from %s:%s to %s [%s]\n",
+ hostname, src, cp, mode);
+ sin.sin_port = port;
+ recvfile(fd, src, mode);
+ break;
+ }
+ cp = tail(src); /* new .. jdg */
+ fd = creat(cp, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: "); perror(cp);
+ continue;
+ }
+ if (verbose)
+ printf("getting from %s:%s to %s [%s]\n",
+ hostname, src, cp, mode);
+ sin.sin_port = port;
+ recvfile(fd, src, mode);
+ }
+}
+
+getusage(s)
+char * s;
+{
+ printf("usage: %s host:file host:file ... file, or\n", s);
+ printf(" %s file file ... file if connected\n", s);
+}
+
+int rexmtval = TIMEOUT;
+
+setrexmt(argc, argv)
+ char *argv[];
+{
+ int t;
+
+ if (argc < 2) {
+ strcpy(line, "Rexmt-timeout ");
+ printf("(value) ");
+ fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, stdin);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc != 2) {
+ printf("usage: %s value\n", argv[0]);
+ return;
+ }
+ t = atoi(argv[1]);
+ if (t < 0)
+ printf("%d: bad value\n", t);
+ else
+ rexmtval = t;
+}
+
+int maxtimeout = 5 * TIMEOUT;
+
+settimeout(argc, argv)
+ char *argv[];
+{
+ int t;
+
+ if (argc < 2) {
+ strcpy(line, "Maximum-timeout ");
+ printf("(value) ");
+ fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, stdin);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc != 2) {
+ printf("usage: %s value\n", argv[0]);
+ return;
+ }
+ t = atoi(argv[1]);
+ if (t < 0)
+ printf("%d: bad value\n", t);
+ else
+ maxtimeout = t;
+}
+
+status(argc, argv)
+ char *argv[];
+{
+ if (connected)
+ printf("Connected to %s.\n", hostname);
+ else
+ printf("Not connected.\n");
+ printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
+ verbose ? "on" : "off", trace ? "on" : "off");
+ printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
+ rexmtval, maxtimeout);
+}
+
+void intr(int sig)
+{
+ signal(SIGALRM, SIG_IGN);
+ alarm(0);
+ longjmp(toplevel, -1);
+}
+
+char *
+tail(filename)
+ char *filename;
+{
+ register char *s;
+
+ while (*filename) {
+ s = rindex(filename, '/');
+ if (s == NULL)
+ break;
+ if (s[1])
+ return (s + 1);
+ *s = '\0';
+ }
+ return (filename);
+}
+
+/*
+ * Command parser.
+ */
+command(top)
+ int top;
+{
+ register struct cmd *c;
+
+ if (!top)
+ putchar('\n');
+ for (;;) {
+ printf("%s> ", prompt);
+ if (fgets(line, sizeof(line), stdin) == 0) {
+ if (feof(stdin)) {
+ quit();
+ } else {
+ continue;
+ }
+ }
+ if (line[0] == 0)
+ continue;
+ makeargv();
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ (*c->handler)(margc, margv);
+ }
+}
+
+struct cmd *
+getcmd(name)
+ register char *name;
+{
+ register char *p, *q;
+ register struct cmd *c, *found;
+ register int nmatches, longest;
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; p = c->name; c++) {
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return (c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return ((struct cmd *)-1);
+ return (found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+makeargv()
+{
+ register char *cp;
+ register char **argp = margv;
+
+ margc = 0;
+ for (cp = line; *cp;) {
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *argp++ = cp;
+ margc += 1;
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *cp++ = '\0';
+ }
+ *argp++ = 0;
+}
+
+/*VARARGS*/
+quit()
+{
+ exit(0);
+}
+
+/*
+ * Help command.
+ */
+help(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct cmd *c;
+
+ if (argc == 1) {
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c->name; c++)
+ printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
+ return;
+ }
+ while (--argc > 0) {
+ register char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%s\n", c->help);
+ }
+}
+
+/*VARARGS*/
+settrace()
+{
+ trace = !trace;
+ printf("Packet tracing %s.\n", trace ? "on" : "off");
+}
+
+/*VARARGS*/
+setverbose()
+{
+ verbose = !verbose;
+ printf("Verbose mode %s.\n", verbose ? "on" : "off");
+}
+
+setblocksize(argc, argv)
+ char *argv[];
+{
+ int t;
+
+ if (argc < 2) {
+ strcpy(line, "blocksize ");
+ printf("(value) ");
+ fgets(&line[strlen(line)], sizeof(line) - strlen(line) - 1, stdin);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc != 2) {
+ printf("usage: %s value\n", argv[0]);
+ return;
+ }
+ t = atoi(argv[1]);
+ if (t < 8 || t > 1432)
+ printf("%d: bad value\n", t);
+ else
+ segsize = t;
+}
diff --git a/gpxe/contrib/tftp/tftp.1 b/gpxe/contrib/tftp/tftp.1
new file mode 100644
index 00000000..fc235b2a
--- /dev/null
+++ b/gpxe/contrib/tftp/tftp.1
@@ -0,0 +1,159 @@
+.\" Copyright (c) 1986 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley. The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" @(#)tftp.1 5.3 (Berkeley) 9/20/88
+.\"
+.TH TFTP 1 "September 20, 1988"
+.UC 6
+.SH NAME
+tftp \- trivial file transfer program
+.SH SYNOPSIS
+.B tftp
+[
+host
+]
+.SH DESCRIPTION
+.I Tftp
+is the user interface to the Internet TFTP
+(Trivial File Transfer Protocol),
+which allows users to transfer files to and from a remote machine.
+The remote
+.I host
+may be specified on the command line, in which case
+.I tftp
+uses
+.I host
+as the default host for future transfers (see the
+.B connect
+command below).
+.SH COMMANDS
+Once
+.I tftp
+is running, it issues the prompt
+.B tftp>
+and recognizes the following commands:
+.TP
+\fBconnect\fP \fIhost-name\fP [ \fIport\fP ]
+Set the
+.I host
+(and optionally
+.IR port )
+for transfers.
+Note that the TFTP protocol, unlike the FTP protocol,
+does not maintain connections betweeen transfers; thus, the
+.I connect
+command does not actually create a connection,
+but merely remembers what host is to be used for transfers.
+You do not have to use the
+.I connect
+command; the remote host can be specified as part of the
+.I get
+or
+.I put
+commands.
+.TP
+\fBmode\fP \fItransfer-mode\fP
+Set the mode for transfers;
+.I transfer-mode
+may be one of
+.IR ascii
+or
+.IR binary .
+The default is
+.IR ascii .
+.TP
+\fBput\fP \fIfile\fP
+.ns
+.TP
+\fBput\fP \fIlocalfile remotefile\fP
+.ns
+.TP
+\fBput\fP \fIfile1 file2 ... fileN remote-directory\fP
+Put a file or set of files to the specified
+remote file or directory.
+The destination
+can be in one of two forms:
+a filename on the remote host, if the host has already been specified,
+or a string of the form
+.I host:filename
+to specify both a host and filename at the same time.
+If the latter form is used,
+the hostname specified becomes the default for future transfers.
+If the remote-directory form is used, the remote host is
+assumed to be a
+.I UNIX
+machine.
+.TP
+\fBget\fP \fIfilename\fP
+.ns
+.TP
+\fBget\fP \fIremotename\fP \fIlocalname\fP
+.ns
+.TP
+\fBget\fP \fIfile1\fP \fIfile2\fP ... \fIfileN\fP
+Get a file or set of files from the specified
+.IR sources .
+.I Source
+can be in one of two forms:
+a filename on the remote host, if the host has already been specified,
+or a string of the form
+.I host:filename
+to specify both a host and filename at the same time.
+If the latter form is used,
+the last hostname specified becomes the default for future transfers.
+.TP
+.B quit
+Exit
+.IR tftp .
+An end of file also exits.
+.TP
+.B verbose
+Toggle verbose mode.
+.TP
+.B trace
+Toggle packet tracing.
+.TP
+.B status
+Show current status.
+.TP
+\fBrexmt\fP \fIretransmission-timeout\fP
+Set the per-packet retransmission timeout, in seconds.
+.TP
+\fBtimeout\fP \fItotal-transmission-timeout\fP
+Set the total transmission timeout, in seconds.
+.TP
+.B ascii
+Shorthand for "mode ascii"
+.TP
+.B binary
+Shorthand for "mode binary"
+.TP
+\fBblocksize\fP \fItransfer-blocksize\fP
+Set the blocksize that is used for transfers. This assumes that the
+server knows about RFC1782 and RFC1783 extensions to the TFTP
+protocol; automatic fallback is supported and will result in a default
+blocksize of 512 octets.
+.TP
+\fB?\fP \ [ \fIcommand-name\fP ... ]
+Print help information.
+.SH BUGS
+.PP
+Because there is no user-login or validation within
+the
+.I TFTP
+protocol, the remote site will probably have some
+sort of file-access restrictions in place. The
+exact methods are specific to each site and therefore
+difficult to document here.
diff --git a/gpxe/contrib/tftp/tftp.c b/gpxe/contrib/tftp/tftp.c
new file mode 100644
index 00000000..894e535e
--- /dev/null
+++ b/gpxe/contrib/tftp/tftp.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tftp.c 5.7 (Berkeley) 6/29/88";
+#endif /* not lint */
+
+/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
+
+/*
+ * TFTP User Program -- Protocol Machines
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+
+#include <arpa/tftp.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <errno.h>
+#include <setjmp.h>
+
+extern int errno;
+
+extern struct sockaddr_in sin; /* filled in by main */
+extern int f; /* the opened socket */
+extern int trace;
+extern int verbose;
+extern int rexmtval;
+extern int maxtimeout;
+extern int segsize;
+
+#define PKTSIZE (1432+4) /* SEGSIZE+4 */
+char ackbuf[PKTSIZE];
+int timeout;
+jmp_buf toplevel;
+jmp_buf timeoutbuf;
+
+#ifndef OACK
+#define OACK 6
+#endif
+
+void timer(int sig)
+{
+
+ signal(SIGALRM, timer);
+ timeout += rexmtval;
+ if (timeout >= maxtimeout) {
+ printf("Transfer timed out.\n");
+ longjmp(toplevel, -1);
+ }
+ longjmp(timeoutbuf, 1);
+}
+
+strnlen(s, n)
+ char *s;
+ int n;
+{
+ int i = 0;
+
+ while (n-- > 0 && *s++) i++;
+ return(i);
+}
+
+/*
+ * Parse an OACK package and set blocksize accordingly
+ */
+parseoack(cp, sz)
+ char *cp;
+ int sz;
+{
+ int n;
+
+ segsize = 512;
+ while (sz > 0 && *cp) {
+ n = strnlen(cp, sz);
+ if (n == 7 && !strncmp("blksize", cp, 7)) {
+ cp += 8;
+ sz -= 8;
+ if (sz <= 0)
+ break;
+ for (segsize = 0, n = strnlen(cp, sz); n > 0;
+ n--, cp++, sz--) {
+ if (*cp < '0' || *cp > '9')
+ break;
+ segsize = 10*segsize + *cp - '0'; }
+ }
+ cp += n + 1;
+ sz -= n + 1;
+ }
+ if (segsize < 8 || segsize > 1432) {
+ printf("Remote host negotiated illegal blocksize %d\n",
+ segsize);
+ segsize = 512;
+ longjmp(timeoutbuf, -1);
+ }
+}
+
+/*
+ * Send the requested file.
+ */
+sendfile(fd, name, mode)
+ int fd;
+ char *name;
+ char *mode;
+{
+ register struct tftphdr *ap; /* data and ack packets */
+ struct tftphdr *r_init(), *dp;
+ register int size, n;
+ u_short block = 0;
+ register unsigned long amount = 0;
+ struct sockaddr_in from;
+ int fromlen;
+ int convert; /* true if doing nl->crlf conversion */
+ FILE *file;
+
+ startclock(); /* start stat's clock */
+ dp = r_init(); /* reset fillbuf/read-ahead code */
+ ap = (struct tftphdr *)ackbuf;
+ file = fdopen(fd, "r");
+ convert = !strcmp(mode, "netascii");
+
+ signal(SIGALRM, timer);
+ do {
+ if (block == 0)
+ size = makerequest(WRQ, name, dp, mode) - 4;
+ else {
+ /* size = read(fd, dp->th_data, SEGSIZE); */
+ size = readit(file, &dp, convert);
+ if (size < 0) {
+ nak(errno + 100);
+ break;
+ }
+ dp->th_opcode = htons((u_short)DATA);
+ dp->th_block = htons(block);
+ }
+ timeout = 0;
+ (void) setjmp(timeoutbuf);
+send_data:
+ if (trace)
+ tpacket("sent", dp, size + 4);
+ n = sendto(f, dp, size + 4, 0, (struct sockaddr *)&sin,
+ sizeof (sin));
+ if (n != size + 4) {
+ perror("tftp: sendto");
+ goto abort;
+ }
+ if (block) /* do not start reading until the blocksize
+ has been negotiated */
+ read_ahead(file, convert);
+ for ( ; ; ) {
+ alarm(rexmtval);
+ do {
+ fromlen = sizeof (from);
+ n = recvfrom(f, ackbuf, sizeof (ackbuf), 0,
+ (struct sockaddr *)&from,
+ &fromlen);
+ } while (n <= 0);
+ alarm(0);
+ if (n < 0) {
+ perror("tftp: recvfrom");
+ goto abort;
+ }
+ sin.sin_port = from.sin_port; /* added */
+ if (trace)
+ tpacket("received", ap, n);
+ /* should verify packet came from server */
+ ap->th_opcode = ntohs(ap->th_opcode);
+ if (ap->th_opcode == ERROR) {
+ printf("Error code %d: %s\n", ap->th_code,
+ ap->th_msg);
+ goto abort;
+ }
+ if (ap->th_opcode == ACK) {
+ int j;
+
+ ap->th_block = ntohs(ap->th_block);
+
+ if (block == 0) {
+ if (trace)
+ printf("server does not know "
+ "about RFC1782; reset"
+ "ting blocksize\n");
+ segsize = 512;
+ }
+ if (ap->th_block == block) {
+ break;
+ }
+ /* On an error, try to synchronize
+ * both sides.
+ */
+ j = synchnet(f);
+ if (j && trace) {
+ printf("discarded %d packets\n",
+ j);
+ }
+ if (ap->th_block == (block-1)) {
+ goto send_data;
+ }
+ }
+ else if (ap->th_opcode == OACK) {
+ if (block) {
+ printf("protocol violation\n");
+ longjmp(toplevel, -1);
+ }
+ parseoack(&ap->th_stuff, n - 2);
+ break;
+ }
+ }
+ if (block > 0)
+ amount += size;
+ else
+ read_ahead(file, convert);
+ block++;
+ } while (size == segsize || block == 1);
+abort:
+ fclose(file);
+ stopclock();
+ if (amount > 0)
+ printstats("Sent", amount);
+}
+
+/*
+ * Receive a file.
+ */
+recvfile(fd, name, mode)
+ int fd;
+ char *name;
+ char *mode;
+{
+ register struct tftphdr *ap;
+ struct tftphdr *dp, *w_init();
+ register int n, size;
+ u_short block = 1;
+ unsigned long amount = 0;
+ struct sockaddr_in from;
+ int fromlen, firsttrip = 1;
+ FILE *file;
+ int convert; /* true if converting crlf -> lf */
+ int waitforoack = 1;
+
+ startclock();
+ dp = w_init();
+ ap = (struct tftphdr *)ackbuf;
+ file = fdopen(fd, "w");
+ convert = !strcmp(mode, "netascii");
+
+ signal(SIGALRM, timer);
+ do {
+ if (firsttrip) {
+ size = makerequest(RRQ, name, ap, mode);
+ firsttrip = 0;
+ } else {
+ ap->th_opcode = htons((u_short)ACK);
+ ap->th_block = htons(block);
+ size = 4;
+ block++;
+ }
+ timeout = 0;
+ (void) setjmp(timeoutbuf);
+send_ack:
+ if (trace)
+ tpacket("sent", ap, size);
+ if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&sin,
+ sizeof (sin)) != size) {
+ alarm(0);
+ perror("tftp: sendto");
+ goto abort;
+ }
+ if (!waitforoack)
+ write_behind(file, convert);
+ for ( ; ; ) {
+ alarm(rexmtval);
+ do {
+ fromlen = sizeof (from);
+ n = recvfrom(f, dp, PKTSIZE, 0,
+ (struct sockaddr *)&from, &fromlen);
+ } while (n <= 0);
+ alarm(0);
+ if (n < 0) {
+ perror("tftp: recvfrom");
+ goto abort;
+ }
+ sin.sin_port = from.sin_port; /* added */
+ if (trace)
+ tpacket("received", dp, n);
+ /* should verify client address */
+ dp->th_opcode = ntohs(dp->th_opcode);
+ if (dp->th_opcode == ERROR) {
+ printf("Error code %d: %s\n", dp->th_code,
+ dp->th_msg);
+ goto abort;
+ }
+ if (dp->th_opcode == DATA) {
+ int j;
+
+ if (waitforoack) {
+ if (trace)
+ printf("server does not know "
+ "about RFC1782; reset"
+ "ting blocksize\n");
+ waitforoack = 0;
+ segsize = 512;
+ }
+ dp->th_block = ntohs(dp->th_block);
+ if (dp->th_block == block) {
+ break; /* have next packet */
+ }
+ /* On an error, try to synchronize
+ * both sides.
+ */
+ j = synchnet(f);
+ if (j && trace) {
+ printf("discarded %d packets\n", j);
+ }
+ if (dp->th_block == (block-1)) {
+ goto send_ack; /* resend ack */
+ }
+ }
+ else if (dp->th_opcode == OACK) {
+ if (block != 1 || !waitforoack) {
+ printf("protocol violation\n");
+ longjmp(toplevel, -1);
+ }
+ waitforoack = 0;
+ parseoack(&dp->th_stuff, n - 2);
+ ap->th_opcode = htons((u_short)ACK);
+ ap->th_block = htons(0);
+ size = 4;
+ goto send_ack;
+ }
+ }
+ /* size = write(fd, dp->th_data, n - 4); */
+ size = writeit(file, &dp, n - 4, convert);
+ if (size < 0) {
+ nak(errno + 100);
+ break;
+ }
+ amount += size;
+ } while (size == segsize);
+abort: /* ok to ack, since user */
+ ap->th_opcode = htons((u_short)ACK); /* has seen err msg */
+ ap->th_block = htons(block);
+ (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&sin, sizeof (sin));
+ write_behind(file, convert); /* flush last buffer */
+ fclose(file);
+ stopclock();
+ if (amount > 0)
+ printstats("Received", amount);
+}
+
+makerequest(request, name, tp, mode)
+ int request;
+ char *name, *mode;
+ struct tftphdr *tp;
+{
+ register char *cp;
+
+ tp->th_opcode = htons((u_short)request);
+ cp = tp->th_stuff;
+ strcpy(cp, name);
+ cp += strlen(name);
+ *cp++ = '\0';
+ strcpy(cp, mode);
+ cp += strlen(mode);
+ *cp++ = '\0';
+ strcpy(cp, "blksize");
+ cp += 7;
+ *cp++ = '\0';
+ sprintf(cp, "%d", segsize);
+ cp += strlen(cp) + 1;
+ return (cp - (char *)tp);
+}
+
+struct errmsg {
+ int e_code;
+ const char *e_msg;
+} errmsgs[] = {
+ { EUNDEF, "Undefined error code" },
+ { ENOTFOUND, "File not found" },
+ { EACCESS, "Access violation" },
+ { ENOSPACE, "Disk full or allocation exceeded" },
+ { EBADOP, "Illegal TFTP operation" },
+ { EBADID, "Unknown transfer ID" },
+ { EEXISTS, "File already exists" },
+ { ENOUSER, "No such user" },
+ { -1, 0 }
+};
+
+/*
+ * Send a nak packet (error message).
+ * Error code passed in is one of the
+ * standard TFTP codes, or a UNIX errno
+ * offset by 100.
+ */
+nak(error)
+ int error;
+{
+ register struct tftphdr *tp;
+ int length;
+ register struct errmsg *pe;
+/* extern char *sys_errlist[]; */
+
+ tp = (struct tftphdr *)ackbuf;
+ tp->th_opcode = htons((u_short)ERROR);
+ tp->th_code = htons((u_short)error);
+ for (pe = errmsgs; pe->e_code >= 0; pe++)
+ if (pe->e_code == error)
+ break;
+ if (pe->e_code < 0) {
+ pe->e_msg = sys_errlist[error - 100];
+ tp->th_code = EUNDEF;
+ }
+ strcpy(tp->th_msg, pe->e_msg);
+ length = strlen(pe->e_msg) + 4;
+ if (trace)
+ tpacket("sent", tp, length);
+ if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&sin, sizeof (sin))
+ != length)
+ perror("nak");
+}
+
+topts(cp, sz)
+ char *cp;
+ int sz;
+{
+ int n, i = 0;
+
+ while (sz > 0 && *cp) {
+ n = strnlen(cp, sz);
+ if (n > 0) {
+ printf("%s%s=", i++ ? ", " : "", cp);
+ cp += n + 1;
+ sz -= n + 1;
+ if (sz <= 0)
+ break;
+ n = strnlen(cp, sz);
+ if (n > 0)
+ printf("%s", cp);
+ }
+ cp += n + 1;
+ sz -= n + 1;
+ }
+}
+
+tpacket(s, tp, n)
+ char *s;
+ struct tftphdr *tp;
+ int n;
+{
+ static char *opcodes[] =
+ { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
+ register char *cp, *file;
+ u_short op = ntohs(tp->th_opcode);
+ char *index();
+
+ if (op < RRQ || op > OACK)
+ printf("%s opcode=%x ", s, op);
+ else
+ printf("%s %s ", s, opcodes[op]);
+ switch (op) {
+
+ case RRQ:
+ case WRQ:
+ n -= 2;
+ file = cp = tp->th_stuff;
+ cp = index(cp, '\0');
+ printf("<file=%s, mode=%s, opts: ", file, cp + 1);
+ topts(index(cp + 1, '\000') + 1, n - strlen(file)
+ - strlen(cp + 1) - 2);
+ printf(">\n");
+ break;
+
+ case DATA:
+ printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
+ break;
+
+ case ACK:
+ printf("<block=%d>\n", ntohs(tp->th_block));
+ break;
+
+ case ERROR:
+ printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
+ break;
+ case OACK:
+ printf("<");
+ topts(tp->th_stuff, n - 2);
+ printf(">\n");
+ break;
+ }
+}
+
+struct timeval tstart;
+struct timeval tstop;
+struct timezone zone;
+
+startclock() {
+ gettimeofday(&tstart, &zone);
+}
+
+stopclock() {
+ gettimeofday(&tstop, &zone);
+}
+
+printstats(direction, amount)
+char *direction;
+unsigned long amount;
+{
+ double delta;
+ /* compute delta in 1/10's second units */
+ delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
+ ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
+ delta = delta/10.; /* back to seconds */
+ printf("%s %ld bytes in %.1f seconds", direction, amount, delta);
+ if ((verbose) && (delta >= 0.1))
+ printf(" [%.0f bits/sec]", (amount*8.)/delta);
+ putchar('\n');
+}
+
diff --git a/gpxe/contrib/tftp/tftpd.8 b/gpxe/contrib/tftp/tftpd.8
new file mode 100644
index 00000000..6a154c10
--- /dev/null
+++ b/gpxe/contrib/tftp/tftpd.8
@@ -0,0 +1,75 @@
+.\" Copyright (c) 1983 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley. The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" @(#)tftpd.8 6.3 (Berkeley) 9/20/88
+.\"
+.TH TFTPD 8 "September 20, 1988"
+.UC 5
+.SH NAME
+tftpd \- DARPA Trivial File Transfer Protocol server
+.SH SYNOPSIS
+.B /etc/tftpd
+[
+.SM \-c
+<rootdir> ] [
+.SM \-d
+] [
+.SM \-r
+<filter> ]
+.SH DESCRIPTION
+.I Tftpd
+is a server which supports the DARPA Trivial File Transfer
+Protocol.
+The TFTP server operates
+at the port indicated in the ``tftp'' service description;
+see
+.IR services (5).
+The server is normally started by
+.IR inetd (8).
+.PP
+The use of
+.I tftp
+does not require an account or password on the remote system.
+Due to the lack of authentication information,
+.I tftpd
+will allow only publicly readable files to be
+accessed.
+Files may be written only if they already exist and are publicly writable.
+Note that this extends the concept of ``public'' to include
+all users on all hosts that can be reached through the network;
+this may not be appropriate on all systems, and its implications
+should be considered before enabling tftp service.
+The server should have the user ID with the lowest possible privilege.
+.SH OPTIONS
+.TP
+.B \-c
+Pathname of a directory that is considered the rootdirectory for all
+transfers. N.B.
+.I tftpd
+does not actually perform a
+.IR chroot (2)
+call; you should be aware of the security implications and you
+probably should run the server from an unpriviledged account.
+.TP
+.B \-d
+Increased debugging level.
+.TP
+.B \-r
+Pathname of a file that is considered to be a filter program. Whenever
+a client tries to download this file, the filter will be started and
+its output is send to the client. An arbitrary amount of these
+filters can be specified.
+.SH "SEE ALSO"
+tftp(1), inetd(8)
diff --git a/gpxe/contrib/tftp/tftpd.c b/gpxe/contrib/tftp/tftpd.c
new file mode 100644
index 00000000..325a7134
--- /dev/null
+++ b/gpxe/contrib/tftp/tftpd.c
@@ -0,0 +1,742 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tftpd.c 5.8 (Berkeley) 6/18/88";
+#endif /* not lint */
+
+/*
+ * Trivial file transfer protocol server.
+ *
+ * This version includes many modifications by Jim Guyton <guyton@rand-unix>
+ *
+ * Further modifications by Markus Gutschke <gutschk@math.uni-muenster.de>
+ * - RFC1782 option parsing
+ * - RFC1783 extended blocksize
+ * - "-c" option for changing the root directory
+ * - "-d" option for debugging output
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+
+#include <arpa/tftp.h>
+
+#include <alloca.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <setjmp.h>
+#include <syslog.h>
+
+#define TIMEOUT 5
+
+#ifndef OACK
+#define OACK 06
+#endif
+
+#ifndef EOPTNEG
+#define EOPTNEG 8
+#endif
+
+extern int errno;
+struct sockaddr_in sin = { AF_INET };
+int peer;
+int rexmtval = TIMEOUT;
+int maxtimeout = 5*TIMEOUT;
+
+#define PKTSIZE (1432+4) /* SEGSIZE+4 */
+int segsize = SEGSIZE;
+char buf[PKTSIZE];
+char ackbuf[PKTSIZE];
+struct sockaddr_in from;
+int fromlen;
+
+char *rootdir = NULL;
+int debug = 0;
+
+struct filters {
+ struct filters *next;
+ char *fname;
+} *filters = NULL;
+int isfilter = 0;
+
+main(argc, argv)
+ char *argv[];
+{
+ register struct tftphdr *tp;
+ register int n;
+ int on = 1;
+ extern int optind;
+ extern char *optarg;
+
+ openlog(argv[0], LOG_PID, LOG_DAEMON);
+
+ while ((n = getopt(argc, argv, "c:dr:")) >= 0) {
+ switch (n) {
+ case 'c':
+ if (rootdir)
+ goto usage;
+ rootdir = optarg;
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'r': {
+ struct filters *fp = (void *)
+ malloc(sizeof(struct filters) +
+ strlen(optarg) + 1);
+ fp->next = filters;
+ fp->fname = (char *)(fp + 1);
+ strcpy(fp->fname, optarg);
+ filters = fp;
+ break; }
+ default:
+ usage:
+ syslog(LOG_ERR, "Usage: %s [-c chroot] "
+ "[-r readfilter] [-d]\n",
+ argv[0]);
+ exit(1);
+ }
+ }
+ if (argc-optind != 0)
+ goto usage;
+
+ ioctl(0, FIONBIO, &on);
+/* if (ioctl(0, FIONBIO, &on) < 0) {
+ syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
+ exit(1);
+ }
+*/
+ fromlen = sizeof (from);
+ n = recvfrom(0, buf, segsize+4, 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (n < 0) {
+ syslog(LOG_ERR, "recvfrom: %m\n");
+ exit(1);
+ }
+ /*
+ * Now that we have read the message out of the UDP
+ * socket, we fork and exit. Thus, inetd will go back
+ * to listening to the tftp port, and the next request
+ * to come in will start up a new instance of tftpd.
+ *
+ * We do this so that inetd can run tftpd in "wait" mode.
+ * The problem with tftpd running in "nowait" mode is that
+ * inetd may get one or more successful "selects" on the
+ * tftp port before we do our receive, so more than one
+ * instance of tftpd may be started up. Worse, if tftpd
+ * break before doing the above "recvfrom", inetd would
+ * spawn endless instances, clogging the system.
+ */
+ {
+ int pid;
+ int i, j;
+
+ for (i = 1; i < 20; i++) {
+ pid = fork();
+ if (pid < 0) {
+ sleep(i);
+ /*
+ * flush out to most recently sent request.
+ *
+ * This may drop some request, but those
+ * will be resent by the clients when
+ * they timeout. The positive effect of
+ * this flush is to (try to) prevent more
+ * than one tftpd being started up to service
+ * a single request from a single client.
+ */
+ j = sizeof from;
+ i = recvfrom(0, buf, segsize+4, 0,
+ (struct sockaddr *)&from, &j);
+ if (i > 0) {
+ n = i;
+ fromlen = j;
+ }
+ } else {
+ break;
+ }
+ }
+ if (pid < 0) {
+ syslog(LOG_ERR, "fork: %m\n");
+ exit(1);
+ } else if (pid != 0) {
+ exit(0);
+ }
+ }
+ from.sin_family = AF_INET;
+ alarm(0);
+ close(0);
+ close(1);
+ peer = socket(AF_INET, SOCK_DGRAM, 0);
+ if (peer < 0) {
+ syslog(LOG_ERR, "socket: %m\n");
+ exit(1);
+ }
+ if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ syslog(LOG_ERR, "bind: %m\n");
+ exit(1);
+ }
+ if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
+ syslog(LOG_ERR, "connect: %m\n");
+ exit(1);
+ }
+ tp = (struct tftphdr *)buf;
+ tp->th_opcode = ntohs(tp->th_opcode);
+ if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
+ tftp(tp, n);
+ exit(1);
+}
+
+int validate_access();
+int sendfile(), recvfile();
+
+struct formats {
+ char *f_mode;
+ int (*f_validate)();
+ int (*f_send)();
+ int (*f_recv)();
+ int f_convert;
+} formats[] = {
+ { "netascii", validate_access, sendfile, recvfile, 1 },
+ { "octet", validate_access, sendfile, recvfile, 0 },
+#ifdef notdef
+ { "mail", validate_user, sendmail, recvmail, 1 },
+#endif
+ { 0 }
+};
+
+int set_blksize();
+
+struct options {
+ char *o_opt;
+ int (*o_fnc)();
+} options[] = {
+ { "blksize", set_blksize },
+ { 0 }
+};
+
+/*
+ * Set a non-standard block size (c.f. RFC1783)
+ */
+
+set_blksize(val, ret)
+ char *val;
+ char **ret;
+{
+ static char b_ret[5];
+ int sz = atoi(val);
+
+ if (sz < 8) {
+ if (debug)
+ syslog(LOG_ERR, "Requested packetsize %d < 8\n", sz);
+ return(0);
+ } else if (sz > PKTSIZE-4) {
+ if (debug)
+ syslog(LOG_INFO, "Requested packetsize %d > %d\n",
+ sz, PKTSIZE-4);
+ sz = PKTSIZE-4;
+ } else if (debug)
+ syslog(LOG_INFO, "Adjusted packetsize to %d octets\n", sz);
+
+ segsize = sz;
+ sprintf(*ret = b_ret, "%d", sz);
+ return(1);
+}
+
+/*
+ * Parse RFC1782 style options
+ */
+
+do_opt(opt, val, ap)
+ char *opt;
+ char *val;
+ char **ap;
+{
+ struct options *po;
+ char *ret;
+
+ for (po = options; po->o_opt; po++)
+ if (strcasecmp(po->o_opt, opt) == 0) {
+ if (po->o_fnc(val, &ret)) {
+ if (*ap + strlen(opt) + strlen(ret) + 2 >=
+ ackbuf + sizeof(ackbuf)) {
+ if (debug)
+ syslog(LOG_ERR,
+ "Ackbuf overflow\n");
+ nak(ENOSPACE);
+ exit(1);
+ }
+ *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt),
+ '\000')+1, val),
+ '\000')+1;
+ } else {
+ nak(EOPTNEG);
+ exit(1);
+ }
+ break;
+ }
+ if (debug && !po->o_opt)
+ syslog(LOG_WARNING, "Unhandled option: %d = %d\n", opt, val);
+ return;
+}
+
+/*
+ * Handle initial connection protocol.
+ */
+tftp(tp, size)
+ struct tftphdr *tp;
+ int size;
+{
+ register char *cp;
+ int argn = 0, ecode;
+ register struct formats *pf;
+ char *filename, *mode;
+ char *val, *opt;
+ char *ap = ackbuf+2;
+ int isopts;
+
+ ((struct tftphdr *)ackbuf)->th_opcode = ntohs(OACK);
+ filename = cp = tp->th_stuff;
+again:
+ while (cp < buf + size) {
+ if (*cp == '\0')
+ break;
+ cp++;
+ }
+ if (*cp != '\0') {
+ if (debug)
+ syslog(LOG_WARNING, "Received illegal request\n");
+ nak(EBADOP);
+ exit(1);
+ }
+ if (!argn++) {
+ mode = ++cp;
+ goto again;
+ } else {
+ if (debug && argn == 3)
+ syslog(LOG_INFO, "Found RFC1782 style options\n");
+ *(argn & 1 ? &val : &opt) = ++cp;
+ if (argn & 1)
+ do_opt(opt, val, &ap);
+ if (cp < buf + size && *cp != '\000')
+ goto again;
+ }
+
+ for (cp = mode; *cp; cp++)
+ if (isupper(*cp))
+ *cp = tolower(*cp);
+ for (pf = formats; pf->f_mode; pf++)
+ if (strcmp(pf->f_mode, mode) == 0)
+ break;
+ if (pf->f_mode == 0) {
+ if (debug)
+ syslog(LOG_WARNING, "Unknown data format: %s\n", mode);
+ nak(EBADOP);
+ exit(1);
+ }
+
+ if (rootdir) {
+ cp = alloca(strlen(rootdir) + strlen(filename) + 1);
+ if (cp == NULL) {
+ nak(100+ENOMEM);
+ exit(1);
+ }
+ if (*filename != '/') {
+ if (debug)
+ syslog(LOG_ERR,
+ "Filename has to be absolute: %s\n",
+ filename);
+ nak(EACCESS);
+ exit(1);
+ }
+ filename = strcat(strcpy(cp, rootdir), filename);
+ }
+
+ ecode = (*pf->f_validate)(filename, tp->th_opcode);
+ if (ecode) {
+ nak(ecode, ERROR);
+ exit(1);
+ }
+ isopts = ap != (ackbuf+2);
+ (tp->th_opcode == WRQ ? *pf->f_recv : *pf->f_send)
+ (pf, isopts ? ackbuf : NULL, isopts ? ap-ackbuf : 0);
+ exit(0);
+}
+
+
+FILE *file;
+
+/*
+ * Validate file access. Since we
+ * have no uid or gid, for now require
+ * file to exist and be publicly
+ * readable/writable.
+ * Note also, full path name must be
+ * given as we have no login directory.
+ */
+validate_access(filename, mode)
+ char *filename;
+ int mode;
+{
+ struct stat stbuf;
+ int fd;
+ char *cp;
+
+ isfilter = 0;
+ if (mode == RRQ) {
+ struct filters *fp = filters;
+ for (; fp; fp = fp->next) {
+ if (!strcmp(fp->fname,
+ filename +
+ (rootdir ? strlen(rootdir) : 0))) {
+ if (debug)
+ syslog(LOG_INFO, "Opening input "
+ "filter: %s\n", filename);
+ if ((file = popen(filename, "r")) == NULL) {
+ syslog(LOG_ERR, "Failed to open input "
+ "filter\n");
+ return (EACCESS); }
+ fd = fileno(file);
+ isfilter = 1;
+ return (0);
+ }
+ }
+ }
+
+ if (*filename != '/') {
+ if (debug)
+ syslog(LOG_ERR, "Filename has to be absolute: %s\n",
+ filename);
+ return (EACCESS);
+ }
+ for (cp = filename; *cp; cp++)
+ if (*cp == '~' || *cp == '$' ||
+ (*cp == '/' && cp[1] == '.' && cp[2] == '.')) {
+ if (debug)
+ syslog(LOG_ERR, "Illegal filename: %s\n",
+ filename);
+ return (EACCESS);
+ }
+ if (debug)
+ syslog(LOG_INFO, "Validating \"%s\" for %sing\n",
+ filename, mode == RRQ ? "read" : "writ");
+ if (stat(filename, &stbuf) < 0)
+ return (errno == ENOENT ? ENOTFOUND : EACCESS);
+ if (mode == RRQ) {
+ if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
+ return (EACCESS);
+ } else {
+ if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
+ return (EACCESS);
+ }
+ fd = open(filename, mode == RRQ ? 0 : 1);
+ if (fd < 0)
+ return (errno + 100);
+ file = fdopen(fd, (mode == RRQ)? "r":"w");
+ if (file == NULL) {
+ return errno+100;
+ }
+ return (0);
+}
+
+int timeout;
+jmp_buf timeoutbuf;
+
+void timer(int sig)
+{
+
+ timeout += rexmtval;
+ if (timeout >= maxtimeout) {
+ if (debug)
+ syslog(LOG_WARNING, "Timeout!\n");
+ exit(1);
+ }
+ longjmp(timeoutbuf, 1);
+}
+
+/*
+ * Send the requested file.
+ */
+sendfile(pf, oap, oacklen)
+ struct formats *pf;
+ struct tftphdr *oap;
+ int oacklen;
+{
+ struct tftphdr *dp, *r_init();
+ register struct tftphdr *ap; /* ack packet */
+ register int size, n;
+ u_short block = 1;
+
+ signal(SIGALRM, timer);
+
+ ap = (struct tftphdr *)ackbuf;
+
+ if (oap) {
+ timeout = 0;
+ (void) setjmp(timeoutbuf);
+ oack:
+ if (send(peer, oap, oacklen, 0) != oacklen) {
+ syslog(LOG_ERR, "tftpd: write: %m\n");
+ goto abort;
+ }
+ for ( ; ; ) {
+ alarm(rexmtval);
+ n = recv(peer, ackbuf, sizeof (ackbuf), 0);
+ alarm(0);
+ if (n < 0) {
+ syslog(LOG_ERR, "tftpd: read: %m\n");
+ goto abort;
+ }
+ ap->th_opcode = ntohs((u_short)ap->th_opcode);
+ ap->th_block = ntohs(ap->th_block);
+
+ if (ap->th_opcode == ERROR) {
+ if (debug)
+ syslog(LOG_ERR, "Client does not "
+ "accept options\n");
+ goto abort; }
+
+ if (ap->th_opcode == ACK) {
+ if (ap->th_block == 0) {
+ if (debug)
+ syslog(LOG_DEBUG,
+ "RFC1782 option "
+ "negotiation "
+ "succeeded\n");
+ break;
+ }
+ /* Re-synchronize with the other side */
+ (void) synchnet(peer);
+ goto oack;
+ }
+ }
+ }
+
+ dp = r_init();
+ do {
+ size = readit(file, &dp, pf->f_convert);
+ if (size < 0) {
+ nak(errno + 100);
+ goto abort;
+ }
+ dp->th_opcode = htons((u_short)DATA);
+ dp->th_block = htons(block);
+ timeout = 0;
+ (void) setjmp(timeoutbuf);
+
+send_data:
+ if (send(peer, dp, size + 4, 0) != size + 4) {
+ syslog(LOG_ERR, "tftpd: write: %m\n");
+ goto abort;
+ }
+ read_ahead(file, pf->f_convert);
+ for ( ; ; ) {
+ alarm(rexmtval); /* read the ack */
+ n = recv(peer, ackbuf, sizeof (ackbuf), 0);
+ alarm(0);
+ if (n < 0) {
+ syslog(LOG_ERR, "tftpd: read: %m\n");
+ goto abort;
+ }
+ ap->th_opcode = ntohs((u_short)ap->th_opcode);
+ ap->th_block = ntohs(ap->th_block);
+
+ if (ap->th_opcode == ERROR)
+ goto abort;
+
+ if (ap->th_opcode == ACK) {
+ if (ap->th_block == block) {
+ break;
+ }
+ /* Re-synchronize with the other side */
+ (void) synchnet(peer);
+ if (ap->th_block == (block -1)) {
+ goto send_data;
+ }
+ }
+
+ }
+ block++;
+ } while (size == segsize);
+abort:
+ if (isfilter)
+ pclose(file);
+ else
+ (void) fclose(file);
+ isfilter = 0;
+}
+
+void justquit(int sig)
+{
+ exit(0);
+}
+
+
+/*
+ * Receive a file.
+ */
+recvfile(pf, oap, oacklen)
+ struct formats *pf;
+ struct tftphdr *oap;
+ int oacklen;
+{
+ struct tftphdr *dp, *w_init();
+ register struct tftphdr *ap; /* ack buffer */
+ register int acksize, n, size;
+ u_short block = 0;
+
+ signal(SIGALRM, timer);
+ dp = w_init();
+ do {
+ timeout = 0;
+
+ if (!block++ && oap) {
+ ap = (struct tftphdr *)oap;
+ acksize = oacklen;
+ } else {
+ ap = (struct tftphdr *)ackbuf;
+ ap->th_opcode = htons((u_short)ACK);
+ ap->th_block = htons(block-1);
+ acksize = 4;
+ }
+ (void) setjmp(timeoutbuf);
+send_ack:
+ if (send(peer, (char *)ap, acksize, 0) != acksize) {
+ syslog(LOG_ERR, "tftpd: write: %m\n");
+ goto abort;
+ }
+ write_behind(file, pf->f_convert);
+ for ( ; ; ) {
+ alarm(rexmtval);
+ n = recv(peer, dp, segsize+4, 0);
+ alarm(0);
+ if (n < 0) { /* really? */
+ syslog(LOG_ERR, "tftpd: read: %m\n");
+ goto abort;
+ }
+ dp->th_opcode = ntohs((u_short)dp->th_opcode);
+ dp->th_block = ntohs(dp->th_block);
+ if (dp->th_opcode == ERROR)
+ goto abort;
+ if (dp->th_opcode == DATA) {
+ if (dp->th_block == block) {
+ break; /* normal */
+ }
+ /* Re-synchronize with the other side */
+ (void) synchnet(peer);
+ if (dp->th_block == (block-1))
+ goto send_ack; /* rexmit */
+ }
+ }
+ /* size = write(file, dp->th_data, n - 4); */
+ size = writeit(file, &dp, n - 4, pf->f_convert);
+ if (size != (n-4)) { /* ahem */
+ if (size < 0) nak(errno + 100);
+ else nak(ENOSPACE);
+ goto abort;
+ }
+ } while (size == segsize);
+ write_behind(file, pf->f_convert);
+ if (isfilter)
+ pclose(file);
+ else
+ (void) fclose(file); /* close data file */
+ isfilter = 0;
+
+ ap = (struct tftphdr *)ackbuf;
+ ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */
+ ap->th_block = htons(block);
+ (void) send(peer, ackbuf, 4, 0);
+
+ signal(SIGALRM, justquit); /* just quit on timeout */
+ alarm(rexmtval);
+ n = recv(peer, buf, segsize, 0); /* normally times out and quits */
+ alarm(0);
+ if (n >= 4 && /* if read some data */
+ dp->th_opcode == DATA && /* and got a data block */
+ block == dp->th_block) { /* then my last ack was lost */
+ (void) send(peer, ackbuf, 4, 0); /* resend final ack */
+ }
+abort:
+ return;
+}
+
+struct errmsg {
+ int e_code;
+ const char *e_msg;
+} errmsgs[] = {
+ { EUNDEF, "Undefined error code" },
+ { ENOTFOUND, "File not found" },
+ { EACCESS, "Access violation" },
+ { ENOSPACE, "Disk full or allocation exceeded" },
+ { EBADOP, "Illegal TFTP operation" },
+ { EBADID, "Unknown transfer ID" },
+ { EEXISTS, "File already exists" },
+ { ENOUSER, "No such user" },
+ { EOPTNEG, "Failure to negotiate RFC1782 options" },
+ { -1, 0 }
+};
+
+/*
+ * Send a nak packet (error message).
+ * Error code passed in is one of the
+ * standard TFTP codes, or a UNIX errno
+ * offset by 100.
+ */
+nak(error)
+ int error;
+{
+ register struct tftphdr *tp;
+ int length;
+ register struct errmsg *pe;
+/* extern char *sys_errlist[]; */
+
+ tp = (struct tftphdr *)buf;
+ tp->th_opcode = htons((u_short)ERROR);
+ tp->th_code = htons((u_short)error);
+ for (pe = errmsgs; pe->e_code >= 0; pe++)
+ if (pe->e_code == error)
+ break;
+ if (pe->e_code < 0) {
+ pe->e_msg = sys_errlist[error -100];
+ tp->th_code = EUNDEF; /* set 'undef' errorcode */
+ }
+ strcpy(tp->th_msg, pe->e_msg);
+ length = strlen(pe->e_msg);
+ tp->th_msg[length] = '\0';
+ length += 5;
+ if (debug)
+ syslog(LOG_ERR, "Negative acknowledge: %s\n", tp->th_msg);
+ if (send(peer, buf, length, 0) != length)
+ syslog(LOG_ERR, "nak: %m\n");
+}
diff --git a/gpxe/contrib/tftp/tftpsubs.c b/gpxe/contrib/tftp/tftpsubs.c
new file mode 100644
index 00000000..608d64ec
--- /dev/null
+++ b/gpxe/contrib/tftp/tftpsubs.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tftpsubs.c 5.4 (Berkeley) 6/29/88";
+#endif /* not lint */
+
+/* Simple minded read-ahead/write-behind subroutines for tftp user and
+ server. Written originally with multiple buffers in mind, but current
+ implementation has two buffer logic wired in.
+
+ Todo: add some sort of final error check so when the write-buffer
+ is finally flushed, the caller can detect if the disk filled up
+ (or had an i/o error) and return a nak to the other side.
+
+ Jim Guyton 10/85
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+#include <stdio.h>
+
+#define PKTSIZE (1432+4) /* SEGSIZE+4 */ /* should be moved to tftp.h */
+
+struct bf {
+ int counter; /* size of data in buffer, or flag */
+ char buf[PKTSIZE]; /* room for data packet */
+} bfs[2];
+
+ /* Values for bf.counter */
+#define BF_ALLOC -3 /* alloc'd but not yet filled */
+#define BF_FREE -2 /* free */
+/* [-1 .. SEGSIZE] = size of data in the data buffer */
+
+extern int segsize;
+
+static int nextone; /* index of next buffer to use */
+static int current; /* index of buffer in use */
+
+ /* control flags for crlf conversions */
+int newline = 0; /* fillbuf: in middle of newline expansion */
+int prevchar = -1; /* putbuf: previous char (cr check) */
+
+struct tftphdr *rw_init();
+
+struct tftphdr *w_init() { return rw_init(0); } /* write-behind */
+struct tftphdr *r_init() { return rw_init(1); } /* read-ahead */
+
+struct tftphdr *
+rw_init(x) /* init for either read-ahead or write-behind */
+int x; /* zero for write-behind, one for read-head */
+{
+ newline = 0; /* init crlf flag */
+ prevchar = -1;
+ bfs[0].counter = BF_ALLOC; /* pass out the first buffer */
+ current = 0;
+ bfs[1].counter = BF_FREE;
+ nextone = x; /* ahead or behind? */
+ return (struct tftphdr *)bfs[0].buf;
+}
+
+
+/* Have emptied current buffer by sending to net and getting ack.
+ Free it and return next buffer filled with data.
+ */
+readit(file, dpp, convert)
+ FILE *file; /* file opened for read */
+ struct tftphdr **dpp;
+ int convert; /* if true, convert to ascii */
+{
+ struct bf *b;
+
+ bfs[current].counter = BF_FREE; /* free old one */
+ current = !current; /* "incr" current */
+
+ b = &bfs[current]; /* look at new buffer */
+ if (b->counter == BF_FREE) /* if it's empty */
+ read_ahead(file, convert); /* fill it */
+/* assert(b->counter != BF_FREE); /* check */
+ *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */
+ return b->counter;
+}
+
+/*
+ * fill the input buffer, doing ascii conversions if requested
+ * conversions are lf -> cr,lf and cr -> cr, nul
+ */
+read_ahead(file, convert)
+ FILE *file; /* file opened for read */
+ int convert; /* if true, convert to ascii */
+{
+ register int i;
+ register char *p;
+ register int c;
+ struct bf *b;
+ struct tftphdr *dp;
+
+ b = &bfs[nextone]; /* look at "next" buffer */
+ if (b->counter != BF_FREE) /* nop if not free */
+ return;
+ nextone = !nextone; /* "incr" next buffer ptr */
+
+ dp = (struct tftphdr *)b->buf;
+
+ if (convert == 0) {
+ int i;
+ b->counter = 0;
+ do {
+ i = read(fileno(file), dp->th_data + b->counter,
+ segsize - b->counter);
+ if (i > 0)
+ b->counter += i;
+ } while (i != 0 && !(i < 0 && errno != EINTR) &&
+ b->counter < segsize);
+ return;
+ }
+
+ p = dp->th_data;
+ for (i = 0 ; i < segsize; i++) {
+ if (newline) {
+ if (prevchar == '\n')
+ c = '\n'; /* lf to cr,lf */
+ else c = '\0'; /* cr to cr,nul */
+ newline = 0;
+ }
+ else {
+ c = getc(file);
+ if (c == EOF) break;
+ if (c == '\n' || c == '\r') {
+ prevchar = c;
+ c = '\r';
+ newline = 1;
+ }
+ }
+ *p++ = c;
+ }
+ b->counter = (int)(p - dp->th_data);
+}
+
+/* Update count associated with the buffer, get new buffer
+ from the queue. Calls write_behind only if next buffer not
+ available.
+ */
+writeit(file, dpp, ct, convert)
+ FILE *file;
+ struct tftphdr **dpp;
+ int convert;
+{
+ bfs[current].counter = ct; /* set size of data to write */
+ current = !current; /* switch to other buffer */
+ if (bfs[current].counter != BF_FREE) /* if not free */
+ write_behind(file, convert); /* flush it */
+ bfs[current].counter = BF_ALLOC; /* mark as alloc'd */
+ *dpp = (struct tftphdr *)bfs[current].buf;
+ return ct; /* this is a lie of course */
+}
+
+/*
+ * Output a buffer to a file, converting from netascii if requested.
+ * CR,NUL -> CR and CR,LF => LF.
+ * Note spec is undefined if we get CR as last byte of file or a
+ * CR followed by anything else. In this case we leave it alone.
+ */
+write_behind(file, convert)
+ FILE *file;
+ int convert;
+{
+ char *buf;
+ int count;
+ register int ct;
+ register char *p;
+ register int c; /* current character */
+ struct bf *b;
+ struct tftphdr *dp;
+
+ b = &bfs[nextone];
+ if (b->counter < -1) /* anything to flush? */
+ return 0; /* just nop if nothing to do */
+
+ count = b->counter; /* remember byte count */
+ b->counter = BF_FREE; /* reset flag */
+ dp = (struct tftphdr *)b->buf;
+ nextone = !nextone; /* incr for next time */
+ buf = dp->th_data;
+
+ if (count <= 0) return -1; /* nak logic? */
+
+ if (convert == 0)
+ return write(fileno(file), buf, count);
+
+ p = buf;
+ ct = count;
+ while (ct--) { /* loop over the buffer */
+ c = *p++; /* pick up a character */
+ if (prevchar == '\r') { /* if prev char was cr */
+ if (c == '\n') /* if have cr,lf then just */
+ fseek(file, -1, 1); /* smash lf on top of the cr */
+ else
+ if (c == '\0') /* if have cr,nul then */
+ goto skipit; /* just skip over the putc */
+ /* else just fall through and allow it */
+ }
+ putc(c, file);
+skipit:
+ prevchar = c;
+ }
+ return count;
+}
+
+
+/* When an error has occurred, it is possible that the two sides
+ * are out of synch. Ie: that what I think is the other side's
+ * response to packet N is really their response to packet N-1.
+ *
+ * So, to try to prevent that, we flush all the input queued up
+ * for us on the network connection on our host.
+ *
+ * We return the number of packets we flushed (mostly for reporting
+ * when trace is active).
+ */
+
+int
+synchnet(f)
+int f; /* socket to flush */
+{
+ int i, j = 0;
+ char rbuf[PKTSIZE];
+ struct sockaddr_in from;
+ int fromlen;
+
+ while (1) {
+ (void) ioctl(f, FIONREAD, &i);
+ if (i) {
+ j++;
+ fromlen = sizeof from;
+ (void) recvfrom(f, rbuf, sizeof (rbuf), 0,
+ (struct sockaddr *)&from, &fromlen);
+ } else {
+ return(j);
+ }
+ }
+}
diff --git a/gpxe/contrib/tomsrtbt/tomsrtbt-net.txt b/gpxe/contrib/tomsrtbt/tomsrtbt-net.txt
new file mode 100644
index 00000000..c5e1b0f2
--- /dev/null
+++ b/gpxe/contrib/tomsrtbt/tomsrtbt-net.txt
@@ -0,0 +1,37 @@
+Notes on turning tomsrtbt El Torito into a Etherboot image:
+
+0. Tomsrtbt (http://www.toms.net/) is an all-purpose rescue and utility
+1-floppy Linux system. You can read all about it at the web site. These
+notes explain how to turn the El Torito version of it into a netbootable
+image for Etherboot. Note that the .img file is not an ISO image, it is
+a 2.88M floppy emulation image for writing onto a CD-R(W) with mkisofs.
+It's actually a minix filesystem. Inside it are the kernel bz2bzImage
+and initrd.bz2.
+
+1. First uncompress the .img:
+
+ bunzip2 tomsrtbt-2.0.103.ElTorito.288.img.bz2
+
+2. Mount the image using loopback. You probably need to be root to do
+this:
+
+ mount -o ro,loop tomsrtbt-2.0.103.ElTorito.288.img /media/floppy
+
+I've specified /media/floppy which is the floppy mount point for my
+system, but any convenient directory will do.
+
+3. Copy the kernel image and initrd off it:
+
+ cp -p /media/floppy/bz2bzImage /media/floppy/initrd.bz2 .
+
+4. Use mkelf-linux (or mknbi-linux) to make a netbootable image:
+
+mkelf-linux --append='root=100' bz2bzImage initrd.bz2 > tomsrtbt.nb
+
+root=100 means use /dev/ram0 (device 1,0) as the root device.
+
+5. That's it. Clean up by unmounting the .img:
+
+ umount /media/cdrom
+
+tomsrtbt.nb can now be loaded with Etherboot. Have fun.
diff --git a/gpxe/contrib/wakeonlan/README b/gpxe/contrib/wakeonlan/README
new file mode 100644
index 00000000..5b03f9b8
--- /dev/null
+++ b/gpxe/contrib/wakeonlan/README
@@ -0,0 +1,40 @@
+From level42@sympatico.ca Tue Mar 18 04:35:31 2008
+Date: Mon, 17 Mar 2008 23:47:39 -0400
+From: Bill <level42@sympatico.ca>
+To: etherboot-developers@lists.sourceforge.net
+Subject: [Etherboot-developers] WOL Routine
+
+Attached is a WOL routine that can be used to wake a remote server from
+gpxe. I put wol.c in src/usr and wol.h in src/include/usr. This is really
+in improved replacement of that in contrib\wakeonlan\wakeserver.patch. This
+version will no longer work since the eth_transmit routine no longer works
+with the newer driver arhitecture such as the e1000 driver.
+
+Please consider adding it to gpxe preferrably in the main src directory or
+in the contrib directory.
+
+Thank you
+
+
+ [ Part 2, Text/PLAIN (Name: "wol.c") 109 lines. ]
+ [ Unable to print this part. ]
+
+
+ [ Part 3, Text/PLAIN (Name: "wol.h") 12 lines. ]
+ [ Unable to print this part. ]
+
+
+ [ Part 4: "Attached Text" ]
+
+-------------------------------------------------------------------------
+This SF.net email is sponsored by: Microsoft
+Defy all challenges. Microsoft(R) Visual Studio 2008.
+http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
+
+ [ Part 5: "Attached Text" ]
+
+_______________________________________________
+Etherboot-developers mailing list
+Etherboot-developers@lists.sourceforge.net
+https://lists.sourceforge.net/lists/listinfo/etherboot-developers
+
diff --git a/gpxe/contrib/wakeonlan/wol.c b/gpxe/contrib/wakeonlan/wol.c
new file mode 100644
index 00000000..40a8415a
--- /dev/null
+++ b/gpxe/contrib/wakeonlan/wol.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008 William Stewart.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/features.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/iobuf.h>
+#include <usr/ifmgmt.h>
+#include <usr/wol.h>
+#include <timer.h>
+
+/** @file
+ *
+ * Wake on lan
+ *
+ */
+
+/**
+ * Boot from a network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+#define WOL_MSG_LEN (6 + 16*6)
+
+void wakeup_server(char *server_adr)
+{
+ int rc, i,j;
+ unsigned char *buf;
+ uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ unsigned int len;
+ struct io_buffer *iobuf;
+ struct ethhdr *ethhdr;
+ struct net_device *netdev;
+
+ for_each_netdev ( netdev ) {
+ break;
+ }
+
+ if (netdev == NULL)
+ {
+ printf("Could not find netdev\n");
+ return;
+ }
+
+ /* Open device and display device status */
+ if ( (ifopen ( netdev ) ) != 0 )
+ {
+ printf("Could not open netdev\n");
+ return;
+ }
+
+ /* Create outgoing I/O buffer */
+ iobuf = alloc_iob ((ETH_HLEN + WOL_MSG_LEN)*2);
+ if (!iobuf)
+ {
+ printf("Could not allocate iob\n");
+ return;
+ }
+
+ ethhdr = iob_put(iobuf, sizeof(*ethhdr));
+
+ /* Build Ethernet header */
+ memcpy (ethhdr->h_dest, eth_broadcast, ETH_ALEN );
+ memcpy (ethhdr->h_source, netdev->ll_addr, ETH_ALEN );
+ ethhdr->h_protocol = htons (0x0842);
+
+ buf = iob_put (iobuf, WOL_MSG_LEN);
+
+ /* Build the message to send - 6 x 0xff then 16 x dest address */
+ len =0;
+ for (i = 0; i < 6; i++)
+ buf[len++] = 0xff;
+ for (j = 0; j < 16; j++)
+ for (i = 0; i < 6; i++)
+ buf[len++] = server_adr[i];
+
+ rc = netdev_tx (netdev, iobuf);
+
+ if (rc !=0)
+ printf("Failed to transmit WOL packet\n");
+
+ /* Give the controller a chance to send it before checking */
+ mdelay(100);
+
+ netdev_poll(netdev);
+}
+
diff --git a/gpxe/contrib/wakeonlan/wol.h b/gpxe/contrib/wakeonlan/wol.h
new file mode 100644
index 00000000..0bae6b66
--- /dev/null
+++ b/gpxe/contrib/wakeonlan/wol.h
@@ -0,0 +1,12 @@
+#ifndef _USR_WOL_H
+#define _USR_WOL_H
+
+/** @file
+ *
+ * Wakeon lan
+ *
+ */
+
+extern void wakeup_server(char *);
+
+#endif /* _USR_WOL_H */
diff --git a/gpxe/src/.gitignore b/gpxe/src/.gitignore
new file mode 100644
index 00000000..cc8e33e2
--- /dev/null
+++ b/gpxe/src/.gitignore
@@ -0,0 +1,4 @@
+.toolcheck
+.echocheck
+TAGS*
+bin*
diff --git a/gpxe/src/Config b/gpxe/src/Config
new file mode 100644
index 00000000..210718d4
--- /dev/null
+++ b/gpxe/src/Config
@@ -0,0 +1,386 @@
+##############################################################################
+##############################################################################
+#
+# IMPORTANT!
+#
+# The use of this file to set options that affect only single object
+# files is deprecated, because changing anything in this file results
+# in a complete rebuild, which is slow. All options are gradually
+# being migrated to config.h, which does not suffer from this problem.
+#
+# Only options that affect the entire build (e.g. overriding the $(CC)
+# Makefile variable) should be placed in here.
+#
+##############################################################################
+##############################################################################
+
+
+#
+# Config for Etherboot/32
+#
+#
+# Do not delete the tag OptionDescription and /OptionDescription
+# It is used to automatically generate the documentation.
+#
+# @OptionDescription@
+# User interaction options:
+#
+# -DASK_BOOT=n
+# Ask "Boot from (N)etwork ... or (Q)uit? "
+# at startup, timeout after n seconds (0 = no timeout).
+# If unset or negative, don't ask and boot immediately
+# using the default.
+# -DBOOT_FIRST
+# -DBOOT_SECOND
+# -DBOOT_THIRD
+# On timeout or Return key from previous
+# question, selects the order to try to boot from
+# various devices.
+# (alternatives: BOOT_NIC, BOOT_DISK,
+# BOOT_FLOPPY, BOOT_NOTHING)
+# See etherboot.h for prompt and answer strings.
+# BOOT_DISK and BOOT_FLOPPY work only where a driver
+# exists, e.g. in LinuxBIOS.
+# They have no effect on PCBIOS.
+# -DBOOT_INDEX The device to boot from 0 == any device.
+# 1 == The first nic found.
+# 2 == The second nic found
+# ...
+# BOOT_INDEX only applies to the BOOT_FIRST. BOOT_SECOND
+# and BOOT_THIRD search through all of the boot devices.
+# -DBAR_PROGRESS
+# Use rotating bar instead of sequential dots
+# to indicate an IP packet transmitted.
+#
+# Boot order options:
+#
+# -DBOOT_CLASS_FIRST
+# -DBOOT_CLASS_SECOND
+# -DBOOT_CLASS_THIRD
+# Select the priority of the boot classes
+# Valid values are:
+# BOOT_NIC
+# BOOT_DISK
+# BOOT_FLOPPY
+# BOOT_DISK and BOOT_FLOPPY work only where a driver exists,
+# e.g. in LinuxBIOS. They have no effect on PCBIOS.
+#
+# Boot autoconfiguration protocol options:
+#
+# -DALTERNATE_DHCP_PORTS_1067_1068
+# Use ports 1067 and 1068 for DHCP instead of 67 and 68.
+# As these ports are non-standard, you need to configure
+# your DHCP server to use them. This option gets around
+# existing DHCP servers which cannot be touched, for
+# one reason or another, at the cost of non-standard
+# boot images.
+# -DNO_DHCP_SUPPORT
+# Use BOOTP instead of DHCP.
+# -DRARP_NOT_BOOTP
+# Use RARP instead of BOOTP/DHCP.
+# -DREQUIRE_VCI_ETHERBOOT
+# Require an encapsulated Vendor Class Identifier
+# of "Etherboot" in the DHCP reply
+# Requires DHCP support.
+# -DDHCP_CLIENT_ID=\"Identifier\"
+# -DDHCP_CLIENT_ID_LEN=<Client ID length in octets>
+# -DDHCP_CLIENT_ID_TYPE=<Client ID type>
+# Specify a RFC2132 Client Identifier option, length and type.
+# Requires DHCP support.
+# -DDHCP_USER_CLASS=\"UserClass\"
+# -DDHCP_USER_CLASS_LEN=<User Class length in octets>
+# Specify a RFC3004 User Class option and length. Use this
+# option to set a UC (or multiple UCs) rather than munge the
+# client Vendor Class ID.
+# Requires DHCP support.
+# -DALLOW_ONLY_ENCAPSULATED
+# Ignore Etherboot-specific options that are not within
+# the Etherboot encapsulated options field. This option
+# should be enabled unless you have a legacy DHCP server
+# configuration from the bad old days before the use of
+# encapsulated Etherboot options.
+# -DDEFAULT_BOOTFILE=\"default_bootfile_name\"
+# Define a default bootfile for the case where your DHCP
+# server does not provide the information. Example:
+# -DDEFAULT_BOOTFILE="tftp:///tftpboot/kernel"
+# If you do not specify this option, then DHCP offers that
+# do not specify bootfiles will be ignored.
+#
+# NIC tuning parameters:
+#
+# -DALLMULTI
+# Turns on multicast reception in the NICs.
+#
+# Boot tuning parameters:
+#
+# -DCONGESTED
+# Turns on packet retransmission. Use it on a
+# congested network, where the normal operation
+# can't boot the image.
+# -DBACKOFF_LIMIT
+# Sets the maximum RFC951 backoff exponent to n.
+# Do not set this unreasonably low, because on networks
+# with many machines they can saturate the link
+# (the delay corresponding to the exponent is a random
+# time in the range 0..3.5*2^n seconds). Use 5 for a
+# VERY small network (max. 2 minutes delay), 7 for a
+# medium sized network (max. 7.5 minutes delay) or 10
+# for a really huge network with many clients, frequent
+# congestions (max. 1 hour delay). On average the
+# delay time will be half the maximum value. If in
+# doubt about the consequences, use a larger value.
+# Also keep in mind that the number of retransmissions
+# is not changed by this setting, so the default of 20
+# may no longer be appropriate. You might need to set
+# MAX_ARP_RETRIES, MAX_BOOTP_RETRIES, MAX_TFTP_RETRIES
+# and MAX_RPC_RETRIES to a larger value.
+# -DTIMEOUT=n
+# Use with care!! See above.
+# Sets the base of RFC2131 sleep interval to n.
+# This can be used with -DBACKOFF_LIMIT=0 to get a small
+# and constant (predictable) retry interval for embedded
+# devices. This is to achieve short boot delays if both
+# the DHCP Server and the embedded device will be powered
+# on the same time. Otherwise if the DHCP server is ready
+# the client could sleep the next exponentially timeout,
+# e.g. 70 seconds or more. This is not what you want.
+# n should be a multiple of TICKS_PER_SEC (18).
+#
+# Boot device options:
+#
+# -DTRY_FLOPPY_FIRST
+# If > 0, tries that many times to read the boot
+# sector from a floppy drive before booting from
+# ROM. If successful, does a local boot.
+# It assumes the floppy is bootable.
+# -DEXIT_IF_NO_OFFER
+# If no IP offer is obtained, exit and
+# let the BIOS continue.
+# The accessibility of the TFTP server has no effect,
+# so configure your DHCP/BOOTP server properly.
+# You should probably reduce MAX_BOOTP_RETRIES
+# to a small number like 3.
+#
+# Boot image options:
+#
+# -DFREEBSD_KERNEL_ENV
+# Pass in FreeBSD kernel environment
+# -DAOUT_LYNX_KDI
+# Add Lynx a.out KDI support
+# -DMULTICAST_LEVEL1
+# Support for sending multicast packets
+# -DMULTICAST_LEVEL2
+# Support for receiving multicast packets
+#
+# Interface export options:
+#
+# -DPXE_EXPORT
+# Export a PXE API interface. This is work in
+# progress. Note that you won't be able to load
+# PXE NBPs unless you also use -DPXE_IMAGE.
+# -DPXE_STRICT
+# Strict(er) compliance with the PXE
+# specification as published by Intel. This may
+# or may not be a good thing depending on your
+# view of the spec...
+# -DPXE_DHCP_STRICT
+# Strict compliance of the DHCP request packets
+# with the PXE specification as published by
+# Intel. This may or may not be a good thing
+# depending on your view of whether requesting
+# vendor options which don't actually exist is
+# pointless or not. You probably want this
+# option if you intend to use Windows RIS or
+# similar.
+#
+# Obscure options you probably don't need to touch:
+#
+# -DZPXE_SUFFIX_STRIP
+# If the last 5 characters of the filename passed to Etherboot is
+# ".zpxe" then strip it off. This is useful in cases where a DHCP server
+# is not able to be configured to support conditionals. The way it works
+# is that the DHCP server is configured with a filename like
+# "foo.nbi.zpxe" so that when PXE asks for a filename it gets that, and
+# loads Etherboot from that file. Etherboot then starts up and once
+# again asks the DHCP server for a filename and once again gets
+# foo.nbi.zpxe, but with this option turned on loads "foo.nbi" instead.
+# This allows people to use Etherboot who might not otherwise be able to
+# because their DHCP servers won't let them.
+#
+# -DPOWERSAVE
+# Halt the processor when waiting for keyboard input
+# which saves power while waiting for user interaction.
+# Good for compute clusters and VMware emulation.
+# But may not work for all CPUs.
+#
+# @/OptionDescription@
+
+# These default settings compile Etherboot with a small number of options.
+# You may wish to enable more of the features if the size of your ROM allows.
+
+
+# For prompting and default on timeout
+# CFLAGS+= -DASK_BOOT=3 -DBOOT_FIRST=BOOT_NIC
+# If you would like to attempt to boot from other devices as well as the network.
+# CFLAGS+= -DBOOT_SECOND=BOOT_FLOPPY
+# CFLAGS+= -DBOOT_THIRD=BOOT_DISK
+# CFLAGS+= -DBOOT_INDEX=0
+
+# If you prefer the old style rotating bar progress display
+# CFLAGS+= -DBAR_PROGRESS
+
+# Show size indicator
+# CFLAGS+= -DSIZEINDICATOR
+
+# Enabling this creates non-standard images which use ports 1067 and 1068
+# for DHCP/BOOTP
+# CFLAGS+= -DALTERNATE_DHCP_PORTS_1067_1068
+
+# Enabling this makes the boot ROM require a Vendor Class Identifier
+# of "Etherboot" in the Vendor Encapsulated Options
+# This can be used to reject replies from servers other than the one
+# we want to give out addresses to us, but it will prevent Etherboot
+# from getting an IP lease until you have configured DHCPD correctly
+# CFLAGS+= -DREQUIRE_VCI_ETHERBOOT
+
+# EXPERIMENTAL! Set DHCP_CLIENT_ID to create a Client Identifier (DHCP
+# option 61, see RFC2132 section 9.14) when Etherboot sends the DHCP
+# DISCOVER and REQUEST packets. This ID must UNIQUELY identify each
+# client on your local network. Set DHCP_CLIENT_ID_TYPE to the
+# appropriate hardware type as described in RFC2132 / RFC1700; this
+# almost certainly means using '1' if the Client ID is an Ethernet MAC
+# address and '0' otherwise. Set DHCP_CLIENT_ID_LEN to the length of
+# the Client ID in octets (this is not a null terminated C string, do
+# NOT add 1 for a terminator and do NOT add an extra 1 for the
+# hardware type octet). Note that to identify your client using the
+# normal default MAC address of your NIC, you do NOT need to set this
+# option, as the MAC address is automatically used in the
+# hwtype/chaddr field; note also that this field only sets the DHCP
+# option: it does NOT change the MAC address used by the client.
+
+# CFLAGS+= -DDHCP_CLIENT_ID="'C','L','I','E','N','T','0','0','1'" \
+# -DDHCP_CLIENT_ID_LEN=9 -DDHCP_CLIENT_ID_TYPE=0
+
+# CFLAGS+= -DDHCP_CLIENT_ID="0xDE,0xAD,0xBE,0xEF,0xDE,0xAD" \
+# -DDHCP_CLIENT_ID_LEN=6 -DDHCP_CLIENT_ID_TYPE=1
+
+# EXPERIMENTAL! Set DHCP_USER_CLASS to create a User Class option (see
+# RFC3004) when Etherboot sends the DHCP DISCOVER and REQUEST packets.
+# This can be used for classification of clients, typically so that a
+# DHCP server can send an appropriately tailored reply. Normally, a
+# string identifies a class of to which this client instance belongs
+# which is useful in your network, such as a department ('FINANCE' or
+# 'MARKETING') or hardware type ('THINCLIENT' or 'KIOSK'). Set
+# DHCP_USER_CLASS_LEN to the length of DHCP_USER_CLASS in octets.
+# This is NOT a null terminated C string, do NOT add 1 for a
+# terminator. RFC3004 advises how to lay out multiple User Class
+# options by using an octet for the length of each string, as in this
+# example. It is, of course, up to the server to parse this.
+
+# CFLAGS+= -DDHCP_USER_CLASS="'T','E','S','T','C','L','A','S','S'" \
+# -DDHCP_USER_CLASS_LEN=9
+
+# CFLAGS+= -DDHCP_USER_CLASS="5,'A','L','P','H','A',4,'B','E','T','A'" \
+# -DDHCP_USER_CLASS_LEN=11
+
+# Enabling this causes Etherboot to ignore Etherboot-specific options
+# that are not within an Etherboot encapsulated options field.
+# This option should be enabled unless you have a legacy DHCP server
+# configuration from the bad old days before the use of
+# encapsulated Etherboot options.
+# CFLAGS+= -DALLOW_ONLY_ENCAPSULATED
+
+# Disable DHCP support
+# CFLAGS+= -DNO_DHCP_SUPPORT
+
+# Specify a default bootfile to be used if the DHCP server does not
+# provide the information. If you do not specify this option, then
+# DHCP offers that do not contain bootfiles will be ignored.
+# CFLAGS+= -DDEFAULT_BOOTFILE=\"tftp:///tftpboot/kernel\"
+
+# Limit the delay on packet loss/congestion to a more bearable value. See
+# description above. If unset, do not limit the delay between resend.
+# CFLAGS+= -DBACKOFF_LIMIT=5 -DCONGESTED
+
+# More optional features
+# CFLAGS+= -DTRY_FLOPPY_FIRST=4
+# CFLAGS+= -DEXIT_IF_NO_OFFER
+
+
+# Multicast Support
+# CFLAGS+= -DALLMULTI -DMULTICAST_LEVEL1 -DMULTICAST_LEVEL2
+
+# Etherboot as a PXE network protocol ROM
+# CFLAGS+= -DPXE_IMAGE -DPXE_EXPORT
+# Etherboot stricter as a PXE network protocol ROM
+# CFLAGS+= -DPXE_DHCP_STRICT
+
+# Support for PXE emulation. Works only with FreeBSD to load the kernel
+# via pxeboot, use only with DOWNLOAD_PROTO_NFS
+# CFLAGS+= -DFREEBSD_PXEEMU
+
+
+
+# Garbage from Makefile.main temporarily placed here until a home can
+# be found for it.
+
+# NS8390 options:
+# -DINCLUDE_NE - Include NE1000/NE2000 support
+# -DNE_SCAN=list - Probe for NE base address using list of
+# comma separated hex addresses
+# -DINCLUDE_3C503 - Include 3c503 support
+# -DT503_SHMEM - Use 3c503 shared memory mode (off by default)
+# -DINCLUDE_WD - Include Western Digital/SMC support
+# -DWD_DEFAULT_MEM- Default memory location for WD/SMC cards
+# -DWD_790_PIO - Read/write to WD/SMC 790 cards in PIO mode (default
+# is to use shared memory) Try this if you get "Bogus
+# packet, ignoring" messages, common on ISA/PCI hybrid
+# systems.
+# -DCOMPEX_RL2000_FIX
+#
+# If you have a Compex RL2000 PCI 32-bit (11F6:1401),
+# and the bootrom hangs in "Probing...[NE*000/PCI]",
+# try enabling this fix... it worked for me :).
+# In the first packet write somehow it somehow doesn't
+# get back the expected data so it is stuck in a loop.
+# I didn't bother to investigate what or why because it works
+# when I interrupt the loop if it takes more then COMPEX_RL2000_TRIES.
+# The code will notify if it does a abort.
+# SomniOne - somnione@gmx.net
+#
+# 3C90X options:
+# Warning Warning Warning
+# If you use any of the XCVR options below, please do not complain about
+# the behaviour with Linux drivers to the kernel developers. You are
+# on your own if you do this. Please read 3c90x.txt to understand
+# what they do. If you don't understand them, ask for help on the
+# Etherboot mailing list. And please document what you did to the NIC
+# on the NIC so that people after you won't get nasty surprises.
+#
+# -DCFG_3C90X_PRESERVE_XCVR - Reset the transceiver type to the value it
+# had initially just before the loaded code is started.
+# -DCFG_3C90X_XCVR - Hardcode the tranceiver type Etherboot uses.
+# -DCFG_3C90X_BOOTROM_FIX - If you have a 3c905B with buggy ROM
+# interface, setting this option might "fix" it. Use
+# with caution and read the docs in 3c90x.txt!
+#
+# See the documentation file 3c90x.txt for more details.
+#
+# CS89X0 (optional) options:
+# -DISA_PROBE_ADDRS=list
+# Probe for CS89x0 base address using list of
+# comma separated hex addresses; increasing the
+# address by one (0x300 -> 0x301) will force a
+# more aggressive probing algorithm. This might
+# be neccessary after a soft-reset of the NIC.
+
+
+CFLAGS_3c503 = -DINCLUDE_3C503 # -DT503_SHMEM
+CFLAGS_ne = -DINCLUDE_NE -DNE_SCAN=0x300,0x280,0x320,0x340,0x380
+CFLAGS_ns8390 = -DINCLUDE_NS8390 # NE2000/PCI!
+CFLAGS_wd = -DINCLUDE_WD -DWD_DEFAULT_MEM=0xCC000
+
+#
+# SYSLINUX: Local addition to build PXELINUX combined image
+#
+EMBEDDED_IMAGE = ../../pxelinux.0
diff --git a/gpxe/src/Makefile b/gpxe/src/Makefile
new file mode 100644
index 00000000..0591bb01
--- /dev/null
+++ b/gpxe/src/Makefile
@@ -0,0 +1,191 @@
+# Location to place generated files
+#
+BIN := bin
+
+# Initialise variables that get added to throughout the various Makefiles
+#
+MAKEDEPS := Makefile .toolcheck .echocheck
+SRCDIRS :=
+SRCS :=
+NON_AUTO_SRCS :=
+DRIVERS :=
+ROMS :=
+MEDIA :=
+NON_AUTO_MEDIA :=
+
+# Locations of utilities
+#
+HOST_CC := gcc
+RM := rm -f
+TOUCH := touch
+MKDIR := mkdir
+CP := cp
+ECHO := echo
+PRINTF := printf
+PERL := /usr/bin/perl
+CC := $(CROSS_COMPILE)gcc
+CPP := $(CROSS_COMPILE)gcc -E -Wp,-Wall
+AS := $(CROSS_COMPILE)as
+LD := $(CROSS_COMPILE)ld
+SIZE := $(CROSS_COMPILE)size
+AR := $(CROSS_COMPILE)ar
+RANLIB := $(CROSS_COMPILE)ranlib
+OBJCOPY := $(CROSS_COMPILE)objcopy
+NM := $(CROSS_COMPILE)nm
+OBJDUMP := $(CROSS_COMPILE)objdump
+PARSEROM := $(PERL) ./util/parserom.pl
+MAKEROM := $(PERL) ./util/makerom.pl
+MKCONFIG := $(PERL) ./util/mkconfig.pl
+SYMCHECK := $(PERL) ./util/symcheck.pl
+SORTOBJDUMP := $(PERL) ./util/sortobjdump.pl
+NRV2B := ./util/nrv2b
+ZBIN := ./util/zbin
+DOXYGEN := doxygen
+
+# If invoked with no build target, print out a helpfully suggestive
+# message.
+#
+noargs : blib $(BIN)/NIC $(BIN)/gpxe.dsk $(BIN)/gpxe.iso $(BIN)/gpxe.usb
+ @$(ECHO) '==========================================================='
+ @$(ECHO)
+ @$(ECHO) 'To create a bootable floppy, type'
+ @$(ECHO) ' cat $(BIN)/gpxe.dsk > /dev/fd0'
+ @$(ECHO) 'where /dev/fd0 is your floppy drive. This will erase any'
+ @$(ECHO) 'data already on the disk.'
+ @$(ECHO)
+ @$(ECHO) 'To create a bootable USB key, type'
+ @$(ECHO) ' cat $(BIN)/gpxe.usb > /dev/sdX'
+ @$(ECHO) 'where /dev/sdX is your USB key, and is *not* a real hard'
+ @$(ECHO) 'disk on your system. This will erase any data already on'
+ @$(ECHO) 'the USB key.'
+ @$(ECHO)
+ @$(ECHO) 'To create a bootable CD-ROM, burn the ISO image '
+ @$(ECHO) '$(BIN)/gpxe.iso to a blank CD-ROM.'
+ @$(ECHO)
+ @$(ECHO) 'These images contain drivers for all supported cards. You'
+ @$(ECHO) 'can build more customised images, and ROM images, using'
+ @$(ECHO) ' make bin/<rom-name>.<output-format>'
+ @$(ECHO)
+ @$(ECHO) '==========================================================='
+
+# Grab the central Config file.
+#
+MAKEDEPS += Config
+include Config
+
+# If no architecture is specified in Config or on the command-line,
+# use that of the build machine.
+#
+ARCH ?= $(shell uname -m | sed -e s,i[3456789]86,i386,)
+
+# handle x86_64 like i386, but set -m32 option for 32bit code only
+ifeq ($(ARCH),x86_64)
+ARCH := i386
+CFLAGS += -m32
+ASFLAGS += --32
+LDFLAGS += -m elf_i386
+endif
+
+# Drag in architecture-specific Config
+#
+MAKEDEPS += arch/$(ARCH)/Config
+include arch/$(ARCH)/Config
+
+# Common flags
+#
+CFLAGS += -I include -I arch/$(ARCH)/include -I . -DARCH=$(ARCH)
+CFLAGS += -Os -ffreestanding
+CFLAGS += -Wall -W
+CFLAGS += -g
+CFLAGS += $(EXTRA_CFLAGS)
+ASFLAGS += $(EXTRA_ASFLAGS)
+LDFLAGS += $(EXTRA_LDFLAGS)
+
+# Embedded image, if present
+#
+EMBEDDED_IMAGE ?= /dev/null
+
+ifneq ($(NO_WERROR),1)
+CFLAGS += -Werror
+endif
+
+# CFLAGS for specific object types
+#
+CFLAGS_c +=
+CFLAGS_S += -DASSEMBLY
+
+# Base object name of the current target
+#
+OBJECT = $(firstword $(subst ., ,$(@F)))
+
+# CFLAGS for specific object files. You can define
+# e.g. CFLAGS_rtl8139, and have those flags automatically used when
+# compiling bin/rtl8139.o.
+#
+OBJ_CFLAGS = $(CFLAGS_$(OBJECT)) -DOBJECT=$(subst -,_,$(OBJECT))
+$(BIN)/%.flags :
+ @$(ECHO) $(OBJ_CFLAGS)
+
+# Rules for specific object types.
+#
+COMPILE_c = $(CC) $(CFLAGS) $(CFLAGS_c) $(OBJ_CFLAGS)
+RULE_c = $(Q)$(COMPILE_c) -c $< -o $@
+RULE_c_to_dbg%.o = $(Q)$(COMPILE_c) -Ddebug_$(OBJECT)=$* -c $< -o $@
+RULE_c_to_c = $(Q)$(COMPILE_c) -E -c $< > $@
+RULE_c_to_s = $(Q)$(COMPILE_c) -S -g0 -c $< -o $@
+
+PREPROCESS_S = $(CPP) $(CFLAGS) $(CFLAGS_S) $(OBJ_CFLAGS)
+ASSEMBLE_S = $(AS) $(ASFLAGS)
+RULE_S = $(Q)$(PREPROCESS_S) $< | $(ASSEMBLE_S) -o $@
+RULE_S_to_s = $(Q)$(PREPROCESS_S) $< > $@
+
+DEBUG_TARGETS += dbg%.o c s
+
+# SRCDIRS lists all directories containing source files.
+#
+SRCDIRS += libgcc
+SRCDIRS += core
+SRCDIRS += proto
+SRCDIRS += net net/tcp net/udp
+SRCDIRS += image
+SRCDIRS += drivers/bus
+SRCDIRS += drivers/net
+SRCDIRS += drivers/net/e1000
+SRCDIRS += drivers/block
+SRCDIRS += drivers/scsi
+SRCDIRS += drivers/ata
+SRCDIRS += drivers/nvs
+SRCDIRS += drivers/bitbash
+SRCDIRS += drivers/infiniband
+SRCDIRS += interface/pxe
+SRCDIRS += tests
+SRCDIRS += crypto crypto/axtls crypto/matrixssl
+SRCDIRS += hci hci/commands hci/tui
+SRCDIRS += hci/mucurses hci/mucurses/widgets
+SRCDIRS += usr
+
+# NON_AUTO_SRCS lists files that are excluded from the normal
+# automatic build system.
+#
+NON_AUTO_SRCS += core/elf_loader.c
+NON_AUTO_SRCS += drivers/net/prism2.c
+
+# Rules for finalising files. TGT_MAKEROM_FLAGS is defined as part of
+# the automatic build system and varies by target; it includes the
+# "-p 0x1234,0x5678" string to set the PCI IDs.
+#
+FINALISE_rom = $(MAKEROM) $(MAKEROM_FLAGS) $(TGT_MAKEROM_FLAGS) \
+ -i$(IDENT) -s 0 $@
+
+# Some ROMs require specific flags to be passed to makerom.pl
+#
+MAKEROM_FLAGS_3c503 = -3
+
+# Drag in architecture-specific Makefile
+#
+MAKEDEPS += arch/$(ARCH)/Makefile
+include arch/$(ARCH)/Makefile
+
+# Drag in the automatic build system and other housekeeping functions
+MAKEDEPS += Makefile.housekeeping
+include Makefile.housekeeping
diff --git a/gpxe/src/Makefile.housekeeping b/gpxe/src/Makefile.housekeeping
new file mode 100644
index 00000000..fe3addc9
--- /dev/null
+++ b/gpxe/src/Makefile.housekeeping
@@ -0,0 +1,603 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# This file contains various boring housekeeping functions that would
+# otherwise seriously clutter up the main Makefile.
+
+# Objects to be removed by "make clean"
+#
+CLEANUP := $(BIN)/*.* # *.* to avoid catching the "CVS" directory
+
+# Version number calculations
+#
+VERSION_MAJOR = 0
+VERSION_MINOR = 9
+VERSION_PATCH = 3
+EXTRAVERSION =
+MM_VERSION = $(VERSION_MAJOR).$(VERSION_MINOR)
+VERSION = $(MM_VERSION).$(VERSION_PATCH)$(EXTRAVERSION)
+CFLAGS += -DVERSION_MAJOR=$(VERSION_MAJOR) \
+ -DVERSION_MINOR=$(VERSION_MINOR) \
+ -DVERSION=\"$(VERSION)\"
+IDENT = '$(@F) $(VERSION) (GPL) etherboot.org'
+version :
+ @$(ECHO) $(VERSION)
+
+configure :
+ @$(ECHO) "No configuration needed."
+
+install :
+ @$(ECHO) "No installation required. Generated images will be placed in the" $(BIN) "directory."
+
+# Check for tools that can cause failed builds
+#
+.toolcheck : Makefile Config
+ @if $(CC) -v 2>&1 | grep -is 'gcc version 2\.96' > /dev/null; then \
+ $(ECHO) 'gcc 2.96 is unsuitable for compiling Etherboot'; \
+ $(ECHO) 'Use gcc 2.95 or gcc 3.x instead'; \
+ exit 1; \
+ fi
+ @if [ `perl -e 'use bytes; print chr(255)' | wc -c` = 2 ]; then \
+ $(ECHO) 'Your Perl version has a Unicode handling bug'; \
+ $(ECHO) 'Execute this command before compiling Etherboot:'; \
+ $(ECHO) 'export LANG=$${LANG%.UTF-8}'; \
+ exit 1; \
+ fi
+ @$(TOUCH) $@
+VERYCLEANUP += .toolcheck
+
+# Find a usable "echo -e" substitute.
+#
+TAB := $(shell $(PRINTF) '\t')
+ECHO_E_ECHO := $(ECHO)
+ECHO_E_ECHO_E := $(ECHO) -e
+ECHO_E_BIN_ECHO := /bin/echo
+ECHO_E_BIN_ECHO_E := /bin/echo -e
+ECHO_E_ECHO_TAB := $(shell $(ECHO_E_ECHO) '\t' | cat)
+ECHO_E_ECHO_E_TAB := $(shell $(ECHO_E_ECHO_E) '\t' | cat)
+ECHO_E_BIN_ECHO_TAB := $(shell $(ECHO_E_BIN_ECHO) '\t')
+ECHO_E_BIN_ECHO_E_TAB := $(shell $(ECHO_E_BIN_ECHO_E) '\t')
+
+ifeq ($(ECHO_E_ECHO_TAB),$(TAB))
+ECHO_E ?= $(ECHO_E_ECHO)
+endif
+ifeq ($(ECHO_E_ECHO_E_TAB),$(TAB))
+ECHO_E ?= $(ECHO_E_ECHO_E)
+endif
+ifeq ($(ECHO_E_BIN_ECHO_TAB),$(TAB))
+ECHO_E ?= $(ECHO_E_BIN_ECHO)
+endif
+ifeq ($(ECHO_E_BIN_ECHO_E_TAB),$(TAB))
+ECHO_E ?= $(ECHO_E_BIN_ECHO_E)
+endif
+
+.echocheck :
+ifdef ECHO_E
+ @$(TOUCH) $@
+else
+ @$(PRINTF) '%24s : x%sx\n' 'tab' '$(TAB)'
+ @$(PRINTF) '%24s : x%sx\n' '"$(ECHO_E_ECHO) \t"' \
+ '$(ECHO_E_ECHO_TAB)'
+ @$(PRINTF) '%24s : x%sx\n' '"$(ECHO_E_ECHO_E) \t"' \
+ '$(ECHO_E_ECHO_E_TAB)'
+ @$(PRINTF) '%24s : x%sx\n' '"$(ECHO_E_BIN_ECHO) \t"' \
+ '$(ECHO_E_BIN_ECHO_TAB)'
+ @$(PRINTF) '%24s : x%sx\n' '"$(ECHO_E_BIN_ECHO_E) \t"' \
+ '$(ECHO_E_BIN_ECHO_E_TAB)'
+ @$(ECHO) "No usable \"echo -e\" substitute found"
+ @exit 1
+endif
+VERYCLEANUP += .echocheck
+
+echo :
+ @$(ECHO) "Using \"$(ECHO_E)\" for \"echo -e\""
+
+# Build verbosity
+#
+ifeq ($(V),1)
+Q =
+QM = @\#
+else
+Q = @
+QM = @
+endif
+
+# Check for an old version of gas (binutils 2.9.1)
+#
+OLDGAS := $(shell $(AS) --version | grep -q '2\.9\.1' && $(ECHO) -DGAS291)
+CFLAGS += $(OLDGAS)
+oldgas :
+ @$(ECHO) $(oldgas)
+
+# Some widespread patched versions of gcc include -fstack-protector by
+# default, even when -ffreestanding is specified. We therefore need
+# to disable -fstack-protector if the compiler supports it.
+#
+SP_TEST = $(CC) -fno-stack-protector -x c -E - < /dev/null >/dev/null 2>&1
+SP_FLAGS := $(shell $(SP_TEST) && $(ECHO) '-fno-stack-protector')
+CFLAGS += $(SP_FLAGS)
+
+# compiler.h is needed for our linking and debugging system
+#
+CFLAGS += -include compiler.h
+
+# config/%.h files are generated from config.h using mkconfig.pl
+config/%.h : config.h
+ $(MKCONFIG) $<
+CLEANUP += config/*.h
+
+# SRCDIRS lists all directories containing source files.
+srcdirs :
+ @$(ECHO) $(SRCDIRS)
+
+# SRCS lists all .c or .S files found in any SRCDIR
+#
+SRCS += $(wildcard $(patsubst %,%/*.c,$(SRCDIRS)))
+SRCS += $(wildcard $(patsubst %,%/*.S,$(SRCDIRS)))
+srcs :
+ @$(ECHO) $(SRCS)
+
+# AUTO_SRCS lists all files in SRCS that are not mentioned in
+# NON_AUTO_SRCS. Files should be added to NON_AUTO_SRCS if they
+# cannot be built using the standard build template.
+#
+AUTO_SRCS = $(filter-out $(NON_AUTO_SRCS),$(SRCS))
+autosrcs :
+ @$(ECHO) $(AUTO_SRCS)
+
+# We automatically generate rules for any file mentioned in AUTO_SRCS
+# using the following set of templates. It would be cleaner to use
+# $(eval ...), but this function exists only in GNU make >= 3.80.
+
+# src_template : generate Makefile rules for a given source file
+#
+# $(1) is the full path to the source file (e.g. "drivers/net/rtl8139.c")
+# $(2) is the full path to the .d file (e.g. "bin/deps/drivers/net/rtl8139.d")
+# $(3) is the source type (e.g. "c")
+# $(4) is the source base name (e.g. "rtl8139")
+#
+define src_template
+
+ @$(ECHO) "Generating Makefile rules for $(1)"
+ @$(MKDIR) -p $(dir $(2))
+ @$(RM) $(2)
+ @$(TOUCH) $(2)
+ $(foreach OBJ,$(if $(OBJS_$(4)),$(OBJS_$(4)),$(4)), \
+ $(call obj_template,$(1),$(2),$(3),$(OBJ)))
+ @$(PARSEROM) $(1) >> $(2)
+
+endef
+
+# obj_template : generate Makefile rules for a given resultant object
+# of a particular source file. (We can have multiple objects per
+# source file via the OBJS_xxx list.)
+#
+# $(1) is the full path to the source file (e.g. "drivers/net/rtl8139.c")
+# $(2) is the full path to the .d file (e.g. "bin/deps/drivers/net/rtl8139.d")
+# $(3) is the source type (e.g. "c")
+# $(4) is the object name (e.g. "rtl8139")
+#
+define obj_template
+
+ @$(CPP) $(CFLAGS) $(CFLAGS_$(3)) $(CFLAGS_$(4)) -DOBJECT=$(4) \
+ -Wno-error -MM $(1) -MT "$(4)_DEPS" -MG -MP | \
+ sed 's/_DEPS\s*:/_DEPS =/' >> $(2)
+ @$(ECHO_E) '\n$$(BIN)/$(4).o : $(1) $$(MAKEDEPS) $$($(4)_DEPS)' \
+ '\n\t$$(QM)$(ECHO) " [BUILD] $$@"\n' \
+ '\n\t$$(RULE_$(3))\n' \
+ '\nBOBJS += $$(BIN)/$(4).o\n' \
+ $(foreach TGT,$(DEBUG_TARGETS), \
+ $(if $(RULE_$(3)_to_$(TGT)), \
+ '\n$$(BIN)/$(4).$(TGT) : $(1) $$(MAKEDEPS) $$($(4)_DEPS)' \
+ '\n\t$$(QM)$(ECHO) " [BUILD] $$@"\n' \
+ '\n\t$$(RULE_$(3)_to_$(TGT))\n' \
+ '\n$(TGT)_OBJS += $$(BIN)/$(4).$(TGT)\n' ) ) \
+ '\n$(2) : $$($(4)_DEPS)\n' \
+ '\nTAGS : $$($(4)_DEPS)\n' \
+ >> $(2)
+
+endef
+
+# Rule to generate the Makefile rules files to be included
+#
+$(BIN)/deps/%.d : % $(MAKEDEPS) $(PARSEROM)
+ $(if $(filter $(AUTO_SRCS),$<),$(call src_template,$<,$@,$(subst .,,$(suffix $<)),$(basename $(notdir $<))),@$(ECHO) 'ERROR: $< is not an AUTO_SRC' ; exit 1)
+
+# Calculate and include the list of Makefile rules files
+#
+AUTO_DEPS = $(patsubst %,$(BIN)/deps/%.d,$(AUTO_SRCS))
+include $(AUTO_DEPS)
+autodeps :
+ @$(ECHO) $(AUTO_DEPS)
+VERYCLEANUP += $(BIN)/deps
+
+# The following variables are created by the Makefile rules files
+#
+bobjs :
+ @$(ECHO) $(BOBJS)
+drivers :
+ @$(ECHO) $(DRIVERS)
+.PHONY : drivers
+roms :
+ @$(ECHO) $(ROMS)
+
+# Embedded binary
+$(BIN)/embedimg.bin: $(EMBEDDED_IMAGE)
+ $(QM)$(ECHO) " [COPY] $@"
+ $(Q)$(CP) -f $(EMBEDDED_IMAGE) $@
+
+$(BIN)/embed.o: $(BIN)/embedimg.bin
+CFLAGS_embed = -DEMBEDIMG=\"$(BIN)/embedimg.bin\"
+
+# Generate the NIC file from the parsed source files. The NIC file is
+# only for rom-o-matic.
+#
+$(BIN)/NIC : $(AUTO_DEPS)
+ @$(ECHO) '# This is an automatically generated file, do not edit' > $@
+ @$(ECHO) '# It does not affect anything in the build, ' \
+ 'it is only for rom-o-matic' >> $@
+ @$(ECHO) >> $@
+ @perl -ne 'chomp; print "$$1\n" if /\# NIC\t(.*)$$/' $^ >> $@
+CLEANUP += $(BIN)/NIC
+
+# Analyse a target name (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and
+# derive the variables:
+#
+# TGT_ELEMENTS : the elements of the target (e.g. "dfe538 prism2_pci")
+# TGT_PREFIX : the prefix type (e.g. "zrom")
+# TGT_DRIVERS : the driver for each element (e.g. "rtl8139 prism2_pci")
+# TGT_ROM_NAME : the ROM name (e.g. "dfe538")
+# TGT_MEDIA : the media type (e.g. "rom")
+#
+DRIVERS_gpxe = $(DRIVERS)
+CARD_DRIVER = $(firstword $(DRIVER_$(1)) $(1))
+TGT_ELEMENTS = $(subst --, ,$(firstword $(subst ., ,$(notdir $@))))
+TGT_PREFIX = $(word 2,$(subst ., ,$(notdir $@)))
+TGT_ROM_NAME = $(firstword $(TGT_ELEMENTS))
+TGT_DRIVERS = $(strip $(if $(DRIVERS_$(TGT_ROM_NAME)), \
+ $(DRIVERS_$(TGT_ROM_NAME)), \
+ $(foreach TGT_ELEMENT,$(TGT_ELEMENTS), \
+ $(call CARD_DRIVER,$(TGT_ELEMENT))) ))
+TGT_MEDIA = $(subst z,,$(TGT_PREFIX))
+
+# Look up ROM IDs for the current target
+# (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and derive the variables:
+#
+# TGT_PCI_VENDOR : the PCI vendor ID (e.g. "0x1186")
+# TGT_PCI_DEVICE : the PCI device ID (e.g. "0x1300")
+#
+TGT_PCI_VENDOR = $(PCI_VENDOR_$(TGT_ROM_NAME))
+TGT_PCI_DEVICE = $(PCI_DEVICE_$(TGT_ROM_NAME))
+
+# Calculate link-time options for the current target
+# (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and derive the variables:
+#
+# TGT_LD_DRIVERS : symbols to require in order to drag in the relevant drivers
+# (e.g. "obj_rtl8139 obj_prism2_pci")
+# TGT_LD_IDS : symbols to define in order to fill in ID structures in the
+# ROM header (e.g."pci_vendor_id=0x1186 pci_device_id=0x1300")
+#
+TGT_LD_DRIVERS = $(subst -,_,$(patsubst %,obj_%,$(TGT_DRIVERS)))
+TGT_LD_PREFIX = obj_$(TGT_PREFIX)prefix
+TGT_LD_IDS = $(if $(TGT_PCI_VENDOR),pci_vendor_id=$(TGT_PCI_VENDOR)) \
+ $(if $(TGT_PCI_DEVICE),pci_device_id=$(TGT_PCI_DEVICE))
+
+# Calculate linker flags based on link-time options for the current
+# target type (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and derive the
+# variables:
+#
+# TGT_LD_FLAGS : target-specific flags to pass to linker (e.g.
+# "-u obj_zpciprefix -u obj_rtl8139 -u obj_prism2_pci
+# --defsym pci_vendor=0x1186 --defsym pci_device=0x1300")
+#
+TGT_LD_FLAGS = $(foreach SYM,$(TGT_LD_PREFIX) $(TGT_LD_DRIVERS) obj_config,\
+ -u $(SYM) --defsym check_$(SYM)=$(SYM) ) \
+ $(patsubst %,--defsym %,$(TGT_LD_IDS))
+
+# Calculate makerom flags for the specific target
+# (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and derive the variables:
+#
+# TGT_MAKEROM_FLAGS : target-specific flags for makerom (e.g.
+# "-p 0x1186,0x1300")
+#
+TGT_MAKEROM_FLAGS = $(strip $(MAKEROM_FLAGS_$(TGT_ROM_NAME)) \
+ $(if $(TGT_PCI_VENDOR),$(strip -p $(TGT_PCI_VENDOR),$(TGT_PCI_DEVICE))))
+
+# Calculate list of debugging versions of objects to be included in
+# the target.
+#
+COMMA := ,
+DEBUG_LIST = $(subst $(COMMA), ,$(DEBUG))
+DEBUG_OBJ_LEVEL = $(firstword $(word 2,$(subst :, ,$(1))) 1)
+DEBUG_OBJ_BASE = $(word 1,$(subst :, ,$(1))).dbg$(call DEBUG_OBJ_LEVEL,$(1))
+DEBUG_OBJ = $(BIN)/$(call DEBUG_OBJ_BASE,$(1)).o
+DEBUG_ORIG_OBJ = $(BIN)/$(word 1,$(subst :, ,$(1))).o
+DEBUG_OBJS = $(foreach D,$(DEBUG_LIST),$(call DEBUG_OBJ,$(D)))
+DEBUG_ORIG_OBJS = $(foreach D,$(DEBUG_LIST),$(call DEBUG_ORIG_OBJ,$(D)))
+BLIB_OBJS = $(DEBUG_OBJS) $(filter-out $(DEBUG_ORIG_OBJS),$(BOBJS))
+
+# Print out all derived information for a given target.
+#
+$(BIN)/%.info :
+ @$(ECHO) 'Elements : $(TGT_ELEMENTS)'
+ @$(ECHO) 'Prefix : $(TGT_PREFIX)'
+ @$(ECHO) 'Drivers : $(TGT_DRIVERS)'
+ @$(ECHO) 'ROM name : $(TGT_ROM_NAME)'
+ @$(ECHO) 'Media : $(TGT_MEDIA)'
+ @$(ECHO)
+ @$(ECHO) 'PCI vendor : $(TGT_PCI_VENDOR)'
+ @$(ECHO) 'PCI device : $(TGT_PCI_DEVICE)'
+ @$(ECHO)
+ @$(ECHO) 'LD driver symbols : $(TGT_LD_DRIVERS)'
+ @$(ECHO) 'LD prefix symbols : $(TGT_LD_PREFIX)'
+ @$(ECHO) 'LD ID symbols : $(TGT_LD_IDS)'
+ @$(ECHO)
+ @$(ECHO) 'LD target flags : $(TGT_LD_FLAGS)'
+ @$(ECHO)
+ @$(ECHO) 'makerom target flags : $(TGT_MAKEROM_FLAGS)'
+ @$(ECHO)
+ @$(ECHO) 'Debugging objects : $(DEBUG_OBJS)'
+ @$(ECHO) 'Replaced objects : $(DEBUG_ORIG_OBJS)'
+
+# List of objects included in the last build of blib. This is needed
+# in order to correctly rebuild blib whenever the list of objects
+# changes.
+#
+BLIB_LIST = $(BIN)/.blib.list
+ifneq ($(shell cat $(BLIB_LIST)),$(BLIB_OBJS))
+$(shell $(ECHO) "$(BLIB_OBJS)" > $(BLIB_LIST))
+endif
+
+$(BLIB_LIST) :
+
+VERYCLEANUP += $(BLIB_LIST)
+
+# Library of all objects
+#
+BLIB = $(BIN)/blib.a
+$(BLIB) : $(BLIB_OBJS) $(BLIB_LIST) $(MAKEDEPS)
+ $(Q)$(RM) $(BLIB)
+ $(QM)$(ECHO) " [AR] $@"
+ $(Q)$(AR) r $@ $(BLIB_OBJS)
+ $(Q)$(RANLIB) $@
+blib : $(BLIB)
+
+# Build an intermediate object file from the objects required for the
+# specified target.
+#
+$(BIN)/%.tmp : $(BLIB) $(MAKEDEPS) $(LDSCRIPT)
+ $(QM)$(ECHO) " [LD] $@"
+ $(Q)$(LD) $(LDFLAGS) -T $(LDSCRIPT) $(TGT_LD_FLAGS) $(BLIB) -o $@ \
+ -Map $(BIN)/$*.tmp.map
+ $(Q)$(OBJDUMP) -ht $@ | $(SORTOBJDUMP) >> $(BIN)/$*.tmp.map
+
+# Keep intermediate object file (useful for debugging)
+.SECONDARY : $(BIN)/%.tmp
+
+# Show a linker map for the specified target
+#
+$(BIN)/%.map : $(BIN)/%.tmp
+ @less $(BIN)/$*.tmp.map
+
+# Extract compression information from intermediate object file
+#
+$(BIN)/%.zinfo : $(BIN)/%.tmp
+ $(QM)$(ECHO) " [ZINFO] $@"
+ $(Q)$(OBJCOPY) -O binary -j .zinfo $< $@
+
+# Build raw binary file from intermediate object file
+#
+$(BIN)/%.bin : $(BIN)/%.tmp
+ $(QM)$(ECHO) " [BIN] $@"
+ $(Q)$(OBJCOPY) -O binary -R .zinfo $< $@
+
+# Compress raw binary file
+#
+$(BIN)/%.zbin : $(BIN)/%.bin $(BIN)/%.zinfo $(ZBIN)
+ $(QM)$(ECHO) " [ZBIN] $@"
+ $(Q)$(ZBIN) $(BIN)/$*.bin $(BIN)/$*.zinfo > $@
+
+# Build bochs symbol table
+$(BIN)/%.bxs : $(BIN)/%.tmp
+ $(NM) $< | cut -d" " -f1,3 > $@
+
+# Rules for each media format. These are generated and placed in an
+# external Makefile fragment. We could do this via $(eval ...), but
+# that would require make >= 3.80.
+#
+# Note that there's an alternative way to generate most .rom images:
+# they can be copied from their 'master' ROM image using cp and
+# reprocessed with makerom to add the PCI IDs and ident string. The
+# relevant rule would look something like:
+#
+# $(BIN)/dfe538%rom : $(BIN)/rtl8139%rom
+# cat $< $@
+# $(FINALISE_rom)
+#
+# You can derive the ROM/driver relationships using the variables
+# DRIVER_<rom> and/or ROMS_<driver>.
+#
+# We don't currently do this, because (a) it would require generating
+# yet more Makefile fragments (since you need a rule for each ROM in
+# ROMS), and (b) the linker is so fast that it probably wouldn't make
+# much difference to the overall build time.
+
+media :
+ @$(ECHO) $(MEDIA)
+
+AUTO_MEDIA = $(filter-out $(NON_AUTO_MEDIA),$(MEDIA))
+automedia :
+ @$(ECHO) $(AUTO_MEDIA)
+
+# media_template : create Makefile rules for specified media
+#
+# $(1) is the media name (e.g. "rom")
+# $(2) is the full path to the .d file (e.g. "bin/deps/rom.media.d")
+#
+define media_template
+
+ @$(ECHO) "Generating Makefile rules for $(1) media"
+ @$(MKDIR) -p $(dir $(2))
+ @$(RM) $(2)
+ @$(TOUCH) $(2)
+ @$(ECHO_E) '$$(BIN)/%.$(1) : $$(BIN)/%.$(1).zbin' \
+ '\n\t$$(QM)$(ECHO) " [FINISH] $$@"' \
+ '\n\t$$(Q)$$(CP) $$< $$@' \
+ '\n\t$$(Q)$$(FINALISE_$(1))' \
+ > $(2)
+
+endef
+
+# Rule to generate the Makefile rules to be included
+#
+$(BIN)/deps/%.media.d : $(MAKEDEPS)
+ $(if $(filter $(AUTO_MEDIA),$*), \
+ $(call media_template,$*,$@), \
+ @$(ECHO) 'ERROR: $* is not an AUTO_MEDIA' ; exit 1)
+
+# Calculate and include the list of Makefile rules files
+#
+MEDIA_DEPS = $(patsubst %,$(BIN)/deps/%.media.d,$(AUTO_MEDIA))
+mediadeps :
+ @$(ECHO) $(MEDIA_DEPS)
+include $(MEDIA_DEPS)
+
+# The "allXXXs" targets for each suffix
+#
+allall: allroms allzroms allpxes allisos alldsks
+allroms allzroms : all%s : $(foreach ROM,$(ROMS),$(BIN)/$(ROM).%)
+allpxes allisos alldsks : all%s : $(foreach DRIVER,$(DRIVERS),$(BIN)/$(DRIVER).%)
+
+# Alias for gpxe.%
+#
+$(BIN)/etherboot.% : $(BIN)/gpxe.%
+ ln -sf $(notdir $<) $@
+
+# Wrap up binary blobs
+#
+$(BIN)/%.o : payload/%.img
+ $(QM)echo " [WRAP] $@"
+ $(Q)$(LD) -b binary -r -o $@ $< --undefined obj_payload \
+ --defsym obj_$*=0
+
+BOBJS += $(patsubst payload/%.img,$(BIN)/%.o,$(wildcard payload/*.img))
+
+# The compression utilities
+#
+$(NRV2B) : util/nrv2b.c $(MAKEDEPS)
+ $(QM)$(ECHO) " [HOSTCC] $@"
+ $(Q)$(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG \
+ -DBITSIZE=32 -DENDIAN=0 -o $@ $<
+CLEANUP += $(NRV2B)
+
+$(ZBIN) : util/zbin.c util/nrv2b.c $(MAKEDEPS)
+ $(QM)$(ECHO) " [HOSTCC] $@"
+ $(Q)$(HOST_CC) -O2 -o $@ $<
+CLEANUP += $(ZBIN)
+
+# Auto-incrementing build serial number. Append "bs" to your list of
+# build targets to get a serial number printed at the end of the
+# build. Enable -DBUILD_SERIAL in order to see it when the code runs.
+#
+BUILDSERIAL_H = config/.buildserial.h
+BUILDSERIAL_NOW = config/.buildserial.now
+BUILDSERIAL_NEXT = config/.buildserial.next
+
+$(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT) :
+ $(ECHO) 1 > $@
+
+$(BUILDSERIAL_H) : $(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT)
+ $(ECHO) '#define BUILD_SERIAL_NUM $(shell cat $<)' > $@
+
+ifeq ($(filter bs,$(MAKECMDGOALS)),bs)
+$(shell diff -q $(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT) > /dev/null || \
+ cp -f $(BUILDSERIAL_NEXT) $(BUILDSERIAL_NOW))
+endif
+
+bs : $(BUILDSERIAL_NOW)
+ @$(ECHO) $$(( $(shell cat $<) + 1 )) > $(BUILDSERIAL_NEXT)
+ @$(ECHO) "Build serial number is $(shell cat $<)"
+
+# List of available architectures
+#
+ARCHS = $(filter-out CVS,$(patsubst arch/%,%,$(wildcard arch/*)))
+archs :
+ @$(ECHO) $(ARCHS)
+
+OTHER_ARCHS = $(filter-out $(ARCH),$(ARCHS))
+otherarchs :
+ @$(ECHO) $(OTHER_ARCHS)
+
+# Build the TAGS file for emacs
+#
+TAGS : TAGS.$(ARCH)
+
+TAGS.$(ARCH) :
+ ctags -e -R -f $@ --exclude=bin \
+ $(foreach ARCH,$(OTHER_ARCHS),--exclude=arch/$(ARCH))
+CLEANUP += TAGS*
+
+# Symbol table checks
+#
+SYMTAB = $(BIN)/symtab
+$(SYMTAB) : $(BLIB)
+ $(OBJDUMP) -w -t $< > $@
+
+CLEANUP += $(BIN)/symtab
+
+symcheck : $(SYMTAB)
+ $(SYMCHECK) $<
+
+# Force rebuild for any given target
+#
+$(BIN)/%.rebuild :
+ rm -f $(BIN)/$*
+ $(MAKE) $(MAKEFLAGS) $(BIN)/$*
+
+# Documentation
+#
+$(BIN)/doxygen.cfg : doxygen.cfg $(MAKEDEPS)
+ $(PERL) -pe 's{\@SRCDIRS\@}{$(SRCDIRS)}; ' \
+ -e 's{\@BIN\@}{$(BIN)}; ' \
+ -e 's{\@ARCH\@}{$(ARCH)}; ' \
+ $< > $@
+
+$(BIN)/doc : $(BIN)/doxygen.cfg
+ $(DOXYGEN) $<
+
+.PHONY : $(BIN)/doc
+
+VERYCLEANUP += $(BIN)/doc
+
+doc : $(BIN)/doc
+
+docview :
+ @[ -f $(BIN)/doc/html/index.html ] || $(MAKE) $(BIN)/doc
+ @if [ -n "$$BROWSER" ] ; then \
+ ( $$BROWSER $(BIN)/doc/html/index.html & ) ; \
+ else \
+ $(ECHO) "Documentation index in $(BIN)/doc/html/index.html" ; \
+ fi
+
+# Clean-up
+#
+clean :
+ $(RM) $(CLEANUP)
+
+veryclean : clean
+ $(RM) -r $(VERYCLEANUP)
+
+# Make clean tarballs for release
+
+tarball : ../VERSION
+ ($(ECHO) -n $(VERSION) ''; date -u +'%Y-%m-%d') > ../VERSION
+ $(RM) -r /tmp/$(USER)/gpxe-$(VERSION)
+ mkdir -p /tmp/$(USER)/gpxe-$(VERSION)
+ cp -rP .. /tmp/$(USER)/gpxe-$(VERSION)
+ ( cd /tmp/$(USER)/gpxe-$(VERSION)/src ; $(MAKE) veryclean ; $(RM) -r bin/deps )
+ ( cd /tmp/$(USER); tar cf /tmp/$(USER)/gpxe-$(VERSION).tar --exclude ".git*" --exclude "#*" \
+ --exclude "*~" gpxe-$(VERSION) )
+ bzip2 -9 < /tmp/$(USER)/gpxe-$(VERSION).tar > /tmp/$(USER)/gpxe-$(VERSION).tar.bz2
+ gzip -9 < /tmp/$(USER)/gpxe-$(VERSION).tar > /tmp/$(USER)/gpxe-$(VERSION).tar.gz
+ $(RM) -r /tmp/$(USER)/gpxe-$(VERSION)
+ $(RM) /tmp/$(USER)/gpxe-$(VERSION).tar
+ ( cd /tmp/$(USER) ; tar -zxf /tmp/$(USER)/gpxe-$(VERSION).tar.gz )
diff --git a/gpxe/src/README.cvs b/gpxe/src/README.cvs
new file mode 100644
index 00000000..56a24f9f
--- /dev/null
+++ b/gpxe/src/README.cvs
@@ -0,0 +1,65 @@
+Changes should be committed to the CVS HEAD only when they are in a
+working state. The definition of "working" is somewhat liquid; a good
+guiding principle is that anyone checking out HEAD should receive a
+checkout of working software.
+
+When you want to work on changes that are likely to temporarily break
+large swathes of code, you should probably work on a private branch.
+Since CVS branching and merging is something of a black art, here are
+some simple step-by-step instructions for creating and using a branch.
+
+To create your private branch:
+
+ # Get most up-to-date tree before branching
+ cvs update
+ # Create a branch called "my-branch"
+ cvs tag -b my-branch
+ # Switch working copy to the "my-branch" branch
+ cvs update -r my-branch
+
+At this point you'll be on a branch called "my-branch". Any changes
+you make will not affect people working on HEAD, or on other branches.
+
+Use name for your branch that is both descriptive and unique.
+Starting the branch name with your SourceForge username
+(e.g. "mcb30-realmode-redesign") is a good idea.
+
+When you want to merge the changes on your branch back into HEAD, do
+the following:
+
+ # Ensure there are no not-yet-checked-in modifications in your tree)
+ cvs -q update
+ # Tag the merge point in the "my-branch" branch
+ cvs tag -c my-branch-merge-1
+ # Switch working copy back to HEAD
+ cvs update -A
+ # Merge changes from the branch
+ cvs update -j my-branch
+ # Commit merged changes to HEAD
+ cvs commit
+
+If you then want to continue working further on the "my-branch" branch,
+do the following
+
+ # Switch working copy back to the "my-branch" branch
+ cvs update -r my-branch
+
+and then when you want to merge some more changes back to HEAD:
+
+ # Ensure there are no not-yet-checked-in modifications in your tree)
+ cvs -q update
+ # Tag the merge point in the "my-branch" branch
+ cvs tag -c my-branch-merge-2
+ # Switch working copy back to HEAD
+ cvs update -A
+ # Merge changes from the branch
+ cvs update -j my-branch-merge-1 -j my-branch
+ # Commit merged changes to HEAD
+ cvs commit
+
+Note that the format of the "merge changes from the branch" command has
+changed, because this time you need to only merge changes since the last
+merge point.
+
+When you have finished with your branch and merged all the changes
+back to HEAD, simply stop using the branch.
diff --git a/gpxe/src/README.pixify b/gpxe/src/README.pixify
new file mode 100644
index 00000000..9aef25d9
--- /dev/null
+++ b/gpxe/src/README.pixify
@@ -0,0 +1,90 @@
+This file documents the driver changes needed to support use as part
+of a PXE stack.
+
+PROPER WAY
+==========
+
+1. The probe() routine.
+
+There are three additional fields that need to be filled in the nic
+structure: ioaddr, irqno and irq.
+
+ ioaddr is the base I/O address and seems to be for information only;
+ no use will be made of this value other than displaying it on the
+ screen.
+
+ irqno must be the IRQ number for the NIC. For PCI NICs this can
+ simply be copied from pci->irq.
+
+ irq is a function pointer, like poll and transmit. It must point to
+ the driver's irq() function.
+
+2. The poll() routine.
+
+This must take an additional parameter: "int retrieve". Calling
+poll() with retrieve!=0 should function exactly as before. Calling
+poll() with retrieve==0 indicates that poll() should check for the
+presence of a packet to read, but must *not* read the packet. The
+packet will be read by a subsequent call to poll() with retrieve!=0.
+
+The easiest way to implement this is to insert the line
+ if ( ! retrieve ) return 1;
+between the "is there a packet ready" and the "fetch packet" parts of
+the existing poll() routine.
+
+Care must be taken that a call to poll() with retrieve==0 does not
+clear the NIC's "packet ready" status indicator, otherwise the
+subsequent call to poll() with retrieve!=0 will fail because it will
+think that there is no packet to read.
+
+poll() should also acknowledge and clear the NIC's "packet received"
+interrupt. It does not need to worry about enabling/disabling
+interrupts; this is taken care of by calls to the driver's irq()
+routine.
+
+Etherboot will forcibly regenerate an interrupt if a packet remains
+pending after all interrupts have been acknowledged. You can
+therefore get away with having poll() just acknolwedge and clear all
+NIC interrupts, without particularly worrying about exactly when this
+should be done.
+
+3. The irq() routine.
+
+This is a new routine, with prototype
+ void DRIVER_irq ( struct nic *nic, irq_action_t action );
+"action" takes one of three possible values: ENABLE, DISABLE or FORCE.
+ENABLE and DISABLE mean to enable/disable the NIC's "packet received"
+interrupt. FORCE means that the NIC should be forced to generate a
+fake "packet received" interrupt.
+
+If you are unable to implement FORCE, your NIC will not work when
+being driven via the UNDI interface under heavy network traffic
+conditions. Since Etherboot's UNDI driver (make bin/undi.zpxe) is the
+only program known to use this interface, it probably doesn't really
+matter.
+
+
+QUICK AND DIRTY WAY
+===================
+
+It is possible to use the system timer interrupt (IRQ 0) rather than a
+genuine NIC interrupt. Since there is a constant stream of timer
+interrupts, the net upshot is a whole load of spurious "NIC"
+interrupts that have no effect other than to cause unnecessary PXE API
+calls. It's inefficient but it works.
+
+To achieve this, simply set nic->irqno=0 in probe() and point nic->irq
+to a dummy routine that does nothing. Add the line
+ if ( ! retrieve ) return 1;
+at the beginning of poll(), to prevent the packet being read (and
+discarded) when poll() is called with retrieve==0;
+
+
+UNCONVERTED DRIVERS
+===================
+
+Drivers that have not yet been converted should continue to function
+when not used as part of a PXE stack, although there will be a
+harmless compile-time warning about assignment from an incompatible
+pointer type in the probe() function, since the prototype for the
+poll() function is missing the "int retrieve" parameter.
diff --git a/gpxe/src/arch/i386/Config b/gpxe/src/arch/i386/Config
new file mode 100644
index 00000000..1c086ecc
--- /dev/null
+++ b/gpxe/src/arch/i386/Config
@@ -0,0 +1,148 @@
+# -*- makefile -*-
+
+##############################################################################
+##############################################################################
+#
+# IMPORTANT!
+#
+# The use of this file to set options that affect only single object
+# files is deprecated, because changing anything in this file results
+# in a complete rebuild, which is slow. All options are gradually
+# being migrated to config.h, which does not suffer from this problem.
+#
+# Only options that affect the entire build (e.g. overriding the $(CC)
+# Makefile variable) should be placed in here.
+#
+##############################################################################
+##############################################################################
+
+
+# Config for i386 Etherboot
+#
+# Do not delete the tag OptionDescription and /OptionDescription
+# It is used to automatically generate the documentation.
+#
+# @OptionDescrition@
+#
+# BIOS interface options:
+#
+# -DPCBIOS
+# Compile in support for the normal pcbios
+# -DLINUXBIOS
+# Compile in support for LinuxBIOS
+# -DBBS_BUT_NOT_PNP_COMPLIANT
+# Some BIOSes claim to be PNP but they don't conform
+# to the BBS spec which specifies that ES:DI must
+# point to the string $PnP on entry. This option
+# works around those. This option must be added to
+# LCONFIG.
+# -DNO_DELAYED_INT
+# Take control as soon as BIOS detects the ROM.
+# Normally hooks onto INT18H or INT19H. Use only if you
+# have a very non-conformant BIOS as it bypasses
+# BIOS initialisation of devices. This only works for
+# legacy ROMs, i.e. PCI_PNP_HEADER not defined.
+# This option was formerly called NOINT19H.
+# -DBOOT_INT18H
+# Etherboot normally hooks onto INT19H for legacy ROMs.
+# You can choose to hook onto INT18H (BASIC interpreter
+# entry point) instead. This entry point is used when
+# all boot devices have been exhausted. This option must
+# be added to LCONFIG.
+# -DCONFIG_PCI_DIRECT
+# Define this for PCI BIOSes that do not implement
+# BIOS32 or not correctly. Normally not needed.
+# Only works for BIOSes of a certain era.
+# -DCONFIG_TSC_CURRTICKS
+# Uses the processor time stamp counter instead of reading
+# the BIOS time counter. This allows Etherboot to work
+# even without a BIOS. This only works on late model
+# 486s and above.
+# -DCONFIG_NO_TIMER2
+# Some systems do not have timer2 implemented.
+# If you have a RTC this will allow you to roughly calibrate
+# it using outb instructions.
+#
+# Extended cpu options
+
+# -DCONFIG_X86_64
+# Compile in support for booting x86_64 64bit binaries.
+#
+# PXE loader options:
+#
+# -DPXELOADER_KEEP_ALL
+# Prevent PXE loader (prefix) from unloading the
+# PXE stack. You will want to use this if, for
+# example, you are booting via PXE-on-floppy.
+# You may want to use it under certain
+# circumstances when using the Etherboot UNDI
+# driver; these are complex and best practice is
+# not yet established.
+#
+# Obscure options you probably don't need to touch:
+#
+# -DIGNORE_E820_MAP
+# Ignore the memory map returned by the E820 BIOS
+# call. May be necessary on some buggy BIOSes.
+# -DT503_AUI
+# Use AUI by default on 3c503 cards.
+# -DFLATTEN_REAL_MODE
+# Use 4GB segment limits when calling out to or
+# returning to real-mode code. This is necessary to
+# work around some buggy code (e.g. OpenBSD's pxeboot)
+# that uses flat real-mode without being sufficiently
+# paranoid about the volatility of its segment limits.
+
+#
+# @/OptionDescription@
+
+# BIOS select don't change unless you know what you are doing
+# CFLAGS+= -DPCBIOS
+
+# Compile in k8/hammer support
+# CFLAGS+= -DCONFIG_X86_64
+
+# Options to make a version of Etherboot that will work under linuxBIOS.
+# CFLAGS+= -DLINUXBIOS -DCONFIG_TSC_CURRTICKS -DCONSOLE_SERIAL -DCOMCONSOLE=0x3f8 -DCOMPRESERVE -DCONFIG_PCI_DIRECT -DELF_IMAGE
+
+# These options affect the loader that is prepended to the Etherboot image
+# LCONFIG+= -DBBS_BUT_NOT_PNP_COMPLIANT
+# LCONFIG+= -DBOOT_INT18H
+
+# Produce code that will work with OpenBSD's pxeboot
+# CFLAGS+= -DFLATTEN_REAL_MODE
+
+CFLAGS+= -fstrength-reduce -fomit-frame-pointer -march=i386
+# Squeeze the code in as little space as possible.
+# gcc3 needs a different syntax to gcc2 if you want to avoid spurious warnings.
+GCC_VERSION = $(subst ., ,$(shell $(CC) -dumpversion))
+GCC_MAJORVERSION = $(firstword $(GCC_VERSION))
+ifeq ($(GCC_MAJORVERSION),2)
+CFLAGS+= -malign-jumps=1 -malign-loops=1 -malign-functions=1
+else
+CFLAGS+= -falign-jumps=1 -falign-loops=1 -falign-functions=1
+endif
+
+# this is almost always a win. the kernel uses it, too.
+CFLAGS+= -mpreferred-stack-boundary=2
+
+# use regparm for all functions - C functions called from assembly (or
+# vice versa) need __cdecl now
+CFLAGS+= -mregparm=3
+
+# use -mrtd (same __cdecl requirements as above)
+CFLAGS+= -mrtd
+
+# this is the logical complement to -mregparm=3.
+# it doesn't currently buy us anything, but if anything ever tries
+# to return small structures, let's be prepared
+CFLAGS+= -freg-struct-return
+
+LDFLAGS+= -N --no-check-sections
+
+ifeq "$(shell uname -s)" "FreeBSD"
+CFLAGS+= -DIMAGE_FREEBSD -DELF_IMAGE -DAOUT_IMAGE
+endif
+
+# An alternate location for isolinux.bin can be set here
+# ISOLINUX_BIN=/path/to/isolinux.bin
diff --git a/gpxe/src/arch/i386/Makefile b/gpxe/src/arch/i386/Makefile
new file mode 100644
index 00000000..da7976df
--- /dev/null
+++ b/gpxe/src/arch/i386/Makefile
@@ -0,0 +1,107 @@
+# Locations of utilities
+#
+ISOLINUX_BIN = /usr/lib/syslinux/isolinux.bin
+
+# i386-specific directories containing source files
+#
+SRCDIRS += arch/i386/core arch/i386/transitions arch/i386/prefix
+SRCDIRS += arch/i386/firmware/pcbios
+SRCDIRS += arch/i386/image
+SRCDIRS += arch/i386/drivers
+SRCDIRS += arch/i386/drivers/bus
+SRCDIRS += arch/i386/drivers/net
+SRCDIRS += arch/i386/drivers/disk
+SRCDIRS += arch/i386/interface/pcbios
+SRCDIRS += arch/i386/interface/pxe
+
+# The various xxx_loader.c files are #included into core/loader.c and
+# should not be compiled directly.
+#
+NON_AUTO_SRCS += arch/i386/core/aout_loader.c
+NON_AUTO_SRCS += arch/i386/core/freebsd_loader.c
+NON_AUTO_SRCS += arch/i386/core/wince_loader.c
+
+# unnrv2b.S is used to generate a 16-bit as well as a 32-bit object.
+#
+OBJS_unnrv2b = unnrv2b unnrv2b16
+CFLAGS_unnrv2b16 = -DCODE16
+
+# We need to undefine the default macro "i386" when compiling .S
+# files, otherwise ".arch i386" translates to ".arch 1"...
+#
+CFLAGS_S += -Ui386
+
+# The i386 linker script
+#
+LDSCRIPT = arch/i386/scripts/i386.lds
+
+# Media types.
+#
+MEDIA += rom
+MEDIA += pxe
+MEDIA += kpxe
+MEDIA += elf
+MEDIA += elfd
+MEDIA += lmelf
+MEDIA += lmelfd
+MEDIA += lkrn
+MEDIA += bImage
+MEDIA += dsk
+MEDIA += nbi
+MEDIA += hd
+MEDIA += raw
+MEDIA += com
+MEDIA += exe
+
+# Special target for building Master Boot Record binary
+$(BIN)/mbr.bin : $(BIN)/mbr.o
+ $(OBJCOPY) -O binary $< $@
+
+# Some suffixes (e.g. %.fd0) are generated directly from other
+# finished files (e.g. %.dsk), rather than having their own prefix.
+
+# rule to write disk images to /dev/fd0
+NON_AUTO_MEDIA += fd0
+%fd0 : %dsk
+ dd if=$< bs=512 conv=sync of=/dev/fd0
+ sync
+
+# rule to create padded disk images
+NON_AUTO_MEDIA += pdsk
+%pdsk : %dsk
+ cp $< $@
+ $(PERL) ./util/dskpad.pl $@
+
+# rule to make a non-emulation ISO boot image
+NON_AUTO_MEDIA += iso
+%iso: %lkrn util/geniso
+ ISOLINUX_BIN=$(ISOLINUX_BIN) bash util/geniso $@ $<
+
+# rule to make a floppy emulation ISO boot image
+NON_AUTO_MEDIA += liso
+%liso: %lkrn util/genliso
+ bash util/genliso $@ $<
+
+# rule to make a USB disk image
+$(BIN)/usbdisk.bin : $(BIN)/usbdisk.o
+ $(OBJCOPY) -O binary $< $@
+
+NON_AUTO_MEDIA += usb
+%usb: $(BIN)/usbdisk.bin %hd
+ cat $^ > $@
+
+# Add NON_AUTO_MEDIA to the media list, so that they show up in the
+# output of "make"
+#
+MEDIA += $(NON_AUTO_MEDIA)
+
+# Shortcut to allow typing just
+# make bin-kir/%
+# rather than
+# make -f arch/i386/kir-Makefile bin-kir/%
+# for building a KEEP_IT_REAL flavour.
+#
+$(BIN)-kir/% : kir-target
+ $(MAKE) -f arch/i386/kir-Makefile $(MAKECMDGOALS)
+
+.PHONY : kir-target
diff --git a/gpxe/src/arch/i386/README.i386 b/gpxe/src/arch/i386/README.i386
new file mode 100644
index 00000000..b9b79cc4
--- /dev/null
+++ b/gpxe/src/arch/i386/README.i386
@@ -0,0 +1,197 @@
+Etherboot/NILO i386 initialisation path and external call interface
+===================================================================
+
+1. Background
+
+GCC compiles 32-bit code. It is capable of producing
+position-independent code, but the resulting binary is about 25%
+bigger than the corresponding fixed-position code. Since one main use
+of Etherboot is as firmware to be burned into an EPROM, code size must
+be kept as small as possible.
+
+This means that we want to compile fixed-position code with GCC, and
+link it to have a predetermined start address. The problem then is
+that we must know the address that the code will be loaded to when it
+runs. There are several ways to solve this:
+
+1. Pick an address, link the code with this start address, then make
+ sure that the code gets loaded at that location. This is
+ problematic, because we may pick an address that we later end up
+ wanting to use to load the operating system that we're booting.
+
+2. Pick an address, link the code with this start address, then set up
+ virtual addressing so that the virtual addresses match the
+ link-time addresses regardless of the real physical address that
+ the code is loaded to. This enables us to relocate Etherboot to
+ the top of high memory, where it will be out of the way of any
+ loading operating system.
+
+3. Link the code with a text start address of zero and a data start
+ address also of zero. Use 16-bit real mode and the
+ quasi-position-independence it gives you via segment addressing.
+ Doing this requires that we generate 16-bit code, rather than
+ 32-bit code, and restricts us to a maximum of 64kB in each segment.
+
+There are other possible approaches (e.g. including a relocation table
+and code that performs standard dynamic relocation), but the three
+options listed above are probably the best available.
+
+Etherboot can be invoked in a variety of ways (ROM, floppy, as a PXE
+NBP, etc). Several of these ways involve control being passed to
+Etherboot with the CPU in 16-bit real mode. Some will involve the CPU
+being in 32-bit protected mode, and there's an outside chance that
+some may involve the CPU being in 16-bit protected mode. We will
+almost certainly have to effect a CPU mode change in order to reach
+the mode we want to be in to execute the C code.
+
+Additionally, Etherboot may wish to call external routines, such as
+BIOS interrupts, which must be called in 16-bit real mode. When
+providing a PXE API, Etherboot must provide a mechanism for external
+code to call it from 16-bit real mode.
+
+Not all i386 builds of Etherboot will want to make real-mode calls.
+For example, when built for LinuxBIOS rather than the standard PCBIOS,
+no real-mode calls are necessary.
+
+For the ultimate in PXE compatibility, we may want to build Etherboot
+to run permanently in real mode.
+
+There is a wide variety of potential combinations of mode switches
+that we may wish to implement. There are additional complications,
+such as the inability to access a high-memory stack when running in
+real mode.
+
+2. Transition libraries
+
+To handle all these various combinations of mode switches, we have
+several "transition" libraries in Etherboot. We also have the concept
+of an "internal" and an "external" environment. The internal
+environment is the environment within which we can execute C code.
+The external environment is the environment of whatever external code
+we're trying to interface to, such as the system BIOS or a PXE NBP.
+
+As well as having a separate addressing scheme, the internal
+environment also has a separate stack.
+
+The transition libraries are:
+
+a) librm
+
+librm handles transitions between an external 16-bit real-mode
+environment and an internal 32-bit protected-mode environment with
+virtual addresses.
+
+b) libkir
+
+libkir handles transitions between an external 16-bit real-mode (or
+16:16 or 16:32 protected-mode) environment and an internal 16-bit
+real-mode (or 16:16 protected-mode) environment.
+
+c) libpm
+
+libpm handles transitions between an external 32-bit protected-mode
+environment with flat physical addresses and an internal 32-bit
+protected-mode environment with virtual addresses.
+
+The transition libraries handle the transitions required when
+Etherboot is started up for the first time, the transitions required
+to execute any external code, and the transitions required when
+Etherboot exits (if it exits). When Etherboot provides a PXE API,
+they also handle the transitions required when a PXE client makes a
+PXE API call to Etherboot.
+
+Etherboot may use multiple transition libraries. For example, an
+Etherboot ELF image does not require librm for its initial transitions
+from prefix to runtime, but may require librm for calling external
+real-mode functions.
+
+3. Setup and initialisation
+
+Etherboot is conceptually divided into the prefix, the decompressor,
+and the runtime image. (For non-compressed images, the decompressor
+is a no-op.) The complete image comprises all three parts and is
+distinct from the runtime image, which exclude the prefix and the
+decompressor.
+
+The prefix does several tasks:
+
+ Load the complete image into memory. (For example, the floppy
+ prefix issues BIOS calls to load the remainder of the complete image
+ from the floppy disk into RAM, and the ISA ROM prefix copies the ROM
+ contents into RAM for faster access.)
+
+ Call the decompressor, if the runtime image is compressed. This
+ decompresses the runtime image.
+
+ Call the runtime image's setup() routine. This is a routine
+ implemented in assembly code which sets up the internal environment
+ so that C code can execute.
+
+ Call the runtime image's arch_initialise() routine. This is a
+ routine implemented in C which does some basic startup tasks, such
+ as initialising the console device, obtaining a memory map and
+ relocating the runtime image to high memory.
+
+ Call the runtime image's arch_main() routine. This records the exit
+ mechanism requested by the prefix and calls main(). (The prefix
+ needs to register an exit mechanism because by the time main()
+ returns, the memory occupied by the prefix has most likely been
+ overwritten.)
+
+When acting as a PXE ROM, the ROM prefix contains an UNDI loader
+routine in addition to its usual code. The UNDI loader performs a
+similar sequence of steps:
+
+ Load the complete image into memory.
+
+ Call the decompressor.
+
+ Call the runtime image's setup() routine.
+
+ Call the runtime image's arch_initialise() routine.
+
+ Call the runtime image's install_pxe_stack() routine.
+
+ Return to caller.
+
+The runtime image's setup() routine will perform the following steps:
+
+ Switch to the internal environment using an appropriate transition
+ library. This will record the parameters of the external
+ environment.
+
+ Set up the internal environment: load a stack, and set up a GDT for
+ virtual addressing if virtual addressing is to be used.
+
+ Switch back to the external environment using the transition
+ library. This will record the parameters of the internal
+ environment.
+
+Once the setup() routine has returned, the internal environment has been
+set up ready for C code to run. The prefix can call C routines using
+a function from the transition library.
+
+The runtime image's arch_initialise() routine will perform the
+following steps:
+
+ Zero the bss
+
+ Initialise the console device(s) and print a welcome message.
+
+ Obtain a memory map via the INT 15,E820 BIOS call or suitable
+ fallback mechanism. [not done if libkir is being used]
+
+ Relocate the runtime image to the top of high memory. [not done if
+ libkir is being used]
+
+ Install librm to base memory. [done only if librm is being used]
+
+ Call initialise().
+
+ Return to the prefix, setting registers to indicate to the prefix
+ the new location of the transition library, if applicable. Which
+ registers these are is specific to the transition library being
+ used.
+
+Once the arch_initialise() routine has returned, the prefix will
+probably call arch_main().
diff --git a/gpxe/src/arch/i386/core/aout_loader.c b/gpxe/src/arch/i386/core/aout_loader.c
new file mode 100644
index 00000000..f85620e9
--- /dev/null
+++ b/gpxe/src/arch/i386/core/aout_loader.c
@@ -0,0 +1,144 @@
+/* a.out */
+struct exec {
+ unsigned long a_midmag; /* flags<<26 | mid<<16 | magic */
+ unsigned long a_text; /* text segment size */
+ unsigned long a_data; /* initialized data size */
+ unsigned long a_bss; /* uninitialized data size */
+ unsigned long a_syms; /* symbol table size */
+ unsigned long a_entry; /* entry point */
+ unsigned long a_trsize; /* text relocation size */
+ unsigned long a_drsize; /* data relocation size */
+};
+
+struct aout_state {
+ struct exec head;
+ unsigned long curaddr;
+ int segment; /* current segment number, -1 for none */
+ unsigned long loc; /* start offset of current block */
+ unsigned long skip; /* padding to be skipped to current segment */
+ unsigned long toread; /* remaining data to be read in the segment */
+};
+
+static struct aout_state astate;
+
+static sector_t aout_download(unsigned char *data, unsigned int len, int eof);
+static inline os_download_t aout_probe(unsigned char *data, unsigned int len)
+{
+ unsigned long start, mid, end, istart, iend;
+ if (len < sizeof(astate.head)) {
+ return 0;
+ }
+ memcpy(&astate.head, data, sizeof(astate.head));
+ if ((astate.head.a_midmag & 0xffff) != 0x010BL) {
+ return 0;
+ }
+
+ printf("(a.out");
+ aout_freebsd_probe();
+ printf(")... ");
+ /* Check the aout image */
+ start = astate.head.a_entry;
+ mid = (((start + astate.head.a_text) + 4095) & ~4095) + astate.head.a_data;
+ end = ((mid + 4095) & ~4095) + astate.head.a_bss;
+ istart = 4096;
+ iend = istart + (mid - start);
+ if (!prep_segment(start, mid, end, istart, iend))
+ return dead_download;
+ astate.segment = -1;
+ astate.loc = 0;
+ astate.skip = 0;
+ astate.toread = 0;
+ return aout_download;
+}
+
+static sector_t aout_download(unsigned char *data, unsigned int len, int eof)
+{
+ unsigned int offset; /* working offset in the current data block */
+
+ offset = 0;
+
+#ifdef AOUT_LYNX_KDI
+ astate.segment++;
+ if (astate.segment == 0) {
+ astate.curaddr = 0x100000;
+ astate.head.a_entry = astate.curaddr + 0x20;
+ }
+ memcpy(phys_to_virt(astate.curaddr), data, len);
+ astate.curaddr += len;
+ return 0;
+#endif
+
+ do {
+ if (astate.segment != -1) {
+ if (astate.skip) {
+ if (astate.skip >= len - offset) {
+ astate.skip -= len - offset;
+ break;
+ }
+ offset += astate.skip;
+ astate.skip = 0;
+ }
+
+ if (astate.toread) {
+ if (astate.toread >= len - offset) {
+ memcpy(phys_to_virt(astate.curaddr), data+offset,
+ len - offset);
+ astate.curaddr += len - offset;
+ astate.toread -= len - offset;
+ break;
+ }
+ memcpy(phys_to_virt(astate.curaddr), data+offset, astate.toread);
+ offset += astate.toread;
+ astate.toread = 0;
+ }
+ }
+
+ /* Data left, but current segment finished - look for the next
+ * segment. This is quite simple for a.out files. */
+ astate.segment++;
+ switch (astate.segment) {
+ case 0:
+ /* read text */
+ astate.curaddr = astate.head.a_entry;
+ astate.skip = 4096;
+ astate.toread = astate.head.a_text;
+ break;
+ case 1:
+ /* read data */
+ /* skip and curaddr may be wrong, but I couldn't find
+ * examples where this failed. There is no reasonable
+ * documentation for a.out available. */
+ astate.skip = ((astate.curaddr + 4095) & ~4095) - astate.curaddr;
+ astate.curaddr = (astate.curaddr + 4095) & ~4095;
+ astate.toread = astate.head.a_data;
+ break;
+ case 2:
+ /* initialize bss and start kernel */
+ astate.curaddr = (astate.curaddr + 4095) & ~4095;
+ astate.skip = 0;
+ astate.toread = 0;
+ memset(phys_to_virt(astate.curaddr), '\0', astate.head.a_bss);
+ goto aout_startkernel;
+ default:
+ break;
+ }
+ } while (offset < len);
+
+ astate.loc += len;
+
+ if (eof) {
+ unsigned long entry;
+
+aout_startkernel:
+ entry = astate.head.a_entry;
+ done(1);
+
+ aout_freebsd_boot();
+#ifdef AOUT_LYNX_KDI
+ xstart32(entry);
+#endif
+ printf("unexpected a.out variant\n");
+ longjmp(restart_etherboot, -2);
+ }
+ return 0;
+}
diff --git a/gpxe/src/arch/i386/core/basemem_packet.c b/gpxe/src/arch/i386/core/basemem_packet.c
new file mode 100644
index 00000000..64e0bcc1
--- /dev/null
+++ b/gpxe/src/arch/i386/core/basemem_packet.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Packet buffer in base memory. Used by various components which
+ * need to pass packets to and from external real-mode code.
+ *
+ */
+
+#include <basemem_packet.h>
+
+#undef basemem_packet
+char __bss16_array ( basemem_packet, [BASEMEM_PACKET_LEN] );
diff --git a/gpxe/src/arch/i386/core/cpu.c b/gpxe/src/arch/i386/core/cpu.c
new file mode 100644
index 00000000..c24fa4e6
--- /dev/null
+++ b/gpxe/src/arch/i386/core/cpu.c
@@ -0,0 +1,73 @@
+#include <stdint.h>
+#include <string.h>
+#include <cpu.h>
+
+/** @file
+ *
+ * CPU identification
+ *
+ */
+
+/**
+ * Test to see if CPU flag is changeable
+ *
+ * @v flag Flag to test
+ * @ret can_change Flag is changeable
+ */
+static inline int flag_is_changeable ( unsigned int flag ) {
+ uint32_t f1, f2;
+
+ __asm__ ( "pushfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "movl %0,%1\n\t"
+ "xorl %2,%0\n\t"
+ "pushl %0\n\t"
+ "popfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "popfl\n\t"
+ : "=&r" ( f1 ), "=&r" ( f2 )
+ : "ir" ( flag ) );
+
+ return ( ( ( f1 ^ f2 ) & flag ) != 0 );
+}
+
+/**
+ * Get CPU information
+ *
+ * @v cpu CPU information structure to fill in
+ */
+void get_cpuinfo ( struct cpuinfo_x86 *cpu ) {
+ unsigned int cpuid_level;
+ unsigned int cpuid_extlevel;
+ unsigned int discard_1, discard_2, discard_3;
+
+ memset ( cpu, 0, sizeof ( *cpu ) );
+
+ /* Check for CPUID instruction */
+ if ( ! flag_is_changeable ( X86_EFLAGS_ID ) ) {
+ DBG ( "CPUID not supported\n" );
+ return;
+ }
+
+ /* Get features, if present */
+ cpuid ( 0x00000000, &cpuid_level, &discard_1,
+ &discard_2, &discard_3 );
+ if ( cpuid_level >= 0x00000001 ) {
+ cpuid ( 0x00000001, &discard_1, &discard_2,
+ &discard_3, &cpu->features );
+ } else {
+ DBG ( "CPUID cannot return capabilities\n" );
+ }
+
+ /* Get 64-bit features, if present */
+ cpuid ( 0x80000000, &cpuid_extlevel, &discard_1,
+ &discard_2, &discard_3 );
+ if ( ( cpuid_extlevel & 0xffff0000 ) == 0x80000000 ) {
+ if ( cpuid_extlevel >= 0x80000001 ) {
+ cpuid ( 0x80000001, &discard_1, &discard_2,
+ &discard_3, &cpu->amd_features );
+ }
+ }
+}
diff --git a/gpxe/src/arch/i386/core/etherboot.prefix.lds b/gpxe/src/arch/i386/core/etherboot.prefix.lds
new file mode 100644
index 00000000..3550a2a3
--- /dev/null
+++ b/gpxe/src/arch/i386/core/etherboot.prefix.lds
@@ -0,0 +1,100 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+ENTRY(_prefix_start)
+SECTIONS {
+ /* Prefix */
+ .prefix : {
+ _verbatim_start = . ;
+ _prefix_start = . ;
+ *(.prefix)
+ . = ALIGN(16);
+ _prefix_end = . ;
+ } = 0x9090
+ _prefix_size = _prefix_end - _prefix_start;
+
+ .text.nocompress : {
+ *(.prefix.udata)
+ } = 0x9090
+
+ decompress_to = . ;
+ .prefix.zdata : {
+ _compressed = . ;
+ *(.prefix.zdata)
+ _compressed_end = . ;
+ }
+ _compressed_size = _compressed_end - _compressed;
+
+ . = ALIGN(16);
+ _verbatim_end = . ;
+
+
+ /* Size of the core of etherboot in memory */
+ _base_size = _end - _text;
+
+ /* _prefix_size is the length of the non-core etherboot prefix */
+ _prefix_size = _prefix_end - _prefix_start;
+
+ /* _verbatim_size is the actual amount that has to be copied to base memory */
+ _verbatim_size = _verbatim_end - _verbatim_start;
+
+ /* _image_size is the amount of base memory needed to run */
+ _image_size = _base_size + _prefix_size;
+
+ /* Standard sizes rounded up to paragraphs */
+ _prefix_size_pgh = (_prefix_size + 15) / 16;
+ _verbatim_size_pgh = (_verbatim_size + 15) / 16;
+ _image_size_pgh = (_image_size + 15) / 16 ;
+
+ /* Standard sizes in sectors */
+ _prefix_size_sct = (_prefix_size + 511) / 512;
+ _verbatim_size_sct = (_verbatim_size + 511) / 512;
+ _image_size_sct = (_image_size + 511) / 512;
+
+ /* Symbol offsets and sizes for the exe prefix */
+ _exe_hdr_size = 32;
+ _exe_size = _verbatim_size; /* Should this be - 32 to exclude the header? */
+ _exe_size_tail = (_exe_size) % 512;
+ _exe_size_pages = ((_exe_size) + 511) / 512;
+ _exe_bss_size = ((_image_size - _verbatim_size) + 15) / 16;
+ _exe_ss_offset = (_stack_offset + _prefix_size - _exe_hdr_size + 15) / 16 ;
+
+ /* This is where we copy the compressed image before decompression.
+ * Prepare to decompress in place. The end mark is about 8.25 bytes long,
+ * and the worst case symbol is about 16.5 bytes long. Therefore
+ * We need to reserve at least 25 bytes of slack here.
+ * Currently I reserve 2048 bytes of just slack to be safe :)
+ * 2048 bytes easily falls within the BSS (the defualt stack is 4096 bytes)
+ * so we really are decompressing in place.
+ *
+ * Hmm. I missed a trick. In the very worst case (no compression)
+ * the encoded data is 9/8 the size as it started out so to be completely
+ * safe I need to be 1/8 of the uncompressed code size past the end.
+ * This will still fit compfortably into our bss in any conceivable scenario.
+ */
+ _compressed_copy = _edata + _prefix_size - _compressed_size +
+ /* The amount to overflow _edata */
+ MAX( ((_edata - _text + 7) / 8) , 2016 ) + 32;
+ _assert = ASSERT( ( _compressed_copy - _prefix_size ) < _ebss , "Cannot decompress in place" ) ;
+
+ decompress = DEFINED(decompress) ? decompress : 0;
+ /DISCARD/ : {
+ *(.comment)
+ *(.note)
+ }
+
+ /* Symbols used by the prefixes whose addresses are inconvinient
+ * to compute, at runtime in the code.
+ */
+ image_basemem_size = DEFINED(image_basemem_size)? image_basemem_size : 65536;
+ image_basemem = DEFINED(image_basemem)? image_basemem : 65536;
+ _prefix_real_to_prot = _real_to_prot + _prefix_size ;
+ _prefix_prot_to_real = _prot_to_real + _prefix_size ;
+ _prefix_image_basemem_size = image_basemem_size + _prefix_size ;
+ _prefix_image_basemem = image_basemem + _prefix_size ;
+ _prefix_rm_in_call = _rm_in_call + _prefix_size ;
+ _prefix_in_call = _in_call + _prefix_size ;
+ _prefix_rom = rom + _prefix_size ;
+ _prefix_rm_etherboot_location = rm_etherboot_location + _prefix_size ;
+ _prefix_stack_end = _stack_end + _prefix_size ;
+}
diff --git a/gpxe/src/arch/i386/core/freebsd_loader.c b/gpxe/src/arch/i386/core/freebsd_loader.c
new file mode 100644
index 00000000..464f6d93
--- /dev/null
+++ b/gpxe/src/arch/i386/core/freebsd_loader.c
@@ -0,0 +1,377 @@
+/* bootinfo */
+#define BOOTINFO_VERSION 1
+#define NODEV (-1) /* non-existent device */
+#define PAGE_SHIFT 12 /* LOG2(PAGE_SIZE) */
+#define PAGE_SIZE (1<<PAGE_SHIFT) /* bytes/page */
+#define PAGE_MASK (PAGE_SIZE-1)
+#define N_BIOS_GEOM 8
+
+struct bootinfo {
+ unsigned int bi_version;
+ const unsigned char *bi_kernelname;
+ struct nfs_diskless *bi_nfs_diskless;
+ /* End of fields that are always present. */
+#define bi_endcommon bi_n_bios_used
+ unsigned int bi_n_bios_used;
+ unsigned long bi_bios_geom[N_BIOS_GEOM];
+ unsigned int bi_size;
+ unsigned char bi_memsizes_valid;
+ unsigned char bi_pad[3];
+ unsigned long bi_basemem;
+ unsigned long bi_extmem;
+ unsigned long bi_symtab;
+ unsigned long bi_esymtab;
+ /* Note that these are in the FreeBSD headers but were not here... */
+ unsigned long bi_kernend; /* end of kernel space */
+ unsigned long bi_envp; /* environment */
+ unsigned long bi_modulep; /* preloaded modules */
+};
+
+static struct bootinfo bsdinfo;
+
+#ifdef ELF_IMAGE
+static Elf32_Shdr *shdr; /* To support the FreeBSD kludge! */
+static Address symtab_load;
+static Address symstr_load;
+static int symtabindex;
+static int symstrindex;
+#endif
+
+static enum {
+ Unknown, Tagged, Aout, Elf, Aout_FreeBSD, Elf_FreeBSD,
+} image_type = Unknown;
+
+static unsigned int off;
+
+
+#ifdef ELF_IMAGE
+static void elf_freebsd_probe(void)
+{
+ image_type = Elf;
+ if ( (estate.e.elf32.e_entry & 0xf0000000) &&
+ (estate.e.elf32.e_type == ET_EXEC))
+ {
+ image_type = Elf_FreeBSD;
+ printf("/FreeBSD");
+ off = -(estate.e.elf32.e_entry & 0xff000000);
+ estate.e.elf32.e_entry += off;
+ }
+ /* Make sure we have a null to start with... */
+ shdr = 0;
+
+ /* Clear the symbol index values... */
+ symtabindex = -1;
+ symstrindex = -1;
+
+ /* ...and the load addresses of the symbols */
+ symtab_load = 0;
+ symstr_load = 0;
+}
+
+static void elf_freebsd_fixup_segment(void)
+{
+ if (image_type == Elf_FreeBSD) {
+ estate.p.phdr32[estate.segment].p_paddr += off;
+ }
+}
+
+static void elf_freebsd_find_segment_end(void)
+{
+ /* Count the bytes read even for the last block
+ * as we will need to know where the last block
+ * ends in order to load the symbols correctly.
+ * (plus it could be useful elsewhere...)
+ * Note that we need to count the actual size,
+ * not just the end of the disk image size.
+ */
+ estate.curaddr +=
+ (estate.p.phdr32[estate.segment].p_memsz -
+ estate.p.phdr32[estate.segment].p_filesz);
+}
+
+static int elf_freebsd_debug_loader(unsigned int offset)
+{
+ /* No more segments to be loaded - time to start the
+ * nasty state machine to support the loading of
+ * FreeBSD debug symbols due to the fact that FreeBSD
+ * uses/exports the kernel's debug symbols in order
+ * to make much of the system work! Amazing (arg!)
+ *
+ * We depend on the fact that for the FreeBSD kernel,
+ * there is only one section of debug symbols and that
+ * the section is after all of the loaded sections in
+ * the file. This assumes a lot but is somewhat required
+ * to make this code not be too annoying. (Where do you
+ * load symbols when the code has not loaded yet?)
+ * Since this function is actually just a callback from
+ * the network data transfer code, we need to be able to
+ * work with the data as it comes in. There is no chance
+ * for doing a seek other than forwards.
+ *
+ * The process we use is to first load the section
+ * headers. Once they are loaded (shdr != 0) we then
+ * look for where the symbol table and symbol table
+ * strings are and setup some state that we found
+ * them and fall into processing the first one (which
+ * is the symbol table) and after that has been loaded,
+ * we try the symbol strings. Note that the order is
+ * actually required as the memory image depends on
+ * the symbol strings being loaded starting at the
+ * end of the symbol table. The kernel assumes this
+ * layout of the image.
+ *
+ * At any point, if we get to the end of the load file
+ * or the section requested is earlier in the file than
+ * the current file pointer, we just end up falling
+ * out of this and booting the kernel without this
+ * information.
+ */
+
+ /* Make sure that the next address is long aligned... */
+ /* Assumes size of long is a power of 2... */
+ estate.curaddr = (estate.curaddr + sizeof(long) - 1) & ~(sizeof(long) - 1);
+
+ /* If we have not yet gotten the shdr loaded, try that */
+ if (shdr == 0)
+ {
+ estate.toread = estate.e.elf32.e_shnum * estate.e.elf32.e_shentsize;
+ estate.skip = estate.e.elf32.e_shoff - (estate.loc + offset);
+ if (estate.toread)
+ {
+#if ELF_DEBUG
+ printf("shdr *, size %lX, curaddr %lX\n",
+ estate.toread, estate.curaddr);
+#endif
+
+ /* Start reading at the curaddr and make that the shdr */
+ shdr = (Elf32_Shdr *)phys_to_virt(estate.curaddr);
+
+ /* Start to read... */
+ return 1;
+ }
+ }
+ else
+ {
+ /* We have the shdr loaded, check if we have found
+ * the indexs where the symbols are supposed to be */
+ if ((symtabindex == -1) && (symstrindex == -1))
+ {
+ int i;
+ /* Make sure that the address is page aligned... */
+ /* Symbols need to start in their own page(s)... */
+ estate.curaddr = (estate.curaddr + 4095) & ~4095;
+
+ /* Need to make new indexes... */
+ for (i=0; i < estate.e.elf32.e_shnum; i++)
+ {
+ if (shdr[i].sh_type == SHT_SYMTAB)
+ {
+ int j;
+ for (j=0; j < estate.e.elf32.e_phnum; j++)
+ {
+ /* Check only for loaded sections */
+ if ((estate.p.phdr32[j].p_type | 0x80) == (PT_LOAD | 0x80))
+ {
+ /* Only the extra symbols */
+ if ((shdr[i].sh_offset >= estate.p.phdr32[j].p_offset) &&
+ ((shdr[i].sh_offset + shdr[i].sh_size) <=
+ (estate.p.phdr32[j].p_offset + estate.p.phdr32[j].p_filesz)))
+ {
+ shdr[i].sh_offset=0;
+ shdr[i].sh_size=0;
+ break;
+ }
+ }
+ }
+ if ((shdr[i].sh_offset != 0) && (shdr[i].sh_size != 0))
+ {
+ symtabindex = i;
+ symstrindex = shdr[i].sh_link;
+ }
+ }
+ }
+ }
+
+ /* Check if we have a symbol table index and have not loaded it */
+ if ((symtab_load == 0) && (symtabindex >= 0))
+ {
+ /* No symbol table yet? Load it first... */
+
+ /* This happens to work out in a strange way.
+ * If we are past the point in the file already,
+ * we will skip a *large* number of bytes which
+ * ends up bringing us to the end of the file and
+ * an old (default) boot. Less code and lets
+ * the state machine work in a cleaner way but this
+ * is a nasty side-effect trick... */
+ estate.skip = shdr[symtabindex].sh_offset - (estate.loc + offset);
+
+ /* And we need to read this many bytes... */
+ estate.toread = shdr[symtabindex].sh_size;
+
+ if (estate.toread)
+ {
+#if ELF_DEBUG
+ printf("db sym, size %lX, curaddr %lX\n",
+ estate.toread, estate.curaddr);
+#endif
+ /* Save where we are loading this... */
+ symtab_load = estate.curaddr;
+
+ *((long *)phys_to_virt(estate.curaddr)) = estate.toread;
+ estate.curaddr += sizeof(long);
+
+ /* Start to read... */
+ return 1;
+ }
+ }
+ else if ((symstr_load == 0) && (symstrindex >= 0))
+ {
+ /* We have already loaded the symbol table, so
+ * now on to the symbol strings... */
+
+
+ /* Same nasty trick as above... */
+ estate.skip = shdr[symstrindex].sh_offset - (estate.loc + offset);
+
+ /* And we need to read this many bytes... */
+ estate.toread = shdr[symstrindex].sh_size;
+
+ if (estate.toread)
+ {
+#if ELF_DEBUG
+ printf("db str, size %lX, curaddr %lX\n",
+ estate.toread, estate.curaddr);
+#endif
+ /* Save where we are loading this... */
+ symstr_load = estate.curaddr;
+
+ *((long *)phys_to_virt(estate.curaddr)) = estate.toread;
+ estate.curaddr += sizeof(long);
+
+ /* Start to read... */
+ return 1;
+ }
+ }
+ }
+ /* all done */
+ return 0;
+}
+
+static void elf_freebsd_boot(unsigned long entry)
+{
+ if (image_type != Elf_FreeBSD)
+ return;
+
+ memset(&bsdinfo, 0, sizeof(bsdinfo));
+ bsdinfo.bi_basemem = meminfo.basememsize;
+ bsdinfo.bi_extmem = meminfo.memsize;
+ bsdinfo.bi_memsizes_valid = 1;
+ bsdinfo.bi_version = BOOTINFO_VERSION;
+ bsdinfo.bi_kernelname = virt_to_phys(KERNEL_BUF);
+ bsdinfo.bi_nfs_diskless = NULL;
+ bsdinfo.bi_size = sizeof(bsdinfo);
+#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */
+ if(freebsd_kernel_env[0] != '\0'){
+ freebsd_howto |= RB_BOOTINFO;
+ bsdinfo.bi_envp = (unsigned long)freebsd_kernel_env;
+ }
+
+ /* Check if we have symbols loaded, and if so,
+ * made the meta_data needed to pass those to
+ * the kernel. */
+ if ((symtab_load !=0) && (symstr_load != 0))
+ {
+ unsigned long *t;
+
+ bsdinfo.bi_symtab = symtab_load;
+
+ /* End of symbols (long aligned...) */
+ /* Assumes size of long is a power of 2... */
+ bsdinfo.bi_esymtab = (symstr_load +
+ sizeof(long) +
+ *((long *)phys_to_virt(symstr_load)) +
+ sizeof(long) - 1) & ~(sizeof(long) - 1);
+
+ /* Where we will build the meta data... */
+ t = phys_to_virt(bsdinfo.bi_esymtab);
+
+#if ELF_DEBUG
+ printf("Metadata at %lX\n",t);
+#endif
+
+ /* Set up the pointer to the memory... */
+ bsdinfo.bi_modulep = virt_to_phys(t);
+
+ /* The metadata structure is an array of 32-bit
+ * words where we store some information about the
+ * system. This is critical, as FreeBSD now looks
+ * only for the metadata for the extended symbol
+ * information rather than in the bootinfo.
+ */
+ /* First, do the kernel name and the kernel type */
+ /* Note that this assumed x86 byte order... */
+
+ /* 'kernel\0\0' */
+ *t++=MODINFO_NAME; *t++= 7; *t++=0x6E72656B; *t++=0x00006C65;
+
+ /* 'elf kernel\0\0' */
+ *t++=MODINFO_TYPE; *t++=11; *t++=0x20666C65; *t++=0x6E72656B; *t++ = 0x00006C65;
+
+ /* Now the symbol start/end - note that they are
+ * here in local/physical address - the Kernel
+ * boot process will relocate the addresses. */
+ *t++=MODINFOMD_SSYM | MODINFO_METADATA; *t++=sizeof(*t); *t++=bsdinfo.bi_symtab;
+ *t++=MODINFOMD_ESYM | MODINFO_METADATA; *t++=sizeof(*t); *t++=bsdinfo.bi_esymtab;
+
+ *t++=MODINFO_END; *t++=0; /* end of metadata */
+
+ /* Since we have symbols we need to make
+ * sure that the kernel knows its own end
+ * of memory... It is not _end but after
+ * the symbols and the metadata... */
+ bsdinfo.bi_kernend = virt_to_phys(t);
+
+ /* Signal locore.s that we have a valid bootinfo
+ * structure that was completely filled in. */
+ freebsd_howto |= 0x80000000;
+ }
+
+ xstart32(entry, freebsd_howto, NODEV, 0, 0, 0,
+ virt_to_phys(&bsdinfo), 0, 0, 0);
+ longjmp(restart_etherboot, -2);
+}
+#endif
+
+#ifdef AOUT_IMAGE
+static void aout_freebsd_probe(void)
+{
+ image_type = Aout;
+ if (((astate.head.a_midmag >> 16) & 0xffff) == 0) {
+ /* Some other a.out variants have a different
+ * value, and use other alignments (e.g. 1K),
+ * not the 4K used by FreeBSD. */
+ image_type = Aout_FreeBSD;
+ printf("/FreeBSD");
+ off = -(astate.head.a_entry & 0xff000000);
+ astate.head.a_entry += off;
+ }
+}
+
+static void aout_freebsd_boot(void)
+{
+ if (image_type == Aout_FreeBSD) {
+ memset(&bsdinfo, 0, sizeof(bsdinfo));
+ bsdinfo.bi_basemem = meminfo.basememsize;
+ bsdinfo.bi_extmem = meminfo.memsize;
+ bsdinfo.bi_memsizes_valid = 1;
+ bsdinfo.bi_version = BOOTINFO_VERSION;
+ bsdinfo.bi_kernelname = virt_to_phys(KERNEL_BUF);
+ bsdinfo.bi_nfs_diskless = NULL;
+ bsdinfo.bi_size = sizeof(bsdinfo);
+ xstart32(astate.head.a_entry, freebsd_howto, NODEV, 0, 0, 0,
+ virt_to_phys(&bsdinfo), 0, 0, 0);
+ longjmp(restart_etherboot, -2);
+ }
+}
+#endif
diff --git a/gpxe/src/arch/i386/core/gdbsym.c b/gpxe/src/arch/i386/core/gdbsym.c
new file mode 100644
index 00000000..2da1a1bd
--- /dev/null
+++ b/gpxe/src/arch/i386/core/gdbsym.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <gpxe/init.h>
+#include <console.h>
+#include <realmode.h>
+
+extern char __text[];
+extern char __rodata[];
+extern char __data[];
+extern char __bss[];
+extern char __text16[];
+extern char __data16[];
+
+
+static void gdb_symbol_line ( void ) {
+ printf ( "Commands to start up gdb:\n\n" );
+ printf ( "gdb\n" );
+ printf ( "target remote localhost:1234\n" );
+ printf ( "set confirm off\n" );
+ printf ( "add-symbol-file symbols %#lx", virt_to_phys ( __text ) );
+ printf ( " -s .rodata %#lx", virt_to_phys ( __rodata ) );
+ printf ( " -s .data %#lx", virt_to_phys ( __data ) );
+ printf ( " -s .bss %#lx", virt_to_phys ( __bss ) );
+ printf ( " -s .text16 %#x", ( ( rm_cs << 4 ) + (int)__text16 ) );
+ printf ( " -s .data16 %#x", ( ( rm_ds << 4 ) + (int)__data16 ) );
+ printf ( "\n" );
+ printf ( "add-symbol-file symbols 0\n" );
+ printf ( "set confirm on\n" );
+ getkey();
+}
+
+struct startup_fn gdb_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
+ .startup = gdb_symbol_line,
+};
diff --git a/gpxe/src/arch/i386/core/i386_string.c b/gpxe/src/arch/i386/core/i386_string.c
new file mode 100644
index 00000000..9917363a
--- /dev/null
+++ b/gpxe/src/arch/i386/core/i386_string.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/** @file
+ *
+ * Optimised string operations
+ *
+ */
+
+#include <string.h>
+
+/**
+ * Copy memory area
+ *
+ * @v dest Destination address
+ * @v src Source address
+ * @v len Length
+ * @ret dest Destination address
+ */
+__attribute__ (( regparm ( 3 ) )) void * __memcpy ( void *dest,
+ const void *src,
+ size_t len ) {
+ void *edi = dest;
+ const void *esi = src;
+ int discard_ecx;
+
+ /* We often do large dword-aligned and dword-length block
+ * moves. Using movsl rather than movsb speeds these up by
+ * around 32%.
+ */
+ if ( len >> 2 ) {
+ __asm__ __volatile__ ( "rep movsl"
+ : "=&D" ( edi ), "=&S" ( esi ),
+ "=&c" ( discard_ecx )
+ : "0" ( edi ), "1" ( esi ),
+ "2" ( len >> 2 )
+ : "memory" );
+ }
+ if ( len & 0x02 ) {
+ __asm__ __volatile__ ( "movsw" : "=&D" ( edi ), "=&S" ( esi )
+ : "0" ( edi ), "1" ( esi ) : "memory" );
+ }
+ if ( len & 0x01 ) {
+ __asm__ __volatile__ ( "movsb" : "=&D" ( edi ), "=&S" ( esi )
+ : "0" ( edi ), "1" ( esi ) : "memory" );
+ }
+ return dest;
+}
diff --git a/gpxe/src/arch/i386/core/i386_timer.c b/gpxe/src/arch/i386/core/i386_timer.c
new file mode 100644
index 00000000..8f90ae05
--- /dev/null
+++ b/gpxe/src/arch/i386/core/i386_timer.c
@@ -0,0 +1,89 @@
+/*
+ * arch/i386/core/i386_timer.c
+ *
+ * Use the "System Timer 2" to implement the udelay callback in
+ * the BIOS timer driver. Also used to calibrate the clock rate
+ * in the RTDSC timer driver.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#include <stddef.h>
+#include <bits/timer2.h>
+#include <gpxe/timer.h>
+#include <io.h>
+
+/* Timers tick over at this rate */
+#define TIMER2_TICK_RATE 1193180U
+
+/* Parallel Peripheral Controller Port B */
+#define PPC_PORTB 0x61
+
+/* Meaning of the port bits */
+#define PPCB_T2OUT 0x20 /* Bit 5 */
+#define PPCB_SPKR 0x02 /* Bit 1 */
+#define PPCB_T2GATE 0x01 /* Bit 0 */
+
+/* Ports for the 8254 timer chip */
+#define TIMER2_PORT 0x42
+#define TIMER_MODE_PORT 0x43
+
+/* Meaning of the mode bits */
+#define TIMER0_SEL 0x00
+#define TIMER1_SEL 0x40
+#define TIMER2_SEL 0x80
+#define READBACK_SEL 0xC0
+
+#define LATCH_COUNT 0x00
+#define LOBYTE_ACCESS 0x10
+#define HIBYTE_ACCESS 0x20
+#define WORD_ACCESS 0x30
+
+#define MODE0 0x00
+#define MODE1 0x02
+#define MODE2 0x04
+#define MODE3 0x06
+#define MODE4 0x08
+#define MODE5 0x0A
+
+#define BINARY_COUNT 0x00
+#define BCD_COUNT 0x01
+
+static void load_timer2(unsigned int ticks)
+{
+ /*
+ * Now let's take care of PPC channel 2
+ *
+ * Set the Gate high, program PPC channel 2 for mode 0,
+ * (interrupt on terminal count mode), binary count,
+ * load 5 * LATCH count, (LSB and MSB) to begin countdown.
+ *
+ * Note some implementations have a bug where the high bits byte
+ * of channel 2 is ignored.
+ */
+ /* Set up the timer gate, turn off the speaker */
+ /* Set the Gate high, disable speaker */
+ outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB);
+ /* binary, mode 0, LSB/MSB, Ch 2 */
+ outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT);
+ /* LSB of ticks */
+ outb(ticks & 0xFF, TIMER2_PORT);
+ /* MSB of ticks */
+ outb(ticks >> 8, TIMER2_PORT);
+}
+
+static int timer2_running(void)
+{
+ return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0);
+}
+
+void i386_timer2_udelay(unsigned int usecs)
+{
+ load_timer2((usecs * TIMER2_TICK_RATE)/USECS_IN_SEC);
+ while (timer2_running())
+ ;
+}
+
diff --git a/gpxe/src/arch/i386/core/nap.c b/gpxe/src/arch/i386/core/nap.c
new file mode 100644
index 00000000..12bb5699
--- /dev/null
+++ b/gpxe/src/arch/i386/core/nap.c
@@ -0,0 +1,12 @@
+
+#include <realmode.h>
+#include <bios.h>
+
+/**************************************************************************
+ * Save power by halting the CPU until the next interrupt
+ **************************************************************************/
+void cpu_nap ( void ) {
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "hlt\n\t"
+ "cli\n\t" ) : : );
+}
diff --git a/gpxe/src/arch/i386/core/nulltrap.c b/gpxe/src/arch/i386/core/nulltrap.c
new file mode 100644
index 00000000..3046fbec
--- /dev/null
+++ b/gpxe/src/arch/i386/core/nulltrap.c
@@ -0,0 +1,51 @@
+#include <stdint.h>
+#include <stdio.h>
+
+__attribute__ (( noreturn, section ( ".text.null_trap" ) ))
+void null_function_trap ( void ) {
+ void *stack;
+
+ /* 128 bytes of NOPs; the idea of this is that if something
+ * dereferences a NULL pointer and overwrites us, we at least
+ * have some chance of still getting to execute the printf()
+ * statement.
+ */
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+ __asm__ __volatile__ ( "nop ; nop ; nop ; nop" );
+
+ __asm__ __volatile__ ( "movl %%esp, %0" : "=r" ( stack ) );
+ printf ( "NULL method called from %p (stack %p)\n",
+ __builtin_return_address ( 0 ), stack );
+ DBG_HD ( stack, 256 );
+ while ( 1 ) {}
+}
diff --git a/gpxe/src/arch/i386/core/pcibios.c b/gpxe/src/arch/i386/core/pcibios.c
new file mode 100644
index 00000000..1c93e4be
--- /dev/null
+++ b/gpxe/src/arch/i386/core/pcibios.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <gpxe/pci.h>
+#include <pcibios.h>
+#include <realmode.h>
+
+/** @file
+ *
+ * PCI configuration space access via PCI BIOS
+ *
+ */
+
+/**
+ * Determine maximum PCI bus number within system
+ *
+ * @ret max_bus Maximum bus number
+ */
+int pcibios_max_bus ( void ) {
+ int discard_a, discard_D;
+ uint8_t max_bus;
+
+ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+ "int $0x1a\n\t"
+ "jnc 1f\n\t"
+ "xorw %%cx, %%cx\n\t"
+ "\n1:\n\t" )
+ : "=c" ( max_bus ), "=a" ( discard_a ),
+ "=D" ( discard_D )
+ : "a" ( PCIBIOS_INSTALLATION_CHECK >> 16 ),
+ "D" ( 0 )
+ : "ebx", "edx" );
+
+ return max_bus;
+}
+
+/**
+ * Read configuration space via PCI BIOS
+ *
+ * @v pci PCI device
+ * @v command PCI BIOS command
+ * @v value Value read
+ * @ret rc Return status code
+ */
+int pcibios_read ( struct pci_device *pci, uint32_t command, uint32_t *value ){
+ int discard_b, discard_D;
+ int status;
+
+ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+ "int $0x1a\n\t"
+ "jnc 1f\n\t"
+ "xorl %%eax, %%eax\n\t"
+ "decl %%eax\n\t"
+ "movl %%eax, %%ecx\n\t"
+ "\n1:\n\t" )
+ : "=a" ( status ), "=b" ( discard_b ),
+ "=c" ( *value ), "=D" ( discard_D )
+ : "a" ( command >> 16 ), "D" ( command ),
+ "b" ( PCI_BUSDEVFN ( pci->bus, pci->devfn ) )
+ : "edx" );
+
+ return ( ( status >> 8 ) & 0xff );
+}
+
+/**
+ * Write configuration space via PCI BIOS
+ *
+ * @v pci PCI device
+ * @v command PCI BIOS command
+ * @v value Value to be written
+ * @ret rc Return status code
+ */
+int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){
+ int discard_b, discard_c, discard_D;
+ int status;
+
+ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+ "int $0x1a\n\t"
+ "jnc 1f\n\t"
+ "movb $0xff, %%ah\n\t"
+ "\n1:\n\t" )
+ : "=a" ( status ), "=b" ( discard_b ),
+ "=c" ( discard_c ), "=D" ( discard_D )
+ : "a" ( command >> 16 ), "D" ( command ),
+ "b" ( PCI_BUSDEVFN ( pci->bus, pci->devfn ) ),
+ "c" ( value )
+ : "edx" );
+
+ return ( ( status >> 8 ) & 0xff );
+}
diff --git a/gpxe/src/arch/i386/core/pcidirect.c b/gpxe/src/arch/i386/core/pcidirect.c
new file mode 100644
index 00000000..2ed8c2ad
--- /dev/null
+++ b/gpxe/src/arch/i386/core/pcidirect.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/pci.h>
+#include <pcidirect.h>
+
+/** @file
+ *
+ * PCI configuration space access via Type 1 accesses
+ *
+ */
+
+/**
+ * Prepare for Type 1 PCI configuration space access
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ */
+void pcidirect_prepare ( struct pci_device *pci, int where ) {
+ outl ( ( 0x80000000 | ( pci->bus << 16 ) | ( pci->devfn << 8 ) |
+ ( where & ~3 ) ), PCIDIRECT_CONFIG_ADDRESS );
+}
+
diff --git a/gpxe/src/arch/i386/core/pic8259.c b/gpxe/src/arch/i386/core/pic8259.c
new file mode 100644
index 00000000..defe2e7d
--- /dev/null
+++ b/gpxe/src/arch/i386/core/pic8259.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <pic8259.h>
+
+/** @file
+ *
+ * Minimal support for the 8259 Programmable Interrupt Controller
+ *
+ */
+
+/**
+ * Send non-specific EOI(s)
+ *
+ * @v irq IRQ number
+ *
+ * This seems to be inherently unsafe.
+ */
+static inline void send_nonspecific_eoi ( unsigned int irq ) {
+ DBG ( "Sending non-specific EOI for IRQ %d\n", irq );
+ if ( irq >= IRQ_PIC_CUTOFF ) {
+ outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR );
+ }
+ outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR );
+}
+
+/**
+ * Send specific EOI(s)
+ *
+ * @v irq IRQ number
+ */
+static inline void send_specific_eoi ( unsigned int irq ) {
+ DBG ( "Sending specific EOI for IRQ %d\n", irq );
+ if ( irq >= IRQ_PIC_CUTOFF ) {
+ outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( CHAINED_IRQ ) ),
+ ICR_REG ( CHAINED_IRQ ) );
+ }
+ outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( irq ) ), ICR_REG ( irq ) );
+}
+
+/**
+ * Send End-Of-Interrupt to the PIC
+ *
+ * @v irq IRQ number
+ */
+void send_eoi ( unsigned int irq ) {
+ send_specific_eoi ( irq );
+}
diff --git a/gpxe/src/arch/i386/core/prefixudata.lds b/gpxe/src/arch/i386/core/prefixudata.lds
new file mode 100644
index 00000000..1c76128e
--- /dev/null
+++ b/gpxe/src/arch/i386/core/prefixudata.lds
@@ -0,0 +1,8 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS {
+ .prefix.udata : {
+ *(*)
+ }
+}
diff --git a/gpxe/src/arch/i386/core/prefixzdata.lds b/gpxe/src/arch/i386/core/prefixzdata.lds
new file mode 100644
index 00000000..bf6ea977
--- /dev/null
+++ b/gpxe/src/arch/i386/core/prefixzdata.lds
@@ -0,0 +1,8 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS {
+ .prefix.zdata : {
+ *(*)
+ }
+}
diff --git a/gpxe/src/arch/i386/core/realmode.c b/gpxe/src/arch/i386/core/realmode.c
new file mode 100644
index 00000000..9a77bd8a
--- /dev/null
+++ b/gpxe/src/arch/i386/core/realmode.c
@@ -0,0 +1,23 @@
+/* Real-mode interface: C portions.
+ *
+ * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
+ */
+
+#include "realmode.h"
+
+/*
+ * Copy data to/from base memory.
+ *
+ */
+
+#ifdef KEEP_IT_REAL
+
+void memcpy_to_real ( segoff_t dest, void *src, size_t n ) {
+
+}
+
+void memcpy_from_real ( void *dest, segoff_t src, size_t n ) {
+
+}
+
+#endif /* KEEP_IT_REAL */
diff --git a/gpxe/src/arch/i386/core/relocate.c b/gpxe/src/arch/i386/core/relocate.c
new file mode 100644
index 00000000..39d00b09
--- /dev/null
+++ b/gpxe/src/arch/i386/core/relocate.c
@@ -0,0 +1,163 @@
+#include <io.h>
+#include <registers.h>
+#include <gpxe/memmap.h>
+
+/*
+ * Originally by Eric Biederman
+ *
+ * Heavily modified by Michael Brown
+ *
+ */
+
+/*
+ * The linker passes in the symbol _max_align, which is the alignment
+ * that we must preserve, in bytes.
+ *
+ */
+extern char _max_align[];
+#define max_align ( ( unsigned int ) _max_align )
+
+/* Linker symbols */
+extern char _text[];
+extern char _end[];
+
+/* within 1MB of 4GB is too close.
+ * MAX_ADDR is the maximum address we can easily do DMA to.
+ *
+ * Not sure where this constraint comes from, but kept it from Eric's
+ * old code - mcb30
+ */
+#define MAX_ADDR (0xfff00000UL)
+
+/**
+ * Relocate Etherboot
+ *
+ * @v ix86 x86 register dump from prefix
+ * @ret ix86 x86 registers to return to prefix
+ *
+ * This finds a suitable location for Etherboot near the top of 32-bit
+ * address space, and returns the physical address of the new location
+ * to the prefix in %edi.
+ */
+__cdecl void relocate ( struct i386_all_regs *ix86 ) {
+ struct memory_map memmap;
+ unsigned long start, end, size, padded_size;
+ unsigned long new_start, new_end;
+ unsigned i;
+
+ /* Get memory map and current location */
+ get_memmap ( &memmap );
+ start = virt_to_phys ( _text );
+ end = virt_to_phys ( _end );
+ size = ( end - start );
+ padded_size = ( size + max_align - 1 );
+
+ DBG ( "Relocate: currently at [%lx,%lx)\n"
+ "...need %lx bytes for %d-byte alignment\n",
+ start, end, padded_size, max_align );
+
+ /* Walk through the memory map and find the highest address
+ * below 4GB that etherboot will fit into. Ensure etherboot
+ * lies entirely within a range with A20=0. This means that
+ * even if something screws up the state of the A20 line, the
+ * etherboot code is still visible and we have a chance to
+ * diagnose the problem.
+ */
+ new_end = end;
+ for ( i = 0 ; i < memmap.count ; i++ ) {
+ struct memory_region *region = &memmap.regions[i];
+ unsigned long r_start, r_end;
+
+ DBG ( "Considering [%llx,%llx)\n", region->start, region->end);
+
+ /* Truncate block to MAX_ADDR. This will be less than
+ * 4GB, which means that we can get away with using
+ * just 32-bit arithmetic after this stage.
+ */
+ if ( region->start > MAX_ADDR ) {
+ DBG ( "...starts after MAX_ADDR=%lx\n", MAX_ADDR );
+ continue;
+ }
+ r_start = region->start;
+ if ( region->end > MAX_ADDR ) {
+ DBG ( "...end truncated to MAX_ADDR=%lx\n", MAX_ADDR );
+ r_end = MAX_ADDR;
+ } else {
+ r_end = region->end;
+ }
+
+ /* Shrink the range down to use only even megabytes
+ * (i.e. A20=0).
+ */
+ if ( ( r_end - 1 ) & 0x100000 ) {
+ /* If last byte that might be used (r_end-1)
+ * is in an odd megabyte, round down r_end to
+ * the top of the next even megabyte.
+ */
+ r_end = ( r_end - 1 ) & ~0xfffff;
+ DBG ( "...end truncated to %lx "
+ "(avoid ending in odd megabyte)\n",
+ r_end );
+ } else if ( ( r_end - size ) & 0x100000 ) {
+ /* If the last byte that might be used
+ * (r_end-1) is in an even megabyte, but the
+ * first byte that might be used (r_end-size)
+ * is an odd megabyte, round down to the top
+ * of the next even megabyte.
+ *
+ * Make sure that we don't accidentally wrap
+ * r_end below 0.
+ */
+ if ( r_end > 0x100000 ) {
+ r_end = ( r_end - 0x100000 ) & ~0xfffff;
+ DBG ( "...end truncated to %lx "
+ "(avoid starting in odd megabyte)\n",
+ r_end );
+ }
+ }
+
+ DBG ( "...usable portion is [%lx,%lx)\n", r_start, r_end );
+
+ /* If we have rounded down r_end below r_ start, skip
+ * this block.
+ */
+ if ( r_end < r_start ) {
+ DBG ( "...truncated to negative size\n" );
+ continue;
+ }
+
+ /* Check that there is enough space to fit in Etherboot */
+ if ( ( r_end - r_start ) < size ) {
+ DBG ( "...too small (need %lx bytes)\n", size );
+ continue;
+ }
+
+ /* If the start address of the Etherboot we would
+ * place in this block is higher than the end address
+ * of the current highest block, use this block.
+ *
+ * Note that this avoids overlaps with the current
+ * Etherboot, as well as choosing the highest of all
+ * viable blocks.
+ */
+ if ( ( r_end - size ) > new_end ) {
+ new_end = r_end;
+ DBG ( "...new best block found.\n" );
+ }
+ }
+
+ /* Calculate new location of Etherboot, and align it to the
+ * required alignemnt.
+ */
+ new_start = new_end - padded_size;
+ new_start += ( start - new_start ) & ( max_align - 1 );
+ new_end = new_start + size;
+
+ DBG ( "Relocating from [%lx,%lx) to [%lx,%lx)\n",
+ start, end, new_start, new_end );
+
+ /* Let prefix know what to copy */
+ ix86->regs.esi = start;
+ ix86->regs.edi = new_start;
+ ix86->regs.ecx = size;
+}
diff --git a/gpxe/src/arch/i386/core/setjmp.S b/gpxe/src/arch/i386/core/setjmp.S
new file mode 100644
index 00000000..59a1b7cb
--- /dev/null
+++ b/gpxe/src/arch/i386/core/setjmp.S
@@ -0,0 +1,40 @@
+/* setjmp and longjmp. Use of these functions is deprecated. */
+
+ .text
+ .arch i386
+ .code32
+
+/**************************************************************************
+SETJMP - Save stack context for non-local goto
+**************************************************************************/
+ .globl setjmp
+setjmp:
+ movl 4(%esp),%ecx /* jmpbuf */
+ movl 0(%esp),%edx /* return address */
+ movl %edx,0(%ecx)
+ movl %ebx,4(%ecx)
+ movl %esp,8(%ecx)
+ movl %ebp,12(%ecx)
+ movl %esi,16(%ecx)
+ movl %edi,20(%ecx)
+ movl $0,%eax
+ ret
+
+/**************************************************************************
+LONGJMP - Non-local jump to a saved stack context
+**************************************************************************/
+ .globl longjmp
+longjmp:
+ movl 4(%esp),%edx /* jumpbuf */
+ movl 8(%esp),%eax /* result */
+ movl 0(%edx),%ecx
+ movl 4(%edx),%ebx
+ movl 8(%edx),%esp
+ movl 12(%edx),%ebp
+ movl 16(%edx),%esi
+ movl 20(%edx),%edi
+ cmpl $0,%eax
+ jne 1f
+ movl $1,%eax
+1: movl %ecx,0(%esp)
+ ret
diff --git a/gpxe/src/arch/i386/core/stack.S b/gpxe/src/arch/i386/core/stack.S
new file mode 100644
index 00000000..c2d138aa
--- /dev/null
+++ b/gpxe/src/arch/i386/core/stack.S
@@ -0,0 +1,13 @@
+ .arch i386
+
+/****************************************************************************
+ * Internal stack
+ ****************************************************************************
+ */
+ .section ".stack"
+ .align 8
+ .globl _stack
+_stack:
+ .space 4096
+ .globl _estack
+_estack:
diff --git a/gpxe/src/arch/i386/core/stack16.S b/gpxe/src/arch/i386/core/stack16.S
new file mode 100644
index 00000000..3380a083
--- /dev/null
+++ b/gpxe/src/arch/i386/core/stack16.S
@@ -0,0 +1,13 @@
+ .arch i386
+
+/****************************************************************************
+ * Internal stack
+ ****************************************************************************
+ */
+ .section ".stack16"
+ .align 8
+ .globl _stack16
+_stack16:
+ .space 4096
+ .globl _estack16
+_estack16:
diff --git a/gpxe/src/arch/i386/core/start16.lds b/gpxe/src/arch/i386/core/start16.lds
new file mode 100644
index 00000000..544fc78f
--- /dev/null
+++ b/gpxe/src/arch/i386/core/start16.lds
@@ -0,0 +1,8 @@
+/* When linking with an uncompressed image, these symbols are not
+ * defined so we provide them here.
+ */
+
+__decompressor_uncompressed = 0 ;
+__decompressor__start = 0 ;
+
+INCLUDE arch/i386/core/start16z.lds
diff --git a/gpxe/src/arch/i386/core/start16z.lds b/gpxe/src/arch/i386/core/start16z.lds
new file mode 100644
index 00000000..711bcf7b
--- /dev/null
+++ b/gpxe/src/arch/i386/core/start16z.lds
@@ -0,0 +1,65 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+/* Linker-generated symbols are prefixed with a double underscore.
+ * Decompressor symbols are prefixed with __decompressor_. All other
+ * symbols are the same as in the original object file, i.e. the
+ * runtime addresses.
+ */
+
+ENTRY(_start16)
+
+SECTIONS {
+ .text : {
+ *(.text)
+ }
+ .payload : {
+ __payload_start = .;
+ *(.data)
+ __payload_end = .;
+ }
+
+ /* _payload_size is the size of the binary image appended to
+ * start16, in bytes.
+ */
+ __payload_size = __payload_end - __payload_start ;
+
+ /* _size is the size of the runtime image
+ * (start32 + the C code), in bytes.
+ */
+ __size = _end - _start ;
+
+ /* _decompressor_size is the size of the decompressor, in
+ * bytes. For a non-compressed image, start16.lds sets
+ * _decompressor_uncompressed = _decompressor__start = 0.
+ */
+ __decompressor_size = __decompressor_uncompressed - __decompressor__start ;
+
+ /* image__size is the total size of the image, after
+ * decompression and including the decompressor if applicable.
+ * It is therefore the amount of memory that start16's payload
+ * needs in order to execute, in bytes.
+ */
+ __image_size = __size + __decompressor_size ;
+
+ /* Amount to add to runtime symbols to obtain the offset of
+ * that symbol within the image.
+ */
+ __offset_adjust = __decompressor_size - _start ;
+
+ /* Calculations for the stack
+ */
+ __stack_size = _estack - _stack ;
+ __offset_stack = _stack + __offset_adjust ;
+
+ /* Some symbols will be larger than 16 bits but guaranteed to
+ * be multiples of 16. We calculate them in paragraphs and
+ * export these symbols which can be used in 16-bit code
+ * without risk of overflow.
+ */
+ __image_size_pgh = ( __image_size / 16 );
+ __start_pgh = ( _start / 16 );
+ __decompressor_size_pgh = ( __decompressor_size / 16 );
+ __offset_stack_pgh = ( __offset_stack / 16 );
+}
+
diff --git a/gpxe/src/arch/i386/core/start32.S b/gpxe/src/arch/i386/core/start32.S
new file mode 100644
index 00000000..37ef5eb9
--- /dev/null
+++ b/gpxe/src/arch/i386/core/start32.S
@@ -0,0 +1,325 @@
+#include "virtaddr.h"
+
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+/*
+ * NOTE: if you write a subroutine that is called from C code (gcc/egcs),
+ * then you only have to take care of %ebx, %esi, %edi and %ebp. These
+ * registers must not be altered under any circumstance. All other registers
+ * may be clobbered without any negative side effects. If you don't follow
+ * this rule then you'll run into strange effects that only occur on some
+ * gcc versions (because the register allocator may use different registers).
+ *
+ * All the data32 prefixes for the ljmp instructions are necessary, because
+ * the assembler emits code with a relocation address of 0. This means that
+ * all destinations are initially negative, which the assembler doesn't grok,
+ * because for some reason negative numbers don't fit into 16 bits. The addr32
+ * prefixes are there for the same reasons, because otherwise the memory
+ * references are only 16 bit wide. Theoretically they are all superfluous.
+ * One last note about prefixes: the data32 prefixes on all call _real_to_prot
+ * instructions could be removed if the _real_to_prot function is changed to
+ * deal correctly with 16 bit return addresses. I tried it, but failed.
+ */
+
+ .text
+ .arch i386
+ .code32
+
+ /* This is a struct os_entry_regs */
+ .globl os_regs
+os_regs: .space 56
+
+/**************************************************************************
+XSTART32 - Transfer control to the kernel just loaded
+**************************************************************************/
+ .globl xstart32
+xstart32:
+ /* Save the callee save registers */
+ movl %ebp, os_regs + 32
+ movl %esi, os_regs + 36
+ movl %edi, os_regs + 40
+ movl %ebx, os_regs + 44
+
+ /* save the return address */
+ popl %eax
+ movl %eax, os_regs + 48
+
+ /* save the stack pointer */
+ movl %esp, os_regs + 52
+
+ /* Get the new destination address */
+ popl %ecx
+
+ /* Store the physical address of xend on the stack */
+ movl $xend32, %ebx
+ addl virt_offset, %ebx
+ pushl %ebx
+
+ /* Store the destination address on the stack */
+ pushl $PHYSICAL_CS
+ pushl %ecx
+
+ /* Cache virt_offset */
+ movl virt_offset, %ebp
+
+ /* Switch to using physical addresses */
+ call _virt_to_phys
+
+ /* Save the target stack pointer */
+ movl %esp, os_regs + 12(%ebp)
+ leal os_regs(%ebp), %esp
+
+ /* Store the pointer to os_regs */
+ movl %esp, os_regs_ptr(%ebp)
+
+ /* Load my new registers */
+ popal
+ movl (-32 + 12)(%esp), %esp
+
+ /* Jump to the new kernel
+ * The lret switches to a flat code segment
+ */
+ lret
+
+ .balign 4
+ .globl xend32
+xend32:
+ /* Fixup %eflags */
+ nop
+ cli
+ cld
+
+ /* Load %esp with &os_regs + virt_offset */
+ .byte 0xbc /* movl $0, %esp */
+os_regs_ptr:
+ .long 0
+
+ /* Save the result registers */
+ addl $32, %esp
+ pushal
+
+ /* Compute virt_offset */
+ movl %esp, %ebp
+ subl $os_regs, %ebp
+
+ /* Load the stack pointer and convert it to physical address */
+ movl 52(%esp), %esp
+ addl %ebp, %esp
+
+ /* Enable the virtual addresses */
+ leal _phys_to_virt(%ebp), %eax
+ call *%eax
+
+ /* Restore the callee save registers */
+ movl os_regs + 32, %ebp
+ movl os_regs + 36, %esi
+ movl os_regs + 40, %edi
+ movl os_regs + 44, %ebx
+ movl os_regs + 48, %edx
+ movl os_regs + 52, %esp
+
+ /* Get the C return value */
+ movl os_regs + 28, %eax
+
+ jmpl *%edx
+
+#ifdef CONFIG_X86_64
+ .arch sledgehammer
+/**************************************************************************
+XSTART_lm - Transfer control to the kernel just loaded in long mode
+**************************************************************************/
+ .globl xstart_lm
+xstart_lm:
+ /* Save the callee save registers */
+ pushl %ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ /* Cache virt_offset && (virt_offset & 0xfffff000) */
+ movl virt_offset, %ebp
+ movl %ebp, %ebx
+ andl $0xfffff000, %ebx
+
+ /* Switch to using physical addresses */
+ call _virt_to_phys
+
+ /* Initialize the page tables */
+ /* Level 4 */
+ leal 0x23 + pgt_level3(%ebx), %eax
+ leal pgt_level4(%ebx), %edi
+ movl %eax, (%edi)
+
+ /* Level 3 */
+ leal 0x23 + pgt_level2(%ebx), %eax
+ leal pgt_level3(%ebx), %edi
+ movl %eax, 0x00(%edi)
+ addl $4096, %eax
+ movl %eax, 0x08(%edi)
+ addl $4096, %eax
+ movl %eax, 0x10(%edi)
+ addl $4096, %eax
+ movl %eax, 0x18(%edi)
+
+ /* Level 2 */
+ movl $0xe3, %eax
+ leal pgt_level2(%ebx), %edi
+ leal 16384(%edi), %esi
+pgt_level2_loop:
+ movl %eax, (%edi)
+ addl $8, %edi
+ addl $0x200000, %eax
+ cmp %esi, %edi
+ jne pgt_level2_loop
+
+ /* Point at the x86_64 page tables */
+ leal pgt_level4(%ebx), %edi
+ movl %edi, %cr3
+
+
+ /* Setup for the return from 64bit mode */
+ /* 64bit align the stack */
+ movl %esp, %ebx /* original stack pointer + 16 */
+ andl $0xfffffff8, %esp
+
+ /* Save original stack pointer + 16 */
+ pushl %ebx
+
+ /* Save virt_offset */
+ pushl %ebp
+
+ /* Setup for the jmp to 64bit long mode */
+ leal start_lm(%ebp), %eax
+ movl %eax, 0x00 + start_lm_addr(%ebp)
+ movl $LM_CODE_SEG, %eax
+ movl %eax, 0x04 + start_lm_addr(%ebp)
+
+ /* Setup for the jump out of 64bit long mode */
+ leal end_lm(%ebp), %eax
+ movl %eax, 0x00 + end_lm_addr(%ebp)
+ movl $FLAT_CODE_SEG, %eax
+ movl %eax, 0x04 + end_lm_addr(%ebp)
+
+ /* Enable PAE mode */
+ movl %cr4, %eax
+ orl $X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Enable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ orl $EFER_LME, %eax
+ wrmsr
+
+ /* Start paging, entering 32bit compatiblity mode */
+ movl %cr0, %eax
+ orl $CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Enter 64bit long mode */
+ ljmp *start_lm_addr(%ebp)
+ .code64
+start_lm:
+ /* Load 64bit data segments */
+ movl $LM_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+
+ andq $0xffffffff, %rbx
+ /* Get the address to jump to */
+ movl 20(%rbx), %edx
+ andq $0xffffffff, %rdx
+
+ /* Get the argument pointer */
+ movl 24(%rbx), %ebx
+ andq $0xffffffff, %rbx
+
+ /* Jump to the 64bit code */
+ call *%rdx
+
+ /* Preserve the result */
+ movl %eax, %edx
+
+ /* Fixup %eflags */
+ cli
+ cld
+
+ /* Switch to 32bit compatibility mode */
+ ljmp *end_lm_addr(%rip)
+
+ .code32
+end_lm:
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ movl %cr4, %eax
+ andl $~X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Compute virt_offset */
+ popl %ebp
+
+ /* Compute the original stack pointer + 16 */
+ popl %ebx
+ movl %ebx, %esp
+
+ /* Enable the virtual addresses */
+ leal _phys_to_virt(%ebp), %eax
+ call *%eax
+
+ /* Restore the callee save registers */
+ popl %ebx
+ popl %esi
+ popl %edi
+ popl %ebp
+
+ /* Get the C return value */
+ movl %edx, %eax
+
+ /* Return */
+ ret
+
+ .arch i386
+#endif /* CONFIG_X86_64 */
+
+#ifdef CONFIG_X86_64
+ .section ".bss"
+ .p2align 12
+ /* Include a dummy space in case we are loaded badly aligned */
+ .space 4096
+ /* Reserve enough space for a page table convering 4GB with 2MB pages */
+pgt_level4:
+ .space 4096
+pgt_level3:
+ .space 4096
+pgt_level2:
+ .space 16384
+start_lm_addr:
+ .space 8
+end_lm_addr:
+ .space 8
+#endif
diff --git a/gpxe/src/arch/i386/core/umalloc.c b/gpxe/src/arch/i386/core/umalloc.c
new file mode 100644
index 00000000..bfd62ef1
--- /dev/null
+++ b/gpxe/src/arch/i386/core/umalloc.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * External memory allocation
+ *
+ */
+
+#include <limits.h>
+#include <errno.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/hidemem.h>
+#include <gpxe/memmap.h>
+#include <gpxe/umalloc.h>
+
+/** Alignment of external allocated memory */
+#define EM_ALIGN ( 4 * 1024 )
+
+/** Equivalent of NOWHERE for user pointers */
+#define UNOWHERE ( ~UNULL )
+
+/** Start of Etherboot text, as defined by the linker */
+extern char _text[];
+
+/** An external memory block */
+struct external_memory {
+ /** Size of this memory block (excluding this header) */
+ size_t size;
+ /** Block is currently in use */
+ int used;
+};
+
+/** Top of heap */
+static userptr_t top = UNULL;
+
+/** Bottom of heap (current lowest allocated block) */
+static userptr_t bottom = UNULL;
+
+/**
+ * Initialise external heap
+ *
+ * @ret rc Return status code
+ */
+static int init_eheap ( void ) {
+ struct memory_map memmap;
+ unsigned long heap_size = 0;
+ unsigned int i;
+
+ DBG ( "Allocating external heap\n" );
+
+ get_memmap ( &memmap );
+ for ( i = 0 ; i < memmap.count ; i++ ) {
+ struct memory_region *region = &memmap.regions[i];
+ unsigned long r_start, r_end;
+ unsigned long r_size;
+
+ DBG ( "Considering [%llx,%llx)\n", region->start, region->end);
+
+ /* Truncate block to 4GB */
+ if ( region->start > UINT_MAX ) {
+ DBG ( "...starts after 4GB\n" );
+ continue;
+ }
+ r_start = region->start;
+ if ( region->end > UINT_MAX ) {
+ DBG ( "...end truncated to 4GB\n" );
+ r_end = 0; /* =4GB, given the wraparound */
+ } else {
+ r_end = region->end;
+ }
+
+ /* Use largest block */
+ r_size = ( r_end - r_start );
+ if ( r_size > heap_size ) {
+ DBG ( "...new best block found\n" );
+ top = bottom = phys_to_user ( r_end );
+ heap_size = r_size;
+ }
+ }
+
+ if ( ! top ) {
+ DBG ( "No external heap available\n" );
+ return -ENOMEM;
+ }
+
+ DBG ( "External heap grows downwards from %lx\n",
+ user_to_phys ( top, 0 ) );
+ return 0;
+}
+
+/**
+ * Collect free blocks
+ *
+ */
+static void ecollect_free ( void ) {
+ struct external_memory extmem;
+
+ /* Walk the free list and collect empty blocks */
+ while ( bottom != top ) {
+ copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
+ sizeof ( extmem ) );
+ if ( extmem.used )
+ break;
+ DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
+ user_to_phys ( bottom, extmem.size ) );
+ bottom = userptr_add ( bottom,
+ ( extmem.size + sizeof ( extmem ) ) );
+ }
+}
+
+/**
+ * Reallocate external memory
+ *
+ * @v old_ptr Memory previously allocated by umalloc(), or UNULL
+ * @v new_size Requested size
+ * @ret new_ptr Allocated memory, or UNULL
+ *
+ * Calling realloc() with a new size of zero is a valid way to free a
+ * memory block.
+ */
+userptr_t urealloc ( userptr_t ptr, size_t new_size ) {
+ struct external_memory extmem;
+ userptr_t new = ptr;
+ size_t align;
+ int rc;
+
+ /* Initialise external memory allocator if necessary */
+ if ( ! top ) {
+ if ( ( rc = init_eheap() ) != 0 )
+ return rc;
+ }
+
+ /* Get block properties into extmem */
+ if ( ptr && ( ptr != UNOWHERE ) ) {
+ /* Determine old size */
+ copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
+ sizeof ( extmem ) );
+ } else {
+ /* Create a zero-length block */
+ ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
+ DBG ( "EXTMEM allocating [%lx,%lx)\n",
+ user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
+ extmem.size = 0;
+ }
+ extmem.used = ( new_size > 0 );
+
+ /* Expand/shrink block if possible */
+ if ( ptr == bottom ) {
+ /* Update block */
+ new = userptr_add ( ptr, - ( new_size - extmem.size ) );
+ align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
+ new_size += align;
+ new = userptr_add ( new, -align );
+ DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
+ user_to_phys ( ptr, 0 ),
+ user_to_phys ( ptr, extmem.size ),
+ user_to_phys ( new, 0 ),
+ user_to_phys ( new, new_size ));
+ memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
+ extmem.size : new_size ) );
+ extmem.size = new_size;
+ bottom = new;
+ } else {
+ /* Cannot expand; can only pretend to shrink */
+ if ( new_size > extmem.size ) {
+ /* Refuse to expand */
+ DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
+ user_to_phys ( ptr, 0 ),
+ user_to_phys ( ptr, extmem.size ) );
+ return UNULL;
+ }
+ }
+
+ /* Write back block properties */
+ copy_to_user ( new, -sizeof ( extmem ), &extmem,
+ sizeof ( extmem ) );
+
+ /* Collect any free blocks and update hidden memory region */
+ ecollect_free();
+ hide_region ( EXTMEM, user_to_phys ( bottom, -sizeof ( extmem ) ),
+ user_to_phys ( top, 0 ) );
+
+ return ( new_size ? new : UNOWHERE );
+}
+
+/**
+ * Allocate external memory
+ *
+ * @v size Requested size
+ * @ret ptr Memory, or UNULL
+ *
+ * Memory is guaranteed to be aligned to a page boundary.
+ */
+userptr_t umalloc ( size_t size ) {
+ return urealloc ( UNULL, size );
+}
+
+/**
+ * Free external memory
+ *
+ * @v ptr Memory allocated by umalloc(), or UNULL
+ *
+ * If @c ptr is UNULL, no action is taken.
+ */
+void ufree ( userptr_t ptr ) {
+ urealloc ( ptr, 0 );
+}
diff --git a/gpxe/src/arch/i386/core/video_subr.c b/gpxe/src/arch/i386/core/video_subr.c
new file mode 100644
index 00000000..bf82cc61
--- /dev/null
+++ b/gpxe/src/arch/i386/core/video_subr.c
@@ -0,0 +1,104 @@
+/*
+ *
+ * modified from linuxbios code
+ * by Cai Qiang <rimy2000@hotmail.com>
+ *
+ */
+
+#include "stddef.h"
+#include "string.h"
+#include "io.h"
+#include "console.h"
+#include <gpxe/init.h>
+#include "vga.h"
+
+struct console_driver vga_console;
+
+static char *vidmem; /* The video buffer */
+static int video_line, video_col;
+
+#define VIDBUFFER 0xB8000
+
+static void memsetw(void *s, int c, unsigned int n)
+{
+ unsigned int i;
+ u16 *ss = (u16 *) s;
+
+ for (i = 0; i < n; i++) {
+ ss[i] = ( u16 ) c;
+ }
+}
+
+static void video_init(void)
+{
+ static int inited=0;
+
+ vidmem = (char *)phys_to_virt(VIDBUFFER);
+
+ if (!inited) {
+ video_line = 0;
+ video_col = 0;
+
+ memsetw(vidmem, VGA_ATTR_CLR_WHT, 2*1024); //
+
+ inited=1;
+ }
+}
+
+static void video_scroll(void)
+{
+ int i;
+
+ memcpy(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2);
+ for (i = (LINES - 1) * COLS * 2; i < LINES * COLS * 2; i += 2)
+ vidmem[i] = ' ';
+}
+
+static void vga_putc(int byte)
+{
+ if (byte == '\n') {
+ video_line++;
+ video_col = 0;
+
+ } else if (byte == '\r') {
+ video_col = 0;
+
+ } else if (byte == '\b') {
+ video_col--;
+
+ } else if (byte == '\t') {
+ video_col += 4;
+
+ } else if (byte == '\a') {
+ //beep
+ //beep(500);
+
+ } else {
+ vidmem[((video_col + (video_line *COLS)) * 2)] = byte;
+ vidmem[((video_col + (video_line *COLS)) * 2) +1] = VGA_ATTR_CLR_WHT;
+ video_col++;
+ }
+ if (video_col < 0) {
+ video_col = 0;
+ }
+ if (video_col >= COLS) {
+ video_line++;
+ video_col = 0;
+ }
+ if (video_line >= LINES) {
+ video_scroll();
+ video_line--;
+ }
+ // move the cursor
+ write_crtc((video_col + (video_line *COLS)) >> 8, CRTC_CURSOR_HI);
+ write_crtc((video_col + (video_line *COLS)) & 0x0ff, CRTC_CURSOR_LO);
+}
+
+struct console_driver vga_console __console_driver = {
+ .putchar = vga_putc,
+ .disabled = 1,
+};
+
+struct init_fn video_init_fn __init_fn ( INIT_EARLY ) = {
+ .initialise = video_init,
+};
diff --git a/gpxe/src/arch/i386/core/virtaddr.S b/gpxe/src/arch/i386/core/virtaddr.S
new file mode 100644
index 00000000..5d762375
--- /dev/null
+++ b/gpxe/src/arch/i386/core/virtaddr.S
@@ -0,0 +1,101 @@
+/*
+ * Functions to support the virtual addressing method of relocation
+ * that Etherboot uses.
+ *
+ */
+
+#include "virtaddr.h"
+
+ .arch i386
+ .text
+ .code32
+
+/****************************************************************************
+ * _virt_to_phys (virtual addressing)
+ *
+ * Switch from virtual to flat physical addresses. %esp is adjusted
+ * to a physical value. Segment registers are set to flat physical
+ * selectors. All other registers are preserved. Flags are
+ * preserved.
+ *
+ * Parameters: none
+ * Returns: none
+ ****************************************************************************
+ */
+ .globl _virt_to_phys
+_virt_to_phys:
+ /* Preserve registers and flags */
+ pushfl
+ pushl %eax
+ pushl %ebp
+
+ /* Change return address to a physical address */
+ movl virt_offset, %ebp
+ addl %ebp, 12(%esp)
+
+ /* Switch to physical code segment */
+ pushl $PHYSICAL_CS
+ leal 1f(%ebp), %eax
+ pushl %eax
+ lret
+1:
+ /* Reload other segment registers and adjust %esp */
+ movl $PHYSICAL_DS, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %fs
+ movl %eax, %gs
+ movl %eax, %ss
+ addl %ebp, %esp
+
+ /* Restore registers and flags, and return */
+ popl %ebp
+ popl %eax
+ popfl
+ ret
+
+/****************************************************************************
+ * _phys_to_virt (flat physical addressing)
+ *
+ * Switch from flat physical to virtual addresses. %esp is adjusted
+ * to a virtual value. Segment registers are set to virtual
+ * selectors. All other registers are preserved. Flags are
+ * preserved.
+ *
+ * Note that this depends on the GDT already being correctly set up
+ * (e.g. by a call to run_here()).
+ *
+ * Parameters: none
+ * Returns: none
+ ****************************************************************************
+ */
+ .globl _phys_to_virt
+_phys_to_virt:
+ /* Preserve registers and flags */
+ pushfl
+ pushl %eax
+ pushl %ebp
+
+ /* Switch to virtual code segment */
+ ljmp $VIRTUAL_CS, $1f
+1:
+ /* Reload data segment registers */
+ movl $VIRTUAL_DS, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* Reload stack segment and adjust %esp */
+ movl virt_offset, %ebp
+ movl %eax, %ss
+ subl %ebp, %esp
+
+ /* Change the return address to a virtual address */
+ subl %ebp, 12(%esp)
+
+ /* Restore registers and flags, and return */
+ popl %ebp
+ popl %eax
+ popfl
+ ret
diff --git a/gpxe/src/arch/i386/core/wince_loader.c b/gpxe/src/arch/i386/core/wince_loader.c
new file mode 100644
index 00000000..f452b659
--- /dev/null
+++ b/gpxe/src/arch/i386/core/wince_loader.c
@@ -0,0 +1,273 @@
+#define LOAD_DEBUG 0
+
+static int get_x_header(unsigned char *data, unsigned long now);
+static void jump_2ep();
+static unsigned char ce_signature[] = {'B', '0', '0', '0', 'F', 'F', '\n',};
+static char ** ep;
+
+#define BOOT_ARG_PTR_LOCATION 0x001FFFFC
+
+typedef struct _BOOT_ARGS{
+ unsigned char ucVideoMode;
+ unsigned char ucComPort;
+ unsigned char ucBaudDivisor;
+ unsigned char ucPCIConfigType;
+
+ unsigned long dwSig;
+ #define BOOTARG_SIG 0x544F4F42
+ unsigned long dwLen;
+
+ unsigned char ucLoaderFlags;
+ unsigned char ucEshellFlags;
+ unsigned char ucEdbgAdapterType;
+ unsigned char ucEdbgIRQ;
+
+ unsigned long dwEdbgBaseAddr;
+ unsigned long dwEdbgDebugZone;
+ unsigned long dwDHCPLeaseTime;
+ unsigned long dwEdbgFlags;
+
+ unsigned long dwEBootFlag;
+ unsigned long dwEBootAddr;
+ unsigned long dwLaunchAddr;
+
+ unsigned long pvFlatFrameBuffer;
+ unsigned short vesaMode;
+ unsigned short cxDisplayScreen;
+ unsigned short cyDisplayScreen;
+ unsigned short cxPhysicalScreen;
+ unsigned short cyPhysicalScreen;
+ unsigned short cbScanLineLength;
+ unsigned short bppScreen;
+
+ unsigned char RedMaskSize;
+ unsigned char REdMaskPosition;
+ unsigned char GreenMaskSize;
+ unsigned char GreenMaskPosition;
+ unsigned char BlueMaskSize;
+ unsigned char BlueMaskPosition;
+} BOOT_ARGS;
+
+BOOT_ARGS BootArgs;
+
+static struct segment_info{
+ unsigned long addr; // Section Address
+ unsigned long size; // Section Size
+ unsigned long checksum; // Section CheckSum
+} X;
+
+#define PSIZE (1500) //Max Packet Size
+#define DSIZE (PSIZE+12)
+static unsigned long dbuffer_available =0;
+static unsigned long not_loadin =0;
+static unsigned long d_now =0;
+
+unsigned long entry;
+static unsigned long ce_curaddr;
+
+
+static sector_t ce_loader(unsigned char *data, unsigned int len, int eof);
+static os_download_t wince_probe(unsigned char *data, unsigned int len)
+{
+ if (strncmp(ce_signature, data, sizeof(ce_signature)) != 0) {
+ return 0;
+ }
+ printf("(WINCE)");
+ return ce_loader;
+}
+
+static sector_t ce_loader(unsigned char *data, unsigned int len, int eof)
+{
+ static unsigned char dbuffer[DSIZE];
+ int this_write = 0;
+ static int firsttime = 1;
+
+ /*
+ * new packet in, we have to
+ * [1] copy data to dbuffer,
+ *
+ * update...
+ * [2] dbuffer_available
+ */
+ memcpy( (dbuffer+dbuffer_available), data, len); //[1]
+ dbuffer_available += len; // [2]
+ len = 0;
+
+ d_now = 0;
+
+#if 0
+ printf("dbuffer_available =%ld \n", dbuffer_available);
+#endif
+
+ if (firsttime)
+ {
+ d_now = sizeof(ce_signature);
+ printf("String Physical Address = %lx \n",
+ *(unsigned long *)(dbuffer+d_now));
+
+ d_now += sizeof(unsigned long);
+ printf("Image Size = %ld [%lx]\n",
+ *(unsigned long *)(dbuffer+d_now),
+ *(unsigned long *)(dbuffer+d_now));
+
+ d_now += sizeof(unsigned long);
+ dbuffer_available -= d_now;
+
+ d_now = (unsigned long)get_x_header(dbuffer, d_now);
+ firsttime = 0;
+ }
+
+ if (not_loadin == 0)
+ {
+ d_now = get_x_header(dbuffer, d_now);
+ }
+
+ while ( not_loadin > 0 )
+ {
+ /* dbuffer do not have enough data to loading, copy all */
+#if LOAD_DEBUG
+ printf("[0] not_loadin = [%ld], dbuffer_available = [%ld] \n",
+ not_loadin, dbuffer_available);
+ printf("[0] d_now = [%ld] \n", d_now);
+#endif
+
+ if( dbuffer_available <= not_loadin)
+ {
+ this_write = dbuffer_available ;
+ memcpy(phys_to_virt(ce_curaddr), (dbuffer+d_now), this_write );
+ ce_curaddr += this_write;
+ not_loadin -= this_write;
+
+ /* reset index and available in the dbuffer */
+ dbuffer_available = 0;
+ d_now = 0;
+#if LOAD_DEBUG
+ printf("[1] not_loadin = [%ld], dbuffer_available = [%ld] \n",
+ not_loadin, dbuffer_available);
+ printf("[1] d_now = [%ld], this_write = [%d] \n",
+ d_now, this_write);
+#endif
+
+ // get the next packet...
+ return (0);
+ }
+
+ /* dbuffer have more data then loading ... , copy partital.... */
+ else
+ {
+ this_write = not_loadin;
+ memcpy(phys_to_virt(ce_curaddr), (dbuffer+d_now), this_write);
+ ce_curaddr += this_write;
+ not_loadin = 0;
+
+ /* reset index and available in the dbuffer */
+ dbuffer_available -= this_write;
+ d_now += this_write;
+#if LOAD_DEBUG
+ printf("[2] not_loadin = [%ld], dbuffer_available = [%ld] \n",
+ not_loadin, dbuffer_available);
+ printf("[2] d_now = [%ld], this_write = [%d] \n\n",
+ d_now, this_write);
+#endif
+
+ /* dbuffer not empty, proceed processing... */
+
+ // don't have enough data to get_x_header..
+ if ( dbuffer_available < (sizeof(unsigned long) * 3) )
+ {
+// printf("we don't have enough data remaining to call get_x. \n");
+ memcpy( (dbuffer+0), (dbuffer+d_now), dbuffer_available);
+ return (0);
+ }
+ else
+ {
+#if LOAD_DEBUG
+ printf("with remaining data to call get_x \n");
+ printf("dbuffer available = %ld , d_now = %ld\n",
+ dbuffer_available, d_now);
+#endif
+ d_now = get_x_header(dbuffer, d_now);
+ }
+ }
+ }
+ return (0);
+}
+
+static int get_x_header(unsigned char *dbuffer, unsigned long now)
+{
+ X.addr = *(unsigned long *)(dbuffer + now);
+ X.size = *(unsigned long *)(dbuffer + now + sizeof(unsigned long));
+ X.checksum = *(unsigned long *)(dbuffer + now + sizeof(unsigned long)*2);
+
+ if (X.addr == 0)
+ {
+ entry = X.size;
+ done(1);
+ printf("Entry Point Address = [%lx] \n", entry);
+ jump_2ep();
+ }
+
+ if (!prep_segment(X.addr, X.addr + X.size, X.addr + X.size, 0, 0)) {
+ longjmp(restart_etherboot, -2);
+ }
+
+ ce_curaddr = X.addr;
+ now += sizeof(unsigned long)*3;
+
+ /* re-calculate dbuffer available... */
+ dbuffer_available -= sizeof(unsigned long)*3;
+
+ /* reset index of this section */
+ not_loadin = X.size;
+
+#if 1
+ printf("\n");
+ printf("\t Section Address = [%lx] \n", X.addr);
+ printf("\t Size = %d [%lx]\n", X.size, X.size);
+ printf("\t Checksum = %ld [%lx]\n", X.checksum, X.checksum);
+#endif
+#if LOAD_DEBUG
+ printf("____________________________________________\n");
+ printf("\t dbuffer_now = %ld \n", now);
+ printf("\t dbuffer available = %ld \n", dbuffer_available);
+ printf("\t not_loadin = %ld \n", not_loadin);
+#endif
+
+ return now;
+}
+
+static void jump_2ep()
+{
+ BootArgs.ucVideoMode = 1;
+ BootArgs.ucComPort = 1;
+ BootArgs.ucBaudDivisor = 1;
+ BootArgs.ucPCIConfigType = 1; // do not fill with 0
+
+ BootArgs.dwSig = BOOTARG_SIG;
+ BootArgs.dwLen = sizeof(BootArgs);
+
+ if(BootArgs.ucVideoMode == 0)
+ {
+ BootArgs.cxDisplayScreen = 640;
+ BootArgs.cyDisplayScreen = 480;
+ BootArgs.cxPhysicalScreen = 640;
+ BootArgs.cyPhysicalScreen = 480;
+ BootArgs.bppScreen = 16;
+ BootArgs.cbScanLineLength = 1024;
+ BootArgs.pvFlatFrameBuffer = 0x800a0000; // ollie say 0x98000000
+ }
+ else if(BootArgs.ucVideoMode != 0xFF)
+ {
+ BootArgs.cxDisplayScreen = 0;
+ BootArgs.cyDisplayScreen = 0;
+ BootArgs.cxPhysicalScreen = 0;
+ BootArgs.cyPhysicalScreen = 0;
+ BootArgs.bppScreen = 0;
+ BootArgs.cbScanLineLength = 0;
+ BootArgs.pvFlatFrameBuffer = 0;
+ }
+
+ ep = phys_to_virt(BOOT_ARG_PTR_LOCATION);
+ *ep= virt_to_phys(&BootArgs);
+ xstart32(entry);
+}
diff --git a/gpxe/src/arch/i386/drivers/net/undi.c b/gpxe/src/arch/i386/drivers/net/undi.c
new file mode 100644
index 00000000..1090cc94
--- /dev/null
+++ b/gpxe/src/arch/i386/drivers/net/undi.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <gpxe/pci.h>
+#include <undi.h>
+#include <undirom.h>
+#include <undiload.h>
+#include <undinet.h>
+#include <undipreload.h>
+
+/** @file
+ *
+ * UNDI PCI driver
+ *
+ */
+
+/**
+ * Find UNDI ROM for PCI device
+ *
+ * @v pci PCI device
+ * @ret undirom UNDI ROM, or NULL
+ *
+ * Try to find a driver for this device. Try an exact match on the
+ * ROM address first, then fall back to a vendor/device ID match only
+ */
+static struct undi_rom * undipci_find_rom ( struct pci_device *pci ) {
+ struct undi_rom *undirom;
+ unsigned long rombase;
+
+ rombase = pci_bar_start ( pci, PCI_ROM_ADDRESS );
+ undirom = undirom_find_pci ( pci->vendor, pci->device, rombase );
+ if ( ! undirom )
+ undirom = undirom_find_pci ( pci->vendor, pci->device, 0 );
+ return undirom;
+}
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @v id PCI ID
+ * @ret rc Return status code
+ */
+static int undipci_probe ( struct pci_device *pci,
+ const struct pci_device_id *id __unused ) {
+ struct undi_device *undi;
+ struct undi_rom *undirom;
+ unsigned int busdevfn = PCI_BUSDEVFN ( pci->bus, pci->devfn );
+ int rc;
+
+ /* Ignore non-network devices */
+ if ( PCI_BASE_CLASS ( pci->class ) != PCI_BASE_CLASS_NETWORK )
+ return -ENOTTY;
+
+ /* Allocate UNDI device structure */
+ undi = zalloc ( sizeof ( *undi ) );
+ if ( ! undi )
+ return -ENOMEM;
+ pci_set_drvdata ( pci, undi );
+
+ /* Find/create our pixie */
+ if ( preloaded_undi.pci_busdevfn == busdevfn ) {
+ /* Claim preloaded UNDI device */
+ DBGC ( undi, "UNDI %p using preloaded UNDI device\n", undi );
+ memcpy ( undi, &preloaded_undi, sizeof ( *undi ) );
+ memset ( &preloaded_undi, 0, sizeof ( preloaded_undi ) );
+ } else {
+ /* Find UNDI ROM for PCI device */
+ if ( ! ( undirom = undipci_find_rom ( pci ) ) ) {
+ rc = -ENODEV;
+ goto err_find_rom;
+ }
+
+ /* Call UNDI ROM loader to create pixie */
+ if ( ( rc = undi_load_pci ( undi, undirom, busdevfn ) ) != 0 )
+ goto err_load_pci;
+ }
+
+ /* Add to device hierarchy */
+ snprintf ( undi->dev.name, sizeof ( undi->dev.name ),
+ "UNDI-%s", pci->dev.name );
+ memcpy ( &undi->dev.desc, &pci->dev.desc, sizeof ( undi->dev.desc ) );
+ undi->dev.parent = &pci->dev;
+ INIT_LIST_HEAD ( &undi->dev.children );
+ list_add ( &undi->dev.siblings, &pci->dev.children );
+
+ /* Create network device */
+ if ( ( rc = undinet_probe ( undi ) ) != 0 )
+ goto err_undinet_probe;
+
+ return 0;
+
+ err_undinet_probe:
+ undi_unload ( undi );
+ list_del ( &undi->dev.siblings );
+ err_find_rom:
+ err_load_pci:
+ free ( undi );
+ pci_set_drvdata ( pci, NULL );
+ return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void undipci_remove ( struct pci_device *pci ) {
+ struct undi_device *undi = pci_get_drvdata ( pci );
+
+ undinet_remove ( undi );
+ undi_unload ( undi );
+ list_del ( &undi->dev.siblings );
+ free ( undi );
+ pci_set_drvdata ( pci, NULL );
+}
+
+static struct pci_device_id undipci_nics[] = {
+PCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)" ),
+};
+
+struct pci_driver undipci_driver __pci_driver = {
+ .ids = undipci_nics,
+ .id_count = ( sizeof ( undipci_nics ) / sizeof ( undipci_nics[0] ) ),
+ .probe = undipci_probe,
+ .remove = undipci_remove,
+};
diff --git a/gpxe/src/arch/i386/drivers/net/undiisr.S b/gpxe/src/arch/i386/drivers/net/undiisr.S
new file mode 100644
index 00000000..a6c6c381
--- /dev/null
+++ b/gpxe/src/arch/i386/drivers/net/undiisr.S
@@ -0,0 +1,87 @@
+#define PXENV_UNDI_ISR 0x0014
+#define PXENV_UNDI_ISR_IN_START 1
+#define PXENV_UNDI_ISR_OUT_OURS 0
+#define PXENV_UNDI_ISR_OUT_NOT_OURS 1
+
+#define IRQ_PIC_CUTOFF 8
+#define ICR_EOI_NON_SPECIFIC 0x20
+#define PIC1_ICR 0x20
+#define PIC2_ICR 0xa0
+
+ .text
+ .arch i386
+ .section ".text16", "ax", @progbits
+ .section ".data16", "aw", @progbits
+ .code16
+
+ .section ".text16"
+ .globl undiisr
+undiisr:
+
+ /* Preserve registers */
+ pushw %ds
+ pushw %es
+ pushw %fs
+ pushw %gs
+ pushfl
+ pushal
+
+ /* Set up our segment registers */
+ movw %cs:rm_ds, %ax
+ movw %ax, %ds
+
+ /* Check that we have an UNDI entry point */
+ cmpw $0, undinet_entry_point
+ je chain
+
+ /* Issue UNDI API call */
+ movw %ax, %es
+ movw $undinet_params, %di
+ movw $PXENV_UNDI_ISR, %bx
+ movw $PXENV_UNDI_ISR_IN_START, funcflag
+ pushw %es
+ pushw %di
+ pushw %bx
+ lcall *undinet_entry_point
+ cli /* Just in case */
+ addw $6, %sp
+ cmpw $PXENV_UNDI_ISR_OUT_OURS, funcflag
+ jne eoi
+
+trig: /* Record interrupt occurence */
+ incb undiisr_trigger_count
+
+eoi: /* Send EOI */
+ movb $ICR_EOI_NON_SPECIFIC, %al
+ cmpb $IRQ_PIC_CUTOFF, undiisr_irq
+ jb 1f
+ outb %al, $PIC2_ICR
+1: outb %al, $PIC1_ICR
+ jmp exit
+
+chain: /* Chain to next handler */
+ pushfw
+ lcall *undiisr_next_handler
+
+exit: /* Restore registers and return */
+ cli
+ popal
+ movzwl %sp, %esp
+ addr32 movl -20(%esp), %esp /* %esp isn't restored by popal */
+ popfl
+ popw %gs
+ popw %fs
+ popw %es
+ popw %ds
+ iret
+
+ .section ".data16"
+undinet_params:
+status: .word 0
+funcflag: .word 0
+bufferlength: .word 0
+framelength: .word 0
+frameheaderlength: .word 0
+frame: .word 0, 0
+prottype: .byte 0
+pkttype: .byte 0
diff --git a/gpxe/src/arch/i386/drivers/net/undiload.c b/gpxe/src/arch/i386/drivers/net/undiload.c
new file mode 100644
index 00000000..a3284f80
--- /dev/null
+++ b/gpxe/src/arch/i386/drivers/net/undiload.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pxe.h>
+#include <realmode.h>
+#include <bios.h>
+#include <pnpbios.h>
+#include <basemem.h>
+#include <gpxe/pci.h>
+#include <undi.h>
+#include <undirom.h>
+#include <undiload.h>
+
+/** @file
+ *
+ * UNDI load/unload
+ *
+ */
+
+/** Parameter block for calling UNDI loader */
+static struct s_UNDI_LOADER __bss16 ( undi_loader );
+#define undi_loader __use_data16 ( undi_loader )
+
+/** UNDI loader entry point */
+static SEGOFF16_t __bss16 ( undi_loader_entry );
+#define undi_loader_entry __use_data16 ( undi_loader_entry )
+
+/**
+ * Call UNDI loader to create a pixie
+ *
+ * @v undi UNDI device
+ * @v undirom UNDI ROM
+ * @ret rc Return status code
+ */
+int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) {
+ struct s_PXE ppxe;
+ unsigned int fbms_seg;
+ uint16_t exit;
+ int rc;
+
+ /* Set up START_UNDI parameters */
+ memset ( &undi_loader, 0, sizeof ( undi_loader ) );
+ undi_loader.AX = undi->pci_busdevfn;
+ undi_loader.BX = undi->isapnp_csn;
+ undi_loader.DX = undi->isapnp_read_port;
+ undi_loader.ES = BIOS_SEG;
+ undi_loader.DI = find_pnp_bios();
+
+ /* Allocate base memory for PXE stack */
+ undi->restore_fbms = get_fbms();
+ fbms_seg = ( undi->restore_fbms << 6 );
+ fbms_seg -= ( ( undirom->code_size + 0x0f ) >> 4 );
+ undi_loader.UNDI_CS = fbms_seg;
+ fbms_seg -= ( ( undirom->data_size + 0x0f ) >> 4 );
+ undi_loader.UNDI_DS = fbms_seg;
+
+ /* Debug info */
+ DBGC ( undi, "UNDI %p loading UNDI ROM %p to CS %04x DS %04x for ",
+ undi, undirom, undi_loader.UNDI_CS, undi_loader.UNDI_DS );
+ if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) {
+ unsigned int bus = ( undi->pci_busdevfn >> 8 );
+ unsigned int devfn = ( undi->pci_busdevfn & 0xff );
+ DBGC ( undi, "PCI %02x:%02x.%x\n",
+ bus, PCI_SLOT ( devfn ), PCI_FUNC ( devfn ) );
+ }
+ if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) {
+ DBGC ( undi, "ISAPnP(%04x) CSN %04x\n",
+ undi->isapnp_read_port, undi->isapnp_csn );
+ }
+
+ /* Call loader */
+ undi_loader_entry = undirom->loader_entry;
+ __asm__ __volatile__ ( REAL_CODE ( "pushw %%ds\n\t"
+ "pushw %%ax\n\t"
+ "lcall *%c2\n\t"
+ "addw $4, %%sp\n\t" )
+ : "=a" ( exit )
+ : "a" ( & __from_data16 ( undi_loader ) ),
+ "p" ( & __from_data16 ( undi_loader_entry ) )
+ : "ebx", "ecx", "edx", "esi", "edi", "ebp" );
+
+ /* UNDI API calls may rudely change the status of A20 and not
+ * bother to restore it afterwards. Intel is known to be
+ * guilty of this.
+ *
+ * Note that we will return to this point even if A20 gets
+ * screwed up by the UNDI driver, because Etherboot always
+ * resides in an even megabyte of RAM.
+ */
+ gateA20_set();
+
+ if ( exit != PXENV_EXIT_SUCCESS ) {
+ rc = -undi_loader.Status;
+ if ( rc == 0 ) /* Paranoia */
+ rc = -EIO;
+ DBGC ( undi, "UNDI %p loader failed: %s\n",
+ undi, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Populate PXE device structure */
+ undi->pxenv = undi_loader.PXENVptr;
+ undi->ppxe = undi_loader.PXEptr;
+ copy_from_real ( &ppxe, undi->ppxe.segment, undi->ppxe.offset,
+ sizeof ( ppxe ) );
+ undi->entry = ppxe.EntryPointSP;
+ DBGC ( undi, "UNDI %p loaded PXENV+ %04x:%04x !PXE %04x:%04x "
+ "entry %04x:%04x\n", undi, undi->pxenv.segment,
+ undi->pxenv.offset, undi->ppxe.segment, undi->ppxe.offset,
+ undi->entry.segment, undi->entry.offset );
+
+ /* Update free base memory counter */
+ undi->fbms = ( fbms_seg >> 6 );
+ set_fbms ( undi->fbms );
+ DBGC ( undi, "UNDI %p using [%d,%d) kB of base memory\n",
+ undi, undi->fbms, undi->restore_fbms );
+
+ return 0;
+}
+
+/**
+ * Unload a pixie
+ *
+ * @v undi UNDI device
+ * @ret rc Return status code
+ *
+ * Erases the PXENV+ and !PXE signatures, and frees the used base
+ * memory (if possible).
+ */
+int undi_unload ( struct undi_device *undi ) {
+ static uint32_t dead = 0xdeaddead;
+
+ DBGC ( undi, "UNDI %p unloading\n", undi );
+
+ /* Erase signatures */
+ if ( undi->pxenv.segment )
+ put_real ( dead, undi->pxenv.segment, undi->pxenv.offset );
+ if ( undi->ppxe.segment )
+ put_real ( dead, undi->ppxe.segment, undi->ppxe.offset );
+
+ /* Free base memory, if possible */
+ if ( undi->fbms == get_fbms() ) {
+ DBGC ( undi, "UNDI %p freeing [%d,%d) kB of base memory\n",
+ undi, undi->fbms, undi->restore_fbms );
+ set_fbms ( undi->restore_fbms );
+ return 0;
+ } else {
+ DBGC ( undi, "UNDI %p leaking [%d,%d) kB of base memory\n",
+ undi, undi->fbms, undi->restore_fbms );
+ return -EBUSY;
+ }
+}
diff --git a/gpxe/src/arch/i386/drivers/net/undinet.c b/gpxe/src/arch/i386/drivers/net/undinet.c
new file mode 100644
index 00000000..e3b9f85a
--- /dev/null
+++ b/gpxe/src/arch/i386/drivers/net/undinet.c
@@ -0,0 +1,793 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <pxe.h>
+#include <realmode.h>
+#include <pic8259.h>
+#include <biosint.h>
+#include <pnpbios.h>
+#include <basemem_packet.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ethernet.h>
+#include <undi.h>
+#include <undinet.h>
+
+
+/** @file
+ *
+ * UNDI network device driver
+ *
+ */
+
+/** An UNDI NIC */
+struct undi_nic {
+ /** Assigned IRQ number */
+ unsigned int irq;
+ /** Currently processing ISR */
+ int isr_processing;
+ /** Bug workarounds */
+ int hacks;
+};
+
+/**
+ * @defgroup undi_hacks UNDI workarounds
+ * @{
+ */
+
+/** Work around Etherboot 5.4 bugs */
+#define UNDI_HACK_EB54 0x0001
+
+/** @} */
+
+static void undinet_close ( struct net_device *netdev );
+
+/*****************************************************************************
+ *
+ * UNDI API call
+ *
+ *****************************************************************************
+ */
+
+/**
+ * Name UNDI API call
+ *
+ * @v function API call number
+ * @ret name API call name
+ */
+static inline __attribute__ (( always_inline )) const char *
+undinet_function_name ( unsigned int function ) {
+ switch ( function ) {
+ case PXENV_START_UNDI:
+ return "PXENV_START_UNDI";
+ case PXENV_STOP_UNDI:
+ return "PXENV_STOP_UNDI";
+ case PXENV_UNDI_STARTUP:
+ return "PXENV_UNDI_STARTUP";
+ case PXENV_UNDI_CLEANUP:
+ return "PXENV_UNDI_CLEANUP";
+ case PXENV_UNDI_INITIALIZE:
+ return "PXENV_UNDI_INITIALIZE";
+ case PXENV_UNDI_RESET_ADAPTER:
+ return "PXENV_UNDI_RESET_ADAPTER";
+ case PXENV_UNDI_SHUTDOWN:
+ return "PXENV_UNDI_SHUTDOWN";
+ case PXENV_UNDI_OPEN:
+ return "PXENV_UNDI_OPEN";
+ case PXENV_UNDI_CLOSE:
+ return "PXENV_UNDI_CLOSE";
+ case PXENV_UNDI_TRANSMIT:
+ return "PXENV_UNDI_TRANSMIT";
+ case PXENV_UNDI_SET_MCAST_ADDRESS:
+ return "PXENV_UNDI_SET_MCAST_ADDRESS";
+ case PXENV_UNDI_SET_STATION_ADDRESS:
+ return "PXENV_UNDI_SET_STATION_ADDRESS";
+ case PXENV_UNDI_SET_PACKET_FILTER:
+ return "PXENV_UNDI_SET_PACKET_FILTER";
+ case PXENV_UNDI_GET_INFORMATION:
+ return "PXENV_UNDI_GET_INFORMATION";
+ case PXENV_UNDI_GET_STATISTICS:
+ return "PXENV_UNDI_GET_STATISTICS";
+ case PXENV_UNDI_CLEAR_STATISTICS:
+ return "PXENV_UNDI_CLEAR_STATISTICS";
+ case PXENV_UNDI_INITIATE_DIAGS:
+ return "PXENV_UNDI_INITIATE_DIAGS";
+ case PXENV_UNDI_FORCE_INTERRUPT:
+ return "PXENV_UNDI_FORCE_INTERRUPT";
+ case PXENV_UNDI_GET_MCAST_ADDRESS:
+ return "PXENV_UNDI_GET_MCAST_ADDRESS";
+ case PXENV_UNDI_GET_NIC_TYPE:
+ return "PXENV_UNDI_GET_NIC_TYPE";
+ case PXENV_UNDI_GET_IFACE_INFO:
+ return "PXENV_UNDI_GET_IFACE_INFO";
+ /*
+ * Duplicate case value; this is a bug in the PXE specification.
+ *
+ * case PXENV_UNDI_GET_STATE:
+ * return "PXENV_UNDI_GET_STATE";
+ */
+ case PXENV_UNDI_ISR:
+ return "PXENV_UNDI_ISR";
+ default:
+ return "UNKNOWN API CALL";
+ }
+}
+
+/**
+ * UNDI parameter block
+ *
+ * Used as the paramter block for all UNDI API calls. Resides in base
+ * memory.
+ */
+static union u_PXENV_ANY __bss16 ( undinet_params );
+#define undinet_params __use_data16 ( undinet_params )
+
+/** UNDI entry point
+ *
+ * Used as the indirection vector for all UNDI API calls. Resides in
+ * base memory.
+ */
+SEGOFF16_t __bss16 ( undinet_entry_point );
+#define undinet_entry_point __use_data16 ( undinet_entry_point )
+
+/**
+ * Issue UNDI API call
+ *
+ * @v undinic UNDI NIC
+ * @v function API call number
+ * @v params UNDI parameter block
+ * @v params_len Length of UNDI parameter block
+ * @ret rc Return status code
+ */
+static int undinet_call ( struct undi_nic *undinic, unsigned int function,
+ void *params, size_t params_len ) {
+ PXENV_EXIT_t exit;
+ int discard_b, discard_D;
+ int rc;
+
+ /* Copy parameter block and entry point */
+ assert ( params_len <= sizeof ( undinet_params ) );
+ memcpy ( &undinet_params, params, params_len );
+
+ /* Call real-mode entry point. This calling convention will
+ * work with both the !PXE and the PXENV+ entry points.
+ */
+ __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
+ "pushw %%di\n\t"
+ "pushw %%bx\n\t"
+ "lcall *%c3\n\t"
+ "addw $6, %%sp\n\t" )
+ : "=a" ( exit ), "=b" ( discard_b ),
+ "=D" ( discard_D )
+ : "p" ( &__from_data16 ( undinet_entry_point )),
+ "b" ( function ),
+ "D" ( &__from_data16 ( undinet_params ) )
+ : "ecx", "edx", "esi", "ebp" );
+
+ /* UNDI API calls may rudely change the status of A20 and not
+ * bother to restore it afterwards. Intel is known to be
+ * guilty of this.
+ *
+ * Note that we will return to this point even if A20 gets
+ * screwed up by the UNDI driver, because Etherboot always
+ * resides in an even megabyte of RAM.
+ */
+ gateA20_set();
+
+ /* Determine return status code based on PXENV_EXIT and
+ * PXENV_STATUS
+ */
+ if ( exit == PXENV_EXIT_SUCCESS ) {
+ rc = 0;
+ } else {
+ rc = -undinet_params.Status;
+ /* Paranoia; don't return success for the combination
+ * of PXENV_EXIT_FAILURE but PXENV_STATUS_SUCCESS
+ */
+ if ( rc == 0 )
+ rc = -EIO;
+ }
+
+ /* If anything goes wrong, print as much debug information as
+ * it's possible to give.
+ */
+ if ( rc != 0 ) {
+ SEGOFF16_t rm_params = {
+ .segment = rm_ds,
+ .offset = (intptr_t) &__from_data16 ( undinet_params ),
+ };
+
+ DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic,
+ undinet_function_name ( function ), strerror ( rc ) );
+ DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length "
+ "%#02zx, entry point at %04x:%04x\n", undinic,
+ rm_params.segment, rm_params.offset, params_len,
+ undinet_entry_point.segment,
+ undinet_entry_point.offset );
+ DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic );
+ DBGC_HDA ( undinic, rm_params, params, params_len );
+ DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic );
+ DBGC_HDA ( undinic, rm_params, &undinet_params, params_len );
+ }
+
+ /* Copy parameter block back */
+ memcpy ( params, &undinet_params, params_len );
+
+ return rc;
+}
+
+/*****************************************************************************
+ *
+ * UNDI interrupt service routine
+ *
+ *****************************************************************************
+ */
+
+/**
+ * UNDI interrupt service routine
+ *
+ * The UNDI ISR increments a counter (@c trigger_count) and exits.
+ */
+extern void undiisr ( void );
+
+/** IRQ number */
+uint8_t __data16 ( undiisr_irq );
+#define undiisr_irq __use_data16 ( undiisr_irq )
+
+/** IRQ chain vector */
+struct segoff __data16 ( undiisr_next_handler );
+#define undiisr_next_handler __use_data16 ( undiisr_next_handler )
+
+/** IRQ trigger count */
+volatile uint8_t __data16 ( undiisr_trigger_count ) = 0;
+#define undiisr_trigger_count __use_data16 ( undiisr_trigger_count )
+
+/** Last observed trigger count */
+static unsigned int last_trigger_count = 0;
+
+/**
+ * Hook UNDI interrupt service routine
+ *
+ * @v irq IRQ number
+ */
+static void undinet_hook_isr ( unsigned int irq ) {
+
+ assert ( irq <= IRQ_MAX );
+ assert ( undiisr_irq == 0 );
+
+ undiisr_irq = irq;
+ hook_bios_interrupt ( IRQ_INT ( irq ),
+ ( ( unsigned int ) undiisr ),
+ &undiisr_next_handler );
+}
+
+/**
+ * Unhook UNDI interrupt service routine
+ *
+ * @v irq IRQ number
+ */
+static void undinet_unhook_isr ( unsigned int irq ) {
+
+ assert ( irq <= IRQ_MAX );
+
+ unhook_bios_interrupt ( IRQ_INT ( irq ),
+ ( ( unsigned int ) undiisr ),
+ &undiisr_next_handler );
+ undiisr_irq = 0;
+}
+
+/**
+ * Test to see if UNDI ISR has been triggered
+ *
+ * @ret triggered ISR has been triggered since last check
+ */
+static int undinet_isr_triggered ( void ) {
+ unsigned int this_trigger_count;
+
+ /* Read trigger_count. Do this only once; it is volatile */
+ this_trigger_count = undiisr_trigger_count;
+
+ if ( this_trigger_count == last_trigger_count ) {
+ /* Not triggered */
+ return 0;
+ } else {
+ /* Triggered */
+ last_trigger_count = this_trigger_count;
+ return 1;
+ }
+}
+
+/*****************************************************************************
+ *
+ * UNDI network device interface
+ *
+ *****************************************************************************
+ */
+
+/** UNDI transmit buffer descriptor */
+static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
+#define undinet_tbd __use_data16 ( undinet_tbd )
+
+/**
+ * Transmit packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int undinet_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf ) {
+ struct undi_nic *undinic = netdev->priv;
+ struct s_PXENV_UNDI_TRANSMIT undi_transmit;
+ size_t len = iob_len ( iobuf );
+ int rc;
+
+ /* Technically, we ought to make sure that the previous
+ * transmission has completed before we re-use the buffer.
+ * However, many PXE stacks (including at least some Intel PXE
+ * stacks and Etherboot 5.4) fail to generate TX completions.
+ * In practice this won't be a problem, since our TX datapath
+ * has a very low packet volume and we can get away with
+ * assuming that a TX will be complete by the time we want to
+ * transmit the next packet.
+ */
+
+ /* Copy packet to UNDI I/O buffer */
+ if ( len > sizeof ( basemem_packet ) )
+ len = sizeof ( basemem_packet );
+ memcpy ( &basemem_packet, iobuf->data, len );
+
+ /* Create PXENV_UNDI_TRANSMIT data structure */
+ memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
+ undi_transmit.DestAddr.segment = rm_ds;
+ undi_transmit.DestAddr.offset
+ = ( ( unsigned ) & __from_data16 ( undinet_tbd ) );
+ undi_transmit.TBD.segment = rm_ds;
+ undi_transmit.TBD.offset
+ = ( ( unsigned ) & __from_data16 ( undinet_tbd ) );
+
+ /* Create PXENV_UNDI_TBD data structure */
+ undinet_tbd.ImmedLength = len;
+ undinet_tbd.Xmit.segment = rm_ds;
+ undinet_tbd.Xmit.offset
+ = ( ( unsigned ) & __from_data16 ( basemem_packet ) );
+
+ /* Issue PXE API call */
+ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT,
+ &undi_transmit,
+ sizeof ( undi_transmit ) ) ) != 0 )
+ goto done;
+
+ /* Free I/O buffer */
+ netdev_tx_complete ( netdev, iobuf );
+
+ done:
+ return rc;
+}
+
+/**
+ * Poll for received packets
+ *
+ * @v netdev Network device
+ *
+ * Fun, fun, fun. UNDI drivers don't use polling; they use
+ * interrupts. We therefore cheat and pretend that an interrupt has
+ * occurred every time undinet_poll() is called. This isn't too much
+ * of a hack; PCI devices share IRQs and so the first thing that a
+ * proper ISR should do is call PXENV_UNDI_ISR to determine whether or
+ * not the UNDI NIC generated the interrupt; there is no harm done by
+ * spurious calls to PXENV_UNDI_ISR. Similarly, we wouldn't be
+ * handling them any more rapidly than the usual rate of
+ * undinet_poll() being called even if we did implement a full ISR.
+ * So it should work. Ha!
+ *
+ * Addendum (21/10/03). Some cards don't play nicely with this trick,
+ * so instead of doing it the easy way we have to go to all the hassle
+ * of installing a genuine interrupt service routine and dealing with
+ * the wonderful 8259 Programmable Interrupt Controller. Joy.
+ *
+ * Addendum (10/07/07). When doing things such as iSCSI boot, in
+ * which we have to co-operate with a running OS, we can't get away
+ * with the "ISR-just-increments-a-counter-and-returns" trick at all,
+ * because it involves tying up the PIC for far too long, and other
+ * interrupt-dependent components (e.g. local disks) start breaking.
+ * We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR
+ * from within interrupt context in order to deassert the device
+ * interrupt, and sends EOI if applicable.
+ */
+static void undinet_poll ( struct net_device *netdev ) {
+ struct undi_nic *undinic = netdev->priv;
+ struct s_PXENV_UNDI_ISR undi_isr;
+ struct io_buffer *iobuf = NULL;
+ size_t len;
+ size_t frag_len;
+ size_t max_frag_len;
+ int rc;
+
+ if ( ! undinic->isr_processing ) {
+ /* Do nothing unless ISR has been triggered */
+ if ( ! undinet_isr_triggered() ) {
+ /* Allow interrupt to occur */
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cli\n\t" ) : : );
+ return;
+ }
+
+ /* Start ISR processing */
+ undinic->isr_processing = 1;
+ undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
+ } else {
+ /* Continue ISR processing */
+ undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ }
+
+ /* Run through the ISR loop */
+ while ( 1 ) {
+ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
+ sizeof ( undi_isr ) ) ) != 0 )
+ break;
+ switch ( undi_isr.FuncFlag ) {
+ case PXENV_UNDI_ISR_OUT_TRANSMIT:
+ /* We don't care about transmit completions */
+ break;
+ case PXENV_UNDI_ISR_OUT_RECEIVE:
+ /* Packet fragment received */
+ len = undi_isr.FrameLength;
+ frag_len = undi_isr.BufferLength;
+ if ( ( len == 0 ) || ( len < frag_len ) ) {
+ /* Don't laugh. VMWare does it. */
+ DBGC ( undinic, "UNDINIC %p reported insane "
+ "fragment (%zd of %zd bytes)\n",
+ undinic, frag_len, len );
+ netdev_rx_err ( netdev, NULL, -EINVAL );
+ break;
+ }
+ if ( ! iobuf )
+ iobuf = alloc_iob ( len );
+ if ( ! iobuf ) {
+ DBGC ( undinic, "UNDINIC %p could not "
+ "allocate %zd bytes for RX buffer\n",
+ undinic, len );
+ /* Fragment will be dropped */
+ netdev_rx_err ( netdev, NULL, -ENOMEM );
+ goto done;
+ }
+ max_frag_len = iob_tailroom ( iobuf );
+ if ( frag_len > max_frag_len ) {
+ DBGC ( undinic, "UNDINIC %p fragment too big "
+ "(%zd+%zd does not fit into %zd)\n",
+ undinic, iob_len ( iobuf ), frag_len,
+ ( iob_len ( iobuf ) + max_frag_len ) );
+ frag_len = max_frag_len;
+ }
+ copy_from_real ( iob_put ( iobuf, frag_len ),
+ undi_isr.Frame.segment,
+ undi_isr.Frame.offset, frag_len );
+ if ( iob_len ( iobuf ) == len ) {
+ /* Whole packet received; deliver it */
+ netdev_rx ( netdev, iobuf );
+ iobuf = NULL;
+ /* Etherboot 5.4 fails to return all packets
+ * under mild load; pretend it retriggered.
+ */
+ if ( undinic->hacks & UNDI_HACK_EB54 )
+ --last_trigger_count;
+ }
+ break;
+ case PXENV_UNDI_ISR_OUT_DONE:
+ /* Processing complete */
+ undinic->isr_processing = 0;
+ goto done;
+ default:
+ /* Should never happen. VMWare does it routinely. */
+ DBGC ( undinic, "UNDINIC %p ISR returned invalid "
+ "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
+ undinic->isr_processing = 0;
+ goto done;
+ }
+ undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ }
+
+ done:
+ if ( iobuf ) {
+ DBGC ( undinic, "UNDINIC %p returned incomplete packet "
+ "(%zd of %zd)\n", undinic, iob_len ( iobuf ),
+ ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
+ netdev_rx_err ( netdev, iobuf, -EINVAL );
+ }
+}
+
+/**
+ * Open NIC
+ *
+ * @v netdev Net device
+ * @ret rc Return status code
+ */
+static int undinet_open ( struct net_device *netdev ) {
+ struct undi_nic *undinic = netdev->priv;
+ struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
+ struct s_PXENV_UNDI_OPEN undi_open;
+ int rc;
+
+ /* Hook interrupt service routine and enable interrupt */
+ undinet_hook_isr ( undinic->irq );
+ enable_irq ( undinic->irq );
+ send_eoi ( undinic->irq );
+
+ /* Set station address. Required for some PXE stacks; will
+ * spuriously fail on others. Ignore failures. We only ever
+ * use it to set the MAC address to the card's permanent value
+ * anyway.
+ */
+ memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
+ sizeof ( undi_set_address.StationAddress ) );
+ undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS,
+ &undi_set_address, sizeof ( undi_set_address ) );
+
+ /* Open NIC */
+ memset ( &undi_open, 0, sizeof ( undi_open ) );
+ undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST );
+ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
+ sizeof ( undi_open ) ) ) != 0 )
+ goto err;
+
+ DBGC ( undinic, "UNDINIC %p opened\n", undinic );
+ return 0;
+
+err:
+ undinet_close ( netdev );
+ return rc;
+}
+
+/**
+ * Close NIC
+ *
+ * @v netdev Net device
+ */
+static void undinet_close ( struct net_device *netdev ) {
+ struct undi_nic *undinic = netdev->priv;
+ struct s_PXENV_UNDI_ISR undi_isr;
+ struct s_PXENV_UNDI_CLOSE undi_close;
+ int rc;
+
+ /* Ensure ISR has exited cleanly */
+ while ( undinic->isr_processing ) {
+ undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
+ sizeof ( undi_isr ) ) ) != 0 )
+ break;
+ switch ( undi_isr.FuncFlag ) {
+ case PXENV_UNDI_ISR_OUT_TRANSMIT:
+ case PXENV_UNDI_ISR_OUT_RECEIVE:
+ /* Continue draining */
+ break;
+ default:
+ /* Stop processing */
+ undinic->isr_processing = 0;
+ break;
+ }
+ }
+
+ /* Close NIC */
+ undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close,
+ sizeof ( undi_close ) );
+
+ /* Disable interrupt and unhook ISR */
+ disable_irq ( undinic->irq );
+ undinet_unhook_isr ( undinic->irq );
+#if 0
+ enable_irq ( undinic->irq );
+ send_eoi ( undinic->irq );
+#endif
+
+ DBGC ( undinic, "UNDINIC %p closed\n", undinic );
+}
+
+/**
+ * Enable/disable interrupts
+ *
+ * @v netdev Net device
+ * @v enable Interrupts should be enabled
+ */
+static void undinet_irq ( struct net_device *netdev, int enable ) {
+ struct undi_nic *undinic = netdev->priv;
+
+ /* Cannot support interrupts yet */
+ DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
+ undinic, ( enable ? "enable" : "disable" ) );
+}
+
+/** UNDI network device operations */
+static struct net_device_operations undinet_operations = {
+ .open = undinet_open,
+ .close = undinet_close,
+ .transmit = undinet_transmit,
+ .poll = undinet_poll,
+ .irq = undinet_irq,
+};
+
+/**
+ * Probe UNDI device
+ *
+ * @v undi UNDI device
+ * @ret rc Return status code
+ */
+int undinet_probe ( struct undi_device *undi ) {
+ struct net_device *netdev;
+ struct undi_nic *undinic;
+ struct s_PXENV_START_UNDI start_undi;
+ struct s_PXENV_UNDI_STARTUP undi_startup;
+ struct s_PXENV_UNDI_INITIALIZE undi_initialize;
+ struct s_PXENV_UNDI_GET_INFORMATION undi_info;
+ struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
+ struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
+ struct s_PXENV_UNDI_CLEANUP undi_cleanup;
+#if 0
+ struct s_PXENV_STOP_UNDI stop_undi;
+#endif
+ int rc;
+
+ /* Allocate net device */
+ netdev = alloc_etherdev ( sizeof ( *undinic ) );
+ if ( ! netdev )
+ return -ENOMEM;
+ netdev_init ( netdev, &undinet_operations );
+ undinic = netdev->priv;
+ undi_set_drvdata ( undi, netdev );
+ netdev->dev = &undi->dev;
+ memset ( undinic, 0, sizeof ( *undinic ) );
+ undinet_entry_point = undi->entry;
+ DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
+
+ /* Hook in UNDI stack */
+ if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
+ memset ( &start_undi, 0, sizeof ( start_undi ) );
+ start_undi.AX = undi->pci_busdevfn;
+ start_undi.BX = undi->isapnp_csn;
+ start_undi.DX = undi->isapnp_read_port;
+ start_undi.ES = BIOS_SEG;
+ start_undi.DI = find_pnp_bios();
+ if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI,
+ &start_undi,
+ sizeof ( start_undi ) ) ) != 0 )
+ goto err_start_undi;
+ /* Bring up UNDI stack */
+ memset ( &undi_startup, 0, sizeof ( undi_startup ) );
+ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP,
+ &undi_startup,
+ sizeof ( undi_startup ) ) ) != 0 )
+ goto err_undi_startup;
+
+ memset ( &undi_initialize, 0, sizeof ( undi_initialize ) );
+ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_INITIALIZE,
+ &undi_initialize,
+ sizeof ( undi_initialize ) ) ) != 0 )
+ goto err_undi_initialize;
+ }
+ undi->flags |= UNDI_FL_STARTED;
+
+ /* Get device information */
+ memset ( &undi_info, 0, sizeof ( undi_info ) );
+ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION,
+ &undi_info, sizeof ( undi_info ) ) ) != 0 )
+ goto err_undi_get_information;
+ memcpy ( netdev->ll_addr, undi_info.PermNodeAddress, ETH_ALEN );
+ undinic->irq = undi_info.IntNumber;
+ if ( undinic->irq > IRQ_MAX ) {
+ DBGC ( undinic, "UNDINIC %p invalid IRQ %d\n",
+ undinic, undinic->irq );
+ goto err_bad_irq;
+ }
+ DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n",
+ undinic, eth_ntoa ( netdev->ll_addr ), undinic->irq );
+
+ /* Get interface information */
+ memset ( &undi_iface, 0, sizeof ( undi_iface ) );
+ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO,
+ &undi_iface,
+ sizeof ( undi_iface ) ) ) != 0 )
+ goto err_undi_get_iface_info;
+ DBGC ( undinic, "UNDINIC %p has type %s and link speed %ld\n",
+ undinic, undi_iface.IfaceType, undi_iface.LinkSpeed );
+ if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
+ sizeof ( undi_iface.IfaceType ) ) == 0 ) {
+ DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
+ undinic );
+ undinic->hacks |= UNDI_HACK_EB54;
+ }
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register;
+
+ DBGC ( undinic, "UNDINIC %p added\n", undinic );
+ return 0;
+
+ err_register:
+ err_undi_get_iface_info:
+ err_bad_irq:
+ err_undi_get_information:
+ err_undi_initialize:
+
+ /* Shut down UNDI stack */
+ memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
+ undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
+ sizeof ( undi_shutdown ) );
+ memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
+ undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
+ sizeof ( undi_cleanup ) );
+ err_undi_startup:
+#if 0
+ /* Unhook UNDI stack */
+ memset ( &stop_undi, 0, sizeof ( stop_undi ) );
+ undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
+ sizeof ( stop_undi ) );
+#endif
+ err_start_undi:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ undi_set_drvdata ( undi, NULL );
+ return rc;
+}
+
+/**
+ * Remove UNDI device
+ *
+ * @v undi UNDI device
+ */
+void undinet_remove ( struct undi_device *undi ) {
+ struct net_device *netdev = undi_get_drvdata ( undi );
+ struct undi_nic *undinic = netdev->priv;
+#if 0
+ struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
+ struct s_PXENV_UNDI_CLEANUP undi_cleanup;
+ struct s_PXENV_STOP_UNDI stop_undi;
+#endif
+
+ /* Unregister net device */
+ unregister_netdev ( netdev );
+
+ /* Shut down UNDI stack */
+#if 0
+ memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
+ undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
+ sizeof ( undi_shutdown ) );
+ memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
+ undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
+ sizeof ( undi_cleanup ) );
+
+ /* Unhook UNDI stack */
+ memset ( &stop_undi, 0, sizeof ( stop_undi ) );
+ undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
+ sizeof ( stop_undi ) );
+ undi->flags &= ~UNDI_FL_STARTED;
+#endif
+
+ /* Clear entry point */
+ memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) );
+
+ /* Free network device */
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+
+ DBGC ( undinic, "UNDINIC %p removed\n", undinic );
+}
diff --git a/gpxe/src/arch/i386/drivers/net/undionly.c b/gpxe/src/arch/i386/drivers/net/undionly.c
new file mode 100644
index 00000000..ee361493
--- /dev/null
+++ b/gpxe/src/arch/i386/drivers/net/undionly.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gpxe/device.h>
+#include <undi.h>
+#include <undinet.h>
+#include <undipreload.h>
+
+/** @file
+ *
+ * "Pure" UNDI driver
+ *
+ * This is the UNDI driver without explicit support for PCI or any
+ * other bus type. It is capable only of using the preloaded UNDI
+ * device. It must not be combined in an image with any other
+ * drivers.
+ *
+ * If you want a PXE-loadable image that contains only the UNDI
+ * driver, build "bin/undionly.kpxe".
+ *
+ * If you want any other image format, or any other drivers in
+ * addition to the UNDI driver, build e.g. "bin/undi.dsk".
+ */
+
+/**
+ * Probe UNDI root bus
+ *
+ * @v rootdev UNDI bus root device
+ *
+ * Scans the UNDI bus for devices and registers all devices it can
+ * find.
+ */
+static int undibus_probe ( struct root_device *rootdev ) {
+ struct undi_device *undi = &preloaded_undi;
+ int rc;
+
+ /* Check for a valie preloaded UNDI device */
+ if ( ! undi->entry.segment ) {
+ DBG ( "No preloaded UNDI device found!\n" );
+ return -ENODEV;
+ }
+
+ /* Add to device hierarchy */
+ strncpy ( undi->dev.name, "UNDI",
+ ( sizeof ( undi->dev.name ) - 1 ) );
+ if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) {
+ undi->dev.desc.bus_type = BUS_TYPE_PCI;
+ undi->dev.desc.location = undi->pci_busdevfn;
+ undi->dev.desc.vendor = undi->pci_vendor;
+ undi->dev.desc.device = undi->pci_device;
+ } else if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) {
+ undi->dev.desc.bus_type = BUS_TYPE_ISAPNP;
+ }
+ undi->dev.parent = &rootdev->dev;
+ list_add ( &undi->dev.siblings, &rootdev->dev.children);
+ INIT_LIST_HEAD ( &undi->dev.children );
+
+ /* Create network device */
+ if ( ( rc = undinet_probe ( undi ) ) != 0 )
+ goto err;
+
+ return 0;
+
+ err:
+ list_del ( &undi->dev.siblings );
+ return rc;
+}
+
+/**
+ * Remove UNDI root bus
+ *
+ * @v rootdev UNDI bus root device
+ */
+static void undibus_remove ( struct root_device *rootdev __unused ) {
+ struct undi_device *undi = &preloaded_undi;
+
+ undinet_remove ( undi );
+ list_del ( &undi->dev.siblings );
+}
+
+/** UNDI bus root device driver */
+static struct root_driver undi_root_driver = {
+ .probe = undibus_probe,
+ .remove = undibus_remove,
+};
+
+/** UNDI bus root device */
+struct root_device undi_root_device __root_device = {
+ .dev = { .name = "UNDI" },
+ .driver = &undi_root_driver,
+};
diff --git a/gpxe/src/arch/i386/drivers/net/undipreload.c b/gpxe/src/arch/i386/drivers/net/undipreload.c
new file mode 100644
index 00000000..e29d150a
--- /dev/null
+++ b/gpxe/src/arch/i386/drivers/net/undipreload.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <realmode.h>
+#include <undipreload.h>
+
+/** @file
+ *
+ * Preloaded UNDI stack
+ *
+ */
+
+/**
+ * Preloaded UNDI device
+ *
+ * This is the UNDI device that was present when Etherboot started
+ * execution (i.e. when loading a .kpxe image). The first driver to
+ * claim this device must zero out this data structure.
+ */
+struct undi_device __data16 ( preloaded_undi );
diff --git a/gpxe/src/arch/i386/drivers/net/undirom.c b/gpxe/src/arch/i386/drivers/net/undirom.c
new file mode 100644
index 00000000..f977a553
--- /dev/null
+++ b/gpxe/src/arch/i386/drivers/net/undirom.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pxe.h>
+#include <realmode.h>
+#include <undirom.h>
+
+/** @file
+ *
+ * UNDI expansion ROMs
+ *
+ */
+
+/** List of all UNDI ROMs */
+static LIST_HEAD ( undiroms );
+
+/**
+ * Parse PXE ROM ID structure
+ *
+ * @v undirom UNDI ROM
+ * @v pxeromid Offset within ROM to PXE ROM ID structure
+ * @ret rc Return status code
+ */
+static int undirom_parse_pxeromid ( struct undi_rom *undirom,
+ unsigned int pxeromid ) {
+ struct undi_rom_id undi_rom_id;
+ unsigned int undiloader;
+
+ DBGC ( undirom, "UNDIROM %p has PXE ROM ID at %04x:%04x\n", undirom,
+ undirom->rom_segment, pxeromid );
+
+ /* Read PXE ROM ID structure and verify */
+ copy_from_real ( &undi_rom_id, undirom->rom_segment, pxeromid,
+ sizeof ( undi_rom_id ) );
+ if ( undi_rom_id.Signature != UNDI_ROM_ID_SIGNATURE ) {
+ DBGC ( undirom, "UNDIROM %p has bad PXE ROM ID signature "
+ "%08lx\n", undirom, undi_rom_id.Signature );
+ return -EINVAL;
+ }
+
+ /* Check for UNDI loader */
+ undiloader = undi_rom_id.UNDILoader;
+ if ( ! undiloader ) {
+ DBGC ( undirom, "UNDIROM %p has no UNDI loader\n", undirom );
+ return -EINVAL;
+ }
+
+ /* Fill in UNDI ROM loader fields */
+ undirom->loader_entry.segment = undirom->rom_segment;
+ undirom->loader_entry.offset = undiloader;
+ undirom->code_size = undi_rom_id.CodeSize;
+ undirom->data_size = undi_rom_id.DataSize;
+
+ DBGC ( undirom, "UNDIROM %p has UNDI loader at %04x:%04x "
+ "(code %04zx data %04zx)\n", undirom,
+ undirom->loader_entry.segment, undirom->loader_entry.offset,
+ undirom->code_size, undirom->data_size );
+ return 0;
+}
+
+/**
+ * Parse PCI expansion header
+ *
+ * @v undirom UNDI ROM
+ * @v pcirheader Offset within ROM to PCI expansion header
+ */
+static int undirom_parse_pcirheader ( struct undi_rom *undirom,
+ unsigned int pcirheader ) {
+ struct pcir_header pcir_header;
+
+ DBGC ( undirom, "UNDIROM %p has PCI expansion header at %04x:%04x\n",
+ undirom, undirom->rom_segment, pcirheader );
+
+ /* Read PCI expansion header and verify */
+ copy_from_real ( &pcir_header, undirom->rom_segment, pcirheader,
+ sizeof ( pcir_header ) );
+ if ( pcir_header.signature != PCIR_SIGNATURE ) {
+ DBGC ( undirom, "UNDIROM %p has bad PCI expansion header "
+ "signature %08lx\n", undirom, pcir_header.signature );
+ return -EINVAL;
+ }
+
+ /* Fill in UNDI ROM PCI device fields */
+ undirom->bus_type = PCI_NIC;
+ undirom->bus_id.pci.vendor_id = pcir_header.vendor_id;
+ undirom->bus_id.pci.device_id = pcir_header.device_id;
+
+ DBGC ( undirom, "UNDIROM %p is for PCI devices %04x:%04x\n", undirom,
+ undirom->bus_id.pci.vendor_id, undirom->bus_id.pci.device_id );
+ return 0;
+
+}
+
+/**
+ * Probe UNDI ROM
+ *
+ * @v rom_segment ROM segment address
+ * @ret rc Return status code
+ */
+static int undirom_probe ( unsigned int rom_segment ) {
+ struct undi_rom *undirom = NULL;
+ struct undi_rom_header romheader;
+ size_t rom_len;
+ unsigned int pxeromid;
+ unsigned int pcirheader;
+ int rc;
+
+ /* Read expansion ROM header and verify */
+ copy_from_real ( &romheader, rom_segment, 0, sizeof ( romheader ) );
+ if ( romheader.Signature != ROM_SIGNATURE ) {
+ rc = -EINVAL;
+ goto err;
+ }
+ rom_len = ( romheader.ROMLength * 512 );
+
+ /* Allocate memory for UNDI ROM */
+ undirom = zalloc ( sizeof ( *undirom ) );
+ if ( ! undirom ) {
+ DBG ( "Could not allocate UNDI ROM structure\n" );
+ rc = -ENOMEM;
+ goto err;
+ }
+ DBGC ( undirom, "UNDIROM %p trying expansion ROM at %04x:0000 "
+ "(%zdkB)\n", undirom, rom_segment, ( rom_len / 1024 ) );
+ undirom->rom_segment = rom_segment;
+
+ /* Check for and parse PXE ROM ID */
+ pxeromid = romheader.PXEROMID;
+ if ( ! pxeromid ) {
+ DBGC ( undirom, "UNDIROM %p has no PXE ROM ID\n", undirom );
+ rc = -EINVAL;
+ goto err;
+ }
+ if ( pxeromid > rom_len ) {
+ DBGC ( undirom, "UNDIROM %p PXE ROM ID outside ROM\n",
+ undirom );
+ rc = -EINVAL;
+ goto err;
+ }
+ if ( ( rc = undirom_parse_pxeromid ( undirom, pxeromid ) ) != 0 )
+ goto err;
+
+ /* Parse PCIR header, if present */
+ pcirheader = romheader.PCIRHeader;
+ if ( pcirheader )
+ undirom_parse_pcirheader ( undirom, pcirheader );
+
+ /* Add to UNDI ROM list and return */
+ DBGC ( undirom, "UNDIROM %p registered\n", undirom );
+ list_add ( &undirom->list, &undiroms );
+ return 0;
+
+ err:
+ free ( undirom );
+ return rc;
+}
+
+/**
+ * Create UNDI ROMs for all possible expansion ROMs
+ *
+ * @ret
+ */
+static void undirom_probe_all_roms ( void ) {
+ static int probed = 0;
+ unsigned int rom_segment;
+
+ /* Perform probe only once */
+ if ( probed )
+ return;
+
+ DBG ( "Scanning for PXE expansion ROMs\n" );
+
+ /* Scan through expansion ROM region at 2kB intervals */
+ for ( rom_segment = 0xc000 ; rom_segment < 0x10000 ;
+ rom_segment += 0x80 ) {
+ undirom_probe ( rom_segment );
+ }
+
+ probed = 1;
+}
+
+/**
+ * Find UNDI ROM for PCI device
+ *
+ * @v vendor_id PCI vendor ID
+ * @v device_id PCI device ID
+ * @v rombase ROM base address, or 0 for any
+ * @ret undirom UNDI ROM, or NULL
+ */
+struct undi_rom * undirom_find_pci ( unsigned int vendor_id,
+ unsigned int device_id,
+ unsigned int rombase ) {
+ struct undi_rom *undirom;
+
+ undirom_probe_all_roms();
+
+ list_for_each_entry ( undirom, &undiroms, list ) {
+ if ( undirom->bus_type != PCI_NIC )
+ continue;
+ if ( undirom->bus_id.pci.vendor_id != vendor_id )
+ continue;
+ if ( undirom->bus_id.pci.device_id != device_id )
+ continue;
+ if ( rombase && ( ( undirom->rom_segment << 4 ) != rombase ) )
+ continue;
+ DBGC ( undirom, "UNDIROM %p matched PCI %04x:%04x (%08x)\n",
+ undirom, vendor_id, device_id, rombase );
+ return undirom;
+ }
+
+ DBG ( "No UNDI ROM matched PCI %04x:%04x (%08x)\n",
+ vendor_id, device_id, rombase );
+ return NULL;
+}
diff --git a/gpxe/src/arch/i386/drivers/timer_bios.c b/gpxe/src/arch/i386/drivers/timer_bios.c
new file mode 100644
index 00000000..f9caf8d9
--- /dev/null
+++ b/gpxe/src/arch/i386/drivers/timer_bios.c
@@ -0,0 +1,57 @@
+/*
+ * Etherboot routines for PCBIOS firmware.
+ *
+ * Body of routines taken from old pcbios.S
+ */
+
+#include <gpxe/init.h>
+#include <gpxe/timer.h>
+#include <stdio.h>
+#include <realmode.h>
+#include <bios.h>
+#include <bits/timer2.h>
+
+/* A bit faster actually, but we don't care. */
+#define TIMER2_TICKS_PER_SEC 18
+
+/*
+ * Use direct memory access to BIOS variables, longword 0040:006C (ticks
+ * today) and byte 0040:0070 (midnight crossover flag) instead of calling
+ * timeofday BIOS interrupt.
+ */
+
+static tick_t bios_currticks ( void ) {
+ static int days = 0;
+ uint32_t ticks;
+ uint8_t midnight;
+
+ /* Re-enable interrupts so that the timer interrupt can occur */
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cli\n\t" ) : : );
+
+ get_real ( ticks, BDA_SEG, 0x006c );
+ get_real ( midnight, BDA_SEG, 0x0070 );
+
+ if ( midnight ) {
+ midnight = 0;
+ put_real ( midnight, BDA_SEG, 0x0070 );
+ days += 0x1800b0;
+ }
+
+ return ( (days + ticks) * (USECS_IN_SEC / TIMER2_TICKS_PER_SEC) );
+}
+
+static int bios_ts_init(void)
+{
+ DBG("BIOS timer installed\n");
+ return 0;
+}
+
+struct timer bios_ts __timer ( 02 ) = {
+ .init = bios_ts_init,
+ .udelay = i386_timer2_udelay,
+ .currticks = bios_currticks,
+};
+
diff --git a/gpxe/src/arch/i386/drivers/timer_rdtsc.c b/gpxe/src/arch/i386/drivers/timer_rdtsc.c
new file mode 100644
index 00000000..09b7df2f
--- /dev/null
+++ b/gpxe/src/arch/i386/drivers/timer_rdtsc.c
@@ -0,0 +1,69 @@
+
+#include <gpxe/init.h>
+#include <gpxe/timer.h>
+#include <errno.h>
+#include <stdio.h>
+#include <bits/cpu.h>
+#include <bits/timer2.h>
+#include <io.h>
+
+
+#define rdtsc(low,high) \
+ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
+
+#define rdtscll(val) \
+ __asm__ __volatile__ ("rdtsc" : "=A" (val))
+
+
+/* Measure how many clocks we get in one microsecond */
+static inline uint64_t calibrate_tsc(void)
+{
+
+ uint64_t rdtsc_start;
+ uint64_t rdtsc_end;
+
+ rdtscll(rdtsc_start);
+ i386_timer2_udelay(USECS_IN_MSEC);
+ rdtscll(rdtsc_end);
+
+ return (rdtsc_end - rdtsc_start) / USECS_IN_MSEC;
+}
+
+static uint32_t clocks_per_usec = 0;
+
+/* We measure time in microseconds. */
+static tick_t rdtsc_currticks(void)
+{
+ uint64_t clocks;
+
+ /* Read the Time Stamp Counter */
+ rdtscll(clocks);
+
+ return clocks / clocks_per_usec;
+}
+
+static int rdtsc_ts_init(void)
+{
+
+ struct cpuinfo_x86 cpu_info;
+
+ get_cpuinfo(&cpu_info);
+ if (cpu_info.features & X86_FEATURE_TSC) {
+ clocks_per_usec= calibrate_tsc();
+ if (clocks_per_usec) {
+ DBG("RDTSC ticksource installed. CPU running at %ld Mhz\n",
+ clocks_per_usec);
+ return 0;
+ }
+ }
+
+ DBG("RDTSC ticksource not available on this machine.\n");
+ return -ENODEV;
+}
+
+struct timer rdtsc_ts __timer (01) = {
+ .init = rdtsc_ts_init,
+ .udelay = generic_currticks_udelay,
+ .currticks = rdtsc_currticks,
+};
+
diff --git a/gpxe/src/arch/i386/firmware/pcbios/basemem.c b/gpxe/src/arch/i386/firmware/pcbios/basemem.c
new file mode 100644
index 00000000..b126d2a7
--- /dev/null
+++ b/gpxe/src/arch/i386/firmware/pcbios/basemem.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <realmode.h>
+#include <bios.h>
+#include <basemem.h>
+#include <gpxe/hidemem.h>
+
+/** @file
+ *
+ * Base memory allocation
+ *
+ */
+
+/**
+ * Set the BIOS free base memory counter
+ *
+ * @v new_fbms New free base memory counter (in kB)
+ */
+void set_fbms ( unsigned int new_fbms ) {
+ uint16_t fbms = new_fbms;
+
+ /* Update the BIOS memory counter */
+ put_real ( fbms, BDA_SEG, BDA_FBMS );
+
+ /* Update our hidden memory region map */
+ hide_basemem();
+}
diff --git a/gpxe/src/arch/i386/firmware/pcbios/bios_console.c b/gpxe/src/arch/i386/firmware/pcbios/bios_console.c
new file mode 100644
index 00000000..dcb0462a
--- /dev/null
+++ b/gpxe/src/arch/i386/firmware/pcbios/bios_console.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <assert.h>
+#include <realmode.h>
+#include <console.h>
+#include <gpxe/ansiesc.h>
+
+#define ATTR_BOLD 0x08
+
+#define ATTR_FCOL_MASK 0x07
+#define ATTR_FCOL_BLACK 0x00
+#define ATTR_FCOL_BLUE 0x01
+#define ATTR_FCOL_GREEN 0x02
+#define ATTR_FCOL_CYAN 0x03
+#define ATTR_FCOL_RED 0x04
+#define ATTR_FCOL_MAGENTA 0x05
+#define ATTR_FCOL_YELLOW 0x06
+#define ATTR_FCOL_WHITE 0x07
+
+#define ATTR_BCOL_MASK 0x70
+#define ATTR_BCOL_BLACK 0x00
+#define ATTR_BCOL_BLUE 0x10
+#define ATTR_BCOL_GREEN 0x20
+#define ATTR_BCOL_CYAN 0x30
+#define ATTR_BCOL_RED 0x40
+#define ATTR_BCOL_MAGENTA 0x50
+#define ATTR_BCOL_YELLOW 0x60
+#define ATTR_BCOL_WHITE 0x70
+
+#define ATTR_DEFAULT ATTR_FCOL_WHITE
+
+/** Current character attribute */
+static unsigned int bios_attr = ATTR_DEFAULT;
+
+/**
+ * Handle ANSI CUP (cursor position)
+ *
+ * @v count Parameter count
+ * @v params[0] Row (1 is top)
+ * @v params[1] Column (1 is left)
+ */
+static void bios_handle_cup ( unsigned int count __unused, int params[] ) {
+ int cx = ( params[1] - 1 );
+ int cy = ( params[0] - 1 );
+
+ if ( cx < 0 )
+ cx = 0;
+ if ( cy < 0 )
+ cy = 0;
+
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "int $0x10\n\t"
+ "cli\n\t" )
+ : : "a" ( 0x0200 ), "b" ( 1 ),
+ "d" ( ( cy << 8 ) | cx ) );
+}
+
+/**
+ * Handle ANSI ED (erase in page)
+ *
+ * @v count Parameter count
+ * @v params[0] Region to erase
+ */
+static void bios_handle_ed ( unsigned int count __unused,
+ int params[] __unused ) {
+ /* We assume that we always clear the whole screen */
+ assert ( params[0] == ANSIESC_ED_ALL );
+
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "int $0x10\n\t"
+ "cli\n\t" )
+ : : "a" ( 0x0600 ), "b" ( bios_attr << 8 ),
+ "c" ( 0 ), "d" ( 0xffff ) );
+}
+
+/**
+ * Handle ANSI SGR (set graphics rendition)
+ *
+ * @v count Parameter count
+ * @v params List of graphic rendition aspects
+ */
+static void bios_handle_sgr ( unsigned int count, int params[] ) {
+ static const uint8_t bios_attr_fcols[10] = {
+ ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN,
+ ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA,
+ ATTR_FCOL_CYAN, ATTR_FCOL_WHITE,
+ ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */
+ };
+ static const uint8_t bios_attr_bcols[10] = {
+ ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN,
+ ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA,
+ ATTR_BCOL_CYAN, ATTR_BCOL_WHITE,
+ ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */
+ };
+ unsigned int i;
+ int aspect;
+
+ for ( i = 0 ; i < count ; i++ ) {
+ aspect = params[i];
+ if ( aspect == 0 ) {
+ bios_attr = ATTR_DEFAULT;
+ } else if ( aspect == 1 ) {
+ bios_attr |= ATTR_BOLD;
+ } else if ( aspect == 22 ) {
+ bios_attr &= ~ATTR_BOLD;
+ } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) {
+ bios_attr &= ~ATTR_FCOL_MASK;
+ bios_attr |= bios_attr_fcols[ aspect - 30 ];
+ } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) {
+ bios_attr &= ~ATTR_BCOL_MASK;
+ bios_attr |= bios_attr_bcols[ aspect - 40 ];
+ }
+ }
+}
+
+/** BIOS console ANSI escape sequence handlers */
+static struct ansiesc_handler bios_ansiesc_handlers[] = {
+ { ANSIESC_CUP, bios_handle_cup },
+ { ANSIESC_ED, bios_handle_ed },
+ { ANSIESC_SGR, bios_handle_sgr },
+ { 0, NULL }
+};
+
+/** BIOS console ANSI escape sequence context */
+static struct ansiesc_context bios_ansiesc_ctx = {
+ .handlers = bios_ansiesc_handlers,
+};
+
+/**
+ * Print a character to BIOS console
+ *
+ * @v character Character to be printed
+ */
+static void bios_putchar ( int character ) {
+ int discard_a, discard_b, discard_c;
+
+ /* Intercept ANSI escape sequences */
+ character = ansiesc_process ( &bios_ansiesc_ctx, character );
+ if ( character < 0 )
+ return;
+
+ /* Print character with attribute */
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ /* Skip non-printable characters */
+ "cmpb $0x20, %%al\n\t"
+ "jb 1f\n\t"
+ /* Set attribute */
+ "movw $0x0001, %%cx\n\t"
+ "movb $0x09, %%ah\n\t"
+ "int $0x10\n\t"
+ "\n1:\n\t"
+ /* Print character */
+ "xorw %%bx, %%bx\n\t"
+ "movb $0x0e, %%ah\n\t"
+ "int $0x10\n\t"
+ "cli\n\t" )
+ : "=a" ( discard_a ), "=b" ( discard_b ),
+ "=c" ( discard_c )
+ : "a" ( character ), "b" ( bios_attr )
+ : "ebp" );
+}
+
+/**
+ * Pointer to current ANSI output sequence
+ *
+ * While we are in the middle of returning an ANSI sequence for a
+ * special key, this will point to the next character to return. When
+ * not in the middle of such a sequence, this will point to a NUL
+ * (note: not "will be NULL").
+ */
+static const char *ansi_input = "";
+
+/**
+ * Lowest BIOS scancode of interest
+ *
+ * Most of the BIOS key scancodes that we are interested in are in a
+ * dense range, so subtracting a constant and treating them as offsets
+ * into an array works efficiently.
+ */
+#define BIOS_KEY_MIN 0x47
+
+/** Offset into list of interesting BIOS scancodes */
+#define BIOS_KEY(scancode) ( (scancode) - BIOS_KEY_MIN )
+
+/** Mapping from BIOS scan codes to ANSI escape sequences */
+static const char *ansi_sequences[] = {
+ [ BIOS_KEY ( 0x47 ) ] = "[H", /* Home */
+ [ BIOS_KEY ( 0x48 ) ] = "[A", /* Up arrow */
+ [ BIOS_KEY ( 0x4b ) ] = "[D", /* Left arrow */
+ [ BIOS_KEY ( 0x4d ) ] = "[C", /* Right arrow */
+ [ BIOS_KEY ( 0x4f ) ] = "[F", /* End */
+ [ BIOS_KEY ( 0x50 ) ] = "[B", /* Down arrow */
+ [ BIOS_KEY ( 0x53 ) ] = "[3~", /* Delete */
+};
+
+/**
+ * Get ANSI escape sequence corresponding to BIOS scancode
+ *
+ * @v scancode BIOS scancode
+ * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL
+ */
+static const char * scancode_to_ansi_seq ( unsigned int scancode ) {
+ unsigned int bios_key = BIOS_KEY ( scancode );
+
+ if ( bios_key < ( sizeof ( ansi_sequences ) /
+ sizeof ( ansi_sequences[0] ) ) ) {
+ return ansi_sequences[bios_key];
+ }
+ return NULL;
+}
+
+/**
+ * Get character from BIOS console
+ *
+ * @ret character Character read from console
+ */
+static int bios_getchar ( void ) {
+ uint16_t keypress;
+ unsigned int character;
+ const char *ansi_seq;
+
+ /* If we are mid-sequence, pass out the next byte */
+ if ( ( character = *ansi_input ) ) {
+ ansi_input++;
+ return character;
+ }
+
+ /* Read character from real BIOS console */
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "int $0x16\n\t"
+ "cli\n\t" )
+ : "=a" ( keypress ) : "a" ( 0x1000 ) );
+ character = ( keypress & 0xff );
+
+ /* If it's a normal character, just return it */
+ if ( character && ( character < 0x80 ) )
+ return character;
+
+ /* Otherwise, check for a special key that we know about */
+ if ( ( ansi_seq = scancode_to_ansi_seq ( keypress >> 8 ) ) ) {
+ /* Start of escape sequence: return ESC (0x1b) */
+ ansi_input = ansi_seq;
+ return 0x1b;
+ }
+
+ return 0;
+}
+
+/**
+ * Check for character ready to read from BIOS console
+ *
+ * @ret True Character available to read
+ * @ret False No character available to read
+ */
+static int bios_iskey ( void ) {
+ unsigned int discard_a;
+ unsigned int flags;
+
+ /* If we are mid-sequence, we are always ready */
+ if ( *ansi_input )
+ return 1;
+
+ /* Otherwise check the real BIOS console */
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "int $0x16\n\t"
+ "pushfw\n\t"
+ "popw %w0\n\t"
+ "cli\n\t" )
+ : "=r" ( flags ), "=a" ( discard_a )
+ : "a" ( 0x0100 ) );
+ return ( ! ( flags & ZF ) );
+}
+
+struct console_driver bios_console __console_driver = {
+ .putchar = bios_putchar,
+ .getchar = bios_getchar,
+ .iskey = bios_iskey,
+};
diff --git a/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S
new file mode 100644
index 00000000..e9328041
--- /dev/null
+++ b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+ .text
+ .arch i386
+ .section ".text16", "ax", @progbits
+ .section ".data16", "aw", @progbits
+ .section ".text16.data", "aw", @progbits
+ .code16
+
+#define SMAP 0x534d4150
+
+/****************************************************************************
+ * Check for overlap
+ *
+ * Parameters:
+ * %edx:%eax Region start
+ * %ecx:%ebx Region end
+ * %si Pointer to hidden region descriptor
+ * Returns:
+ * CF set Region overlaps
+ * CF clear No overlap
+ ****************************************************************************
+ */
+ .section ".text16"
+check_overlap:
+ /* If start >= hidden_end, there is no overlap. */
+ testl %edx, %edx
+ jnz no_overlap
+ cmpl 4(%si), %eax
+ jae no_overlap
+ /* If end <= hidden_start, there is no overlap; equivalently,
+ * if end > hidden_start, there is overlap.
+ */
+ testl %ecx, %ecx
+ jnz overlap
+ cmpl 0(%si), %ebx
+ ja overlap
+no_overlap:
+ clc
+ ret
+overlap:
+ stc
+ ret
+ .size check_overlap, . - check_overlap
+
+/****************************************************************************
+ * Check for overflow/underflow
+ *
+ * Parameters:
+ * %edx:%eax Region start
+ * %ecx:%ebx Region end
+ * Returns:
+ * CF set start < end
+ * CF clear start >= end
+ ****************************************************************************
+ */
+ .section ".text16"
+check_overflow:
+ pushl %ecx
+ pushl %ebx
+ subl %eax, %ebx
+ sbbl %edx, %ecx
+ popl %ebx
+ popl %ecx
+ ret
+ .size check_overflow, . - check_overflow
+
+/****************************************************************************
+ * Truncate towards start of region
+ *
+ * Parameters:
+ * %edx:%eax Region start
+ * %ecx:%ebx Region end
+ * %si Pointer to hidden region descriptor
+ * Returns:
+ * %edx:%eax Modified region start
+ * %ecx:%ebx Modified region end
+ * CF set Region was truncated
+ * CF clear Region was not truncated
+ ****************************************************************************
+ */
+ .section ".text16"
+truncate_to_start:
+ /* If overlaps, set region end = hidden region start */
+ call check_overlap
+ jnc 99f
+ movl 0(%si), %ebx
+ xorl %ecx, %ecx
+ /* If region end < region start, set region end = region start */
+ call check_overflow
+ jnc 1f
+ movl %eax, %ebx
+ movl %edx, %ecx
+1: stc
+99: ret
+ .size truncate_to_start, . - truncate_to_start
+
+/****************************************************************************
+ * Truncate towards end of region
+ *
+ * Parameters:
+ * %edx:%eax Region start
+ * %ecx:%ebx Region end
+ * %si Pointer to hidden region descriptor
+ * Returns:
+ * %edx:%eax Modified region start
+ * %ecx:%ebx Modified region end
+ * CF set Region was truncated
+ * CF clear Region was not truncated
+ ****************************************************************************
+ */
+ .section ".text16"
+truncate_to_end:
+ /* If overlaps, set region start = hidden region end */
+ call check_overlap
+ jnc 99f
+ movl 4(%si), %eax
+ xorl %edx, %edx
+ /* If region start > region end, set region start = region end */
+ call check_overflow
+ jnc 1f
+ movl %ebx, %eax
+ movl %ecx, %edx
+1: stc
+99: ret
+ .size truncate_to_end, . - truncate_to_end
+
+/****************************************************************************
+ * Truncate region
+ *
+ * Parameters:
+ * %edx:%eax Region start
+ * %ecx:%ebx Region length (*not* region end)
+ * %bp truncate_to_start or truncate_to_end
+ * Returns:
+ * %edx:%eax Modified region start
+ * %ecx:%ebx Modified region length
+ * CF set Region was truncated
+ * CF clear Region was not truncated
+ ****************************************************************************
+ */
+ .section ".text16"
+truncate:
+ pushw %si
+ pushfw
+ /* Convert (start,len) to (start,end) */
+ addl %eax, %ebx
+ adcl %edx, %ecx
+ /* Hide all hidden regions, truncating as directed */
+ movw $hidden_regions, %si
+1: call *%bp
+ jnc 2f
+ popfw /* If CF was set, set stored CF in flags word on stack */
+ stc
+ pushfw
+2: addw $8, %si
+ cmpl $0, 0(%si)
+ jne 1b
+ /* Convert modified (start,end) back to (start,len) */
+ subl %eax, %ebx
+ sbbl %edx, %ecx
+ popfw
+ popw %si
+ ret
+ .size truncate, . - truncate
+
+/****************************************************************************
+ * Patch "memory above 1MB" figure
+ *
+ * Parameters:
+ * %ax Memory above 1MB, in 1kB blocks
+ * Returns:
+ * %ax Modified memory above 1M in 1kB blocks
+ * CF set Region was truncated
+ * CF clear Region was not truncated
+ ****************************************************************************
+ */
+ .section ".text16"
+patch_1m:
+ pushal
+ /* Convert to (start,len) format and call truncate */
+ movw $truncate_to_start, %bp
+ xorl %ecx, %ecx
+ movzwl %ax, %ebx
+ shll $10, %ebx
+ xorl %edx, %edx
+ movl $0x100000, %eax
+ call truncate
+ /* Convert back to "memory above 1MB" format and return via %ax */
+ pushfw
+ shrl $10, %ebx
+ popfw
+ movw %sp, %bp
+ movw %bx, 28(%bp)
+ popal
+ ret
+ .size patch_1m, . - patch_1m
+
+/****************************************************************************
+ * Patch "memory above 16MB" figure
+ *
+ * Parameters:
+ * %bx Memory above 16MB, in 64kB blocks
+ * Returns:
+ * %bx Modified memory above 16M in 64kB blocks
+ * CF set Region was truncated
+ * CF clear Region was not truncated
+ ****************************************************************************
+ */
+ .section ".text16"
+patch_16m:
+ pushal
+ /* Convert to (start,len) format and call truncate */
+ movw $truncate_to_start, %bp
+ xorl %ecx, %ecx
+ shll $16, %ebx
+ xorl %edx, %edx
+ movl $0x1000000, %eax
+ call truncate
+ /* Convert back to "memory above 16MB" format and return via %bx */
+ pushfw
+ shrl $16, %ebx
+ popfw
+ movw %sp, %bp
+ movw %bx, 16(%bp)
+ popal
+ ret
+ .size patch_16m, . - patch_16m
+
+/****************************************************************************
+ * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures
+ *
+ * Parameters:
+ * %ax Memory between 1MB and 16MB, in 1kB blocks
+ * %bx Memory above 16MB, in 64kB blocks
+ * Returns:
+ * %ax Modified memory between 1MB and 16MB, in 1kB blocks
+ * %bx Modified memory above 16MB, in 64kB blocks
+ * CF set Region was truncated
+ * CF clear Region was not truncated
+ ****************************************************************************
+ */
+ .section ".text16"
+patch_1m_16m:
+ call patch_1m
+ jc 1f
+ call patch_16m
+ ret
+1: /* 1m region was truncated; kill the 16m region */
+ xorw %bx, %bx
+ ret
+ .size patch_1m_16m, . - patch_1m_16m
+
+/****************************************************************************
+ * Patch E820 memory map entry
+ *
+ * Parameters:
+ * %es:di Pointer to E820 memory map descriptor
+ * %bp truncate_to_start or truncate_to_end
+ * Returns:
+ * %es:di Pointer to now-modified E820 memory map descriptor
+ * CF set Region was truncated
+ * CF clear Region was not truncated
+ ****************************************************************************
+ */
+ .section ".text16"
+patch_e820:
+ pushal
+ movl %es:0(%di), %eax
+ movl %es:4(%di), %edx
+ movl %es:8(%di), %ebx
+ movl %es:12(%di), %ecx
+ call truncate
+ movl %eax, %es:0(%di)
+ movl %edx, %es:4(%di)
+ movl %ebx, %es:8(%di)
+ movl %ecx, %es:12(%di)
+ popal
+ ret
+ .size patch_e820, . - patch_e820
+
+/****************************************************************************
+ * Split E820 memory map entry if necessary
+ *
+ * Parameters:
+ * As for INT 15,e820
+ * Returns:
+ * As for INT 15,e820
+ *
+ * Calls the underlying INT 15,e820 and returns a modified memory map.
+ * Regions will be split around any hidden regions.
+ ****************************************************************************
+ */
+ .section ".text16"
+split_e820:
+ pushw %si
+ pushw %bp
+ /* Caller's %bx => %si, real %ebx to %ebx, call previous handler */
+ pushfw
+ movw %bx, %si
+ testl %ebx, %ebx
+ jnz 1f
+ movl %ebx, %cs:real_ebx
+1: movl %cs:real_ebx, %ebx
+ lcall *%cs:int15_vector
+ pushfw
+ /* Edit result */
+ pushw %ds
+ pushw %cs:rm_ds
+ popw %ds
+ movw $truncate_to_start, %bp
+ incw %si
+ jns 2f
+ movw $truncate_to_end, %bp
+2: call patch_e820
+ jnc 3f
+ xorw $0x8000, %si
+3: testw %si, %si
+ js 4f
+ movl %ebx, %cs:real_ebx
+ testl %ebx, %ebx
+ jz 5f
+4: movw %si, %bx
+5: popw %ds
+ /* Restore flags returned by previous handler and return */
+ popfw
+ popw %bp
+ popw %si
+ ret
+ .size split_e820, . - split_e820
+
+ .section ".text16.data"
+real_ebx:
+ .long 0
+ .size real_ebx, . - real_ebx
+
+/****************************************************************************
+ * INT 15,e820 handler
+ ****************************************************************************
+ */
+ .section ".text16"
+int15_e820:
+ pushl %eax
+ pushl %ecx
+ pushl %edx
+ call split_e820
+ pushfw
+ /* If we've hit an error, exit immediately */
+ jc 99f
+ /* If region is non-empty, return this region */
+ pushl %eax
+ movl %es:8(%di), %eax
+ orl %es:12(%di), %eax
+ popl %eax
+ jnz 99f
+ /* Region is empty. If this is not the end of the map,
+ * skip over this region.
+ */
+ testl %ebx, %ebx
+ jz 1f
+ popfw
+ popl %edx
+ popl %ecx
+ popl %eax
+ jmp int15_e820
+1: /* Region is empty and this is the end of the map. Return
+ * with CF set to avoid placing an empty region at the end of
+ * the map.
+ */
+ popfw
+ stc
+ pushfw
+99: /* Restore flags from original INT 15,e820 call and return */
+ popfw
+ addr32 leal 12(%esp), %esp /* avoid changing flags */
+ lret $2
+ .size int15_e820, . - int15_e820
+
+/****************************************************************************
+ * INT 15,e801 handler
+ ****************************************************************************
+ */
+ .section ".text16"
+int15_e801:
+ /* Call previous handler */
+ pushfw
+ lcall *%cs:int15_vector
+ pushfw
+ /* Edit result */
+ pushw %ds
+ pushw %cs:rm_ds
+ popw %ds
+ call patch_1m_16m
+ xchgw %ax, %cx
+ xchgw %bx, %dx
+ call patch_1m_16m
+ xchgw %ax, %cx
+ xchgw %bx, %dx
+ popw %ds
+ /* Restore flags returned by previous handler and return */
+ popfw
+ lret $2
+ .size int15_e801, . - int15_e801
+
+/****************************************************************************
+ * INT 15,88 handler
+ ****************************************************************************
+ */
+ .section ".text16"
+int15_88:
+ /* Call previous handler */
+ pushfw
+ lcall *%cs:int15_vector
+ pushfw
+ /* Edit result */
+ pushw %ds
+ pushw %cs:rm_ds
+ popw %ds
+ call patch_1m
+ popw %ds
+ /* Restore flags returned by previous handler and return */
+ popfw
+ lret $2
+ .size int15_88, . - int15_88
+
+/****************************************************************************
+ * INT 15 handler
+ ****************************************************************************
+ */
+ .section ".text16"
+ .globl int15
+int15:
+ /* See if we want to intercept this call */
+ pushfw
+ cmpw $0xe820, %ax
+ jne 1f
+ cmpl $SMAP, %edx
+ jne 1f
+ popfw
+ jmp int15_e820
+1: cmpw $0xe801, %ax
+ jne 2f
+ popfw
+ jmp int15_e801
+2: cmpb $0x88, %ah
+ jne 3f
+ popfw
+ jmp int15_88
+3: popfw
+ ljmp *%cs:int15_vector
+ .size int15, . - int15
+
+ .section ".text16.data"
+ .globl int15_vector
+int15_vector:
+ .long 0
+ .size int15_vector, . - int15_vector
diff --git a/gpxe/src/arch/i386/firmware/pcbios/gateA20.c b/gpxe/src/arch/i386/firmware/pcbios/gateA20.c
new file mode 100644
index 00000000..2caac894
--- /dev/null
+++ b/gpxe/src/arch/i386/firmware/pcbios/gateA20.c
@@ -0,0 +1,170 @@
+#include <stdio.h>
+#include <realmode.h>
+#include <bios.h>
+#include <gpxe/timer.h>
+
+#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
+#define K_STATUS 0x64 /* keyboard status */
+#define K_CMD 0x64 /* keybd ctlr command (write-only) */
+
+#define K_OBUF_FUL 0x01 /* output buffer full */
+#define K_IBUF_FUL 0x02 /* input buffer full */
+
+#define KC_CMD_WIN 0xd0 /* read output port */
+#define KC_CMD_WOUT 0xd1 /* write output port */
+#define KB_SET_A20 0xdf /* enable A20,
+ enable output buffer full interrupt
+ enable data line
+ disable clock line */
+#define KB_UNSET_A20 0xdd /* enable A20,
+ enable output buffer full interrupt
+ enable data line
+ disable clock line */
+
+#define SCP_A 0x92 /* System Control Port A */
+
+enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402,
+ Query_A20_Support = 0x2403 };
+
+enum a20_methods {
+ A20_UNKNOWN = 0,
+ A20_INT15,
+ A20_KBC,
+ A20_SCPA,
+};
+
+#define A20_MAX_RETRIES 32
+#define A20_INT15_RETRIES 32
+#define A20_KBC_RETRIES (2^21)
+#define A20_SCPA_RETRIES (2^21)
+
+/**
+ * Drain keyboard controller
+ */
+static void empty_8042 ( void ) {
+ unsigned long time;
+
+ time = currticks() + TICKS_PER_SEC; /* max wait of 1 second */
+ while ( ( inb ( K_CMD ) & ( K_IBUF_FUL | K_OBUF_FUL ) ) &&
+ currticks() < time ) {
+ SLOW_DOWN_IO;
+ ( void ) inb ( K_RDWR );
+ SLOW_DOWN_IO;
+ }
+}
+
+/**
+ * Fast test to see if gate A20 is already set
+ *
+ * @v retries Number of times to retry before giving up
+ * @ret set Gate A20 is set
+ */
+static int gateA20_is_set ( int retries ) {
+ static uint32_t test_pattern = 0xdeadbeef;
+ physaddr_t test_pattern_phys = virt_to_phys ( &test_pattern );
+ physaddr_t verify_pattern_phys = ( test_pattern_phys ^ 0x100000 );
+ userptr_t verify_pattern_user = phys_to_user ( verify_pattern_phys );
+ uint32_t verify_pattern;
+
+ do {
+ /* Check for difference */
+ copy_from_user ( &verify_pattern, verify_pattern_user, 0,
+ sizeof ( verify_pattern ) );
+ if ( verify_pattern != test_pattern )
+ return 1;
+
+ /* Avoid false negatives */
+ test_pattern++;
+
+ SLOW_DOWN_IO;
+
+ /* Always retry at least once, to avoid false negatives */
+ } while ( retries-- >= 0 );
+
+ /* Pattern matched every time; gate A20 is not set */
+ return 0;
+}
+
+/*
+ * Gate A20 for high memory
+ *
+ * Note that this function gets called as part of the return path from
+ * librm's real_call, which is used to make the int15 call if librm is
+ * being used. To avoid an infinite recursion, we make gateA20_set
+ * return immediately if it is already part of the call stack.
+ */
+void gateA20_set ( void ) {
+ static char reentry_guard = 0;
+ static int a20_method = A20_UNKNOWN;
+ unsigned int discard_a;
+ unsigned int scp_a;
+ int retries = 0;
+
+ /* Avoid potential infinite recursion */
+ if ( reentry_guard )
+ return;
+ reentry_guard = 1;
+
+ /* Fast check to see if gate A20 is already enabled */
+ if ( gateA20_is_set ( 0 ) )
+ goto out;
+
+ for ( ; retries < A20_MAX_RETRIES ; retries++ ) {
+ switch ( a20_method ) {
+ case A20_UNKNOWN:
+ case A20_INT15:
+ /* Try INT 15 method */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x15" )
+ : "=a" ( discard_a )
+ : "a" ( Enable_A20 ) );
+ if ( gateA20_is_set ( A20_INT15_RETRIES ) ) {
+ DBG ( "Enabled gate A20 using BIOS\n" );
+ a20_method = A20_INT15;
+ goto out;
+ }
+ /* fall through */
+ case A20_KBC:
+ /* Try keyboard controller method */
+ empty_8042();
+ outb ( KC_CMD_WOUT, K_CMD );
+ empty_8042();
+ outb ( KB_SET_A20, K_RDWR );
+ empty_8042();
+ if ( gateA20_is_set ( A20_KBC_RETRIES ) ) {
+ DBG ( "Enabled gate A20 using "
+ "keyboard controller\n" );
+ a20_method = A20_KBC;
+ goto out;
+ }
+ /* fall through */
+ case A20_SCPA:
+ /* Try "Fast gate A20" method */
+ scp_a = inb ( SCP_A );
+ scp_a &= ~0x01; /* Avoid triggering a reset */
+ scp_a |= 0x02; /* Enable A20 */
+ SLOW_DOWN_IO;
+ outb ( scp_a, SCP_A );
+ SLOW_DOWN_IO;
+ if ( gateA20_is_set ( A20_SCPA_RETRIES ) ) {
+ DBG ( "Enabled gate A20 using "
+ "Fast Gate A20\n" );
+ a20_method = A20_SCPA;
+ goto out;
+ }
+ }
+ }
+
+ /* Better to die now than corrupt memory later */
+ printf ( "FATAL: Gate A20 stuck\n" );
+ while ( 1 ) {}
+
+ out:
+ if ( retries )
+ DBG ( "%d attempts were required to enable A20\n",
+ ( retries + 1 ) );
+ reentry_guard = 0;
+}
+
+void gateA20_unset ( void ) {
+ /* Not currently implemented */
+}
diff --git a/gpxe/src/arch/i386/firmware/pcbios/hidemem.c b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c
new file mode 100644
index 00000000..eba94007
--- /dev/null
+++ b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c
@@ -0,0 +1,156 @@
+/* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <realmode.h>
+#include <biosint.h>
+#include <basemem.h>
+#include <gpxe/init.h>
+#include <gpxe/hidemem.h>
+
+/** Alignment for hidden memory regions */
+#define ALIGN_HIDDEN 4096 /* 4kB page alignment should be enough */
+
+/**
+ * A hidden region of Etherboot
+ *
+ * This represents a region that will be edited out of the system's
+ * memory map.
+ *
+ * This structure is accessed by assembly code, so must not be
+ * changed.
+ */
+struct hidden_region {
+ /* Physical start address */
+ physaddr_t start;
+ /* Physical end address */
+ physaddr_t end;
+};
+
+/**
+ * List of hidden regions
+ *
+ * Must be terminated by a zero entry.
+ */
+struct hidden_region __data16_array ( hidden_regions, [] ) = {
+ [TEXT] = { 0, 0 },
+ [BASEMEM] = { ( 640 * 1024 ), ( 640 * 1024 ) },
+ [EXTMEM] = { 0, 0 },
+ { 0, 0, } /* Terminator */
+};
+#define hidden_regions __use_data16 ( hidden_regions )
+
+/** Assembly routine in e820mangler.S */
+extern void int15();
+
+/** Vector for storing original INT 15 handler */
+extern struct segoff __text16 ( int15_vector );
+#define int15_vector __use_text16 ( int15_vector )
+
+/**
+ * Hide region of memory from system memory map
+ *
+ * @v start Start of region
+ * @v end End of region
+ */
+void hide_region ( unsigned int region_id, physaddr_t start, physaddr_t end ) {
+ struct hidden_region *region = &hidden_regions[region_id];
+
+ /* Some operating systems get a nasty shock if a region of the
+ * E820 map seems to start on a non-page boundary. Make life
+ * safer by rounding out our edited region.
+ */
+ region->start = ( start & ~( ALIGN_HIDDEN - 1 ) );
+ region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) );
+
+ DBG ( "Hiding region %d [%lx,%lx)\n",
+ region_id, region->start, region->end );
+}
+
+/**
+ * Hide Etherboot text
+ *
+ */
+static void hide_text ( void ) {
+
+ /* The linker defines these symbols for us */
+ extern char _text[];
+ extern char _end[];
+
+ hide_region ( TEXT, virt_to_phys ( _text ), virt_to_phys ( _end ) );
+}
+
+/**
+ * Hide used base memory
+ *
+ */
+void hide_basemem ( void ) {
+ /* Hide from the top of free base memory to 640kB. Don't use
+ * hide_region(), because we don't want this rounded to the
+ * nearest page boundary.
+ */
+ hidden_regions[BASEMEM].start = ( get_fbms() * 1024 );
+}
+
+/**
+ * Hide Etherboot
+ *
+ * Installs an INT 15 handler to edit Etherboot out of the memory map
+ * returned by the BIOS.
+ */
+static void hide_etherboot ( void ) {
+
+ /* Initialise the hidden regions */
+ hide_text();
+ hide_basemem();
+
+ /* Hook INT 15 */
+ hook_bios_interrupt ( 0x15, ( unsigned int ) int15,
+ &int15_vector );
+}
+
+/**
+ * Unhide Etherboot
+ *
+ * Uninstalls the INT 15 handler installed by hide_etherboot(), if
+ * possible.
+ */
+static void unhide_etherboot ( void ) {
+
+ /* If we have more than one hooked interrupt at this point, it
+ * means that some other vector is still hooked, in which case
+ * we can't safely unhook INT 15 because we need to keep our
+ * memory protected. (We expect there to be at least one
+ * hooked interrupt, because INT 15 itself is still hooked).
+ */
+ if ( hooked_bios_interrupts > 1 ) {
+ DBG ( "Cannot unhide: %d interrupt vectors still hooked\n",
+ hooked_bios_interrupts );
+ return;
+ }
+
+ /* Try to unhook INT 15. If it fails, then just leave it
+ * hooked; it takes care of protecting itself. :)
+ */
+ unhook_bios_interrupt ( 0x15, ( unsigned int ) int15,
+ &int15_vector );
+}
+
+/** Hide Etherboot startup function */
+struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = {
+ .startup = hide_etherboot,
+ .shutdown = unhide_etherboot,
+};
diff --git a/gpxe/src/arch/i386/firmware/pcbios/memmap.c b/gpxe/src/arch/i386/firmware/pcbios/memmap.c
new file mode 100644
index 00000000..b6a8ca3c
--- /dev/null
+++ b/gpxe/src/arch/i386/firmware/pcbios/memmap.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <realmode.h>
+#include <bios.h>
+#include <memsizes.h>
+#include <gpxe/memmap.h>
+
+/**
+ * @file
+ *
+ * Memory mapping
+ *
+ */
+
+/** Magic value for INT 15,e820 calls */
+#define SMAP ( 0x534d4150 )
+
+/** An INT 15,e820 memory map entry */
+struct e820_entry {
+ /** Start of region */
+ uint64_t start;
+ /** Length of region */
+ uint64_t len;
+ /** Type of region */
+ uint32_t type;
+} __attribute__ (( packed ));
+
+#define E820_TYPE_RAM 1 /**< Normal memory */
+#define E820_TYPE_RESERVED 2 /**< Reserved and unavailable */
+#define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */
+#define E820_TYPE_NVS 4 /**< ACPI NVS memory */
+
+/** Buffer for INT 15,e820 calls */
+static struct e820_entry __bss16 ( e820buf );
+#define e820buf __use_data16 ( e820buf )
+
+/**
+ * Get size of extended memory via INT 15,e801
+ *
+ * @ret extmem Extended memory size, in kB, or 0
+ */
+static unsigned int extmemsize_e801 ( void ) {
+ uint16_t extmem_1m_to_16m_k, extmem_16m_plus_64k;
+ uint16_t confmem_1m_to_16m_k, confmem_16m_plus_64k;
+ unsigned int flags;
+ unsigned int extmem;
+
+ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+ "int $0x15\n\t"
+ "pushfw\n\t"
+ "popw %w0\n\t" )
+ : "=r" ( flags ),
+ "=a" ( extmem_1m_to_16m_k ),
+ "=b" ( extmem_16m_plus_64k ),
+ "=c" ( confmem_1m_to_16m_k ),
+ "=d" ( confmem_16m_plus_64k )
+ : "a" ( 0xe801 ) );
+
+ if ( flags & CF ) {
+ DBG ( "INT 15,e801 failed with CF set\n" );
+ return 0;
+ }
+
+ if ( ! ( extmem_1m_to_16m_k | extmem_16m_plus_64k ) ) {
+ DBG ( "INT 15,e801 extmem=0, using confmem\n" );
+ extmem_1m_to_16m_k = confmem_1m_to_16m_k;
+ extmem_16m_plus_64k = confmem_16m_plus_64k;
+ }
+
+ extmem = ( extmem_1m_to_16m_k + ( extmem_16m_plus_64k * 64 ) );
+ DBG ( "INT 15,e801 extended memory size %d+64*%d=%d kB\n",
+ extmem_1m_to_16m_k, extmem_16m_plus_64k, extmem );
+ return extmem;
+}
+
+/**
+ * Get size of extended memory via INT 15,88
+ *
+ * @ret extmem Extended memory size, in kB
+ */
+static unsigned int extmemsize_88 ( void ) {
+ uint16_t extmem;
+
+ /* Ignore CF; it is not reliable for this call */
+ __asm__ __volatile__ ( REAL_CODE ( "int $0x15" )
+ : "=a" ( extmem ) : "a" ( 0x8800 ) );
+
+ DBG ( "INT 15,88 extended memory size %d kB\n", extmem );
+ return extmem;
+}
+
+/**
+ * Get size of extended memory
+ *
+ * @ret extmem Extended memory size, in kB
+ *
+ * Note that this is only an approximation; for an accurate picture,
+ * use the E820 memory map obtained via get_memmap();
+ */
+unsigned int extmemsize ( void ) {
+ unsigned int extmem;
+
+ /* Try INT 15,e801 first, then fall back to INT 15,88 */
+ extmem = extmemsize_e801();
+ if ( ! extmem )
+ extmem = extmemsize_88();
+ return extmem;
+}
+
+/**
+ * Get e820 memory map
+ *
+ * @v memmap Memory map to fill in
+ * @ret rc Return status code
+ */
+static int meme820 ( struct memory_map *memmap ) {
+ struct memory_region *region = memmap->regions;
+ uint32_t next = 0;
+ uint32_t smap;
+ unsigned int flags;
+ unsigned int discard_c, discard_d, discard_D;
+
+ do {
+ __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+ "int $0x15\n\t"
+ "pushfw\n\t"
+ "popw %w0\n\t" )
+ : "=r" ( flags ), "=a" ( smap ),
+ "=b" ( next ), "=D" ( discard_D ),
+ "=c" ( discard_c ), "=d" ( discard_d )
+ : "a" ( 0xe820 ), "b" ( next ),
+ "D" ( &__from_data16 ( e820buf ) ),
+ "c" ( sizeof ( e820buf ) ),
+ "d" ( SMAP )
+ : "memory" );
+
+ if ( smap != SMAP ) {
+ DBG ( "INT 15,e820 failed SMAP signature check\n" );
+ return -ENOTSUP;
+ }
+
+ if ( flags & CF ) {
+ DBG ( "INT 15,e820 terminated on CF set\n" );
+ break;
+ }
+
+ DBG ( "INT 15,e820 region [%llx,%llx) type %d\n",
+ e820buf.start, ( e820buf.start + e820buf.len ),
+ ( int ) e820buf.type );
+ if ( e820buf.type != E820_TYPE_RAM )
+ continue;
+
+ region->start = e820buf.start;
+ region->end = e820buf.start + e820buf.len;
+ region++;
+ memmap->count++;
+
+ if ( memmap->count >= ( sizeof ( memmap->regions ) /
+ sizeof ( memmap->regions[0] ) ) ) {
+ DBG ( "INT 15,e820 too many regions returned\n" );
+ /* Not a fatal error; what we've got so far at
+ * least represents valid regions of memory,
+ * even if we couldn't get them all.
+ */
+ break;
+ }
+ } while ( next != 0 );
+
+ return 0;
+}
+
+/**
+ * Get memory map
+ *
+ * @v memmap Memory map to fill in
+ */
+void get_memmap ( struct memory_map *memmap ) {
+ unsigned int basemem, extmem;
+ int rc;
+
+ DBG ( "Fetching system memory map\n" );
+
+ /* Clear memory map */
+ memset ( memmap, 0, sizeof ( *memmap ) );
+
+ /* Get base and extended memory sizes */
+ basemem = basememsize();
+ DBG ( "FBMS base memory size %d kB\n", basemem );
+ extmem = extmemsize();
+
+ /* Try INT 15,e820 first */
+ if ( ( rc = meme820 ( memmap ) ) == 0 ) {
+ DBG ( "Obtained system memory map via INT 15,e820\n" );
+ return;
+ }
+
+ /* Fall back to constructing a map from basemem and extmem sizes */
+ DBG ( "INT 15,e820 failed; constructing map\n" );
+ memmap->regions[0].end = ( basemem * 1024 );
+ memmap->regions[1].start = 0x100000;
+ memmap->regions[1].end = 0x100000 + ( extmem * 1024 );
+ memmap->count = 2;
+}
diff --git a/gpxe/src/arch/i386/firmware/pcbios/pnpbios.c b/gpxe/src/arch/i386/firmware/pcbios/pnpbios.c
new file mode 100644
index 00000000..420d2ae8
--- /dev/null
+++ b/gpxe/src/arch/i386/firmware/pcbios/pnpbios.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <realmode.h>
+#include <pnpbios.h>
+
+/** @file
+ *
+ * PnP BIOS
+ *
+ */
+
+/** PnP BIOS structure */
+struct pnp_bios {
+ /** Signature
+ *
+ * Must be equal to @c PNP_BIOS_SIGNATURE
+ */
+ uint32_t signature;
+ /** Version as BCD (e.g. 1.0 is 0x10) */
+ uint8_t version;
+ /** Length of this structure */
+ uint8_t length;
+ /** System capabilities */
+ uint16_t control;
+ /** Checksum */
+ uint8_t checksum;
+} __attribute__ (( packed ));
+
+/** Signature for a PnP BIOS structure */
+#define PNP_BIOS_SIGNATURE \
+ ( ( '$' << 0 ) + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
+
+/**
+ * Test address for PnP BIOS structure
+ *
+ * @v offset Offset within BIOS segment to test
+ * @ret rc Return status code
+ */
+static int is_pnp_bios ( unsigned int offset ) {
+ union {
+ struct pnp_bios pnp_bios;
+ uint8_t bytes[256]; /* 256 is maximum length possible */
+ } u;
+ size_t len;
+ unsigned int i;
+ uint8_t sum = 0;
+
+ /* Read start of header and verify signature */
+ copy_from_real ( &u.pnp_bios, BIOS_SEG, offset, sizeof ( u.pnp_bios ));
+ if ( u.pnp_bios.signature != PNP_BIOS_SIGNATURE )
+ return -EINVAL;
+
+ /* Read whole header and verify checksum */
+ len = u.pnp_bios.length;
+ copy_from_real ( &u.bytes, BIOS_SEG, offset, len );
+ for ( i = 0 ; i < len ; i++ ) {
+ sum += u.bytes[i];
+ }
+ if ( sum != 0 )
+ return -EINVAL;
+
+ DBG ( "Found PnP BIOS at %04x:%04x\n", BIOS_SEG, offset );
+
+ return 0;
+}
+
+/**
+ * Locate Plug-and-Play BIOS
+ *
+ * @ret pnp_offset Offset of PnP BIOS structure within BIOS segment
+ *
+ * The PnP BIOS structure will be at BIOS_SEG:pnp_offset. If no PnP
+ * BIOS is found, -1 is returned.
+ */
+int find_pnp_bios ( void ) {
+ static int pnp_offset = 0;
+
+ if ( pnp_offset )
+ return pnp_offset;
+
+ for ( pnp_offset = 0 ; pnp_offset < 0x10000 ; pnp_offset += 0x10 ) {
+ if ( is_pnp_bios ( pnp_offset ) == 0 )
+ return pnp_offset;
+ }
+
+ pnp_offset = -1;
+ return pnp_offset;
+}
diff --git a/gpxe/src/arch/i386/firmware/pcbios/smbios.c b/gpxe/src/arch/i386/firmware/pcbios/smbios.c
new file mode 100644
index 00000000..4d710d68
--- /dev/null
+++ b/gpxe/src/arch/i386/firmware/pcbios/smbios.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/uuid.h>
+#include <realmode.h>
+#include <pnpbios.h>
+#include <smbios.h>
+
+/** @file
+ *
+ * System Management BIOS
+ *
+ */
+
+/** Signature for SMBIOS entry point */
+#define SMBIOS_SIGNATURE \
+ ( ( '_' << 0 ) + ( 'S' << 8 ) + ( 'M' << 16 ) + ( '_' << 24 ) )
+
+/**
+ * SMBIOS entry point
+ *
+ * This is the single table which describes the list of SMBIOS
+ * structures. It is located by scanning through the BIOS segment.
+ */
+struct smbios_entry {
+ /** Signature
+ *
+ * Must be equal to SMBIOS_SIGNATURE
+ */
+ uint32_t signature;
+ /** Checksum */
+ uint8_t checksum;
+ /** Length */
+ uint8_t length;
+ /** Major version */
+ uint8_t major;
+ /** Minor version */
+ uint8_t minor;
+ /** Maximum structure size */
+ uint16_t max;
+ /** Entry point revision */
+ uint8_t revision;
+ /** Formatted area */
+ uint8_t formatted[5];
+ /** DMI Signature */
+ uint8_t dmi_signature[5];
+ /** DMI checksum */
+ uint8_t dmi_checksum;
+ /** Structure table length */
+ uint16_t smbios_length;
+ /** Structure table address */
+ physaddr_t smbios_address;
+ /** Number of SMBIOS structures */
+ uint16_t smbios_count;
+ /** BCD revision */
+ uint8_t bcd_revision;
+} __attribute__ (( packed ));
+
+/**
+ * SMBIOS entry point descriptor
+ *
+ * This contains the information from the SMBIOS entry point that we
+ * care about.
+ */
+struct smbios {
+ /** Start of SMBIOS structures */
+ userptr_t address;
+ /** Length of SMBIOS structures */
+ size_t length;
+ /** Number of SMBIOS structures */
+ unsigned int count;
+};
+
+/**
+ * SMBIOS strings descriptor
+ *
+ * This is returned as part of the search for an SMBIOS structure, and
+ * contains the information needed for extracting the strings within
+ * the "unformatted" portion of the structure.
+ */
+struct smbios_strings {
+ /** Start of strings data */
+ userptr_t data;
+ /** Length of strings data */
+ size_t length;
+};
+
+/**
+ * Find SMBIOS
+ *
+ * @ret smbios SMBIOS entry point descriptor, or NULL if not found
+ */
+static struct smbios * find_smbios ( void ) {
+ static struct smbios smbios = {
+ .address = UNULL,
+ };
+ union {
+ struct smbios_entry entry;
+ uint8_t bytes[256]; /* 256 is maximum length possible */
+ } u;
+ unsigned int offset;
+ size_t len;
+ unsigned int i;
+ uint8_t sum;
+
+ /* Return cached result if available */
+ if ( smbios.address != UNULL )
+ return &smbios;
+
+ /* Try to find SMBIOS */
+ for ( offset = 0 ; offset < 0x10000 ; offset += 0x10 ) {
+
+ /* Read start of header and verify signature */
+ copy_from_real ( &u.entry, BIOS_SEG, offset,
+ sizeof ( u.entry ));
+ if ( u.entry.signature != SMBIOS_SIGNATURE )
+ continue;
+
+ /* Read whole header and verify checksum */
+ len = u.entry.length;
+ copy_from_real ( &u.bytes, BIOS_SEG, offset, len );
+ for ( i = 0 , sum = 0 ; i < len ; i++ ) {
+ sum += u.bytes[i];
+ }
+ if ( sum != 0 ) {
+ DBG ( "SMBIOS at %04x:%04x has bad checksum %02x\n",
+ BIOS_SEG, offset, sum );
+ continue;
+ }
+
+ /* Fill result structure */
+ DBG ( "Found SMBIOS entry point at %04x:%04x\n",
+ BIOS_SEG, offset );
+ smbios.address = phys_to_user ( u.entry.smbios_address );
+ smbios.length = u.entry.smbios_length;
+ smbios.count = u.entry.smbios_count;
+ return &smbios;
+ }
+
+ DBG ( "No SMBIOS found\n" );
+ return NULL;
+}
+
+/**
+ * Find SMBIOS strings terminator
+ *
+ * @v smbios SMBIOS entry point descriptor
+ * @v offset Offset to start of strings
+ * @ret offset Offset to strings terminator, or 0 if not found
+ */
+static size_t find_strings_terminator ( struct smbios *smbios,
+ size_t offset ) {
+ size_t max_offset = ( smbios->length - 2 );
+ uint16_t nulnul;
+
+ for ( ; offset <= max_offset ; offset++ ) {
+ copy_from_user ( &nulnul, smbios->address, offset, 2 );
+ if ( nulnul == 0 )
+ return ( offset + 1 );
+ }
+ return 0;
+}
+
+/**
+ * Find specific structure type within SMBIOS
+ *
+ * @v type Structure type to search for
+ * @v structure Buffer to fill in with structure
+ * @v length Length of buffer
+ * @v strings Strings descriptor to fill in, or NULL
+ * @ret rc Return status code
+ */
+int find_smbios_structure ( unsigned int type, void *structure,
+ size_t length, struct smbios_strings *strings ) {
+ struct smbios *smbios;
+ struct smbios_header header;
+ struct smbios_strings temp_strings;
+ unsigned int count = 0;
+ size_t offset = 0;
+ size_t strings_offset;
+ size_t terminator_offset;
+
+ /* Locate SMBIOS entry point */
+ if ( ! ( smbios = find_smbios() ) )
+ return -ENOENT;
+
+ /* Ensure that we have a usable strings descriptor buffer */
+ if ( ! strings )
+ strings = &temp_strings;
+
+ /* Scan through list of structures */
+ while ( ( ( offset + sizeof ( header ) ) < smbios->length ) &&
+ ( count < smbios->count ) ) {
+
+ /* Read next SMBIOS structure header */
+ copy_from_user ( &header, smbios->address, offset,
+ sizeof ( header ) );
+
+ /* Determine start and extent of strings block */
+ strings_offset = ( offset + header.length );
+ if ( strings_offset > smbios->length ) {
+ DBG ( "SMBIOS structure at offset %zx with length "
+ "%x extends beyond SMBIOS\n", offset,
+ header.length );
+ return -ENOENT;
+ }
+ terminator_offset =
+ find_strings_terminator ( smbios, strings_offset );
+ if ( ! terminator_offset ) {
+ DBG ( "SMBIOS structure at offset %zx has "
+ "unterminated strings section\n", offset );
+ return -ENOENT;
+ }
+ strings->data = userptr_add ( smbios->address,
+ strings_offset );
+ strings->length = ( terminator_offset - strings_offset );
+
+ DBG ( "SMBIOS structure at offset %zx has type %d, "
+ "length %x, strings length %zx\n",
+ offset, header.type, header.length, strings->length );
+
+ /* If this is the structure we want, return */
+ if ( header.type == type ) {
+ if ( length > header.length )
+ length = header.length;
+ copy_from_user ( structure, smbios->address,
+ offset, length );
+ return 0;
+ }
+
+ /* Move to next SMBIOS structure */
+ offset = ( terminator_offset + 1 );
+ count++;
+ }
+
+ DBG ( "SMBIOS structure type %d not found\n", type );
+ return -ENOENT;
+}
+
+/**
+ * Find indexed string within SMBIOS structure
+ *
+ * @v strings SMBIOS strings descriptor
+ * @v index String index
+ * @v buffer Buffer for string
+ * @v length Length of string buffer
+ * @ret rc Return status code
+ */
+int find_smbios_string ( struct smbios_strings *strings, unsigned int index,
+ char *buffer, size_t length ) {
+ size_t offset = 0;
+ size_t string_len;
+
+ /* Zero buffer. This ensures that a valid NUL terminator is
+ * always present (unless length==0).
+ */
+ memset ( buffer, 0, length );
+
+ /* String numbers start at 1 (0 is used to indicate "no string") */
+ if ( ! index )
+ return 0;
+
+ while ( offset < strings->length ) {
+ /* Get string length. This is known safe, since the
+ * smbios_strings struct is constructed so as to
+ * always end on a string boundary.
+ */
+ string_len = strlen_user ( strings->data, offset );
+ if ( --index == 0 ) {
+ /* Copy string, truncating as necessary. */
+ if ( string_len >= length )
+ string_len = ( length - 1 );
+ copy_from_user ( buffer, strings->data,
+ offset, string_len );
+ return 0;
+ }
+ offset += ( string_len + 1 );
+ }
+
+ DBG ( "SMBIOS string index %d not found\n", index );
+ return -ENOENT;
+}
+
+/**
+ * Get UUID from SMBIOS
+ *
+ * @v uuid UUID to fill in
+ * @ret rc Return status code
+ */
+int smbios_get_uuid ( union uuid *uuid ) {
+ struct smbios_system_information sysinfo;
+ int rc;
+
+ if ( ( rc = find_smbios_structure ( SMBIOS_TYPE_SYSTEM_INFORMATION,
+ &sysinfo, sizeof ( sysinfo ),
+ NULL ) ) != 0 )
+ return rc;
+
+ memcpy ( uuid, sysinfo.uuid, sizeof ( *uuid ) );
+ DBG ( "SMBIOS found UUID %s\n", uuid_ntoa ( uuid ) );
+
+ return 0;
+}
diff --git a/gpxe/src/arch/i386/image/bootsector.c b/gpxe/src/arch/i386/image/bootsector.c
new file mode 100644
index 00000000..0f297a26
--- /dev/null
+++ b/gpxe/src/arch/i386/image/bootsector.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * x86 bootsector image format
+ *
+ */
+
+#include <errno.h>
+#include <realmode.h>
+#include <biosint.h>
+#include <bootsector.h>
+
+/** Vector for storing original INT 18 handler
+ *
+ * We do not chain to this vector, so there is no need to place it in
+ * .text16.
+ */
+static struct segoff int18_vector;
+
+/** Vector for storing original INT 19 handler
+ *
+ * We do not chain to this vector, so there is no need to place it in
+ * .text16.
+ */
+static struct segoff int19_vector;
+
+/** Restart point for INT 18 or 19 */
+extern void bootsector_exec_fail ( void );
+
+/**
+ * Jump to preloaded bootsector
+ *
+ * @v segment Real-mode segment
+ * @v offset Real-mode offset
+ * @v drive Drive number to pass to boot sector
+ * @ret rc Return status code
+ */
+int call_bootsector ( unsigned int segment, unsigned int offset,
+ unsigned int drive ) {
+ int discard_b, discard_D, discard_d;
+
+ DBG ( "Booting from boot sector at %04x:%04x\n", segment, offset );
+
+ /* Hook INTs 18 and 19 to capture failure paths */
+ hook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail,
+ &int18_vector );
+ hook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail,
+ &int19_vector );
+
+ /* Boot the loaded sector
+ *
+ * We assume that the boot sector may completely destroy our
+ * real-mode stack, so we preserve everything we need in
+ * static storage.
+ */
+ __asm__ __volatile__ ( REAL_CODE ( /* Save return address off-stack */
+ "popw %%cs:saved_retaddr\n\t"
+ /* Save stack pointer */
+ "movw %%ss, %%ax\n\t"
+ "movw %%ax, %%cs:saved_ss\n\t"
+ "movw %%sp, %%cs:saved_sp\n\t"
+ /* Jump to boot sector */
+ "pushw %%bx\n\t"
+ "pushw %%di\n\t"
+ "sti\n\t"
+ "lret\n\t"
+ /* Preserved variables */
+ "\nsaved_ss: .word 0\n\t"
+ "\nsaved_sp: .word 0\n\t"
+ "\nsaved_retaddr: .word 0\n\t"
+ /* Boot failure return point */
+ "\nbootsector_exec_fail:\n\t"
+ /* Restore stack pointer */
+ "movw %%cs:saved_ss, %%ax\n\t"
+ "movw %%ax, %%ss\n\t"
+ "movw %%cs:saved_sp, %%sp\n\t"
+ /* Return via saved address */
+ "jmp *%%cs:saved_retaddr\n\t" )
+ : "=b" ( discard_b ), "=D" ( discard_D ),
+ "=d" ( discard_d )
+ : "b" ( segment ), "D" ( offset ),
+ "d" ( drive )
+ : "eax", "ecx", "esi", "ebp" );
+
+ DBG ( "Booted disk returned via INT 18 or 19\n" );
+
+ /* Unhook INTs 18 and 19 */
+ unhook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail,
+ &int18_vector );
+ unhook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail,
+ &int19_vector );
+
+ return -ECANCELED;
+}
diff --git a/gpxe/src/arch/i386/image/bzimage.c b/gpxe/src/arch/i386/image/bzimage.c
new file mode 100644
index 00000000..ed9d286d
--- /dev/null
+++ b/gpxe/src/arch/i386/image/bzimage.c
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Linux bzImage image format
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <bzimage.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/image.h>
+#include <gpxe/segment.h>
+#include <gpxe/init.h>
+#include <gpxe/initrd.h>
+#include <gpxe/cpio.h>
+#include <gpxe/features.h>
+
+FEATURE ( FEATURE_IMAGE, "bzImage", DHCP_EB_FEATURE_BZIMAGE, 1 );
+
+struct image_type bzimage_image_type __image_type ( PROBE_NORMAL );
+
+/**
+ * bzImage load context
+ */
+struct bzimage_load_context {
+ /** Real-mode kernel portion load segment address */
+ unsigned int rm_kernel_seg;
+ /** Real-mode kernel portion load address */
+ userptr_t rm_kernel;
+ /** Real-mode kernel portion file size */
+ size_t rm_filesz;
+ /** Real-mode heap top (offset from rm_kernel) */
+ size_t rm_heap;
+ /** Command line (offset from rm_kernel) */
+ size_t rm_cmdline;
+ /** Real-mode kernel portion total memory size */
+ size_t rm_memsz;
+ /** Non-real-mode kernel portion load address */
+ userptr_t pm_kernel;
+ /** Non-real-mode kernel portion file and memory size */
+ size_t pm_sz;
+};
+
+/**
+ * bzImage execution context
+ */
+struct bzimage_exec_context {
+ /** Real-mode kernel portion load segment address */
+ unsigned int rm_kernel_seg;
+ /** Real-mode kernel portion load address */
+ userptr_t rm_kernel;
+ /** Real-mode heap top (offset from rm_kernel) */
+ size_t rm_heap;
+ /** Command line (offset from rm_kernel) */
+ size_t rm_cmdline;
+ /** Video mode */
+ unsigned int vid_mode;
+ /** Memory limit */
+ uint64_t mem_limit;
+ /** Initrd address */
+ physaddr_t ramdisk_image;
+ /** Initrd size */
+ physaddr_t ramdisk_size;
+};
+
+/**
+ * Parse kernel command line for bootloader parameters
+ *
+ * @v image bzImage file
+ * @v exec_ctx Execution context
+ * @v cmdline Kernel command line
+ * @ret rc Return status code
+ */
+static int bzimage_parse_cmdline ( struct image *image,
+ struct bzimage_exec_context *exec_ctx,
+ const char *cmdline ) {
+ char *vga;
+ char *mem;
+
+ /* Look for "vga=" */
+ if ( ( vga = strstr ( cmdline, "vga=" ) ) ) {
+ vga += 4;
+ if ( strcmp ( vga, "normal" ) == 0 ) {
+ exec_ctx->vid_mode = BZI_VID_MODE_NORMAL;
+ } else if ( strcmp ( vga, "ext" ) == 0 ) {
+ exec_ctx->vid_mode = BZI_VID_MODE_EXT;
+ } else if ( strcmp ( vga, "ask" ) == 0 ) {
+ exec_ctx->vid_mode = BZI_VID_MODE_ASK;
+ } else {
+ exec_ctx->vid_mode = strtoul ( vga, &vga, 0 );
+ if ( *vga && ( *vga != ' ' ) ) {
+ DBGC ( image, "bzImage %p strange \"vga=\""
+ "terminator '%c'\n", image, *vga );
+ }
+ }
+ }
+
+ /* Look for "mem=" */
+ if ( ( mem = strstr ( cmdline, "mem=" ) ) ) {
+ mem += 4;
+ exec_ctx->mem_limit = strtoul ( mem, &mem, 0 );
+ switch ( *mem ) {
+ case 'G':
+ case 'g':
+ exec_ctx->mem_limit <<= 10;
+ case 'M':
+ case 'm':
+ exec_ctx->mem_limit <<= 10;
+ case 'K':
+ case 'k':
+ exec_ctx->mem_limit <<= 10;
+ break;
+ case '\0':
+ case ' ':
+ break;
+ default:
+ DBGC ( image, "bzImage %p strange \"mem=\" "
+ "terminator '%c'\n", image, *mem );
+ break;
+ }
+ exec_ctx->mem_limit -= 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Set command line
+ *
+ * @v image bzImage image
+ * @v exec_ctx Execution context
+ * @v cmdline Kernel command line
+ * @ret rc Return status code
+ */
+static int bzimage_set_cmdline ( struct image *image,
+ struct bzimage_exec_context *exec_ctx,
+ const char *cmdline ) {
+ size_t cmdline_len;
+
+ /* Copy command line down to real-mode portion */
+ cmdline_len = ( strlen ( cmdline ) + 1 );
+ if ( cmdline_len > BZI_CMDLINE_SIZE )
+ cmdline_len = BZI_CMDLINE_SIZE;
+ copy_to_user ( exec_ctx->rm_kernel, exec_ctx->rm_cmdline,
+ cmdline, cmdline_len );
+ DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline );
+
+ return 0;
+}
+
+/**
+ * Load initrd
+ *
+ * @v image bzImage image
+ * @v initrd initrd image
+ * @v address Address at which to load, or UNULL
+ * @ret len Length of loaded image, rounded up to 4 bytes
+ */
+static size_t bzimage_load_initrd ( struct image *image,
+ struct image *initrd,
+ userptr_t address ) {
+ char *filename = initrd->cmdline;
+ struct cpio_header cpio;
+ size_t offset = 0;
+
+ /* Ignore images which aren't initrds */
+ if ( initrd->type != &initrd_image_type )
+ return 0;
+
+ /* Create cpio header before non-prebuilt images */
+ if ( filename && filename[0] ) {
+ size_t name_len = ( strlen ( filename ) + 1 );
+
+ DBGC ( image, "bzImage %p inserting initrd %p as %s\n",
+ image, initrd, filename );
+ memset ( &cpio, '0', sizeof ( cpio ) );
+ memcpy ( cpio.c_magic, CPIO_MAGIC, sizeof ( cpio.c_magic ) );
+ cpio_set_field ( cpio.c_mode, 0100644 );
+ cpio_set_field ( cpio.c_nlink, 1 );
+ cpio_set_field ( cpio.c_filesize, initrd->len );
+ cpio_set_field ( cpio.c_namesize, name_len );
+ if ( address ) {
+ copy_to_user ( address, offset, &cpio,
+ sizeof ( cpio ) );
+ }
+ offset += sizeof ( cpio );
+ if ( address ) {
+ copy_to_user ( address, offset, filename,
+ name_len );
+ }
+ offset += name_len;
+ offset = ( ( offset + 0x03 ) & ~0x03 );
+ }
+
+ /* Copy in initrd image body */
+ if ( address ) {
+ DBGC ( image, "bzImage %p has initrd %p at [%lx,%lx)\n",
+ image, initrd, address, ( address + offset ) );
+ memcpy_user ( address, offset, initrd->data, 0,
+ initrd->len );
+ }
+ offset += initrd->len;
+
+ offset = ( ( offset + 0x03 ) & ~0x03 );
+ return offset;
+}
+
+/**
+ * Load initrds, if any
+ *
+ * @v image bzImage image
+ * @v exec_ctx Execution context
+ * @ret rc Return status code
+ */
+static int bzimage_load_initrds ( struct image *image,
+ struct bzimage_exec_context *exec_ctx ) {
+ struct image *initrd;
+ size_t total_len = 0;
+ physaddr_t address;
+ int rc;
+
+ /* Add up length of all initrd images */
+ for_each_image ( initrd ) {
+ total_len += bzimage_load_initrd ( image, initrd, UNULL );
+ }
+
+ /* Give up if no initrd images found */
+ if ( ! total_len )
+ return 0;
+
+ /* Find a suitable start address. Try 1MB boundaries,
+ * starting from the downloaded kernel image itself and
+ * working downwards until we hit an available region.
+ */
+ for ( address = ( user_to_phys ( image->data, 0 ) & ~0xfffff ) ; ;
+ address -= 0x100000 ) {
+ /* Check that we're not going to overwrite the
+ * kernel itself. This check isn't totally
+ * accurate, but errs on the side of caution.
+ */
+ if ( address <= ( BZI_LOAD_HIGH_ADDR + image->len ) ) {
+ DBGC ( image, "bzImage %p could not find a location "
+ "for initrd\n", image );
+ return -ENOBUFS;
+ }
+ /* Check that we are within the kernel's range */
+ if ( ( address + total_len - 1 ) > exec_ctx->mem_limit )
+ continue;
+ /* Prepare and verify segment */
+ if ( ( rc = prep_segment ( phys_to_user ( address ), 0,
+ total_len ) ) != 0 )
+ continue;
+ /* Use this address */
+ break;
+ }
+
+ /* Record initrd location */
+ exec_ctx->ramdisk_image = address;
+ exec_ctx->ramdisk_size = total_len;
+
+ /* Construct initrd */
+ DBGC ( image, "bzImage %p constructing initrd at [%lx,%lx)\n",
+ image, address, ( address + total_len ) );
+ for_each_image ( initrd ) {
+ address += bzimage_load_initrd ( image, initrd,
+ phys_to_user ( address ) );
+ }
+
+ return 0;
+}
+
+/**
+ * Execute bzImage image
+ *
+ * @v image bzImage image
+ * @ret rc Return status code
+ */
+static int bzimage_exec ( struct image *image ) {
+ struct bzimage_exec_context exec_ctx;
+ struct bzimage_header bzhdr;
+ const char *cmdline = ( image->cmdline ? image->cmdline : "" );
+ int rc;
+
+ /* Initialise context */
+ memset ( &exec_ctx, 0, sizeof ( exec_ctx ) );
+
+ /* Retrieve kernel header */
+ exec_ctx.rm_kernel_seg = image->priv.ul;
+ exec_ctx.rm_kernel = real_to_user ( exec_ctx.rm_kernel_seg, 0 );
+ copy_from_user ( &bzhdr, exec_ctx.rm_kernel, BZI_HDR_OFFSET,
+ sizeof ( bzhdr ) );
+ exec_ctx.rm_cmdline = exec_ctx.rm_heap =
+ ( bzhdr.heap_end_ptr + 0x200 );
+ exec_ctx.vid_mode = bzhdr.vid_mode;
+ if ( bzhdr.version >= 0x0203 ) {
+ exec_ctx.mem_limit = bzhdr.initrd_addr_max;
+ } else {
+ exec_ctx.mem_limit = BZI_INITRD_MAX;
+ }
+
+ /* Parse command line for bootloader parameters */
+ if ( ( rc = bzimage_parse_cmdline ( image, &exec_ctx, cmdline ) ) != 0)
+ return rc;
+
+ /* Store command line */
+ if ( ( rc = bzimage_set_cmdline ( image, &exec_ctx, cmdline ) ) != 0 )
+ return rc;
+
+ /* Load any initrds */
+ if ( ( rc = bzimage_load_initrds ( image, &exec_ctx ) ) != 0 )
+ return rc;
+
+ /* Update and store kernel header */
+ bzhdr.vid_mode = exec_ctx.vid_mode;
+ bzhdr.ramdisk_image = exec_ctx.ramdisk_image;
+ bzhdr.ramdisk_size = exec_ctx.ramdisk_size;
+ copy_to_user ( exec_ctx.rm_kernel, BZI_HDR_OFFSET, &bzhdr,
+ sizeof ( bzhdr ) );
+
+ /* Prepare for exiting */
+ shutdown();
+
+ DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 "
+ "(stack %04x:%04zx)\n", image,
+ ( exec_ctx.rm_kernel_seg + 0x20 ),
+ exec_ctx.rm_kernel_seg, exec_ctx.rm_heap );
+
+ /* Jump to the kernel */
+ __asm__ __volatile__ ( REAL_CODE ( "movw %w0, %%ds\n\t"
+ "movw %w0, %%es\n\t"
+ "movw %w0, %%fs\n\t"
+ "movw %w0, %%gs\n\t"
+ "movw %w0, %%ss\n\t"
+ "movw %w1, %%sp\n\t"
+ "pushw %w2\n\t"
+ "pushw $0\n\t"
+ "lret\n\t" )
+ : : "r" ( exec_ctx.rm_kernel_seg ),
+ "r" ( exec_ctx.rm_heap ),
+ "r" ( exec_ctx.rm_kernel_seg + 0x20 ) );
+
+ /* There is no way for the image to return, since we provide
+ * no return address.
+ */
+ assert ( 0 );
+
+ return -ECANCELED; /* -EIMPOSSIBLE */
+}
+
+/**
+ * Load and parse bzImage header
+ *
+ * @v image bzImage file
+ * @v load_ctx Load context
+ * @v bzhdr Buffer for bzImage header
+ * @ret rc Return status code
+ */
+static int bzimage_load_header ( struct image *image,
+ struct bzimage_load_context *load_ctx,
+ struct bzimage_header *bzhdr ) {
+
+ /* Sanity check */
+ if ( image->len < ( BZI_HDR_OFFSET + sizeof ( *bzhdr ) ) ) {
+ DBGC ( image, "bzImage %p too short for kernel header\n",
+ image );
+ return -ENOEXEC;
+ }
+
+ /* Read and verify header */
+ copy_from_user ( bzhdr, image->data, BZI_HDR_OFFSET,
+ sizeof ( *bzhdr ) );
+ if ( bzhdr->header != BZI_SIGNATURE ) {
+ DBGC ( image, "bzImage %p bad signature %08lx\n",
+ image, bzhdr->header );
+ return -ENOEXEC;
+ }
+
+ /* We don't support ancient kernels */
+ if ( bzhdr->version < 0x0200 ) {
+ DBGC ( image, "bzImage %p version %04x not supported\n",
+ image, bzhdr->version );
+ return -ENOTSUP;
+ }
+
+ /* Calculate load address and size of real-mode portion */
+ load_ctx->rm_kernel_seg = 0x1000; /* place RM kernel at 1000:0000 */
+ load_ctx->rm_kernel = real_to_user ( load_ctx->rm_kernel_seg, 0 );
+ load_ctx->rm_filesz =
+ ( ( bzhdr->setup_sects ? bzhdr->setup_sects : 4 ) + 1 ) << 9;
+ load_ctx->rm_memsz = BZI_ASSUMED_RM_SIZE;
+ if ( load_ctx->rm_filesz > image->len ) {
+ DBGC ( image, "bzImage %p too short for %zd byte of setup\n",
+ image, load_ctx->rm_filesz );
+ return -ENOEXEC;
+ }
+
+ /* Calculate load address and size of non-real-mode portion */
+ load_ctx->pm_kernel = ( ( bzhdr->loadflags & BZI_LOAD_HIGH ) ?
+ phys_to_user ( BZI_LOAD_HIGH_ADDR ) :
+ phys_to_user ( BZI_LOAD_LOW_ADDR ) );
+ load_ctx->pm_sz = ( image->len - load_ctx->rm_filesz );
+
+ DBGC ( image, "bzImage %p version %04x RM %#zx bytes PM %#zx bytes\n",
+ image, bzhdr->version, load_ctx->rm_filesz, load_ctx->pm_sz );
+ return 0;
+}
+
+/**
+ * Load real-mode portion of bzImage
+ *
+ * @v image bzImage file
+ * @v load_ctx Load context
+ * @ret rc Return status code
+ */
+static int bzimage_load_real ( struct image *image,
+ struct bzimage_load_context *load_ctx ) {
+ int rc;
+
+ /* Allow space for the stack and heap */
+ load_ctx->rm_memsz += BZI_STACK_SIZE;
+ load_ctx->rm_heap = load_ctx->rm_memsz;
+
+ /* Allow space for the command line */
+ load_ctx->rm_cmdline = load_ctx->rm_memsz;
+ load_ctx->rm_memsz += BZI_CMDLINE_SIZE;
+
+ /* Prepare, verify, and load the real-mode segment */
+ if ( ( rc = prep_segment ( load_ctx->rm_kernel, load_ctx->rm_filesz,
+ load_ctx->rm_memsz ) ) != 0 ) {
+ DBGC ( image, "bzImage %p could not prepare RM segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+ memcpy_user ( load_ctx->rm_kernel, 0, image->data, 0,
+ load_ctx->rm_filesz );
+
+ return 0;
+}
+
+/**
+ * Load non-real-mode portion of bzImage
+ *
+ * @v image bzImage file
+ * @v load_ctx Load context
+ * @ret rc Return status code
+ */
+static int bzimage_load_non_real ( struct image *image,
+ struct bzimage_load_context *load_ctx ) {
+ int rc;
+
+ /* Prepare, verify and load the non-real-mode segment */
+ if ( ( rc = prep_segment ( load_ctx->pm_kernel, load_ctx->pm_sz,
+ load_ctx->pm_sz ) ) != 0 ) {
+ DBGC ( image, "bzImage %p could not prepare PM segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+ memcpy_user ( load_ctx->pm_kernel, 0, image->data, load_ctx->rm_filesz,
+ load_ctx->pm_sz );
+
+ return 0;
+}
+
+/**
+ * Update and store bzImage header
+ *
+ * @v image bzImage file
+ * @v load_ctx Load context
+ * @v bzhdr Original bzImage header
+ * @ret rc Return status code
+ */
+static int bzimage_write_header ( struct image *image __unused,
+ struct bzimage_load_context *load_ctx,
+ struct bzimage_header *bzhdr ) {
+ struct bzimage_cmdline cmdline;
+
+ /* Update the header and copy it into the loaded kernel */
+ bzhdr->type_of_loader = BZI_LOADER_TYPE_GPXE;
+ if ( bzhdr->version >= 0x0201 ) {
+ bzhdr->heap_end_ptr = ( load_ctx->rm_heap - 0x200 );
+ bzhdr->loadflags |= BZI_CAN_USE_HEAP;
+ }
+ if ( bzhdr->version >= 0x0202 ) {
+ bzhdr->cmd_line_ptr = user_to_phys ( load_ctx->rm_kernel,
+ load_ctx->rm_cmdline );
+ } else {
+ cmdline.magic = BZI_CMDLINE_MAGIC;
+ cmdline.offset = load_ctx->rm_cmdline;
+ copy_to_user ( load_ctx->rm_kernel, BZI_CMDLINE_OFFSET,
+ &cmdline, sizeof ( cmdline ) );
+ bzhdr->setup_move_size = load_ctx->rm_memsz;
+ }
+ copy_to_user ( load_ctx->rm_kernel, BZI_HDR_OFFSET,
+ bzhdr, sizeof ( *bzhdr ) );
+
+ return 0;
+}
+
+/**
+ * Load bzImage image into memory
+ *
+ * @v image bzImage file
+ * @ret rc Return status code
+ */
+int bzimage_load ( struct image *image ) {
+ struct bzimage_load_context load_ctx;
+ struct bzimage_header bzhdr;
+ int rc;
+
+ /* Initialise context */
+ memset ( &load_ctx, 0, sizeof ( load_ctx ) );
+
+ /* Load and verify header */
+ if ( ( rc = bzimage_load_header ( image, &load_ctx, &bzhdr ) ) != 0 )
+ return rc;
+
+ /* This is a bzImage image, valid or otherwise */
+ if ( ! image->type )
+ image->type = &bzimage_image_type;
+
+ /* Load real-mode portion */
+ if ( ( rc = bzimage_load_real ( image, &load_ctx ) ) != 0 )
+ return rc;
+
+ /* Load non-real-mode portion */
+ if ( ( rc = bzimage_load_non_real ( image, &load_ctx ) ) != 0 )
+ return rc;
+
+ /* Update and write out header */
+ if ( ( rc = bzimage_write_header ( image, &load_ctx, &bzhdr ) ) != 0 )
+ return rc;
+
+ /* Record real-mode segment in image private data field */
+ image->priv.ul = load_ctx.rm_kernel_seg;
+
+ return 0;
+}
+
+/** Linux bzImage image type */
+struct image_type bzimage_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "bzImage",
+ .load = bzimage_load,
+ .exec = bzimage_exec,
+};
diff --git a/gpxe/src/arch/i386/image/eltorito.c b/gpxe/src/arch/i386/image/eltorito.c
new file mode 100644
index 00000000..9d573106
--- /dev/null
+++ b/gpxe/src/arch/i386/image/eltorito.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * El Torito bootable ISO image format
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <bootsector.h>
+#include <int13.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/image.h>
+#include <gpxe/segment.h>
+#include <gpxe/ramdisk.h>
+#include <gpxe/init.h>
+
+#define ISO9660_BLKSIZE 2048
+#define ELTORITO_VOL_DESC_OFFSET ( 17 * ISO9660_BLKSIZE )
+
+/** An El Torito Boot Record Volume Descriptor */
+struct eltorito_vol_desc {
+ /** Boot record indicator; must be 0 */
+ uint8_t record_indicator;
+ /** ISO-9660 identifier; must be "CD001" */
+ uint8_t iso9660_id[5];
+ /** Version, must be 1 */
+ uint8_t version;
+ /** Boot system indicator; must be "EL TORITO SPECIFICATION" */
+ uint8_t system_indicator[32];
+ /** Unused */
+ uint8_t unused[32];
+ /** Boot catalog sector */
+ uint32_t sector;
+} __attribute__ (( packed ));
+
+/** An El Torito Boot Catalog Validation Entry */
+struct eltorito_validation_entry {
+ /** Header ID; must be 1 */
+ uint8_t header_id;
+ /** Platform ID
+ *
+ * 0 = 80x86
+ * 1 = PowerPC
+ * 2 = Mac
+ */
+ uint8_t platform_id;
+ /** Reserved */
+ uint16_t reserved;
+ /** ID string */
+ uint8_t id_string[24];
+ /** Checksum word */
+ uint16_t checksum;
+ /** Signature; must be 0xaa55 */
+ uint16_t signature;
+} __attribute__ (( packed ));
+
+/** A bootable entry in the El Torito Boot Catalog */
+struct eltorito_boot_entry {
+ /** Boot indicator
+ *
+ * Must be @c ELTORITO_BOOTABLE for a bootable ISO image
+ */
+ uint8_t indicator;
+ /** Media type
+ *
+ */
+ uint8_t media_type;
+ /** Load segment */
+ uint16_t load_segment;
+ /** System type */
+ uint8_t filesystem;
+ /** Unused */
+ uint8_t reserved_a;
+ /** Sector count */
+ uint16_t length;
+ /** Starting sector */
+ uint32_t start;
+ /** Unused */
+ uint8_t reserved_b[20];
+} __attribute__ (( packed ));
+
+/** Boot indicator for a bootable ISO image */
+#define ELTORITO_BOOTABLE 0x88
+
+/** El Torito media types */
+enum eltorito_media_type {
+ /** No emulation */
+ ELTORITO_NO_EMULATION = 0,
+};
+
+struct image_type eltorito_image_type __image_type ( PROBE_NORMAL );
+
+/**
+ * Calculate 16-bit word checksum
+ *
+ * @v data Data to checksum
+ * @v len Length (in bytes, must be even)
+ * @ret sum Checksum
+ */
+static unsigned int word_checksum ( void *data, size_t len ) {
+ uint16_t *words;
+ uint16_t sum = 0;
+
+ for ( words = data ; len ; words++, len -= 2 ) {
+ sum += *words;
+ }
+ return sum;
+}
+
+/**
+ * Execute El Torito image
+ *
+ * @v image El Torito image
+ * @ret rc Return status code
+ */
+static int eltorito_exec ( struct image *image ) {
+ struct ramdisk ramdisk;
+ struct int13_drive int13_drive;
+ unsigned int load_segment = image->priv.ul;
+ unsigned int load_offset = ( load_segment ? 0 : 0x7c00 );
+ int rc;
+
+ memset ( &ramdisk, 0, sizeof ( ramdisk ) );
+ init_ramdisk ( &ramdisk, image->data, image->len, ISO9660_BLKSIZE );
+
+ memset ( &int13_drive, 0, sizeof ( int13_drive ) );
+ int13_drive.blockdev = &ramdisk.blockdev;
+ register_int13_drive ( &int13_drive );
+
+ if ( ( rc = call_bootsector ( load_segment, load_offset,
+ int13_drive.drive ) ) != 0 ) {
+ DBGC ( image, "ElTorito %p boot failed: %s\n",
+ image, strerror ( rc ) );
+ goto err;
+ }
+
+ rc = -ECANCELED; /* -EIMPOSSIBLE */
+ err:
+ unregister_int13_drive ( &int13_drive );
+ return rc;
+}
+
+/**
+ * Read and verify El Torito Boot Record Volume Descriptor
+ *
+ * @v image El Torito file
+ * @ret catalog_offset Offset of Boot Catalog
+ * @ret rc Return status code
+ */
+static int eltorito_read_voldesc ( struct image *image,
+ unsigned long *catalog_offset ) {
+ static const struct eltorito_vol_desc vol_desc_signature = {
+ .record_indicator = 0,
+ .iso9660_id = "CD001",
+ .version = 1,
+ .system_indicator = "EL TORITO SPECIFICATION",
+ };
+ struct eltorito_vol_desc vol_desc;
+
+ /* Sanity check */
+ if ( image->len < ( ELTORITO_VOL_DESC_OFFSET + ISO9660_BLKSIZE ) ) {
+ DBGC ( image, "ElTorito %p too short\n", image );
+ return -ENOEXEC;
+ }
+
+ /* Read and verify Boot Record Volume Descriptor */
+ copy_from_user ( &vol_desc, image->data, ELTORITO_VOL_DESC_OFFSET,
+ sizeof ( vol_desc ) );
+ if ( memcmp ( &vol_desc, &vol_desc_signature,
+ offsetof ( typeof ( vol_desc ), sector ) ) != 0 ) {
+ DBGC ( image, "ElTorito %p invalid Boot Record Volume "
+ "Descriptor\n", image );
+ return -ENOEXEC;
+ }
+ *catalog_offset = ( vol_desc.sector * ISO9660_BLKSIZE );
+
+ DBGC ( image, "ElTorito %p boot catalog at offset %#lx\n",
+ image, *catalog_offset );
+
+ return 0;
+}
+
+/**
+ * Read and verify El Torito Boot Catalog
+ *
+ * @v image El Torito file
+ * @v catalog_offset Offset of Boot Catalog
+ * @ret boot_entry El Torito boot entry
+ * @ret rc Return status code
+ */
+static int eltorito_read_catalog ( struct image *image,
+ unsigned long catalog_offset,
+ struct eltorito_boot_entry *boot_entry ) {
+ struct eltorito_validation_entry validation_entry;
+
+ /* Sanity check */
+ if ( image->len < ( catalog_offset + ISO9660_BLKSIZE ) ) {
+ DBGC ( image, "ElTorito %p bad boot catalog offset %#lx\n",
+ image, catalog_offset );
+ return -ENOEXEC;
+ }
+
+ /* Read and verify the Validation Entry of the Boot Catalog */
+ copy_from_user ( &validation_entry, image->data, catalog_offset,
+ sizeof ( validation_entry ) );
+ if ( word_checksum ( &validation_entry,
+ sizeof ( validation_entry ) ) != 0 ) {
+ DBGC ( image, "ElTorito %p bad Validation Entry checksum\n",
+ image );
+ return -ENOEXEC;
+ }
+
+ /* Read and verify the Initial/Default entry */
+ copy_from_user ( boot_entry, image->data,
+ ( catalog_offset + sizeof ( validation_entry ) ),
+ sizeof ( *boot_entry ) );
+ if ( boot_entry->indicator != ELTORITO_BOOTABLE ) {
+ DBGC ( image, "ElTorito %p not bootable\n", image );
+ return -ENOEXEC;
+ }
+ if ( boot_entry->media_type != ELTORITO_NO_EMULATION ) {
+ DBGC ( image, "ElTorito %p cannot support media type %d\n",
+ image, boot_entry->media_type );
+ return -ENOTSUP;
+ }
+
+ DBGC ( image, "ElTorito %p media type %d segment %04x\n",
+ image, boot_entry->media_type, boot_entry->load_segment );
+
+ return 0;
+}
+
+/**
+ * Load El Torito virtual disk image into memory
+ *
+ * @v image El Torito file
+ * @v boot_entry El Torito boot entry
+ * @ret rc Return status code
+ */
+static int eltorito_load_disk ( struct image *image,
+ struct eltorito_boot_entry *boot_entry ) {
+ unsigned long start = ( boot_entry->start * ISO9660_BLKSIZE );
+ unsigned long length = ( boot_entry->length * ISO9660_BLKSIZE );
+ unsigned int load_segment;
+ userptr_t buffer;
+ int rc;
+
+ /* Sanity check */
+ if ( image->len < ( start + length ) ) {
+ DBGC ( image, "ElTorito %p virtual disk lies outside image\n",
+ image );
+ return -ENOEXEC;
+ }
+ DBGC ( image, "ElTorito %p virtual disk at %#lx+%#lx\n",
+ image, start, length );
+
+ /* Calculate load address */
+ load_segment = boot_entry->load_segment;
+ buffer = real_to_user ( load_segment, ( load_segment ? 0 : 0x7c00 ) );
+
+ /* Verify and prepare segment */
+ if ( ( rc = prep_segment ( buffer, length, length ) ) != 0 ) {
+ DBGC ( image, "ElTorito %p could not prepare segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy image to segment */
+ memcpy_user ( buffer, 0, image->data, start, length );
+
+ return 0;
+}
+
+/**
+ * Load El Torito image into memory
+ *
+ * @v image El Torito file
+ * @ret rc Return status code
+ */
+static int eltorito_load ( struct image *image ) {
+ struct eltorito_boot_entry boot_entry;
+ unsigned long bootcat_offset;
+ int rc;
+
+ /* Read Boot Record Volume Descriptor, if present */
+ if ( ( rc = eltorito_read_voldesc ( image, &bootcat_offset ) ) != 0 )
+ return rc;
+
+ /* This is an El Torito image, valid or otherwise */
+ if ( ! image->type )
+ image->type = &eltorito_image_type;
+
+ /* Read Boot Catalog */
+ if ( ( rc = eltorito_read_catalog ( image, bootcat_offset,
+ &boot_entry ) ) != 0 )
+ return rc;
+
+ /* Load Virtual Disk image */
+ if ( ( rc = eltorito_load_disk ( image, &boot_entry ) ) != 0 )
+ return rc;
+
+ /* Record load segment in image private data field */
+ image->priv.ul = boot_entry.load_segment;
+
+ return 0;
+}
+
+/** El Torito image type */
+struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "El Torito",
+ .load = eltorito_load,
+ .exec = eltorito_exec,
+};
diff --git a/gpxe/src/arch/i386/image/multiboot.c b/gpxe/src/arch/i386/image/multiboot.c
new file mode 100644
index 00000000..fbaebd5c
--- /dev/null
+++ b/gpxe/src/arch/i386/image/multiboot.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Multiboot image format
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <multiboot.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/image.h>
+#include <gpxe/segment.h>
+#include <gpxe/memmap.h>
+#include <gpxe/elf.h>
+#include <gpxe/init.h>
+#include <gpxe/features.h>
+
+FEATURE ( FEATURE_IMAGE, "Multiboot", DHCP_EB_FEATURE_MULTIBOOT, 1 );
+
+struct image_type multiboot_image_type __image_type ( PROBE_MULTIBOOT );
+
+/**
+ * Maximum number of modules we will allow for
+ *
+ * If this has bitten you: sorry. I did have a perfect scheme with a
+ * dynamically allocated list of modules on the protected-mode stack,
+ * but it was incompatible with some broken OSes that can only access
+ * low memory at boot time (even though we kindly set up 4GB flat
+ * physical addressing as per the multiboot specification.
+ *
+ */
+#define MAX_MODULES 8
+
+/**
+ * Maximum combined length of command lines
+ *
+ * Again; sorry. Some broken OSes zero out any non-base memory that
+ * isn't part of the loaded module set, so we can't just use
+ * virt_to_phys(cmdline) to point to the command lines, even though
+ * this would comply with the Multiboot spec.
+ */
+#define MB_MAX_CMDLINE 512
+
+/** Multiboot flags that we support */
+#define MB_SUPPORTED_FLAGS ( MB_FLAG_PGALIGN | MB_FLAG_MEMMAP | \
+ MB_FLAG_VIDMODE | MB_FLAG_RAW )
+
+/** Compulsory feature multiboot flags */
+#define MB_COMPULSORY_FLAGS 0x0000ffff
+
+/** Optional feature multiboot flags */
+#define MB_OPTIONAL_FLAGS 0xffff0000
+
+/**
+ * Multiboot flags that we don't support
+ *
+ * We only care about the compulsory feature flags (bits 0-15); we are
+ * allowed to ignore the optional feature flags.
+ */
+#define MB_UNSUPPORTED_FLAGS ( MB_COMPULSORY_FLAGS & ~MB_SUPPORTED_FLAGS )
+
+/** A multiboot header descriptor */
+struct multiboot_header_info {
+ /** The actual multiboot header */
+ struct multiboot_header mb;
+ /** Offset of header within the multiboot image */
+ size_t offset;
+};
+
+/** Multiboot module command lines */
+static char __bss16_array ( mb_cmdlines, [MB_MAX_CMDLINE] );
+#define mb_cmdlines __use_data16 ( mb_cmdlines )
+
+/** Offset within module command lines */
+static unsigned int mb_cmdline_offset;
+
+/**
+ * Build multiboot memory map
+ *
+ * @v image Multiboot image
+ * @v mbinfo Multiboot information structure
+ * @v mbmemmap Multiboot memory map
+ * @v limit Maxmimum number of memory map entries
+ */
+static void multiboot_build_memmap ( struct image *image,
+ struct multiboot_info *mbinfo,
+ struct multiboot_memory_map *mbmemmap,
+ unsigned int limit ) {
+ struct memory_map memmap;
+ unsigned int i;
+
+ /* Get memory map */
+ get_memmap ( &memmap );
+
+ /* Translate into multiboot format */
+ memset ( mbmemmap, 0, sizeof ( *mbmemmap ) );
+ for ( i = 0 ; i < memmap.count ; i++ ) {
+ if ( i >= limit ) {
+ DBGC ( image, "MULTIBOOT %p limit of %d memmap "
+ "entries reached\n", image, limit );
+ break;
+ }
+ mbmemmap[i].size = ( sizeof ( mbmemmap[i] ) -
+ sizeof ( mbmemmap[i].size ) );
+ mbmemmap[i].base_addr = memmap.regions[i].start;
+ mbmemmap[i].length = ( memmap.regions[i].end -
+ memmap.regions[i].start );
+ mbmemmap[i].type = MBMEM_RAM;
+ mbinfo->mmap_length += sizeof ( mbmemmap[i] );
+ if ( memmap.regions[i].start == 0 )
+ mbinfo->mem_lower = ( memmap.regions[i].end / 1024 );
+ if ( memmap.regions[i].start == 0x100000 )
+ mbinfo->mem_upper = ( ( memmap.regions[i].end -
+ 0x100000 ) / 1024 );
+ }
+}
+
+/**
+ * Add command line in base memory
+ *
+ * @v cmdline Command line
+ * @ret physaddr Physical address of command line
+ */
+physaddr_t multiboot_add_cmdline ( const char *cmdline ) {
+ char *mb_cmdline;
+
+ if ( ! cmdline )
+ cmdline = "";
+
+ /* Copy command line to base memory buffer */
+ mb_cmdline = ( mb_cmdlines + mb_cmdline_offset );
+ mb_cmdline_offset +=
+ ( snprintf ( mb_cmdline,
+ ( sizeof ( mb_cmdlines ) - mb_cmdline_offset ),
+ "%s", cmdline ) + 1 );
+
+ /* Truncate to terminating NUL in buffer if necessary */
+ if ( mb_cmdline_offset > sizeof ( mb_cmdlines ) )
+ mb_cmdline_offset = ( sizeof ( mb_cmdlines ) - 1 );
+
+ return virt_to_phys ( mb_cmdline );
+}
+
+/**
+ * Build multiboot module list
+ *
+ * @v image Multiboot image
+ * @v modules Module list to fill, or NULL
+ * @ret count Number of modules
+ */
+static unsigned int
+multiboot_build_module_list ( struct image *image,
+ struct multiboot_module *modules,
+ unsigned int limit ) {
+ struct image *module_image;
+ struct multiboot_module *module;
+ unsigned int count = 0;
+ unsigned int insert;
+ physaddr_t start;
+ physaddr_t end;
+ unsigned int i;
+
+ /* Add each image as a multiboot module */
+ for_each_image ( module_image ) {
+
+ if ( count >= limit ) {
+ DBGC ( image, "MULTIBOOT %p limit of %d modules "
+ "reached\n", image, limit );
+ break;
+ }
+
+ /* Do not include kernel image itself as a module */
+ if ( module_image == image )
+ continue;
+
+ /* At least some OSes expect the multiboot modules to
+ * be in ascending order, so we have to support it.
+ */
+ start = user_to_phys ( module_image->data, 0 );
+ end = user_to_phys ( module_image->data, module_image->len );
+ for ( insert = 0 ; insert < count ; insert++ ) {
+ if ( start < modules[insert].mod_start )
+ break;
+ }
+ module = &modules[insert];
+ memmove ( ( module + 1 ), module,
+ ( ( count - insert ) * sizeof ( *module ) ) );
+ module->mod_start = start;
+ module->mod_end = end;
+ module->string =
+ multiboot_add_cmdline ( module_image->cmdline );
+ module->reserved = 0;
+
+ /* We promise to page-align modules */
+ assert ( ( module->mod_start & 0xfff ) == 0 );
+
+ count++;
+ }
+
+ /* Dump module configuration */
+ for ( i = 0 ; i < count ; i++ ) {
+ DBGC ( image, "MULTIBOOT %p module %d is [%lx,%lx)\n",
+ image, i, modules[i].mod_start,
+ modules[i].mod_end );
+ }
+
+ return count;
+}
+
+/**
+ * The multiboot information structure
+ *
+ * Kept in base memory because some OSes won't find it elsewhere,
+ * along with the other structures belonging to the Multiboot
+ * information table.
+ */
+static struct multiboot_info __bss16 ( mbinfo );
+#define mbinfo __use_data16 ( mbinfo )
+
+/** The multiboot bootloader name */
+static char __data16_array ( mb_bootloader_name, [] ) = "gPXE " VERSION;
+#define mb_bootloader_name __use_data16 ( mb_bootloader_name )
+
+/** The multiboot memory map */
+static struct multiboot_memory_map
+ __bss16_array ( mbmemmap, [MAX_MEMORY_REGIONS] );
+#define mbmemmap __use_data16 ( mbmemmap )
+
+/** The multiboot module list */
+static struct multiboot_module __bss16_array ( mbmodules, [MAX_MODULES] );
+#define mbmodules __use_data16 ( mbmodules )
+
+/**
+ * Execute multiboot image
+ *
+ * @v image Multiboot image
+ * @ret rc Return status code
+ */
+static int multiboot_exec ( struct image *image ) {
+ physaddr_t entry = image->priv.phys;
+
+ /* Populate multiboot information structure */
+ memset ( &mbinfo, 0, sizeof ( mbinfo ) );
+ mbinfo.flags = ( MBI_FLAG_LOADER | MBI_FLAG_MEM | MBI_FLAG_MMAP |
+ MBI_FLAG_CMDLINE | MBI_FLAG_MODS );
+ multiboot_build_memmap ( image, &mbinfo, mbmemmap,
+ ( sizeof(mbmemmap) / sizeof(mbmemmap[0]) ) );
+ mb_cmdline_offset = 0;
+ mbinfo.cmdline = multiboot_add_cmdline ( image->cmdline );
+ mbinfo.mods_count = multiboot_build_module_list ( image, mbmodules,
+ ( sizeof(mbmodules) / sizeof(mbmodules[0]) ) );
+ mbinfo.mods_addr = virt_to_phys ( mbmodules );
+ mbinfo.mmap_addr = virt_to_phys ( mbmemmap );
+ mbinfo.boot_loader_name = virt_to_phys ( mb_bootloader_name );
+
+ /* Multiboot images may not return and have no callback
+ * interface, so shut everything down prior to booting the OS.
+ */
+ shutdown();
+
+ /* Jump to OS with flat physical addressing */
+ __asm__ __volatile__ ( PHYS_CODE ( "call *%%edi\n\t" )
+ : : "a" ( MULTIBOOT_BOOTLOADER_MAGIC ),
+ "b" ( virt_to_phys ( &mbinfo ) ),
+ "D" ( entry )
+ : "ecx", "edx", "esi", "ebp", "memory" );
+
+ DBGC ( image, "MULTIBOOT %p returned\n", image );
+
+ /* It isn't safe to continue after calling shutdown() */
+ while ( 1 ) {}
+
+ return -ECANCELED; /* -EIMPOSSIBLE, anyone? */
+}
+
+/**
+ * Find multiboot header
+ *
+ * @v image Multiboot file
+ * @v hdr Multiboot header descriptor to fill in
+ * @ret rc Return status code
+ */
+static int multiboot_find_header ( struct image *image,
+ struct multiboot_header_info *hdr ) {
+ uint32_t buf[64];
+ size_t offset;
+ unsigned int buf_idx;
+ uint32_t checksum;
+
+ /* Scan through first 8kB of image file 256 bytes at a time.
+ * (Use the buffering to avoid the overhead of a
+ * copy_from_user() for every dword.)
+ */
+ for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) {
+ /* Check for end of image */
+ if ( offset > image->len )
+ break;
+ /* Refill buffer if applicable */
+ buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) );
+ if ( buf_idx == 0 ) {
+ copy_from_user ( buf, image->data, offset,
+ sizeof ( buf ) );
+ }
+ /* Check signature */
+ if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC )
+ continue;
+ /* Copy header and verify checksum */
+ copy_from_user ( &hdr->mb, image->data, offset,
+ sizeof ( hdr->mb ) );
+ checksum = ( hdr->mb.magic + hdr->mb.flags +
+ hdr->mb.checksum );
+ if ( checksum != 0 )
+ continue;
+ /* Record offset of multiboot header and return */
+ hdr->offset = offset;
+ return 0;
+ }
+
+ /* No multiboot header found */
+ return -ENOEXEC;
+}
+
+/**
+ * Load raw multiboot image into memory
+ *
+ * @v image Multiboot file
+ * @v hdr Multiboot header descriptor
+ * @ret rc Return status code
+ */
+static int multiboot_load_raw ( struct image *image,
+ struct multiboot_header_info *hdr ) {
+ size_t offset;
+ size_t filesz;
+ size_t memsz;
+ userptr_t buffer;
+ int rc;
+
+ /* Verify and prepare segment */
+ offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr );
+ filesz = ( hdr->mb.load_end_addr - hdr->mb.load_addr );
+ memsz = ( hdr->mb.bss_end_addr - hdr->mb.load_addr );
+ buffer = phys_to_user ( hdr->mb.load_addr );
+ if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
+ DBGC ( image, "MULTIBOOT %p could not prepare segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy image to segment */
+ memcpy_user ( buffer, 0, image->data, offset, filesz );
+
+ /* Record execution entry point in image private data field */
+ image->priv.phys = hdr->mb.entry_addr;
+
+ return 0;
+}
+
+/**
+ * Load ELF multiboot image into memory
+ *
+ * @v image Multiboot file
+ * @ret rc Return status code
+ */
+static int multiboot_load_elf ( struct image *image ) {
+ int rc;
+
+ /* Load ELF image*/
+ if ( ( rc = elf_load ( image ) ) != 0 ) {
+ DBGC ( image, "MULTIBOOT %p ELF image failed to load: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Load multiboot image into memory
+ *
+ * @v image Multiboot file
+ * @ret rc Return status code
+ */
+static int multiboot_load ( struct image *image ) {
+ struct multiboot_header_info hdr;
+ int rc;
+
+ /* Locate multiboot header, if present */
+ if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) {
+ DBGC ( image, "MULTIBOOT %p has no multiboot header\n",
+ image );
+ return rc;
+ }
+ DBGC ( image, "MULTIBOOT %p found header with flags %08lx\n",
+ image, hdr.mb.flags );
+
+ /* This is a multiboot image, valid or otherwise */
+ if ( ! image->type )
+ image->type = &multiboot_image_type;
+
+ /* Abort if we detect flags that we cannot support */
+ if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) {
+ DBGC ( image, "MULTIBOOT %p flags %08lx not supported\n",
+ image, ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) );
+ return -ENOTSUP;
+ }
+
+ /* Load the actual image */
+ if ( hdr.mb.flags & MB_FLAG_RAW ) {
+ if ( ( rc = multiboot_load_raw ( image, &hdr ) ) != 0 )
+ return rc;
+ } else {
+ if ( ( rc = multiboot_load_elf ( image ) ) != 0 )
+ return rc;
+ }
+
+ return 0;
+}
+
+/** Multiboot image type */
+struct image_type multiboot_image_type __image_type ( PROBE_MULTIBOOT ) = {
+ .name = "Multiboot",
+ .load = multiboot_load,
+ .exec = multiboot_exec,
+};
diff --git a/gpxe/src/arch/i386/image/nbi.c b/gpxe/src/arch/i386/image/nbi.c
new file mode 100644
index 00000000..73791be9
--- /dev/null
+++ b/gpxe/src/arch/i386/image/nbi.c
@@ -0,0 +1,458 @@
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <gateA20.h>
+#include <memsizes.h>
+#include <basemem_packet.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/segment.h>
+#include <gpxe/init.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/fakedhcp.h>
+#include <gpxe/image.h>
+#include <gpxe/features.h>
+
+/** @file
+ *
+ * NBI image format.
+ *
+ * The Net Boot Image format is defined by the "Draft Net Boot Image
+ * Proposal 0.3" by Jamie Honan, Gero Kuhlmann and Ken Yap. It is now
+ * considered to be a legacy format, but it still included because a
+ * large amount of software (e.g. nymph, LTSP) makes use of NBI files.
+ *
+ * Etherboot does not implement the INT 78 callback interface
+ * described by the NBI specification. For a callback interface on
+ * x86 architecture, use PXE.
+ *
+ */
+
+FEATURE ( FEATURE_IMAGE, "NBI", DHCP_EB_FEATURE_NBI, 1 );
+
+struct image_type nbi_image_type __image_type ( PROBE_NORMAL );
+
+/**
+ * An NBI image header
+ *
+ * Note that the length field uses a peculiar encoding; use the
+ * NBI_LENGTH() macro to decode the actual header length.
+ *
+ */
+struct imgheader {
+ unsigned long magic; /**< Magic number (NBI_MAGIC) */
+ union {
+ unsigned char length; /**< Nibble-coded header length */
+ unsigned long flags; /**< Image flags */
+ };
+ segoff_t location; /**< 16-bit seg:off header location */
+ union {
+ segoff_t segoff; /**< 16-bit seg:off entry point */
+ unsigned long linear; /**< 32-bit entry point */
+ } execaddr;
+} __attribute__ (( packed ));
+
+/** NBI magic number */
+#define NBI_MAGIC 0x1B031336UL
+
+/* Interpretation of the "length" fields */
+#define NBI_NONVENDOR_LENGTH(len) ( ( (len) & 0x0f ) << 2 )
+#define NBI_VENDOR_LENGTH(len) ( ( (len) & 0xf0 ) >> 2 )
+#define NBI_LENGTH(len) ( NBI_NONVENDOR_LENGTH(len) + NBI_VENDOR_LENGTH(len) )
+
+/* Interpretation of the "flags" fields */
+#define NBI_PROGRAM_RETURNS(flags) ( (flags) & ( 1 << 8 ) )
+#define NBI_LINEAR_EXEC_ADDR(flags) ( (flags) & ( 1 << 31 ) )
+
+/** NBI header length */
+#define NBI_HEADER_LENGTH 512
+
+/**
+ * An NBI segment header
+ *
+ * Note that the length field uses a peculiar encoding; use the
+ * NBI_LENGTH() macro to decode the actual header length.
+ *
+ */
+struct segheader {
+ unsigned char length; /**< Nibble-coded header length */
+ unsigned char vendortag; /**< Vendor-defined private tag */
+ unsigned char reserved;
+ unsigned char flags; /**< Segment flags */
+ unsigned long loadaddr; /**< Load address */
+ unsigned long imglength; /**< Segment length in NBI file */
+ unsigned long memlength; /**< Segment length in memory */
+};
+
+/* Interpretation of the "flags" fields */
+#define NBI_LOADADDR_FLAGS(flags) ( (flags) & 0x03 )
+#define NBI_LOADADDR_ABS 0x00
+#define NBI_LOADADDR_AFTER 0x01
+#define NBI_LOADADDR_END 0x02
+#define NBI_LOADADDR_BEFORE 0x03
+#define NBI_LAST_SEGHEADER(flags) ( (flags) & ( 1 << 2 ) )
+
+/* Define a type for passing info to a loaded program */
+struct ebinfo {
+ uint8_t major, minor; /* Version */
+ uint16_t flags; /* Bit flags */
+};
+
+/** Info passed to NBI image */
+static struct ebinfo loaderinfo = {
+ VERSION_MAJOR, VERSION_MINOR,
+ 0
+};
+
+/**
+ * Prepare a segment for an NBI image
+ *
+ * @v image NBI image
+ * @v offset Offset within NBI image
+ * @v filesz Length of initialised-data portion of the segment
+ * @v memsz Total length of the segment
+ * @v src Source for initialised data
+ * @ret rc Return status code
+ */
+static int nbi_prepare_segment ( struct image *image, size_t offset __unused,
+ userptr_t dest, size_t filesz, size_t memsz ){
+ int rc;
+
+ if ( ( rc = prep_segment ( dest, filesz, memsz ) ) != 0 ) {
+ DBGC ( image, "NBI %p could not prepare segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Load a segment for an NBI image
+ *
+ * @v image NBI image
+ * @v offset Offset within NBI image
+ * @v filesz Length of initialised-data portion of the segment
+ * @v memsz Total length of the segment
+ * @v src Source for initialised data
+ * @ret rc Return status code
+ */
+static int nbi_load_segment ( struct image *image, size_t offset,
+ userptr_t dest, size_t filesz,
+ size_t memsz __unused ) {
+ memcpy_user ( dest, 0, image->data, offset, filesz );
+ return 0;
+}
+
+/**
+ * Process segments of an NBI image
+ *
+ * @v image NBI image
+ * @v imgheader Image header information
+ * @v process Function to call for each segment
+ * @ret rc Return status code
+ */
+static int nbi_process_segments ( struct image *image,
+ struct imgheader *imgheader,
+ int ( * process ) ( struct image *image,
+ size_t offset,
+ userptr_t dest,
+ size_t filesz,
+ size_t memsz ) ) {
+ struct segheader sh;
+ size_t offset = 0;
+ size_t sh_off;
+ userptr_t dest;
+ size_t filesz;
+ size_t memsz;
+ int rc;
+
+ /* Copy image header to target location */
+ dest = real_to_user ( imgheader->location.segment,
+ imgheader->location.offset );
+ filesz = memsz = NBI_HEADER_LENGTH;
+ if ( ( rc = process ( image, offset, dest, filesz, memsz ) ) != 0 )
+ return rc;
+ offset += filesz;
+
+ /* Process segments in turn */
+ sh_off = NBI_LENGTH ( imgheader->length );
+ do {
+ /* Read segment header */
+ copy_from_user ( &sh, image->data, sh_off, sizeof ( sh ) );
+ if ( sh.length == 0 ) {
+ /* Avoid infinite loop? */
+ DBGC ( image, "NBI %p invalid segheader length 0\n",
+ image );
+ return -ENOEXEC;
+ }
+
+ /* Calculate segment load address */
+ switch ( NBI_LOADADDR_FLAGS ( sh.flags ) ) {
+ case NBI_LOADADDR_ABS:
+ dest = phys_to_user ( sh.loadaddr );
+ break;
+ case NBI_LOADADDR_AFTER:
+ dest = userptr_add ( dest, memsz + sh.loadaddr );
+ break;
+ case NBI_LOADADDR_BEFORE:
+ dest = userptr_add ( dest, -sh.loadaddr );
+ break;
+ case NBI_LOADADDR_END:
+ /* Not correct according to the spec, but
+ * maintains backwards compatibility with
+ * previous versions of Etherboot.
+ */
+ dest = phys_to_user ( ( extmemsize() + 1024 ) * 1024
+ - sh.loadaddr );
+ break;
+ default:
+ /* Cannot be reached */
+ assert ( 0 );
+ }
+
+ /* Process this segment */
+ filesz = sh.imglength;
+ memsz = sh.memlength;
+ if ( ( offset + filesz ) > image->len ) {
+ DBGC ( image, "NBI %p segment outside file\n", image );
+ return -ENOEXEC;
+ }
+ if ( ( rc = process ( image, offset, dest,
+ filesz, memsz ) ) != 0 ) {
+ return rc;
+ }
+ offset += filesz;
+
+ /* Next segheader */
+ sh_off += NBI_LENGTH ( sh.length );
+ if ( sh_off >= NBI_HEADER_LENGTH ) {
+ DBGC ( image, "NBI %p header overflow\n", image );
+ return -ENOEXEC;
+ }
+
+ } while ( ! NBI_LAST_SEGHEADER ( sh.flags ) );
+
+ if ( offset != image->len ) {
+ DBGC ( image, "NBI %p length wrong (file %zd, metadata %zd)\n",
+ image, image->len, offset );
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+/**
+ * Load an NBI image into memory
+ *
+ * @v image NBI image
+ * @ret rc Return status code
+ */
+static int nbi_load ( struct image *image ) {
+ struct imgheader imgheader;
+ int rc;
+
+ /* If we don't have enough data give up */
+ if ( image->len < NBI_HEADER_LENGTH ) {
+ DBGC ( image, "NBI %p too short for an NBI image\n", image );
+ return -ENOEXEC;
+ }
+
+ /* Check image header */
+ copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) );
+ if ( imgheader.magic != NBI_MAGIC ) {
+ DBGC ( image, "NBI %p has no NBI signature\n", image );
+ return -ENOEXEC;
+ }
+
+ /* This is an NBI image, valid or otherwise */
+ if ( ! image->type )
+ image->type = &nbi_image_type;
+
+ DBGC ( image, "NBI %p placing header at %hx:%hx\n", image,
+ imgheader.location.segment, imgheader.location.offset );
+
+ /* NBI files can have overlaps between segments; the bss of
+ * one segment may overlap the initialised data of another. I
+ * assume this is a design flaw, but there are images out
+ * there that we need to work with. We therefore do two
+ * passes: first to initialise the segments, then to copy the
+ * data. This avoids zeroing out already-copied data.
+ */
+ if ( ( rc = nbi_process_segments ( image, &imgheader,
+ nbi_prepare_segment ) ) != 0 )
+ return rc;
+ if ( ( rc = nbi_process_segments ( image, &imgheader,
+ nbi_load_segment ) ) != 0 )
+ return rc;
+
+ /* Record header address in image private data field */
+ image->priv.user = real_to_user ( imgheader.location.segment,
+ imgheader.location.offset );
+
+ return 0;
+}
+
+/**
+ * Boot a 16-bit NBI image
+ *
+ * @v imgheader Image header information
+ * @ret rc Return status code, if image returns
+ */
+static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
+ int discard_D, discard_S, discard_b;
+ int rc;
+
+ DBGC ( image, "NBI %p executing 16-bit image at %04x:%04x\n", image,
+ imgheader->execaddr.segoff.segment,
+ imgheader->execaddr.segoff.offset );
+
+ gateA20_unset();
+
+ __asm__ __volatile__ (
+ REAL_CODE ( "pushw %%ds\n\t" /* far pointer to bootp data */
+ "pushw %%bx\n\t"
+ "pushl %%esi\n\t" /* location */
+ "pushw %%cs\n\t" /* lcall execaddr */
+ "call 1f\n\t"
+ "jmp 2f\n\t"
+ "\n1:\n\t"
+ "pushl %%edi\n\t"
+ "lret\n\t"
+ "\n2:\n\t"
+ "addw $8,%%sp\n\t" /* clean up stack */ )
+ : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ),
+ "=b" ( discard_b )
+ : "D" ( imgheader->execaddr.segoff ),
+ "S" ( imgheader->location ),
+ "b" ( __from_data16 ( basemem_packet ) )
+ : "ecx", "edx", "ebp" );
+
+ gateA20_set();
+
+ return rc;
+}
+
+/**
+ * Boot a 32-bit NBI image
+ *
+ * @v imgheader Image header information
+ * @ret rc Return status code, if image returns
+ */
+static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) {
+ int discard_D, discard_S, discard_b;
+ int rc;
+
+ DBGC ( image, "NBI %p executing 32-bit image at %lx\n",
+ image, imgheader->execaddr.linear );
+
+ /* no gateA20_unset for PM call */
+
+ /* Jump to OS with flat physical addressing */
+ __asm__ __volatile__ (
+ PHYS_CODE ( "pushl %%ebx\n\t" /* bootp data */
+ "pushl %%esi\n\t" /* imgheader */
+ "pushl %%eax\n\t" /* loaderinfo */
+ "call *%%edi\n\t"
+ "addl $12, %%esp\n\t" /* clean up stack */ )
+ : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ),
+ "=b" ( discard_b )
+ : "D" ( imgheader->execaddr.linear ),
+ "S" ( ( imgheader->location.segment << 4 ) +
+ imgheader->location.offset ),
+ "b" ( virt_to_phys ( basemem_packet ) ),
+ "a" ( virt_to_phys ( &loaderinfo ) )
+ : "ecx", "edx", "ebp", "memory" );
+
+ return rc;
+}
+
+/**
+ * Guess boot network device
+ *
+ * @ret netdev Boot network device
+ */
+static struct net_device * guess_boot_netdev ( void ) {
+ struct net_device *boot_netdev;
+
+ /* Just use the first network device */
+ for_each_netdev ( boot_netdev ) {
+ return boot_netdev;
+ }
+
+ return NULL;
+}
+
+/**
+ * Prepare DHCP parameter block for NBI image
+ *
+ * @v image NBI image
+ * @ret rc Return status code
+ */
+static int nbi_prepare_dhcp ( struct image *image ) {
+ struct net_device *boot_netdev;
+ int rc;
+
+ boot_netdev = guess_boot_netdev();
+ if ( ! boot_netdev ) {
+ DBGC ( image, "NBI %p could not identify a network device\n",
+ image );
+ return -ENODEV;
+ }
+
+ if ( ( rc = create_fakedhcpack ( boot_netdev, basemem_packet,
+ sizeof ( basemem_packet ) ) ) != 0 ) {
+ DBGC ( image, "NBI %p failed to build DHCP packet\n", image );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Execute a loaded NBI image
+ *
+ * @v image NBI image
+ * @ret rc Return status code
+ */
+static int nbi_exec ( struct image *image ) {
+ struct imgheader imgheader;
+ int may_return;
+ int rc;
+
+ copy_from_user ( &imgheader, image->priv.user, 0,
+ sizeof ( imgheader ) );
+
+ /* Prepare DHCP option block */
+ if ( ( rc = nbi_prepare_dhcp ( image ) ) != 0 )
+ return rc;
+
+ /* Shut down now if NBI image will not return */
+ may_return = NBI_PROGRAM_RETURNS ( imgheader.flags );
+ if ( ! may_return )
+ shutdown();
+
+ /* Execute NBI image */
+ if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) {
+ rc = nbi_boot32 ( image, &imgheader );
+ } else {
+ rc = nbi_boot16 ( image, &imgheader );
+ }
+
+ if ( ! may_return ) {
+ /* Cannot continue after shutdown() called */
+ DBGC ( image, "NBI %p returned %d from non-returnable image\n",
+ image, rc );
+ while ( 1 ) {}
+ }
+
+ DBGC ( image, "NBI %p returned %d\n", image, rc );
+
+ return rc;
+}
+
+/** NBI image type */
+struct image_type nbi_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "NBI",
+ .load = nbi_load,
+ .exec = nbi_exec,
+};
diff --git a/gpxe/src/arch/i386/image/pxe_image.c b/gpxe/src/arch/i386/image/pxe_image.c
new file mode 100644
index 00000000..9e634f14
--- /dev/null
+++ b/gpxe/src/arch/i386/image/pxe_image.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * PXE image format
+ *
+ */
+
+#include <pxe.h>
+#include <pxe_call.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/image.h>
+#include <gpxe/segment.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/features.h>
+
+FEATURE ( FEATURE_IMAGE, "PXE", DHCP_EB_FEATURE_PXE, 1 );
+
+struct image_type pxe_image_type __image_type ( PROBE_PXE );
+
+/**
+ * Execute PXE image
+ *
+ * @v image PXE image
+ * @ret rc Return status code
+ */
+static int pxe_exec ( struct image *image ) {
+ struct net_device *netdev;
+ int rc;
+
+ /* Ensure that PXE stack is ready to use */
+ pxe_init_structures();
+ pxe_hook_int1a();
+
+ /* Arbitrarily pick the first open network device to use for PXE */
+ for_each_netdev ( netdev ) {
+ pxe_set_netdev ( netdev );
+ break;
+ }
+
+ /* Many things will break if pxe_netdev is NULL */
+ if ( ! pxe_netdev ) {
+ DBGC ( image, "IMAGE %p could not locate PXE net device\n",
+ image );
+ return -ENODEV;
+ }
+
+ /* Start PXE NBP */
+ rc = pxe_start_nbp();
+
+ /* Deactivate PXE */
+ pxe_set_netdev ( NULL );
+ pxe_unhook_int1a();
+
+ return rc;
+}
+
+/**
+ * Load PXE image into memory
+ *
+ * @v image PXE file
+ * @ret rc Return status code
+ */
+int pxe_load ( struct image *image ) {
+ userptr_t buffer = real_to_user ( 0, 0x7c00 );
+ size_t filesz = image->len;
+ size_t memsz = image->len;
+ int rc;
+
+ /* There are no signature checks for PXE; we will accept anything */
+ if ( ! image->type )
+ image->type = &pxe_image_type;
+
+ /* Verify and prepare segment */
+ if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
+ DBGC ( image, "IMAGE %p could not prepare segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy image to segment */
+ memcpy_user ( buffer, 0, image->data, 0, filesz );
+
+ return 0;
+}
+
+/** PXE image type */
+struct image_type pxe_image_type __image_type ( PROBE_PXE ) = {
+ .name = "PXE",
+ .load = pxe_load,
+ .exec = pxe_exec,
+};
diff --git a/gpxe/src/arch/i386/include/basemem.h b/gpxe/src/arch/i386/include/basemem.h
new file mode 100644
index 00000000..cd5668e0
--- /dev/null
+++ b/gpxe/src/arch/i386/include/basemem.h
@@ -0,0 +1,33 @@
+#ifndef _BASEMEM_H
+#define _BASEMEM_H
+
+/** @file
+ *
+ * Base memory allocation
+ *
+ */
+
+#include <stdint.h>
+#include <realmode.h>
+#include <bios.h>
+
+/**
+ * Read the BIOS free base memory counter
+ *
+ * @ret fbms Free base memory counter (in kB)
+ */
+static inline unsigned int get_fbms ( void ) {
+ uint16_t fbms;
+
+ get_real ( fbms, BDA_SEG, BDA_FBMS );
+ return fbms;
+}
+
+extern void set_fbms ( unsigned int new_fbms );
+
+/* Actually in hidemem.c, but putting it here avoids polluting the
+ * architecture-independent include/hidemem.h.
+ */
+extern void hide_basemem ( void );
+
+#endif /* _BASEMEM_H */
diff --git a/gpxe/src/arch/i386/include/basemem_packet.h b/gpxe/src/arch/i386/include/basemem_packet.h
new file mode 100644
index 00000000..e4d4f49c
--- /dev/null
+++ b/gpxe/src/arch/i386/include/basemem_packet.h
@@ -0,0 +1,13 @@
+#ifndef BASEMEM_PACKET_H
+#define BASEMEM_PACKET_H
+
+#include <realmode.h>
+
+/** Maximum length of base memory packet buffer */
+#define BASEMEM_PACKET_LEN 1514
+
+/** Base memory packet buffer */
+extern char __bss16_array ( basemem_packet, [BASEMEM_PACKET_LEN] );
+#define basemem_packet __use_data16 ( basemem_packet )
+
+#endif /* BASEMEM_PACKET_H */
diff --git a/gpxe/src/arch/i386/include/bios.h b/gpxe/src/arch/i386/include/bios.h
new file mode 100644
index 00000000..630a898b
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bios.h
@@ -0,0 +1,11 @@
+#ifndef BIOS_H
+#define BIOS_H
+
+#define BDA_SEG 0x0040
+#define BDA_FBMS 0x0013
+#define BDA_NUM_DRIVES 0x0075
+
+extern unsigned long currticks ( void );
+extern void cpu_nap ( void );
+
+#endif /* BIOS_H */
diff --git a/gpxe/src/arch/i386/include/bios_disks.h b/gpxe/src/arch/i386/include/bios_disks.h
new file mode 100644
index 00000000..0dd7c4eb
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bios_disks.h
@@ -0,0 +1,69 @@
+#ifndef BIOS_DISKS_H
+#define BIOS_DISKS_H
+
+#include "dev.h"
+
+/*
+ * Constants
+ *
+ */
+
+#define BIOS_DISK_MAX_NAME_LEN 6
+
+struct bios_disk_sector {
+ char data[512];
+};
+
+/*
+ * The location of a BIOS disk
+ *
+ */
+struct bios_disk_loc {
+ uint8_t drive;
+};
+
+/*
+ * A physical BIOS disk device
+ *
+ */
+struct bios_disk_device {
+ char name[BIOS_DISK_MAX_NAME_LEN];
+ uint8_t drive;
+ uint8_t type;
+};
+
+/*
+ * A BIOS disk driver, with a valid device ID range and naming
+ * function.
+ *
+ */
+struct bios_disk_driver {
+ void ( *fill_drive_name ) ( char *buf, uint8_t drive );
+ uint8_t min_drive;
+ uint8_t max_drive;
+};
+
+/*
+ * Define a BIOS disk driver
+ *
+ */
+#define BIOS_DISK_DRIVER( _name, _fill_drive_name, _min_drive, _max_drive ) \
+ static struct bios_disk_driver _name = { \
+ .fill_drive_name = _fill_drive_name, \
+ .min_drive = _min_drive, \
+ .max_drive = _max_drive, \
+ }
+
+/*
+ * Functions in bios_disks.c
+ *
+ */
+
+
+/*
+ * bios_disk bus global definition
+ *
+ */
+extern struct bus_driver bios_disk_driver;
+
+#endif /* BIOS_DISKS_H */
diff --git a/gpxe/src/arch/i386/include/biosint.h b/gpxe/src/arch/i386/include/biosint.h
new file mode 100644
index 00000000..d4e34963
--- /dev/null
+++ b/gpxe/src/arch/i386/include/biosint.h
@@ -0,0 +1,18 @@
+#ifndef BIOSINT_H
+#define BIOSINT_H
+
+/**
+ * @file BIOS interrupts
+ *
+ */
+
+struct segoff;
+
+extern int hooked_bios_interrupts;
+extern void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler,
+ struct segoff *chain_vector );
+extern int unhook_bios_interrupt ( unsigned int interrupt,
+ unsigned int handler,
+ struct segoff *chain_vector );
+
+#endif /* BIOSINT_H */
diff --git a/gpxe/src/arch/i386/include/bits/byteswap.h b/gpxe/src/arch/i386/include/bits/byteswap.h
new file mode 100644
index 00000000..54b93ab9
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/byteswap.h
@@ -0,0 +1,76 @@
+#ifndef ETHERBOOT_BITS_BYTESWAP_H
+#define ETHERBOOT_BITS_BYTESWAP_H
+
+static inline __attribute__ ((always_inline, const)) uint16_t
+__i386_bswap_16(uint16_t x)
+{
+ __asm__("xchgb %b0,%h0\n\t"
+ : "=q" (x)
+ : "0" (x));
+ return x;
+}
+
+static inline __attribute__ ((always_inline, const)) uint32_t
+__i386_bswap_32(uint32_t x)
+{
+ __asm__("xchgb %b0,%h0\n\t"
+ "rorl $16,%0\n\t"
+ "xchgb %b0,%h0"
+ : "=q" (x)
+ : "0" (x));
+ return x;
+}
+
+static inline __attribute__ ((always_inline, const)) uint64_t
+__i386_bswap_64(uint64_t x)
+{
+ union {
+ uint64_t qword;
+ uint32_t dword[2];
+ } u;
+
+ u.qword = x;
+ u.dword[0] = __i386_bswap_32(u.dword[0]);
+ u.dword[1] = __i386_bswap_32(u.dword[1]);
+ __asm__("xchgl %0,%1"
+ : "=r" ( u.dword[0] ), "=r" ( u.dword[1] )
+ : "0" ( u.dword[0] ), "1" ( u.dword[1] ) );
+ return u.qword;
+}
+
+#define __bswap_constant_16(x) \
+ ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \
+ (((uint16_t)(x) & 0xff00) >> 8)))
+
+#define __bswap_constant_32(x) \
+ ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \
+ (((uint32_t)(x) & 0x0000ff00U) << 8) | \
+ (((uint32_t)(x) & 0x00ff0000U) >> 8) | \
+ (((uint32_t)(x) & 0xff000000U) >> 24)))
+
+#define __bswap_constant_64(x) \
+ ((uint64_t)((((uint64_t)(x) & 0x00000000000000ffULL) << 56) | \
+ (((uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \
+ (((uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \
+ (((uint64_t)(x) & 0x00000000ff000000ULL) << 8) | \
+ (((uint64_t)(x) & 0x000000ff00000000ULL) >> 8) | \
+ (((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \
+ (((uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \
+ (((uint64_t)(x) & 0xff00000000000000ULL) >> 56)))
+
+#define __bswap_16(x) \
+ ((uint16_t)(__builtin_constant_p(x) ? \
+ __bswap_constant_16(x) : \
+ __i386_bswap_16(x)))
+
+#define __bswap_32(x) \
+ ((uint32_t)(__builtin_constant_p(x) ? \
+ __bswap_constant_32(x) : \
+ __i386_bswap_32(x)))
+
+#define __bswap_64(x) \
+ ((uint64_t)(__builtin_constant_p(x) ? \
+ __bswap_constant_64(x) : \
+ __i386_bswap_64(x)))
+
+#endif /* ETHERBOOT_BITS_BYTESWAP_H */
diff --git a/gpxe/src/arch/i386/include/bits/cpu.h b/gpxe/src/arch/i386/include/bits/cpu.h
new file mode 100644
index 00000000..83339ddd
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/cpu.h
@@ -0,0 +1,86 @@
+#ifndef I386_BITS_CPU_H
+#define I386_BITS_CPU_H
+
+/* Intel-defined CPU features, CPUID level 0x00000001, word 0 */
+#define X86_FEATURE_FPU 0 /* Onboard FPU */
+#define X86_FEATURE_VME 1 /* Virtual Mode Extensions */
+#define X86_FEATURE_DE 2 /* Debugging Extensions */
+#define X86_FEATURE_PSE 3 /* Page Size Extensions */
+#define X86_FEATURE_TSC 4 /* Time Stamp Counter */
+#define X86_FEATURE_MSR 5 /* Model-Specific Registers, RDMSR, WRMSR */
+#define X86_FEATURE_PAE 6 /* Physical Address Extensions */
+#define X86_FEATURE_MCE 7 /* Machine Check Architecture */
+#define X86_FEATURE_CX8 8 /* CMPXCHG8 instruction */
+#define X86_FEATURE_APIC 9 /* Onboard APIC */
+#define X86_FEATURE_SEP 11 /* SYSENTER/SYSEXIT */
+#define X86_FEATURE_MTRR 12 /* Memory Type Range Registers */
+#define X86_FEATURE_PGE 13 /* Page Global Enable */
+#define X86_FEATURE_MCA 14 /* Machine Check Architecture */
+#define X86_FEATURE_CMOV 15 /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */
+#define X86_FEATURE_PAT 16 /* Page Attribute Table */
+#define X86_FEATURE_PSE36 17 /* 36-bit PSEs */
+#define X86_FEATURE_PN 18 /* Processor serial number */
+#define X86_FEATURE_CLFLSH 19 /* Supports the CLFLUSH instruction */
+#define X86_FEATURE_DTES 21 /* Debug Trace Store */
+#define X86_FEATURE_ACPI 22 /* ACPI via MSR */
+#define X86_FEATURE_MMX 23 /* Multimedia Extensions */
+#define X86_FEATURE_FXSR 24 /* FXSAVE and FXRSTOR instructions (fast save and restore */
+ /* of FPU context), and CR4.OSFXSR available */
+#define X86_FEATURE_XMM 25 /* Streaming SIMD Extensions */
+#define X86_FEATURE_XMM2 26 /* Streaming SIMD Extensions-2 */
+#define X86_FEATURE_SELFSNOOP 27 /* CPU self snoop */
+#define X86_FEATURE_HT 28 /* Hyper-Threading */
+#define X86_FEATURE_ACC 29 /* Automatic clock control */
+#define X86_FEATURE_IA64 30 /* IA-64 processor */
+
+/* AMD-defined CPU features, CPUID level 0x80000001, word 1 */
+/* Don't duplicate feature flags which are redundant with Intel! */
+#define X86_FEATURE_SYSCALL 11 /* SYSCALL/SYSRET */
+#define X86_FEATURE_MMXEXT 22 /* AMD MMX extensions */
+#define X86_FEATURE_LM 29 /* Long Mode (x86-64) */
+#define X86_FEATURE_3DNOWEXT 30 /* AMD 3DNow! extensions */
+#define X86_FEATURE_3DNOW 31 /* 3DNow! */
+
+/** x86 CPU information */
+struct cpuinfo_x86 {
+ /** CPU features */
+ unsigned int features;
+ /** 64-bit CPU features */
+ unsigned int amd_features;
+};
+
+/*
+ * EFLAGS bits
+ */
+#define X86_EFLAGS_CF 0x00000001 /* Carry Flag */
+#define X86_EFLAGS_PF 0x00000004 /* Parity Flag */
+#define X86_EFLAGS_AF 0x00000010 /* Auxillary carry Flag */
+#define X86_EFLAGS_ZF 0x00000040 /* Zero Flag */
+#define X86_EFLAGS_SF 0x00000080 /* Sign Flag */
+#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */
+#define X86_EFLAGS_IF 0x00000200 /* Interrupt Flag */
+#define X86_EFLAGS_DF 0x00000400 /* Direction Flag */
+#define X86_EFLAGS_OF 0x00000800 /* Overflow Flag */
+#define X86_EFLAGS_IOPL 0x00003000 /* IOPL mask */
+#define X86_EFLAGS_NT 0x00004000 /* Nested Task */
+#define X86_EFLAGS_RF 0x00010000 /* Resume Flag */
+#define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */
+#define X86_EFLAGS_AC 0x00040000 /* Alignment Check */
+#define X86_EFLAGS_VIF 0x00080000 /* Virtual Interrupt Flag */
+#define X86_EFLAGS_VIP 0x00100000 /* Virtual Interrupt Pending */
+#define X86_EFLAGS_ID 0x00200000 /* CPUID detection flag */
+
+/*
+ * Generic CPUID function
+ */
+static inline __attribute__ (( always_inline )) void
+cpuid ( int op, unsigned int *eax, unsigned int *ebx,
+ unsigned int *ecx, unsigned int *edx ) {
+ __asm__ ( "cpuid" :
+ "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx )
+ : "0" ( op ) );
+}
+
+extern void get_cpuinfo ( struct cpuinfo_x86 *cpu );
+
+#endif /* I386_BITS_CPU_H */
diff --git a/gpxe/src/arch/i386/include/bits/elf.h b/gpxe/src/arch/i386/include/bits/elf.h
new file mode 100644
index 00000000..dad9c7b8
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/elf.h
@@ -0,0 +1,91 @@
+#ifndef I386_BITS_ELF_H
+#define I386_BITS_ELF_H
+
+#include "cpu.h"
+
+#ifdef CONFIG_X86_64
+/* ELF Defines for the 64bit version of the current architecture */
+#define EM_CURRENT_64 EM_X86_64
+#define EM_CURRENT_64_PRESENT ( \
+ CPU_FEATURE_P(cpu_info.x86_capability, LM) && \
+ CPU_FEATURE_P(cpu_info.x86_capability, PAE) && \
+ CPU_FEATURE_P(cpu_info.x86_capability, PSE))
+
+#define ELF_CHECK_X86_64_ARCH(x) \
+ (EM_CURRENT_64_PRESENT && ((x).e_machine == EM_X86_64))
+#define __unused_i386
+#else
+#define ELF_CHECK_X86_64_ARCH(x) 0
+#define __unused_i386 __unused
+#endif
+
+
+/* ELF Defines for the current architecture */
+#define EM_CURRENT EM_386
+#define ELFDATA_CURRENT ELFDATA2LSB
+
+#define ELF_CHECK_I386_ARCH(x) \
+ (((x).e_machine == EM_386) || ((x).e_machine == EM_486))
+
+#define ELF_CHECK_ARCH(x) \
+ ((ELF_CHECK_I386_ARCH(x) || ELF_CHECK_X86_64_ARCH(x)) && \
+ ((x).e_entry <= 0xffffffffUL))
+
+#ifdef IMAGE_FREEBSD
+/*
+ * FreeBSD has this rather strange "feature" of its design.
+ * At some point in its evolution, FreeBSD started to rely
+ * externally on private/static/debug internal symbol information.
+ * That is, some of the interfaces that software uses to access
+ * and work with the FreeBSD kernel are made available not
+ * via the shared library symbol information (the .DYNAMIC section)
+ * but rather the debug symbols. This means that any symbol, not
+ * just publicly defined symbols can be (and are) used by system
+ * tools to make the system work. (such as top, swapinfo, swapon,
+ * etc)
+ *
+ * Even worse, however, is the fact that standard ELF loaders do
+ * not know how to load the symbols since they are not within
+ * an ELF PT_LOAD section. The kernel needs these symbols to
+ * operate so the following changes/additions to the boot
+ * loading of EtherBoot have been made to get the kernel to load.
+ * All of the changes are within IMAGE_FREEBSD such that the
+ * extra/changed code only compiles when FREEBSD support is
+ * enabled.
+ */
+
+/*
+ * Section header for FreeBSD (debug symbol kludge!) support
+ */
+typedef struct {
+ Elf32_Word sh_name; /* Section name (index into the
+ section header string table). */
+ Elf32_Word sh_type; /* Section type. */
+ Elf32_Word sh_flags; /* Section flags. */
+ Elf32_Addr sh_addr; /* Address in memory image. */
+ Elf32_Off sh_offset; /* Offset in file. */
+ Elf32_Size sh_size; /* Size in bytes. */
+ Elf32_Word sh_link; /* Index of a related section. */
+ Elf32_Word sh_info; /* Depends on section type. */
+ Elf32_Size sh_addralign; /* Alignment in bytes. */
+ Elf32_Size sh_entsize; /* Size of each entry in section. */
+} Elf32_Shdr;
+
+/* sh_type */
+#define SHT_SYMTAB 2 /* symbol table section */
+#define SHT_STRTAB 3 /* string table section */
+
+/*
+ * Module information subtypes (for the metadata that we need to build)
+ */
+#define MODINFO_END 0x0000 /* End of list */
+#define MODINFO_NAME 0x0001 /* Name of module (string) */
+#define MODINFO_TYPE 0x0002 /* Type of module (string) */
+#define MODINFO_METADATA 0x8000 /* Module-specfic */
+
+#define MODINFOMD_SSYM 0x0003 /* start of symbols */
+#define MODINFOMD_ESYM 0x0004 /* end of symbols */
+
+#endif /* IMAGE_FREEBSD */
+
+#endif /* I386_BITS_ELF_H */
diff --git a/gpxe/src/arch/i386/include/bits/elf_x.h b/gpxe/src/arch/i386/include/bits/elf_x.h
new file mode 100644
index 00000000..86c67250
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/elf_x.h
@@ -0,0 +1,5 @@
+#define ARCH_ELF_CLASS ELFCLASS32
+#define ARCH_ELF_DATA ELFDATA2LSB
+#define ARCH_ELF_MACHINE_OK(x) ((x)==EM_386 || (x)==EM_486)
+typedef Elf32_Ehdr Elf_ehdr;
+typedef Elf32_Phdr Elf_phdr;
diff --git a/gpxe/src/arch/i386/include/bits/eltorito.h b/gpxe/src/arch/i386/include/bits/eltorito.h
new file mode 100644
index 00000000..d43e9aac
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/eltorito.h
@@ -0,0 +1,3 @@
+#ifndef ELTORITO_PLATFORM
+#define ELTORITO_PLATFORM ELTORITO_PLATFORM_X86
+#endif /* ELTORITO_PLATFORM */
diff --git a/gpxe/src/arch/i386/include/bits/endian.h b/gpxe/src/arch/i386/include/bits/endian.h
new file mode 100644
index 00000000..b23b233a
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/endian.h
@@ -0,0 +1,9 @@
+#ifndef ETHERBOOT_BITS_ENDIAN_H
+#define ETHERBOOT_BITS_ENDIAN_H
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#define le32_to_cpup(x) (*(uint32_t *)(x))
+#define cpu_to_le16p(x) (*(uint16_t*)(x))
+
+#endif /* ETHERBOOT_BITS_ENDIAN_H */
diff --git a/gpxe/src/arch/i386/include/bits/errfile.h b/gpxe/src/arch/i386/include/bits/errfile.h
new file mode 100644
index 00000000..0f140214
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/errfile.h
@@ -0,0 +1,34 @@
+#ifndef _BITS_ERRFILE_H
+#define _BITS_ERRFILE_H
+
+/**
+ * @addtogroup errfile Error file identifiers
+ * @{
+ */
+
+#define ERRFILE_umalloc ( ERRFILE_ARCH | ERRFILE_CORE | 0x00000000 )
+#define ERRFILE_memmap ( ERRFILE_ARCH | ERRFILE_CORE | 0x00010000 )
+#define ERRFILE_pnpbios ( ERRFILE_ARCH | ERRFILE_CORE | 0x00020000 )
+#define ERRFILE_smbios ( ERRFILE_ARCH | ERRFILE_CORE | 0x00030000 )
+#define ERRFILE_biosint ( ERRFILE_ARCH | ERRFILE_CORE | 0x00040000 )
+#define ERRFILE_int13 ( ERRFILE_ARCH | ERRFILE_CORE | 0x00050000 )
+
+#define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
+#define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )
+#define ERRFILE_eltorito ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00020000 )
+#define ERRFILE_multiboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00030000 )
+#define ERRFILE_nbi ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00040000 )
+#define ERRFILE_pxe_image ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00050000 )
+
+#define ERRFILE_undi ( ERRFILE_ARCH | ERRFILE_NET | 0x00000000 )
+#define ERRFILE_undiload ( ERRFILE_ARCH | ERRFILE_NET | 0x00010000 )
+#define ERRFILE_undinet ( ERRFILE_ARCH | ERRFILE_NET | 0x00020000 )
+#define ERRFILE_undionly ( ERRFILE_ARCH | ERRFILE_NET | 0x00030000 )
+#define ERRFILE_undirom ( ERRFILE_ARCH | ERRFILE_NET | 0x00040000 )
+
+#define ERRFILE_timer_rdtsc ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 )
+#define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 )
+
+/** @} */
+
+#endif /* _BITS_ERRFILE_H */
diff --git a/gpxe/src/arch/i386/include/bits/stdint.h b/gpxe/src/arch/i386/include/bits/stdint.h
new file mode 100644
index 00000000..a2947cda
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/stdint.h
@@ -0,0 +1,21 @@
+#ifndef _BITS_STDINT_H
+#define _BITS_STDINT_H
+
+typedef typeof(sizeof(int)) size_t;
+typedef signed long ssize_t;
+typedef signed long off_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned long uint32_t;
+typedef unsigned long long uint64_t;
+
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed long int32_t;
+typedef signed long long int64_t;
+
+typedef unsigned long physaddr_t;
+typedef unsigned long intptr_t;
+
+#endif /* _BITS_STDINT_H */
diff --git a/gpxe/src/arch/i386/include/bits/string.h b/gpxe/src/arch/i386/include/bits/string.h
new file mode 100644
index 00000000..c05a7df8
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/string.h
@@ -0,0 +1,252 @@
+#ifndef ETHERBOOT_BITS_STRING_H
+#define ETHERBOOT_BITS_STRING_H
+/*
+ * Taken from Linux /usr/include/asm/string.h
+ * All except memcpy, memmove, memset and memcmp removed.
+ *
+ * Non-standard memswap() function added because it saves quite a bit
+ * of code (mbrown@fensystems.co.uk).
+ */
+
+/*
+ * This string-include defines all string functions as inline
+ * functions. Use gcc. It also assumes ds=es=data space, this should be
+ * normal. Most of the string-functions are rather heavily hand-optimized,
+ * see especially strtok,strstr,str[c]spn. They should work, but are not
+ * very easy to understand. Everything is done entirely within the register
+ * set, making the functions fast and clean. String instructions have been
+ * used through-out, making for "slightly" unclear code :-)
+ *
+ * NO Copyright (C) 1991, 1992 Linus Torvalds,
+ * consider these trivial functions to be PD.
+ */
+
+#define __HAVE_ARCH_MEMCPY
+
+extern __attribute__ (( regparm ( 3 ) )) void * __memcpy ( void *dest,
+ const void *src,
+ size_t len );
+
+#if 0
+static inline __attribute__ (( always_inline )) void *
+__memcpy ( void *dest, const void *src, size_t len ) {
+ int d0, d1, d2;
+ __asm__ __volatile__ ( "rep ; movsb"
+ : "=&c" ( d0 ), "=&S" ( d1 ), "=&D" ( d2 )
+ : "0" ( len ), "1" ( src ), "2" ( dest )
+ : "memory" );
+ return dest;
+}
+#endif
+
+static inline __attribute__ (( always_inline )) void *
+__constant_memcpy ( void *dest, const void *src, size_t len ) {
+ union {
+ uint32_t u32[2];
+ uint16_t u16[4];
+ uint8_t u8[8];
+ } __attribute__ (( __may_alias__ )) *dest_u = dest;
+ const union {
+ uint32_t u32[2];
+ uint16_t u16[4];
+ uint8_t u8[8];
+ } __attribute__ (( __may_alias__ )) *src_u = src;
+ const void *esi;
+ void *edi;
+
+ switch ( len ) {
+ case 0 : /* 0 bytes */
+ return dest;
+ /*
+ * Single-register moves; these are always better than a
+ * string operation. We can clobber an arbitrary two
+ * registers (data, source, dest can re-use source register)
+ * instead of being restricted to esi and edi. There's also a
+ * much greater potential for optimising with nearby code.
+ *
+ */
+ case 1 : /* 4 bytes */
+ dest_u->u8[0] = src_u->u8[0];
+ return dest;
+ case 2 : /* 6 bytes */
+ dest_u->u16[0] = src_u->u16[0];
+ return dest;
+ case 4 : /* 4 bytes */
+ dest_u->u32[0] = src_u->u32[0];
+ return dest;
+ /*
+ * Double-register moves; these are probably still a win.
+ *
+ */
+ case 3 : /* 12 bytes */
+ dest_u->u16[0] = src_u->u16[0];
+ dest_u->u8[2] = src_u->u8[2];
+ return dest;
+ case 5 : /* 10 bytes */
+ dest_u->u32[0] = src_u->u32[0];
+ dest_u->u8[4] = src_u->u8[4];
+ return dest;
+ case 6 : /* 12 bytes */
+ dest_u->u32[0] = src_u->u32[0];
+ dest_u->u16[2] = src_u->u16[2];
+ return dest;
+ case 8 : /* 10 bytes */
+ dest_u->u32[0] = src_u->u32[0];
+ dest_u->u32[1] = src_u->u32[1];
+ return dest;
+ }
+
+ /* Even if we have to load up esi and edi ready for a string
+ * operation, we can sometimes save space by using multiple
+ * single-byte "movs" operations instead of loading up ecx and
+ * using "rep movsb".
+ *
+ * "load ecx, rep movsb" is 7 bytes, plus an average of 1 byte
+ * to allow for saving/restoring ecx 50% of the time.
+ *
+ * "movsl" and "movsb" are 1 byte each, "movsw" is two bytes.
+ * (In 16-bit mode, "movsl" is 2 bytes and "movsw" is 1 byte,
+ * but "movsl" moves twice as much data, so it balances out).
+ *
+ * The cutoff point therefore occurs around 26 bytes; the byte
+ * requirements for each method are:
+ *
+ * len 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+ * #bytes (ecx) 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
+ * #bytes (no ecx) 4 5 6 7 5 6 7 8 6 7 8 9 7 8 9 10
+ */
+
+ esi = src;
+ edi = dest;
+
+ if ( len >= 26 )
+ return __memcpy ( dest, src, len );
+
+ if ( len >= 6*4 )
+ __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi )
+ : "0" ( edi ), "1" ( esi ) : "memory" );
+ if ( len >= 5*4 )
+ __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi )
+ : "0" ( edi ), "1" ( esi ) : "memory" );
+ if ( len >= 4*4 )
+ __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi )
+ : "0" ( edi ), "1" ( esi ) : "memory" );
+ if ( len >= 3*4 )
+ __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi )
+ : "0" ( edi ), "1" ( esi ) : "memory" );
+ if ( len >= 2*4 )
+ __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi )
+ : "0" ( edi ), "1" ( esi ) : "memory" );
+ if ( len >= 1*4 )
+ __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi )
+ : "0" ( edi ), "1" ( esi ) : "memory" );
+ if ( ( len % 4 ) >= 2 )
+ __asm__ __volatile__ ( "movsw" : "=&D" ( edi ), "=&S" ( esi )
+ : "0" ( edi ), "1" ( esi ) : "memory" );
+ if ( ( len % 2 ) >= 1 )
+ __asm__ __volatile__ ( "movsb" : "=&D" ( edi ), "=&S" ( esi )
+ : "0" ( edi ), "1" ( esi ) : "memory" );
+
+ return dest;
+}
+
+#define memcpy( dest, src, len ) \
+ ( __builtin_constant_p ( (len) ) ? \
+ __constant_memcpy ( (dest), (src), (len) ) : \
+ __memcpy ( (dest), (src), (len) ) )
+
+#define __HAVE_ARCH_MEMMOVE
+static inline void * memmove(void * dest,const void * src, size_t n)
+{
+int d0, d1, d2;
+if (dest<src)
+__asm__ __volatile__(
+ "cld\n\t"
+ "rep\n\t"
+ "movsb"
+ : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+ :"0" (n),"1" (src),"2" (dest)
+ : "memory");
+else
+__asm__ __volatile__(
+ "std\n\t"
+ "rep\n\t"
+ "movsb\n\t"
+ "cld"
+ : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+ :"0" (n),
+ "1" (n-1+(const char *)src),
+ "2" (n-1+(char *)dest)
+ :"memory");
+return dest;
+}
+
+#define __HAVE_ARCH_MEMSET
+static inline void * memset(void *s, int c,size_t count)
+{
+int d0, d1;
+__asm__ __volatile__(
+ "cld\n\t"
+ "rep\n\t"
+ "stosb"
+ : "=&c" (d0), "=&D" (d1)
+ :"a" (c),"1" (s),"0" (count)
+ :"memory");
+return s;
+}
+
+#define __HAVE_ARCH_MEMSWAP
+static inline void * memswap(void *dest, void *src, size_t n)
+{
+int d0, d1, d2, d3;
+__asm__ __volatile__(
+ "\n1:\t"
+ "movb (%%edi),%%al\n\t"
+ "xchgb (%%esi),%%al\n\t"
+ "incl %%esi\n\t"
+ "stosb\n\t"
+ "loop 1b"
+ : "=&c" (d0), "=&S" (d1), "=&D" (d2), "=&a" (d3)
+ : "0" (n), "1" (src), "2" (dest)
+ : "memory" );
+return dest;
+}
+
+#define __HAVE_ARCH_STRNCMP
+static inline int strncmp(const char * cs,const char * ct,size_t count)
+{
+register int __res;
+int d0, d1, d2;
+__asm__ __volatile__(
+ "1:\tdecl %3\n\t"
+ "js 2f\n\t"
+ "lodsb\n\t"
+ "scasb\n\t"
+ "jne 3f\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n"
+ "2:\txorl %%eax,%%eax\n\t"
+ "jmp 4f\n"
+ "3:\tsbbl %%eax,%%eax\n\t"
+ "orb $1,%%al\n"
+ "4:"
+ :"=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2)
+ :"1" (cs),"2" (ct),"3" (count));
+return __res;
+}
+
+#define __HAVE_ARCH_STRLEN
+static inline size_t strlen(const char * s)
+{
+int d0;
+register int __res;
+__asm__ __volatile__(
+ "repne\n\t"
+ "scasb\n\t"
+ "notl %0\n\t"
+ "decl %0"
+ :"=c" (__res), "=&D" (d0) :"1" (s),"a" (0), "0" (0xffffffff));
+return __res;
+}
+
+#endif /* ETHERBOOT_BITS_STRING_H */
diff --git a/gpxe/src/arch/i386/include/bits/timer2.h b/gpxe/src/arch/i386/include/bits/timer2.h
new file mode 100644
index 00000000..83923b29
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/timer2.h
@@ -0,0 +1,8 @@
+#ifndef BITS_TIMER2_H
+#define BITS_TIMER2_H
+
+#include <stddef.h>
+
+void i386_timer2_udelay(unsigned int usecs);
+
+#endif
diff --git a/gpxe/src/arch/i386/include/bits/uaccess.h b/gpxe/src/arch/i386/include/bits/uaccess.h
new file mode 100644
index 00000000..9c6d0c21
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/uaccess.h
@@ -0,0 +1,6 @@
+#ifndef _BITS_UACCESS_H
+#define _BITS_UACCESS_H
+
+#include <realmode.h>
+
+#endif /* _BITS_UACCESS_H */
diff --git a/gpxe/src/arch/i386/include/bits/uuid.h b/gpxe/src/arch/i386/include/bits/uuid.h
new file mode 100644
index 00000000..0cbd320a
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bits/uuid.h
@@ -0,0 +1,10 @@
+#ifndef _I386_UUID_H
+#define _I386_UUID_H
+
+#include <smbios.h>
+
+static inline int get_uuid ( union uuid *uuid ) {
+ return smbios_get_uuid ( uuid );
+}
+
+#endif /* _I386_UUID_H */
diff --git a/gpxe/src/arch/i386/include/bochs.h b/gpxe/src/arch/i386/include/bochs.h
new file mode 100644
index 00000000..9d090fc1
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bochs.h
@@ -0,0 +1,34 @@
+#ifndef BOCHS_H
+#define BOCHS_H
+
+/** @file
+ *
+ * bochs breakpoints
+ *
+ * This file defines @c bochsbp, the magic breakpoint instruction that
+ * is incredibly useful when debugging under bochs. This file should
+ * never be included in production code.
+ *
+ * Use the pseudo-instruction @c bochsbp in assembly code, or the
+ * bochsbp() function in C code.
+ *
+ */
+
+#ifdef ASSEMBLY
+
+/* Breakpoint for when debugging under bochs */
+#define bochsbp xchgw %bx, %bx
+#define BOCHSBP bochsbp
+
+#else /* ASSEMBLY */
+
+/** Breakpoint for when debugging under bochs */
+static inline void bochsbp ( void ) {
+ __asm__ __volatile__ ( "xchgw %bx, %bx" );
+}
+
+#endif /* ASSEMBLY */
+
+#warning "bochs.h should not be included into production code"
+
+#endif /* BOCHS_H */
diff --git a/gpxe/src/arch/i386/include/bootsector.h b/gpxe/src/arch/i386/include/bootsector.h
new file mode 100644
index 00000000..e9071052
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bootsector.h
@@ -0,0 +1,12 @@
+#ifndef _BOOTSECTOR_H
+#define _BOOTSECTOR_H
+
+/** @file
+ *
+ * x86 bootsector image format
+ */
+
+extern int call_bootsector ( unsigned int segment, unsigned int offset,
+ unsigned int drive );
+
+#endif /* _BOOTSECTOR_H */
diff --git a/gpxe/src/arch/i386/include/bzimage.h b/gpxe/src/arch/i386/include/bzimage.h
new file mode 100644
index 00000000..609e8362
--- /dev/null
+++ b/gpxe/src/arch/i386/include/bzimage.h
@@ -0,0 +1,129 @@
+#ifndef _BZIMAGE_H
+#define _BZIMAGE_H
+
+#include <stdint.h>
+
+/**
+ * A bzImage header
+ *
+ * As documented in Documentation/i386/boot.txt
+ */
+struct bzimage_header {
+ /** The size of the setup in sectors
+ *
+ * If this field contains 0, assume it contains 4.
+ */
+ uint8_t setup_sects;
+ /** If set, the root is mounted readonly */
+ uint16_t root_flags;
+ /** DO NOT USE - for bootsect.S use only */
+ uint16_t syssize;
+ /** DO NOT USE - obsolete */
+ uint16_t swap_dev;
+ /** DO NOT USE - for bootsect.S use only */
+ uint16_t ram_size;
+ /** Video mode control */
+ uint16_t vid_mode;
+ /** Default root device number */
+ uint16_t root_dev;
+ /** 0xAA55 magic number */
+ uint16_t boot_flag;
+ /** Jump instruction */
+ uint16_t jump;
+ /** Magic signature "HdrS" */
+ uint32_t header;
+ /** Boot protocol version supported */
+ uint16_t version;
+ /** Boot loader hook (see below) */
+ uint32_t realmode_swtch;
+ /** The load-low segment (0x1000) (obsolete) */
+ uint16_t start_sys;
+ /** Pointer to kernel version string */
+ uint16_t kernel_version;
+ /** Boot loader identifier */
+ uint8_t type_of_loader;
+ /** Boot protocol option flags */
+ uint8_t loadflags;
+ /** Move to high memory size (used with hooks) */
+ uint16_t setup_move_size;
+ /** Boot loader hook (see below) */
+ uint32_t code32_start;
+ /** initrd load address (set by boot loader) */
+ uint32_t ramdisk_image;
+ /** initrd size (set by boot loader) */
+ uint32_t ramdisk_size;
+ /** DO NOT USE - for bootsect.S use only */
+ uint32_t bootsect_kludge;
+ /** Free memory after setup end */
+ uint16_t heap_end_ptr;
+ /** Unused */
+ uint16_t pad1;
+ /** 32-bit pointer to the kernel command line */
+ uint32_t cmd_line_ptr;
+ /** Highest legal initrd address */
+ uint32_t initrd_addr_max;
+} __attribute__ (( packed ));
+
+/** Offset of bzImage header within kernel image */
+#define BZI_HDR_OFFSET 0x1f1
+
+/** bzImage magic signature value */
+#define BZI_SIGNATURE 0x53726448
+
+/** bzImage boot loader identifier for Etherboot */
+#define BZI_LOADER_TYPE_ETHERBOOT 0x40
+
+/** bzImage boot loader identifier for gPXE
+ *
+ * We advertise ourselves as Etherboot version 6.
+ */
+#define BZI_LOADER_TYPE_GPXE ( BZI_LOADER_TYPE_ETHERBOOT | 0x06 )
+
+/** bzImage "load high" flag */
+#define BZI_LOAD_HIGH 0x01
+
+/** Load address for high-loaded kernels */
+#define BZI_LOAD_HIGH_ADDR 0x100000
+
+/** Load address for low-loaded kernels */
+#define BZI_LOAD_LOW_ADDR 0x10000
+
+/** bzImage "kernel can use heap" flag */
+#define BZI_CAN_USE_HEAP 0x80
+
+/** bzImage special video mode "normal" */
+#define BZI_VID_MODE_NORMAL 0xffff
+
+/** bzImage special video mode "ext" */
+#define BZI_VID_MODE_EXT 0xfffe
+
+/** bzImage special video mode "ask" */
+#define BZI_VID_MODE_ASK 0xfffd
+
+/** bzImage maximum initrd address for versions < 2.03 */
+#define BZI_INITRD_MAX 0x37ffffff
+
+/** bzImage command-line structure used by older kernels */
+struct bzimage_cmdline {
+ /** Magic signature */
+ uint16_t magic;
+ /** Offset to command line */
+ uint16_t offset;
+} __attribute__ (( packed ));
+
+/** Offset of bzImage command-line structure within kernel image */
+#define BZI_CMDLINE_OFFSET 0x20
+
+/** bzImage command line present magic marker value */
+#define BZI_CMDLINE_MAGIC 0xa33f
+
+/** Assumed size of real-mode portion (including .bss) */
+#define BZI_ASSUMED_RM_SIZE 0x8000
+
+/** Amount of stack space to provide */
+#define BZI_STACK_SIZE 0x1000
+
+/** Maximum size of command line */
+#define BZI_CMDLINE_SIZE 0x100
+
+#endif /* _BZIMAGE_H */
diff --git a/gpxe/src/arch/i386/include/callbacks_arch.h b/gpxe/src/arch/i386/include/callbacks_arch.h
new file mode 100644
index 00000000..f9cba488
--- /dev/null
+++ b/gpxe/src/arch/i386/include/callbacks_arch.h
@@ -0,0 +1,243 @@
+/* Callout/callback interface for Etherboot
+ *
+ * This file provides the mechanisms for making calls from Etherboot
+ * to external programs and vice-versa.
+ *
+ * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
+ *
+ * $Id$
+ */
+
+#ifndef CALLBACKS_ARCH_H
+#define CALLBACKS_ARCH_H
+
+/* Skip the definitions that won't make sense to the assembler */
+#ifndef ASSEMBLY
+
+/* Struct to hold general-purpose register values. PUSHAL and POPAL
+ * can work directly with this structure; do not change the order of
+ * registers.
+ */
+typedef struct {
+ union {
+ uint16_t di;
+ uint32_t edi;
+ };
+ union {
+ uint16_t si;
+ uint32_t esi;
+ };
+ union {
+ uint16_t bp;
+ uint32_t ebp;
+ };
+ union {
+ uint16_t sp;
+ uint32_t esp;
+ };
+ union {
+ struct {
+ uint8_t bl;
+ uint8_t bh;
+ } PACKED;
+ uint16_t bx;
+ uint32_t ebx;
+ };
+ union {
+ struct {
+ uint8_t dl;
+ uint8_t dh;
+ } PACKED;
+ uint16_t dx;
+ uint32_t edx;
+ };
+ union {
+ struct {
+ uint8_t cl;
+ uint8_t ch;
+ } PACKED;
+ uint16_t cx;
+ uint32_t ecx;
+ };
+ union {
+ struct {
+ uint8_t al;
+ uint8_t ah;
+ } PACKED;
+ uint16_t ax;
+ uint32_t eax;
+ };
+} regs_t;
+
+/* Struct to hold segment register values. Don't change the order;
+ * many bits of assembly code rely on it.
+ */
+typedef struct {
+ uint16_t cs;
+ uint16_t ss;
+ uint16_t ds;
+ uint16_t es;
+ uint16_t fs;
+ uint16_t gs;
+} PACKED seg_regs_t;
+
+/* Struct for a GDT descriptor */
+typedef struct {
+ uint16_t limit;
+ uint32_t address;
+ uint16_t padding;
+} PACKED gdt_descriptor_t;
+
+/* Struct for a GDT entry. Use GDT_SEGMENT() to fill it in.
+ */
+typedef struct {
+ uint16_t limit_0_15;
+ uint16_t base_0_15;
+ uint8_t base_16_23;
+ uint8_t accessed__type__sflag__dpl__present;
+ uint8_t limit_16_19__avl__size__granularity;
+ uint8_t base_24_31;
+} PACKED gdt_segment_t;
+
+#define GDT_SEGMENT(base,limit,type,sflag,dpl,avl,size,granularity) \
+ ( (gdt_segment_t) { \
+ ( (limit) & 0xffff ), \
+ ( (base) & 0xffff ), \
+ ( ( (base) >> 16 ) & 0xff ), \
+ ( ( 1 << 0 ) | ( (type) << 1 ) | \
+ ( (sflag) << 4 ) | ( (dpl) << 5 ) | ( 1 << 7 ) ), \
+ ( ( (limit) >> 16 ) | \
+ ( (avl) << 4 ) | ( (size) << 5 ) | ( (granularity) << 7 ) ),\
+ ( (base) >> 24 ) \
+ } )
+#define GDT_SEGMENT_BASE(gdt_segment) \
+ ( (gdt_segment)->base_0_15 | \
+ (gdt_segment)->base_16_23 << 16 | \
+ (gdt_segment)->base_24_31 << 24 )
+#define GDT_SEGMENT_LIMIT(gdt_segment) \
+ ( (gdt_segment)->limit_0_15 | \
+ ( ( (gdt_segment)->limit_16_19__avl__size__granularity \
+ & 0xf ) << 16 ) )
+#define GDT_SEGMENT_GRANULARITY(gdt_segment) \
+ ( ( (gdt_segment)->limit_16_19__avl__size__granularity \
+ & 0x80 ) >> 7 )
+#define GDT_SEGMENT_TYPE(gdt_segment) \
+ ( ( (gdt_segment)->accessed__type__sflag__dpl__present & 0x0e ) >> 1 )
+#define GDT_SEGMENT_SIZE(gdt_segment) \
+ ( ( (gdt_segment)->limit_16_19__avl__size__granularity \
+ & 0x60 ) >> 5 )
+
+#define GDT_TYPE_DATA (0x0)
+#define GDT_TYPE_STACK (0x2)
+#define GDT_TYPE_WRITEABLE (0x1)
+#define GDT_TYPE_CODE (0x6)
+#define GDT_TYPE_EXEC_ONLY_CODE (0x4)
+#define GDT_TYPE_CONFORMING (0x1)
+#define GDT_SFLAG_SYSTEM (0)
+#define GDT_SFLAG_NORMAL (1)
+#define GDT_AVL_NORMAL (0)
+#define GDT_SIZE_16BIT (0x0)
+#define GDT_SIZE_32BIT (0x2)
+#define GDT_SIZE_64BIT (0x1)
+#define GDT_SIZE_UNKNOWN (0x3)
+#define GDT_GRANULARITY_SMALL (0)
+#define GDT_GRANULARITY_LARGE (1)
+#define GDT_SEGMENT_NORMAL(base,limit,type,size,granularity) \
+ GDT_SEGMENT ( base, limit, type, \
+ GDT_SFLAG_NORMAL, 0, GDT_AVL_NORMAL, \
+ size, granularity )
+
+/* Protected mode code segment */
+#define GDT_SEGMENT_PMCS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \
+ GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_PMCS_PHYS GDT_SEGMENT_PMCS(0)
+/* Protected mode data segment */
+#define GDT_SEGMENT_PMDS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
+ GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_PMDS_PHYS GDT_SEGMENT_PMDS(0)
+/* Real mode code segment */
+/* Not sure if there's any reason to use GDT_TYPE_EXEC_ONLY_CODE
+ * instead of just GDT_TYPE_CODE, but that's what our old GDT did and
+ * it worked, so I'm not changing it.
+ */
+#define GDT_SEGMENT_RMCS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xffff, GDT_TYPE_EXEC_ONLY_CODE | GDT_TYPE_CONFORMING, \
+ GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL )
+/* Real mode data segment */
+#define GDT_SEGMENT_RMDS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
+ GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL )
+/* Long mode code segment */
+#define GDT_SEGMENT_LMCS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \
+ GDT_SIZE_64BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_LMCS_PHYS GDT_SEGMENT_LMCS(0)
+/* Long mode data segment */
+/* AFIACT, GDT_SIZE_64BIT applies only to code segments */
+#define GDT_SEGMENT_LMDS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
+ GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_LMDS_PHYS GDT_SEGMENT_LMDS(0)
+
+/* Template for creating GDT structures (including segment register
+ * lists), suitable for passing as parameters to external_call().
+ */
+#define GDT_STRUCT_t(num_segments) \
+ struct { \
+ gdt_descriptor_t descriptor; \
+ gdt_segment_t segments[num_segments]; \
+ } PACKED
+/* And utility function for filling it in */
+#define GDT_ADJUST(structure) { \
+ (structure)->descriptor.address = \
+ virt_to_phys(&((structure)->descriptor.limit)); \
+ (structure)->descriptor.limit = \
+ sizeof((structure)->segments) + 8 - 1; \
+ (structure)->descriptor.padding = 0; \
+}
+
+/* Data passed in to in_call() by assembly wrapper.
+ */
+typedef struct {
+ regs_t regs;
+ seg_regs_t seg_regs;
+ gdt_descriptor_t gdt_desc;
+ uint32_t flags;
+ struct {
+ uint32_t offset;
+ uint32_t segment;
+ } ret_addr;
+} PACKED i386_pm_in_call_data_t;
+
+typedef struct {
+ seg_regs_t seg_regs;
+ union {
+ uint16_t pad;
+ uint16_t prefix_sp;
+ };
+ uint16_t flags;
+ struct {
+ uint16_t offset;
+ uint16_t segment;
+ } ret_addr;
+ uint32_t orig_opcode;
+} PACKED i386_rm_in_call_data_t;
+
+typedef struct {
+ i386_pm_in_call_data_t *pm;
+ i386_rm_in_call_data_t *rm;
+} i386_in_call_data_t;
+#define in_call_data_t i386_in_call_data_t
+
+/* Function prototypes
+ */
+extern int install_rm_callback_interface ( void *address, size_t available );
+
+#endif /* ASSEMBLY */
+
+#define RM_IN_CALL (0)
+#define RM_IN_CALL_FAR (2)
+
+#endif /* CALLBACKS_ARCH_H */
diff --git a/gpxe/src/arch/i386/include/gateA20.h b/gpxe/src/arch/i386/include/gateA20.h
new file mode 100644
index 00000000..297ad6f2
--- /dev/null
+++ b/gpxe/src/arch/i386/include/gateA20.h
@@ -0,0 +1,7 @@
+#ifndef GATEA20_H
+#define GATEA20_H
+
+extern void gateA20_set ( void );
+extern void gateA20_unset ( void );
+
+#endif /* GATEA20_H */
diff --git a/gpxe/src/arch/i386/include/int13.h b/gpxe/src/arch/i386/include/int13.h
new file mode 100644
index 00000000..2a193831
--- /dev/null
+++ b/gpxe/src/arch/i386/include/int13.h
@@ -0,0 +1,277 @@
+#ifndef INT13_H
+#define INT13_H
+
+/** @file
+ *
+ * INT 13 emulation
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/list.h>
+
+struct block_device;
+
+/**
+ * @defgroup int13ops INT 13 operation codes
+ * @{
+ */
+
+/** Reset disk system */
+#define INT13_RESET 0x00
+/** Get status of last operation */
+#define INT13_GET_LAST_STATUS 0x01
+/** Read sectors */
+#define INT13_READ_SECTORS 0x02
+/** Write sectors */
+#define INT13_WRITE_SECTORS 0x03
+/** Get drive parameters */
+#define INT13_GET_PARAMETERS 0x08
+/** Get disk type */
+#define INT13_GET_DISK_TYPE 0x15
+/** Extensions installation check */
+#define INT13_EXTENSION_CHECK 0x41
+/** Extended read */
+#define INT13_EXTENDED_READ 0x42
+/** Extended write */
+#define INT13_EXTENDED_WRITE 0x43
+/** Get extended drive parameters */
+#define INT13_GET_EXTENDED_PARAMETERS 0x48
+/** Get CD-ROM status / terminate emulation */
+#define INT13_CDROM_STATUS_TERMINATE 0x4b
+
+/** @} */
+
+/**
+ * @defgroup int13status INT 13 status codes
+ * @{
+ */
+
+/** Operation completed successfully */
+#define INT13_STATUS_SUCCESS 0x00
+/** Invalid function or parameter */
+#define INT13_STATUS_INVALID 0x01
+/** Read error */
+#define INT13_STATUS_READ_ERROR 0x04
+/** Write error */
+#define INT13_STATUS_WRITE_ERROR 0xcc
+
+/** @} */
+
+/** Block size for non-extended INT 13 calls */
+#define INT13_BLKSIZE 512
+
+/** An INT 13 emulated drive */
+struct int13_drive {
+ /** List of all registered drives */
+ struct list_head list;
+
+ /** Underlying block device */
+ struct block_device *blockdev;
+
+ /** BIOS drive number (0x80-0xff) */
+ unsigned int drive;
+ /** Number of cylinders
+ *
+ * The cylinder number field in an INT 13 call is ten bits
+ * wide, giving a maximum of 1024 cylinders. Conventionally,
+ * when the 7.8GB limit of a CHS address is exceeded, it is
+ * the number of cylinders that is increased beyond the
+ * addressable limit.
+ */
+ unsigned int cylinders;
+ /** Number of heads
+ *
+ * The head number field in an INT 13 call is eight bits wide,
+ * giving a maximum of 256 heads. However, apparently all
+ * versions of MS-DOS up to and including Win95 fail with 256
+ * heads, so the maximum encountered in practice is 255.
+ */
+ unsigned int heads;
+ /** Number of sectors per track
+ *
+ * The sector number field in an INT 13 call is six bits wide,
+ * giving a maximum of 63 sectors, since sector numbering
+ * (unlike head and cylinder numbering) starts at 1, not 0.
+ */
+ unsigned int sectors_per_track;
+
+ /** Status of last operation */
+ int last_status;
+};
+
+/** An INT 13 disk address packet */
+struct int13_disk_address {
+ /** Size of the packet, in bytes */
+ uint8_t bufsize;
+ /** Reserved, must be zero */
+ uint8_t reserved;
+ /** Block count */
+ uint16_t count;
+ /** Data buffer */
+ struct segoff buffer;
+ /** Starting block number */
+ uint64_t lba;
+ /** Data buffer (EDD-3.0 only) */
+ uint64_t buffer_phys;
+} __attribute__ (( packed ));
+
+/** INT 13 disk parameters */
+struct int13_disk_parameters {
+ /** Size of this structure */
+ uint16_t bufsize;
+ /** Flags */
+ uint16_t flags;
+ /** Number of cylinders */
+ uint32_t cylinders;
+ /** Number of heads */
+ uint32_t heads;
+ /** Number of sectors per track */
+ uint32_t sectors_per_track;
+ /** Total number of sectors on drive */
+ uint64_t sectors;
+ /** Bytes per sector */
+ uint16_t sector_size;
+
+} __attribute__ (( packed ));
+
+/**
+ * @defgroup int13types INT 13 disk types
+ * @{
+ */
+
+/** No such drive */
+#define INT13_DISK_TYPE_NONE 0x00
+/** Floppy without change-line support */
+#define INT13_DISK_TYPE_FDD 0x01
+/** Floppy with change-line support */
+#define INT13_DISK_TYPE_FDD_CL 0x02
+/** Hard disk */
+#define INT13_DISK_TYPE_HDD 0x03
+
+/** @} */
+
+/**
+ * @defgroup int13flags INT 13 disk parameter flags
+ * @{
+ */
+
+/** DMA boundary errors handled transparently */
+#define INT13_FL_DMA_TRANSPARENT 0x01
+/** CHS information is valid */
+#define INT13_FL_CHS_VALID 0x02
+/** Removable drive */
+#define INT13_FL_REMOVABLE 0x04
+/** Write with verify supported */
+#define INT13_FL_VERIFIABLE 0x08
+/** Has change-line supported (valid only for removable drives) */
+#define INT13_FL_CHANGE_LINE 0x10
+/** Drive can be locked (valid only for removable drives) */
+#define INT13_FL_LOCKABLE 0x20
+/** CHS is max possible, not current media (valid only for removable drives) */
+#define INT13_FL_CHS_MAX 0x40
+
+/** @} */
+
+/**
+ * @defgroup int13exts INT 13 extension flags
+ * @{
+ */
+
+/** Extended disk access functions supported */
+#define INT13_EXTENSION_LINEAR 0x01
+/** Removable drive functions supported */
+#define INT13_EXTENSION_REMOVABLE 0x02
+/** EDD functions supported */
+#define INT13_EXTENSION_EDD 0x04
+
+/** @} */
+
+/**
+ * @defgroup int13vers INT 13 extension versions
+ * @{
+ */
+
+/** INT13 extensions version 1.x */
+#define INT13_EXTENSION_VER_1_X 0x01
+/** INT13 extensions version 2.0 (EDD-1.0) */
+#define INT13_EXTENSION_VER_2_0 0x20
+/** INT13 extensions version 2.1 (EDD-1.1) */
+#define INT13_EXTENSION_VER_2_1 0x21
+/** INT13 extensions version 3.0 (EDD-3.0) */
+#define INT13_EXTENSION_VER_3_0 0x30
+
+/** @} */
+
+/** Bootable CD-ROM specification packet */
+struct int13_cdrom_specification {
+ /** Size of packet in bytes */
+ uint8_t size;
+ /** Boot media type */
+ uint8_t media_type;
+ /** Drive number */
+ uint8_t drive;
+ /** CD-ROM controller number */
+ uint8_t controller;
+ /** LBA of disk image to emulate */
+ uint32_t lba;
+ /** Device specification */
+ uint16_t device;
+ /** Segment of 3K buffer for caching CD-ROM reads */
+ uint16_t cache_segment;
+ /** Load segment for initial boot image */
+ uint16_t load_segment;
+ /** Number of 512-byte sectors to load */
+ uint16_t load_sectors;
+ /** Low 8 bits of cylinder number */
+ uint8_t cyl;
+ /** Sector number, plus high 2 bits of cylinder number */
+ uint8_t cyl_sector;
+ /** Head number */
+ uint8_t head;
+} __attribute__ (( packed ));
+
+/** A C/H/S address within a partition table entry */
+struct partition_chs {
+ /** Head number */
+ uint8_t head;
+ /** Sector number, plus high 2 bits of cylinder number */
+ uint8_t cyl_sector;
+ /** Low 8 bits of cylinder number */
+ uint8_t cyl;
+} __attribute__ (( packed ));
+
+#define PART_HEAD(chs) ( (chs).head )
+#define PART_SECTOR(chs) ( (chs).cyl_sector & 0x3f )
+#define PART_CYLINDER(chs) ( (chs).cyl | ( ( (chs).cyl_sector & 0xc0 ) << 2 ) )
+
+/** A partition table entry within the MBR */
+struct partition_table_entry {
+ /** Bootable flag */
+ uint8_t bootable;
+ /** C/H/S start address */
+ struct partition_chs chs_start;
+ /** System indicator (partition type) */
+ uint8_t type;
+ /** C/H/S end address */
+ struct partition_chs chs_end;
+ /** Linear start address */
+ uint32_t start;
+ /** Linear length */
+ uint32_t length;
+} __attribute__ (( packed ));
+
+/** A Master Boot Record */
+struct master_boot_record {
+ uint8_t pad[446];
+ /** Partition table */
+ struct partition_table_entry partitions[4];
+ /** 0x55aa MBR signature */
+ uint16_t signature;
+} __attribute__ (( packed ));
+
+extern void register_int13_drive ( struct int13_drive *drive );
+extern void unregister_int13_drive ( struct int13_drive *drive );
+extern int int13_boot ( unsigned int drive );
+
+#endif /* INT13_H */
diff --git a/gpxe/src/arch/i386/include/io.h b/gpxe/src/arch/i386/include/io.h
new file mode 100644
index 00000000..c26fdf7e
--- /dev/null
+++ b/gpxe/src/arch/i386/include/io.h
@@ -0,0 +1,265 @@
+#ifndef ETHERBOOT_IO_H
+#define ETHERBOOT_IO_H
+
+#include <stdint.h>
+#include "virtaddr.h"
+
+/* virt_to_bus converts an addresss inside of etherboot [_start, _end]
+ * into a memory access cards can use.
+ */
+#define virt_to_bus virt_to_phys
+
+
+/* bus_to_virt reverses virt_to_bus, the address must be output
+ * from virt_to_bus to be valid. This function does not work on
+ * all bus addresses.
+ */
+#define bus_to_virt phys_to_virt
+
+/* ioremap converts a random 32bit bus address into something
+ * etherboot can access.
+ */
+static inline void *ioremap(unsigned long bus_addr, unsigned long length __unused)
+{
+ return bus_to_virt(bus_addr);
+}
+
+/* iounmap cleans up anything ioremap had to setup */
+static inline void iounmap(void *virt_addr __unused)
+{
+ return;
+}
+
+/*
+ * This file contains the definitions for the x86 IO instructions
+ * inb/inw/inl/outb/outw/outl and the "string versions" of the same
+ * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing"
+ * versions of the single-IO instructions (inb_p/inw_p/..).
+ *
+ * This file is not meant to be obfuscating: it's just complicated
+ * to (a) handle it all in a way that makes gcc able to optimize it
+ * as well as possible and (b) trying to avoid writing the same thing
+ * over and over again with slight variations and possibly making a
+ * mistake somewhere.
+ */
+
+/*
+ * Thanks to James van Artsdalen for a better timing-fix than
+ * the two short jumps: using outb's to a nonexistent port seems
+ * to guarantee better timings even on fast machines.
+ *
+ * On the other hand, I'd like to be sure of a non-existent port:
+ * I feel a bit unsafe about using 0x80 (should be safe, though)
+ *
+ * Linus
+ */
+
+#ifdef SLOW_IO_BY_JUMPING
+#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
+#else
+#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
+#endif
+
+#ifdef REALLY_SLOW_IO
+#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; }
+#else
+#define SLOW_DOWN_IO __SLOW_DOWN_IO
+#endif
+
+/*
+ * readX/writeX() are used to access memory mapped devices. On some
+ * architectures the memory mapped IO stuff needs to be accessed
+ * differently. On the x86 architecture, we just read/write the
+ * memory location directly.
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+_readb ( volatile uint8_t *addr ) {
+ unsigned long data = *addr;
+ DBGIO ( "[%08lx] => %02lx\n", virt_to_phys ( addr ), data );
+ return data;
+}
+static inline __attribute__ (( always_inline )) unsigned long
+_readw ( volatile uint16_t *addr ) {
+ unsigned long data = *addr;
+ DBGIO ( "[%08lx] => %04lx\n", virt_to_phys ( addr ), data );
+ return data;
+}
+static inline __attribute__ (( always_inline )) unsigned long
+_readl ( volatile uint32_t *addr ) {
+ unsigned long data = *addr;
+ DBGIO ( "[%08lx] => %08lx\n", virt_to_phys ( addr ), data );
+ return data;
+}
+#define readb( addr ) _readb ( ( volatile uint8_t * ) (addr) )
+#define readw( addr ) _readw ( ( volatile uint16_t * ) (addr) )
+#define readl( addr ) _readl ( ( volatile uint32_t * ) (addr) )
+
+static inline __attribute__ (( always_inline )) void
+_writeb ( unsigned long data, volatile uint8_t *addr ) {
+ DBGIO ( "[%08lx] <= %02lx\n", virt_to_phys ( addr ), data );
+ *addr = data;
+}
+static inline __attribute__ (( always_inline )) void
+_writew ( unsigned long data, volatile uint16_t *addr ) {
+ DBGIO ( "[%08lx] <= %04lx\n", virt_to_phys ( addr ), data );
+ *addr = data;
+}
+static inline __attribute__ (( always_inline )) void
+_writel ( unsigned long data, volatile uint32_t *addr ) {
+ DBGIO ( "[%08lx] <= %08lx\n", virt_to_phys ( addr ), data );
+ *addr = data;
+}
+#define writeb( b, addr ) _writeb ( (b), ( volatile uint8_t * ) (addr) )
+#define writew( b, addr ) _writew ( (b), ( volatile uint16_t * ) (addr) )
+#define writel( b, addr ) _writel ( (b), ( volatile uint32_t * ) (addr) )
+
+#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
+
+/*
+ * Force strict CPU ordering.
+ * And yes, this is required on UP too when we're talking
+ * to devices.
+ *
+ * For now, "wmb()" doesn't actually do anything, as all
+ * Intel CPU's follow what Intel calls a *Processor Order*,
+ * in which all writes are seen in the program order even
+ * outside the CPU.
+ *
+ * I expect future Intel CPU's to have a weaker ordering,
+ * but I'd also expect them to finally get their act together
+ * and add some real memory barriers if so.
+ *
+ * Some non intel clones support out of order store. wmb() ceases to be a
+ * nop for these.
+ */
+
+#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")
+#define rmb() mb()
+#define wmb() mb();
+
+
+/*
+ * Talk about misusing macros..
+ */
+
+#define __OUT1(s,x) \
+extern void __out##s(unsigned x value, unsigned short port); \
+extern inline void __out##s(unsigned x value, unsigned short port) {
+
+#define __OUT2(s,s1,s2) \
+__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1"
+
+#define __OUT(s,s1,x) \
+__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \
+__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \
+__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \
+__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; }
+
+#define __IN1(s,x) \
+extern unsigned x __in##s(unsigned short port); \
+extern inline unsigned x __in##s(unsigned short port) { unsigned x _v;
+
+#define __IN2(s,s1,s2) \
+__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0"
+
+#define __IN(s,s1,x,i...) \
+__IN1(s,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \
+__IN1(s##c,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \
+__IN1(s##_p,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \
+__IN1(s##c_p,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; }
+
+#define __INS(s) \
+extern void ins##s(unsigned short port, void * addr, unsigned long count); \
+extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("cld ; rep ; ins" #s \
+: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+#define __OUTS(s) \
+extern void outs##s(unsigned short port, const void * addr, unsigned long count); \
+extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("cld ; rep ; outs" #s \
+: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+__IN(b,"", char)
+__IN(w,"",short)
+__IN(l,"", long)
+
+__OUT(b,"b",char)
+__OUT(w,"w",short)
+__OUT(l,,int)
+
+__INS(b)
+__INS(w)
+__INS(l)
+
+__OUTS(b)
+__OUTS(w)
+__OUTS(l)
+
+/*
+ * Note that due to the way __builtin_constant_p() works, you
+ * - can't use it inside a inline function (it will never be true)
+ * - you don't have to worry about side effects within the __builtin..
+ */
+#define outb(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outbc((val),(port)) : \
+ __outb((val),(port)))
+
+#define inb(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inbc(port) : \
+ __inb(port))
+
+#define outb_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outbc_p((val),(port)) : \
+ __outb_p((val),(port)))
+
+#define inb_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inbc_p(port) : \
+ __inb_p(port))
+
+#define outw(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outwc((val),(port)) : \
+ __outw((val),(port)))
+
+#define inw(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inwc(port) : \
+ __inw(port))
+
+#define outw_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outwc_p((val),(port)) : \
+ __outw_p((val),(port)))
+
+#define inw_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inwc_p(port) : \
+ __inw_p(port))
+
+#define outl(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outlc((val),(port)) : \
+ __outl((val),(port)))
+
+#define inl(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inlc(port) : \
+ __inl(port))
+
+#define outl_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outlc_p((val),(port)) : \
+ __outl_p((val),(port)))
+
+#define inl_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inlc_p(port) : \
+ __inl_p(port))
+
+#endif /* ETHERBOOT_IO_H */
diff --git a/gpxe/src/arch/i386/include/kir.h b/gpxe/src/arch/i386/include/kir.h
new file mode 100644
index 00000000..84633d26
--- /dev/null
+++ b/gpxe/src/arch/i386/include/kir.h
@@ -0,0 +1,18 @@
+#ifndef KIR_H
+#define KIR_H
+
+#ifndef KEEP_IT_REAL
+#error "kir.h can be used only with -DKEEP_IT_REAL"
+#endif
+
+#ifdef ASSEMBLY
+
+#define code32 code16gcc
+
+#else /* ASSEMBLY */
+
+__asm__ ( ".code16gcc" );
+
+#endif /* ASSEMBLY */
+
+#endif /* KIR_H */
diff --git a/gpxe/src/arch/i386/include/libkir.h b/gpxe/src/arch/i386/include/libkir.h
new file mode 100644
index 00000000..5f67a56d
--- /dev/null
+++ b/gpxe/src/arch/i386/include/libkir.h
@@ -0,0 +1,233 @@
+#ifndef LIBKIR_H
+#define LIBKIR_H
+
+#include "realmode.h"
+
+#ifndef ASSEMBLY
+
+/*
+ * Full API documentation for these functions is in realmode.h.
+ *
+ */
+
+/* Access to variables in .data16 and .text16 in a way compatible with librm */
+#define __data16( variable ) variable
+#define __data16_array( variable, array ) variable array
+#define __bss16( variable ) variable
+#define __bss16_array( variable, array ) variable array
+#define __text16( variable ) variable
+#define __text16_array( variable,array ) variable array
+#define __use_data16( variable ) variable
+#define __use_text16( variable ) variable
+#define __from_data16( variable ) variable
+#define __from_text16( variable ) variable
+
+/* Real-mode data and code segments */
+static inline __attribute__ (( always_inline )) unsigned int _rm_cs ( void ) {
+ uint16_t cs;
+ __asm__ __volatile__ ( "movw %%cs, %w0" : "=r" ( cs ) );
+ return cs;
+}
+
+static inline __attribute__ (( always_inline )) unsigned int _rm_ds ( void ) {
+ uint16_t ds;
+ __asm__ __volatile__ ( "movw %%ds, %w0" : "=r" ( ds ) );
+ return ds;
+}
+
+#define rm_cs ( _rm_cs() )
+#define rm_ds ( _rm_ds() )
+
+/* Copy to/from base memory */
+
+static inline void copy_to_real_libkir ( unsigned int dest_seg,
+ unsigned int dest_off,
+ const void *src, size_t n ) {
+ unsigned int discard_D, discard_S, discard_c;
+
+ __asm__ __volatile__ ( "pushw %%es\n\t"
+ "movw %3, %%es\n\t"
+ "rep movsb\n\t"
+ "popw %%es\n\t"
+ : "=D" ( discard_D ), "=S" ( discard_S ),
+ "=c" ( discard_c )
+ : "r" ( dest_seg ), "D" ( dest_off ),
+ "S" ( src ),
+ "c" ( n )
+ : "memory" );
+}
+
+static inline void copy_from_real_libkir ( void *dest,
+ unsigned int src_seg,
+ unsigned int src_off,
+ size_t n ) {
+ unsigned int discard_D, discard_S, discard_c;
+
+ __asm__ __volatile__ ( "pushw %%ds\n\t"
+ "movw %4, %%ds\n\t"
+ "rep movsb\n\t"
+ "popw %%ds\n\t"
+ : "=D" ( discard_D ), "=S" ( discard_S ),
+ "=c" ( discard_c )
+ : "D" ( dest ),
+ "r" ( src_seg ), "S" ( src_off ),
+ "c" ( n )
+ : "memory" );
+}
+
+#define copy_to_real copy_to_real_libkir
+#define copy_from_real copy_from_real_libkir
+
+/*
+ * Transfer individual values to/from base memory. There may well be
+ * a neater way to do this. We have two versions: one for constant
+ * offsets (where the mov instruction must be of the form "mov
+ * %es:123, %xx") and one for non-constant offsets (where the mov
+ * instruction must be of the form "mov %es:(%xx), %yx". If it's
+ * possible to incorporate both forms into one __asm__ instruction, I
+ * don't know how to do it.
+ *
+ * Ideally, the mov instruction should be "mov%z0"; the "%z0" is meant
+ * to expand to either "b", "w" or "l" depending on the size of
+ * operand 0. This would remove the (minor) ambiguity in the mov
+ * instruction. However, gcc on at least my system barfs with an
+ * "internal compiler error" when confronted with %z0.
+ *
+ */
+
+#define put_real_kir_const_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %0, %%es:%c2\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : \
+ : "r,r" ( var ), "rm,rm" ( seg ), "i,!r" ( off ) \
+ )
+
+#define put_real_kir_nonconst_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %0, %%es:(%2)\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : \
+ : "r" ( var ), "rm" ( seg ), "r" ( off ) \
+ )
+
+#define put_real_kir( var, seg, off ) \
+ do { \
+ if ( __builtin_constant_p ( off ) ) \
+ put_real_kir_const_off ( var, seg, off ); \
+ else \
+ put_real_kir_nonconst_off ( var, seg, off ); \
+ } while ( 0 )
+
+#define get_real_kir_const_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %%es:%c2, %0\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : "=r,r" ( var ) \
+ : "rm,rm" ( seg ), "i,!r" ( off ) \
+ )
+
+#define get_real_kir_nonconst_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %%es:(%2), %0\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : "=r" ( var ) \
+ : "rm" ( seg ), "r" ( off ) \
+ )
+
+#define get_real_kir( var, seg, off ) \
+ do { \
+ if ( __builtin_constant_p ( off ) ) \
+ get_real_kir_const_off ( var, seg, off ); \
+ else \
+ get_real_kir_nonconst_off ( var, seg, off ); \
+ } while ( 0 )
+
+#define put_real put_real_kir
+#define get_real get_real_kir
+
+/**
+ * A pointer to a user buffer
+ *
+ * This is actually a struct segoff, but encoded as a uint32_t to
+ * ensure that gcc passes it around efficiently.
+ */
+typedef uint32_t userptr_t;
+
+/**
+ * Copy data to user buffer
+ *
+ * @v buffer User buffer
+ * @v offset Offset within user buffer
+ * @v src Source
+ * @v len Length
+ */
+static inline __attribute__ (( always_inline )) void
+copy_to_user ( userptr_t buffer, off_t offset, const void *src, size_t len ) {
+ copy_to_real ( ( buffer >> 16 ), ( ( buffer & 0xffff ) + offset ),
+ src, len );
+}
+
+/**
+ * Copy data from user buffer
+ *
+ * @v dest Destination
+ * @v buffer User buffer
+ * @v offset Offset within user buffer
+ * @v len Length
+ */
+static inline __attribute__ (( always_inline )) void
+copy_from_user ( void *dest, userptr_t buffer, off_t offset, size_t len ) {
+ copy_from_real ( dest, ( buffer >> 16 ),
+ ( ( buffer & 0xffff ) + offset ), len );
+}
+
+/**
+ * Convert segment:offset address to user buffer
+ *
+ * @v segment Real-mode segment
+ * @v offset Real-mode offset
+ * @ret buffer User buffer
+ */
+static inline __attribute__ (( always_inline )) userptr_t
+real_to_user ( unsigned int segment, unsigned int offset ) {
+ return ( ( segment << 16 ) | offset );
+}
+
+/**
+ * Convert virtual address to user buffer
+ *
+ * @v virtual Virtual address
+ * @ret buffer User buffer
+ *
+ * This constructs a user buffer from an ordinary pointer. Use it
+ * when you need to pass a pointer to an internal buffer to a function
+ * that expects a @c userptr_t.
+ */
+static inline __attribute__ (( always_inline )) userptr_t
+virt_to_user ( void * virtual ) {
+ return real_to_user ( rm_ds, ( intptr_t ) virtual );
+}
+
+/* TEXT16_CODE: declare a fragment of code that resides in .text16 */
+#define TEXT16_CODE( asm_code_str ) \
+ ".section \".text16\", \"ax\", @progbits\n\t" \
+ ".code16\n\t" \
+ ".arch i386\n\t" \
+ asm_code_str "\n\t" \
+ ".code16gcc\n\t" \
+ ".previous\n\t"
+
+/* REAL_CODE: declare a fragment of code that executes in real mode */
+#define REAL_CODE( asm_code_str ) \
+ ".code16\n\t" \
+ asm_code_str "\n\t" \
+ ".code16gcc\n\t"
+
+#endif /* ASSEMBLY */
+
+#endif /* LIBKIR_H */
diff --git a/gpxe/src/arch/i386/include/librm.h b/gpxe/src/arch/i386/include/librm.h
new file mode 100644
index 00000000..32dceed6
--- /dev/null
+++ b/gpxe/src/arch/i386/include/librm.h
@@ -0,0 +1,289 @@
+#ifndef LIBRM_H
+#define LIBRM_H
+
+/* Drag in protected-mode segment selector values */
+#include "virtaddr.h"
+#include "realmode.h"
+
+#ifndef ASSEMBLY
+
+#include "stddef.h"
+#include "string.h"
+
+/*
+ * Data structures and type definitions
+ *
+ */
+
+/* Access to variables in .data16 and .text16 */
+extern char *data16;
+extern char *text16;
+
+#define __data16( variable ) \
+ __attribute__ (( section ( ".data16" ) )) \
+ _data16_ ## variable __asm__ ( #variable )
+
+#define __data16_array( variable, array ) \
+ __attribute__ (( section ( ".data16" ) )) \
+ _data16_ ## variable array __asm__ ( #variable )
+
+#define __bss16( variable ) \
+ __attribute__ (( section ( ".bss16" ) )) \
+ _data16_ ## variable __asm__ ( #variable )
+
+#define __bss16_array( variable, array ) \
+ __attribute__ (( section ( ".bss16" ) )) \
+ _data16_ ## variable array __asm__ ( #variable )
+
+#define __text16( variable ) \
+ __attribute__ (( section ( ".text16.data" ) )) \
+ _text16_ ## variable __asm__ ( #variable )
+
+#define __text16_array( variable, array ) \
+ __attribute__ (( section ( ".text16.data" ) )) \
+ _text16_ ## variable array __asm__ ( #variable )
+
+#define __use_data16( variable ) \
+ ( * ( ( typeof ( _data16_ ## variable ) * ) \
+ & ( data16 [ ( size_t ) & ( _data16_ ## variable ) ] ) ) )
+
+#define __use_text16( variable ) \
+ ( * ( ( typeof ( _text16_ ## variable ) * ) \
+ & ( text16 [ ( size_t ) & ( _text16_ ## variable ) ] ) ) )
+
+#define __from_data16( variable ) \
+ ( * ( ( typeof ( variable ) * ) \
+ ( ( ( void * ) &(variable) ) - ( ( void * ) data16 ) ) ) )
+
+#define __from_text16( variable ) \
+ ( * ( ( typeof ( variable ) * ) \
+ ( ( ( void * ) &(variable) ) - ( ( void * ) text16 ) ) ) )
+
+/* Variables in librm.S, present in the normal data segment */
+extern uint16_t __data16 ( rm_cs );
+#define rm_cs __use_data16 ( rm_cs )
+extern uint16_t __text16 ( rm_ds );
+#define rm_ds __use_text16 ( rm_ds )
+
+/* Functions that librm expects to be able to link to. Included here
+ * so that the compiler will catch prototype mismatches.
+ */
+extern void gateA20_set ( void );
+
+/*
+ * librm_mgmt: functions for manipulating base memory and executing
+ * real-mode code.
+ *
+ * Full API documentation for these functions is in realmode.h.
+ *
+ */
+
+/* Macro for obtaining a physical address from a segment:offset pair. */
+#define VIRTUAL(x,y) ( phys_to_virt ( ( ( x ) << 4 ) + ( y ) ) )
+
+/* Copy to/from base memory */
+static inline __attribute__ (( always_inline )) void
+copy_to_real_librm ( unsigned int dest_seg, unsigned int dest_off,
+ void *src, size_t n ) {
+ memcpy ( VIRTUAL ( dest_seg, dest_off ), src, n );
+}
+static inline __attribute__ (( always_inline )) void
+copy_from_real_librm ( void *dest, unsigned int src_seg,
+ unsigned int src_off, size_t n ) {
+ memcpy ( dest, VIRTUAL ( src_seg, src_off ), n );
+}
+#define put_real_librm( var, dest_seg, dest_off ) \
+ do { \
+ * ( ( typeof(var) * ) VIRTUAL ( dest_seg, dest_off ) ) = var; \
+ } while ( 0 )
+#define get_real_librm( var, src_seg, src_off ) \
+ do { \
+ var = * ( ( typeof(var) * ) VIRTUAL ( src_seg, src_off ) ); \
+ } while ( 0 )
+#define copy_to_real copy_to_real_librm
+#define copy_from_real copy_from_real_librm
+#define put_real put_real_librm
+#define get_real get_real_librm
+
+/**
+ * A pointer to a user buffer
+ *
+ * Even though we could just use a void *, we use an intptr_t so that
+ * attempts to use normal pointers show up as compiler warnings. Such
+ * code is actually valid for librm, but not for libkir (i.e. under
+ * KEEP_IT_REAL), so it's good to have the warnings even under librm.
+ */
+typedef intptr_t userptr_t;
+
+/**
+ * Add offset to user pointer
+ *
+ * @v ptr User pointer
+ * @v offset Offset
+ * @ret new_ptr New pointer value
+ */
+static inline __attribute__ (( always_inline )) userptr_t
+userptr_add ( userptr_t ptr, off_t offset ) {
+ return ( ptr + offset );
+}
+
+/**
+ * Copy data to user buffer
+ *
+ * @v buffer User buffer
+ * @v offset Offset within user buffer
+ * @v src Source
+ * @v len Length
+ */
+static inline __attribute__ (( always_inline )) void
+copy_to_user ( userptr_t buffer, off_t offset, const void *src, size_t len ) {
+ memcpy ( ( ( void * ) buffer + offset ), src, len );
+}
+
+/**
+ * Copy data from user buffer
+ *
+ * @v dest Destination
+ * @v buffer User buffer
+ * @v offset Offset within user buffer
+ * @v len Length
+ */
+static inline __attribute__ (( always_inline )) void
+copy_from_user ( void *dest, userptr_t buffer, off_t offset, size_t len ) {
+ memcpy ( dest, ( ( void * ) buffer + offset ), len );
+}
+
+/**
+ * Copy data between user buffers
+ *
+ * @v dest Destination user buffer
+ * @v dest_off Offset within destination buffer
+ * @v src Source user buffer
+ * @v src_off Offset within source buffer
+ * @v len Length
+ */
+static inline __attribute__ (( always_inline )) void
+memcpy_user ( userptr_t dest, off_t dest_off, userptr_t src, off_t src_off,
+ size_t len ) {
+ memcpy ( ( ( void * ) dest + dest_off ), ( ( void * ) src + src_off ),
+ len );
+}
+
+/**
+ * Copy data between user buffers, allowing for overlap
+ *
+ * @v dest Destination user buffer
+ * @v dest_off Offset within destination buffer
+ * @v src Source user buffer
+ * @v src_off Offset within source buffer
+ * @v len Length
+ */
+static inline __attribute__ (( always_inline )) void
+memmove_user ( userptr_t dest, off_t dest_off, userptr_t src, off_t src_off,
+ size_t len ) {
+ memmove ( ( ( void * ) dest + dest_off ), ( ( void * ) src + src_off ),
+ len );
+}
+
+/**
+ * Fill user buffer with a constant byte
+ *
+ * @v buffer User buffer
+ * @v offset Offset within buffer
+ * @v c Constant byte with which to fill
+ * @v len Length
+ */
+static inline __attribute__ (( always_inline )) void
+memset_user ( userptr_t buffer, off_t offset, int c, size_t len ) {
+ memset ( ( ( void * ) buffer + offset ), c, len );
+}
+
+/**
+ * Find length of NUL-terminated string in user buffer
+ *
+ * @v buffer User buffer
+ * @v offset Offset within buffer
+ * @ret len Length of string (excluding NUL)
+ */
+static inline __attribute__ (( always_inline )) size_t
+strlen_user ( userptr_t buffer, off_t offset ) {
+ return strlen ( ( void * ) buffer + offset );
+}
+
+/**
+ * Convert virtual address to user buffer
+ *
+ * @v virtual Virtual address
+ * @ret buffer User buffer
+ *
+ * This constructs a user buffer from an ordinary pointer. Use it
+ * when you need to pass a pointer to an internal buffer to a function
+ * that expects a @c userptr_t.
+ */
+static inline __attribute__ (( always_inline )) userptr_t
+virt_to_user ( void * virtual ) {
+ return ( ( intptr_t ) virtual );
+}
+
+/**
+ * Convert segment:offset address to user buffer
+ *
+ * @v segment Real-mode segment
+ * @v offset Real-mode offset
+ * @ret buffer User buffer
+ */
+static inline __attribute__ (( always_inline )) userptr_t
+real_to_user ( unsigned int segment, unsigned int offset ) {
+ return virt_to_user ( VIRTUAL ( segment, offset ) );
+}
+
+/**
+ * Convert physical address to user buffer
+ *
+ * @v physical Physical address
+ * @ret buffer User buffer
+ */
+static inline __attribute__ (( always_inline )) userptr_t
+phys_to_user ( physaddr_t physical ) {
+ return virt_to_user ( phys_to_virt ( physical ) );
+}
+
+/**
+ * Convert user buffer to physical address
+ *
+ * @v buffer User buffer
+ * @v offset Offset within user buffer
+ * @ret physical Physical address
+ */
+static inline __attribute__ (( always_inline )) physaddr_t
+user_to_phys ( userptr_t buffer, off_t offset ) {
+ return virt_to_phys ( ( void * ) buffer + offset );
+}
+
+/* TEXT16_CODE: declare a fragment of code that resides in .text16 */
+#define TEXT16_CODE( asm_code_str ) \
+ ".section \".text16\", \"ax\", @progbits\n\t" \
+ ".code16\n\t" \
+ asm_code_str "\n\t" \
+ ".code32\n\t" \
+ ".previous\n\t"
+
+/* REAL_CODE: declare a fragment of code that executes in real mode */
+#define REAL_CODE( asm_code_str ) \
+ "pushl $1f\n\t" \
+ "call real_call\n\t" \
+ "addl $4, %%esp\n\t" \
+ TEXT16_CODE ( "\n1:\n\t" \
+ asm_code_str \
+ "\n\t" \
+ "ret\n\t" )
+
+/* PHYS_CODE: declare a fragment of code that executes in flat physical mode */
+#define PHYS_CODE( asm_code_str ) \
+ "call _virt_to_phys\n\t" \
+ asm_code_str \
+ "call _phys_to_virt\n\t"
+
+#endif /* ASSEMBLY */
+
+#endif /* LIBRM_H */
diff --git a/gpxe/src/arch/i386/include/limits.h b/gpxe/src/arch/i386/include/limits.h
new file mode 100644
index 00000000..f13db267
--- /dev/null
+++ b/gpxe/src/arch/i386/include/limits.h
@@ -0,0 +1,59 @@
+#ifndef LIMITS_H
+#define LIMITS_H 1
+
+/* Number of bits in a `char' */
+#define CHAR_BIT 8
+
+/* Minimum and maximum values a `signed char' can hold */
+#define SCHAR_MIN (-128)
+#define SCHAR_MAX 127
+
+/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */
+#define UCHAR_MAX 255
+
+/* Minimum and maximum values a `char' can hold */
+#define CHAR_MIN SCHAR_MIN
+#define CHAR_MAX SCHAR_MAX
+
+/* Minimum and maximum values a `signed short int' can hold */
+#define SHRT_MIN (-32768)
+#define SHRT_MAX 32767
+
+/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */
+#define USHRT_MAX 65535
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MIN (-INT_MAX - 1)
+#define INT_MAX 2147483647
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MAX 2147483647
+#define INT_MIN (-INT_MAX - 1)
+
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+
+/* Minimum and maximum values a `signed long' can hold */
+#define LONG_MAX 2147483647
+#define LONG_MIN (-LONG_MAX - 1L)
+
+/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */
+#define ULONG_MAX 4294967295UL
+
+/* Minimum and maximum values a `signed long long' can hold */
+#define LLONG_MAX 9223372036854775807LL
+#define LLONG_MIN (-LONG_MAX - 1LL)
+
+
+/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */
+#define ULLONG_MAX 18446744073709551615ULL
+
+
+#endif /* LIMITS_H */
diff --git a/gpxe/src/arch/i386/include/memsizes.h b/gpxe/src/arch/i386/include/memsizes.h
new file mode 100644
index 00000000..6222fd66
--- /dev/null
+++ b/gpxe/src/arch/i386/include/memsizes.h
@@ -0,0 +1,17 @@
+#ifndef _MEMSIZES_H
+#define _MEMSIZES_H
+
+#include <basemem.h>
+
+/**
+ * Get size of base memory from BIOS free base memory counter
+ *
+ * @ret basemem Base memory size, in kB
+ */
+static inline unsigned int basememsize ( void ) {
+ return get_fbms();
+}
+
+extern unsigned int extmemsize ( void );
+
+#endif /* _MEMSIZES_H */
diff --git a/gpxe/src/arch/i386/include/multiboot.h b/gpxe/src/arch/i386/include/multiboot.h
new file mode 100644
index 00000000..4ca7089b
--- /dev/null
+++ b/gpxe/src/arch/i386/include/multiboot.h
@@ -0,0 +1,147 @@
+#ifndef _MULTIBOOT_H
+#define _MULTIBOOT_H
+
+/**
+ * @file
+ *
+ * Multiboot operating systems
+ *
+ */
+
+#include <stdint.h>
+
+/** The magic number for the Multiboot header */
+#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
+
+/** Boot modules must be page aligned */
+#define MB_FLAG_PGALIGN 0x00000001
+
+/** Memory map must be provided */
+#define MB_FLAG_MEMMAP 0x00000002
+
+/** Video mode information must be provided */
+#define MB_FLAG_VIDMODE 0x00000004
+
+/** Image is a raw multiboot image (not ELF) */
+#define MB_FLAG_RAW 0x00010000
+
+/**
+ * The magic number passed by a Multiboot-compliant boot loader
+ *
+ * Must be passed in register %eax when jumping to the Multiboot OS
+ * image.
+ */
+#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
+
+/** Multiboot information structure mem_* fields are valid */
+#define MBI_FLAG_MEM 0x00000001
+
+/** Multiboot information structure boot_device field is valid */
+#define MBI_FLAG_BOOTDEV 0x00000002
+
+/** Multiboot information structure cmdline field is valid */
+#define MBI_FLAG_CMDLINE 0x00000004
+
+/** Multiboot information structure module fields are valid */
+#define MBI_FLAG_MODS 0x00000008
+
+/** Multiboot information structure a.out symbol table is valid */
+#define MBI_FLAG_AOUT 0x00000010
+
+/** Multiboot information struture ELF section header table is valid */
+#define MBI_FLAG_ELF 0x00000020
+
+/** Multiboot information structure memory map is valid */
+#define MBI_FLAG_MMAP 0x00000040
+
+/** Multiboot information structure drive list is valid */
+#define MBI_FLAG_DRIVES 0x00000080
+
+/** Multiboot information structure ROM configuration field is valid */
+#define MBI_FLAG_CFGTBL 0x00000100
+
+/** Multiboot information structure boot loader name field is valid */
+#define MBI_FLAG_LOADER 0x00000200
+
+/** Multiboot information structure APM table is valid */
+#define MBI_FLAG_APM 0x00000400
+
+/** Multiboot information structure video information is valid */
+#define MBI_FLAG_VBE 0x00000800
+
+/** A multiboot header */
+struct multiboot_header {
+ uint32_t magic;
+ uint32_t flags;
+ uint32_t checksum;
+ uint32_t header_addr;
+ uint32_t load_addr;
+ uint32_t load_end_addr;
+ uint32_t bss_end_addr;
+ uint32_t entry_addr;
+} __attribute__ (( packed, may_alias ));
+
+/** A multiboot a.out symbol table */
+struct multiboot_aout_symbol_table {
+ uint32_t tabsize;
+ uint32_t strsize;
+ uint32_t addr;
+ uint32_t reserved;
+} __attribute__ (( packed, may_alias ));
+
+/** A multiboot ELF section header table */
+struct multiboot_elf_section_header_table {
+ uint32_t num;
+ uint32_t size;
+ uint32_t addr;
+ uint32_t shndx;
+} __attribute__ (( packed, may_alias ));
+
+/** A multiboot information structure */
+struct multiboot_info {
+ uint32_t flags;
+ uint32_t mem_lower;
+ uint32_t mem_upper;
+ uint32_t boot_device;
+ uint32_t cmdline;
+ uint32_t mods_count;
+ uint32_t mods_addr;
+ union {
+ struct multiboot_aout_symbol_table aout_syms;
+ struct multiboot_elf_section_header_table elf_sections;
+ } syms;
+ uint32_t mmap_length;
+ uint32_t mmap_addr;
+ uint32_t drives_length;
+ uint32_t drives_addr;
+ uint32_t config_table;
+ uint32_t boot_loader_name;
+ uint32_t apm_table;
+ uint32_t vbe_control_info;
+ uint32_t vbe_mode_info;
+ uint16_t vbe_mode;
+ uint16_t vbe_interface_seg;
+ uint16_t vbe_interface_off;
+ uint16_t vbe_interface_len;
+} __attribute__ (( packed, may_alias ));
+
+/** A multiboot module structure */
+struct multiboot_module {
+ uint32_t mod_start;
+ uint32_t mod_end;
+ uint32_t string;
+ uint32_t reserved;
+} __attribute__ (( packed, may_alias ));
+
+/** A multiboot memory map entry */
+struct multiboot_memory_map {
+ uint32_t size;
+ uint64_t base_addr;
+ uint64_t length;
+ uint32_t type;
+} __attribute__ (( packed, may_alias ));
+
+/** Usable RAM */
+#define MBMEM_RAM 1
+
+#endif /* _MULTIBOOT_H */
diff --git a/gpxe/src/arch/i386/include/pci_io.h b/gpxe/src/arch/i386/include/pci_io.h
new file mode 100644
index 00000000..4888d557
--- /dev/null
+++ b/gpxe/src/arch/i386/include/pci_io.h
@@ -0,0 +1,35 @@
+#ifndef _PCI_IO_H
+#define _PCI_IO_H
+
+#include <pcibios.h>
+#include <pcidirect.h>
+
+/** @file
+ *
+ * i386 PCI configuration space access
+ *
+ * We have two methods of PCI configuration space access: the PCI BIOS
+ * and direct Type 1 accesses. Selecting between them is via the
+ * compile-time switch -DCONFIG_PCI_DIRECT.
+ *
+ */
+
+#if CONFIG_PCI_DIRECT
+#define pci_max_bus pcidirect_max_bus
+#define pci_read_config_byte pcidirect_read_config_byte
+#define pci_read_config_word pcidirect_read_config_word
+#define pci_read_config_dword pcidirect_read_config_dword
+#define pci_write_config_byte pcidirect_write_config_byte
+#define pci_write_config_word pcidirect_write_config_word
+#define pci_write_config_dword pcidirect_write_config_dword
+#else /* CONFIG_PCI_DIRECT */
+#define pci_max_bus pcibios_max_bus
+#define pci_read_config_byte pcibios_read_config_byte
+#define pci_read_config_word pcibios_read_config_word
+#define pci_read_config_dword pcibios_read_config_dword
+#define pci_write_config_byte pcibios_write_config_byte
+#define pci_write_config_word pcibios_write_config_word
+#define pci_write_config_dword pcibios_write_config_dword
+#endif /* CONFIG_PCI_DIRECT */
+
+#endif /* _PCI_IO_H */
diff --git a/gpxe/src/arch/i386/include/pcibios.h b/gpxe/src/arch/i386/include/pcibios.h
new file mode 100644
index 00000000..3d08d135
--- /dev/null
+++ b/gpxe/src/arch/i386/include/pcibios.h
@@ -0,0 +1,122 @@
+#ifndef _PCIBIOS_H
+#define _PCIBIOS_H
+
+#include <stdint.h>
+
+/** @file
+ *
+ * PCI configuration space access via PCI BIOS
+ *
+ */
+
+struct pci_device;
+
+#define PCIBIOS_INSTALLATION_CHECK 0xb1010000
+#define PCIBIOS_READ_CONFIG_BYTE 0xb1080000
+#define PCIBIOS_READ_CONFIG_WORD 0xb1090000
+#define PCIBIOS_READ_CONFIG_DWORD 0xb10a0000
+#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b0000
+#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c0000
+#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d0000
+
+extern int pcibios_max_bus ( void );
+extern int pcibios_read ( struct pci_device *pci, uint32_t command,
+ uint32_t *value );
+extern int pcibios_write ( struct pci_device *pci, uint32_t command,
+ uint32_t value );
+
+/**
+ * Read byte from PCI configuration space via PCI BIOS
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value read
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcibios_read_config_byte ( struct pci_device *pci, unsigned int where,
+ uint8_t *value ) {
+ uint32_t tmp;
+ int rc;
+
+ rc = pcibios_read ( pci, PCIBIOS_READ_CONFIG_BYTE | where, &tmp );
+ *value = tmp;
+ return rc;
+}
+
+/**
+ * Read word from PCI configuration space via PCI BIOS
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value read
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcibios_read_config_word ( struct pci_device *pci, unsigned int where,
+ uint16_t *value ) {
+ uint32_t tmp;
+ int rc;
+
+ rc = pcibios_read ( pci, PCIBIOS_READ_CONFIG_WORD | where, &tmp );
+ *value = tmp;
+ return rc;
+}
+
+/**
+ * Read dword from PCI configuration space via PCI BIOS
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value read
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcibios_read_config_dword ( struct pci_device *pci, unsigned int where,
+ uint32_t *value ) {
+ return pcibios_read ( pci, PCIBIOS_READ_CONFIG_DWORD | where, value );
+}
+
+/**
+ * Write byte to PCI configuration space via PCI BIOS
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value to be written
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcibios_write_config_byte ( struct pci_device *pci, unsigned int where,
+ uint8_t value ) {
+ return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_BYTE | where, value );
+}
+
+/**
+ * Write word to PCI configuration space via PCI BIOS
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value to be written
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcibios_write_config_word ( struct pci_device *pci, unsigned int where,
+ uint16_t value ) {
+ return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_WORD | where, value );
+}
+
+/**
+ * Write dword to PCI configuration space via PCI BIOS
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value to be written
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcibios_write_config_dword ( struct pci_device *pci, unsigned int where,
+ uint32_t value ) {
+ return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_DWORD | where, value);
+}
+
+#endif /* _PCIBIOS_H */
diff --git a/gpxe/src/arch/i386/include/pcidirect.h b/gpxe/src/arch/i386/include/pcidirect.h
new file mode 100644
index 00000000..4e2e9d12
--- /dev/null
+++ b/gpxe/src/arch/i386/include/pcidirect.h
@@ -0,0 +1,126 @@
+#ifndef _PCIDIRECT_H
+#define _PCIDIRECT_H
+
+#include <stdint.h>
+#include <io.h>
+
+/** @file
+ *
+ * PCI configuration space access via Type 1 accesses
+ *
+ */
+
+#define PCIDIRECT_CONFIG_ADDRESS 0xcf8
+#define PCIDIRECT_CONFIG_DATA 0xcfc
+
+struct pci_device;
+
+extern void pcidirect_prepare ( struct pci_device *pci, int where );
+
+/**
+ * Determine maximum PCI bus number within system
+ *
+ * @ret max_bus Maximum bus number
+ */
+static inline int pcidirect_max_bus ( void ) {
+ /* No way to work this out via Type 1 accesses */
+ return 0xff;
+}
+
+/**
+ * Read byte from PCI configuration space via Type 1 access
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value read
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcidirect_read_config_byte ( struct pci_device *pci, unsigned int where,
+ uint8_t *value ) {
+ pcidirect_prepare ( pci, where );
+ *value = inb ( PCIDIRECT_CONFIG_DATA + ( where & 3 ) );
+ return 0;
+}
+
+/**
+ * Read word from PCI configuration space via Type 1 access
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value read
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcidirect_read_config_word ( struct pci_device *pci, unsigned int where,
+ uint16_t *value ) {
+ pcidirect_prepare ( pci, where );
+ *value = inw ( PCIDIRECT_CONFIG_DATA + ( where & 2 ) );
+ return 0;
+}
+
+/**
+ * Read dword from PCI configuration space via Type 1 access
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value read
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcidirect_read_config_dword ( struct pci_device *pci, unsigned int where,
+ uint32_t *value ) {
+ pcidirect_prepare ( pci, where );
+ *value = inl ( PCIDIRECT_CONFIG_DATA );
+ return 0;
+}
+
+/**
+ * Write byte to PCI configuration space via Type 1 access
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value to be written
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcidirect_write_config_byte ( struct pci_device *pci, unsigned int where,
+ uint8_t value ) {
+ pcidirect_prepare ( pci, where );
+ outb ( value, PCIDIRECT_CONFIG_DATA + ( where & 3 ) );
+ return 0;
+}
+
+/**
+ * Write word to PCI configuration space via Type 1 access
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value to be written
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcidirect_write_config_word ( struct pci_device *pci, unsigned int where,
+ uint16_t value ) {
+ pcidirect_prepare ( pci, where );
+ outw ( value, PCIDIRECT_CONFIG_DATA + ( where & 2 ) );
+ return 0;
+}
+
+/**
+ * Write dword to PCI configuration space via Type 1 access
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ * @v value Value to be written
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+pcidirect_write_config_dword ( struct pci_device *pci, unsigned int where,
+ uint32_t value ) {
+ pcidirect_prepare ( pci, where );
+ outl ( value, PCIDIRECT_CONFIG_DATA );
+ return 0;
+}
+
+#endif /* _PCIDIRECT_H */
diff --git a/gpxe/src/arch/i386/include/pic8259.h b/gpxe/src/arch/i386/include/pic8259.h
new file mode 100644
index 00000000..0c501a9c
--- /dev/null
+++ b/gpxe/src/arch/i386/include/pic8259.h
@@ -0,0 +1,69 @@
+/*
+ * Basic support for controlling the 8259 Programmable Interrupt Controllers.
+ *
+ * Initially written by Michael Brown (mcb30).
+ */
+
+#ifndef PIC8259_H
+#define PIC8259_H
+
+/* For segoff_t */
+#include "realmode.h"
+
+#define IRQ_PIC_CUTOFF 8
+
+/* 8259 register locations */
+#define PIC1_ICW1 0x20
+#define PIC1_OCW2 0x20
+#define PIC1_OCW3 0x20
+#define PIC1_ICR 0x20
+#define PIC1_IRR 0x20
+#define PIC1_ISR 0x20
+#define PIC1_ICW2 0x21
+#define PIC1_ICW3 0x21
+#define PIC1_ICW4 0x21
+#define PIC1_IMR 0x21
+#define PIC2_ICW1 0xa0
+#define PIC2_OCW2 0xa0
+#define PIC2_OCW3 0xa0
+#define PIC2_ICR 0xa0
+#define PIC2_IRR 0xa0
+#define PIC2_ISR 0xa0
+#define PIC2_ICW2 0xa1
+#define PIC2_ICW3 0xa1
+#define PIC2_ICW4 0xa1
+#define PIC2_IMR 0xa1
+
+/* Register command values */
+#define OCW3_ID 0x08
+#define OCW3_READ_IRR 0x03
+#define OCW3_READ_ISR 0x02
+#define ICR_EOI_NON_SPECIFIC 0x20
+#define ICR_EOI_NOP 0x40
+#define ICR_EOI_SPECIFIC 0x60
+#define ICR_EOI_SET_PRIORITY 0xc0
+
+/* Macros to enable/disable IRQs */
+#define IMR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_IMR : PIC2_IMR )
+#define IMR_BIT(x) ( 1 << ( (x) % IRQ_PIC_CUTOFF ) )
+#define irq_enabled(x) ( ( inb ( IMR_REG(x) ) & IMR_BIT(x) ) == 0 )
+#define enable_irq(x) outb ( inb( IMR_REG(x) ) & ~IMR_BIT(x), IMR_REG(x) )
+#define disable_irq(x) outb ( inb( IMR_REG(x) ) | IMR_BIT(x), IMR_REG(x) )
+
+/* Macros for acknowledging IRQs */
+#define ICR_REG( irq ) ( (irq) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR )
+#define ICR_VALUE( irq ) ( (irq) % IRQ_PIC_CUTOFF )
+#define CHAINED_IRQ 2
+
+/* Utility macros to convert IRQ numbers to INT numbers and INT vectors */
+#define IRQ_INT( irq ) ( ( ( (irq) - IRQ_PIC_CUTOFF ) ^ 0x70 ) & 0x7f )
+
+/* Other constants */
+#define IRQ_MAX 15
+#define IRQ_NONE -1U
+
+/* Function prototypes
+ */
+void send_eoi ( unsigned int irq );
+
+#endif /* PIC8259_H */
diff --git a/gpxe/src/arch/i386/include/pnpbios.h b/gpxe/src/arch/i386/include/pnpbios.h
new file mode 100644
index 00000000..ab31c699
--- /dev/null
+++ b/gpxe/src/arch/i386/include/pnpbios.h
@@ -0,0 +1,15 @@
+#ifndef _PNPBIOS_H
+#define _PNPBIOS_H
+
+/** @file
+ *
+ * PnP BIOS
+ *
+ */
+
+/* BIOS segment address */
+#define BIOS_SEG 0xf000
+
+extern int find_pnp_bios ( void );
+
+#endif /* _PNPBIOS_H */
diff --git a/gpxe/src/arch/i386/include/pxe_addr.h b/gpxe/src/arch/i386/include/pxe_addr.h
new file mode 100644
index 00000000..954551e8
--- /dev/null
+++ b/gpxe/src/arch/i386/include/pxe_addr.h
@@ -0,0 +1,17 @@
+/*
+ * Architecture-specific portion of pxe.h for Etherboot
+ *
+ * This file has to define the types SEGOFF16_t, SEGDESC_t and
+ * SEGSEL_t for use in other PXE structures. See pxe.h for details.
+ */
+
+#ifndef PXE_ADDR_H
+#define PXE_ADDR_H
+
+#define IS_NULL_SEGOFF16(x) ( ( (x).segment == 0 ) && ( (x).offset == 0 ) )
+#define SEGOFF16_TO_PTR(x) ( VIRTUAL( (x).segment, (x).offset ) )
+#define PTR_TO_SEGOFF16(ptr,segoff16) \
+ (segoff16).segment = SEGMENT(ptr); \
+ (segoff16).offset = OFFSET(ptr);
+
+#endif /* PXE_ADDR_H */
diff --git a/gpxe/src/arch/i386/include/pxe_call.h b/gpxe/src/arch/i386/include/pxe_call.h
new file mode 100644
index 00000000..dc585310
--- /dev/null
+++ b/gpxe/src/arch/i386/include/pxe_call.h
@@ -0,0 +1,34 @@
+#ifndef _PXE_CALL_H
+#define _PXE_CALL_H
+
+/** @file
+ *
+ * PXE API entry point
+ */
+
+#include <pxe_api.h>
+#include <realmode.h>
+
+/** PXE load address segment */
+#define PXE_LOAD_SEGMENT 0
+
+/** PXE load address offset */
+#define PXE_LOAD_OFFSET 0x7c00
+
+/** PXE physical load address */
+#define PXE_LOAD_PHYS ( ( PXE_LOAD_SEGMENT << 4 ) + PXE_LOAD_OFFSET )
+
+/** !PXE structure */
+extern struct s_PXE __text16 ( ppxe );
+#define ppxe __use_text16 ( ppxe )
+
+/** PXENV+ structure */
+extern struct s_PXENV __text16 ( pxenv );
+#define pxenv __use_text16 ( pxenv )
+
+extern void pxe_hook_int1a ( void );
+extern int pxe_unhook_int1a ( void );
+extern void pxe_init_structures ( void );
+extern int pxe_start_nbp ( void );
+
+#endif /* _PXE_CALL_H */
diff --git a/gpxe/src/arch/i386/include/realmode.h b/gpxe/src/arch/i386/include/realmode.h
new file mode 100644
index 00000000..5d3ddf50
--- /dev/null
+++ b/gpxe/src/arch/i386/include/realmode.h
@@ -0,0 +1,128 @@
+#ifndef REALMODE_H
+#define REALMODE_H
+
+#ifndef ASSEMBLY
+
+#include "stdint.h"
+#include "registers.h"
+#include "io.h"
+
+/*
+ * Data structures and type definitions
+ *
+ */
+
+/* Segment:offset structure. Note that the order within the structure
+ * is offset:segment.
+ */
+struct segoff {
+ uint16_t offset;
+ uint16_t segment;
+} __attribute__ (( packed ));
+
+typedef struct segoff segoff_t;
+
+/* Macro hackery needed to stringify bits of inline assembly */
+#define RM_XSTR(x) #x
+#define RM_STR(x) RM_XSTR(x)
+
+/* Drag in the selected real-mode transition library header */
+#ifdef KEEP_IT_REAL
+#include "libkir.h"
+#else
+#include "librm.h"
+#endif
+
+/*
+ * The API to some functions is identical between librm and libkir, so
+ * they are documented here, even though the prototypes are in librm.h
+ * and libkir.h.
+ *
+ */
+
+/*
+ * Declaration of variables in .data16
+ *
+ * To place a variable in the .data16 segment, declare it using the
+ * pattern:
+ *
+ * int __data16 ( foo );
+ * #define foo __use_data16 ( foo );
+ *
+ * extern uint32_t __data16 ( bar );
+ * #define bar __use_data16 ( bar );
+ *
+ * static long __data16 ( baz ) = 0xff000000UL;
+ * #define baz __use_data16 ( baz );
+ *
+ * i.e. take a normal declaration, add __data16() around the variable
+ * name, and add a line saying "#define <name> __use_data16 ( <name> )
+ *
+ * You can then access them just like any other variable, for example
+ *
+ * int x = foo + bar;
+ *
+ * This magic is achieved at a cost of only around 7 extra bytes per
+ * group of accesses to .data16 variables. When using KEEP_IT_REAL,
+ * there is no extra cost.
+ *
+ * You should place variables in .data16 when they need to be accessed
+ * by real-mode code. Real-mode assembly (e.g. as created by
+ * REAL_CODE()) can access these variables via the usual data segment.
+ * You can therefore write something like
+ *
+ * static uint16_t __data16 ( foo );
+ * #define foo __use_data16 ( foo )
+ *
+ * int bar ( void ) {
+ * __asm__ __volatile__ ( REAL_CODE ( "int $0xff\n\t"
+ * "movw %ax, foo" )
+ * : : );
+ * return foo;
+ * }
+ *
+ * Variables may also be placed in .text16 using __text16 and
+ * __use_text16. Some variables (e.g. chained interrupt vectors) fit
+ * most naturally in .text16; most should be in .data16.
+ *
+ * If you have only a pointer to a magic symbol within .data16 or
+ * .text16, rather than the symbol itself, you can attempt to extract
+ * the underlying symbol name using __from_data16() or
+ * __from_text16(). This is not for the faint-hearted; check the
+ * assembler output to make sure that it's doing the right thing.
+ */
+
+/*
+ * void copy_to_real ( uint16_t dest_seg, uint16_t dest_off,
+ * void *src, size_t n )
+ * void copy_from_real ( void *dest, uint16_t src_seg, uint16_t src_off,
+ * size_t n )
+ *
+ * These functions can be used to copy data to and from arbitrary
+ * locations in base memory.
+ */
+
+/*
+ * put_real ( variable, uint16_t dest_seg, uint16_t dest_off )
+ * get_real ( variable, uint16_t src_seg, uint16_t src_off )
+ *
+ * These macros can be used to read or write single variables to and
+ * from arbitrary locations in base memory. "variable" must be a
+ * variable of either 1, 2 or 4 bytes in length.
+ */
+
+/*
+ * REAL_CODE ( asm_code_str )
+ *
+ * This can be used in inline assembly to create a fragment of code
+ * that will execute in real mode. For example: to write a character
+ * to the BIOS console using INT 10, you would do something like:
+ *
+ * __asm__ __volatile__ ( REAL_CODE ( "int $0x16" )
+ * : "=a" ( character ) : "a" ( 0x0000 ) );
+ *
+ */
+
+#endif /* ASSEMBLY */
+
+#endif /* REALMODE_H */
diff --git a/gpxe/src/arch/i386/include/registers.h b/gpxe/src/arch/i386/include/registers.h
new file mode 100644
index 00000000..2b9b2b43
--- /dev/null
+++ b/gpxe/src/arch/i386/include/registers.h
@@ -0,0 +1,187 @@
+#ifndef REGISTERS_H
+#define REGISTERS_H
+
+/** @file
+ *
+ * i386 registers.
+ *
+ * This file defines data structures that allow easy access to i386
+ * register dumps.
+ *
+ */
+
+#include "compiler.h" /* for doxygen */
+#include "stdint.h"
+
+/**
+ * A 16-bit general register.
+ *
+ * This type encapsulates a 16-bit register such as %ax, %bx, %cx,
+ * %dx, %si, %di, %bp or %sp.
+ *
+ */
+typedef union {
+ struct {
+ union {
+ uint8_t l;
+ uint8_t byte;
+ };
+ uint8_t h;
+ } PACKED;
+ uint16_t word;
+} PACKED reg16_t;
+
+/**
+ * A 32-bit general register.
+ *
+ * This type encapsulates a 32-bit register such as %eax, %ebx, %ecx,
+ * %edx, %esi, %edi, %ebp or %esp.
+ *
+ */
+typedef union {
+ struct {
+ union {
+ uint8_t l;
+ uint8_t byte;
+ };
+ uint8_t h;
+ } PACKED;
+ uint16_t word;
+ uint32_t dword;
+} PACKED reg32_t;
+
+/**
+ * A 32-bit general register dump.
+ *
+ * This is the data structure that is created on the stack by the @c
+ * pushal instruction, and can be read back using the @c popal
+ * instruction.
+ *
+ */
+struct i386_regs {
+ union {
+ uint16_t di;
+ uint32_t edi;
+ };
+ union {
+ uint16_t si;
+ uint32_t esi;
+ };
+ union {
+ uint16_t bp;
+ uint32_t ebp;
+ };
+ union {
+ uint16_t sp;
+ uint32_t esp;
+ };
+ union {
+ struct {
+ uint8_t bl;
+ uint8_t bh;
+ } PACKED;
+ uint16_t bx;
+ uint32_t ebx;
+ };
+ union {
+ struct {
+ uint8_t dl;
+ uint8_t dh;
+ } PACKED;
+ uint16_t dx;
+ uint32_t edx;
+ };
+ union {
+ struct {
+ uint8_t cl;
+ uint8_t ch;
+ } PACKED;
+ uint16_t cx;
+ uint32_t ecx;
+ };
+ union {
+ struct {
+ uint8_t al;
+ uint8_t ah;
+ } PACKED;
+ uint16_t ax;
+ uint32_t eax;
+ };
+} PACKED;
+
+/**
+ * A segment register dump.
+ *
+ * The i386 has no equivalent of the @c pushal or @c popal
+ * instructions for the segment registers. We adopt the convention of
+ * always using the sequences
+ *
+ * @code
+ *
+ * pushw %gs ; pushw %fs ; pushw %es ; pushw %ds ; pushw %ss ; pushw %cs
+ *
+ * @endcode
+ *
+ * and
+ *
+ * @code
+ *
+ * addw $4, %sp ; popw %ds ; popw %es ; popw %fs ; popw %gs
+ *
+ * @endcode
+ *
+ * This is the data structure that is created and read back by these
+ * instruction sequences.
+ *
+ */
+struct i386_seg_regs {
+ uint16_t cs;
+ uint16_t ss;
+ uint16_t ds;
+ uint16_t es;
+ uint16_t fs;
+ uint16_t gs;
+} PACKED;
+
+/**
+ * A full register dump.
+ *
+ * This data structure is created by the instructions
+ *
+ * @code
+ *
+ * pushfl
+ * pushal
+ * pushw %gs ; pushw %fs ; pushw %es ; pushw %ds ; pushw %ss ; pushw %cs
+ *
+ * @endcode
+ *
+ * and can be read back using the instructions
+ *
+ * @code
+ *
+ * addw $4, %sp ; popw %ds ; popw %es ; popw %fs ; popw %gs
+ * popal
+ * popfl
+ *
+ * @endcode
+ *
+ * prot_call() and kir_call() create this data structure on the stack
+ * and pass in a pointer to this structure.
+ *
+ */
+struct i386_all_regs {
+ struct i386_seg_regs segs;
+ struct i386_regs regs;
+ uint32_t flags;
+} PACKED;
+
+/* Flags */
+#define CF ( 1 << 0 )
+#define PF ( 1 << 2 )
+#define AF ( 1 << 4 )
+#define ZF ( 1 << 6 )
+#define SF ( 1 << 7 )
+#define OF ( 1 << 11 )
+
+#endif /* REGISTERS_H */
diff --git a/gpxe/src/arch/i386/include/setjmp.h b/gpxe/src/arch/i386/include/setjmp.h
new file mode 100644
index 00000000..ed2be270
--- /dev/null
+++ b/gpxe/src/arch/i386/include/setjmp.h
@@ -0,0 +1,12 @@
+#ifndef ETHERBOOT_SETJMP_H
+#define ETHERBOOT_SETJMP_H
+
+
+/* Define a type for use by setjmp and longjmp */
+#define JBLEN 6
+typedef unsigned long jmp_buf[JBLEN];
+
+extern int setjmp (jmp_buf env);
+extern void longjmp (jmp_buf env, int val);
+
+#endif /* ETHERBOOT_SETJMP_H */
diff --git a/gpxe/src/arch/i386/include/smbios.h b/gpxe/src/arch/i386/include/smbios.h
new file mode 100644
index 00000000..821eda17
--- /dev/null
+++ b/gpxe/src/arch/i386/include/smbios.h
@@ -0,0 +1,51 @@
+#ifndef _SMBIOS_H
+#define _SMBIOS_H
+
+/** @file
+ *
+ * System Management BIOS
+ */
+
+#include <stdint.h>
+
+/** An SMBIOS structure header */
+struct smbios_header {
+ /** Type */
+ uint8_t type;
+ /** Length */
+ uint8_t length;
+ /** Handle */
+ uint16_t handle;
+} __attribute__ (( packed ));
+
+/** SMBIOS system information structure */
+struct smbios_system_information {
+ /** SMBIOS structure header */
+ struct smbios_header header;
+ /** Manufacturer string */
+ uint8_t manufacturer;
+ /** Product string */
+ uint8_t product;
+ /** Version string */
+ uint8_t version;
+ /** Serial number string */
+ uint8_t serial;
+ /** UUID */
+ uint8_t uuid[16];
+ /** Wake-up type */
+ uint8_t wakeup;
+} __attribute__ (( packed ));
+
+/** SMBIOS system information structure type */
+#define SMBIOS_TYPE_SYSTEM_INFORMATION 1
+
+struct smbios_strings;
+extern int find_smbios_structure ( unsigned int type,
+ void *structure, size_t length,
+ struct smbios_strings *strings );
+extern int find_smbios_string ( struct smbios_strings *strings,
+ unsigned int index,
+ char *buffer, size_t length );
+extern int smbios_get_uuid ( union uuid *uuid );
+
+#endif /* _SMBIOS_H */
diff --git a/gpxe/src/arch/i386/include/undi.h b/gpxe/src/arch/i386/include/undi.h
new file mode 100644
index 00000000..9936e17f
--- /dev/null
+++ b/gpxe/src/arch/i386/include/undi.h
@@ -0,0 +1,102 @@
+#ifndef _UNDI_H
+#define _UNDI_H
+
+/** @file
+ *
+ * UNDI driver
+ *
+ */
+
+#ifndef ASSEMBLY
+
+#include <gpxe/device.h>
+#include <pxe_types.h>
+
+/** An UNDI device
+ *
+ * This structure is used by assembly code as well as C; do not alter
+ * this structure without editing pxeprefix.S to match.
+ */
+struct undi_device {
+ /** PXENV+ structure address */
+ SEGOFF16_t pxenv;
+ /** !PXE structure address */
+ SEGOFF16_t ppxe;
+ /** Entry point */
+ SEGOFF16_t entry;
+ /** Return stack */
+ UINT16_t return_stack[3];
+ /** Return type */
+ UINT16_t return_type;
+ /** Free base memory after load */
+ UINT16_t fbms;
+ /** Free base memory prior to load */
+ UINT16_t restore_fbms;
+ /** PCI bus:dev.fn, or @c UNDI_NO_PCI_BUSDEVFN */
+ UINT16_t pci_busdevfn;
+ /** ISAPnP card select number, or @c UNDI_NO_ISAPNP_CSN */
+ UINT16_t isapnp_csn;
+ /** ISAPnP read port, or @c UNDI_NO_ISAPNP_READ_PORT */
+ UINT16_t isapnp_read_port;
+ /** PCI vendor ID
+ *
+ * Filled in only for the preloaded UNDI device by pxeprefix.S
+ */
+ UINT16_t pci_vendor;
+ /** PCI device ID
+ *
+ * Filled in only for the preloaded UNDI device by pxeprefix.S
+ */
+ UINT16_t pci_device;
+ /** Flags
+ *
+ * This is the bitwise OR of zero or more UNDI_FL_XXX
+ * constants.
+ */
+ UINT16_t flags;
+
+ /** Generic device */
+ struct device dev;
+ /** Driver-private data
+ *
+ * Use undi_set_drvdata() and undi_get_drvdata() to access this
+ * field.
+ */
+ void *priv;
+} __attribute__ (( packed ));
+
+/**
+ * Set UNDI driver-private data
+ *
+ * @v undi UNDI device
+ * @v priv Private data
+ */
+static inline void undi_set_drvdata ( struct undi_device *undi, void *priv ) {
+ undi->priv = priv;
+}
+
+/**
+ * Get UNDI driver-private data
+ *
+ * @v undi UNDI device
+ * @ret priv Private data
+ */
+static inline void * undi_get_drvdata ( struct undi_device *undi ) {
+ return undi->priv;
+}
+
+#endif /* ASSEMBLY */
+
+/** PCI bus:dev.fn field is invalid */
+#define UNDI_NO_PCI_BUSDEVFN 0xffff
+
+/** ISAPnP card select number field is invalid */
+#define UNDI_NO_ISAPNP_CSN 0xffff
+
+/** ISAPnP read port field is invalid */
+#define UNDI_NO_ISAPNP_READ_PORT 0xffff
+
+/** UNDI flag: START_UNDI has been called */
+#define UNDI_FL_STARTED 0x0001
+
+#endif /* _UNDI_H */
diff --git a/gpxe/src/arch/i386/include/undiload.h b/gpxe/src/arch/i386/include/undiload.h
new file mode 100644
index 00000000..bfc11874
--- /dev/null
+++ b/gpxe/src/arch/i386/include/undiload.h
@@ -0,0 +1,33 @@
+#ifndef _UNDILOAD_H
+#define _UNDILOAD_H
+
+/** @file
+ *
+ * UNDI load/unload
+ *
+ */
+
+struct undi_device;
+struct undi_rom;
+
+extern int undi_load ( struct undi_device *undi, struct undi_rom *undirom );
+extern int undi_unload ( struct undi_device *undi );
+
+/**
+ * Call UNDI loader to create a pixie
+ *
+ * @v undi UNDI device
+ * @v undirom UNDI ROM
+ * @v pci_busdevfn PCI bus:dev.fn
+ * @ret rc Return status code
+ */
+static inline int undi_load_pci ( struct undi_device *undi,
+ struct undi_rom *undirom,
+ unsigned int pci_busdevfn ) {
+ undi->pci_busdevfn = pci_busdevfn;
+ undi->isapnp_csn = UNDI_NO_ISAPNP_CSN;
+ undi->isapnp_read_port = UNDI_NO_ISAPNP_READ_PORT;
+ return undi_load ( undi, undirom );
+}
+
+#endif /* _UNDILOAD_H */
diff --git a/gpxe/src/arch/i386/include/undinet.h b/gpxe/src/arch/i386/include/undinet.h
new file mode 100644
index 00000000..1a4a385e
--- /dev/null
+++ b/gpxe/src/arch/i386/include/undinet.h
@@ -0,0 +1,15 @@
+#ifndef _UNDINET_H
+#define _UNDINET_H
+
+/** @file
+ *
+ * UNDI network device driver
+ *
+ */
+
+struct undi_device;
+
+extern int undinet_probe ( struct undi_device *undi );
+extern void undinet_remove ( struct undi_device *undi );
+
+#endif /* _UNDINET_H */
diff --git a/gpxe/src/arch/i386/include/undipreload.h b/gpxe/src/arch/i386/include/undipreload.h
new file mode 100644
index 00000000..d9bc8cb9
--- /dev/null
+++ b/gpxe/src/arch/i386/include/undipreload.h
@@ -0,0 +1,16 @@
+#ifndef _UNDIPRELOAD_H
+#define _UNDIPRELOAD_H
+
+/** @file
+ *
+ * Preloaded UNDI stack
+ *
+ */
+
+#include <realmode.h>
+#include <undi.h>
+
+extern struct undi_device __data16 ( preloaded_undi );
+#define preloaded_undi __use_data16 ( preloaded_undi )
+
+#endif /* _UNDIPRELOAD_H */
diff --git a/gpxe/src/arch/i386/include/undirom.h b/gpxe/src/arch/i386/include/undirom.h
new file mode 100644
index 00000000..a2636007
--- /dev/null
+++ b/gpxe/src/arch/i386/include/undirom.h
@@ -0,0 +1,51 @@
+#ifndef _UNDIROM_H
+#define _UNDIROM_H
+
+/** @file
+ *
+ * UNDI expansion ROMs
+ *
+ */
+
+#include <pxe_types.h>
+
+/** An UNDI PCI device ID */
+struct undi_pci_device_id {
+ /** PCI vendor ID */
+ unsigned int vendor_id;
+ /** PCI device ID */
+ unsigned int device_id;
+};
+
+/** An UNDI device ID */
+union undi_device_id {
+ /** PCI device ID */
+ struct undi_pci_device_id pci;
+};
+
+/** An UNDI ROM */
+struct undi_rom {
+ /** List of UNDI ROMs */
+ struct list_head list;
+ /** ROM segment address */
+ unsigned int rom_segment;
+ /** UNDI loader entry point */
+ SEGOFF16_t loader_entry;
+ /** Code segment size */
+ size_t code_size;
+ /** Data segment size */
+ size_t data_size;
+ /** Bus type
+ *
+ * Values are as used by @c PXENV_UNDI_GET_NIC_TYPE
+ */
+ unsigned int bus_type;
+ /** Device ID */
+ union undi_device_id bus_id;
+};
+
+extern struct undi_rom * undirom_find_pci ( unsigned int vendor_id,
+ unsigned int device_id,
+ unsigned int rombase );
+
+#endif /* _UNDIROM_H */
diff --git a/gpxe/src/arch/i386/include/vga.h b/gpxe/src/arch/i386/include/vga.h
new file mode 100644
index 00000000..01fc39d8
--- /dev/null
+++ b/gpxe/src/arch/i386/include/vga.h
@@ -0,0 +1,228 @@
+/*
+ *
+ * modified
+ * by Steve M. Gehlbach <steve@kesa.com>
+ *
+ * Originally from linux/drivers/video/vga16.c by
+ * Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
+ * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ */
+
+#ifndef VGA_H_INCL
+#define VGA_H_INCL 1
+
+//#include <cpu/p5/io.h>
+
+#define u8 unsigned char
+#define u16 unsigned short
+#define u32 unsigned int
+#define __u32 u32
+
+#define VERROR -1
+#define CHAR_HEIGHT 16
+#define LINES 25
+#define COLS 80
+
+// macros for writing to vga regs
+#define write_crtc(data,addr) outb(addr,CRT_IC); outb(data,CRT_DC)
+#define write_att(data,addr) inb(IS1_RC); inb(0x80); outb(addr,ATT_IW); inb(0x80); outb(data,ATT_IW); inb(0x80)
+#define write_seq(data,addr) outb(addr,SEQ_I); outb(data,SEQ_D)
+#define write_gra(data,addr) outb(addr,GRA_I); outb(data,GRA_D)
+u8 read_seq_b(u16 addr);
+u8 read_gra_b(u16 addr);
+u8 read_crtc_b(u16 addr);
+u8 read_att_b(u16 addr);
+
+
+#ifdef VGA_HARDWARE_FIXUP
+void vga_hardware_fixup(void);
+#else
+#define vga_hardware_fixup() do{} while(0)
+#endif
+
+#define SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */
+#define SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */
+#define SYNC_EXT 4 /* external sync */
+#define SYNC_COMP_HIGH_ACT 8 /* composite sync high active */
+#define SYNC_BROADCAST 16 /* broadcast video timings */
+ /* vtotal = 144d/288n/576i => PAL */
+ /* vtotal = 121d/242n/484i => NTSC */
+
+#define SYNC_ON_GREEN 32 /* sync on green */
+
+#define VMODE_NONINTERLACED 0 /* non interlaced */
+#define VMODE_INTERLACED 1 /* interlaced */
+#define VMODE_DOUBLE 2 /* double scan */
+#define VMODE_MASK 255
+
+#define VMODE_YWRAP 256 /* ywrap instead of panning */
+#define VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */
+#define VMODE_CONUPDATE 512 /* don't update x/yoffset */
+
+/* VGA data register ports */
+#define CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */
+#define CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */
+#define ATT_R 0x3C1 /* Attribute Controller Data Read Register */
+#define GRA_D 0x3CF /* Graphics Controller Data Register */
+#define SEQ_D 0x3C5 /* Sequencer Data Register */
+
+#define MIS_R 0x3CC // Misc Output Read Register
+#define MIS_W 0x3C2 // Misc Output Write Register
+
+#define IS1_RC 0x3DA /* Input Status Register 1 - color emulation */
+#define IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */
+#define PEL_D 0x3C9 /* PEL Data Register */
+#define PEL_MSK 0x3C6 /* PEL mask register */
+
+/* EGA-specific registers */
+#define GRA_E0 0x3CC /* Graphics enable processor 0 */
+#define GRA_E1 0x3CA /* Graphics enable processor 1 */
+
+
+/* VGA index register ports */
+#define CRT_IC 0x3D4 /* CRT Controller Index - color emulation */
+#define CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */
+#define ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */
+#define GRA_I 0x3CE /* Graphics Controller Index */
+#define SEQ_I 0x3C4 /* Sequencer Index */
+#define PEL_IW 0x3C8 /* PEL Write Index */
+#define PEL_IR 0x3C7 /* PEL Read Index */
+
+/* standard VGA indexes max counts */
+#define CRTC_C 25 /* 25 CRT Controller Registers sequentially set*/
+ // the remainder are not in the par array
+#define ATT_C 21 /* 21 Attribute Controller Registers */
+#define GRA_C 9 /* 9 Graphics Controller Registers */
+#define SEQ_C 5 /* 5 Sequencer Registers */
+#define MIS_C 1 /* 1 Misc Output Register */
+
+#define CRTC_H_TOTAL 0
+#define CRTC_H_DISP 1
+#define CRTC_H_BLANK_START 2
+#define CRTC_H_BLANK_END 3
+#define CRTC_H_SYNC_START 4
+#define CRTC_H_SYNC_END 5
+#define CRTC_V_TOTAL 6
+#define CRTC_OVERFLOW 7
+#define CRTC_PRESET_ROW 8
+#define CRTC_MAX_SCAN 9
+#define CRTC_CURSOR_START 0x0A
+#define CRTC_CURSOR_END 0x0B
+#define CRTC_START_HI 0x0C
+#define CRTC_START_LO 0x0D
+#define CRTC_CURSOR_HI 0x0E
+#define CRTC_CURSOR_LO 0x0F
+#define CRTC_V_SYNC_START 0x10
+#define CRTC_V_SYNC_END 0x11
+#define CRTC_V_DISP_END 0x12
+#define CRTC_OFFSET 0x13
+#define CRTC_UNDERLINE 0x14
+#define CRTC_V_BLANK_START 0x15
+#define CRTC_V_BLANK_END 0x16
+#define CRTC_MODE 0x17
+#define CRTC_LINE_COMPARE 0x18
+
+#define ATC_MODE 0x10
+#define ATC_OVERSCAN 0x11
+#define ATC_PLANE_ENABLE 0x12
+#define ATC_PEL 0x13
+#define ATC_COLOR_PAGE 0x14
+
+#define SEQ_CLOCK_MODE 0x01
+#define SEQ_PLANE_WRITE 0x02
+#define SEQ_CHARACTER_MAP 0x03
+#define SEQ_MEMORY_MODE 0x04
+
+#define GDC_SR_VALUE 0x00
+#define GDC_SR_ENABLE 0x01
+#define GDC_COMPARE_VALUE 0x02
+#define GDC_DATA_ROTATE 0x03
+#define GDC_PLANE_READ 0x04
+#define GDC_MODE 0x05
+#define GDC_MISC 0x06
+#define GDC_COMPARE_MASK 0x07
+#define GDC_BIT_MASK 0x08
+
+// text attributes
+#define VGA_ATTR_CLR_RED 0x4
+#define VGA_ATTR_CLR_GRN 0x2
+#define VGA_ATTR_CLR_BLU 0x1
+#define VGA_ATTR_CLR_YEL (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN)
+#define VGA_ATTR_CLR_CYN (VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
+#define VGA_ATTR_CLR_MAG (VGA_ATTR_CLR_BLU | VGA_ATTR_CLR_RED)
+#define VGA_ATTR_CLR_BLK 0
+#define VGA_ATTR_CLR_WHT (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
+#define VGA_ATTR_BNK 0x80
+#define VGA_ATTR_ITN 0x08
+
+/*
+ * vga register parameters
+ * these are copied to the
+ * registers.
+ *
+ */
+struct vga_par {
+ u8 crtc[CRTC_C];
+ u8 atc[ATT_C];
+ u8 gdc[GRA_C];
+ u8 seq[SEQ_C];
+ u8 misc; // the misc register, MIS_W
+ u8 vss;
+};
+
+
+/* Interpretation of offset for color fields: All offsets are from the right,
+ * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
+ * can use the offset as right argument to <<). A pixel afterwards is a bit
+ * stream and is written to video memory as that unmodified. This implies
+ * big-endian byte order if bits_per_pixel is greater than 8.
+ */
+struct fb_bitfield {
+ __u32 offset; /* beginning of bitfield */
+ __u32 length; /* length of bitfield */
+ __u32 msb_right; /* != 0 : Most significant bit is */
+ /* right */
+};
+
+struct screeninfo {
+ __u32 xres; /* visible resolution */
+ __u32 yres;
+ __u32 xres_virtual; /* virtual resolution */
+ __u32 yres_virtual;
+ __u32 xoffset; /* offset from virtual to visible */
+ __u32 yoffset; /* resolution */
+
+ __u32 bits_per_pixel; /* guess what */
+ __u32 grayscale; /* != 0 Graylevels instead of colors */
+
+ struct fb_bitfield red; /* bitfield in fb mem if true color, */
+ struct fb_bitfield green; /* else only length is significant */
+ struct fb_bitfield blue;
+ struct fb_bitfield transp; /* transparency */
+
+ __u32 nonstd; /* != 0 Non standard pixel format */
+
+ __u32 activate; /* see FB_ACTIVATE_* */
+
+ __u32 height; /* height of picture in mm */
+ __u32 width; /* width of picture in mm */
+
+ __u32 accel_flags; /* acceleration flags (hints) */
+
+ /* Timing: All values in pixclocks, except pixclock (of course) */
+ __u32 pixclock; /* pixel clock in ps (pico seconds) */
+ __u32 left_margin; /* time from sync to picture */
+ __u32 right_margin; /* time from picture to sync */
+ __u32 upper_margin; /* time from sync to picture */
+ __u32 lower_margin;
+ __u32 hsync_len; /* length of horizontal sync */
+ __u32 vsync_len; /* length of vertical sync */
+ __u32 sync; /* sync polarity */
+ __u32 vmode; /* interlaced etc */
+ __u32 reserved[6]; /* Reserved for future compatibility */
+};
+
+#endif
diff --git a/gpxe/src/arch/i386/include/virtaddr.h b/gpxe/src/arch/i386/include/virtaddr.h
new file mode 100644
index 00000000..f2ffa2a1
--- /dev/null
+++ b/gpxe/src/arch/i386/include/virtaddr.h
@@ -0,0 +1,105 @@
+#ifndef VIRTADDR_H
+#define VIRTADDR_H
+
+/* Segment selectors as used in our protected-mode GDTs.
+ *
+ * Don't change these unless you really know what you're doing.
+ */
+
+#define VIRTUAL_CS 0x08
+#define VIRTUAL_DS 0x10
+#define PHYSICAL_CS 0x18
+#define PHYSICAL_DS 0x20
+#define REAL_CS 0x28
+#define REAL_DS 0x30
+#if 0
+#define LONG_CS 0x38
+#define LONG_DS 0x40
+#endif
+
+#ifndef ASSEMBLY
+
+#include "stdint.h"
+#include "string.h"
+
+#ifndef KEEP_IT_REAL
+
+/*
+ * Without -DKEEP_IT_REAL, we are in 32-bit protected mode with a
+ * fixed link address but an unknown physical start address. Our GDT
+ * sets up code and data segments with an offset of virt_offset, so
+ * that link-time addresses can still work.
+ *
+ */
+
+/* C-callable function prototypes */
+
+extern void relocate_to ( uint32_t new_phys_addr );
+
+/* Variables in virtaddr.S */
+extern unsigned long virt_offset;
+
+/*
+ * Convert between virtual and physical addresses
+ *
+ */
+static inline unsigned long virt_to_phys ( volatile const void *virt_addr ) {
+ return ( ( unsigned long ) virt_addr ) + virt_offset;
+}
+
+static inline void * phys_to_virt ( unsigned long phys_addr ) {
+ return ( void * ) ( phys_addr - virt_offset );
+}
+
+static inline void copy_to_phys ( physaddr_t dest, const void *src,
+ size_t len ) {
+ memcpy ( phys_to_virt ( dest ), src, len );
+}
+
+static inline void copy_from_phys ( void *dest, physaddr_t src, size_t len ) {
+ memcpy ( dest, phys_to_virt ( src ), len );
+}
+
+static inline void copy_phys_to_phys ( physaddr_t dest, physaddr_t src,
+ size_t len ) {
+ memcpy ( phys_to_virt ( dest ), phys_to_virt ( src ), len );
+}
+
+#else /* KEEP_IT_REAL */
+
+/*
+ * With -DKEEP_IT_REAL, we are in 16-bit real mode with fixed link
+ * addresses and a segmented memory model. We have separate code and
+ * data segments.
+ *
+ * Because we may be called in 16-bit protected mode (damn PXE spec),
+ * we cannot simply assume that physical = segment * 16 + offset.
+ * Instead, we have to look up the physical start address of the
+ * segment in the !PXE structure. We have to assume that
+ * virt_to_phys() is called only on pointers within the data segment,
+ * because nothing passes segment information to us.
+ *
+ * We don't implement phys_to_virt at all, because there will be many
+ * addresses that simply cannot be reached via a virtual address when
+ * the virtual address space is limited to 64kB!
+ */
+
+static inline unsigned long virt_to_phys ( volatile const void *virt_addr ) {
+ /* Cheat: just for now, do the segment*16+offset calculation */
+ uint16_t ds;
+
+ __asm__ ( "movw %%ds, %%ax" : "=a" ( ds ) : );
+ return ( 16 * ds + ( ( unsigned long ) virt_addr ) );
+}
+
+/* Define it as a deprecated function so that we get compile-time
+ * warnings, rather than just the link-time errors.
+ */
+extern void * phys_to_virt ( unsigned long phys_addr )
+ __attribute__ ((deprecated));
+
+#endif /* KEEP_IT_REAL */
+
+#endif /* ASSEMBLY */
+
+#endif /* VIRTADDR_H */
diff --git a/gpxe/src/arch/i386/interface/pcbios/biosint.c b/gpxe/src/arch/i386/interface/pcbios/biosint.c
new file mode 100644
index 00000000..8ef2d7ab
--- /dev/null
+++ b/gpxe/src/arch/i386/interface/pcbios/biosint.c
@@ -0,0 +1,101 @@
+#include <errno.h>
+#include <realmode.h>
+#include <biosint.h>
+
+/**
+ * @file BIOS interrupts
+ *
+ */
+
+/**
+ * Hooked interrupt count
+ *
+ * At exit, after unhooking all possible interrupts, this counter
+ * should be examined. If it is non-zero, it means that we failed to
+ * unhook at least one interrupt vector, and so must not free up the
+ * memory we are using. (Note that this also implies that we should
+ * re-hook INT 15 in order to hide ourselves from the memory map).
+ */
+int hooked_bios_interrupts = 0;
+
+/**
+ * Hook INT vector
+ *
+ * @v interrupt INT number
+ * @v handler Offset within .text16 to interrupt handler
+ * @v chain_vector Vector for chaining to previous handler
+ *
+ * Hooks in an i386 INT handler. The handler itself must reside
+ * within the .text16 segment. @c chain_vector will be filled in with
+ * the address of the previously-installed handler for this interrupt;
+ * the handler should probably exit by ljmping via this vector.
+ */
+void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler,
+ struct segoff *chain_vector ) {
+ struct segoff vector = {
+ .segment = rm_cs,
+ .offset = handler,
+ };
+
+ DBG ( "Hooking INT %#02x to %04x:%04x\n",
+ interrupt, rm_cs, handler );
+
+ if ( ( chain_vector->segment != 0 ) ||
+ ( chain_vector->offset != 0 ) ) {
+ /* Already hooked; do nothing */
+ DBG ( "...already hooked\n" );
+ return;
+ }
+
+ copy_from_real ( chain_vector, 0, ( interrupt * 4 ),
+ sizeof ( *chain_vector ) );
+ DBG ( "...chaining to %04x:%04x\n",
+ chain_vector->segment, chain_vector->offset );
+ if ( DBG_LOG ) {
+ char code[64];
+ copy_from_real ( code, chain_vector->segment,
+ chain_vector->offset, sizeof ( code ) );
+ DBG_HDA ( *chain_vector, code, sizeof ( code ) );
+ }
+
+ copy_to_real ( 0, ( interrupt * 4 ), &vector, sizeof ( vector ) );
+ hooked_bios_interrupts++;
+}
+
+/**
+ * Unhook INT vector
+ *
+ * @v interrupt INT number
+ * @v handler Offset within .text16 to interrupt handler
+ * @v chain_vector Vector containing address of previous handler
+ *
+ * Unhooks an i386 interrupt handler hooked by hook_i386_vector().
+ * Note that this operation may fail, if some external code has hooked
+ * the vector since we hooked in our handler. If it fails, it means
+ * that it is not possible to unhook our handler, and we must leave it
+ * (and its chaining vector) resident in memory.
+ */
+int unhook_bios_interrupt ( unsigned int interrupt, unsigned int handler,
+ struct segoff *chain_vector ) {
+ struct segoff vector;
+
+ DBG ( "Unhooking INT %#02x from %04x:%04x\n",
+ interrupt, rm_cs, handler );
+
+ copy_from_real ( &vector, 0, ( interrupt * 4 ), sizeof ( vector ) );
+ if ( ( vector.segment != rm_cs ) || ( vector.offset != handler ) ) {
+ DBG ( "...cannot unhook; vector points to %04x:%04x\n",
+ vector.segment, vector.offset );
+ return -EBUSY;
+ }
+
+ DBG ( "...restoring to %04x:%04x\n",
+ chain_vector->segment, chain_vector->offset );
+ copy_to_real ( 0, ( interrupt * 4 ), chain_vector,
+ sizeof ( *chain_vector ) );
+
+ chain_vector->segment = 0;
+ chain_vector->offset = 0;
+ hooked_bios_interrupts--;
+ return 0;
+}
diff --git a/gpxe/src/arch/i386/interface/pcbios/int13.c b/gpxe/src/arch/i386/interface/pcbios/int13.c
new file mode 100644
index 00000000..7e09fb5f
--- /dev/null
+++ b/gpxe/src/arch/i386/interface/pcbios/int13.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <assert.h>
+#include <gpxe/list.h>
+#include <gpxe/blockdev.h>
+#include <gpxe/memmap.h>
+#include <realmode.h>
+#include <bios.h>
+#include <biosint.h>
+#include <bootsector.h>
+#include <int13.h>
+
+/** @file
+ *
+ * INT 13 emulation
+ *
+ * This module provides a mechanism for exporting block devices via
+ * the BIOS INT 13 disk interrupt interface.
+ *
+ */
+
+/** Vector for chaining to other INT 13 handlers */
+static struct segoff __text16 ( int13_vector );
+#define int13_vector __use_text16 ( int13_vector )
+
+/** Assembly wrapper */
+extern void int13_wrapper ( void );
+
+/** List of registered emulated drives */
+static LIST_HEAD ( drives );
+
+/**
+ * INT 13, 00 - Reset disk system
+ *
+ * @v drive Emulated drive
+ * @ret status Status code
+ */
+static int int13_reset ( struct int13_drive *drive __unused,
+ struct i386_all_regs *ix86 __unused ) {
+ DBG ( "Reset drive\n" );
+ return 0;
+}
+
+/**
+ * INT 13, 01 - Get status of last operation
+ *
+ * @v drive Emulated drive
+ * @ret status Status code
+ */
+static int int13_get_last_status ( struct int13_drive *drive,
+ struct i386_all_regs *ix86 __unused ) {
+ DBG ( "Get status of last operation\n" );
+ return drive->last_status;
+}
+
+/**
+ * Read / write sectors
+ *
+ * @v drive Emulated drive
+ * @v al Number of sectors to read or write (must be nonzero)
+ * @v ch Low bits of cylinder number
+ * @v cl (bits 7:6) High bits of cylinder number
+ * @v cl (bits 5:0) Sector number
+ * @v dh Head number
+ * @v es:bx Data buffer
+ * @v io Read / write method
+ * @ret status Status code
+ * @ret al Number of sectors read or written
+ */
+static int int13_rw_sectors ( struct int13_drive *drive,
+ struct i386_all_regs *ix86,
+ int ( * io ) ( struct block_device *blockdev,
+ uint64_t block,
+ unsigned long count,
+ userptr_t buffer ) ) {
+ struct block_device *blockdev = drive->blockdev;
+ unsigned int cylinder, head, sector;
+ unsigned long lba;
+ unsigned int count;
+ userptr_t buffer;
+
+ /* Validate blocksize */
+ if ( blockdev->blksize != INT13_BLKSIZE ) {
+ DBG ( "Invalid blocksize (%zd) for non-extended read/write\n",
+ blockdev->blksize );
+ return -INT13_STATUS_INVALID;
+ }
+
+ /* Calculate parameters */
+ cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 2 ) | ix86->regs.ch );
+ assert ( cylinder < drive->cylinders );
+ head = ix86->regs.dh;
+ assert ( head < drive->heads );
+ sector = ( ix86->regs.cl & 0x3f );
+ assert ( ( sector >= 1 ) && ( sector <= drive->sectors_per_track ) );
+ lba = ( ( ( ( cylinder * drive->heads ) + head )
+ * drive->sectors_per_track ) + sector - 1 );
+ count = ix86->regs.al;
+ buffer = real_to_user ( ix86->segs.es, ix86->regs.bx );
+
+ DBG ( "C/H/S %d/%d/%d = LBA %#lx <-> %04x:%04x (count %d)\n", cylinder,
+ head, sector, lba, ix86->segs.es, ix86->regs.bx, count );
+
+ /* Read from / write to block device */
+ if ( io ( blockdev, lba, count, buffer ) != 0 )
+ return -INT13_STATUS_READ_ERROR;
+
+ return 0;
+}
+
+/**
+ * INT 13, 02 - Read sectors
+ *
+ * @v drive Emulated drive
+ * @v al Number of sectors to read (must be nonzero)
+ * @v ch Low bits of cylinder number
+ * @v cl (bits 7:6) High bits of cylinder number
+ * @v cl (bits 5:0) Sector number
+ * @v dh Head number
+ * @v es:bx Data buffer
+ * @ret status Status code
+ * @ret al Number of sectors read
+ */
+static int int13_read_sectors ( struct int13_drive *drive,
+ struct i386_all_regs *ix86 ) {
+ DBG ( "Read: " );
+ return int13_rw_sectors ( drive, ix86, drive->blockdev->read );
+}
+
+/**
+ * INT 13, 03 - Write sectors
+ *
+ * @v drive Emulated drive
+ * @v al Number of sectors to write (must be nonzero)
+ * @v ch Low bits of cylinder number
+ * @v cl (bits 7:6) High bits of cylinder number
+ * @v cl (bits 5:0) Sector number
+ * @v dh Head number
+ * @v es:bx Data buffer
+ * @ret status Status code
+ * @ret al Number of sectors written
+ */
+static int int13_write_sectors ( struct int13_drive *drive,
+ struct i386_all_regs *ix86 ) {
+ DBG ( "Write: " );
+ return int13_rw_sectors ( drive, ix86, drive->blockdev->write );
+}
+
+/**
+ * INT 13, 08 - Get drive parameters
+ *
+ * @v drive Emulated drive
+ * @ret status Status code
+ * @ret ch Low bits of maximum cylinder number
+ * @ret cl (bits 7:6) High bits of maximum cylinder number
+ * @ret cl (bits 5:0) Maximum sector number
+ * @ret dh Maximum head number
+ * @ret dl Number of drives
+ */
+static int int13_get_parameters ( struct int13_drive *drive,
+ struct i386_all_regs *ix86 ) {
+ unsigned int max_cylinder = drive->cylinders - 1;
+ unsigned int max_head = drive->heads - 1;
+ unsigned int max_sector = drive->sectors_per_track; /* sic */
+
+ DBG ( "Get drive parameters\n" );
+
+ ix86->regs.ch = ( max_cylinder & 0xff );
+ ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector );
+ ix86->regs.dh = max_head;
+ get_real ( ix86->regs.dl, BDA_SEG, BDA_NUM_DRIVES );
+ return 0;
+}
+
+/**
+ * INT 13, 15 - Get disk type
+ *
+ * @v drive Emulated drive
+ * @ret ah Type code
+ * @ret cx:dx Sector count
+ * @ret status Status code / disk type
+ */
+static int int13_get_disk_type ( struct int13_drive *drive,
+ struct i386_all_regs *ix86 ) {
+ DBG ( "Get disk type\n" );
+ ix86->regs.cx = ( drive->cylinders >> 16 );
+ ix86->regs.dx = ( drive->cylinders & 0xffff );
+ return INT13_DISK_TYPE_HDD;
+}
+
+/**
+ * INT 13, 41 - Extensions installation check
+ *
+ * @v drive Emulated drive
+ * @v bx 0x55aa
+ * @ret bx 0xaa55
+ * @ret cx Extensions API support bitmap
+ * @ret status Status code / API version
+ */
+static int int13_extension_check ( struct int13_drive *drive __unused,
+ struct i386_all_regs *ix86 ) {
+ if ( ix86->regs.bx == 0x55aa ) {
+ DBG ( "INT 13 extensions installation check\n" );
+ ix86->regs.bx = 0xaa55;
+ ix86->regs.cx = INT13_EXTENSION_LINEAR;
+ return INT13_EXTENSION_VER_1_X;
+ } else {
+ return -INT13_STATUS_INVALID;
+ }
+}
+
+/**
+ * Extended read / write
+ *
+ * @v drive Emulated drive
+ * @v ds:si Disk address packet
+ * @v io Read / write method
+ * @ret status Status code
+ */
+static int int13_extended_rw ( struct int13_drive *drive,
+ struct i386_all_regs *ix86,
+ int ( * io ) ( struct block_device *blockdev,
+ uint64_t block,
+ unsigned long count,
+ userptr_t buffer ) ) {
+ struct block_device *blockdev = drive->blockdev;
+ struct int13_disk_address addr;
+ uint64_t lba;
+ unsigned long count;
+ userptr_t buffer;
+
+ /* Read parameters from disk address structure */
+ copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, sizeof ( addr ));
+ lba = addr.lba;
+ count = addr.count;
+ buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset );
+
+ DBG ( "LBA %#llx <-> %04x:%04x (count %ld)\n", (unsigned long long)lba,
+ addr.buffer.segment, addr.buffer.offset, count );
+
+ /* Read from / write to block device */
+ if ( io ( blockdev, lba, count, buffer ) != 0 )
+ return -INT13_STATUS_READ_ERROR;
+
+ return 0;
+}
+
+/**
+ * INT 13, 42 - Extended read
+ *
+ * @v drive Emulated drive
+ * @v ds:si Disk address packet
+ * @ret status Status code
+ */
+static int int13_extended_read ( struct int13_drive *drive,
+ struct i386_all_regs *ix86 ) {
+ DBG ( "Extended read: " );
+ return int13_extended_rw ( drive, ix86, drive->blockdev->read );
+}
+
+/**
+ * INT 13, 43 - Extended write
+ *
+ * @v drive Emulated drive
+ * @v ds:si Disk address packet
+ * @ret status Status code
+ */
+static int int13_extended_write ( struct int13_drive *drive,
+ struct i386_all_regs *ix86 ) {
+ DBG ( "Extended write: " );
+ return int13_extended_rw ( drive, ix86, drive->blockdev->write );
+}
+
+/**
+ * INT 13, 48 - Get extended parameters
+ *
+ * @v drive Emulated drive
+ * @v ds:si Drive parameter table
+ * @ret status Status code
+ */
+static int int13_get_extended_parameters ( struct int13_drive *drive,
+ struct i386_all_regs *ix86 ) {
+ struct int13_disk_parameters params = {
+ .bufsize = sizeof ( params ),
+ .flags = INT13_FL_DMA_TRANSPARENT,
+ .cylinders = drive->cylinders,
+ .heads = drive->heads,
+ .sectors_per_track = drive->sectors_per_track,
+ .sectors = drive->blockdev->blocks,
+ .sector_size = drive->blockdev->blksize,
+ };
+
+ DBG ( "Get extended drive parameters to %04x:%04x\n",
+ ix86->segs.ds, ix86->regs.si );
+
+ copy_to_real ( ix86->segs.ds, ix86->regs.si, &params,
+ sizeof ( params ) );
+ return 0;
+}
+
+/**
+ * INT 13 handler
+ *
+ */
+static __cdecl void int13 ( struct i386_all_regs *ix86 ) {
+ int command = ix86->regs.ah;
+ unsigned int bios_drive = ix86->regs.dl;
+ unsigned int original_bios_drive = bios_drive;
+ struct int13_drive *drive;
+ int status;
+
+ list_for_each_entry ( drive, &drives, list ) {
+ if ( drive->drive > bios_drive )
+ continue;
+ if ( drive->drive < bios_drive ) {
+ original_bios_drive--;
+ continue;
+ }
+
+ DBG ( "INT 13,%04x (%02x): ", ix86->regs.ax, drive->drive );
+
+ switch ( command ) {
+ case INT13_RESET:
+ status = int13_reset ( drive, ix86 );
+ break;
+ case INT13_GET_LAST_STATUS:
+ status = int13_get_last_status ( drive, ix86 );
+ break;
+ case INT13_READ_SECTORS:
+ status = int13_read_sectors ( drive, ix86 );
+ break;
+ case INT13_WRITE_SECTORS:
+ status = int13_write_sectors ( drive, ix86 );
+ break;
+ case INT13_GET_PARAMETERS:
+ status = int13_get_parameters ( drive, ix86 );
+ break;
+ case INT13_GET_DISK_TYPE:
+ status = int13_get_disk_type ( drive, ix86 );
+ break;
+ case INT13_EXTENSION_CHECK:
+ status = int13_extension_check ( drive, ix86 );
+ break;
+ case INT13_EXTENDED_READ:
+ status = int13_extended_read ( drive, ix86 );
+ break;
+ case INT13_EXTENDED_WRITE:
+ status = int13_extended_write ( drive, ix86 );
+ break;
+ case INT13_GET_EXTENDED_PARAMETERS:
+ status = int13_get_extended_parameters ( drive, ix86 );
+ break;
+ default:
+ DBG ( "*** Unrecognised INT 13 ***\n" );
+ status = -INT13_STATUS_INVALID;
+ break;
+ }
+
+ /* Store status for INT 13,01 */
+ drive->last_status = status;
+
+ /* Negative status indicates an error */
+ if ( status < 0 ) {
+ status = -status;
+ DBG ( "INT13 failed with status %x\n", status );
+ } else {
+ ix86->flags &= ~CF;
+ }
+ ix86->regs.ah = status;
+
+ /* Set OF to indicate to wrapper not to chain this call */
+ ix86->flags |= OF;
+
+ return;
+ }
+
+ /* Remap BIOS drive */
+ if ( bios_drive != original_bios_drive ) {
+ DBG ( "INT 13,%04x (%02x) remapped to (%02x)\n",
+ ix86->regs.ax, bios_drive, original_bios_drive );
+ }
+ ix86->regs.dl = original_bios_drive;
+}
+
+/**
+ * Hook INT 13 handler
+ *
+ */
+static void hook_int13 ( void ) {
+ /* Assembly wrapper to call int13(). int13() sets OF if we
+ * should not chain to the previous handler. (The wrapper
+ * clears CF and OF before calling int13()).
+ */
+ __asm__ __volatile__ (
+ TEXT16_CODE ( "\nint13_wrapper:\n\t"
+ /* Preserve %ax and %dx for future reference */
+ "pushw %%bp\n\t"
+ "movw %%sp, %%bp\n\t"
+ "pushw %%ax\n\t"
+ "pushw %%dx\n\t"
+ /* Clear OF, set CF, call int13() */
+ "orb $0, %%al\n\t"
+ "stc\n\t"
+ "pushl %0\n\t"
+ "pushw %%cs\n\t"
+ "call prot_call\n\t"
+ /* Chain if OF not set */
+ "jo 1f\n\t"
+ "pushfw\n\t"
+ "lcall *%%cs:int13_vector\n\t"
+ "\n1:\n\t"
+ /* Overwrite flags for iret */
+ "pushfw\n\t"
+ "popw 6(%%bp)\n\t"
+ /* Fix up %dl:
+ *
+ * INT 13,15 : do nothing
+ * INT 13,08 : load with number of drives
+ * all others: restore original value
+ */
+ "cmpb $0x15, -1(%%bp)\n\t"
+ "je 2f\n\t"
+ "movb -4(%%bp), %%dl\n\t"
+ "cmpb $0x08, -1(%%bp)\n\t"
+ "jne 2f\n\t"
+ "pushw %%ds\n\t"
+ "pushw %1\n\t"
+ "popw %%ds\n\t"
+ "movb %c2, %%dl\n\t"
+ "popw %%ds\n\t"
+ /* Return */
+ "\n2:\n\t"
+ "movw %%bp, %%sp\n\t"
+ "popw %%bp\n\t"
+ "iret\n\t" )
+ : : "i" ( int13 ), "i" ( BDA_SEG ), "i" ( BDA_NUM_DRIVES ) );
+
+ hook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
+ &int13_vector );
+}
+
+/**
+ * Unhook INT 13 handler
+ */
+static void unhook_int13 ( void ) {
+ unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
+ &int13_vector );
+}
+
+/**
+ * Guess INT 13 drive geometry
+ *
+ * @v drive Emulated drive
+ *
+ * Guesses the drive geometry by inspecting the partition table.
+ */
+static void guess_int13_geometry ( struct int13_drive *drive ) {
+ struct master_boot_record mbr;
+ struct partition_table_entry *partition;
+ unsigned int guessed_heads = 255;
+ unsigned int guessed_sectors_per_track = 63;
+ unsigned long blocks;
+ unsigned long blocks_per_cyl;
+ unsigned int i;
+
+ /* Don't even try when the blksize is invalid for C/H/S access */
+ if ( drive->blockdev->blksize != INT13_BLKSIZE )
+ return;
+
+ /* Scan through partition table and modify guesses for heads
+ * and sectors_per_track if we find any used partitions.
+ */
+ if ( drive->blockdev->read ( drive->blockdev, 0, 1,
+ virt_to_user ( &mbr ) ) == 0 ) {
+ for ( i = 0 ; i < 4 ; i++ ) {
+ partition = &mbr.partitions[i];
+ if ( ! partition->type )
+ continue;
+ guessed_heads =
+ ( PART_HEAD ( partition->chs_end ) + 1 );
+ guessed_sectors_per_track =
+ PART_SECTOR ( partition->chs_end );
+ DBG ( "Guessing C/H/S xx/%d/%d based on partition "
+ "%d\n", guessed_heads,
+ guessed_sectors_per_track, ( i + 1 ) );
+ }
+ } else {
+ DBG ( "Could not read partition table to guess geometry\n" );
+ }
+
+ /* Apply guesses if no geometry already specified */
+ if ( ! drive->heads )
+ drive->heads = guessed_heads;
+ if ( ! drive->sectors_per_track )
+ drive->sectors_per_track = guessed_sectors_per_track;
+ if ( ! drive->cylinders ) {
+ /* Avoid attempting a 64-bit divide on a 32-bit system */
+ blocks = ( ( drive->blockdev->blocks <= ULONG_MAX ) ?
+ drive->blockdev->blocks : ULONG_MAX );
+ blocks_per_cyl = ( drive->heads * drive->sectors_per_track );
+ assert ( blocks_per_cyl != 0 );
+ drive->cylinders = ( blocks / blocks_per_cyl );
+ if ( drive->cylinders > 1024 )
+ drive->cylinders = 1024;
+ }
+}
+
+/**
+ * Register INT 13 emulated drive
+ *
+ * @v drive Emulated drive
+ *
+ * Registers the drive with the INT 13 emulation subsystem, and hooks
+ * the INT 13 interrupt vector (if not already hooked).
+ *
+ * The underlying block device must be valid. A drive number and
+ * geometry will be assigned if left blank.
+ */
+void register_int13_drive ( struct int13_drive *drive ) {
+ uint8_t num_drives;
+
+ /* Give drive a default geometry if none specified */
+ guess_int13_geometry ( drive );
+
+ /* Assign drive number if none specified, update BIOS drive count */
+ get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
+ if ( ( drive->drive & 0xff ) == 0xff )
+ drive->drive = num_drives;
+ drive->drive |= 0x80;
+ num_drives++;
+ if ( num_drives <= ( drive->drive & 0x7f ) )
+ num_drives = ( ( drive->drive & 0x7f ) + 1 );
+ put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
+
+ DBG ( "Registered INT13 drive %02x with C/H/S geometry %d/%d/%d\n",
+ drive->drive, drive->cylinders, drive->heads,
+ drive->sectors_per_track );
+
+ /* Hook INT 13 vector if not already hooked */
+ if ( list_empty ( &drives ) )
+ hook_int13();
+
+ /* Add to list of emulated drives */
+ list_add ( &drive->list, &drives );
+}
+
+/**
+ * Unregister INT 13 emulated drive
+ *
+ * @v drive Emulated drive
+ *
+ * Unregisters the drive from the INT 13 emulation subsystem. If this
+ * is the last emulated drive, the INT 13 vector is unhooked (if
+ * possible).
+ */
+void unregister_int13_drive ( struct int13_drive *drive ) {
+ /* Remove from list of emulated drives */
+ list_del ( &drive->list );
+
+ /* Should adjust BIOS drive count, but it's difficult to do so
+ * reliably.
+ */
+
+ DBG ( "Unregistered INT13 drive %02x\n", drive->drive );
+
+ /* Unhook INT 13 vector if no more drives */
+ if ( list_empty ( &drives ) )
+ unhook_int13();
+}
+
+/**
+ * Attempt to boot from an INT 13 drive
+ *
+ * @v drive Drive number
+ * @ret rc Return status code
+ *
+ * This boots from the specified INT 13 drive by loading the Master
+ * Boot Record to 0000:7c00 and jumping to it. INT 18 is hooked to
+ * capture an attempt by the MBR to boot the next device. (This is
+ * the closest thing to a return path from an MBR).
+ *
+ * Note that this function can never return success, by definition.
+ */
+int int13_boot ( unsigned int drive ) {
+ struct memory_map memmap;
+ int status, signature;
+ int discard_c, discard_d;
+ int rc;
+
+ DBG ( "Booting from INT 13 drive %02x\n", drive );
+
+ /* Use INT 13 to read the boot sector */
+ __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
+ "pushw $0\n\t"
+ "popw %%es\n\t"
+ "stc\n\t"
+ "sti\n\t"
+ "int $0x13\n\t"
+ "sti\n\t" /* BIOS bugs */
+ "jc 1f\n\t"
+ "xorl %%eax, %%eax\n\t"
+ "\n1:\n\t"
+ "movzwl %%es:0x7dfe, %%ebx\n\t"
+ "popw %%es\n\t" )
+ : "=a" ( status ), "=b" ( signature ),
+ "=c" ( discard_c ), "=d" ( discard_d )
+ : "a" ( 0x0201 ), "b" ( 0x7c00 ),
+ "c" ( 1 ), "d" ( drive ) );
+ if ( status )
+ return -EIO;
+
+ /* Check signature is correct */
+ if ( signature != be16_to_cpu ( 0x55aa ) ) {
+ DBG ( "Invalid disk signature %#04x (should be 0x55aa)\n",
+ cpu_to_be16 ( signature ) );
+ return -ENOEXEC;
+ }
+
+ /* Dump out memory map prior to boot, if memmap debugging is
+ * enabled. Not required for program flow, but we have so
+ * many problems that turn out to be memory-map related that
+ * it's worth doing.
+ */
+ get_memmap ( &memmap );
+
+ /* Jump to boot sector */
+ if ( ( rc = call_bootsector ( 0x0, 0x7c00, drive ) ) != 0 ) {
+ DBG ( "INT 13 drive %02x boot returned\n", drive );
+ return rc;
+ }
+
+ return -ECANCELED; /* -EIMPOSSIBLE */
+}
diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_call.c b/gpxe/src/arch/i386/interface/pxe/pxe_call.c
new file mode 100644
index 00000000..5b307d40
--- /dev/null
+++ b/gpxe/src/arch/i386/interface/pxe/pxe_call.c
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/uaccess.h>
+#include <registers.h>
+#include <biosint.h>
+#include <pxe.h>
+#include <pxe_call.h>
+
+/** @file
+ *
+ * PXE API entry point
+ */
+
+/** Vector for chaining INT 1A */
+extern struct segoff __text16 ( pxe_int_1a_vector );
+#define pxe_int_1a_vector __use_text16 ( pxe_int_1a_vector )
+
+/** INT 1A handler */
+extern void pxe_int_1a ( void );
+
+/** A function pointer to hold any PXE API call
+ *
+ * Used by pxe_api_call() to avoid large swathes of duplicated code.
+ */
+union pxenv_call {
+ PXENV_EXIT_t ( * any ) ( union u_PXENV_ANY * );
+ PXENV_EXIT_t ( * unknown ) ( struct s_PXENV_UNKNOWN * );
+ PXENV_EXIT_t ( * unload_stack ) ( struct s_PXENV_UNLOAD_STACK * );
+ PXENV_EXIT_t ( * get_cached_info )
+ ( struct s_PXENV_GET_CACHED_INFO * );
+ PXENV_EXIT_t ( * restart_tftp ) ( struct s_PXENV_TFTP_READ_FILE * );
+ PXENV_EXIT_t ( * start_undi ) ( struct s_PXENV_START_UNDI * );
+ PXENV_EXIT_t ( * stop_undi ) ( struct s_PXENV_STOP_UNDI * );
+ PXENV_EXIT_t ( * start_base ) ( struct s_PXENV_START_BASE * );
+ PXENV_EXIT_t ( * stop_base ) ( struct s_PXENV_STOP_BASE * );
+ PXENV_EXIT_t ( * tftp_open ) ( struct s_PXENV_TFTP_OPEN * );
+ PXENV_EXIT_t ( * tftp_close ) ( struct s_PXENV_TFTP_CLOSE * );
+ PXENV_EXIT_t ( * tftp_read ) ( struct s_PXENV_TFTP_READ * );
+ PXENV_EXIT_t ( * tftp_read_file ) ( struct s_PXENV_TFTP_READ_FILE * );
+ PXENV_EXIT_t ( * tftp_get_fsize ) ( struct s_PXENV_TFTP_GET_FSIZE * );
+ PXENV_EXIT_t ( * udp_open ) ( struct s_PXENV_UDP_OPEN * );
+ PXENV_EXIT_t ( * udp_close ) ( struct s_PXENV_UDP_CLOSE * );
+ PXENV_EXIT_t ( * udp_write ) ( struct s_PXENV_UDP_WRITE * );
+ PXENV_EXIT_t ( * udp_read ) ( struct s_PXENV_UDP_READ * );
+ PXENV_EXIT_t ( * undi_startup ) ( struct s_PXENV_UNDI_STARTUP * );
+ PXENV_EXIT_t ( * undi_cleanup ) ( struct s_PXENV_UNDI_CLEANUP * );
+ PXENV_EXIT_t ( * undi_initialize )
+ ( struct s_PXENV_UNDI_INITIALIZE * );
+ PXENV_EXIT_t ( * undi_reset_adapter ) ( struct s_PXENV_UNDI_RESET * );
+ PXENV_EXIT_t ( * undi_shutdown ) ( struct s_PXENV_UNDI_SHUTDOWN * );
+ PXENV_EXIT_t ( * undi_open ) ( struct s_PXENV_UNDI_OPEN * );
+ PXENV_EXIT_t ( * undi_close ) ( struct s_PXENV_UNDI_CLOSE * );
+ PXENV_EXIT_t ( * undi_transmit ) ( struct s_PXENV_UNDI_TRANSMIT * );
+ PXENV_EXIT_t ( * undi_set_mcast_address )
+ ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS * );
+ PXENV_EXIT_t ( * undi_set_station_address )
+ ( struct s_PXENV_UNDI_SET_STATION_ADDRESS * );
+ PXENV_EXIT_t ( * undi_set_packet_filter )
+ ( struct s_PXENV_UNDI_SET_PACKET_FILTER * );
+ PXENV_EXIT_t ( * undi_get_information )
+ ( struct s_PXENV_UNDI_GET_INFORMATION * );
+ PXENV_EXIT_t ( * undi_get_statistics )
+ ( struct s_PXENV_UNDI_GET_STATISTICS * );
+ PXENV_EXIT_t ( * undi_clear_statistics )
+ ( struct s_PXENV_UNDI_CLEAR_STATISTICS * );
+ PXENV_EXIT_t ( * undi_initiate_diags )
+ ( struct s_PXENV_UNDI_INITIATE_DIAGS * );
+ PXENV_EXIT_t ( * undi_force_interrupt )
+ ( struct s_PXENV_UNDI_FORCE_INTERRUPT * );
+ PXENV_EXIT_t ( * undi_get_mcast_address )
+ ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS * );
+ PXENV_EXIT_t ( * undi_get_nic_type )
+ ( struct s_PXENV_UNDI_GET_NIC_TYPE * );
+ PXENV_EXIT_t ( * undi_get_iface_info )
+ ( struct s_PXENV_UNDI_GET_IFACE_INFO * );
+ PXENV_EXIT_t ( * undi_get_state ) ( struct s_PXENV_UNDI_GET_STATE * );
+ PXENV_EXIT_t ( * undi_isr ) ( struct s_PXENV_UNDI_ISR * );
+ PXENV_EXIT_t ( * file_open ) ( struct s_PXENV_FILE_OPEN * );
+ PXENV_EXIT_t ( * file_close ) ( struct s_PXENV_FILE_CLOSE * );
+ PXENV_EXIT_t ( * file_select ) ( struct s_PXENV_FILE_SELECT * );
+ PXENV_EXIT_t ( * file_read ) ( struct s_PXENV_FILE_READ * );
+ PXENV_EXIT_t ( * get_file_size ) ( struct s_PXENV_GET_FILE_SIZE * );
+ PXENV_EXIT_t ( * file_exec ) ( struct s_PXENV_FILE_EXEC * );
+ PXENV_EXIT_t ( * file_api_check ) ( struct s_PXENV_FILE_API_CHECK * );
+};
+
+/**
+ * Handle an unknown PXE API call
+ *
+ * @v pxenv_unknown Pointer to a struct s_PXENV_UNKNOWN
+ * @ret #PXENV_EXIT_FAILURE Always
+ * @err #PXENV_STATUS_UNSUPPORTED Always
+ */
+static PXENV_EXIT_t pxenv_unknown ( struct s_PXENV_UNKNOWN *pxenv_unknown ) {
+ pxenv_unknown->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/**
+ * Dispatch PXE API call
+ *
+ * @v bx PXE opcode
+ * @v es:di Address of PXE parameter block
+ * @ret ax PXE exit code
+ */
+__cdecl void pxe_api_call ( struct i386_all_regs *ix86 ) {
+ int opcode = ix86->regs.bx;
+ userptr_t parameters = real_to_user ( ix86->segs.es, ix86->regs.di );
+ size_t param_len;
+ union u_PXENV_ANY pxenv_any;
+ union pxenv_call pxenv_call;
+ PXENV_EXIT_t ret;
+
+ switch ( opcode ) {
+ case PXENV_UNLOAD_STACK:
+ pxenv_call.unload_stack = pxenv_unload_stack;
+ param_len = sizeof ( pxenv_any.unload_stack );
+ break;
+ case PXENV_GET_CACHED_INFO:
+ pxenv_call.get_cached_info = pxenv_get_cached_info;
+ param_len = sizeof ( pxenv_any.get_cached_info );
+ break;
+ case PXENV_RESTART_TFTP:
+ pxenv_call.restart_tftp = pxenv_restart_tftp;
+ param_len = sizeof ( pxenv_any.restart_tftp );
+ break;
+ case PXENV_START_UNDI:
+ pxenv_call.start_undi = pxenv_start_undi;
+ param_len = sizeof ( pxenv_any.start_undi );
+ break;
+ case PXENV_STOP_UNDI:
+ pxenv_call.stop_undi = pxenv_stop_undi;
+ param_len = sizeof ( pxenv_any.stop_undi );
+ break;
+ case PXENV_START_BASE:
+ pxenv_call.start_base = pxenv_start_base;
+ param_len = sizeof ( pxenv_any.start_base );
+ break;
+ case PXENV_STOP_BASE:
+ pxenv_call.stop_base = pxenv_stop_base;
+ param_len = sizeof ( pxenv_any.stop_base );
+ break;
+ case PXENV_TFTP_OPEN:
+ pxenv_call.tftp_open = pxenv_tftp_open;
+ param_len = sizeof ( pxenv_any.tftp_open );
+ break;
+ case PXENV_TFTP_CLOSE:
+ pxenv_call.tftp_close = pxenv_tftp_close;
+ param_len = sizeof ( pxenv_any.tftp_close );
+ break;
+ case PXENV_TFTP_READ:
+ pxenv_call.tftp_read = pxenv_tftp_read;
+ param_len = sizeof ( pxenv_any.tftp_read );
+ break;
+ case PXENV_TFTP_READ_FILE:
+ pxenv_call.tftp_read_file = pxenv_tftp_read_file;
+ param_len = sizeof ( pxenv_any.tftp_read_file );
+ break;
+ case PXENV_TFTP_GET_FSIZE:
+ pxenv_call.tftp_get_fsize = pxenv_tftp_get_fsize;
+ param_len = sizeof ( pxenv_any.tftp_get_fsize );
+ break;
+ case PXENV_UDP_OPEN:
+ pxenv_call.udp_open = pxenv_udp_open;
+ param_len = sizeof ( pxenv_any.udp_open );
+ break;
+ case PXENV_UDP_CLOSE:
+ pxenv_call.udp_close = pxenv_udp_close;
+ param_len = sizeof ( pxenv_any.udp_close );
+ break;
+ case PXENV_UDP_WRITE:
+ pxenv_call.udp_write = pxenv_udp_write;
+ param_len = sizeof ( pxenv_any.udp_write );
+ break;
+ case PXENV_UDP_READ:
+ pxenv_call.udp_read = pxenv_udp_read;
+ param_len = sizeof ( pxenv_any.udp_read );
+ break;
+ case PXENV_UNDI_STARTUP:
+ pxenv_call.undi_startup = pxenv_undi_startup;
+ param_len = sizeof ( pxenv_any.undi_startup );
+ break;
+ case PXENV_UNDI_CLEANUP:
+ pxenv_call.undi_cleanup = pxenv_undi_cleanup;
+ param_len = sizeof ( pxenv_any.undi_cleanup );
+ break;
+ case PXENV_UNDI_INITIALIZE:
+ pxenv_call.undi_initialize = pxenv_undi_initialize;
+ param_len = sizeof ( pxenv_any.undi_initialize );
+ break;
+ case PXENV_UNDI_RESET_ADAPTER:
+ pxenv_call.undi_reset_adapter = pxenv_undi_reset_adapter;
+ param_len = sizeof ( pxenv_any.undi_reset_adapter );
+ break;
+ case PXENV_UNDI_SHUTDOWN:
+ pxenv_call.undi_shutdown = pxenv_undi_shutdown;
+ param_len = sizeof ( pxenv_any.undi_shutdown );
+ break;
+ case PXENV_UNDI_OPEN:
+ pxenv_call.undi_open = pxenv_undi_open;
+ param_len = sizeof ( pxenv_any.undi_open );
+ break;
+ case PXENV_UNDI_CLOSE:
+ pxenv_call.undi_close = pxenv_undi_close;
+ param_len = sizeof ( pxenv_any.undi_close );
+ break;
+ case PXENV_UNDI_TRANSMIT:
+ pxenv_call.undi_transmit = pxenv_undi_transmit;
+ param_len = sizeof ( pxenv_any.undi_transmit );
+ break;
+ case PXENV_UNDI_SET_MCAST_ADDRESS:
+ pxenv_call.undi_set_mcast_address =
+ pxenv_undi_set_mcast_address;
+ param_len = sizeof ( pxenv_any.undi_set_mcast_address );
+ break;
+ case PXENV_UNDI_SET_STATION_ADDRESS:
+ pxenv_call.undi_set_station_address =
+ pxenv_undi_set_station_address;
+ param_len = sizeof ( pxenv_any.undi_set_station_address );
+ break;
+ case PXENV_UNDI_SET_PACKET_FILTER:
+ pxenv_call.undi_set_packet_filter =
+ pxenv_undi_set_packet_filter;
+ param_len = sizeof ( pxenv_any.undi_set_packet_filter );
+ break;
+ case PXENV_UNDI_GET_INFORMATION:
+ pxenv_call.undi_get_information = pxenv_undi_get_information;
+ param_len = sizeof ( pxenv_any.undi_get_information );
+ break;
+ case PXENV_UNDI_GET_STATISTICS:
+ pxenv_call.undi_get_statistics = pxenv_undi_get_statistics;
+ param_len = sizeof ( pxenv_any.undi_get_statistics );
+ break;
+ case PXENV_UNDI_CLEAR_STATISTICS:
+ pxenv_call.undi_clear_statistics = pxenv_undi_clear_statistics;
+ param_len = sizeof ( pxenv_any.undi_clear_statistics );
+ break;
+ case PXENV_UNDI_INITIATE_DIAGS:
+ pxenv_call.undi_initiate_diags = pxenv_undi_initiate_diags;
+ param_len = sizeof ( pxenv_any.undi_initiate_diags );
+ break;
+ case PXENV_UNDI_FORCE_INTERRUPT:
+ pxenv_call.undi_force_interrupt = pxenv_undi_force_interrupt;
+ param_len = sizeof ( pxenv_any.undi_force_interrupt );
+ break;
+ case PXENV_UNDI_GET_MCAST_ADDRESS:
+ pxenv_call.undi_get_mcast_address =
+ pxenv_undi_get_mcast_address;
+ param_len = sizeof ( pxenv_any.undi_get_mcast_address );
+ break;
+ case PXENV_UNDI_GET_NIC_TYPE:
+ pxenv_call.undi_get_nic_type = pxenv_undi_get_nic_type;
+ param_len = sizeof ( pxenv_any.undi_get_nic_type );
+ break;
+ case PXENV_UNDI_GET_IFACE_INFO:
+ pxenv_call.undi_get_iface_info = pxenv_undi_get_iface_info;
+ param_len = sizeof ( pxenv_any.undi_get_iface_info );
+ break;
+ case PXENV_UNDI_ISR:
+ pxenv_call.undi_isr = pxenv_undi_isr;
+ param_len = sizeof ( pxenv_any.undi_isr );
+ break;
+ case PXENV_FILE_OPEN:
+ pxenv_call.file_open = pxenv_file_open;
+ param_len = sizeof ( pxenv_any.file_open );
+ break;
+ case PXENV_FILE_CLOSE:
+ pxenv_call.file_close = pxenv_file_close;
+ param_len = sizeof ( pxenv_any.file_close );
+ break;
+ case PXENV_FILE_SELECT:
+ pxenv_call.file_select = pxenv_file_select;
+ param_len = sizeof ( pxenv_any.file_select );
+ break;
+ case PXENV_FILE_READ:
+ pxenv_call.file_read = pxenv_file_read;
+ param_len = sizeof ( pxenv_any.file_read );
+ break;
+ case PXENV_GET_FILE_SIZE:
+ pxenv_call.get_file_size = pxenv_get_file_size;
+ param_len = sizeof ( pxenv_any.get_file_size );
+ break;
+ case PXENV_FILE_EXEC:
+ pxenv_call.file_exec = pxenv_file_exec;
+ param_len = sizeof ( pxenv_any.file_exec );
+ break;
+ case PXENV_FILE_API_CHECK:
+ pxenv_call.file_api_check = pxenv_file_api_check;
+ param_len = sizeof ( pxenv_any.file_api_check );
+ break;
+ default:
+ DBG ( "PXENV_UNKNOWN_%hx", opcode );
+ pxenv_call.unknown = pxenv_unknown;
+ param_len = sizeof ( pxenv_any.unknown );
+ break;
+ }
+
+ /* Copy parameter block from caller */
+ copy_from_user ( &pxenv_any, parameters, 0, param_len );
+
+ /* Set default status in case child routine fails to do so */
+ pxenv_any.Status = PXENV_STATUS_FAILURE;
+
+ /* Hand off to relevant API routine */
+ DBG ( "[" );
+ ret = pxenv_call.any ( &pxenv_any );
+ if ( pxenv_any.Status != PXENV_STATUS_SUCCESS ) {
+ DBG ( " %02x", pxenv_any.Status );
+ }
+ if ( ret != PXENV_EXIT_SUCCESS ) {
+ DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" );
+ }
+ DBG ( "]" );
+
+ /* Copy modified parameter block back to caller and return */
+ copy_to_user ( parameters, 0, &pxenv_any, param_len );
+ ix86->regs.ax = ret;
+}
+
+/**
+ * Dispatch PXE loader call
+ *
+ * @v es:di Address of PXE parameter block
+ * @ret ax PXE exit code
+ */
+__cdecl void pxe_loader_call ( struct i386_all_regs *ix86 ) {
+ userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
+ struct s_UNDI_LOADER params;
+ PXENV_EXIT_t ret;
+
+ /* Copy parameter block from caller */
+ copy_from_user ( &params, uparams, 0, sizeof ( params ) );
+
+ /* Set default status in case child routine fails to do so */
+ params.Status = PXENV_STATUS_FAILURE;
+
+ /* Call UNDI loader */
+ ret = undi_loader ( &params );
+
+ /* Copy modified parameter block back to caller and return */
+ copy_to_user ( uparams, 0, &params, sizeof ( params ) );
+ ix86->regs.ax = ret;
+}
+
+/**
+ * Hook INT 1A for PXE
+ *
+ */
+void pxe_hook_int1a ( void ) {
+ hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a,
+ &pxe_int_1a_vector );
+}
+
+/**
+ * Unhook INT 1A for PXE
+ *
+ * @ret rc Return status code
+ */
+int pxe_unhook_int1a ( void ) {
+ return unhook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a,
+ &pxe_int_1a_vector );
+}
+
+/**
+ * Calculate byte checksum as used by PXE
+ *
+ * @v data Data
+ * @v size Length of data
+ * @ret sum Checksum
+ */
+static uint8_t pxe_checksum ( void *data, size_t size ) {
+ uint8_t *bytes = data;
+ uint8_t sum = 0;
+
+ while ( size-- ) {
+ sum += *bytes++;
+ }
+ return sum;
+}
+
+/**
+ * Initialise !PXE and PXENV+ structures
+ *
+ */
+void pxe_init_structures ( void ) {
+ uint32_t rm_cs_phys = ( rm_cs << 4 );
+ uint32_t rm_ds_phys = ( rm_ds << 4 );
+
+ /* Fill in missing segment fields */
+ ppxe.EntryPointSP.segment = rm_cs;
+ ppxe.EntryPointESP.segment = rm_cs;
+ ppxe.Stack.segment_address = rm_ds;
+ ppxe.Stack.Physical_address = rm_ds_phys;
+ ppxe.UNDIData.segment_address = rm_ds;
+ ppxe.UNDIData.Physical_address = rm_ds_phys;
+ ppxe.UNDICode.segment_address = rm_cs;
+ ppxe.UNDICode.Physical_address = rm_cs_phys;
+ ppxe.UNDICodeWrite.segment_address = rm_cs;
+ ppxe.UNDICodeWrite.Physical_address = rm_cs_phys;
+ pxenv.RMEntry.segment = rm_cs;
+ pxenv.StackSeg = rm_ds;
+ pxenv.UNDIDataSeg = rm_ds;
+ pxenv.UNDICodeSeg = rm_cs;
+ pxenv.PXEPtr.segment = rm_cs;
+
+ /* Update checksums */
+ ppxe.StructCksum -= pxe_checksum ( &ppxe, sizeof ( ppxe ) );
+ pxenv.Checksum -= pxe_checksum ( &pxenv, sizeof ( pxenv ) );
+}
+
+/**
+ * Start PXE NBP at 0000:7c00
+ *
+ * @ret rc Return status code
+ */
+int pxe_start_nbp ( void ) {
+ int discard_b, discard_c;
+ uint16_t rc;
+
+ /* Far call to PXE NBP */
+ __asm__ __volatile__ ( REAL_CODE ( "pushw %%cx\n\t"
+ "pushw %%ax\n\t"
+ "movw %%cx, %%es\n\t"
+ "lcall $0, $0x7c00\n\t"
+ "addw $4, %%sp\n\t" )
+ : "=a" ( rc ), "=b" ( discard_b ),
+ "=c" ( discard_c )
+ : "a" ( & __from_text16 ( ppxe ) ),
+ "b" ( & __from_text16 ( pxenv ) ),
+ "c" ( rm_cs )
+ : "edx", "esi", "edi", "ebp", "memory" );
+
+ return rc;
+}
diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_entry.S b/gpxe/src/arch/i386/interface/pxe/pxe_entry.S
new file mode 100644
index 00000000..204894ef
--- /dev/null
+++ b/gpxe/src/arch/i386/interface/pxe/pxe_entry.S
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+ .arch i386
+ .section ".text16", "awx", @progbits
+ .section ".text16.data", "aw", @progbits
+ .section ".data16", "aw", @progbits
+
+/****************************************************************************
+ * !PXE structure
+ ****************************************************************************
+ */
+ .section ".text16.data"
+ .globl ppxe
+ .align 16
+ppxe:
+ .ascii "!PXE" /* Signature */
+ .byte pxe_length /* StructLength */
+ .byte 0 /* StructCksum */
+ .byte 0 /* StructRev */
+ .byte 0 /* reserved_1 */
+ .word 0, 0 /* UNDIROMID */
+ .word 0, 0 /* BaseROMID */
+ .word pxe_entry_sp, 0 /* EntryPointSP */
+ .word pxe_entry_esp, 0 /* EntryPointESP */
+ .word -1, -1 /* StatusCallout */
+ .byte 0 /* reserved_2 */
+ .byte SegDescCnt /* SegDescCnt */
+ .word 0 /* FirstSelector */
+pxe_segments:
+ .word 0, 0, 0, _data16_size /* Stack */
+ .word 0, 0, 0, _data16_size /* UNDIData */
+ .word 0, 0, 0, _text16_size /* UNDICode */
+ .word 0, 0, 0, _text16_size /* UNDICodeWrite */
+ .word 0, 0, 0, 0 /* BC_Data */
+ .word 0, 0, 0, 0 /* BC_Code */
+ .word 0, 0, 0, 0 /* BC_CodeWrite */
+ .equ SegDescCnt, ( ( . - pxe_segments ) / 8 )
+ .equ pxe_length, . - ppxe
+ .size ppxe, . - ppxe
+
+/****************************************************************************
+ * PXENV+ structure
+ ****************************************************************************
+ */
+ .section ".text16.data"
+ .globl pxenv
+ .align 16
+pxenv:
+ .ascii "PXENV+" /* Signature */
+ .word 0x0201 /* Version */
+ .byte pxenv_length /* Length */
+ .byte 0 /* Checksum */
+ .word pxenv_entry, 0 /* RMEntry */
+ .long 0 /* PMEntry */
+ .word 0 /* PMSelector */
+ .word 0 /* StackSeg */
+ .word _data16_size /* StackSize */
+ .word 0 /* BC_CodeSeg */
+ .word 0 /* BC_CodeSize */
+ .word 0 /* BC_DataSeg */
+ .word 0 /* BC_DataSize */
+ .word 0 /* UNDIDataSeg */
+ .word _data16_size /* UNDIDataSize */
+ .word 0 /* UNDICodeSeg */
+ .word _text16_size /* UNDICodeSize */
+ .word ppxe, 0 /* PXEPtr */
+ .equ pxenv_length, . - pxenv
+ .size pxenv, . - pxenv
+
+/****************************************************************************
+ * pxenv_entry (16-bit far call)
+ *
+ * PXE API call PXENV+ entry point
+ *
+ * Parameters:
+ * %es:di : Far pointer to PXE parameter structure
+ * %bx : PXE API call
+ * Returns:
+ * %ax : PXE exit status
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".text16"
+ .code16
+pxenv_entry:
+ pushl $pxe_api_call
+ pushw %cs
+ call prot_call
+ addl $4, %esp
+ lret
+ .size pxenv_entry, . - pxenv_entry
+
+/****************************************************************************
+ * pxe_entry
+ *
+ * PXE API call !PXE entry point
+ *
+ * Parameters:
+ * stack : Far pointer to PXE parameter structure
+ * stack : PXE API call
+ * Returns:
+ * %ax : PXE exit status
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".text16"
+ .code16
+pxe_entry:
+pxe_entry_sp:
+ /* Preserve original %esp */
+ pushl %esp
+ /* Zero high word of %esp to allow use of common code */
+ movzwl %sp, %esp
+ jmp pxe_entry_common
+pxe_entry_esp:
+ /* Preserve %esp to match behaviour of pxe_entry_sp */
+ pushl %esp
+pxe_entry_common:
+ /* Save PXENV+ API call registers */
+ pushw %es
+ pushw %di
+ pushw %bx
+ /* Load !PXE parameters from stack into PXENV+ registers */
+ addr32 movw 18(%esp), %bx
+ movw %bx, %es
+ addr32 movw 16(%esp), %di
+ addr32 movw 14(%esp), %bx
+ /* Make call as for PXENV+ */
+ pushw %cs
+ call pxenv_entry
+ /* Restore PXENV+ registers */
+ popw %bx
+ popw %di
+ popw %es
+ /* Restore original %esp and return */
+ popl %esp
+ lret
+ .size pxe_entry, . - pxe_entry
+
+/****************************************************************************
+ * pxe_int_1a
+ *
+ * PXE INT 1A handler
+ *
+ * Parameters:
+ * %ax : 0x5650
+ * Returns:
+ * %ax : 0x564e
+ * %es:bx : Far pointer to the PXENV+ structure
+ * CF cleared
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".text16"
+ .code16
+ .globl pxe_int_1a
+pxe_int_1a:
+ pushfw
+ cmpw $0x5650, %ax
+ jne 1f
+ /* INT 1A,5650 - PXE installation check */
+ pushw %cs
+ popw %es
+ movw $pxenv, %bx
+ movw $0x564e, %ax
+ popfw
+ clc
+ lret $2
+1: /* INT 1A,other - pass through */
+ popfw
+ ljmp *%cs:pxe_int_1a_vector
+
+ .section ".text16.data"
+ .globl pxe_int_1a_vector
+pxe_int_1a_vector: .long 0
diff --git a/gpxe/src/arch/i386/kir-Makefile b/gpxe/src/arch/i386/kir-Makefile
new file mode 100644
index 00000000..bbfc1a3a
--- /dev/null
+++ b/gpxe/src/arch/i386/kir-Makefile
@@ -0,0 +1,26 @@
+# Makefile to build a KEEP_IT_REAL flavour
+#
+# KEEP_IT_REAL, by its nature, requires a different build of every
+# single object file, since the inclusion of ".code16gcc" will
+# generate different machine code from the assembly. Unlike the other
+# config options, there is no way that this global dependency can ever
+# be reduced, so it makes sense to be able to build both the normal
+# and the KIR versions without having to force a full rebuild each
+# time.
+
+# Add this Makefile to MAKEDEPS
+#
+MAKEDEPS += arch/i386/kir-Makefile
+
+# Place binaries in bin-kir
+#
+BIN = bin-kir
+
+# Compile with -DKEEP_IT_REAL, forcibly include kir.h at the start of
+# each file to drag in ".code16gcc"
+#
+CFLAGS += -DKEEP_IT_REAL -include kir.h
+
+include Makefile
+
+LDSCRIPT = arch/i386/scripts/i386-kir.lds
diff --git a/gpxe/src/arch/i386/prefix/bImageprefix.S b/gpxe/src/arch/i386/prefix/bImageprefix.S
new file mode 100644
index 00000000..30020f73
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/bImageprefix.S
@@ -0,0 +1,611 @@
+/*
+ Copyright (C) 2000, Entity Cyber, Inc.
+
+ Authors: Gary Byers (gb@thinguin.org)
+ Marty Connor (mdc@thinguin.org)
+ Eric Biederman (ebiederman@lnxi.com)
+
+ This code also derives a lot from arch/i386/boot/setup.S in
+ the linux kernel.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ Description:
+
+ This is just a little bit of code and data that can get prepended
+ to an Etherboot ROM image in order to allow LILO to load the
+ result as if it were a Linux kernel image.
+
+ A real Linux kernel image consists of a one-sector boot loader
+ (to load the image from a floppy disk), followed a few sectors
+ of setup code, followed by the kernel code itself. There's
+ a table in the first sector (starting at offset 497) that indicates
+ how many sectors of setup code follow the first sector and which
+ contains some other parameters that aren't interesting in this
+ case.
+
+ When LILO loads the sectors that comprise a kernel image, it doesn't
+ execute the code in the first sector (since that code would try to
+ load the image from a floppy disk.) The code in the first sector
+ below doesn't expect to get executed (and prints an error message
+ if it ever -is- executed.) LILO's only interested in knowing the
+ number of setup sectors advertised in the table (at offset 497 in
+ the first sector.)
+
+ Etherboot doesn't require much in the way of setup code.
+ Historically, the Linux kernel required at least 4 sectors of
+ setup code. Current versions of LILO look at the byte at
+ offset 497 in the first sector to indicate how many sectors
+ of setup code are contained in the image.
+
+ The setup code that is present here does a lot of things
+ exactly the way the linux kernel does them instead of in
+ ways more typical of etherboot. Generally this is so
+ the code can be strongly compatible with the linux kernel.
+ In addition the general etherboot technique of enabling the a20
+ after we switch into protected mode does not work if etherboot
+ is being loaded at 1MB.
+*/
+
+ .equ CR0_PE,1
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+/* Simple and small GDT entries for booting only */
+#define GDT_ENTRY_BOOT_CS 2
+#define GDT_ENTRY_BOOT_DS (GDT_ENTRY_BOOT_CS + 1)
+#define __BOOT_CS (GDT_ENTRY_BOOT_CS * 8)
+#define __BOOT_DS (GDT_ENTRY_BOOT_DS * 8)
+
+
+#define SETUPSECS 4 /* Minimal nr of setup-sectors */
+#define PREFIXSIZE ((SETUPSECS+1)*512)
+#define PREFIXPGH (PREFIXSIZE / 16 )
+#define BOOTSEG 0x07C0 /* original address of boot-sector */
+#define INITSEG 0x9000 /* we move boot here - out of the way */
+#define SETUPSEG 0x9020 /* setup starts here */
+#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */
+
+#define DELTA_INITSEG (SETUPSEG - INITSEG) /* 0x0020 */
+
+/* Signature words to ensure LILO loaded us right */
+#define SIG1 0xAA55
+#define SIG2 0x5A5A
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+_prefix:
+
+/*
+ This is a minimal boot sector. If anyone tries to execute it (e.g., if
+ a .lkrn file is dd'ed to a floppy), print an error message.
+*/
+
+bootsector:
+ jmp $BOOTSEG, $go - _prefix /* reload cs:ip to match relocation addr */
+go:
+ movw $0x2000, %di /* 0x2000 is arbitrary value >= length
+ of bootsect + room for stack */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+
+ cli
+ movw %ax, %ss /* put stack at BOOTSEG:0x2000. */
+ movw %di,%sp
+ sti
+
+ movw $why_end-why, %cx
+ movw $why - _prefix, %si
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+freeze: jmp freeze
+
+why: .ascii "This image cannot be loaded from a floppy disk.\r\n"
+why_end:
+
+
+ .org 497
+setup_sects:
+ .byte SETUPSECS
+root_flags:
+ .word 0
+syssize:
+ .word _verbatim_size_pgh - PREFIXPGH
+swap_dev:
+ .word 0
+ram_size:
+ .word 0
+vid_mode:
+ .word 0
+root_dev:
+ .word 0
+boot_flag:
+ .word 0xAA55
+
+/*
+ We're now at the beginning of the second sector of the image -
+ where the setup code goes.
+
+ We don't need to do too much setup for Etherboot.
+
+ This code gets loaded at SETUPSEG:0. It wants to start
+ executing the Etherboot image that's loaded at SYSSEG:0 and
+ whose entry point is SYSSEG:0.
+*/
+setup_code:
+ jmp trampoline
+# This is the setup header, and it must start at %cs:2 (old 0x9020:2)
+
+ .ascii "HdrS" # header signature
+ .word 0x0203 # header version number (>= 0x0105)
+ # or else old loadlin-1.5 will fail)
+realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
+start_sys_seg: .word SYSSEG # low load segment (obsolete)
+ .word kernel_version - setup_code
+ # pointing to kernel version string
+ # above section of header is compatible
+ # with loadlin-1.5 (header v1.5). Don't
+ # change it.
+
+type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin,
+ # Bootlin, SYSLX, bootsect...)
+ # See Documentation/i386/boot.txt for
+ # assigned ids
+
+# flags, unused bits must be zero (RFU) bit within loadflags
+loadflags:
+LOADED_HIGH = 1 # If set, the kernel is loaded high
+CAN_USE_HEAP = 0x80 # If set, the loader also has set
+ # heap_end_ptr to tell how much
+ # space behind setup.S can be used for
+ # heap purposes.
+ # Only the loader knows what is free
+ .byte LOADED_HIGH
+
+setup_move_size: .word 0x8000 # size to move, when setup is not
+ # loaded at 0x90000. We will move setup
+ # to 0x90000 then just before jumping
+ # into the kernel. However, only the
+ # loader knows how much data behind
+ # us also needs to be loaded.
+
+code32_start: # here loaders can put a different
+ # start address for 32-bit code.
+ .long 0x100000 # 0x100000 = default for big kernel
+
+ramdisk_image: .long 0 # address of loaded ramdisk image
+ # Here the loader puts the 32-bit
+ # address where it loaded the image.
+ # This only will be read by the kernel.
+
+ramdisk_size: .long 0 # its size in bytes
+
+bootsect_kludge:
+ .long 0 # obsolete
+
+heap_end_ptr: .word 0 # (Header version 0x0201 or later)
+ # space from here (exclusive) down to
+ # end of setup code can be used by setup
+ # for local heap purposes.
+
+pad1: .word 0
+cmd_line_ptr: .long 0 # (Header version 0x0202 or later)
+ # If nonzero, a 32-bit pointer
+ # to the kernel command line.
+ # The command line should be
+ # located between the start of
+ # setup and the end of low
+ # memory (0xa0000), or it may
+ # get overwritten before it
+ # gets read. If this field is
+ # used, there is no longer
+ # anything magical about the
+ # 0x90000 segment; the setup
+ # can be located anywhere in
+ # low memory 0x10000 or higher.
+
+ramdisk_max: .long 0 # (Header version 0x0203 or later)
+ # The highest safe address for
+ # the contents of an initrd
+
+trampoline: call start_of_setup
+trampoline_end:
+ .space 1024
+# End of setup header #####################################################
+
+start_of_setup:
+# Set %ds = %cs, we know that SETUPSEG = %cs at this point
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+# Check signature at end of setup
+ cmpw $SIG1, (setup_sig1 - setup_code)
+ jne bad_sig
+
+ cmpw $SIG2, (setup_sig2 - setup_code)
+ jne bad_sig
+
+ jmp good_sig1
+
+# Routine to print asciiz string at ds:si
+prtstr:
+ lodsb
+ andb %al, %al
+ jz fin
+
+ call prtchr
+ jmp prtstr
+
+fin: ret
+
+# Part of above routine, this one just prints ascii al
+prtchr: pushw %ax
+ pushw %cx
+ movw $7,%bx
+ movw $0x01, %cx
+ movb $0x0e, %ah
+ int $0x10
+ popw %cx
+ popw %ax
+ ret
+
+no_sig_mess: .string "No setup signature found ..."
+
+good_sig1:
+ jmp good_sig
+
+# We now have to find the rest of the setup code/data
+bad_sig:
+ movw %cs, %ax # SETUPSEG
+ subw $DELTA_INITSEG, %ax # INITSEG
+ movw %ax, %ds
+ xorb %bh, %bh
+ movb (497), %bl # get setup sect from bootsect
+ subw $4, %bx # LILO loads 4 sectors of setup
+ shlw $8, %bx # convert to words (1sect=2^8 words)
+ movw %bx, %cx
+ shrw $3, %bx # convert to segment
+ addw $SYSSEG, %bx
+ movw %bx, %cs:(start_sys_seg - setup_code)
+# Move rest of setup code/data to here
+ movw $2048, %di # four sectors loaded by LILO
+ subw %si, %si
+ pushw %cs
+ popw %es
+ movw $SYSSEG, %ax
+ movw %ax, %ds
+ rep
+ movsw
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+ cmpw $SIG1, (setup_sig1 - setup_code)
+ jne no_sig
+
+ cmpw $SIG2, (setup_sig2 - setup_code)
+ jne no_sig
+
+ jmp good_sig
+
+no_sig:
+ lea (no_sig_mess - setup_code), %si
+ call prtstr
+
+no_sig_loop:
+ hlt
+ jmp no_sig_loop
+
+good_sig:
+ cmpw $0, %cs:(realmode_swtch - setup_code)
+ jz rmodeswtch_normal
+
+ lcall *%cs:(realmode_swtch - setup_code)
+ jmp rmodeswtch_end
+
+rmodeswtch_normal:
+ pushw %cs
+ call default_switch
+
+rmodeswtch_end:
+# we get the code32 start address and modify the below 'jmpi'
+# (loader may have changed it)
+ movl %cs:(code32_start - setup_code), %eax
+ movl %eax, %cs:(code32 - setup_code)
+
+# then we load the segment descriptors
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+
+#
+# Enable A20. This is at the very best an annoying procedure.
+# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin.
+#
+
+A20_TEST_LOOPS = 32 # Iterations per wait
+A20_ENABLE_LOOPS = 255 # Total loops to try
+
+a20_try_loop:
+
+ # First, see if we are on a system with no A20 gate.
+a20_none:
+ call a20_test
+ jnz a20_done
+
+ # Next, try the BIOS (INT 0x15, AX=0x2401)
+a20_bios:
+ movw $0x2401, %ax
+ pushfl # Be paranoid about flags
+ int $0x15
+ popfl
+
+ call a20_test
+ jnz a20_done
+
+ # Try enabling A20 through the keyboard controller
+a20_kbc:
+ call empty_8042
+
+ call a20_test # Just in case the BIOS worked
+ jnz a20_done # but had a delayed reaction.
+
+ movb $0xD1, %al # command write
+ outb %al, $0x64
+ call empty_8042
+
+ movb $0xDF, %al # A20 on
+ outb %al, $0x60
+ call empty_8042
+
+ # Wait until a20 really *is* enabled; it can take a fair amount of
+ # time on certain systems; Toshiba Tecras are known to have this
+ # problem.
+a20_kbc_wait:
+ xorw %cx, %cx
+a20_kbc_wait_loop:
+ call a20_test
+ jnz a20_done
+ loop a20_kbc_wait_loop
+
+ # Final attempt: use "configuration port A"
+a20_fast:
+ inb $0x92, %al # Configuration Port A
+ orb $0x02, %al # "fast A20" version
+ andb $0xFE, %al # don't accidentally reset
+ outb %al, $0x92
+
+ # Wait for configuration port A to take effect
+a20_fast_wait:
+ xorw %cx, %cx
+a20_fast_wait_loop:
+ call a20_test
+ jnz a20_done
+ loop a20_fast_wait_loop
+
+ # A20 is still not responding. Try frobbing it again.
+ #
+ decb (a20_tries - setup_code)
+ jnz a20_try_loop
+
+ movw $(a20_err_msg - setup_code), %si
+ call prtstr
+
+a20_die:
+ hlt
+ jmp a20_die
+
+a20_tries:
+ .byte A20_ENABLE_LOOPS
+
+a20_err_msg:
+ .ascii "linux: fatal error: A20 gate not responding!"
+ .byte 13, 10, 0
+
+ # If we get here, all is good
+a20_done:
+ # Leave the idt alone
+
+ # set up gdt
+ xorl %eax, %eax # Compute gdt_base
+ movw %ds, %ax # (Convert %ds:gdt to a linear ptr)
+ shll $4, %eax
+ addl $(bImage_gdt - setup_code), %eax
+ movl %eax, (bImage_gdt_48+2 - setup_code)
+ DATA32 lgdt %ds:(bImage_gdt_48 - setup_code) # load gdt with whatever is
+ # appropriate
+
+ # Switch to protected mode
+ movl %cr0, %eax
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+
+ DATA32 ljmp *%ds:(code32 - setup_code)
+code32:
+ .long 0x100000
+ .word __BOOT_CS, 0
+
+# Here's a bunch of information about your current kernel..
+kernel_version: .ascii "Etherboot "
+ .ascii VERSION
+ .byte 0
+
+# This is the default real mode switch routine.
+# to be called just before protected mode transition
+default_switch:
+ cli # no interrupts allowed !
+ movb $0x80, %al # disable NMI for bootup
+ # sequence
+ outb %al, $0x70
+ lret
+
+# This routine tests whether or not A20 is enabled. If so, it
+# exits with zf = 0.
+#
+# The memory address used, 0x200, is the int $0x80 vector, which
+# should be safe.
+
+A20_TEST_ADDR = 4*0x80
+
+a20_test:
+ pushw %cx
+ pushw %ax
+ xorw %cx, %cx
+ movw %cx, %fs # Low memory
+ decw %cx
+ movw %cx, %gs # High memory area
+ movw $A20_TEST_LOOPS, %cx
+ movw %fs:(A20_TEST_ADDR), %ax
+ pushw %ax
+a20_test_wait:
+ incw %ax
+ movw %ax, %fs:(A20_TEST_ADDR)
+ call delay # Serialize and make delay constant
+ cmpw %gs:(A20_TEST_ADDR+0x10), %ax
+ loope a20_test_wait
+
+ popw %fs:(A20_TEST_ADDR)
+ popw %ax
+ popw %cx
+ ret
+
+
+# This routine checks that the keyboard command queue is empty
+# (after emptying the output buffers)
+#
+# Some machines have delusions that the keyboard buffer is always full
+# with no keyboard attached...
+#
+# If there is no keyboard controller, we will usually get 0xff
+# to all the reads. With each IO taking a microsecond and
+# a timeout of 100,000 iterations, this can take about half a
+# second ("delay" == outb to port 0x80). That should be ok,
+# and should also be plenty of time for a real keyboard controller
+# to empty.
+#
+
+empty_8042:
+ pushl %ecx
+ movl $100000, %ecx
+
+empty_8042_loop:
+ decl %ecx
+ jz empty_8042_end_loop
+
+ call delay
+
+ inb $0x64, %al # 8042 status port
+ testb $1, %al # output buffer?
+ jz no_output
+
+ call delay
+ inb $0x60, %al # read it
+ jmp empty_8042_loop
+
+no_output:
+ testb $2, %al # is input buffer full?
+ jnz empty_8042_loop # yes - loop
+empty_8042_end_loop:
+ popl %ecx
+
+
+# Delay is needed after doing I/O
+delay:
+ outb %al,$0x80
+ ret
+
+# Descriptor tables
+#
+# NOTE: The intel manual says gdt should be sixteen bytes aligned for
+# efficiency reasons. However, there are machines which are known not
+# to boot with misaligned GDTs, so alter this at your peril! If you alter
+# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two
+# empty GDT entries (one for NULL and one reserved).
+#
+# NOTE: On some CPUs, the GDT must be 8 byte aligned. This is
+# true for the Voyager Quad CPU card which will not boot without
+# This directive. 16 byte aligment is recommended by intel.
+#
+ .balign 16
+bImage_gdt:
+ .fill GDT_ENTRY_BOOT_CS,8,0
+
+ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
+ .word 0 # base address = 0
+ .word 0x9A00 # code read/exec
+ .word 0x00CF # granularity = 4096, 386
+ # (+5th nibble of limit)
+
+ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
+ .word 0 # base address = 0
+ .word 0x9200 # data read/write
+ .word 0x00CF # granularity = 4096, 386
+ # (+5th nibble of limit)
+bImage_gdt_end:
+ .balign 4
+
+ .word 0 # alignment byte
+bImage_idt_48:
+ .word 0 # idt limit = 0
+ .long 0 # idt base = 0L
+
+ .word 0 # alignment byte
+bImage_gdt_48:
+ .word bImage_gdt_end - bImage_gdt - 1 # gdt limit
+ .long bImage_gdt_48 - setup_code # gdt base (filled in later)
+
+ .section ".text16", "ax", @progbits
+prefix_exit:
+ int $0x19 /* should try to boot machine */
+prefix_exit_end:
+ .previous
+
+
+ .org (PREFIXSIZE - 4)
+# Setup signature -- must be last
+setup_sig1: .word SIG1
+setup_sig2: .word SIG2
+ /* Etherboot expects to be contiguous in memory once loaded.
+ * The linux bImage protocol does not do this, but since we
+ * don't need any information that's left in the prefix, it
+ * doesn't matter: we just have to ensure that we make it to _start
+ *
+ * protected_start will live at 0x100000 and it will be the
+ * the first code called as we enter protected mode.
+ */
+ .code32
+protected_start:
+ /* Load segment registers */
+ movw $__BOOT_DS, %ax
+ movw %ax, %ss
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+
+ /* Use the internal etherboot stack */
+ movl $(_prefix_stack_end - protected_start + 0x100000), %esp
+
+ pushl $0 /* No parameters to preserve for exit path */
+ pushl $0 /* Use prefix exit path mechanism */
+
+ jmp _start
+/*
+ That's about it.
+*/
diff --git a/gpxe/src/arch/i386/prefix/bootpart.S b/gpxe/src/arch/i386/prefix/bootpart.S
new file mode 100644
index 00000000..d60fe9bc
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/bootpart.S
@@ -0,0 +1,216 @@
+#define BOOT_SEG 0x07c0
+#define EXEC_SEG 0x0100
+#define STACK_SEG 0x0200
+#define STACK_SIZE 0x2000
+
+ .text
+ .arch i386
+ .section ".prefix", "awx", @progbits
+ .code16
+
+/*
+ * Find active partition
+ *
+ * Parameters:
+ * %dl : BIOS drive number
+ * %bp : Active partition handler routine
+ */
+find_active_partition:
+ /* Set up stack at STACK_SEG:STACK_SIZE */
+ movw $STACK_SEG, %ax
+ movw %ax, %ss
+ movw $STACK_SIZE, %sp
+
+ /* Relocate self to EXEC_SEG */
+ pushw $BOOT_SEG
+ popw %ds
+ pushw $EXEC_SEG
+ popw %es
+ xorw %si, %si
+ xorw %di, %di
+ movw $0x200, %cx
+ rep movsb
+ ljmp $EXEC_SEG, $1f
+1: pushw %ds
+ popw %es
+ pushw %cs
+ popw %ds
+
+ /* Check for LBA extensions */
+ movb $0x41, %ah
+ movw $0x55aa, %bx
+ stc
+ int $0x13
+ jc 1f
+ cmpw $0xaa55, %bx
+ jne 1f
+ movw $read_lba, read_sectors
+1:
+ /* Read and process root partition table */
+ xorb %dh, %dh
+ movw $0x0001, %cx
+ xorl %esi, %esi
+ xorl %edi, %edi
+ call process_table
+
+ /* Print failure message */
+ movw $10f, %si
+ jmp boot_error
+10: .asciz "Could not locate active partition\r\n"
+
+/*
+ * Print failure message and boot next device
+ *
+ * Parameters:
+ * %si : Failure string
+ */
+boot_error:
+ cld
+ movw $0x0007, %bx
+ movb $0x0e, %ah
+1: lodsb
+ testb %al, %al
+ je 99f
+ int $0x10
+ jmp 1b
+99: /* Boot next device */
+ int $0x18
+
+/*
+ * Process partition table
+ *
+ * Parameters:
+ * %dl : BIOS drive number
+ * %dh : Head
+ * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7)
+ * %ch : Low eight bits of cylinder
+ * %esi:%edi : LBA address
+ * %bp : Active partition handler routine
+ *
+ * Returns:
+ * CF set on error
+ */
+process_table:
+ pushal
+ call read_boot_sector
+ jc 99f
+ movw $446, %bx
+1: call process_partition
+ addw $16, %bx
+ cmpw $510, %bx
+ jne 1b
+99: popal
+ ret
+
+/*
+ * Process partition
+ *
+ * Parameters:
+ * %dl : BIOS drive number
+ * %dh : Head
+ * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7)
+ * %ch : Low eight bits of cylinder
+ * %esi:%edi : LBA address
+ * %bx : Offset within partition table
+ * %bp : Active partition handler routine
+ */
+process_partition:
+ pushal
+ /* Load C/H/S values from partition entry */
+ movb %es:1(%bx), %dh
+ movw %es:2(%bx), %cx
+ /* Update LBA address from partition entry */
+ addl %es:8(%bx), %edi
+ adcl $0, %esi
+ /* Check active flag */
+ testb $0x80, %es:(%bx)
+ jz 1f
+ call read_boot_sector
+ jc 99f
+ jmp *%bp
+1: /* Check for extended partition */
+ movb %es:4(%bx), %al
+ cmpb $0x05, %al
+ je 2f
+ cmpb $0x0f, %al
+ je 2f
+ cmpb $0x85, %al
+ jne 99f
+2: call process_table
+99: popal
+ /* Reload original partition table */
+ call read_boot_sector
+ ret
+
+/*
+ * Read single sector to %es:0000 and verify 0x55aa signature
+ *
+ * Parameters:
+ * %dl : BIOS drive number
+ * %dh : Head
+ * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7)
+ * %ch : Low eight bits of cylinder
+ * %esi:%edi : LBA address
+ *
+ * Returns:
+ * CF set on error
+ */
+read_boot_sector:
+ pushw %ax
+ movw $1, %ax
+ call *read_sectors
+ jc 99f
+ cmpw $0xaa55, %es:(510)
+ je 99f
+ stc
+99: popw %ax
+ ret
+
+/*
+ * Read sectors to %es:0000
+ *
+ * Parameters:
+ * %dl : BIOS drive number
+ * %dh : Head
+ * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7)
+ * %ch : Low eight bits of cylinder
+ * %esi:%edi : LBA address
+ * %ax : Number of sectors (max 127)
+ *
+ * Returns:
+ * CF set on error
+ */
+read_sectors: .word read_chs
+
+read_chs:
+ /* Read sectors using C/H/S address */
+ pushal
+ xorw %bx, %bx
+ movb $0x02, %ah
+ stc
+ int $0x13
+ sti
+ popal
+ ret
+
+read_lba:
+ /* Read sectors using LBA address */
+ pushal
+ movw %ax, (lba_desc + 2)
+ pushw %es
+ popw (lba_desc + 6)
+ movl %edi, (lba_desc + 8)
+ movl %esi, (lba_desc + 12)
+ movw $lba_desc, %si
+ movb $0x42, %ah
+ int $0x13
+ popal
+ ret
+
+lba_desc:
+ .byte 0x10
+ .byte 0
+ .word 1
+ .word 0x0000
+ .word 0x0000
+ .long 0, 0
diff --git a/gpxe/src/arch/i386/prefix/comprefix.S b/gpxe/src/arch/i386/prefix/comprefix.S
new file mode 100644
index 00000000..2cbbca50
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/comprefix.S
@@ -0,0 +1,46 @@
+/* We need a real mode stack that won't be stomped on by Etherboot
+ which starts at 0x20000. Choose something that's sufficiently high,
+ but not in DOC territory. Note that we couldn't do this in a real
+ .com program since stack variables are in the same segment as the
+ code and data, but this isn't really a .com program, it just looks
+ like one to make DOS load it into memory. It still has the 64kB
+ limitation of .com files though. */
+#define STACK_SEG 0x7000
+#define STACK_SIZE 0x4000
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+
+/* Cheat a little with the relocations: .COM files are loaded at 0x100 */
+_prefix:
+ /* Set up temporary stack */
+ movw $STACK_SEG, %ax
+ movw %ax, %ss
+ movw $STACK_SIZE, %sp
+
+ pushl $0 /* No parameters to preserve for exit path */
+ pushw $0 /* Dummy return address - use prefix_exit */
+
+ /* Calculate segment address of image start */
+ pushw %cs
+ popw %ax
+ addw $(0x100/16), %ax
+ pushw %ax
+ pushw $_start
+ /* Calculated lcall to _start with %cs:0000 = image start */
+ lret
+
+ .section ".text16", "ax", @progbits
+prefix_exit:
+ movw $0x4c00,%ax /* return to DOS */
+ int $0x21 /* reach this on Quit */
+prefix_exit_end:
+ .previous
+
+/* The body of etherboot is attached here at build time.
+ * Force 16 byte alignment
+ */
+ .align 16,0
+_body:
diff --git a/gpxe/src/arch/i386/prefix/dskprefix.S b/gpxe/src/arch/i386/prefix/dskprefix.S
new file mode 100644
index 00000000..cdc43b37
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/dskprefix.S
@@ -0,0 +1,375 @@
+/* NOTE: this boot sector contains instructions that need at least an 80186.
+ * Yes, as86 has a bug somewhere in the valid instruction set checks.
+ *
+ */
+
+/* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds
+ * modified by Drew Eckhardt
+ * modified by Bruce Evans (bde)
+ *
+ * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines.
+ *
+ * It then loads the system at SYSSEG<<4, using BIOS interrupts.
+ *
+ * The loader has been made as simple as possible, and continuous read errors
+ * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by
+ * getting whole tracks at a time whenever possible.
+ */
+
+.equ BOOTSEG, 0x07C0 /* original address of boot-sector */
+
+.equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */
+
+ .org 0
+ .arch i386
+ .text
+ .section ".prefix", "ax", @progbits
+ .code16
+
+ jmp $BOOTSEG, $go /* reload cs:ip to match relocation addr */
+go:
+ movw $0x2000-12, %di /* 0x2000 is arbitrary value >= length */
+ /* of bootsect + room for stack + 12 for */
+ /* saved disk parm block */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+ movw %ax,%ss /* put stack at BOOTSEG:0x4000-12. */
+ movw %di,%sp
+
+/* Many BIOS's default disk parameter tables will not recognize multi-sector
+ * reads beyond the maximum sector number specified in the default diskette
+ * parameter tables - this may mean 7 sectors in some cases.
+ *
+ * Since single sector reads are slow and out of the question, we must take care
+ * of this by creating new parameter tables (for the first disk) in RAM. We
+ * will set the maximum sector count to 36 - the most we will encounter on an
+ * ED 2.88. High doesn't hurt. Low does.
+ *
+ * Segments are as follows: ds=es=ss=cs - BOOTSEG
+ */
+
+ xorw %cx,%cx
+ movw %cx,%es /* access segment 0 */
+ movw $0x78, %bx /* 0:bx is parameter table address */
+ pushw %ds /* save ds */
+/* 0:bx is parameter table address */
+ ldsw %es:(%bx),%si /* loads ds and si */
+
+ movw %ax,%es /* ax is BOOTSECT (loaded above) */
+ movb $6, %cl /* copy 12 bytes */
+ cld
+ pushw %di /* keep a copy for later */
+ rep
+ movsw /* ds:si is source, es:di is dest */
+ popw %di
+
+ movb $36,%es:4(%di)
+
+ movw %cx,%ds /* access segment 0 */
+ xchgw %di,(%bx)
+ movw %es,%si
+ xchgw %si,2(%bx)
+ popw %ds /* restore ds */
+ movw %di, dpoff /* save old parameters */
+ movw %si, dpseg /* to restore just before finishing */
+ pushw %ds
+ popw %es /* reload es */
+
+/* Note that es is already set up. Also cx is 0 from rep movsw above. */
+
+ xorb %ah,%ah /* reset FDC */
+ xorb %dl,%dl
+ int $0x13
+
+/* Get disk drive parameters, specifically number of sectors/track.
+ *
+ * It seems that there is no BIOS call to get the number of sectors. Guess
+ * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read,
+ * 15 if sector 15 can be read. Otherwise guess 9.
+ */
+
+ movw $disksizes, %si /* table of sizes to try */
+
+probe_loop:
+ lodsb
+ cbtw /* extend to word */
+ movw %ax, sectors
+ cmpw $disksizes+4, %si
+ jae got_sectors /* if all else fails, try 9 */
+ xchgw %cx,%ax /* cx = track and sector */
+ xorw %dx,%dx /* drive 0, head 0 */
+ movw $0x0200, %bx /* address after boot sector */
+ /* (512 bytes from origin, es = cs) */
+ movw $0x0201, %ax /* service 2, 1 sector */
+ int $0x13
+ jc probe_loop /* try next value */
+
+got_sectors:
+ movw $msg1end-msg1, %cx
+ movw $msg1, %si
+ call print_str
+
+/* ok, we've written the Loading... message, now we want to load the system */
+
+ movw $SYSSEG, %ax
+ movw %ax,%es /* segment of SYSSEG<<4 */
+ pushw %es
+ call read_it
+
+/* This turns off the floppy drive motor, so that we enter the kernel in a
+ * known state, and don't have to worry about it later.
+ */
+ movw $0x3f2, %dx
+ xorb %al,%al
+ outb %al,%dx
+
+ call print_nl
+ pop %es /* = SYSSEG */
+
+/* Restore original disk parameters */
+ movw $0x78, %bx
+ movw dpoff, %di
+ movw dpseg, %si
+ xorw %ax,%ax
+ movw %ax,%ds
+ movw %di,(%bx)
+ movw %si,2(%bx)
+
+ /* Everything now loaded. %es = SYSSEG, so %es:0000 points to
+ * start of loaded image.
+ */
+
+ /* Jump to loaded copy */
+ ljmp $SYSSEG, $start_runtime
+
+endseg: .word SYSSEG + _load_size_pgh
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBW"
+ .long endseg
+ .long 16
+ .long 0
+ .previous
+
+/* This routine loads the system at address SYSSEG<<4, making sure no 64kB
+ * boundaries are crossed. We try to load it as fast as possible, loading whole
+ * tracks whenever we can.
+ *
+ * in: es - starting address segment (normally SYSSEG)
+ */
+read_it:
+ movw $0,sread /* load whole image including prefix */
+ movw %es,%ax
+ testw $0x0fff, %ax
+die: jne die /* es must be at 64kB boundary */
+ xorw %bx,%bx /* bx is starting address within segment */
+rp_read:
+ movw %es,%ax
+ movw %bx,%dx
+ movb $4, %cl
+ shrw %cl,%dx /* bx is always divisible by 16 */
+ addw %dx,%ax
+ cmpw endseg, %ax /* have we loaded all yet? */
+ jb ok1_read
+ ret
+ok1_read:
+ movw sectors, %ax
+ subw sread, %ax
+ movw %ax,%cx
+ shlw $9, %cx
+ addw %bx,%cx
+ jnc ok2_read
+ je ok2_read
+ xorw %ax,%ax
+ subw %bx,%ax
+ shrw $9, %ax
+ok2_read:
+ call read_track
+ movw %ax,%cx
+ addw sread, %ax
+ cmpw sectors, %ax
+ jne ok3_read
+ movw $1, %ax
+ subw head, %ax
+ jne ok4_read
+ incw track
+ok4_read:
+ movw %ax, head
+ xorw %ax,%ax
+ok3_read:
+ movw %ax, sread
+ shlw $9, %cx
+ addw %cx,%bx
+ jnc rp_read
+ movw %es,%ax
+ addb $0x10, %ah
+ movw %ax,%es
+ xorw %bx,%bx
+ jmp rp_read
+
+read_track:
+ pusha
+ pushw %ax
+ pushw %bx
+ pushw %bp /* just in case the BIOS is buggy */
+ movw $0x0e2e, %ax /* 0x2e = . */
+ movw $0x0007, %bx
+ int $0x10
+ popw %bp
+ popw %bx
+ popw %ax
+
+ movw track, %dx
+ movw sread, %cx
+ incw %cx
+ movb %dl,%ch
+ movw head, %dx
+ movb %dl,%dh
+ andw $0x0100, %dx
+ movb $2, %ah
+
+ pushw %dx /* save for error dump */
+ pushw %cx
+ pushw %bx
+ pushw %ax
+
+ int $0x13
+ jc bad_rt
+ addw $8, %sp
+ popa
+ ret
+
+bad_rt: pushw %ax /* save error code */
+ call print_all /* ah = error, al = read */
+
+ xorb %ah,%ah
+ xorb %dl,%dl
+ int $0x13
+
+ addw $10, %sp
+ popa
+ jmp read_track
+
+/* print_all is for debugging purposes. It will print out all of the registers.
+ * The assumption is that this is called from a routine, with a stack frame like
+ * dx
+ * cx
+ * bx
+ * ax
+ * error
+ * ret <- sp
+ */
+
+print_all:
+ call print_nl /* nl for readability */
+ movw $5, %cx /* error code + 4 registers */
+ movw %sp,%bp
+
+print_loop:
+ pushw %cx /* save count left */
+
+ cmpb $5, %cl
+ jae no_reg /* see if register name is needed */
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movw $0xe05+0x41-1, %ax
+ subb %cl,%al
+ int $0x10
+
+ movb $0x58, %al /* 'X' */
+ int $0x10
+
+ movb $0x3A, %al /* ':' */
+ int $0x10
+
+no_reg:
+ addw $2, %bp /* next register */
+ call print_hex /* print it */
+ movb $0x20, %al /* print a space */
+ int $0x10
+ popw %cx
+ loop print_loop
+ call print_nl /* nl for readability */
+ ret
+
+print_str:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+ ret
+
+print_nl:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movw $0xe0d, %ax /* CR */
+ int $0x10
+ movb $0xa, %al /* LF */
+ int $0x10
+ ret
+
+/* print_hex prints the word pointed to by ss:bp in hexadecimal. */
+
+print_hex:
+ movw (%bp),%dx /* load word into dx */
+ movb $4, %cl
+ movb $0x0e, %ah /* write char, tty mode */
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ call print_digit
+ call print_digit
+ call print_digit
+/* fall through */
+print_digit:
+ rol %cl,%dx /* rotate so that lowest 4 bits are used */
+ movb $0x0f, %al /* mask for nybble */
+ andb %dl,%al
+ addb $0x90, %al /* convert al to ascii hex (four instructions) */
+ daa
+ adcb $0x40, %al
+ daa
+ int $0x10
+ ret
+
+sread: .word 0 /* sectors read of current track */
+head: .word 0 /* current head */
+track: .word 0 /* current track */
+
+sectors:
+ .word 0
+
+dpseg: .word 0
+dpoff: .word 0
+
+disksizes:
+ .byte 36,18,15,9
+
+msg1:
+ .ascii "Loading ROM image"
+msg1end:
+
+ .org 510, 0
+ .word 0xAA55
+
+start_runtime:
+ call install
+
+ /* Set up real-mode stack */
+ movw %bx, %ss
+ movw $_estack16, %sp
+
+ /* Jump to .text16 segment */
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "awx", @progbits
+1:
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+ /* Boot next device */
+ int $0x18
+
diff --git a/gpxe/src/arch/i386/prefix/elf_dprefix.S b/gpxe/src/arch/i386/prefix/elf_dprefix.S
new file mode 100644
index 00000000..0eac77e0
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/elf_dprefix.S
@@ -0,0 +1,94 @@
+#include "elf.h"
+
+ .arch i386
+ .section ".prefix", "a", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_DYN
+e_machine: .short EM_386
+e_version: .long 1
+e_entry: .long LOAD_ADDR + _start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+prefix_exit:
+
+prefix_exit_end:
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/elfprefix.S b/gpxe/src/arch/i386/prefix/elfprefix.S
new file mode 100644
index 00000000..d712753a
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/elfprefix.S
@@ -0,0 +1,94 @@
+#include "elf.h"
+
+ .arch i386
+ .section ".prefix", "a", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_EXEC
+e_machine: .short EM_386
+e_version: .long 1
+e_entry: .long LOAD_ADDR + _start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+prefix_exit:
+
+prefix_exit_end:
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/exeprefix.S b/gpxe/src/arch/i386/prefix/exeprefix.S
new file mode 100755
index 00000000..f1b402b7
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/exeprefix.S
@@ -0,0 +1,41 @@
+/*
+ Prefix for .exe images
+ Doesn't work yet, even though it starts off the same as a .com
+ image as shown by DOS debug.
+*/
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+
+_prefix:
+ .byte 'M', 'Z'
+ .short _exe_size_tail /* tail */
+ .short _exe_size_pages /* pages */
+ .short 0 /* relocations */
+ .short 2 /* header paras */
+ .short _exe_bss_size /* min */
+ .short 0xFFFF /* max paras */
+ .short _exe_ss_offset /* SS */
+ .short _stack_size /* SP */
+ .short 0 /* checksum */
+ .short 0 /* IP */
+ .short 0 /* CS */
+ .short 0x1C /* reloc offset */
+ .short 0 /* overlay number */
+ .short 0 /* fill */
+ .short 0 /* fill */
+
+ .section ".text16", "ax", @progbits
+prefix_exit:
+ movw $0x4c00,%ax /* return to DOS */
+ int $0x21 /* reach this on Quit */
+prefix_exit_end:
+ .previous
+
+/* The body of etherboot is attached here at build time.
+ * Force 16 byte alignment
+ */
+ .align 16,0
+_body:
diff --git a/gpxe/src/arch/i386/prefix/hdprefix.S b/gpxe/src/arch/i386/prefix/hdprefix.S
new file mode 100644
index 00000000..56fcb36d
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/hdprefix.S
@@ -0,0 +1,103 @@
+ .text
+ .arch i386
+ .section ".prefix", "awx", @progbits
+ .code16
+ .org 0
+
+ movw $load_image, %bp
+ jmp find_active_partition
+
+#include "bootpart.S"
+
+load_image:
+ /* Get disk geometry */
+ pushal
+ pushw %es
+ movb $0x08, %ah
+ int $0x13
+ jc load_failed
+ movb %cl, max_sector
+ movb %dh, max_head
+ popw %es
+ popal
+
+1: /* Read to end of current track */
+ movb %cl, %al
+ negb %al
+ addb max_sector, %al
+ incb %al
+ andb $0x3f, %al
+ movzbl %al, %eax
+ call *read_sectors
+ jc load_failed
+
+ /* Update %es */
+ movw %es, %bx
+ shll $5, %eax
+ addw %ax, %bx
+ movw %bx, %es
+ shrl $5, %eax
+
+ /* Update LBA address */
+ addl %eax, %edi
+ adcl $0, %esi
+
+ /* Update CHS address */
+ andb $0xc0, %cl
+ orb $0x01, %cl
+ incb %dh
+ cmpb max_head, %dh
+ jbe 2f
+ xorb %dh, %dh
+ incb %ch
+ jnc 2f
+ addb $0xc0, %cl
+2:
+ /* Loop until whole image is read */
+ subl %eax, load_length
+ ja 1b
+ ljmp $BOOT_SEG, $start_image
+
+max_sector:
+ .byte 0
+max_head:
+ .byte 0
+load_length:
+ .long _load_size_sect
+
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBL"
+ .long load_length
+ .long 512
+ .long 0
+ .previous
+
+
+load_failed:
+ movw $10f, %si
+ jmp boot_error
+10: .asciz "Could not load gPXE\r\n"
+
+ .org 510
+ .byte 0x55, 0xaa
+
+start_image:
+ call install
+
+ /* Set up real-mode stack */
+ movw %bx, %ss
+ movw $_estack16, %sp
+
+ /* Jump to .text16 segment */
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "awx", @progbits
+1:
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+ /* Boot next device */
+ int $0x18
diff --git a/gpxe/src/arch/i386/prefix/kpxeprefix.S b/gpxe/src/arch/i386/prefix/kpxeprefix.S
new file mode 100644
index 00000000..d708604b
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/kpxeprefix.S
@@ -0,0 +1,7 @@
+/*****************************************************************************
+ * PXE prefix that keep the UNDI portion of the PXE stack present
+ *****************************************************************************
+ */
+
+#define PXELOADER_KEEP_UNDI
+#include "pxeprefix.S"
diff --git a/gpxe/src/arch/i386/prefix/libprefix.S b/gpxe/src/arch/i386/prefix/libprefix.S
new file mode 100644
index 00000000..deea5ab3
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/libprefix.S
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+ .arch i386
+ .section ".prefix.lib", "awx", @progbits
+ .section ".data16", "aw", @progbits
+
+/**
+ * High memory temporary load address
+ *
+ * Temporary buffer into which to copy (or decompress) our runtime
+ * image, prior to calling get_memmap() and relocate(). We don't
+ * actually leave anything here once install() has returned.
+ *
+ * We use the start of an even megabyte so that we don't have to worry
+ * about the current state of the A20 line.
+ *
+ * We use 4MB rather than 2MB because some PXE stack / PMM BIOS
+ * combinations are known to place data required by other UNDI ROMs
+ * loader around the 2MB mark.
+ */
+ .globl HIGHMEM_LOADPOINT
+ .equ HIGHMEM_LOADPOINT, ( 4 << 20 )
+
+/* Image compression enabled */
+#define COMPRESS 1
+
+#define CR0_PE 1
+
+/*****************************************************************************
+ * Utility function: print character (with LF -> LF,CR translation)
+ *
+ * Parameters:
+ * %al : character to print
+ * Returns:
+ * Nothing
+ * Corrupts:
+ * %ax
+ *****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl print_character
+print_character:
+ /* Preserve registers */
+ pushw %bx
+ pushw %bp
+ /* Print character */
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+ cmpb $0x0a, %al /* '\n'? */
+ jne 1f
+ int $0x10
+ movb $0x0d, %al
+1: int $0x10
+ /* Restore registers and return */
+ popw %bp
+ popw %bx
+ ret
+ .size print_character, . - print_character
+
+/*****************************************************************************
+ * Utility function: print a NUL-terminated string
+ *
+ * Parameters:
+ * %ds:si : string to print
+ * Returns:
+ * %ds:si : character after terminating NUL
+ *****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl print_message
+print_message:
+ /* Preserve registers */
+ pushw %ax
+ /* Print string */
+1: lodsb
+ testb %al, %al
+ je 2f
+ call print_character
+ jmp 1b
+2: /* Restore registers and return */
+ popw %ax
+ ret
+ .size print_message, . - print_message
+
+/*****************************************************************************
+ * Utility functions: print hex digit/byte/word/dword
+ *
+ * Parameters:
+ * %al (low nibble) : digit to print
+ * %al : byte to print
+ * %ax : word to print
+ * %eax : dword to print
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl print_hex_dword
+print_hex_dword:
+ rorl $16, %eax
+ call print_hex_word
+ rorl $16, %eax
+ /* Fall through */
+ .size print_hex_dword, . - print_hex_dword
+ .globl print_hex_word
+print_hex_word:
+ xchgb %al, %ah
+ call print_hex_byte
+ xchgb %al, %ah
+ /* Fall through */
+ .size print_hex_word, . - print_hex_word
+ .globl print_hex_byte
+print_hex_byte:
+ rorb $4, %al
+ call print_hex_nibble
+ rorb $4, %al
+ /* Fall through */
+ .size print_hex_byte, . - print_hex_byte
+ .globl print_hex_nibble
+print_hex_nibble:
+ /* Preserve registers */
+ pushw %ax
+ /* Print digit (technique by Norbert Juffa <norbert.juffa@amd.com> */
+ andb $0x0f, %al
+ cmpb $10, %al
+ sbbb $0x69, %al
+ das
+ call print_character
+ /* Restore registers and return */
+ popw %ax
+ ret
+ .size print_hex_nibble, . - print_hex_nibble
+
+/****************************************************************************
+ * pm_call (real-mode near call)
+ *
+ * Call routine in 16-bit protected mode for access to extended memory
+ *
+ * Parameters:
+ * %ax : address of routine to call in 16-bit protected mode
+ * Returns:
+ * none
+ * Corrupts:
+ * %ax
+ *
+ * The specified routine is called in 16-bit protected mode, with:
+ *
+ * %cs : 16-bit code segment with base matching real-mode %cs
+ * %ss : 16-bit data segment with base matching real-mode %ss
+ * %ds,%es,%fs,%gs : 32-bit data segment with zero base and 4GB limit
+ *
+ ****************************************************************************
+ */
+
+#ifndef KEEP_IT_REAL
+
+ /* GDT for protected-mode calls */
+ .section ".prefix.lib"
+ .align 16
+pm_call_vars:
+gdt:
+gdt_limit: .word gdt_length - 1
+gdt_base: .long 0
+ .word 0 /* padding */
+pm_cs: /* 16-bit protected-mode code segment */
+ .equ PM_CS, pm_cs - gdt
+ .word 0xffff, 0
+ .byte 0, 0x9b, 0x00, 0
+pm_ss: /* 16-bit protected-mode stack segment */
+ .equ PM_SS, pm_ss - gdt
+ .word 0xffff, 0
+ .byte 0, 0x93, 0x00, 0
+pm_ds: /* 32-bit protected-mode flat data segment */
+ .equ PM_DS, pm_ds - gdt
+ .word 0xffff, 0
+ .byte 0, 0x93, 0xcf, 0
+gdt_end:
+ .equ gdt_length, . - gdt
+ .size gdt, . - gdt
+
+ .section ".prefix.lib"
+ .align 16
+pm_saved_gdt:
+ .long 0, 0
+ .size pm_saved_gdt, . - pm_saved_gdt
+
+ .equ pm_call_vars_size, . - pm_call_vars
+#define PM_CALL_VAR(x) ( -pm_call_vars_size + ( (x) - pm_call_vars ) )
+
+ .section ".prefix.lib"
+ .code16
+pm_call:
+ /* Preserve registers, flags, and RM return point */
+ pushw %bp
+ movw %sp, %bp
+ subw $pm_call_vars_size, %sp
+ andw $0xfff0, %sp
+ pushfl
+ pushw %gs
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %ss
+ pushw %cs
+ pushw $99f
+
+ /* Set up local variable block, and preserve GDT */
+ pushw %cx
+ pushw %si
+ pushw %di
+ pushw %ss
+ popw %es
+ movw $pm_call_vars, %si
+ leaw PM_CALL_VAR(pm_call_vars)(%bp), %di
+ movw $pm_call_vars_size, %cx
+ cs rep movsb
+ popw %di
+ popw %si
+ popw %cx
+ sgdt PM_CALL_VAR(pm_saved_gdt)(%bp)
+
+ /* Set up GDT bases */
+ pushl %eax
+ pushl %edi
+ xorl %eax, %eax
+ movw %ss, %ax
+ shll $4, %eax
+ movzwl %bp, %edi
+ leal PM_CALL_VAR(gdt)(%eax, %edi), %eax
+ movl %eax, PM_CALL_VAR(gdt_base)(%bp)
+ movw %cs, %ax
+ movw $PM_CALL_VAR(pm_cs), %di
+ call set_seg_base
+ movw %ss, %ax
+ movw $PM_CALL_VAR(pm_ss), %di
+ call set_seg_base
+ popl %edi
+ popl %eax
+
+ /* Switch CPU to protected mode and load up segment registers */
+ pushl %eax
+ cli
+ lgdt PM_CALL_VAR(gdt)(%bp)
+ movl %cr0, %eax
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+ ljmp $PM_CS, $1f
+1: movw $PM_SS, %ax
+ movw %ax, %ss
+ movw $PM_DS, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ popl %eax
+
+ /* Call PM routine */
+ call *%ax
+
+ /* Set real-mode segment limits on %ds, %es, %fs and %gs */
+ movw %ss, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+
+ /* Return CPU to real mode */
+ movl %cr0, %eax
+ andb $0!CR0_PE, %al
+ movl %eax, %cr0
+
+ /* Restore registers and flags */
+ lret /* will ljmp to 99f */
+99: popw %ss
+ popw %ds
+ popw %es
+ popw %fs
+ popw %gs
+ lgdt PM_CALL_VAR(pm_saved_gdt)(%bp)
+ popfl
+ movw %bp, %sp
+ popw %bp
+ ret
+ .size pm_call, . - pm_call
+
+set_seg_base:
+ rolw $4, %ax
+ movw %ax, 2(%bp,%di)
+ andw $0xfff0, 2(%bp,%di)
+ movb %al, 4(%bp,%di)
+ andb $0x0f, 4(%bp,%di)
+ ret
+ .size set_seg_base, . - set_seg_base
+
+#endif /* KEEP_IT_REAL */
+
+/****************************************************************************
+ * copy_bytes (real-mode or 16-bit protected-mode near call)
+ *
+ * Copy bytes
+ *
+ * Parameters:
+ * %ds:esi : source address
+ * %es:edi : destination address
+ * %ecx : length
+ * Returns:
+ * %ds:esi : next source address
+ * %es:edi : next destination address
+ * Corrupts:
+ * None
+ ****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+copy_bytes:
+ pushl %ecx
+ rep addr32 movsb
+ popl %ecx
+ ret
+ .size copy_bytes, . - copy_bytes
+
+/****************************************************************************
+ * install_block (real-mode near call)
+ *
+ * Install block to specified address
+ *
+ * Parameters:
+ * %esi : source physical address (must be a multiple of 16)
+ * %edi : destination physical address (must be a multiple of 16)
+ * %ecx : length of (decompressed) data
+ * %edx : total length of block (including any uninitialised data portion)
+ * Returns:
+ * %esi : next source physical address (will be a multiple of 16)
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+install_block:
+
+#ifdef KEEP_IT_REAL
+
+ /* Preserve registers */
+ pushw %ds
+ pushw %es
+ pushl %ecx
+ pushl %edi
+
+ /* Convert %esi and %edi to segment registers */
+ shrl $4, %esi
+ movw %si, %ds
+ xorw %si, %si
+ shrl $4, %edi
+ movw %di, %es
+ xorw %di, %di
+
+#else /* KEEP_IT_REAL */
+
+ /* Call self in protected mode */
+ pushw %ax
+ movw $1f, %ax
+ call pm_call
+ popw %ax
+ ret
+1:
+ /* Preserve registers */
+ pushl %ecx
+ pushl %edi
+
+#endif /* KEEP_IT_REAL */
+
+
+#if COMPRESS
+ /* Decompress source to destination */
+ call decompress16
+#else
+ /* Copy source to destination */
+ call copy_bytes
+#endif
+
+ /* Zero .bss portion */
+ negl %ecx
+ addl %edx, %ecx
+ pushw %ax
+ xorw %ax, %ax
+ rep addr32 stosb
+ popw %ax
+
+ /* Round up %esi to start of next source block */
+ addl $0xf, %esi
+ andl $~0xf, %esi
+
+
+#ifdef KEEP_IT_REAL
+
+ /* Convert %ds:esi back to a physical address */
+ movzwl %ds, %cx
+ shll $4, %ecx
+ addl %ecx, %esi
+
+ /* Restore registers */
+ popl %edi
+ popl %ecx
+ popw %es
+ popw %ds
+
+#else /* KEEP_IT_REAL */
+
+ /* Restore registers */
+ popl %edi
+ popl %ecx
+
+#endif
+
+ ret
+ .size install_block, . - install_block
+
+/****************************************************************************
+ * alloc_basemem (real-mode near call)
+ *
+ * Allocate space for .text16 and .data16 from top of base memory.
+ * Memory is allocated using the BIOS free base memory counter at
+ * 0x40:13.
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * %ax : .text16 segment address
+ * %bx : .data16 segment address
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl alloc_basemem
+alloc_basemem:
+ /* FBMS => %ax as segment address */
+ movw $0x40, %ax
+ movw %ax, %fs
+ movw %fs:0x13, %ax
+ shlw $6, %ax
+
+ /* .data16 segment address */
+ subw $_data16_size_pgh, %ax
+ pushw %ax
+
+ /* .text16 segment address */
+ subw $_text16_size_pgh, %ax
+ pushw %ax
+
+ /* Update FBMS */
+ shrw $6, %ax
+ movw %ax, %fs:0x13
+
+ /* Return */
+ popw %ax
+ popw %bx
+ ret
+ .size alloc_basemem, . - alloc_basemem
+
+/****************************************************************************
+ * install (real-mode near call)
+ *
+ * Install all text and data segments.
+ *
+ * Parameters:
+ * none
+ * Returns:
+ * %ax : .text16 segment address
+ * %bx : .data16 segment address
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl install
+install:
+ /* Preserve registers */
+ pushl %esi
+ pushl %edi
+ /* Allocate space for .text16 and .data16 */
+ call alloc_basemem
+ /* Image source = %cs:0000 */
+ xorl %esi, %esi
+ /* Image destination = HIGHMEM_LOADPOINT */
+ movl $HIGHMEM_LOADPOINT, %edi
+ /* Install text and data segments */
+ call install_prealloc
+ /* Restore registers and return */
+ popl %edi
+ popl %esi
+ ret
+ .size install, . - install
+
+/****************************************************************************
+ * install_prealloc (real-mode near call)
+ *
+ * Install all text and data segments.
+ *
+ * Parameters:
+ * %ax : .text16 segment address
+ * %bx : .data16 segment address
+ * %esi : Image source physical address (or zero for %cs:0000)
+ * %edi : Decompression temporary area physical address
+ * Corrupts:
+ * none
+ ****************************************************************************
+ */
+ .section ".prefix.lib"
+ .code16
+ .globl install_prealloc
+install_prealloc:
+ /* Save registers */
+ pushal
+ pushw %ds
+ pushw %es
+
+ /* Sanity: clear the direction flag asap */
+ cld
+
+ /* Calculate physical address of payload (i.e. first source) */
+ testl %esi, %esi
+ jnz 1f
+ movw %cs, %si
+ shll $4, %esi
+1: addl $_payload_offset, %esi
+
+ /* Install .text16 and .data16 */
+ pushl %edi
+ movzwl %ax, %edi
+ shll $4, %edi
+ movl $_text16_size, %ecx
+ movl %ecx, %edx
+ call install_block /* .text16 */
+ movzwl %bx, %edi
+ shll $4, %edi
+ movl $_data16_progbits_size, %ecx
+ movl $_data16_size, %edx
+ call install_block /* .data16 */
+ popl %edi
+
+ /* Set up %ds for access to .data16 */
+ movw %bx, %ds
+
+#ifdef KEEP_IT_REAL
+ /* Initialise libkir */
+ movw %ax, (init_libkir_vector+2)
+ lcall *init_libkir_vector
+#else
+ /* Install .text and .data to temporary area in high memory,
+ * prior to reading the E820 memory map and relocating
+ * properly.
+ */
+ movl $_textdata_progbits_size, %ecx
+ movl $_textdata_size, %edx
+ call install_block
+
+ /* Initialise librm at current location */
+ movw %ax, (init_librm_vector+2)
+ lcall *init_librm_vector
+
+ /* Call relocate() to determine target address for relocation.
+ * relocate() will return with %esi, %edi and %ecx set up
+ * ready for the copy to the new location.
+ */
+ movw %ax, (prot_call_vector+2)
+ pushl $relocate
+ lcall *prot_call_vector
+ popl %edx /* discard */
+
+ /* Copy code to new location */
+ pushl %edi
+ pushw %ax
+ movw $copy_bytes, %ax
+ call pm_call
+ popw %ax
+ popl %edi
+
+ /* Initialise librm at new location */
+ lcall *init_librm_vector
+
+#endif
+ /* Restore registers */
+ popw %es
+ popw %ds
+ popal
+ ret
+ .size install_prealloc, . - install_prealloc
+
+ /* Vectors for far calls to .text16 functions */
+ .section ".data16"
+#ifdef KEEP_IT_REAL
+init_libkir_vector:
+ .word init_libkir
+ .word 0
+ .size init_libkir_vector, . - init_libkir_vector
+#else
+init_librm_vector:
+ .word init_librm
+ .word 0
+ .size init_librm_vector, . - init_librm_vector
+prot_call_vector:
+ .word prot_call
+ .word 0
+ .size prot_call_vector, . - prot_call_vector
+#endif
+
+
+ /* File split information for the compressor */
+#if COMPRESS
+ .section ".zinfo", "a"
+ .ascii "COPY"
+ .long _prefix_load_offset
+ .long _prefix_progbits_size
+ .long _max_align
+ .ascii "PACK"
+ .long _text16_load_offset
+ .long _text16_progbits_size
+ .long _max_align
+ .ascii "PACK"
+ .long _data16_load_offset
+ .long _data16_progbits_size
+ .long _max_align
+ .ascii "PACK"
+ .long _textdata_load_offset
+ .long _textdata_progbits_size
+ .long _max_align
+#else /* COMPRESS */
+ .section ".zinfo", "a"
+ .ascii "COPY"
+ .long _prefix_load_offset
+ .long _load_size
+ .long _max_align
+#endif /* COMPRESS */
diff --git a/gpxe/src/arch/i386/prefix/lkrnprefix.S b/gpxe/src/arch/i386/prefix/lkrnprefix.S
new file mode 100644
index 00000000..a3774d1a
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/lkrnprefix.S
@@ -0,0 +1,155 @@
+/*
+ Copyright (C) 2000, Entity Cyber, Inc.
+
+ Authors: Gary Byers (gb@thinguin.org)
+ Marty Connor (mdc@thinguin.org)
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ Description:
+
+ This is just a little bit of code and data that can get prepended
+ to an Etherboot ROM image in order to allow LILO to load the
+ result as if it were a Linux kernel image.
+
+ A real Linux kernel image consists of a one-sector boot loader
+ (to load the image from a floppy disk), followed a few sectors
+ of setup code, followed by the kernel code itself. There's
+ a table in the first sector (starting at offset 497) that indicates
+ how many sectors of setup code follow the first sector and which
+ contains some other parameters that aren't interesting in this
+ case.
+
+ When LILO loads the sectors that comprise a kernel image, it doesn't
+ execute the code in the first sector (since that code would try to
+ load the image from a floppy disk.) The code in the first sector
+ below doesn't expect to get executed (and prints an error message
+ if it ever -is- executed.) LILO's only interested in knowing the
+ number of setup sectors advertised in the table (at offset 497 in
+ the first sector.)
+
+ Etherboot doesn't require much in the way of setup code.
+ Historically, the Linux kernel required at least 4 sectors of
+ setup code. Current versions of LILO look at the byte at
+ offset 497 in the first sector to indicate how many sectors
+ of setup code are contained in the image.
+
+*/
+
+#define SETUPSECS 4 /* Minimal nr of setup-sectors */
+#define PREFIXSIZE ((SETUPSECS+1)*512)
+#define PREFIXPGH (PREFIXSIZE / 16 )
+#define BOOTSEG 0x07C0 /* original address of boot-sector */
+#define INITSEG 0x9000 /* we move boot here - out of the way */
+#define SETUPSEG 0x9020 /* setup starts here */
+#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+/*
+ This is a minimal boot sector. If anyone tries to execute it (e.g., if
+ a .lilo file is dd'ed to a floppy), print an error message.
+*/
+
+bootsector:
+ jmp $BOOTSEG, $1f /* reload cs:ip to match relocation addr */
+1:
+ movw $0x2000, %di /* 0x2000 is arbitrary value >= length
+ of bootsect + room for stack */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+
+ cli
+ movw %ax, %ss /* put stack at BOOTSEG:0x2000. */
+ movw %di,%sp
+ sti
+
+ movw $why_end-why, %cx
+ movw $why, %si
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+freeze: jmp freeze
+
+why: .ascii "This image cannot be loaded from a floppy disk.\r\n"
+why_end:
+
+
+ .org 497
+setup_sects:
+ .byte SETUPSECS
+root_flags:
+ .word 0
+syssize:
+ .word _load_size_pgh - PREFIXPGH
+swap_dev:
+ .word 0
+ram_size:
+ .word 0
+vid_mode:
+ .word 0
+root_dev:
+ .word 0
+boot_flag:
+ .word 0xAA55
+
+
+ .org 512
+
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBW"
+ .long syssize
+ .long 16
+ .long 0
+ .previous
+
+/*
+ We're now at the beginning of the second sector of the image -
+ where the setup code goes.
+
+ We don't need to do too much setup for Etherboot.
+
+ This code gets loaded at SETUPSEG:0. It wants to start
+ executing the Etherboot image that's loaded at SYSSEG:0 and
+ whose entry point is SYSSEG:0.
+*/
+setup_code:
+ /* Etherboot expects to be contiguous in memory once loaded.
+ * LILO doesn't do this, but since we don't need any
+ * information that's left in the prefix, it doesn't matter:
+ * we just have to ensure that %cs:0000 is where the start of
+ * the Etherboot image *would* be.
+ */
+ ljmp $(SYSSEG-(PREFIXSIZE/16)), $run_etherboot
+
+
+ .org PREFIXSIZE
+/*
+ We're now at the beginning of the kernel proper.
+ */
+run_etherboot:
+ call install
+
+ /* Jump to .text16 segment */
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "awx", @progbits
+1:
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+ /* Boot next device */
+ int $0x18
diff --git a/gpxe/src/arch/i386/prefix/lmelf_dprefix.S b/gpxe/src/arch/i386/prefix/lmelf_dprefix.S
new file mode 100644
index 00000000..93534df5
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/lmelf_dprefix.S
@@ -0,0 +1,161 @@
+#include "elf.h"
+ .arch sledgehammer
+ .code32
+ .equ FLAT_CODE_SEG,_pmcs-_gdt
+ .equ FLAT_DATA_SEG,_pmds-_gdt
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+ .section ".prefix", "ax", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_DYN
+e_machine: .short EM_X86_64
+e_version: .long 1
+e_entry: .long LOAD_ADDR + elf_start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+elf_start:
+ .code64
+ /* Reload the gdt to something I know */
+ leaq _gdt(%rip), %rax
+ movq %rax, 0x02 + gdtptr(%rip)
+ lgdt gdtptr(%rip)
+
+ /* Enter 32bit compatibility mode */
+ leaq elf_start32(%rip), %rax
+ movl %eax, 0x00 + elf_start32_addr(%rip)
+ ljmp *elf_start32_addr(%rip)
+
+elf_start32:
+ .code32
+ /* Reload the data segments */
+ movl $FLAT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ movl %cr4, %eax
+ andl $~X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Save the first argument */
+ pushl %ebx
+ jmp _start
+
+gdtptr:
+ .word _gdt_end - _gdt -1
+ .long _gdt
+ .long 0
+_gdt:
+elf_start32_addr:
+ .long elf_start32
+ .long FLAT_CODE_SEG
+_pmcs:
+ /* 32 bit protected mode code segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds:
+ /* 32 bit protected mode data segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+_gdt_end:
+
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+prefix_exit:
+
+prefix_exit_end:
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/lmelf_prefix.S b/gpxe/src/arch/i386/prefix/lmelf_prefix.S
new file mode 100644
index 00000000..3c1e7251
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/lmelf_prefix.S
@@ -0,0 +1,161 @@
+#include "elf.h"
+ .arch sledgehammer
+ .code32
+ .equ FLAT_CODE_SEG,_pmcs-_gdt
+ .equ FLAT_DATA_SEG,_pmds-_gdt
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+ .section ".prefix", "ax", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_EXEC
+e_machine: .short EM_X86_64
+e_version: .long 1
+e_entry: .long LOAD_ADDR + elf_start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+elf_start:
+ .code64
+ /* Reload the gdt to something I know */
+ leaq _gdt(%rip), %rax
+ movq %rax, 0x02 + gdtptr(%rip)
+ lgdt gdtptr(%rip)
+
+ /* Enter 32bit compatibility mode */
+ leaq elf_start32(%rip), %rax
+ movl %eax, 0x00 + elf_start32_addr(%rip)
+ ljmp *elf_start32_addr(%rip)
+
+elf_start32:
+ .code32
+ /* Reload the data segments */
+ movl $FLAT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ movl %cr4, %eax
+ andl $~X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Save the first argument */
+ pushl %ebx
+ jmp _start
+
+gdtptr:
+ .word _gdt_end - _gdt -1
+ .long _gdt
+ .long 0
+_gdt:
+elf_start32_addr:
+ .long elf_start32
+ .long FLAT_CODE_SEG
+_pmcs:
+ /* 32 bit protected mode code segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds:
+ /* 32 bit protected mode data segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+_gdt_end:
+
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+prefix_exit:
+
+prefix_exit_end:
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/mbr.S b/gpxe/src/arch/i386/prefix/mbr.S
new file mode 100644
index 00000000..adfe2041
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/mbr.S
@@ -0,0 +1,13 @@
+ .text
+ .arch i386
+ .section ".prefix", "awx", @progbits
+ .code16
+ .org 0
+
+mbr:
+ movw $exec_sector, %bp
+ jmp find_active_partition
+exec_sector:
+ ljmp $0x0000, $0x7c00
+
+#include "bootpart.S"
diff --git a/gpxe/src/arch/i386/prefix/nbiprefix.S b/gpxe/src/arch/i386/prefix/nbiprefix.S
new file mode 100644
index 00000000..d4904b73
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/nbiprefix.S
@@ -0,0 +1,76 @@
+ .text
+ .arch i386
+ .section ".prefix", "ax", @progbits
+ .section ".prefix.data", "aw", @progbits
+ .code16
+ .section ".prefix"
+ .org 0
+
+nbi_header:
+
+/*****************************************************************************
+ * NBI file header
+ *****************************************************************************
+ */
+file_header:
+ .long 0x1b031336 /* Signature */
+ .byte 0x04 /* 16 bytes header, no vendor info */
+ .byte 0
+ .byte 0
+ .byte 0 /* No flags */
+ .word 0x0000, 0x07c0 /* Load header to 0x07c0:0x0000 */
+ .word entry, 0x07c0 /* Start execution at 0x07c0:entry */
+ .size file_header, . - file_header
+
+/*****************************************************************************
+ * NBI segment header
+ *****************************************************************************
+ */
+segment_header:
+ .byte 0x04 /* 16 bytes header, no vendor info */
+ .byte 0
+ .byte 0
+ .byte 0x04 /* Last segment */
+ .long 0x00007e00
+imglen: .long _load_size - 512
+memlen: .long _load_size - 512
+ .size segment_header, . - segment_header
+
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBL"
+ .long imglen
+ .long 1
+ .long 0
+ .ascii "SUBL"
+ .long memlen
+ .long 1
+ .long 0
+ .previous
+
+/*****************************************************************************
+ * NBI entry point
+ *****************************************************************************
+ */
+entry:
+ /* Install low and high memory regions */
+ call install
+
+ /* Jump to .text16 segment */
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "awx", @progbits
+1:
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+ /* Reboot system */
+ int $0x19
+
+ .previous
+ .size entry, . - entry
+
+nbi_header_end:
+ .org 512
diff --git a/gpxe/src/arch/i386/prefix/nullprefix.S b/gpxe/src/arch/i386/prefix/nullprefix.S
new file mode 100644
index 00000000..032d41e0
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/nullprefix.S
@@ -0,0 +1,13 @@
+ .org 0
+ .text
+ .arch i386
+
+ .section ".prefix", "ax", @progbits
+ .code16
+_prefix:
+
+ .section ".text16", "ax", @progbits
+prefix_exit:
+
+prefix_exit_end:
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/pxeprefix.S b/gpxe/src/arch/i386/prefix/pxeprefix.S
new file mode 100644
index 00000000..d7125b61
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/pxeprefix.S
@@ -0,0 +1,666 @@
+#define PXENV_UNDI_SHUTDOWN 0x0005
+#define PXENV_UNDI_GET_NIC_TYPE 0x0012
+#define PXENV_STOP_UNDI 0x0015
+#define PXENV_UNLOAD_STACK 0x0070
+
+ .text
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+ .section ".prefix.data", "aw", @progbits
+ .code16
+
+#include <undi.h>
+
+/*****************************************************************************
+ * Entry point: set operating context, print welcome message
+ *****************************************************************************
+ */
+ .section ".prefix"
+ /* Set up our non-stack segment registers */
+ jmp $0x7c0, $1f
+1: pushfl
+ /* %ax here is the default return type... */
+ movw $5, %ax /* Keep PXE+UNDI */
+ pushal
+ pushw %ds
+ pushw %es
+ pushw %fs
+ pushw %gs
+ movw %cs, %ax
+ movw %ax, %ds
+
+ movw $0x40, %ax /* BIOS data segment access */
+ movw %ax, %fs
+
+ pushw %fs:0x13
+ /* Record PXENV+ and !PXE nominal addresses */
+ movw %es, pxenv_segment
+ movw %bx, pxenv_offset
+ movw %sp, %bp
+ movw %ss, return_stack_segment
+ movl %esp, return_stack_offset
+ movl 50(%bp), %eax
+ movl %eax, ppxe_segoff /* !PXE address */
+ /* Set up stack just below 0x7c00 */
+ xorw %ax, %ax
+ movw %ax, %ss
+ movw $0x7c00, %sp
+ /* Clear direction flag, for the sake of sanity */
+ cld
+ /* Print welcome message */
+ movw $10f, %si
+ call print_message
+ .section ".prefix.data"
+10: .asciz "PXE->EB:"
+ .previous
+
+/*****************************************************************************
+ * Verify PXENV+ structure and record parameters of interest
+ *****************************************************************************
+ */
+detect_pxenv:
+ /* Signature check */
+ les pxenv_segoff, %di
+ cmpl $0x4e455850, %es:(%di) /* 'PXEN' signature */
+ jne no_pxenv
+ cmpw $0x2b56, %es:4(%di) /* 'V+' signature */
+ jne no_pxenv
+ /* Record entry point and UNDI segments */
+ pushl %es:0x0a(%di) /* Entry point */
+ popl entry_segoff
+ pushw %es:0x24(%di) /* UNDI code segment */
+ pushw %es:0x26(%di) /* UNDI code size */
+ popl undi_code_segoff
+ pushw %es:0x20(%di) /* UNDI data segment */
+ pushw %es:0x22(%di) /* UNDI data size */
+ popl undi_data_segoff
+ /* Print "PXENV+ at <address>" */
+ movw $10f, %si
+ call print_message
+ movw %bx, %di
+ call print_segoff
+ movb $',', %al
+ call print_character
+ jmp 99f
+ .section ".prefix.data"
+10: .asciz " PXENV+ at "
+ .previous
+
+no_pxenv:
+ xorl %eax, %eax
+ movl %eax, pxenv_segoff
+
+99:
+
+/*****************************************************************************
+ * Verify !PXE structure and record parameters of interest
+ *****************************************************************************
+ */
+detect_ppxe:
+ /* Signature check */
+ les ppxe_segoff, %di
+ cmpl $0x45585021, %es:(%di) /* '!PXE' signature */
+ jne no_ppxe
+ /* Record structure address, entry point, and UNDI segments */
+ pushw %es
+ popw ppxe_segment
+ movw %di, ppxe_offset
+ pushl %es:0x10(%di) /* Entry point */
+ popl entry_segoff
+ pushw %es:0x30(%di) /* UNDI code segment */
+ pushw %es:0x36(%di) /* UNDI code size */
+ popl undi_code_segoff
+ pushw %es:0x28(%di) /* UNDI data segment */
+ pushw %es:0x2e(%di) /* UNDI data size */
+ popl undi_data_segoff
+ /* Print "!PXE at <address>" */
+ movw $10f, %si
+ call print_message
+ call print_segoff
+ movb $',', %al
+ call print_character
+ jmp 99f
+ .section ".prefix.data"
+10: .asciz " !PXE at "
+ .previous
+
+no_ppxe:
+ xorl %eax, %eax
+ movl %eax, ppxe_segoff
+
+99:
+
+/*****************************************************************************
+ * Sanity check: we must have an entry point
+ *****************************************************************************
+ */
+check_have_stack:
+ /* Check for entry point */
+ movl entry_segoff, %eax
+ testl %eax, %eax
+ jnz 99f
+ /* No entry point: print message and skip everything else */
+ movw $10f, %si
+ call print_message
+ jmp finished
+ .section ".prefix.data"
+10: .asciz " No PXE stack found!\n"
+ .previous
+99:
+
+/*****************************************************************************
+ * Calculate base memory usage by UNDI
+ *****************************************************************************
+ */
+find_undi_basemem_usage:
+ movw undi_code_segment, %ax
+ movw undi_code_size, %bx
+ movw undi_data_segment, %cx
+ movw undi_data_size, %dx
+ cmpw %ax, %cx
+ ja 1f
+ xchgw %ax, %cx
+ xchgw %bx, %dx
+1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */
+ shrw $6, %ax /* Round down to nearest kB */
+ movw %ax, undi_fbms_start
+ addw $0x0f, %dx /* Round up to next segment */
+ shrw $4, %dx
+ addw %dx, %cx
+ addw $((1024 / 16) - 1), %cx /* Round up to next kB */
+ shrw $6, %cx
+ movw %cx, undi_fbms_end
+
+/*****************************************************************************
+ * Print information about detected PXE stack
+ *****************************************************************************
+ */
+print_structure_information:
+ /* Print entry point */
+ movw $10f, %si
+ call print_message
+ les entry_segoff, %di
+ call print_segoff
+ .section ".prefix.data"
+10: .asciz " entry point at "
+ .previous
+ /* Print UNDI code segment */
+ movw $10f, %si
+ call print_message
+ les undi_code_segoff, %di
+ call print_segoff
+ .section ".prefix.data"
+10: .asciz "\n UNDI code segment "
+ .previous
+ /* Print UNDI data segment */
+ movw $10f, %si
+ call print_message
+ les undi_data_segoff, %di
+ call print_segoff
+ .section ".prefix.data"
+10: .asciz ", data segment "
+ .previous
+ /* Print UNDI memory usage */
+ movw $10f, %si
+ call print_message
+ movw undi_fbms_start, %ax
+ call print_word
+ movb $'-', %al
+ call print_character
+ movw undi_fbms_end, %ax
+ call print_word
+ movw $20f, %si
+ call print_message
+ .section ".prefix.data"
+10: .asciz " ("
+20: .asciz "kB)\n"
+ .previous
+
+/*****************************************************************************
+ * Determine physical device
+ *****************************************************************************
+ */
+get_physical_device:
+ /* Issue PXENV_UNDI_GET_NIC_TYPE */
+ movw $PXENV_UNDI_GET_NIC_TYPE, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+ jmp no_physical_device
+1: /* Determine physical device type */
+ movb ( pxe_parameter_structure + 0x02 ), %al
+ cmpb $2, %al
+ je pci_physical_device
+ jmp no_physical_device
+
+pci_physical_device:
+ /* Record PCI bus:dev.fn and vendor/device IDs */
+ movl ( pxe_parameter_structure + 0x03 ), %eax
+ movl %eax, pci_vendor
+ movw ( pxe_parameter_structure + 0x0b ), %ax
+ movw %ax, pci_busdevfn
+ movw $10f, %si
+ call print_message
+ call print_pci_busdevfn
+ movb $0x0a, %al
+ call print_character
+ jmp 99f
+ .section ".prefix.data"
+10: .asciz " UNDI device is PCI "
+ .previous
+
+no_physical_device:
+ /* No device found, or device type not understood */
+ movw $10f, %si
+ call print_message
+ .section ".prefix.data"
+10: .asciz " Unable to determine UNDI physical device\n"
+ .previous
+
+99:
+
+/*****************************************************************************
+ * Leave NIC in a safe state
+ *****************************************************************************
+ */
+#ifndef PXELOADER_KEEP_UNDI
+shutdown_nic:
+ /* Issue PXENV_UNDI_SHUTDOWN */
+ movw $PXENV_UNDI_SHUTDOWN, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+1:
+
+/*****************************************************************************
+ * Unload PXE base code
+ *****************************************************************************
+ */
+unload_base_code:
+ /* Issue PXENV_UNLOAD_STACK */
+ movw $PXENV_UNLOAD_STACK, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+ jmp 99f
+1: /* Free base memory used by PXE base code */
+ movw %fs:(0x13), %si
+ movw undi_fbms_start, %di
+ call free_basemem
+99:
+
+/*****************************************************************************
+ * Unload UNDI driver
+ *****************************************************************************
+ */
+
+unload_undi:
+ /* Issue PXENV_STOP_UNDI */
+ movw $PXENV_STOP_UNDI, %bx
+ call pxe_call
+ jnc 1f
+ call print_pxe_error
+ jmp 99f
+1: /* Free base memory used by UNDI */
+ movw undi_fbms_start, %si
+ movw undi_fbms_end, %di
+ call free_basemem
+ /* Clear UNDI_FL_STARTED */
+ andw $~UNDI_FL_STARTED, flags
+99:
+
+/*****************************************************************************
+ * Print remaining free base memory
+ *****************************************************************************
+ */
+print_free_basemem:
+ movw $10f, %si
+ call print_message
+ movw %fs:(0x13), %ax
+ call print_word
+ movw $20f, %si
+ call print_message
+ .section ".prefix.data"
+10: .asciz " "
+20: .asciz "kB free base memory after PXE unload\n"
+ .previous
+#endif /* PXELOADER_KEEP_UNDI */
+
+/*****************************************************************************
+ * Exit point
+ *****************************************************************************
+ */
+finished:
+ jmp run_etherboot
+
+/*****************************************************************************
+ * Subroutine: print segment:offset address
+ *
+ * Parameters:
+ * %es:%di : segment:offset address to print
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+print_segoff:
+ /* Preserve registers */
+ pushw %ax
+ /* Print "<segment>:offset" */
+ movw %es, %ax
+ call print_hex_word
+ movb $':', %al
+ call print_character
+ movw %di, %ax
+ call print_hex_word
+ /* Restore registers and return */
+ popw %ax
+ ret
+
+/*****************************************************************************
+ * Subroutine: print decimal word
+ *
+ * Parameters:
+ * %ax : word to print
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+print_word:
+ /* Preserve registers */
+ pushw %ax
+ pushw %bx
+ pushw %cx
+ pushw %dx
+ /* Build up digit sequence on stack */
+ movw $10, %bx
+ xorw %cx, %cx
+1: xorw %dx, %dx
+ divw %bx, %ax
+ pushw %dx
+ incw %cx
+ testw %ax, %ax
+ jnz 1b
+ /* Print digit sequence */
+1: popw %ax
+ call print_hex_nibble
+ loop 1b
+ /* Restore registers and return */
+ popw %dx
+ popw %cx
+ popw %bx
+ popw %ax
+ ret
+
+/*****************************************************************************
+ * Subroutine: print PCI bus:dev.fn
+ *
+ * Parameters:
+ * %ax : PCI bus:dev.fn to print
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+print_pci_busdevfn:
+ /* Preserve registers */
+ pushw %ax
+ /* Print bus */
+ xchgb %al, %ah
+ call print_hex_byte
+ /* Print ":" */
+ movb $':', %al
+ call print_character
+ /* Print device */
+ movb %ah, %al
+ shrb $3, %al
+ call print_hex_byte
+ /* Print "." */
+ movb $'.', %al
+ call print_character
+ /* Print function */
+ movb %ah, %al
+ andb $0x07, %al
+ call print_hex_nibble
+ /* Restore registers and return */
+ popw %ax
+ ret
+
+/*****************************************************************************
+ * Subroutine: zero 1kB block of base memory
+ *
+ * Parameters:
+ * %si : block to zero (in kB)
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+zero_kb:
+ /* Preserve registers */
+ pushw %ax
+ pushw %cx
+ pushw %di
+ pushw %es
+ /* Zero block */
+ movw %si, %ax
+ shlw $6, %ax
+ movw %ax, %es
+ movw $0x400, %cx
+ xorw %di, %di
+ xorw %ax, %ax
+ rep stosb
+ /* Restore registers and return */
+ popw %es
+ popw %di
+ popw %cx
+ popw %ax
+ ret
+
+/*****************************************************************************
+ * Subroutine: free and zero base memory
+ *
+ * Parameters:
+ * %si : Expected current free base memory counter (in kB)
+ * %di : Desired new free base memory counter (in kB)
+ * %fs : BIOS data segment (0x40)
+ * Returns:
+ * %ax : Actual new free base memory counter (in kB)
+ *
+ * The base memory from %si kB to %di kB is unconditionally zeroed.
+ * It will be freed if and only if the expected current free base
+ * memory counter (%si) matches the actual current free base memory
+ * counter in 0x40:0x13; if this does not match then the memory will
+ * be leaked.
+ *****************************************************************************
+ */
+free_basemem:
+ /* Zero base memory */
+ pushw %si
+1: cmpw %si, %di
+ je 2f
+ call zero_kb
+ incw %si
+ jmp 1b
+2: popw %si
+ /* Free base memory */
+ movw %fs:(0x13), %ax /* Current FBMS to %ax */
+ cmpw %ax, %si /* Update FBMS only if "old" value */
+ jne 1f /* is correct */
+ movw %di, %ax
+1: movw %ax, %fs:(0x13)
+ ret
+
+/*****************************************************************************
+ * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API.
+ *
+ * Parameters:
+ * %bx : PXE API call number
+ * %ds:pxe_parameter_structure : Parameters for PXE API call
+ * Returns:
+ * %ax : PXE status code (not exit code)
+ * CF set if %ax is non-zero
+ *****************************************************************************
+ */
+pxe_call:
+ /* Preserve registers */
+ pushw %di
+ pushw %es
+ /* Set up registers for PXENV+ API. %bx already set up */
+ pushw %ds
+ popw %es
+ movw $pxe_parameter_structure, %di
+ /* Set up stack for !PXE API */
+ pushw %es
+ pushw %di
+ pushw %bx
+ /* Make the API call */
+ lcall *entry_segoff
+ /* Reset the stack */
+ addw $6, %sp
+ movw pxe_parameter_structure, %ax
+ clc
+ testw %ax, %ax
+ jz 1f
+ stc
+1: /* Restore registers and return */
+ popw %es
+ popw %di
+ ret
+
+/*****************************************************************************
+ * Subroutine: print PXE API call error message
+ *
+ * Parameters:
+ * %ax : PXE status code
+ * %bx : PXE API call number
+ * Returns:
+ * Nothing
+ *****************************************************************************
+ */
+print_pxe_error:
+ pushw %si
+ movw $10f, %si
+ call print_message
+ xchgw %ax, %bx
+ call print_hex_word
+ movw $20f, %si
+ call print_message
+ xchgw %ax, %bx
+ call print_hex_word
+ movw $30f, %si
+ call print_message
+ popw %si
+ ret
+ .section ".prefix.data"
+10: .asciz " UNDI API call "
+20: .asciz " failed: status code "
+30: .asciz "\n"
+ .previous
+
+/*****************************************************************************
+ * PXE data structures
+ *****************************************************************************
+ */
+
+pxe_parameter_structure: .fill 20
+
+undi_code_segoff:
+undi_code_size: .word 0
+undi_code_segment: .word 0
+
+undi_data_segoff:
+undi_data_size: .word 0
+undi_data_segment: .word 0
+
+/* The following fields are part of a struct undi_device */
+
+undi_device:
+
+pxenv_segoff:
+pxenv_offset: .word 0
+pxenv_segment: .word 0
+
+ppxe_segoff:
+ppxe_offset: .word 0
+ppxe_segment: .word 0
+
+entry_segoff:
+entry_offset: .word 0
+entry_segment: .word 0
+
+return_stack_segoff:
+return_stack_offset: .long 0
+return_stack_segment: .word 0
+
+return_type: .word 0 /* Default: unload PXE and boot next */
+
+undi_fbms_start: .word 0
+undi_fbms_end: .word 0
+
+pci_busdevfn: .word UNDI_NO_PCI_BUSDEVFN
+isapnp_csn: .word UNDI_NO_ISAPNP_CSN
+isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT
+
+pci_vendor: .word 0
+pci_device: .word 0
+flags: .word UNDI_FL_STARTED
+
+ .equ undi_device_size, ( . - undi_device )
+
+/*****************************************************************************
+ * Run Etherboot main code
+ *****************************************************************************
+ */
+run_etherboot:
+ /* Install Etherboot */
+ call install
+
+ /* Set up real-mode stack */
+ movw %bx, %ss
+ movw $_estack16, %sp
+
+#ifdef PXELOADER_KEEP_UNDI
+ /* Copy our undi_device structure to the preloaded_undi variable */
+ movw %bx, %es
+ movw $preloaded_undi, %di
+ movw $undi_device, %si
+ movw $undi_device_size, %cx
+ rep movsb
+#endif
+
+ /* Jump to .text16 segment with %ds pointing to .data16 */
+ movw %bx, %ds
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "ax", @progbits
+1:
+ /* Run main program */
+ pushl $main
+ pushw %cs
+ call prot_call
+ popl %eax /* discard */
+
+#ifdef PXELOADER_KEEP_UNDI
+ /* Boot next device */
+ movw $0x40, %ax
+ movw %ax, %fs
+ movw $preloaded_undi,%bx
+
+ cli
+ movw %ss:return_type-undi_device(%bx),%ax
+ lssl %ss:return_stack_segoff-undi_device(%bx), %esp
+ movw %sp,%bp
+ movw %ax,38(%bp) /* Overwrite return AX value */
+ popw %fs:0x13 /* 0 */
+ popw %gs /* 2 */
+ popw %fs /* 4 */
+ popw %es /* 6 */
+ popw %ds /* 8 */
+ popal /* 10, 14, 18, 22, 26, 30, 34, 38 */
+ popfl /* 42 */
+ lret /* 46 */
+#else
+ int $0x18
+#endif
+
+ .previous
diff --git a/gpxe/src/arch/i386/prefix/romprefix.S b/gpxe/src/arch/i386/prefix/romprefix.S
new file mode 100644
index 00000000..d37cce94
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/romprefix.S
@@ -0,0 +1,383 @@
+/* At entry, the processor is in 16 bit real mode and the code is being
+ * executed from an address it was not linked to. Code must be pic and
+ * 32 bit sensitive until things are fixed up.
+ *
+ * Also be very careful as the stack is at the rear end of the interrupt
+ * table so using a noticeable amount of stack space is a no-no.
+ */
+
+#define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
+#define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
+#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
+#define PNP_GET_BBS_VERSION 0x60
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+
+ .org 0x00
+romheader:
+ .word 0xAA55 /* BIOS extension signature */
+romheader_size: .byte _load_size_sect /* Size in 512-byte blocks */
+ jmp init /* Initialisation vector */
+checksum:
+ .byte 0
+ .org 0x16
+ .word undiheader
+ .org 0x18
+ .word pciheader
+ .org 0x1a
+ .word pnpheader
+ .size romheader, . - romheader
+
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBB"
+ .long romheader_size
+ .long 512
+ .long 0
+ .previous
+
+pciheader:
+ .ascii "PCIR" /* Signature */
+ .word pci_vendor_id /* Vendor ID */
+ .word pci_device_id /* Device ID */
+ .word 0x0000 /* pointer to vital product data */
+ .word pciheader_len /* PCI data structure length */
+ .byte 0x00 /* PCI data structure revision */
+ .byte 0x02 /* Device Base Type code */
+ .byte 0x00 /* Device Sub-Type code */
+ .byte 0x00 /* Device Interface Type code */
+pciheader_size: .word _load_size_sect /* Image length same as offset 02h */
+ .word 0x0001 /* revision level of code/data */
+ .byte 0x00 /* code type */
+ .byte 0x80 /* Flags (last PCI data structure) */
+ .word 0x0000 /* reserved */
+ .equ pciheader_len, . - pciheader
+ .size pciheader, . - pciheader
+
+ .section ".zinfo.fixup", "a" /* Compressor fixup information */
+ .ascii "SUBW"
+ .long pciheader_size
+ .long 512
+ .long 0
+ .previous
+
+pnpheader:
+ .ascii "$PnP" /* Signature */
+ .byte 0x01 /* Structure revision */
+ .byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */
+ .word 0x0000 /* Offset of next header */
+ .byte 0x00 /* Reserved */
+ .byte 0x00 /* Checksum */
+ .long 0x00000000 /* Device identifier */
+ .word mfgstr /* Manufacturer string */
+ .word prodstr /* Product name */
+ .byte 0x02 /* Device base type code */
+ .byte 0x00 /* Device sub-type code */
+ .byte 0x00 /* Device interface type code */
+ .byte 0x54 /* Device indicator */
+ .word 0x0000 /* Boot connection vector */
+ .word 0x0000 /* Disconnect vector */
+ .word bev_entry /* Boot execution vector */
+ .word 0x0000 /* Reserved */
+ .word 0x0000 /* Static resource information vector*/
+ .equ pnpheader_len, . - pnpheader
+ .size pnpheader, . - pnpheader
+
+mfgstr:
+ .asciz "http://etherboot.org"
+ .size mfgstr, . - mfgstr
+prodstr:
+ .asciz "gPXE"
+ .size prodstr, . - prodstr
+
+undiheader:
+ .ascii "UNDI" /* Signature */
+ .byte undiheader_len /* Length of structure */
+ .byte 0 /* Checksum */
+ .byte 0 /* Structure revision */
+ .byte 0,1,2 /* PXE version: 2.1.0 */
+ .word undiloader /* Offset to loader routine */
+ .word _data16_size /* Stack segment size */
+ .word _data16_size /* Data segment size */
+ .word _text16_size /* Code segment size */
+ .equ undiheader_len, . - undiheader
+ .size undiheader, . - undiheader
+
+/* Initialisation (called once during POST)
+ *
+ * Determine whether or not this is a PnP system via a signature
+ * check. If it is PnP, return to the PnP BIOS indicating that we are
+ * a boot-capable device; the BIOS will call our boot execution vector
+ * if it wants to boot us. If it is not PnP, hook INT 19.
+ */
+init:
+ /* Preserve registers, clear direction flag, set %ds=%cs */
+ pushaw
+ pushw %ds
+ pushw %es
+ cld
+ pushw %cs
+ popw %ds
+ /* Print message as early as possible */
+ movw $init_message, %si
+ call print_message
+ /* Check for PnP BIOS */
+ testw $0x0f, %di /* PnP signature must be aligned - bochs */
+ jnz hook_int19 /* uses unalignment to indicate 'fake' PnP. */
+ cmpl $PNP_SIGNATURE, %es:0(%di)
+ jne hook_int19
+ /* Is PnP: print PnP message */
+ movw $init_message_pnp, %si
+ call print_message
+ xchgw %bx, %bx
+ /* Check for BBS */
+ pushw %es:0x1b(%di) /* Real-mode data segment */
+ pushw %ds /* &(bbs_version) */
+ pushw $bbs_version
+ pushw $PNP_GET_BBS_VERSION
+ lcall *%es:0xd(%di)
+ addw $8, %sp
+ testw %ax, %ax
+ jne hook_int19
+ movw $init_message_bbs, %si
+ call print_message
+ jmp hook_bbs
+ /* Not BBS-compliant - must hook INT 19 */
+hook_int19:
+ movw $init_message_int19, %si
+ call print_message
+ xorw %ax, %ax
+ movw %ax, %es
+ pushw %cs
+ pushw $int19_entry
+ popl %es:( 0x19 * 4 )
+hook_bbs:
+ /* Check for PMM */
+ movw $( 0xe000 - 1 ), %di
+pmm_scan:
+ incw %di
+ jz no_pmm
+ movw %di, %es
+ cmpl $PMM_SIGNATURE, %es:0
+ jne pmm_scan
+ xorw %bx, %bx
+ xorw %si, %si
+ movzbw %es:5, %cx
+1: es lodsb
+ addb %al, %bl
+ loop 1b
+ jnz pmm_scan
+ /* PMM found: print PMM message */
+ movw $init_message_pmm, %si
+ call print_message
+ /* Try to allocate 2MB block via PMM */
+ pushw $0x0006 /* Aligned, extended memory */
+ pushl $0xffffffff /* No handle */
+ pushl $( 0x00200000 / 16 ) /* 2MB in paragraphs */
+ pushw $0x0000 /* pmmAllocate */
+ lcall *%es:7
+ addw $12, %sp
+ testw %dx, %dx /* %ax==0 even on success, since align=2MB */
+ jnz gotpmm
+ movw $init_message_pmm_failed, %si
+ call print_message
+ jmp no_pmm
+gotpmm: /* PMM allocation succeeded: copy ROM to PMM block */
+ pushal /* PMM presence implies 1kB stack */
+ movw %ax, %es /* %ax=0 already - see above */
+ pushw %dx
+ pushw %ax
+ popl %edi
+ movl %edi, image_source
+ xorl %esi, %esi
+ movzbl romheader_size, %ecx
+ shll $9, %ecx
+ addr32 rep movsb /* PMM presence implies flat real mode */
+ movl %edi, decompress_to
+ /* Shrink ROM and update checksum */
+ xorw %bx, %bx
+ xorw %si, %si
+ movw $_prefix_size_sect, %cx
+ movb %cl, romheader_size
+ shlw $9, %cx
+1: lodsb
+ addb %al, %bl
+ loop 1b
+ subb %bl, checksum
+ popal
+no_pmm:
+ /* Print CRLF to terminate messages */
+ movw $'\n', %ax
+ call print_character
+ /* Restore registers */
+ popw %es
+ popw %ds
+ popaw
+ /* Indicate boot capability to PnP BIOS, if present */
+ movw $0x20, %ax
+ lret
+ .size init, . - init
+
+init_message:
+ .asciz "gPXE (http://etherboot.org) -"
+ .size init_message, . - init_message
+init_message_pnp:
+ .asciz " PnP"
+ .size init_message_pnp, . - init_message_pnp
+init_message_bbs:
+ .asciz " BBS"
+ .size init_message_bbs, . - init_message_bbs
+init_message_pmm:
+ .asciz " PMM"
+ .size init_message_pmm, . - init_message_pmm
+init_message_pmm_failed:
+ .asciz "(failed)"
+ .size init_message_pmm_failed, . - init_message_pmm_failed
+init_message_int19:
+ .asciz " INT19"
+ .size init_message_int19, . - init_message_int19
+
+/* ROM image location
+ *
+ * May be either within option ROM space, or within PMM-allocated block.
+ */
+image_source:
+ .long 0
+ .size image_source, . - image_source
+
+/* Temporary decompression area
+ *
+ * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
+ */
+decompress_to:
+ .long HIGHMEM_LOADPOINT
+ .size decompress_to, . - decompress_to
+
+/* BBS version
+ *
+ * Filled in by BBS BIOS. We ignore the value.
+ */
+bbs_version:
+ .word 0
+
+/* Boot Execution Vector entry point
+ *
+ * Called by the PnP BIOS when it wants to boot us.
+ */
+bev_entry:
+ pushw %cs
+ call exec
+ lret
+ .size bev_entry, . - bev_entry
+
+/* INT19 entry point
+ *
+ * Called via the hooked INT 19 if we detected a non-PnP BIOS.
+ */
+int19_entry:
+ pushw %cs
+ call exec
+ /* No real way to return from INT19 */
+ int $0x18
+ .size int19_entry, . - int19_entry
+
+/* Execute as a boot device
+ *
+ */
+exec: /* Set %ds = %cs */
+ pushw %cs
+ popw %ds
+
+ /* Print message as soon as possible */
+ movw $exec_message, %si
+ call print_message
+
+ /* Store magic word on BIOS stack and remember BIOS %ss:sp */
+ pushl $STACK_MAGIC
+ movw %ss, %dx
+ movw %sp, %bp
+
+ /* Obtain a reasonably-sized temporary stack */
+ xorw %ax, %ax
+ movw %ax, %ss
+ movw $0x7c00, %sp
+
+ /* Install gPXE */
+ movl image_source, %esi
+ movl decompress_to, %edi
+ call alloc_basemem
+ call install_prealloc
+
+ /* Set up real-mode stack */
+ movw %bx, %ss
+ movw $_estack16, %sp
+
+ /* Jump to .text16 segment */
+ pushw %ax
+ pushw $1f
+ lret
+ .section ".text16", "awx", @progbits
+1: /* Call main() */
+ pushl $main
+ pushw %cs
+ call prot_call
+ /* No need to clean up stack; we are about to reload %ss:sp */
+
+ /* Restore BIOS stack */
+ movw %dx, %ss
+ movw %bp, %sp
+
+ /* Check magic word on BIOS stack */
+ popl %eax
+ cmpl $STACK_MAGIC, %eax
+ jne 1f
+ /* BIOS stack OK: return to caller */
+ lret
+1: /* BIOS stack corrupt: use INT 18 */
+ int $0x18
+ .previous
+
+exec_message:
+ .asciz "gPXE starting boot\n"
+ .size exec_message, . - exec_message
+
+/* UNDI loader
+ *
+ * Called by an external program to load our PXE stack.
+ */
+undiloader:
+ /* Save registers */
+ pushl %esi
+ pushl %edi
+ pushw %es
+ pushw %bx
+ /* UNDI loader parameter structure address into %es:%di */
+ movw %sp, %bx
+ movw %ss:12(%bx), %di
+ movw %ss:14(%bx), %es
+ /* Install to specified real-mode addresses */
+ pushw %di
+ movw %es:12(%di), %bx
+ movw %es:14(%di), %ax
+ movl %cs:image_source, %esi
+ movl %cs:decompress_to, %edi
+ call install_prealloc
+ popw %di
+ /* Call UNDI loader C code */
+ pushl $pxe_loader_call
+ pushw %cs
+ pushw $1f
+ pushw %ax
+ pushw $prot_call
+ lret
+1: popw %bx /* discard */
+ popw %bx /* discard */
+ /* Restore registers and return */
+ popw %bx
+ popw %es
+ popl %edi
+ popl %esi
+ lret
+ .size undiloader, . - undiloader
diff --git a/gpxe/src/arch/i386/prefix/unnrv2b.S b/gpxe/src/arch/i386/prefix/unnrv2b.S
new file mode 100644
index 00000000..70167a14
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/unnrv2b.S
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * Originally this code was part of ucl the data compression library
+ * for upx the ``Ultimate Packer of eXecutables''.
+ *
+ * - Converted to gas assembly, and refitted to work with etherboot.
+ * Eric Biederman 20 Aug 2002
+ *
+ * - Structure modified to be a subroutine call rather than an
+ * executable prefix.
+ * Michael Brown 30 Mar 2004
+ *
+ * - Modified to be compilable as either 16-bit or 32-bit code.
+ * Michael Brown 9 Mar 2005
+ */
+
+/****************************************************************************
+ * This file provides the decompress() and decompress16() functions
+ * which can be called in order to decompress an image compressed with
+ * the nrv2b utility in src/util.
+ *
+ * These functions are designed to be called by the prefix. They are
+ * position-independent code.
+ *
+ * The same basic assembly code is used to compile both
+ * decompress() and decompress16().
+ ****************************************************************************
+ */
+
+ .text
+ .arch i386
+ .section ".prefix.lib", "ax", @progbits
+
+#ifdef CODE16
+/****************************************************************************
+ * decompress16 (real-mode near call, position independent)
+ *
+ * Decompress data in 16-bit mode
+ *
+ * Parameters (passed via registers):
+ * %ds:%esi - Start of compressed input data
+ * %es:%edi - Start of output buffer
+ * Returns:
+ * %ds:%esi - End of compressed input data
+ * %es:%edi - End of decompressed output data
+ * All other registers are preserved
+ *
+ * NOTE: It would be possible to build a smaller version of the
+ * decompression code for -DKEEP_IT_REAL by using
+ * #define REG(x) x
+ * to use 16-bit registers where possible. This would impose limits
+ * that the compressed data size must be in the range [1,65533-%si]
+ * and the uncompressed data size must be in the range [1,65536-%di]
+ * (where %si and %di are the input values for those registers). Note
+ * particularly that the lower limit is 1, not 0, and that the upper
+ * limit on the input (compressed) data really is 65533, since the
+ * algorithm may read up to three bytes beyond the end of the input
+ * data, since it reads dwords.
+ ****************************************************************************
+ */
+
+#define REG(x) e ## x
+#define ADDR32 addr32
+
+ .code16
+ .globl decompress16
+decompress16:
+
+#else /* CODE16 */
+
+/****************************************************************************
+ * decompress (32-bit protected-mode near call, position independent)
+ *
+ * Parameters (passed via registers):
+ * %ds:%esi - Start of compressed input data
+ * %es:%edi - Start of output buffer
+ * Returns:
+ * %ds:%esi - End of compressed input data
+ * %es:%edi - End of decompressed output data
+ * All other registers are preserved
+ ****************************************************************************
+ */
+
+#define REG(x) e ## x
+#define ADDR32
+
+ .code32
+ .globl decompress
+decompress:
+
+#endif /* CODE16 */
+
+#define xAX REG(ax)
+#define xCX REG(cx)
+#define xBP REG(bp)
+#define xSI REG(si)
+#define xDI REG(di)
+
+ /* Save registers */
+ push %xAX
+ pushl %ebx
+ push %xCX
+ push %xBP
+ /* Do the decompression */
+ cld
+ xor %xBP, %xBP
+ dec %xBP /* last_m_off = -1 */
+ jmp dcl1_n2b
+
+decompr_literals_n2b:
+ ADDR32 movsb
+decompr_loop_n2b:
+ addl %ebx, %ebx
+ jnz dcl2_n2b
+dcl1_n2b:
+ call getbit32
+dcl2_n2b:
+ jc decompr_literals_n2b
+ xor %xAX, %xAX
+ inc %xAX /* m_off = 1 */
+loop1_n2b:
+ call getbit1
+ adc %xAX, %xAX /* m_off = m_off*2 + getbit() */
+ call getbit1
+ jnc loop1_n2b /* while(!getbit()) */
+ sub $3, %xAX
+ jb decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */
+ shl $8, %xAX
+ ADDR32 movb (%xSI), %al /* m_off = (m_off - 3)*256 + src[ilen++] */
+ inc %xSI
+ xor $-1, %xAX
+ jz decompr_end_n2b /* if (m_off == 0xffffffff) goto decomp_end_n2b */
+ mov %xAX, %xBP /* last_m_off = m_off ?*/
+decompr_ebpeax_n2b:
+ xor %xCX, %xCX
+ call getbit1
+ adc %xCX, %xCX /* m_len = getbit() */
+ call getbit1
+ adc %xCX, %xCX /* m_len = m_len*2 + getbit()) */
+ jnz decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */
+ inc %xCX /* m_len++ */
+loop2_n2b:
+ call getbit1
+ adc %xCX, %xCX /* m_len = m_len*2 + getbit() */
+ call getbit1
+ jnc loop2_n2b /* while(!getbit()) */
+ inc %xCX
+ inc %xCX /* m_len += 2 */
+decompr_got_mlen_n2b:
+ cmp $-0xd00, %xBP
+ adc $1, %xCX /* m_len = m_len + 1 + (last_m_off > 0xd00) */
+ push %xSI
+ ADDR32 lea (%xBP,%xDI), %xSI /* m_pos = dst + olen + -m_off */
+ rep
+ es ADDR32 movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */
+ pop %xSI
+ jmp decompr_loop_n2b
+
+
+getbit1:
+ addl %ebx, %ebx
+ jnz 1f
+getbit32:
+ ADDR32 movl (%xSI), %ebx
+ sub $-4, %xSI /* sets carry flag */
+ adcl %ebx, %ebx
+1:
+ ret
+
+decompr_end_n2b:
+ /* Restore registers and return */
+ pop %xBP
+ pop %xCX
+ popl %ebx
+ pop %xAX
+ ret
diff --git a/gpxe/src/arch/i386/prefix/usbdisk.S b/gpxe/src/arch/i386/prefix/usbdisk.S
new file mode 100644
index 00000000..fa7d1956
--- /dev/null
+++ b/gpxe/src/arch/i386/prefix/usbdisk.S
@@ -0,0 +1,23 @@
+ .text
+ .arch i386
+ .section ".prefix", "awx", @progbits
+ .code16
+ .org 0
+
+#include "mbr.S"
+
+/* Partition table: ZIP-compatible partition 4, 64 heads, 32 sectors/track */
+ .org 446
+ .space 16
+ .space 16
+ .space 16
+ .byte 0x80, 0x01, 0x01, 0x00
+ .byte 0xeb, 0x3f, 0x20, 0x01
+ .long 0x00000020
+ .long 0x00000fe0
+
+ .org 510
+ .byte 0x55, 0xaa
+
+/* Skip to start of partition */
+ .org 32 * 512
diff --git a/gpxe/src/arch/i386/scripts/i386-kir.lds b/gpxe/src/arch/i386/scripts/i386-kir.lds
new file mode 100644
index 00000000..b213c605
--- /dev/null
+++ b/gpxe/src/arch/i386/scripts/i386-kir.lds
@@ -0,0 +1,196 @@
+/* -*- sh -*- */
+
+/*
+ * Linker script for i386 images
+ *
+ */
+
+OUTPUT_FORMAT ( "elf32-i386", "elf32-i386", "elf32-i386" )
+OUTPUT_ARCH ( i386 )
+ENTRY ( _entry )
+
+SECTIONS {
+
+ /* All sections in the resulting file have consecutive load
+ * addresses, but may have individual link addresses depending on
+ * the memory model being used.
+ *
+ * The linker symbols _prefix_link_addr, load_addr, and
+ * _max_align may be specified explicitly. If not specified, they
+ * will default to:
+ *
+ * _prefix_link_addr = 0
+ * _load_addr = 0
+ * _max_align = 16
+ *
+ * We guarantee alignment of virtual addresses to any alignment
+ * specified by the constituent object files (e.g. via
+ * __attribute__((aligned(x)))). Load addresses are guaranteed
+ * only up to _max_align. Provided that all loader and relocation
+ * code honours _max_align, this means that physical addresses are
+ * also guaranteed up to _max_align.
+ *
+ * Note that when using -DKEEP_IT_REAL, the UNDI segments are only
+ * guaranteed to be loaded on a paragraph boundary (i.e. 16-byte
+ * alignment). Using _max_align>16 will therefore not guarantee
+ * >16-byte alignment of physical addresses when -DKEEP_IT_REAL is
+ * used (though virtual addresses will still be fully aligned).
+ *
+ */
+
+ /*
+ * The prefix
+ */
+
+ _prefix_link_addr = DEFINED ( _prefix_link_addr ) ? _prefix_link_addr : 0;
+ . = _prefix_link_addr;
+ _prefix = .;
+
+ .prefix : AT ( _prefix_load_offset + __prefix ) {
+ __prefix = .;
+ _entry = .;
+ *(.prefix)
+ *(.prefix.*)
+ _eprefix_progbits = .;
+ }
+
+ _eprefix = .;
+
+ /*
+ * The 16-bit sections
+ */
+
+ _text16_link_addr = 0;
+ . = _text16_link_addr;
+ _text16 = .;
+
+ . += 1; /* Prevent NULL being valid */
+
+ .text16 : AT ( _text16_load_offset + __text16 ) {
+ __text16 = .;
+ *(.text.null_trap)
+ *(.text16)
+ *(.text16.*)
+ *(.text)
+ *(.text.*)
+ _etext16_progbits = .;
+ } = 0x9090
+
+ _etext16 = .;
+
+ _data16_link_addr = 0;
+ . = _data16_link_addr;
+ _data16 = .;
+
+ . += 1; /* Prevent NULL being valid */
+
+ .rodata16 : AT ( _data16_load_offset + __rodata16 ) {
+ __rodata16 = .;
+ *(.rodata16)
+ *(.rodata16.*)
+ *(.rodata)
+ *(.rodata.*)
+ }
+ .data16 : AT ( _data16_load_offset + __data16 ) {
+ __data16 = .;
+ *(.data16)
+ *(.data16.*)
+ *(.data)
+ *(.data.*)
+ *(SORT(.tbl.*)) /* Various tables. See include/tables.h */
+ _edata16_progbits = .;
+ }
+ .bss16 : AT ( _data16_load_offset + __bss16 ) {
+ __bss16 = .;
+ _bss16 = .;
+ *(.bss16)
+ *(.bss16.*)
+ *(.bss)
+ *(.bss.*)
+ *(COMMON)
+ _ebss16 = .;
+ }
+ .stack16 : AT ( _data16_load_offset + __stack16 ) {
+ __stack16 = .;
+ *(.stack16)
+ *(.stack16.*)
+ *(.stack)
+ *(.stack.*)
+ }
+
+ _edata16 = .;
+
+ _end = .;
+
+ /*
+ * Dispose of the comment and note sections to make the link map
+ * easier to read
+ */
+
+ /DISCARD/ : {
+ *(.comment)
+ *(.note)
+ }
+
+ /*
+ * Load address calculations. The slightly obscure nature of the
+ * calculations is because ALIGN(x) can only operate on the
+ * location counter.
+ */
+
+ _max_align = DEFINED ( _max_align ) ? _max_align : 16;
+ _load_addr = DEFINED ( _load_addr ) ? _load_addr : 0;
+
+ . = _load_addr;
+
+ . -= _prefix_link_addr;
+ _prefix_load_offset = ALIGN ( _max_align );
+ _prefix_load_addr = _prefix_link_addr + _prefix_load_offset;
+ _prefix_size = _eprefix - _prefix;
+ _prefix_progbits_size = _eprefix_progbits - _prefix;
+ . = _prefix_load_addr + _prefix_progbits_size;
+
+ . -= _text16_link_addr;
+ _text16_load_offset = ALIGN ( _max_align );
+ _text16_load_addr = _text16_link_addr + _text16_load_offset;
+ _text16_size = _etext16 - _text16;
+ _text16_progbits_size = _etext16_progbits - _text16;
+ . = _text16_load_addr + _text16_progbits_size;
+
+ . -= _data16_link_addr;
+ _data16_load_offset = ALIGN ( _max_align );
+ _data16_load_addr = _data16_link_addr + _data16_load_offset;
+ _data16_size = _edata16 - _data16;
+ _data16_progbits_size = _edata16_progbits - _data16;
+ . = _data16_load_addr + _data16_progbits_size;
+
+ . = ALIGN ( _max_align );
+
+ _load_size = . - _load_addr;
+
+ /*
+ * Alignment checks. ALIGN() can only operate on the location
+ * counter, so we set the location counter to each value we want
+ * to check.
+ */
+
+ . = _prefix_load_addr - _prefix_link_addr;
+ _assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
+ "_prefix is badly aligned" );
+
+ . = _text16_load_addr - _text16_link_addr;
+ _assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
+ "_text16 is badly aligned" );
+
+ . = _data16_load_addr - _data16_link_addr;
+ _assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
+ "_data16 is badly aligned" );
+
+ /*
+ * Values calculated to save code from doing it
+ */
+ _text16_size_pgh = ( ( _text16_size + 15 ) / 16 );
+ _data16_size_pgh = ( ( _data16_size + 15 ) / 16 );
+ _load_size_pgh = ( ( _load_size + 15 ) / 16 );
+ _load_size_sect = ( ( _load_size + 511 ) / 512 );
+}
diff --git a/gpxe/src/arch/i386/scripts/i386.lds b/gpxe/src/arch/i386/scripts/i386.lds
new file mode 100644
index 00000000..a5a01056
--- /dev/null
+++ b/gpxe/src/arch/i386/scripts/i386.lds
@@ -0,0 +1,272 @@
+/* -*- sh -*- */
+
+/*
+ * Linker script for i386 images
+ *
+ */
+
+OUTPUT_FORMAT ( "elf32-i386", "elf32-i386", "elf32-i386" )
+OUTPUT_ARCH ( i386 )
+ENTRY ( _entry )
+
+SECTIONS {
+
+ /* All sections in the resulting file have consecutive load
+ * addresses, but may have individual link addresses depending on
+ * the memory model being used.
+ *
+ * We guarantee alignment of virtual addresses to any alignment
+ * specified by the constituent object files (e.g. via
+ * __attribute__((aligned(x)))). Load addresses are guaranteed
+ * only up to _max_align. Provided that all loader and relocation
+ * code honours _max_align, this means that physical addresses are
+ * also guaranteed up to _max_align.
+ *
+ * Note that when using -DKEEP_IT_REAL, the UNDI segments are only
+ * guaranteed to be loaded on a paragraph boundary (i.e. 16-byte
+ * alignment). Using _max_align>16 will therefore not guarantee
+ * >16-byte alignment of physical addresses when -DKEEP_IT_REAL is
+ * used (though virtual addresses will still be fully aligned).
+ *
+ */
+
+ /*
+ * The prefix
+ */
+
+ _prefix_link_addr = 0;
+ . = _prefix_link_addr;
+ _prefix = .;
+
+ .prefix : AT ( _prefix_load_offset + __prefix ) {
+ __prefix = .;
+ _entry = .;
+ *(.prefix)
+ *(.prefix.*)
+ _eprefix_progbits = .;
+ }
+
+ _eprefix = .;
+
+ /*
+ * The 16-bit sections, if present
+ */
+
+ _text16_link_addr = 0;
+ . = _text16_link_addr;
+ _text16 = .;
+
+ . += 1; /* Prevent NULL being valid */
+
+ .text16 : AT ( _text16_load_offset + __text16 ) {
+ __text16 = .;
+ *(.text16)
+ *(.text16.*)
+ _etext16_progbits = .;
+ } = 0x9090
+
+ _etext16 = .;
+
+ _data16_link_addr = 0;
+ . = _data16_link_addr;
+ _data16 = .;
+
+ . += 1; /* Prevent NULL being valid */
+
+ .rodata16 : AT ( _data16_load_offset + __rodata16 ) {
+ __rodata16 = .;
+ *(.rodata16)
+ *(.rodata16.*)
+ }
+ .data16 : AT ( _data16_load_offset + __data16 ) {
+ __data16 = .;
+ *(.data16)
+ *(.data16.*)
+ _edata16_progbits = .;
+ }
+ .bss16 : AT ( _data16_load_offset + __bss16 ) {
+ __bss16 = .;
+ _bss16 = .;
+ *(.bss16)
+ *(.bss16.*)
+ _ebss16 = .;
+ }
+ .stack16 : AT ( _data16_load_offset + __stack16 ) {
+ __stack16 = .;
+ *(.stack16)
+ *(.stack16.*)
+ }
+
+ _edata16 = .;
+
+ /*
+ * The 32-bit sections
+ */
+
+ _textdata_link_addr = 0;
+ . = _textdata_link_addr;
+ _textdata = .;
+
+ _text = .;
+
+ . += 1; /* Prevent NULL being valid */
+
+ .text : AT ( _textdata_load_offset + __text ) {
+ __text = .;
+ *(.text.null_trap)
+ *(.text)
+ *(.text.*)
+ } = 0x9090
+
+ _etext = .;
+
+ _data = .;
+
+ .rodata : AT ( _textdata_load_offset + __rodata ) {
+ __rodata = .;
+ *(.rodata)
+ *(.rodata.*)
+ }
+ .data : AT ( _textdata_load_offset + __data ) {
+ __data = .;
+ *(.data)
+ *(.data.*)
+ *(SORT(.tbl.*)) /* Various tables. See include/tables.h */
+ _etextdata_progbits = .;
+ }
+ .bss : AT ( _textdata_load_offset + __bss ) {
+ __bss = .;
+ _bss = .;
+ *(.bss)
+ *(.bss.*)
+ *(COMMON)
+ _ebss = .;
+ }
+ .stack : AT ( _textdata_load_offset + __stack ) {
+ __stack = .;
+ *(.stack)
+ *(.stack.*)
+ }
+
+ _edata = .;
+
+ _etextdata = .;
+
+ _end = .;
+
+ /*
+ * Compressor information block
+ */
+
+ _zinfo_link_addr = 0;
+ . = _zinfo_link_addr;
+ _zinfo = .;
+
+ .zinfo : AT ( _zinfo_load_offset + __zinfo ) {
+ __zinfo = .;
+ _entry = .;
+ *(.zinfo)
+ *(.zinfo.*)
+ _ezinfo_progbits = .;
+ }
+
+ _ezinfo = .;
+
+ /*
+ * Dispose of the comment and note sections to make the link map
+ * easier to read
+ */
+
+ /DISCARD/ : {
+ *(.comment)
+ *(.note)
+ }
+
+ /*
+ * Load address calculations. The slightly obscure nature of the
+ * calculations is because ALIGN(x) can only operate on the
+ * location counter.
+ */
+
+ _max_align = 16;
+ _load_addr = 0;
+
+ . = _load_addr;
+
+ . -= _prefix_link_addr;
+ _prefix_load_offset = ALIGN ( _max_align );
+ _prefix_load_addr = _prefix_link_addr + _prefix_load_offset;
+ _prefix_size = _eprefix - _prefix;
+ _prefix_progbits_size = _eprefix_progbits - _prefix;
+ . = _prefix_load_addr + _prefix_progbits_size;
+
+ . -= _text16_link_addr;
+ _text16_load_offset = ALIGN ( _max_align );
+ _text16_load_addr = _text16_link_addr + _text16_load_offset;
+ _text16_size = _etext16 - _text16;
+ _text16_progbits_size = _etext16_progbits - _text16;
+ . = _text16_load_addr + _text16_progbits_size;
+
+ . -= _data16_link_addr;
+ _data16_load_offset = ALIGN ( _max_align );
+ _data16_load_addr = _data16_link_addr + _data16_load_offset;
+ _data16_size = _edata16 - _data16;
+ _data16_progbits_size = _edata16_progbits - _data16;
+ . = _data16_load_addr + _data16_progbits_size;
+
+ . -= _textdata_link_addr;
+ _textdata_load_offset = ALIGN ( _max_align );
+ _textdata_load_addr = _textdata_link_addr + _textdata_load_offset;
+ _textdata_size = _etextdata - _textdata;
+ _textdata_progbits_size = _etextdata_progbits - _textdata;
+ . = _textdata_load_addr + _textdata_progbits_size;
+
+ _load_size = . - _load_addr;
+
+ . -= _zinfo_link_addr;
+ _zinfo_load_offset = ALIGN ( _max_align );
+ _zinfo_load_addr = _zinfo_link_addr + _zinfo_load_offset;
+ _zinfo_size = _ezinfo - _zinfo;
+ _zinfo_progbits_size = _ezinfo_progbits - _zinfo;
+ . = _zinfo_load_addr + _zinfo_progbits_size;
+
+ _payload_offset = _text16_load_offset;
+
+ /*
+ * Alignment checks. ALIGN() can only operate on the location
+ * counter, so we set the location counter to each value we want
+ * to check.
+ */
+
+ . = _prefix_load_addr - _prefix_link_addr;
+ _assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
+ "_prefix is badly aligned" );
+
+ . = _text16_load_addr - _text16_link_addr;
+ _assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
+ "_text16 is badly aligned" );
+
+ . = _data16_load_addr - _data16_link_addr;
+ _assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
+ "_data16 is badly aligned" );
+
+ . = _textdata_load_addr - _textdata_link_addr;
+ _assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
+ "_text is badly aligned" );
+
+ /*
+ * Values calculated to save code from doing it
+ */
+ _prefix_size_pgh = ( ( _prefix_size + 15 ) / 16 );
+ _prefix_size_sect = ( ( _prefix_size + 511 ) / 512 );
+ _text16_size_pgh = ( ( _text16_size + 15 ) / 16 );
+ _data16_size_pgh = ( ( _data16_size + 15 ) / 16 );
+
+ /*
+ * Load sizes in paragraphs and sectors. Note that wherever the
+ * _load_size variables are used, there must be a corresponding
+ * .zinfo.fixup section.
+ */
+ _load_size_pgh = ( ( _load_size + 15 ) / 16 );
+ _load_size_sect = ( ( _load_size + 511 ) / 512 );
+}
diff --git a/gpxe/src/arch/i386/transitions/libkir.S b/gpxe/src/arch/i386/transitions/libkir.S
new file mode 100644
index 00000000..1023ddd0
--- /dev/null
+++ b/gpxe/src/arch/i386/transitions/libkir.S
@@ -0,0 +1,254 @@
+/*
+ * libkir: a transition library for -DKEEP_IT_REAL
+ *
+ * Michael Brown <mbrown@fensystems.co.uk>
+ *
+ */
+
+/****************************************************************************
+ * This file defines libkir: an interface between external and
+ * internal environments when -DKEEP_IT_REAL is used, so that both
+ * internal and external environments are in real mode. It deals with
+ * switching data segments and the stack. It provides the following
+ * functions:
+ *
+ * ext_to_kir & switch between external and internal (kir)
+ * kir_to_ext environments, preserving all non-segment
+ * registers
+ *
+ * kir_call issue a call to an internal routine from external
+ * code
+ *
+ * libkir is written to avoid assuming that segments are anything
+ * other than opaque data types, and also avoids assuming that the
+ * stack pointer is 16-bit. This should enable it to run just as well
+ * in 16:16 or 16:32 protected mode as in real mode.
+ ****************************************************************************
+ */
+
+/* Breakpoint for when debugging under bochs */
+#define BOCHSBP xchgw %bx, %bx
+
+ .text
+ .arch i386
+ .section ".text16", "awx", @progbits
+ .code16
+
+/****************************************************************************
+ * init_libkir (real-mode or 16:xx protected-mode far call)
+ *
+ * Initialise libkir ready for transitions to the kir environment
+ *
+ * Parameters:
+ * %cs : .text16 segment
+ * %ds : .data16 segment
+ ****************************************************************************
+ */
+ .globl init_libkir
+init_libkir:
+ /* Record segment registers */
+ pushw %ds
+ popw %cs:kir_ds
+ lret
+
+/****************************************************************************
+ * ext_to_kir (real-mode or 16:xx protected-mode near call)
+ *
+ * Switch from external stack and segment registers to internal stack
+ * and segment registers. %ss:sp is restored from the saved kir_ds
+ * and kir_sp. %ds, %es, %fs and %gs are all restored from the saved
+ * kir_ds. All other registers are preserved.
+ *
+ * %cs:0000 must point to the start of the runtime image code segment
+ * on entry.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+
+ .globl ext_to_kir
+ext_to_kir:
+ /* Record external segment registers */
+ movw %ds, %cs:ext_ds
+ pushw %cs
+ popw %ds /* Set %ds = %cs for easier access to variables */
+ movw %es, %ds:ext_es
+ movw %fs, %ds:ext_fs
+ movw %gs, %ds:ext_fs
+
+ /* Preserve registers */
+ movw %ax, %ds:save_ax
+
+ /* Extract near return address from stack */
+ popw %ds:save_retaddr
+
+ /* Record external %ss:esp */
+ movw %ss, %ds:ext_ss
+ movl %esp, %ds:ext_esp
+
+ /* Load internal segment registers and stack pointer */
+ movw %ds:kir_ds, %ax
+ movw %ax, %ss
+ movzwl %ds:kir_sp, %esp
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+1:
+
+ /* Place return address on new stack */
+ pushw %cs:save_retaddr
+
+ /* Restore registers and return */
+ movw %cs:save_ax, %ax
+ ret
+
+/****************************************************************************
+ * kir_to_ext (real-mode or 16:xx protected-mode near call)
+ *
+ * Switch from internal stack and segment registers to external stack
+ * and segment registers. %ss:%esp is restored from the saved ext_ss
+ * and ext_esp. Other segment registers are restored from the
+ * corresponding locations. All other registers are preserved.
+ *
+ * Note that it is actually %ss that is recorded as kir_ds, on the
+ * assumption that %ss == %ds when kir_to_ext is called.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+
+ .globl kir_to_ext
+kir_to_ext:
+ /* Record near return address */
+ pushw %cs
+ popw %ds /* Set %ds = %cs for easier access to variables */
+ popw %ds:save_retaddr
+
+ /* Record internal segment registers and %sp */
+ movw %ss, %ds:kir_ds
+ movw %sp, %ds:kir_sp
+
+ /* Load external segment registers and stack pointer */
+ movw %ds:ext_ss, %ss
+ movl %ds:ext_esp, %esp
+ movw %ds:ext_gs, %gs
+ movw %ds:ext_fs, %fs
+ movw %ds:ext_es, %es
+ movw %ds:ext_ds, %ds
+
+ /* Return */
+ pushw %cs:save_retaddr
+ ret
+
+/****************************************************************************
+ * kir_call (real-mode or 16:xx protected-mode far call)
+ *
+ * Call a specific C function in the internal code. The prototype of
+ * the C function must be
+ * void function ( struct i386_all_resg *ix86 );
+ * ix86 will point to a struct containing the real-mode registers
+ * at entry to kir_call.
+ *
+ * All registers will be preserved across kir_call(), unless the C
+ * function explicitly overwrites values in ix86. Interrupt status
+ * will also be preserved.
+ *
+ * Parameters:
+ * function : (32-bit) virtual address of C function to call
+ *
+ * Example usage:
+ * pushl $pxe_api_call
+ * lcall $UNDI_CS, $kir_call
+ * addw $4, %sp
+ * to call in to the C function
+ * void pxe_api_call ( struct i386_all_regs *ix86 );
+ ****************************************************************************
+ */
+
+ .globl kir_call
+kir_call:
+ /* Preserve flags. Must do this before any operation that may
+ * affect flags.
+ */
+ pushfl
+ popl %cs:save_flags
+
+ /* Disable interrupts. We do funny things with the stack, and
+ * we're not re-entrant.
+ */
+ cli
+
+ /* Extract address of internal routine from stack. We must do
+ * this without using (%bp), because we may be called with
+ * either a 16-bit or a 32-bit stack segment.
+ */
+ popl %cs:save_retaddr /* Scratch location */
+ popl %cs:save_function
+ subl $8, %esp /* Restore %esp */
+
+ /* Switch to internal stack. Note that the external stack is
+ * inaccessible once we're running internally (since we have
+ * no concept of 48-bit far pointers)
+ */
+ call ext_to_kir
+
+ /* Store external registers on internal stack */
+ pushl %cs:save_flags
+ pushal
+ pushl %cs:ext_fs_and_gs
+ pushl %cs:ext_ds_and_es
+ pushl %cs:ext_cs_and_ss
+
+ /* Push &ix86 on stack and call function */
+ sti
+ pushl %esp
+ data32 call *%cs:save_function
+ popl %eax /* discard */
+
+ /* Restore external registers from internal stack */
+ popl %cs:ext_cs_and_ss
+ popl %cs:ext_ds_and_es
+ popl %cs:ext_fs_and_gs
+ popal
+ popl %cs:save_flags
+
+ /* Switch to external stack */
+ call kir_to_ext
+
+ /* Restore flags */
+ pushl %cs:save_flags
+ popfl
+
+ /* Return */
+ lret
+
+/****************************************************************************
+ * Stored internal and external stack and segment registers
+ ****************************************************************************
+ */
+
+ext_cs_and_ss:
+ext_cs: .word 0
+ext_ss: .word 0
+ext_ds_and_es:
+ext_ds: .word 0
+ext_es: .word 0
+ext_fs_and_gs:
+ext_fs: .word 0
+ext_gs: .word 0
+ext_esp: .long 0
+
+ .globl kir_ds
+kir_ds: .word 0
+ .globl kir_sp
+kir_sp: .word _estack
+
+/****************************************************************************
+ * Temporary variables
+ ****************************************************************************
+ */
+save_ax: .word 0
+save_retaddr: .long 0
+save_flags: .long 0
+save_function: .long 0
diff --git a/gpxe/src/arch/i386/transitions/libpm.S b/gpxe/src/arch/i386/transitions/libpm.S
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/gpxe/src/arch/i386/transitions/libpm.S
diff --git a/gpxe/src/arch/i386/transitions/librm.S b/gpxe/src/arch/i386/transitions/librm.S
new file mode 100644
index 00000000..b1f9dd59
--- /dev/null
+++ b/gpxe/src/arch/i386/transitions/librm.S
@@ -0,0 +1,559 @@
+/*
+ * librm: a library for interfacing to real-mode code
+ *
+ * Michael Brown <mbrown@fensystems.co.uk>
+ *
+ */
+
+/* Drag in local definitions */
+#include "librm.h"
+
+/* For switches to/from protected mode */
+#define CR0_PE 1
+
+/* Size of various C data structures */
+#define SIZEOF_I386_SEG_REGS 12
+#define SIZEOF_I386_REGS 32
+#define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
+#define SIZEOF_I386_FLAGS 4
+#define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS )
+
+ .arch i386
+ .section ".text16", "ax", @progbits
+ .section ".text16.data", "aw", @progbits
+ .section ".data16", "aw", @progbits
+
+/****************************************************************************
+ * Global descriptor table
+ *
+ * Call init_librm to set up the GDT before attempting to use any
+ * protected-mode code.
+ *
+ * Define FLATTEN_REAL_MODE if you want to use so-called "flat real
+ * mode" with 4GB limits instead.
+ *
+ * NOTE: This must be located before prot_to_real, otherwise gas
+ * throws a "can't handle non absolute segment in `ljmp'" error due to
+ * not knowing the value of REAL_CS when the ljmp is encountered.
+ *
+ * Note also that putting ".word gdt_end - gdt - 1" directly into
+ * gdt_limit, rather than going via gdt_length, will also produce the
+ * "non absolute segment" error. This is most probably a bug in gas.
+ ****************************************************************************
+ */
+
+#ifdef FLATTEN_REAL_MODE
+#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
+#else
+#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
+#endif
+ .section ".data16"
+ .align 16
+gdt:
+gdt_limit: .word gdt_length - 1
+gdt_base: .long 0
+ .word 0 /* padding */
+
+ .org gdt + VIRTUAL_CS, 0
+virtual_cs: /* 32 bit protected mode code segment, virtual addresses */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+
+ .org gdt + VIRTUAL_DS, 0
+virtual_ds: /* 32 bit protected mode data segment, virtual addresses */
+ .word 0xffff, 0
+ .byte 0, 0x93, 0xcf, 0
+
+ .org gdt + PHYSICAL_CS, 0
+physical_cs: /* 32 bit protected mode code segment, physical addresses */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+
+ .org gdt + PHYSICAL_DS, 0
+physical_ds: /* 32 bit protected mode data segment, physical addresses */
+ .word 0xffff, 0
+ .byte 0, 0x93, 0xcf, 0
+
+ .org gdt + REAL_CS, 0
+real_cs: /* 16 bit real mode code segment */
+ .word 0xffff, 0
+ .byte 0, 0x9b, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0
+
+ .org gdt + REAL_DS
+real_ds: /* 16 bit real mode data segment */
+ .word 0xffff, 0
+ .byte 0, 0x93, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0
+
+gdt_end:
+ .equ gdt_length, gdt_end - gdt
+
+/****************************************************************************
+ * init_librm (real-mode far call, 16-bit real-mode far return address)
+ *
+ * Initialise the GDT ready for transitions to protected mode.
+ *
+ * Parameters:
+ * %cs : .text16 segment
+ * %ds : .data16 segment
+ * %edi : Physical base of protected-mode code (virt_offset)
+ ****************************************************************************
+ */
+ .section ".text16"
+ .code16
+ .globl init_librm
+init_librm:
+ /* Preserve registers */
+ pushl %eax
+ pushl %ebx
+
+ /* Store _virt_offset and set up virtual_cs and virtual_ds segments */
+ movl %edi, %eax
+ movw $virtual_cs, %bx
+ call set_seg_base
+ movw $virtual_ds, %bx
+ call set_seg_base
+ movl %edi, _virt_offset
+
+ /* Negate virt_offset */
+ negl %edi
+
+ /* Store rm_cs and _text16, set up real_cs segment */
+ xorl %eax, %eax
+ movw %cs, %ax
+ movw %ax, rm_cs
+ shll $4, %eax
+ movw $real_cs, %bx
+ call set_seg_base
+ addr32 leal (%eax, %edi), %ebx
+ movl %ebx, _text16
+
+ /* Store rm_ds and _data16, set up real_ds segment and set GDT base */
+ xorl %eax, %eax
+ movw %ds, %ax
+ movw %ax, %cs:rm_ds
+ shll $4, %eax
+ movw $real_ds, %bx
+ call set_seg_base
+ addr32 leal (%eax, %edi), %ebx
+ movl %ebx, _data16
+ addl $gdt, %eax
+ movl %eax, gdt_base
+
+ /* Restore registers */
+ negl %edi
+ popl %ebx
+ popl %eax
+ lret
+
+ .section ".text16"
+ .code16
+set_seg_base:
+1: movw %ax, 2(%bx)
+ rorl $16, %eax
+ movb %al, 4(%bx)
+ movb %ah, 7(%bx)
+ roll $16, %eax
+ ret
+
+/****************************************************************************
+ * real_to_prot (real-mode near call, 32-bit virtual return address)
+ *
+ * Switch from 16-bit real-mode to 32-bit protected mode with virtual
+ * addresses. The real-mode %ss:sp is stored in rm_ss and rm_sp, and
+ * the protected-mode %esp is restored from the saved pm_esp.
+ * Interrupts are disabled. All other registers may be destroyed.
+ *
+ * The return address for this function should be a 32-bit virtual
+ * address.
+ *
+ * Parameters:
+ * %ecx : number of bytes to move from RM stack to PM stack
+ *
+ ****************************************************************************
+ */
+ .section ".text16"
+ .code16
+real_to_prot:
+ /* Make sure we have our data segment available */
+ movw %cs:rm_ds, %ax
+ movw %ax, %ds
+
+ /* Add _virt_offset, _text16 and _data16 to stack to be
+ * copied, and also copy the return address.
+ */
+ pushl _virt_offset
+ pushl _text16
+ pushl _data16
+ addw $16, %cx /* %ecx must be less than 64kB anyway */
+
+ /* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */
+ xorl %ebp, %ebp
+ movw %ss, %bp
+ movzwl %sp, %edx
+ movl %ebp, %eax
+ shll $4, %eax
+ addr32 leal (%eax,%edx), %esi
+ subl _virt_offset, %esi
+
+ /* Switch to protected mode */
+ cli
+ data32 lgdt gdt
+ movl %cr0, %eax
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+ data32 ljmp $VIRTUAL_CS, $1f
+ .section ".text"
+ .code32
+1:
+ /* Set up protected-mode data segments and stack pointer */
+ movw $VIRTUAL_DS, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ movl pm_esp, %esp
+
+ /* Record real-mode %ss:sp (after removal of data) */
+ movw %bp, rm_ss
+ addl %ecx, %edx
+ movw %dx, rm_sp
+
+ /* Move data from RM stack to PM stack */
+ subl %ecx, %esp
+ movl %esp, %edi
+ rep movsb
+
+ /* Publish virt_offset, text16 and data16 for PM code to use */
+ popl data16
+ popl text16
+ popl virt_offset
+
+ /* Return to virtual address */
+ ret
+
+/****************************************************************************
+ * prot_to_real (protected-mode near call, 32-bit real-mode return address)
+ *
+ * Switch from 32-bit protected mode with virtual addresses to 16-bit
+ * real mode. The protected-mode %esp is stored in pm_esp and the
+ * real-mode %ss:sp is restored from the saved rm_ss and rm_sp. The
+ * high word of the real-mode %esp is set to zero. All real-mode data
+ * segment registers are loaded from the saved rm_ds. Interrupts are
+ * *not* enabled, since we want to be able to use prot_to_real in an
+ * ISR. All other registers may be destroyed.
+ *
+ * The return address for this function should be a 32-bit (sic)
+ * real-mode offset within .code16.
+ *
+ * Parameters:
+ * %ecx : number of bytes to move from PM stack to RM stack
+ *
+ ****************************************************************************
+ */
+ .section ".text"
+ .code32
+prot_to_real:
+ /* Add return address to data to be moved to RM stack */
+ addl $4, %ecx
+
+ /* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */
+ movzwl rm_ss, %ebp
+ movzwl rm_sp, %edx
+ subl %ecx, %edx
+ movl %ebp, %eax
+ shll $4, %eax
+ leal (%eax,%edx), %edi
+ subl virt_offset, %edi
+
+ /* Move data from PM stack to RM stack */
+ movl %esp, %esi
+ rep movsb
+
+ /* Record protected-mode %esp (after removal of data) */
+ movl %esi, pm_esp
+
+ /* Load real-mode segment limits */
+ movw $REAL_DS, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ ljmp $REAL_CS, $1f
+ .section ".text16"
+ .code16
+1:
+ /* Switch to real mode */
+ movl %cr0, %eax
+ andb $0!CR0_PE, %al
+ movl %eax, %cr0
+ ljmp *p2r_jump_vector
+p2r_jump_target:
+
+ /* Set up real-mode data segments and stack pointer */
+ movw %cs:rm_ds, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %bp, %ss
+ movl %edx, %esp
+
+ /* Return to real-mode address */
+ data32 ret
+
+
+ /* Real-mode code and data segments. Assigned by the call to
+ * init_librm. rm_cs doubles as the segment part of the jump
+ * vector used by prot_to_real. rm_ds is located in .text16
+ * rather than .data16 because code needs to be able to locate
+ * the data segment.
+ */
+ .section ".data16"
+p2r_jump_vector:
+ .word p2r_jump_target
+ .globl rm_cs
+rm_cs: .word 0
+ .globl rm_ds
+ .section ".text16.data"
+rm_ds: .word 0
+
+/****************************************************************************
+ * prot_call (real-mode far call, 16-bit real-mode far return address)
+ *
+ * Call a specific C function in the protected-mode code. The
+ * prototype of the C function must be
+ * void function ( struct i386_all_regs *ix86 );
+ * ix86 will point to a struct containing the real-mode registers
+ * at entry to prot_call.
+ *
+ * All registers will be preserved across prot_call(), unless the C
+ * function explicitly overwrites values in ix86. Interrupt status
+ * and GDT will also be preserved. Gate A20 will be enabled.
+ *
+ * Note that prot_call() does not rely on the real-mode stack
+ * remaining intact in order to return, since everything relevant is
+ * copied to the protected-mode stack for the duration of the call.
+ * In particular, this means that a real-mode prefix can make a call
+ * to main() which will return correctly even if the prefix's stack
+ * gets vapourised during the Etherboot run. (The prefix cannot rely
+ * on anything else on the stack being preserved, so should move any
+ * critical data to registers before calling main()).
+ *
+ * Parameters:
+ * function : virtual address of protected-mode function to call
+ *
+ * Example usage:
+ * pushl $pxe_api_call
+ * call prot_call
+ * addw $4, %sp
+ * to call in to the C function
+ * void pxe_api_call ( struct i386_all_regs *ix86 );
+ ****************************************************************************
+ */
+
+#define PC_OFFSET_GDT ( 0 )
+#define PC_OFFSET_IX86 ( PC_OFFSET_GDT + 8 /* pad to 8 to keep alignment */ )
+#define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS )
+#define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
+#define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 )
+
+ .section ".text16"
+ .code16
+ .globl prot_call
+prot_call:
+ /* Preserve registers, flags and GDT on external RM stack */
+ pushfl
+ pushal
+ pushw %gs
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %ss
+ pushw %cs
+ subw $8, %sp
+ movw %sp, %bp
+ sgdt (%bp)
+
+ /* For sanity's sake, clear the direction flag as soon as possible */
+ cld
+
+ /* Switch to protected mode and move register dump to PM stack */
+ movl $PC_OFFSET_END, %ecx
+ pushl $1f
+ jmp real_to_prot
+ .section ".text"
+ .code32
+1:
+ /* Set up environment expected by C code */
+ call gateA20_set
+
+ /* Call function */
+ leal PC_OFFSET_IX86(%esp), %eax
+ pushl %eax
+ call *(PC_OFFSET_FUNCTION+4)(%esp)
+ popl %eax /* discard */
+
+ /* Switch to real mode and move register dump back to RM stack */
+ movl $PC_OFFSET_END, %ecx
+ pushl $1f
+ jmp prot_to_real
+ .section ".text16"
+ .code16
+1:
+ /* Reload GDT, restore registers and flags and return */
+ movw %sp, %bp
+ lgdt (%bp)
+ addw $12, %sp /* also skip %cs and %ss */
+ popw %ds
+ popw %es
+ popw %fs
+ popw %gs
+ popal
+ /* popal skips %esp. We therefore want to do "movl -20(%sp),
+ * %esp", but -20(%sp) is not a valid 80386 expression.
+ * Fortunately, prot_to_real() zeroes the high word of %esp, so
+ * we can just use -20(%esp) instead.
+ */
+ addr32 movl -20(%esp), %esp
+ popfl
+ lret
+
+/****************************************************************************
+ * real_call (protected-mode near call, 32-bit virtual return address)
+ *
+ * Call a real-mode function from protected-mode code.
+ *
+ * The non-segment register values will be passed directly to the
+ * real-mode code. The segment registers will be set as per
+ * prot_to_real. The non-segment register values set by the real-mode
+ * function will be passed back to the protected-mode caller. A
+ * result of this is that this routine cannot be called directly from
+ * C code, since it clobbers registers that the C ABI expects the
+ * callee to preserve. Gate A20 will *not* be automatically
+ * re-enabled. Since we always run from an even megabyte of memory,
+ * we are guaranteed to return successfully to the protected-mode
+ * code, which should then call gateA20_set() if it suspects that gate
+ * A20 may have been disabled. Note that enabling gate A20 is a
+ * potentially slow operation that may also cause keyboard input to be
+ * lost; this is why it is not done automatically.
+ *
+ * librm.h defines a convenient macro REAL_CODE() for using real_call.
+ * See librm.h and realmode.h for details and examples.
+ *
+ * Parameters:
+ * (32-bit) near pointer to real-mode function to call
+ *
+ * Returns: none
+ ****************************************************************************
+ */
+
+#define RC_OFFSET_PRESERVE_REGS ( 0 )
+#define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + SIZEOF_I386_REGS )
+#define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 )
+#define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 )
+
+ .section ".text"
+ .code32
+ .globl real_call
+real_call:
+ /* Create register dump and function pointer copy on PM stack */
+ pushal
+ pushl RC_OFFSET_FUNCTION(%esp)
+
+ /* Switch to real mode and move register dump to RM stack */
+ movl $( RC_OFFSET_RETADDR + 4 /* function pointer copy */ ), %ecx
+ pushl $1f
+ jmp prot_to_real
+ .section ".text16"
+ .code16
+1:
+ /* Call real-mode function */
+ popl rc_function
+ popal
+ call *rc_function
+ pushal
+
+ /* For sanity's sake, clear the direction flag as soon as possible */
+ cld
+
+ /* Switch to protected mode and move register dump back to PM stack */
+ movl $RC_OFFSET_RETADDR, %ecx
+ pushl $1f
+ jmp real_to_prot
+ .section ".text"
+ .code32
+1:
+ /* Restore registers and return */
+ popal
+ ret
+
+
+ /* Function vector, used because "call xx(%sp)" is not a valid
+ * 16-bit expression.
+ */
+ .section ".data16"
+rc_function: .word 0, 0
+
+/****************************************************************************
+ * Stored real-mode and protected-mode stack pointers
+ *
+ * The real-mode stack pointer is stored here whenever real_to_prot
+ * is called and restored whenever prot_to_real is called. The
+ * converse happens for the protected-mode stack pointer.
+ *
+ * Despite initial appearances this scheme is, in fact re-entrant,
+ * because program flow dictates that we always return via the point
+ * we left by. For example:
+ * PXE API call entry
+ * 1 real => prot
+ * ...
+ * Print a text string
+ * ...
+ * 2 prot => real
+ * INT 10
+ * 3 real => prot
+ * ...
+ * ...
+ * 4 prot => real
+ * PXE API call exit
+ *
+ * At point 1, the RM mode stack value, say RPXE, is stored in
+ * rm_ss,sp. We want this value to still be present in rm_ss,sp when
+ * we reach point 4.
+ *
+ * At point 2, the RM stack value is restored from RPXE. At point 3,
+ * the RM stack value is again stored in rm_ss,sp. This *does*
+ * overwrite the RPXE that we have stored there, but it's the same
+ * value, since the code between points 2 and 3 has managed to return
+ * to us.
+ ****************************************************************************
+ */
+ .section ".data"
+rm_sp: .word 0
+rm_ss: .word 0
+pm_esp: .long _estack
+
+/****************************************************************************
+ * Virtual address offsets
+ *
+ * These are used by the protected-mode code to map between virtual
+ * and physical addresses, and to access variables in the .text16 or
+ * .data16 segments.
+ ****************************************************************************
+ */
+ /* Internal copies, created by init_librm (which runs in real mode) */
+ .section ".data16"
+_virt_offset: .long 0
+_text16: .long 0
+_data16: .long 0
+
+ /* Externally-visible copies, created by real_to_prot */
+ .section ".data"
+ .globl virt_offset
+virt_offset: .long 0
+ .globl text16
+text16: .long 0
+ .globl data16
+data16: .long 0
diff --git a/gpxe/src/config.h b/gpxe/src/config.h
new file mode 100644
index 00000000..9a447ad5
--- /dev/null
+++ b/gpxe/src/config.h
@@ -0,0 +1,169 @@
+/*
+ * This file defines the configuration for Etherboot.
+ *
+ * The build system splits this file into several individual header
+ * files of the form config/%.h, so that changing one option doesn't
+ * necessitate a rebuild of every single object. For this reason, it
+ * is important to maintain the strict formatting in this file.
+ *
+ */
+
+/* @BEGIN general.h
+ *
+ * Console configuration
+ *
+ * These options specify the console types that Etherboot will use for
+ * interaction with the user.
+ *
+ */
+
+#define CONSOLE_FIRMWARE /* Default BIOS console */
+#undef CONSOLE_SERIAL /* Serial port */
+#undef CONSOLE_DIRECT_VGA /* Direct access to VGA card */
+#undef CONSOLE_BTEXT /* Who knows what this does? */
+#undef CONSOLE_PC_KBD /* Direct access to PC keyboard */
+
+/* @END general.h */
+
+/* @BEGIN serial.h
+ *
+ * Serial port configuration
+ *
+ * These options affect the operation of the serial console. They
+ * take effect only if the serial console is included using the
+ * CONSOLE_SERIAL option.
+ *
+ */
+
+#define COMCONSOLE 0x3f8 /* I/O port address */
+
+/* Keep settings from a previous user of the serial port (e.g. lilo or
+ * LinuxBIOS), ignoring COMSPEED, COMDATA, COMPARITY and COMSTOP.
+ */
+#undef COMPRESERVE
+
+#ifndef COMPRESERVE
+#define COMSPEED 115200 /* Baud rate */
+#define COMDATA 8 /* Data bits */
+#define COMPARITY 0 /* Parity: 0=None, 1=Odd, 2=Even */
+#define COMSTOP 1 /* Stop bits */
+#endif
+
+/* @END serial.h */
+
+/* @BEGIN general.h
+ *
+ * Timer configuration
+ *
+ */
+#define TIMER_BIOS /* 18Hz BIOS timer */
+#define TIMER_RDTSC /* CPU TimeStamp Counter timer */
+
+/* @END general.h */
+
+/* @BEGIN isa.h
+ *
+ * ISA probe address configuration
+ *
+ * You can override the list of addresses that will be probed by any
+ * ISA drivers.
+ *
+ */
+#undef ISA_PROBE_ADDRS /* e.g. 0x200, 0x300 */
+#undef ISA_PROBE_ONLY /* Do not probe any other addresses */
+
+/* @END isa.h */
+
+/* @BEGIN general.h
+ *
+ * Network protocols
+ *
+ */
+
+#define NET_PROTO_IPV4 /* IPv4 protocol */
+
+/* @END general.h */
+
+/* @BEGIN general.h
+ *
+ * Download protocols
+ *
+ */
+
+#define DOWNLOAD_PROTO_TFTP /* Trivial File Transfer Protocol */
+#undef DOWNLOAD_PROTO_NFS /* Network File System */
+#define DOWNLOAD_PROTO_HTTP /* Hypertext Transfer Protocol */
+#undef DOWNLOAD_PROTO_HTTPS /* Secure Hypertext Transfer Protocol */
+#undef DOWNLOAD_PROTO_FTP /* File Transfer Protocol */
+#undef DOWNLOAD_PROTO_TFTM /* Multicast Trivial File Transfer Protocol */
+#undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */
+#undef DOWNLOAD_PROTO_FSP /* FSP? */
+
+/* @END general.h */
+
+/* @BEGIN general.h
+ *
+ * Name resolution modules
+ *
+ */
+
+#define DNS_RESOLVER /* DNS resolver */
+#undef NMB_RESOLVER /* NMB resolver */
+
+/* @END general.h */
+
+/* @BEGIN general.h
+ *
+ * Image types
+ *
+ * Etherboot supports various image formats. Select whichever ones
+ * you want to use.
+ *
+ */
+#undef IMAGE_NBI /* NBI image support */
+#undef IMAGE_ELF64 /* ELF64 image support */
+#undef IMAGE_ELF /* ELF image support */
+#undef IMAGE_FREEBSD /* FreeBSD kernel image support */
+#define IMAGE_MULTIBOOT /* MultiBoot image support */
+#undef IMAGE_AOUT /* a.out image support */
+#undef IMAGE_WINCE /* WinCE image support */
+#define IMAGE_PXE /* PXE image support */
+#define IMAGE_SCRIPT /* gPXE script image support */
+#define IMAGE_BZIMAGE /* Linux bzImage image support */
+
+/* @END general.h */
+
+/* @BEGIN general.h
+ *
+ * Command-line commands to include
+ *
+ */
+#define AUTOBOOT_CMD /* Automatic booting */
+#define NVO_CMD /* Non-volatile option storage commands */
+#define CONFIG_CMD /* Option configuration console */
+#define IFMGMT_CMD /* Interface management commands */
+#define ROUTE_CMD /* Routing table management commands */
+#define IMAGE_CMD /* Image management commands */
+#define DHCP_CMD /* DHCP management commands */
+#define SANBOOT_CMD /* SAN boot commands */
+
+/* @END general.h */
+
+/* @BEGIN general.h
+ *
+ * Obscure configuration options
+ *
+ * You probably don't need to touch these.
+ *
+ */
+
+#undef BUILD_SERIAL /* Include an automatic build serial
+ * number. Add "bs" to the list of
+ * make targets. For example:
+ * "make bin/rtl8139.dsk bs" */
+#undef BUILD_ID /* Include a custom build ID string,
+ * e.g "test-foo" */
+#undef NULL_TRAP /* Attempt to catch NULL function calls */
+#undef DUMP_GDBSYM /* Dump GDB symbol table information */
+
+/* @END general.h */
diff --git a/gpxe/src/config/.gitignore b/gpxe/src/config/.gitignore
new file mode 100644
index 00000000..499ae122
--- /dev/null
+++ b/gpxe/src/config/.gitignore
@@ -0,0 +1,2 @@
+*.h
+.buildserial.*
diff --git a/gpxe/src/core/abft.c b/gpxe/src/core/abft.c
new file mode 100644
index 00000000..af28bbcf
--- /dev/null
+++ b/gpxe/src/core/abft.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <realmode.h>
+#include <gpxe/aoe.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/abft.h>
+
+/** @file
+ *
+ * AoE Boot Firmware Table
+ *
+ */
+
+#define abftab __use_data16 ( abftab )
+/** The aBFT used by gPXE */
+struct abft_table __data16 ( abftab ) __attribute__ (( aligned ( 16 ) )) = {
+ /* ACPI header */
+ .acpi = {
+ .signature = ABFT_SIG,
+ .length = sizeof ( abftab ),
+ .revision = 1,
+ .oem_id = "FENSYS",
+ .oem_table_id = "gPXE",
+ },
+};
+
+/**
+ * Fill in all variable portions of aBFT
+ *
+ * @v aoe AoE session
+ */
+void abft_fill_data ( struct aoe_session *aoe ) {
+
+ /* Fill in boot parameters */
+ abftab.shelf = aoe->major;
+ abftab.slot = aoe->minor;
+ memcpy ( abftab.mac, aoe->netdev->ll_addr, sizeof ( abftab.mac ) );
+
+ /* Update checksum */
+ acpi_fix_checksum ( &abftab.acpi );
+
+ DBG ( "AoE boot firmware table:\n" );
+ DBG_HD ( &abftab, sizeof ( abftab ) );
+}
diff --git a/gpxe/src/core/acpi.c b/gpxe/src/core/acpi.c
new file mode 100644
index 00000000..94b7b2a1
--- /dev/null
+++ b/gpxe/src/core/acpi.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/acpi.h>
+
+/** @file
+ *
+ * ACPI support functions
+ *
+ */
+
+/**
+ * Fix up ACPI table checksum
+ *
+ * @v acpi ACPI table header
+ */
+void acpi_fix_checksum ( struct acpi_description_header *acpi ) {
+ unsigned int i = 0;
+ uint8_t sum = 0;
+
+ for ( i = 0 ; i < acpi->length ; i++ ) {
+ sum += *( ( ( uint8_t * ) acpi ) + i );
+ }
+ acpi->checksum -= sum;
+}
diff --git a/gpxe/src/core/ansiesc.c b/gpxe/src/core/ansiesc.c
new file mode 100644
index 00000000..6b820ada
--- /dev/null
+++ b/gpxe/src/core/ansiesc.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <gpxe/ansiesc.h>
+
+/** @file
+ *
+ * ANSI escape sequences
+ *
+ */
+
+/**
+ * Call ANSI escape sequence handler
+ *
+ * @v handlers List of escape sequence handlers
+ * @v function Control function identifier
+ * @v count Parameter count
+ * @v params Parameter list
+ */
+static void ansiesc_call_handler ( struct ansiesc_handler *handlers,
+ unsigned int function, int count,
+ int params[] ) {
+ struct ansiesc_handler *handler;
+
+ for ( handler = handlers ; handler->function ; handler++ ) {
+ if ( handler->function == function ) {
+ handler->handle ( count, params );
+ break;
+ }
+ }
+}
+
+/**
+ * Process character that may be part of ANSI escape sequence
+ *
+ * @v ctx ANSI escape sequence context
+ * @v c Character
+ * @ret c Original character if not part of escape sequence
+ * @ret <0 Character was part of escape sequence
+ *
+ * ANSI escape sequences will be plucked out of the character stream
+ * and interpreted; once complete they will be passed to the
+ * appropriate handler if one exists in this ANSI escape sequence
+ * context.
+ *
+ * In the interests of code size, we are rather liberal about the
+ * sequences we are prepared to accept as valid.
+ */
+int ansiesc_process ( struct ansiesc_context *ctx, int c ) {
+ if ( ctx->count == 0 ) {
+ if ( c == ESC ) {
+ /* First byte of CSI : begin escape sequence */
+ ctx->count = 1;
+ memset ( ctx->params, 0xff, sizeof ( ctx->params ) );
+ ctx->function = 0;
+ return -1;
+ } else {
+ /* Normal character */
+ return c;
+ }
+ } else {
+ if ( c == '[' ) {
+ /* Second byte of CSI : do nothing */
+ } else if ( ( c >= '0' ) && ( c <= '9' ) ) {
+ /* Parameter Byte : part of a parameter value */
+ int *param = &ctx->params[ctx->count - 1];
+ if ( *param < 0 )
+ *param = 0;
+ *param = ( ( *param * 10 ) + ( c - '0' ) );
+ } else if ( c == ';' ) {
+ /* Parameter Byte : parameter delimiter */
+ ctx->count++;
+ if ( ctx->count > ( sizeof ( ctx->params ) /
+ sizeof ( ctx->params[0] ) ) ) {
+ /* Excessive parameters : abort sequence */
+ ctx->count = 0;
+ DBG ( "Too many parameters in ANSI escape "
+ "sequence\n" );
+ }
+ } else if ( ( c >= 0x20 ) && ( c <= 0x2f ) ) {
+ /* Intermediate Byte */
+ ctx->function <<= 8;
+ ctx->function |= c;
+ } else {
+ /* Treat as Final Byte. Zero ctx->count before
+ * calling handler to avoid potential infinite loops.
+ */
+ int count = ctx->count;
+ ctx->count = 0;
+ ctx->function <<= 8;
+ ctx->function |= c;
+ ansiesc_call_handler ( ctx->handlers, ctx->function,
+ count, ctx->params );
+ }
+ return -1;
+ }
+}
diff --git a/gpxe/src/core/asprintf.c b/gpxe/src/core/asprintf.c
new file mode 100644
index 00000000..94d7e7c4
--- /dev/null
+++ b/gpxe/src/core/asprintf.c
@@ -0,0 +1,47 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+/**
+ * Write a formatted string to newly allocated memory.
+ *
+ * @v strp Pointer to hold allocated string
+ * @v fmt Format string
+ * @v args Arguments corresponding to the format string
+ * @ret len Length of formatted string
+ */
+int vasprintf ( char **strp, const char *fmt, va_list args ) {
+ size_t len;
+ va_list args_tmp;
+
+ /* Calculate length needed for string */
+ va_copy ( args_tmp, args );
+ len = ( vsnprintf ( NULL, 0, fmt, args_tmp ) + 1 );
+ va_end ( args_tmp );
+
+ /* Allocate and fill string */
+ *strp = malloc ( len );
+ if ( ! *strp )
+ return -ENOMEM;
+ return vsnprintf ( *strp, len, fmt, args );
+}
+
+/**
+ * Write a formatted string to newly allocated memory.
+ *
+ * @v strp Pointer to hold allocated string
+ * @v fmt Format string
+ * @v ... Arguments corresponding to the format string
+ * @ret len Length of formatted string
+ */
+int asprintf ( char **strp, const char *fmt, ... ) {
+ va_list args;
+ int len;
+
+ va_start ( args, fmt );
+ len = vasprintf ( strp, fmt, args );
+ va_end ( args );
+ return len;
+}
diff --git a/gpxe/src/core/basename.c b/gpxe/src/core/basename.c
new file mode 100644
index 00000000..7340c0d5
--- /dev/null
+++ b/gpxe/src/core/basename.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Get base name of path
+ *
+ */
+
+#include <string.h>
+#include <libgen.h>
+
+/**
+ * Return base name from path
+ *
+ * @v path Full path
+ * @ret basename Base name
+ */
+char * basename ( char *path ) {
+ char *basename;
+
+ basename = strrchr ( path, '/' );
+ return ( basename ? ( basename + 1 ) : path );
+}
+
+/**
+ * Return directory name from path
+ *
+ * @v path Full path
+ * @ret dirname Directory name
+ *
+ * Note that this function may modify its argument.
+ */
+char * dirname ( char *path ) {
+ char *separator;
+
+ separator = strrchr ( path, '/' );
+ if ( separator == path ) {
+ return "/";
+ } else if ( separator ) {
+ *separator = 0;
+ return path;
+ } else {
+ return ".";
+ }
+}
diff --git a/gpxe/src/core/bitmap.c b/gpxe/src/core/bitmap.c
new file mode 100644
index 00000000..d0266471
--- /dev/null
+++ b/gpxe/src/core/bitmap.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <errno.h>
+#include <gpxe/bitmap.h>
+
+/** @file
+ *
+ * Bitmaps for multicast downloads
+ *
+ */
+
+/**
+ * Resize bitmap
+ *
+ * @v bitmap Bitmap
+ * @v new_length New length of bitmap, in bits
+ * @ret rc Return status code
+ */
+int bitmap_resize ( struct bitmap *bitmap, unsigned int new_length ) {
+ unsigned int old_num_blocks;
+ unsigned int new_num_blocks;
+ size_t new_size;
+ bitmap_block_t *new_blocks;
+
+ old_num_blocks = BITMAP_INDEX ( bitmap->length + BITMAP_BLKSIZE - 1 );
+ new_num_blocks = BITMAP_INDEX ( new_length + BITMAP_BLKSIZE - 1 );
+
+ if ( old_num_blocks != new_num_blocks ) {
+ new_size = ( new_num_blocks * sizeof ( bitmap->blocks[0] ) );
+ new_blocks = realloc ( bitmap->blocks, new_size );
+ if ( ! new_blocks ) {
+ DBGC ( bitmap, "Bitmap %p could not resize to %d "
+ "bits\n", bitmap, new_length );
+ return -ENOMEM;
+ }
+ bitmap->blocks = new_blocks;
+ }
+ bitmap->length = new_length;
+
+ while ( old_num_blocks < new_num_blocks ) {
+ bitmap->blocks[old_num_blocks++] = 0;
+ }
+
+ DBGC ( bitmap, "Bitmap %p resized to %d bits\n", bitmap, new_length );
+ return 0;
+}
+
+/**
+ * Test bit in bitmap
+ *
+ * @v bitmap Bitmap
+ * @v bit Bit index
+ * @ret is_set Bit is set
+ */
+int bitmap_test ( struct bitmap *bitmap, unsigned int bit ) {
+ unsigned int index = BITMAP_INDEX ( bit );
+ bitmap_block_t mask = BITMAP_MASK ( bit );
+
+ if ( bit >= bitmap->length )
+ return 0;
+ return ( bitmap->blocks[index] & mask );
+}
+
+/**
+ * Set bit in bitmap
+ *
+ * @v bitmap Bitmap
+ * @v bit Bit index
+ */
+void bitmap_set ( struct bitmap *bitmap, unsigned int bit ) {
+ unsigned int index = BITMAP_INDEX ( bit );
+ bitmap_block_t mask = BITMAP_MASK ( bit );
+
+ DBGC ( bitmap, "Bitmap %p setting bit %d\n", bitmap, bit );
+
+ /* Update bitmap */
+ bitmap->blocks[index] |= mask;
+
+ /* Update first gap counter */
+ while ( bitmap_test ( bitmap, bitmap->first_gap ) ) {
+ bitmap->first_gap++;
+ }
+}
diff --git a/gpxe/src/core/bitops.c b/gpxe/src/core/bitops.c
new file mode 100644
index 00000000..53abaaea
--- /dev/null
+++ b/gpxe/src/core/bitops.c
@@ -0,0 +1,11 @@
+#include <strings.h>
+
+int __flsl ( long x ) {
+ unsigned long value = x;
+ int ls = 0;
+
+ for ( ls = 0 ; value ; ls++ ) {
+ value >>= 1;
+ }
+ return ls;
+}
diff --git a/gpxe/src/core/btext.c b/gpxe/src/core/btext.c
new file mode 100644
index 00000000..122d89fb
--- /dev/null
+++ b/gpxe/src/core/btext.c
@@ -0,0 +1,5039 @@
+#if 0
+
+/*
+ * Procedures for drawing on the screen early on in the boot process.
+ *
+ * Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ *
+ * move to LinuxBIOS by LYH yhlu@tyan.com
+ * move to Etherboot by LYH
+ */
+
+#include "console.h"
+#include <gpxe/init.h>
+#include <gpxe/pci.h>
+
+#undef __BIG_ENDIAN
+#if 0
+#define __LITTLE_ENDIAN
+#endif
+
+#include "btext.h"
+
+//#define NO_SCROLL
+
+#ifndef NO_SCROLL
+static void scrollscreen(void);
+#endif
+
+static void draw_byte(const unsigned char c, u32 locX, u32 locY);
+#if 0
+static void draw_byte_32(const unsigned char *bits, u32 *base, u32 rb);
+static void draw_byte_16(const unsigned char *bits, u32 *base, u32 rb);
+#endif
+static void draw_byte_8(const unsigned char *bits, u32 *base, u32 rb);
+
+static u32 g_loc_X;
+static u32 g_loc_Y;
+static u32 g_max_loc_X;
+static u32 g_max_loc_Y;
+
+#define CHAR_256 0
+
+#if CHAR_256==1
+#define cmapsz (16*256)
+#else
+#define cmapsz (16*96)
+#endif
+
+static const unsigned char vga_font[cmapsz];
+
+u32 boot_text_mapped;
+
+boot_infos_t disp_bi;
+
+#define BTEXT
+#define BTDATA
+
+
+/* This function will enable the early boot text when doing OF booting. This
+ * way, xmon output should work too
+ */
+static void
+btext_setup_display(u32 width, u32 height, u32 depth, u32 pitch,
+ unsigned long address)
+{
+ boot_infos_t* bi = &disp_bi;
+
+ g_loc_X = 0;
+ g_loc_Y = 0;
+ g_max_loc_X = width / 8;
+ g_max_loc_Y = height / 16;
+// bi->logicalDisplayBase = (unsigned char *)address;
+ bi->dispDeviceBase = address;
+ bi->dispDeviceRowBytes = pitch;
+ bi->dispDeviceDepth = depth;
+ bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0;
+ bi->dispDeviceRect[2] = width;
+ bi->dispDeviceRect[3] = height;
+ boot_text_mapped = 0;
+}
+
+/* Here's a small text engine to use during early boot
+ * or for debugging purposes
+ *
+ * todo:
+ *
+ * - build some kind of vgacon with it to enable early printk
+ * - move to a separate file
+ * - add a few video driver hooks to keep in sync with display
+ * changes.
+ */
+
+static void
+map_boot_text(void)
+{
+ boot_infos_t *bi = &disp_bi;
+
+ if (bi->dispDeviceBase == 0)
+ return;
+
+ boot_text_mapped = 0;
+
+ bi->logicalDisplayBase = phys_to_virt(bi->dispDeviceBase);
+
+ boot_text_mapped = 1;
+}
+
+/* Calc the base address of a given point (x,y) */
+static unsigned char * BTEXT
+calc_base(boot_infos_t *bi, u32 x, u32 y)
+{
+ unsigned char *base;
+ base = bi->logicalDisplayBase;
+#if 0
+ /* Ummm... which moron wrote this? */
+ if (base == 0)
+ base = bi->dispDeviceBase;
+#endif
+ base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3);
+ base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes;
+ return base;
+}
+
+
+static void BTEXT btext_clearscreen(void)
+{
+ boot_infos_t* bi = &disp_bi;
+ u32 *base = (u32 *)calc_base(bi, 0, 0);
+ u32 width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
+ (bi->dispDeviceDepth >> 3)) >> 2;
+ u32 i,j;
+
+ for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
+ {
+ u32 *ptr = base;
+ for(j=width; j; --j)
+ *(ptr++) = 0;
+ base += (bi->dispDeviceRowBytes >> 2);
+ }
+}
+
+#if 0
+__inline__ void dcbst(const void* addr)
+{
+ __asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr));
+}
+
+static void BTEXT btext_flushscreen(void)
+{
+ boot_infos_t* bi = &disp_bi;
+ u32 *base = (unsigned long *)calc_base(bi, 0, 0);
+ u32 width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
+ (bi->dispDeviceDepth >> 3)) >> 2;
+ u32 i,j;
+
+ for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
+ {
+ u32 *ptr = base;
+ for(j=width; j>0; j-=8) {
+ dcbst(ptr);
+ ptr += 8;
+ }
+ base += (bi->dispDeviceRowBytes >> 2);
+ }
+}
+#endif
+
+
+#ifndef NO_SCROLL
+static BTEXT void
+scrollscreen(void)
+{
+ boot_infos_t* bi = &disp_bi;
+ u32 *src = (u32 *)calc_base(bi,0,16);
+ u32 *dst = (u32 *)calc_base(bi,0,0);
+ u32 width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
+ (bi->dispDeviceDepth >> 3)) >> 2;
+ u32 i,j;
+
+ for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1] - 16); i++)
+ {
+ u32 *src_ptr = src;
+ u32 *dst_ptr = dst;
+ for(j=width; j; --j)
+ *(dst_ptr++) = *(src_ptr++);
+ src += (bi->dispDeviceRowBytes >> 2);
+ dst += (bi->dispDeviceRowBytes >> 2);
+ }
+ for (i=0; i<16; i++)
+ {
+ u32 *dst_ptr = dst;
+ for(j=width; j; --j)
+ *(dst_ptr++) = 0;
+ dst += (bi->dispDeviceRowBytes >> 2);
+ }
+}
+#endif /* ndef NO_SCROLL */
+
+static void BTEXT btext_drawchar(char c)
+{
+ u32 cline = 0;
+
+ if (!boot_text_mapped)
+ return;
+
+ switch (c) {
+ case '\b':
+ if (g_loc_X > 0)
+ --g_loc_X;
+ break;
+ case '\t':
+ g_loc_X = (g_loc_X & -8) + 8;
+ break;
+ case '\r':
+ g_loc_X = 0;
+ break;
+ case '\n':
+ g_loc_X = 0;
+ g_loc_Y++;
+ cline = 1;
+ break;
+ default:
+ draw_byte(c, g_loc_X++, g_loc_Y);
+ }
+ if (g_loc_X >= g_max_loc_X) {
+ g_loc_X = 0;
+ g_loc_Y++;
+ cline = 1;
+ }
+#ifndef NO_SCROLL
+ while (g_loc_Y >= g_max_loc_Y) {
+ scrollscreen();
+ g_loc_Y--;
+ }
+#else
+ /* wrap around from bottom to top of screen so we don't
+ waste time scrolling each line. -- paulus. */
+ if (g_loc_Y >= g_max_loc_Y)
+ g_loc_Y = 0;
+ if (cline) {
+ for (x = 0; x < g_max_loc_X; ++x)
+ draw_byte(' ', x, g_loc_Y);
+ }
+#endif
+}
+#if 0
+static void BTEXT
+btext_drawstring(const char *c)
+{
+ if (!boot_text_mapped)
+ return;
+ while (*c)
+ btext_drawchar(*c++);
+}
+static void BTEXT
+btext_drawhex(u32 v)
+{
+ static char hex_table[] = "0123456789abcdef";
+
+ if (!boot_text_mapped)
+ return;
+ btext_drawchar(hex_table[(v >> 28) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 24) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 20) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 16) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 12) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 8) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 4) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 0) & 0x0000000FUL]);
+ btext_drawchar(' ');
+}
+#endif
+
+static void BTEXT
+draw_byte(const unsigned char c, u32 locX, u32 locY)
+{
+ boot_infos_t* bi = &disp_bi;
+ unsigned char *base = calc_base(bi, locX << 3, locY << 4);
+#if CHAR_256==1
+ unsigned const char *font = &vga_font[(((u32)c)) * 16];
+#else
+ unsigned const char *font = &vga_font[(((u32)c-0x20)) * 16];
+#endif
+
+ u32 rb = bi->dispDeviceRowBytes;
+
+ switch(bi->dispDeviceDepth) {
+#if 0
+ case 24:
+ case 32:
+ draw_byte_32(font, (u32 *)base, rb);
+ break;
+ case 15:
+ case 16:
+ draw_byte_16(font, (u32 *)base, rb);
+ break;
+#endif
+ case 8:
+ draw_byte_8(font, (u32 *)base, rb);
+ break;
+ }
+}
+static u32 expand_bits_8[16] BTDATA = {
+#if defined(__BIG_ENDIAN)
+ 0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
+ 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
+ 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
+ 0xffff0000,0xffff00ff,0xffffff00,0xffffffff
+#elif defined(__LITTLE_ENDIAN)
+ 0x00000000,0xff000000,0x00ff0000,0xffff0000,
+ 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
+ 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
+ 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
+#else
+#error FIXME: No endianness??
+#endif
+};
+#if 0
+static const u32 expand_bits_16[4] BTDATA = {
+#if defined(__BIG_ENDIAN)
+ 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
+#elif defined(__LITTLE_ENDIAN)
+ 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
+#else
+#error FIXME: No endianness??
+#endif
+};
+#endif
+#if 0
+static void BTEXT
+draw_byte_32(const unsigned char *font, u32 *base, u32 rb)
+{
+ u32 l, bits;
+ u32 fg = 0xFFFFFFFF;
+ u32 bg = 0x00000000;
+
+ for (l = 0; l < 16; ++l)
+ {
+ bits = *font++;
+ base[0] = (-(bits >> 7) & fg) ^ bg;
+ base[1] = (-((bits >> 6) & 1) & fg) ^ bg;
+ base[2] = (-((bits >> 5) & 1) & fg) ^ bg;
+ base[3] = (-((bits >> 4) & 1) & fg) ^ bg;
+ base[4] = (-((bits >> 3) & 1) & fg) ^ bg;
+ base[5] = (-((bits >> 2) & 1) & fg) ^ bg;
+ base[6] = (-((bits >> 1) & 1) & fg) ^ bg;
+ base[7] = (-(bits & 1) & fg) ^ bg;
+ base = (u32 *) ((char *)base + rb);
+ }
+}
+
+static void BTEXT
+draw_byte_16(const unsigned char *font, u32 *base, u32 rb)
+{
+ u32 l, bits;
+ u32 fg = 0xFFFFFFFF;
+ u32 bg = 0x00000000;
+ u32 *eb = expand_bits_16;
+
+ for (l = 0; l < 16; ++l)
+ {
+ bits = *font++;
+ base[0] = (eb[bits >> 6] & fg) ^ bg;
+ base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg;
+ base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg;
+ base[3] = (eb[bits & 3] & fg) ^ bg;
+ base = (u32 *) ((char *)base + rb);
+ }
+}
+#endif
+static void BTEXT
+draw_byte_8(const unsigned char *font, u32 *base, u32 rb)
+{
+ u32 l, bits;
+ u32 fg = 0x0F0F0F0F;
+ u32 bg = 0x00000000;
+ u32 *eb = expand_bits_8;
+
+ for (l = 0; l < 16; ++l)
+ {
+ bits = *font++;
+ base[0] = (eb[bits >> 4] & fg) ^ bg;
+ base[1] = (eb[bits & 0xf] & fg) ^ bg;
+ base = (u32 *) ((char *)base + rb);
+ }
+}
+
+static void btext_init(void)
+{
+#if 0
+// for debug
+#define frame_buffer 0xfc000000
+#else
+ uint32_t frame_buffer;// 0xfc000000
+
+ struct pci_device dev;
+
+ #warning "pci_find_device_x no longer exists; use find_pci_device instead"
+ /* pci_find_device_x(0x1002, 0x4752, 0, &dev); */
+ if(dev.vendor==0) return; // no fb
+
+ frame_buffer = (uint32_t)dev.membase;
+#endif
+
+ btext_setup_display(640, 480, 8, 640,frame_buffer);
+ btext_clearscreen();
+ map_boot_text();
+}
+static void btext_putc(int c)
+{
+ btext_drawchar((unsigned char)c);
+}
+
+struct console_driver btext_console __console_driver = {
+ .putchar = btext_putc,
+ .disabled = 1,
+};
+
+//come from linux/drivers/video/font-8x16.c
+/**********************************************/
+/* */
+/* Font file generated by cpi2fnt */
+/* */
+/**********************************************/
+
+
+static const unsigned char vga_font[cmapsz] BTDATA = {
+#if CHAR_256==1
+ /* 0 0x00 '^@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 1 0x01 '^A' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x81, /* 10000001 */
+ 0xa5, /* 10100101 */
+ 0x81, /* 10000001 */
+ 0x81, /* 10000001 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0x81, /* 10000001 */
+ 0x81, /* 10000001 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 2 0x02 '^B' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xdb, /* 11011011 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 3 0x03 '^C' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 4 0x04 '^D' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 5 0x05 '^E' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 6 0x06 '^F' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 7 0x07 '^G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 8 0x08 '^H' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xe7, /* 11100111 */
+ 0xc3, /* 11000011 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 9 0x09 '^I' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x42, /* 01000010 */
+ 0x42, /* 01000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 10 0x0a '^J' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0x99, /* 10011001 */
+ 0xbd, /* 10111101 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0xc3, /* 11000011 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 11 0x0b '^K' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x0e, /* 00001110 */
+ 0x1a, /* 00011010 */
+ 0x32, /* 00110010 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 12 0x0c '^L' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 13 0x0d '^M' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x33, /* 00110011 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x70, /* 01110000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 14 0x0e '^N' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x67, /* 01100111 */
+ 0xe7, /* 11100111 */
+ 0xe6, /* 11100110 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 15 0x0f '^O' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xdb, /* 11011011 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0x3c, /* 00111100 */
+ 0xdb, /* 11011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 16 0x10 '^P' */
+ 0x00, /* 00000000 */
+ 0x80, /* 10000000 */
+ 0xc0, /* 11000000 */
+ 0xe0, /* 11100000 */
+ 0xf0, /* 11110000 */
+ 0xf8, /* 11111000 */
+ 0xfe, /* 11111110 */
+ 0xf8, /* 11111000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+ 0xc0, /* 11000000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 17 0x11 '^Q' */
+ 0x00, /* 00000000 */
+ 0x02, /* 00000010 */
+ 0x06, /* 00000110 */
+ 0x0e, /* 00001110 */
+ 0x1e, /* 00011110 */
+ 0x3e, /* 00111110 */
+ 0xfe, /* 11111110 */
+ 0x3e, /* 00111110 */
+ 0x1e, /* 00011110 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 18 0x12 '^R' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 19 0x13 '^S' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 20 0x14 '^T' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7f, /* 01111111 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7b, /* 01111011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 21 0x15 '^U' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 22 0x16 '^V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 23 0x17 '^W' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 24 0x18 '^X' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 25 0x19 '^Y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 26 0x1a '^Z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 27 0x1b '^[' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xfe, /* 11111110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 28 0x1c '^\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 29 0x1d '^]' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x28, /* 00101000 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x28, /* 00101000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 30 0x1e '^^' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 31 0x1f '^_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+#endif
+ /* 32 0x20 ' ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 33 0x21 '!' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 34 0x22 '"' */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x24, /* 00100100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 35 0x23 '#' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 36 0x24 '$' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x86, /* 10000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 37 0x25 '%' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc2, /* 11000010 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0x86, /* 10000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 38 0x26 '&' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 39 0x27 ''' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 40 0x28 '(' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 41 0x29 ')' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 42 0x2a '*' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0xff, /* 11111111 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 43 0x2b '+' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 44 0x2c ',' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 45 0x2d '-' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 46 0x2e '.' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 47 0x2f '/' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x02, /* 00000010 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 48 0x30 '0' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 49 0x31 '1' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x38, /* 00111000 */
+ 0x78, /* 01111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 50 0x32 '2' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 51 0x33 '3' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x3c, /* 00111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 52 0x34 '4' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x1c, /* 00011100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 53 0x35 '5' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 54 0x36 '6' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 55 0x37 '7' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 56 0x38 '8' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 57 0x39 '9' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 58 0x3a ':' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 59 0x3b ';' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 60 0x3c '<' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 61 0x3d '=' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 62 0x3e '>' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 63 0x3f '?' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 64 0x40 '@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xdc, /* 11011100 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 65 0x41 'A' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 66 0x42 'B' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 67 0x43 'C' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc2, /* 11000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 68 0x44 'D' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 69 0x45 'E' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x60, /* 01100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 70 0x46 'F' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 71 0x47 'G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xde, /* 11011110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x66, /* 01100110 */
+ 0x3a, /* 00111010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 72 0x48 'H' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 73 0x49 'I' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 74 0x4a 'J' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 75 0x4b 'K' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe6, /* 11100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 76 0x4c 'L' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 77 0x4d 'M' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xee, /* 11101110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 78 0x4e 'N' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xfe, /* 11111110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 79 0x4f 'O' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 80 0x50 'P' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 81 0x51 'Q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xde, /* 11011110 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 82 0x52 'R' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 83 0x53 'S' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 84 0x54 'T' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x5a, /* 01011010 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 85 0x55 'U' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 86 0x56 'V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 87 0x57 'W' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0xee, /* 11101110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 88 0x58 'X' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 89 0x59 'Y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 90 0x5a 'Z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x86, /* 10000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc2, /* 11000010 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 91 0x5b '[' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 92 0x5c '\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x80, /* 10000000 */
+ 0xc0, /* 11000000 */
+ 0xe0, /* 11100000 */
+ 0x70, /* 01110000 */
+ 0x38, /* 00111000 */
+ 0x1c, /* 00011100 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 93 0x5d ']' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 94 0x5e '^' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 95 0x5f '_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 96 0x60 '`' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 97 0x61 'a' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 98 0x62 'b' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 99 0x63 'c' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 100 0x64 'd' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 101 0x65 'e' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 102 0x66 'f' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x36, /* 00110110 */
+ 0x32, /* 00110010 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 103 0x67 'g' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 104 0x68 'h' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x6c, /* 01101100 */
+ 0x76, /* 01110110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 105 0x69 'i' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 106 0x6a 'j' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 107 0x6b 'k' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 108 0x6c 'l' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 109 0x6d 'm' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 110 0x6e 'n' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 111 0x6f 'o' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 112 0x70 'p' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+
+ /* 113 0x71 'q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+
+ /* 114 0x72 'r' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x66, /* 01100110 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 115 0x73 's' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 116 0x74 't' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0xfc, /* 11111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x36, /* 00110110 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 117 0x75 'u' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 118 0x76 'v' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 119 0x77 'w' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 120 0x78 'x' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 121 0x79 'y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+
+ /* 122 0x7a 'z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 123 0x7b '{' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 124 0x7c '|' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 125 0x7d '}' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 126 0x7e '~' */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 127 0x7f '' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+#if CHAR_256256==1
+ /* 128 0x80 '€' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc2, /* 11000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 129 0x81 '' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 130 0x82 '‚' */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 131 0x83 'ƒ' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 132 0x84 '„' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 133 0x85 '…' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 134 0x86 '†' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 135 0x87 '‡' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 136 0x88 'ˆ' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 137 0x89 '‰' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 138 0x8a 'Š' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 139 0x8b '‹' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 140 0x8c 'Œ' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 141 0x8d '' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 142 0x8e 'Ž' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 143 0x8f '' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 144 0x90 '' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 145 0x91 '‘' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x6e, /* 01101110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 146 0x92 '’' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3e, /* 00111110 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xce, /* 11001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 147 0x93 '“' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 148 0x94 '”' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 149 0x95 '•' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 150 0x96 '–' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 151 0x97 '—' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 152 0x98 '˜' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 153 0x99 '™' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 154 0x9a 'š' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 155 0x9b '›' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 156 0x9c 'œ' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x64, /* 01100100 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xe6, /* 11100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 157 0x9d '' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 158 0x9e 'ž' */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xf8, /* 11111000 */
+ 0xc4, /* 11000100 */
+ 0xcc, /* 11001100 */
+ 0xde, /* 11011110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 159 0x9f 'Ÿ' */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 160 0xa0 ' ' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 161 0xa1 '¡' */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 162 0xa2 '¢' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 163 0xa3 '£' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 164 0xa4 '¤' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 165 0xa5 '¥' */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xfe, /* 11111110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 166 0xa6 '¦' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 167 0xa7 '§' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 168 0xa8 '¨' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 169 0xa9 '©' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 170 0xaa 'ª' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 171 0xab '«' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0xe0, /* 11100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xdc, /* 11011100 */
+ 0x86, /* 10000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 172 0xac '¬' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0xe0, /* 11100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x66, /* 01100110 */
+ 0xce, /* 11001110 */
+ 0x9a, /* 10011010 */
+ 0x3f, /* 00111111 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 173 0xad '­' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 174 0xae '®' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x36, /* 00110110 */
+ 0x6c, /* 01101100 */
+ 0xd8, /* 11011000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 175 0xaf '¯' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xd8, /* 11011000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x6c, /* 01101100 */
+ 0xd8, /* 11011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 176 0xb0 '°' */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+
+ /* 177 0xb1 '±' */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+
+ /* 178 0xb2 '²' */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+
+ /* 179 0xb3 '³' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 180 0xb4 '´' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 181 0xb5 'µ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 182 0xb6 '¶' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 183 0xb7 '·' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 184 0xb8 '¸' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 185 0xb9 '¹' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 186 0xba 'º' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 187 0xbb '»' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 188 0xbc '¼' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 189 0xbd '½' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 190 0xbe '¾' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 191 0xbf '¿' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 192 0xc0 'À' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 193 0xc1 'Á' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 194 0xc2 'Â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 195 0xc3 'Ã' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 196 0xc4 'Ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 197 0xc5 'Å' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 198 0xc6 'Æ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 199 0xc7 'Ç' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 200 0xc8 'È' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 201 0xc9 'É' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 202 0xca 'Ê' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 203 0xcb 'Ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 204 0xcc 'Ì' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 205 0xcd 'Í' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 206 0xce 'Î' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 207 0xcf 'Ï' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 208 0xd0 'Ð' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 209 0xd1 'Ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 210 0xd2 'Ò' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 211 0xd3 'Ó' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 212 0xd4 'Ô' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 213 0xd5 'Õ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 214 0xd6 'Ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 215 0xd7 '×' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 216 0xd8 'Ø' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 217 0xd9 'Ù' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 218 0xda 'Ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 219 0xdb 'Û' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 220 0xdc 'Ü' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 221 0xdd 'Ý' */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+
+ /* 222 0xde 'Þ' */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+
+ /* 223 0xdf 'ß' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 224 0xe0 'à' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 225 0xe1 'á' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xd8, /* 11011000 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 226 0xe2 'â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 227 0xe3 'ã' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 228 0xe4 'ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 229 0xe5 'å' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 230 0xe6 'æ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+
+ /* 231 0xe7 'ç' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 232 0xe8 'è' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 233 0xe9 'é' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 234 0xea 'ê' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xee, /* 11101110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 235 0xeb 'ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x3e, /* 00111110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 236 0xec 'ì' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 237 0xed 'í' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x03, /* 00000011 */
+ 0x06, /* 00000110 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xf3, /* 11110011 */
+ 0x7e, /* 01111110 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 238 0xee 'î' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 239 0xef 'ï' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 240 0xf0 'ð' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 241 0xf1 'ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 242 0xf2 'ò' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 243 0xf3 'ó' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 244 0xf4 'ô' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 245 0xf5 'õ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 246 0xf6 'ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 247 0xf7 '÷' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 248 0xf8 'ø' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 249 0xf9 'ù' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 250 0xfa 'ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 251 0xfb 'û' */
+ 0x00, /* 00000000 */
+ 0x0f, /* 00001111 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xec, /* 11101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3c, /* 00111100 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 252 0xfc 'ü' */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 253 0xfd 'ý' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x32, /* 00110010 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 254 0xfe 'þ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 255 0xff 'ÿ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+#endif
+};
+
+#endif
diff --git a/gpxe/src/core/config.c b/gpxe/src/core/config.c
new file mode 100644
index 00000000..ffd11256
--- /dev/null
+++ b/gpxe/src/core/config.c
@@ -0,0 +1,203 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#include "config/general.h"
+
+/*
+ * Build ID string calculations
+ *
+ */
+#undef XSTR
+#undef STR
+#define XSTR(s) STR(s)
+#define STR(s) #s
+
+#ifdef BUILD_SERIAL
+#include "config/.buildserial.h"
+#define BUILD_SERIAL_STR " #" XSTR(BUILD_SERIAL_NUM)
+#else
+#define BUILD_SERIAL_STR ""
+#endif
+
+#ifdef BUILD_ID
+#define BUILD_ID_STR " " BUILD_ID
+#else
+#define BUILD_ID_STR ""
+#endif
+
+#if defined(BUILD_ID) || defined(BUILD_SERIAL)
+#define BUILD_STRING " [build" BUILD_ID_STR BUILD_SERIAL_STR "]"
+#else
+#define BUILD_STRING ""
+#endif
+
+/*
+ * Drag in all requested console types
+ *
+ * CONSOLE_DUAL sets both CONSOLE_FIRMWARE and CONSOLE_SERIAL for
+ * legacy compatibility.
+ *
+ */
+
+#ifdef CONSOLE_DUAL
+#undef CONSOLE_FIRMWARE
+#define CONSOLE_FIRMWARE 1
+#undef CONSOLE_SERIAL
+#define CONSOLE_SERIAL 1
+#endif
+
+#ifdef CONSOLE_FIRMWARE
+REQUIRE_OBJECT ( bios_console );
+#endif
+#ifdef CONSOLE_SERIAL
+REQUIRE_OBJECT ( serial );
+#endif
+#ifdef CONSOLE_DIRECT_VGA
+REQUIRE_OBJECT ( video_subr );
+#endif
+#ifdef CONSOLE_BTEXT
+REQUIRE_OBJECT ( btext );
+#endif
+#ifdef CONSOLE_PC_KBD
+REQUIRE_OBJECT ( pc_kbd );
+#endif
+#ifdef CONSOLE_SYSLOG
+REQUIRE_OBJECT ( syslog );
+#endif
+
+/*
+ * Drag in all requested timers
+ */
+#ifdef TIMER_BIOS
+REQUIRE_OBJECT ( timer_bios );
+#endif
+#ifdef TIMER_RDTSC
+REQUIRE_OBJECT ( timer_rdtsc );
+#endif
+
+/*
+ * Drag in all requested network protocols
+ *
+ */
+#ifdef NET_PROTO_IPV4
+REQUIRE_OBJECT ( ipv4 );
+#endif
+
+/*
+ * Drag in all requested download protocols
+ *
+ */
+#ifdef DOWNLOAD_PROTO_TFTP
+REQUIRE_OBJECT ( tftp );
+#endif
+#ifdef DOWNLOAD_PROTO_NFS
+REQUIRE_OBJECT ( nfs );
+#endif
+#ifdef DOWNLOAD_PROTO_HTTP
+REQUIRE_OBJECT ( http );
+#endif
+#ifdef DOWNLOAD_PROTO_HTTPS
+REQUIRE_OBJECT ( https );
+#endif
+#ifdef DOWNLOAD_PROTO_FTP
+REQUIRE_OBJECT ( ftp );
+#endif
+#ifdef DOWNLOAD_PROTO_TFTM
+REQUIRE_OBJECT ( tftm );
+#endif
+#ifdef DOWNLOAD_PROTO_SLAM
+REQUIRE_OBJECT ( slam );
+#endif
+
+/*
+ * Drag in all requested resolvers
+ *
+ */
+#ifdef DNS_RESOLVER
+REQUIRE_OBJECT ( dns );
+#endif
+#ifdef NMB_RESOLVER
+REQUIRE_OBJECT ( nmb );
+#endif
+
+/*
+ * Drag in all requested image formats
+ *
+ */
+#ifdef IMAGE_NBI
+REQUIRE_OBJECT ( nbi );
+#endif
+#ifdef IMAGE_ELF64
+REQUIRE_OBJECT ( elf64 );
+#endif
+#ifdef IMAGE_ELF
+REQUIRE_OBJECT ( elf );
+#endif
+#ifdef IMAGE_FREEBSD
+REQUIRE_OBJECT ( freebsd );
+#endif
+#ifdef IMAGE_MULTIBOOT
+REQUIRE_OBJECT ( multiboot );
+#endif
+#ifdef IMAGE_AOUT
+REQUIRE_OBJECT ( aout );
+#endif
+#ifdef IMAGE_WINCE
+REQUIRE_OBJECT ( wince );
+#endif
+#ifdef IMAGE_PXE
+REQUIRE_OBJECT ( pxe_image );
+#endif
+#ifdef IMAGE_SCRIPT
+REQUIRE_OBJECT ( script );
+#endif
+#ifdef IMAGE_BZIMAGE
+REQUIRE_OBJECT ( bzimage );
+#endif
+#ifdef IMAGE_ELTORITO
+REQUIRE_OBJECT ( eltorito );
+#endif
+
+/*
+ * Drag in all requested commands
+ *
+ */
+#ifdef AUTOBOOT_CMD
+REQUIRE_OBJECT ( autoboot_cmd );
+#endif
+#ifdef NVO_CMD
+REQUIRE_OBJECT ( nvo_cmd );
+#endif
+#ifdef CONFIG_CMD
+REQUIRE_OBJECT ( config_cmd );
+#endif
+#ifdef IFMGMT_CMD
+REQUIRE_OBJECT ( ifmgmt_cmd );
+#endif
+#ifdef ROUTE_CMD
+REQUIRE_OBJECT ( route_cmd );
+#endif
+#ifdef IMAGE_CMD
+REQUIRE_OBJECT ( image_cmd );
+#endif
+#ifdef DHCP_CMD
+REQUIRE_OBJECT ( dhcp_cmd );
+#endif
+#ifdef SANBOOT_CMD
+REQUIRE_OBJECT ( sanboot_cmd );
+#endif
+
+/*
+ * Drag in miscellaneous objects
+ *
+ */
+#ifdef NULL_TRAP
+REQUIRE_OBJECT ( nulltrap );
+#endif
+#ifdef DUMP_GDBSYM
+REQUIRE_OBJECT ( gdbsym );
+#endif
diff --git a/gpxe/src/core/console.c b/gpxe/src/core/console.c
new file mode 100644
index 00000000..6f55d558
--- /dev/null
+++ b/gpxe/src/core/console.c
@@ -0,0 +1,137 @@
+#include "stddef.h"
+#include "console.h"
+#include <gpxe/process.h>
+
+/** @file */
+
+#include "bios.h"
+
+static struct console_driver console_drivers[0]
+ __table_start ( struct console_driver, console );
+static struct console_driver console_drivers_end[0]
+ __table_end ( struct console_driver, console );
+
+/**
+ * Write a single character to each console device.
+ *
+ * @v character Character to be written
+ * @ret None -
+ * @err None -
+ *
+ * The character is written out to all enabled console devices, using
+ * each device's console_driver::putchar() method.
+ *
+ */
+void putchar ( int character ) {
+ struct console_driver *console;
+
+ /* Automatic LF -> CR,LF translation */
+ if ( character == '\n' )
+ putchar ( '\r' );
+
+ for ( console = console_drivers; console < console_drivers_end ;
+ console++ ) {
+ if ( ( ! console->disabled ) && console->putchar )
+ console->putchar ( character );
+ }
+}
+
+/**
+ * Check to see if any input is available on any console.
+ *
+ * @v None -
+ * @ret console Console device that has input available, if any.
+ * @ret NULL No console device has input available.
+ * @err None -
+ *
+ * All enabled console devices are checked once for available input
+ * using each device's console_driver::iskey() method. The first
+ * console device that has available input will be returned, if any.
+ *
+ */
+static struct console_driver * has_input ( void ) {
+ struct console_driver *console;
+
+ for ( console = console_drivers; console < console_drivers_end ;
+ console++ ) {
+ if ( ( ! console->disabled ) && console->iskey ) {
+ if ( console->iskey () )
+ return console;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Read a single character from any console.
+ *
+ * @v None -
+ * @ret character Character read from a console.
+ * @err None -
+ *
+ * A character will be read from the first enabled console device that
+ * has input available using that console's console_driver::getchar()
+ * method. If no console has input available to be read, this method
+ * will block. To perform a non-blocking read, use something like
+ *
+ * @code
+ *
+ * int key = iskey() ? getchar() : -1;
+ *
+ * @endcode
+ *
+ * The character read will not be echoed back to any console.
+ *
+ * @bug We need a cleaner way to pick up cpu_nap(). It makes a
+ * real-mode call, and so we don't want to use it with LinuxBIOS.
+ *
+ */
+int getchar ( void ) {
+ struct console_driver *console;
+ int character = 256;
+
+ while ( character == 256 ) {
+ /* Doze for a while (until the next interrupt). This works
+ * fine, because the keyboard is interrupt-driven, and the
+ * timer interrupt (approx. every 50msec) takes care of the
+ * serial port, which is read by polling. This reduces the
+ * power dissipation of a modern CPU considerably, and also
+ * makes Etherboot waiting for user interaction waste a lot
+ * less CPU time in a VMware session.
+ */
+ cpu_nap();
+
+ /* Keep processing background tasks while we wait for
+ * input.
+ */
+ step();
+
+ console = has_input();
+ if ( console && console->getchar )
+ character = console->getchar ();
+ }
+
+ /* CR -> LF translation */
+ if ( character == '\r' )
+ character = '\n';
+
+ return character;
+}
+
+/** Check for available input on any console.
+ *
+ * @v None -
+ * @ret True Input is available on a console
+ * @ret False Input is not available on any console
+ * @err None -
+ *
+ * All enabled console devices are checked once for available input
+ * using each device's console_driver::iskey() method. If any console
+ * device has input available, this call will return True. If this
+ * call returns True, you can then safely call getchar() without
+ * blocking.
+ *
+ */
+int iskey ( void ) {
+ return has_input() ? 1 : 0;
+}
diff --git a/gpxe/src/core/cpio.c b/gpxe/src/core/cpio.c
new file mode 100644
index 00000000..7d2e8828
--- /dev/null
+++ b/gpxe/src/core/cpio.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/** @file
+ *
+ * CPIO archives
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <gpxe/cpio.h>
+
+/**
+ * Set field within a CPIO header
+ *
+ * @v field Field within CPIO header
+ * @v value Value to set
+ */
+void cpio_set_field ( char *field, unsigned long value ) {
+ char buf[9];
+
+ snprintf ( buf, sizeof ( buf ), "%08lx", value );
+ memcpy ( field, buf, 8 );
+}
diff --git a/gpxe/src/core/cwuri.c b/gpxe/src/core/cwuri.c
new file mode 100644
index 00000000..c7f01386
--- /dev/null
+++ b/gpxe/src/core/cwuri.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <gpxe/uri.h>
+
+/** @file
+ *
+ * Current working URI
+ *
+ * Somewhat analogous to the current working directory in a POSIX
+ * system.
+ */
+
+/** Current working URI */
+struct uri *cwuri = NULL;
+
+/**
+ * Change working URI
+ *
+ * @v uri New working URI, or NULL
+ */
+void churi ( struct uri *uri ) {
+ uri_put ( cwuri );
+ cwuri = uri_get ( uri );
+}
diff --git a/gpxe/src/core/debug.c b/gpxe/src/core/debug.c
new file mode 100644
index 00000000..09830420
--- /dev/null
+++ b/gpxe/src/core/debug.c
@@ -0,0 +1,195 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <io.h>
+#include <console.h>
+
+void pause ( void ) {
+ printf ( "\nPress a key" );
+ getchar();
+ printf ( "\r \r" );
+}
+
+void more ( void ) {
+ printf ( "---more---" );
+ getchar();
+ printf ( "\r \r" );
+}
+
+/**
+ * Print row of a hex dump with specified display address
+ *
+ * @v dispaddr Display address
+ * @v data Data to print
+ * @v len Length of data
+ * @v offset Starting offset within data
+ */
+static void dbg_hex_dump_da_row ( unsigned long dispaddr, const void *data,
+ unsigned long len, unsigned int offset ) {
+ const uint8_t *bytes = data;
+ unsigned int i;
+ uint8_t byte;
+
+ printf ( "%08lx :", ( dispaddr + offset ) );
+ for ( i = offset ; i < ( offset + 16 ) ; i++ ) {
+ if ( i >= len ) {
+ printf ( " " );
+ continue;
+ }
+ printf ( " %02x", bytes[i] );
+ }
+ printf ( " : " );
+ for ( i = offset ; i < ( offset + 16 ) ; i++ ) {
+ if ( i >= len ) {
+ printf ( " " );
+ continue;
+ }
+ byte = bytes[i];
+ if ( ( byte < 0x20 ) || ( byte >= 0x7f ) )
+ byte = '.';
+ printf ( "%c", byte );
+ }
+ printf ( "\n" );
+}
+
+/**
+ * Print hex dump with specified display address
+ *
+ * @v dispaddr Display address
+ * @v data Data to print
+ * @v len Length of data
+ */
+void dbg_hex_dump_da ( unsigned long dispaddr, const void *data,
+ unsigned long len ) {
+ unsigned int offset;
+
+ for ( offset = 0 ; offset < len ; offset += 16 ) {
+ dbg_hex_dump_da_row ( dispaddr, data, len, offset );
+ }
+}
+
+#define GUARD_SYMBOL ( ( 'M' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | 'E' )
+/* Fill a region with guard markers. We use a 4-byte pattern to make
+ * it less likely that check_region will find spurious 1-byte regions
+ * of non-corruption.
+ */
+void guard_region ( void *region, size_t len ) {
+ uint32_t offset = 0;
+
+ len &= ~0x03;
+ for ( offset = 0; offset < len ; offset += 4 ) {
+ *((uint32_t *)(region + offset)) = GUARD_SYMBOL;
+ }
+}
+
+/* Check a region that has been guarded with guard_region() for
+ * corruption.
+ */
+int check_region ( void *region, size_t len ) {
+ uint8_t corrupted = 0;
+ uint8_t in_corruption = 0;
+ uint32_t offset = 0;
+ uint32_t test = 0;
+
+ len &= ~0x03;
+ for ( offset = 0; offset < len ; offset += 4 ) {
+ test = *((uint32_t *)(region + offset)) = GUARD_SYMBOL;
+ if ( ( in_corruption == 0 ) &&
+ ( test != GUARD_SYMBOL ) ) {
+ /* Start of corruption */
+ if ( corrupted == 0 ) {
+ corrupted = 1;
+ printf ( "Region %p-%p (physical %#lx-%#lx) "
+ "corrupted\n",
+ region, region + len,
+ virt_to_phys ( region ),
+ virt_to_phys ( region + len ) );
+ }
+ in_corruption = 1;
+ printf ( "--- offset %#lx ", offset );
+ } else if ( ( in_corruption != 0 ) &&
+ ( test == GUARD_SYMBOL ) ) {
+ /* End of corruption */
+ in_corruption = 0;
+ printf ( "to offset %#lx", offset );
+ }
+
+ }
+ if ( in_corruption != 0 ) {
+ printf ( "to offset %#zx (end of region)\n", len-1 );
+ }
+ return corrupted;
+}
+
+/**
+ * Maximum number of separately coloured message streams
+ *
+ * Six is the realistic maximum; there are 8 basic ANSI colours, one
+ * of which will be the terminal default and one of which will be
+ * invisible on the terminal because it matches the background colour.
+ */
+#define NUM_AUTO_COLOURS 6
+
+/** A colour assigned to an autocolourised debug message stream */
+struct autocolour {
+ /** Message stream ID */
+ unsigned long stream;
+ /** Last recorded usage */
+ unsigned long last_used;
+};
+
+/**
+ * Choose colour index for debug autocolourisation
+ *
+ * @v stream Message stream ID
+ * @ret colour Colour ID
+ */
+static int dbg_autocolour ( unsigned long stream ) {
+ static struct autocolour acs[NUM_AUTO_COLOURS];
+ static unsigned long use;
+ unsigned int i;
+ unsigned int oldest;
+ unsigned int oldest_last_used;
+
+ /* Increment usage iteration counter */
+ use++;
+
+ /* Scan through list for a currently assigned colour */
+ for ( i = 0 ; i < ( sizeof ( acs ) / sizeof ( acs[0] ) ) ; i++ ) {
+ if ( acs[i].stream == stream ) {
+ acs[i].last_used = use;
+ return i;
+ }
+ }
+
+ /* No colour found; evict the oldest from the list */
+ oldest = 0;
+ oldest_last_used = use;
+ for ( i = 0 ; i < ( sizeof ( acs ) / sizeof ( acs[0] ) ) ; i++ ) {
+ if ( acs[i].last_used < oldest_last_used ) {
+ oldest_last_used = acs[i].last_used;
+ oldest = i;
+ }
+ }
+ acs[oldest].stream = stream;
+ acs[oldest].last_used = use;
+ return oldest;
+}
+
+/**
+ * Select automatic colour for debug messages
+ *
+ * @v stream Message stream ID
+ */
+void dbg_autocolourise ( unsigned long stream ) {
+ printf ( "\033[%dm",
+ ( stream ? ( 31 + dbg_autocolour ( stream ) ) : 0 ) );
+}
+
+/**
+ * Revert to normal colour
+ *
+ */
+void dbg_decolourise ( void ) {
+ printf ( "\033[0m" );
+}
diff --git a/gpxe/src/core/device.c b/gpxe/src/core/device.c
new file mode 100644
index 00000000..b1b148e8
--- /dev/null
+++ b/gpxe/src/core/device.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <gpxe/list.h>
+#include <gpxe/tables.h>
+#include <gpxe/device.h>
+
+/**
+ * @file
+ *
+ * Device model
+ *
+ */
+
+static struct root_device root_devices[0]
+ __table_start ( struct root_device, root_devices );
+static struct root_device root_devices_end[0]
+ __table_end ( struct root_device, root_devices );
+
+/** Registered root devices */
+static LIST_HEAD ( devices );
+
+/**
+ * Probe a root device
+ *
+ * @v rootdev Root device
+ * @ret rc Return status code
+ */
+static int rootdev_probe ( struct root_device *rootdev ) {
+ int rc;
+
+ DBG ( "Adding %s root bus\n", rootdev->dev.name );
+ if ( ( rc = rootdev->driver->probe ( rootdev ) ) != 0 ) {
+ DBG ( "Failed to add %s root bus: %s\n",
+ rootdev->dev.name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Remove a root device
+ *
+ * @v rootdev Root device
+ */
+static void rootdev_remove ( struct root_device *rootdev ) {
+ rootdev->driver->remove ( rootdev );
+ DBG ( "Removed %s root bus\n", rootdev->dev.name );
+}
+
+/**
+ * Probe all devices
+ *
+ * @ret rc Return status code
+ *
+ * This initiates probing for all devices in the system. After this
+ * call, the device hierarchy will be populated, and all hardware
+ * should be ready to use.
+ */
+int probe_devices ( void ) {
+ struct root_device *rootdev;
+ int rc;
+
+ for ( rootdev = root_devices; rootdev < root_devices_end; rootdev++ ) {
+ list_add ( &rootdev->dev.siblings, &devices );
+ INIT_LIST_HEAD ( &rootdev->dev.children );
+ if ( ( rc = rootdev_probe ( rootdev ) ) != 0 )
+ list_del ( &rootdev->dev.siblings );
+ }
+ return 0;
+}
+
+/**
+ * Remove all devices
+ *
+ */
+void remove_devices ( void ) {
+ struct root_device *rootdev;
+ struct root_device *tmp;
+
+ list_for_each_entry_safe ( rootdev, tmp, &devices, dev.siblings ) {
+ rootdev_remove ( rootdev );
+ list_del ( &rootdev->dev.siblings );
+ }
+}
diff --git a/gpxe/src/core/downloader.c b/gpxe/src/core/downloader.c
new file mode 100644
index 00000000..0a443db2
--- /dev/null
+++ b/gpxe/src/core/downloader.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/job.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/umalloc.h>
+#include <gpxe/image.h>
+#include <gpxe/downloader.h>
+
+/** @file
+ *
+ * Image downloader
+ *
+ */
+
+/** A downloader */
+struct downloader {
+ /** Reference count for this object */
+ struct refcnt refcnt;
+
+ /** Job control interface */
+ struct job_interface job;
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+
+ /** Image to contain downloaded file */
+ struct image *image;
+ /** Current position within image buffer */
+ size_t pos;
+ /** Image registration routine */
+ int ( * register_image ) ( struct image *image );
+};
+
+/**
+ * Free downloader object
+ *
+ * @v refcnt Downloader reference counter
+ */
+static void downloader_free ( struct refcnt *refcnt ) {
+ struct downloader *downloader =
+ container_of ( refcnt, struct downloader, refcnt );
+
+ image_put ( downloader->image );
+ free ( downloader );
+}
+
+/**
+ * Terminate download
+ *
+ * @v downloader Downloader
+ * @v rc Reason for termination
+ */
+static void downloader_finished ( struct downloader *downloader, int rc ) {
+
+ /* Block further incoming messages */
+ job_nullify ( &downloader->job );
+ xfer_nullify ( &downloader->xfer );
+
+ /* Free resources and close interfaces */
+ xfer_close ( &downloader->xfer, rc );
+ job_done ( &downloader->job, rc );
+}
+
+/**
+ * Ensure that download buffer is large enough for the specified size
+ *
+ * @v downloader Downloader
+ * @v len Required minimum size
+ * @ret rc Return status code
+ */
+static int downloader_ensure_size ( struct downloader *downloader,
+ size_t len ) {
+ userptr_t new_buffer;
+
+ /* If buffer is already large enough, do nothing */
+ if ( len <= downloader->image->len )
+ return 0;
+
+ DBGC ( downloader, "Downloader %p extending to %zd bytes\n",
+ downloader, len );
+
+ /* Extend buffer */
+ new_buffer = urealloc ( downloader->image->data, len );
+ if ( ! new_buffer ) {
+ DBGC ( downloader, "Downloader %p could not extend buffer to "
+ "%zd bytes\n", downloader, len );
+ return -ENOBUFS;
+ }
+ downloader->image->data = new_buffer;
+ downloader->image->len = len;
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * Job control interface
+ *
+ */
+
+/**
+ * Handle kill() event received via job control interface
+ *
+ * @v job Downloader job control interface
+ */
+static void downloader_job_kill ( struct job_interface *job ) {
+ struct downloader *downloader =
+ container_of ( job, struct downloader, job );
+
+ /* Terminate download */
+ downloader_finished ( downloader, -ECANCELED );
+}
+
+/** Downloader job control interface operations */
+static struct job_interface_operations downloader_job_operations = {
+ .done = ignore_job_done,
+ .kill = downloader_job_kill,
+ .progress = ignore_job_progress,
+};
+
+/****************************************************************************
+ *
+ * Data transfer interface
+ *
+ */
+
+/**
+ * Handle deliver_raw() event received via data transfer interface
+ *
+ * @v xfer Downloader data transfer interface
+ * @v iobuf Datagram I/O buffer
+ * @v meta Data transfer metadata
+ * @ret rc Return status code
+ */
+static int downloader_xfer_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct downloader *downloader =
+ container_of ( xfer, struct downloader, xfer );
+ size_t len;
+ size_t max;
+ int rc;
+
+ /* Calculate new buffer position */
+ if ( meta->whence != SEEK_CUR )
+ downloader->pos = 0;
+ downloader->pos += meta->offset;
+
+ /* Ensure that we have enough buffer space for this data */
+ len = iob_len ( iobuf );
+ max = ( downloader->pos + len );
+ if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 )
+ goto done;
+
+ /* Copy data to buffer */
+ copy_to_user ( downloader->image->data, downloader->pos,
+ iobuf->data, len );
+
+ /* Update current buffer position */
+ downloader->pos += len;
+
+ done:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * Handle close() event received via data transfer interface
+ *
+ * @v xfer Downloader data transfer interface
+ * @v rc Reason for close
+ */
+static void downloader_xfer_close ( struct xfer_interface *xfer, int rc ) {
+ struct downloader *downloader =
+ container_of ( xfer, struct downloader, xfer );
+
+ /* Register image if download was successful */
+ if ( rc == 0 )
+ rc = downloader->register_image ( downloader->image );
+
+ /* Terminate download */
+ downloader_finished ( downloader, rc );
+}
+
+/** Downloader data transfer interface operations */
+static struct xfer_interface_operations downloader_xfer_operations = {
+ .close = downloader_xfer_close,
+ .vredirect = xfer_vopen,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = downloader_xfer_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/****************************************************************************
+ *
+ * Instantiator
+ *
+ */
+
+/**
+ * Instantiate a downloader
+ *
+ * @v job Job control interface
+ * @v image Image to fill with downloaded file
+ * @v register_image Image registration routine
+ * @v type Location type to pass to xfer_open()
+ * @v ... Remaining arguments to pass to xfer_open()
+ * @ret rc Return status code
+ *
+ * Instantiates a downloader object to download the specified URI into
+ * the specified image object. If the download is successful, the
+ * image registration routine @c register_image() will be called.
+ */
+int create_downloader ( struct job_interface *job, struct image *image,
+ int ( * register_image ) ( struct image *image ),
+ int type, ... ) {
+ struct downloader *downloader;
+ va_list args;
+ int rc;
+
+ /* Allocate and initialise structure */
+ downloader = zalloc ( sizeof ( *downloader ) );
+ if ( ! downloader )
+ return -ENOMEM;
+ downloader->refcnt.free = downloader_free;
+ job_init ( &downloader->job, &downloader_job_operations,
+ &downloader->refcnt );
+ xfer_init ( &downloader->xfer, &downloader_xfer_operations,
+ &downloader->refcnt );
+ downloader->image = image_get ( image );
+ downloader->register_image = register_image;
+ va_start ( args, type );
+
+ /* Instantiate child objects and attach to our interfaces */
+ if ( ( rc = xfer_vopen ( &downloader->xfer, type, args ) ) != 0 )
+ goto err;
+
+ /* Attach parent interface, mortalise self, and return */
+ job_plug_plug ( &downloader->job, job );
+ ref_put ( &downloader->refcnt );
+ va_end ( args );
+ return 0;
+
+ err:
+ downloader_finished ( downloader, rc );
+ ref_put ( &downloader->refcnt );
+ va_end ( args );
+ return rc;
+}
diff --git a/gpxe/src/core/errno.c b/gpxe/src/core/errno.c
new file mode 100644
index 00000000..b4f44cec
--- /dev/null
+++ b/gpxe/src/core/errno.c
@@ -0,0 +1,18 @@
+#include <errno.h>
+
+/** @file
+ *
+ * Error codes
+ *
+ * This file provides the global variable #errno.
+ *
+ */
+
+/**
+ * Global "last error" number.
+ *
+ * This is valid only when a function has just returned indicating a
+ * failure.
+ *
+ */
+int errno;
diff --git a/gpxe/src/core/exec.c b/gpxe/src/core/exec.c
new file mode 100644
index 00000000..a1c073e3
--- /dev/null
+++ b/gpxe/src/core/exec.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <assert.h>
+#include <gpxe/tables.h>
+#include <gpxe/command.h>
+
+/** @file
+ *
+ * Command execution
+ *
+ */
+
+static struct command commands[0]
+ __table_start ( struct command, commands );
+static struct command commands_end[0]
+ __table_end ( struct command, commands );
+
+/* Avoid dragging in getopt.o unless a command really uses it */
+int optind;
+int nextchar;
+
+/**
+ * Execute command
+ *
+ * @v command Command name
+ * @v argv Argument list
+ * @ret rc Command exit status
+ *
+ * Execute the named command. Unlike a traditional POSIX execv(),
+ * this function returns the exit status of the command.
+ */
+int execv ( const char *command, char * const argv[] ) {
+ struct command *cmd;
+ int argc;
+
+ /* Count number of arguments */
+ for ( argc = 0 ; argv[argc] ; argc++ ) {}
+
+ /* Sanity checks */
+ if ( ! command ) {
+ DBG ( "No command\n" );
+ return -EINVAL;
+ }
+ if ( ! argc ) {
+ DBG ( "%s: empty argument list\n", command );
+ return -EINVAL;
+ }
+
+ /* Reset getopt() library ready for use by the command. This
+ * is an artefact of the POSIX getopt() API within the context
+ * of Etherboot; see the documentation for reset_getopt() for
+ * details.
+ */
+ reset_getopt();
+
+ /* Hand off to command implementation */
+ for ( cmd = commands ; cmd < commands_end ; cmd++ ) {
+ if ( strcmp ( command, cmd->name ) == 0 )
+ return cmd->exec ( argc, ( char ** ) argv );
+ }
+
+ printf ( "%s: command not found\n", command );
+ return -ENOEXEC;
+}
+
+/**
+ * Split command line into argv array
+ *
+ * @v args Command line
+ * @v argv Argument array to populate, or NULL
+ * @ret argc Argument count
+ *
+ * Splits the command line into whitespace-delimited arguments. If @c
+ * argv is non-NULL, any whitespace in the command line will be
+ * replaced with NULs.
+ */
+static int split_args ( char *args, char * argv[] ) {
+ int argc = 0;
+
+ while ( 1 ) {
+ /* Skip over any whitespace / convert to NUL */
+ while ( *args == ' ' ) {
+ if ( argv )
+ *args = '\0';
+ args++;
+ }
+ /* Check for end of line */
+ if ( ! *args )
+ break;
+ /* We have found the start of the next argument */
+ if ( argv )
+ argv[argc] = args;
+ argc++;
+ /* Skip to start of next whitespace, if any */
+ while ( *args && ( *args != ' ' ) ) {
+ args++;
+ }
+ }
+ return argc;
+}
+
+/**
+ * Execute command line
+ *
+ * @v command Command line
+ * @ret rc Command exit status
+ *
+ * Execute the named command and arguments.
+ */
+int system ( const char *command ) {
+ char *args;
+ int argc;
+ int rc = 0;
+
+ /* Obtain temporary modifiable copy of command line */
+ args = strdup ( command );
+ if ( ! args )
+ return -ENOMEM;
+
+ /* Count arguments */
+ argc = split_args ( args, NULL );
+
+ /* Create argv array and execute command */
+ if ( argc ) {
+ char * argv[argc + 1];
+
+ split_args ( args, argv );
+ argv[argc] = NULL;
+
+ if ( argv[0][0] != '#' )
+ rc = execv ( argv[0], argv );
+ }
+
+ free ( args );
+ return rc;
+}
diff --git a/gpxe/src/core/filter.c b/gpxe/src/core/filter.c
new file mode 100644
index 00000000..f9ebe7cf
--- /dev/null
+++ b/gpxe/src/core/filter.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/xfer.h>
+#include <gpxe/filter.h>
+
+/** @file
+ *
+ * Data transfer filters
+ *
+ */
+
+/*
+ * Pass-through methods to be used by filters which don't want to
+ * intercept all events.
+ *
+ */
+
+void filter_close ( struct xfer_interface *xfer, int rc ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ xfer_close ( other, rc );
+}
+
+int filter_vredirect ( struct xfer_interface *xfer, int type,
+ va_list args ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ return xfer_vredirect ( other, type, args );
+}
+
+size_t filter_window ( struct xfer_interface *xfer ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ return xfer_window ( other );
+}
+
+struct io_buffer * filter_alloc_iob ( struct xfer_interface *xfer,
+ size_t len ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ return xfer_alloc_iob ( other, len );
+}
+
+int filter_deliver_iob ( struct xfer_interface *xfer, struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ return xfer_deliver_iob_meta ( other, iobuf, meta );
+}
+
+int filter_deliver_raw ( struct xfer_interface *xfer, const void *data,
+ size_t len ) {
+ struct xfer_interface *other = filter_other_half ( xfer );
+
+ return xfer_deliver_raw ( other, data, len );
+}
diff --git a/gpxe/src/core/getkey.c b/gpxe/src/core/getkey.c
new file mode 100644
index 00000000..1551cf37
--- /dev/null
+++ b/gpxe/src/core/getkey.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <console.h>
+#include <gpxe/process.h>
+#include <gpxe/keys.h>
+#include <gpxe/timer.h>
+
+/** @file
+ *
+ * Special key interpretation
+ *
+ */
+
+#define GETKEY_TIMEOUT ( TICKS_PER_SEC / 4 )
+
+/**
+ * Read character from console if available within timeout period
+ *
+ * @v timeout Timeout period, in ticks
+ * @ret character Character read from console
+ */
+static int getchar_timeout ( unsigned long timeout ) {
+ unsigned long expiry = ( currticks() + timeout );
+
+ while ( currticks() < expiry ) {
+ step();
+ if ( iskey() )
+ return getchar();
+ }
+
+ return -1;
+}
+
+/**
+ * Get single keypress
+ *
+ * @ret key Key pressed
+ *
+ * The returned key will be an ASCII value or a KEY_XXX special
+ * constant. This function differs from getchar() in that getchar()
+ * will return "special" keys (e.g. cursor keys) as a series of
+ * characters forming an ANSI escape sequence.
+ */
+int getkey ( void ) {
+ int character;
+ int key;
+
+ character = getchar();
+ if ( character != ESC )
+ return character;
+
+ key = 0;
+ while ( ( character = getchar_timeout ( GETKEY_TIMEOUT ) ) >= 0 ) {
+ if ( character == '[' )
+ continue;
+ if ( ! key )
+ key = KEY_ANSI ( character );
+ if ( character >= 0x40 )
+ break;
+ }
+
+ return ( key ? key : ESC );
+}
diff --git a/gpxe/src/core/getopt.c b/gpxe/src/core/getopt.c
new file mode 100644
index 00000000..6de412bb
--- /dev/null
+++ b/gpxe/src/core/getopt.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <getopt.h>
+
+/** @file
+ *
+ * Parse command-line options
+ *
+ */
+
+/**
+ * Option argument
+ *
+ * This will point to the argument for the most recently returned
+ * option, if applicable.
+ */
+char *optarg;
+
+/**
+ * Current option index
+ *
+ * This is an index into the argv[] array. When getopt() returns -1,
+ * @c optind is the index to the first element that is not an option.
+ */
+int optind;
+
+/**
+ * Current option character index
+ *
+ * This is an index into the current element of argv[].
+ */
+int nextchar;
+
+/**
+ * Unrecognised option
+ *
+ * When an unrecognised option is encountered, the actual option
+ * character is stored in @c optopt.
+ */
+int optopt;
+
+/**
+ * Get option argument from argv[] array
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret argument Option argument, or NULL
+ *
+ * Grab the next element of argv[], if it exists and is not an option.
+ */
+static const char * get_argv_argument ( int argc, char * const argv[] ) {
+ char *arg;
+
+ /* Don't overrun argv[] */
+ if ( optind >= argc )
+ return NULL;
+ arg = argv[optind];
+
+ /* If next argv element is an option, then it's not usable as
+ * an argument.
+ */
+ if ( *arg == '-' )
+ return NULL;
+
+ /** Consume this argv element, and return it */
+ optind++;
+ return arg;
+}
+
+/**
+ * Match long option
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @v opttext Option text within current argv[] element
+ * @v longopt Long option specification
+ * @ret option Option to return from getopt()
+ * @ret matched Found a match for this long option
+ */
+static int match_long_option ( int argc, char * const argv[],
+ const char *opttext,
+ const struct option *longopt, int *option ) {
+ size_t optlen;
+ const char *argument = NULL;
+
+ /* Compare option name */
+ optlen = strlen ( longopt->name );
+ if ( strncmp ( opttext, longopt->name, optlen ) != 0 )
+ return 0;
+
+ /* Check for inline argument */
+ if ( opttext[optlen] == '=' ) {
+ argument = &opttext[ optlen + 1 ];
+ } else if ( opttext[optlen] ) {
+ /* Long option with trailing garbage - no match */
+ return 0;
+ }
+
+ /* Consume this argv element */
+ optind++;
+
+ /* If we want an argument but don't have one yet, try to grab
+ * the next argv element
+ */
+ if ( ( longopt->has_arg != no_argument ) && ( ! argument ) )
+ argument = get_argv_argument ( argc, argv );
+
+ /* If we need an argument but don't have one, sulk */
+ if ( ( longopt->has_arg == required_argument ) && ( ! argument ) ) {
+ printf ( "Option \"%s\" requires an argument\n",
+ longopt->name );
+ *option = ':';
+ return 1;
+ }
+
+ /* If we have an argument where we shouldn't have one, sulk */
+ if ( ( longopt->has_arg == no_argument ) && argument ) {
+ printf ( "Option \"%s\" takes no argument\n", longopt->name );
+ *option = ':';
+ return 1;
+ }
+
+ /* Store values and return success */
+ optarg = ( char * ) argument;
+ if ( longopt->flag ) {
+ *(longopt->flag) = longopt->val;
+ *option = 0;
+ } else {
+ *option = longopt->val;
+ }
+ return 1;
+}
+
+/**
+ * Match short option
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @v opttext Option text within current argv[] element
+ * @v shortopt Option character from option specification
+ * @ret option Option to return from getopt()
+ * @ret matched Found a match for this short option
+ */
+static int match_short_option ( int argc, char * const argv[],
+ const char *opttext, int shortopt,
+ enum getopt_argument_requirement has_arg,
+ int *option ) {
+ const char *argument = NULL;
+
+ /* Compare option character */
+ if ( *opttext != shortopt )
+ return 0;
+
+ /* Consume option character */
+ opttext++;
+ nextchar++;
+ if ( *opttext ) {
+ if ( has_arg != no_argument ) {
+ /* Consume remainder of element as inline argument */
+ argument = opttext;
+ optind++;
+ nextchar = 0;
+ }
+ } else {
+ /* Reached end of argv element */
+ optind++;
+ nextchar = 0;
+ }
+
+ /* If we want an argument but don't have one yet, try to grab
+ * the next argv element
+ */
+ if ( ( has_arg != no_argument ) && ( ! argument ) )
+ argument = get_argv_argument ( argc, argv );
+
+ /* If we need an argument but don't have one, sulk */
+ if ( ( has_arg == required_argument ) && ( ! argument ) ) {
+ printf ( "Option \"%c\" requires an argument\n", shortopt );
+ *option = ':';
+ return 1;
+ }
+
+ /* Store values and return success */
+ optarg = ( char * ) argument;
+ *option = shortopt;
+ return 1;
+}
+
+/**
+ * Parse command-line options
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @v optstring Option specification string
+ * @v longopts Long option specification table
+ * @ret longindex Index of long option (or NULL)
+ * @ret option Option found, or -1 for no more options
+ *
+ * Note that the caller must arrange for reset_getopt() to be called
+ * before each set of calls to getopt_long(). In Etherboot, this is
+ * done automatically by execv().
+ */
+int getopt_long ( int argc, char * const argv[], const char *optstring,
+ const struct option *longopts, int *longindex ) {
+ const char *opttext = argv[optind];
+ const struct option *longopt;
+ int shortopt;
+ enum getopt_argument_requirement has_arg;
+ int option;
+
+ /* Check for end of argv array */
+ if ( optind >= argc )
+ return -1;
+
+ /* Check for end of options */
+ if ( *(opttext++) != '-' )
+ return -1;
+
+ /* Check for long options */
+ if ( *(opttext++) == '-' ) {
+ for ( longopt = longopts ; longopt->name ; longopt++ ) {
+ if ( ! match_long_option ( argc, argv, opttext,
+ longopt, &option ) )
+ continue;
+ if ( longindex )
+ *longindex = ( longopt - longopts );
+ return option;
+ }
+ optopt = '?';
+ printf ( "Unrecognised option \"--%s\"\n", opttext );
+ return '?';
+ }
+
+ /* Check for short options */
+ if ( nextchar < 1 )
+ nextchar = 1;
+ opttext = ( argv[optind] + nextchar );
+ while ( ( shortopt = *(optstring++) ) ) {
+ has_arg = no_argument;
+ while ( *optstring == ':' ) {
+ has_arg++;
+ optstring++;
+ }
+ if ( match_short_option ( argc, argv, opttext, shortopt,
+ has_arg, &option ) ) {
+ return option;
+ }
+ }
+ optopt = *opttext;
+ printf ( "Unrecognised option \"-%c\"\n", optopt );
+ return '?';
+}
diff --git a/gpxe/src/core/hw.c b/gpxe/src/core/hw.c
new file mode 100644
index 00000000..65604ee0
--- /dev/null
+++ b/gpxe/src/core/hw.c
@@ -0,0 +1,74 @@
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/process.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+
+/** @file
+ *
+ * "Hello World" data source
+ *
+ */
+
+struct hw {
+ struct refcnt refcnt;
+ struct xfer_interface xfer;
+ struct process process;
+};
+
+static const char hw_msg[] = "Hello world!\n";
+
+static void hw_finished ( struct hw *hw, int rc ) {
+ xfer_nullify ( &hw->xfer );
+ xfer_close ( &hw->xfer, rc );
+ process_del ( &hw->process );
+}
+
+static void hw_xfer_close ( struct xfer_interface *xfer, int rc ) {
+ struct hw *hw = container_of ( xfer, struct hw, xfer );
+
+ hw_finished ( hw, rc );
+}
+
+static struct xfer_interface_operations hw_xfer_operations = {
+ .close = hw_xfer_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = ignore_xfer_deliver_raw,
+};
+
+static void hw_step ( struct process *process ) {
+ struct hw *hw = container_of ( process, struct hw, process );
+ int rc;
+
+ if ( xfer_window ( &hw->xfer ) ) {
+ rc = xfer_deliver_raw ( &hw->xfer, hw_msg, sizeof ( hw_msg ) );
+ hw_finished ( hw, rc );
+ }
+}
+
+static int hw_open ( struct xfer_interface *xfer, struct uri *uri __unused ) {
+ struct hw *hw;
+
+ /* Allocate and initialise structure */
+ hw = zalloc ( sizeof ( *hw ) );
+ if ( ! hw )
+ return -ENOMEM;
+ xfer_init ( &hw->xfer, &hw_xfer_operations, &hw->refcnt );
+ process_init ( &hw->process, hw_step, &hw->refcnt );
+
+ /* Attach parent interface, mortalise self, and return */
+ xfer_plug_plug ( &hw->xfer, xfer );
+ ref_put ( &hw->refcnt );
+ return 0;
+}
+
+struct uri_opener hw_uri_opener __uri_opener = {
+ .scheme = "hw",
+ .open = hw_open,
+};
diff --git a/gpxe/src/core/i82365.c b/gpxe/src/core/i82365.c
new file mode 100644
index 00000000..c26639e0
--- /dev/null
+++ b/gpxe/src/core/i82365.c
@@ -0,0 +1,656 @@
+#ifdef CONFIG_PCMCIA
+
+/*
+ * i82365.c
+ * Support for i82365 and similar ISA-to-PCMCIA bridges
+ *
+ * Taken from Linux kernel sources, distributed under GPL2
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ * Ported by: Anselm Martin Hoffmeister, Stockholm Projekt Computer-Service, Sankt Augustin/Bonn, GERMANY
+ */
+
+/*
+ *
+ *
+ * ******************************
+ * PLEASE DO NOT YET WORK ON THIS
+ * ******************************
+ *
+ * I'm still fixing it up on every end, so we most probably would interfere
+ * at some point. If there's anything obvious or better, not-so-obvious,
+ * please contact me by e-mail: anselm (AT) hoffmeister (DOT) be *THANKS*
+ */
+#include "../include/pcmcia.h"
+#include "../include/pcmcia-opts.h"
+#include "../include/i82365.h"
+
+#ifndef CONFIG_ISA
+#error PCMCIA_I82365 only works with ISA defined - set CONFIG_ISA
+#endif
+
+typedef enum pcic_id {
+ IS_I82365A, IS_I82365B, IS_I82365DF,
+ IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469,
+ IS_PD6710, IS_PD672X, IS_VT83C469,
+} pcic_id;
+
+/* Flags for classifying groups of controllers */
+#define IS_VADEM 0x0001
+#define IS_CIRRUS 0x0002
+#define IS_TI 0x0004
+#define IS_O2MICRO 0x0008
+#define IS_VIA 0x0010
+#define IS_TOPIC 0x0020
+#define IS_RICOH 0x0040
+#define IS_UNKNOWN 0x0400
+#define IS_VG_PWR 0x0800
+#define IS_DF_PWR 0x1000
+#define IS_PCI 0x2000
+#define IS_ALIVE 0x8000
+
+typedef struct pcic_t {
+ char *name;
+ u_short flags;
+} pcic_t;
+
+static pcic_t pcic[] = {
+ { "Intel i82365sl A step", 0 },
+ { "Intel i82365sl B step", 0 },
+ { "Intel i82365sl DF", IS_DF_PWR },
+ { "IBM Clone", 0 },
+ { "Ricoh RF5C296/396", 0 },
+ { "VLSI 82C146", 0 },
+ { "Vadem VG-468", IS_VADEM },
+ { "Vadem VG-469", IS_VADEM|IS_VG_PWR },
+ { "Cirrus PD6710", IS_CIRRUS },
+ { "Cirrus PD672x", IS_CIRRUS },
+ { "VIA VT83C469", IS_CIRRUS|IS_VIA },
+};
+
+typedef struct cirrus_state_t {
+ u_char misc1, misc2;
+ u_char timer[6];
+} cirrus_state_t;
+
+typedef struct vg46x_state_t {
+ u_char ctl, ema;
+} vg46x_state_t;
+
+typedef struct socket_info_t {
+ u_short type, flags;
+ socket_cap_t cap;
+ ioaddr_t ioaddr;
+ u_short psock;
+ u_char cs_irq, intr;
+ void (*handler)(void *info, u_int events);
+ void *info;
+ union {
+ cirrus_state_t cirrus;
+ vg46x_state_t vg46x;
+ } state;
+} socket_info_t;
+
+//static socket_info_t socket[8];
+
+int i365_base = 0x3e0; // Default in Linux kernel
+int cycle_time = 120; // External clock time in ns, 120ns =~ 8.33 MHz
+int mydriverid = 0;
+
+void phex ( unsigned char c );
+/*static int to_cycles(int ns)
+{
+ return ns/cycle_time;
+}
+*/
+/*static int to_ns(int cycles)
+{
+ return cycle_time*cycles;
+}
+*/
+
+static u_char i365_get(u_short sock, u_short reg)
+{
+ //unsigned long flags;
+ //spin_lock_irqsave(&bus_lock,flags);
+ {
+ ioaddr_t port = pccsock[sock].ioaddr;
+ u_char val;
+ reg = I365_REG(pccsock[sock].internalid, reg);
+ outb(reg, port); val = inb(port+1);
+ //spin_unlock_irqrestore(&bus_lock,flags);
+ return val;
+ }
+}
+
+static void i365_set(u_short sock, u_short reg, u_char data)
+{
+ //unsigned long flags;
+ //spin_lock_irqsave(&bus_lock,flags);
+ {
+ ioaddr_t port = pccsock[sock].ioaddr;
+ u_char val = I365_REG(pccsock[sock].internalid, reg);
+ outb(val, port); outb(data, port+1);
+ //spin_unlock_irqrestore(&bus_lock,flags);
+ }
+}
+
+void add_socket_i365(u_short port, int psock, int type) {
+ pccsock[pccsocks].ioaddr = port;
+ pccsock[pccsocks].internalid = psock;
+ pccsock[pccsocks].type = type;
+ pccsock[pccsocks].flags = pcic[type].flags;
+ pccsock[pccsocks].drivernum = mydriverid;
+ pccsock[pccsocks].configoffset = -1;
+ // Find out if a card in inside that socket
+ pccsock[pccsocks].status = (( 12 == (i365_get(pccsocks,I365_STATUS)&12) ) ? HASCARD : EMPTY );
+ // *TODO* check if that's all
+ if ( 0 == (psock & 1) ) {
+ printf ( "Found a PCMCIA controller (i82365) at io %x, type '%s'\n", port, pcic[type].name );
+ // pccsock[pccsocks].status == HASCARD? "holds card":"empty" );
+ }
+ pccsocks++;
+ return;
+}
+
+void i365_bset(u_short sock, u_short reg, u_char mask) {
+ u_char d = i365_get(sock, reg);
+ d |= mask;
+ i365_set(sock, reg, d);
+}
+
+void i365_bclr(u_short sock, u_short reg, u_char mask) {
+ u_char d = i365_get(sock, reg);
+ d &= ~mask;
+ i365_set(sock, reg, d);
+}
+
+
+/*static void i365_bflip(u_short sock, u_short reg, u_char mask, int b)
+{
+ u_char d = i365_get(sock, reg);
+ if (b)
+ d |= mask;
+ else
+ d &= ~mask;
+ i365_set(sock, reg, d);
+}
+*/
+
+/*
+static u_short i365_get_pair(u_short sock, u_short reg)
+{
+ u_short a, b;
+ a = i365_get(sock, reg);
+ b = i365_get(sock, reg+1);
+ return (a + (b<<8));
+}
+*/
+
+/*
+static void i365_set_pair(u_short sock, u_short reg, u_short data)
+{
+ i365_set(sock, reg, data & 0xff);
+ i365_set(sock, reg+1, data >> 8);
+}
+*/
+int identify_i365 ( u_short port, u_short sock ) {
+ u_char val;
+ int type = -1;
+ /* Use the next free entry in the socket table */
+ pccsock[pccsocks].ioaddr = port;
+ pccsock[pccsocks].internalid = sock;
+ // *TODO* wakeup a sleepy cirrus controller?
+
+ if ((val = i365_get(pccsocks, I365_IDENT)) & 0x70)
+ return -1;
+ switch (val) {
+ case 0x82:
+ type = IS_I82365A; break;
+ case 0x83:
+ type = IS_I82365B; break;
+ case 0x84:
+ type = IS_I82365DF; break;
+ case 0x88: case 0x89: case 0x8a:
+ type = IS_IBM; break;
+ }
+ /* Check for Vadem VG-468 chips */
+ outb(0x0e, port);
+ outb(0x37, port);
+ i365_bset(pccsocks, VG468_MISC, VG468_MISC_VADEMREV);
+ val = i365_get(pccsocks, I365_IDENT);
+ if (val & I365_IDENT_VADEM) {
+ i365_bclr(pccsocks, VG468_MISC, VG468_MISC_VADEMREV);
+ type = ((val & 7) >= 4) ? IS_VG469 : IS_VG468;
+ }
+
+ /* Check for Ricoh chips */
+ val = i365_get(pccsocks, RF5C_CHIP_ID);
+ if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396)) type = IS_RF5Cx96;
+
+ /* Check for Cirrus CL-PD67xx chips */
+ i365_set(pccsocks, PD67_CHIP_INFO, 0);
+ val = i365_get(pccsocks, PD67_CHIP_INFO);
+ if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) {
+ val = i365_get(pccsocks, PD67_CHIP_INFO);
+ if ((val & PD67_INFO_CHIP_ID) == 0) {
+ type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710;
+ i365_set(pccsocks, PD67_EXT_INDEX, 0xe5);
+ if (i365_get(pccsocks, PD67_EXT_INDEX) != 0xe5) type = IS_VT83C469;
+ }
+ }
+ return type;
+}
+
+int init_i82365(void) {
+ int i, j, sock, k, ns, id;
+ //unsigned int ui,uj;
+ //unsigned char * upc;
+ ioaddr_t port;
+ int i82365s = 0;
+ // Change from kernel: No irq init, no check_region, no isapnp support
+ // No ignore socket, no extra sockets to check (so it's easier here :-/)
+ // Probably we don't need any of them; in case YOU do, SHOUT AT ME!
+ id = identify_i365(i365_base, 0);
+ if ((id == IS_I82365DF) && (identify_i365(i365_base, 1) != id)) {
+ for (i = 0; i < 4; i++) {
+ port = i365_base + ((i & 1) << 2) + ((i & 2) << 1);
+ sock = (i & 1) << 1;
+ if (identify_i365(port, sock) == IS_I82365DF) {
+ add_socket_i365(port, sock, IS_VLSI);
+ }
+ }
+ } else {
+ for (i = 0; i < 4; i += 2) {
+ port = i365_base + 2*(i>>2);
+ sock = (i & 3);
+ id = identify_i365(port, sock);
+ if (id < 0) continue;
+
+ for (j = ns = 0; j < 2; j++) {
+ /* Does the socket exist? */
+ if (identify_i365(port, sock+j) < 0) continue;
+ /* Check for bad socket decode */
+ for (k = 0; k <= i82365s; k++)
+ i365_set(k, I365_MEM(0)+I365_W_OFF, k);
+ for (k = 0; k <= i82365s; k++)
+ if (i365_get(k, I365_MEM(0)+I365_W_OFF) != k)
+ break;
+ if (k <= i82365s) break;
+ add_socket_i365(port, sock+j, id); ns++;
+ }
+ }
+ }
+ return 0;
+
+
+
+
+
+
+
+/* printf ( "Selecting config 1: io 0x300 @byte 87*2.." );
+ upc[(2*87)] = 2;
+ i365_bclr(1, I365_ADDRWIN, 1 );
+ i365_set(1,I365_INTCTL, 0x65 ); //no-reset, memory-card
+ i365_set(1, I365_IO(0)+0, 0x20 );
+ i365_set(1, I365_IO(0)+1, 0x03 );
+ i365_set(1, I365_IO(0)+2, 0x3f );
+ i365_set(1, I365_IO(0)+3, 0x03 );
+ i365_set(1, 0x3a, 0x05 );
+ i365_set(1, 0x3b, 0x05 );
+ i365_set(1, 0x3c, 0x05 );
+ i365_set(1, 0x3d, 0x05 );
+ i365_set(1, 0x3e, 0x05 );
+ i365_set(1, 0x3f, 0x05 );
+ i365_set(1, 0x07, 0x0a );
+ i365_set(1, I365_ADDRWIN, 0x40 ); // 0x40
+ printf ( "!\n" ); getchar();
+ printf ( "\n" );
+ return 0; */
+}
+
+void phex ( unsigned char c ) {
+ unsigned char a = 0, b = 0;
+ b = ( c & 0xf );
+ if ( b > 9 ) b += ('a'-'9'-1);
+ b += '0';
+ a = ( c & 0xf0 ) >> 4;
+ if ( a > 9 ) a += ('a'-'9'-1);
+ a += '0';
+ printf ( "%c%c ", a, b );
+ return;
+}
+
+int deinit_i82365(void) {
+ printf("Deinitializing i82365\n" );
+ return 0;
+}
+
+/*static int i365_get_status(u_short sock, u_int *value)
+{
+ u_int status;
+
+ status = i365_get(sock, I365_STATUS);
+ *value = ((status & I365_CS_DETECT) == I365_CS_DETECT)
+ ? SS_DETECT : 0;
+
+ if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD)
+ *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG;
+ else {
+ *value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD;
+ *value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN;
+ }
+ *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0;
+ *value |= (status & I365_CS_READY) ? SS_READY : 0;
+ *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0;
+
+#ifdef CONFIG_ISA
+ if (pccsock[sock].type == IS_VG469) {
+ status = i365_get(sock, VG469_VSENSE);
+ if (pccsock[sock].internalid & 1) {
+ *value |= (status & VG469_VSENSE_B_VS1) ? 0 : SS_3VCARD;
+ *value |= (status & VG469_VSENSE_B_VS2) ? 0 : SS_XVCARD;
+ } else {
+ *value |= (status & VG469_VSENSE_A_VS1) ? 0 : SS_3VCARD;
+ *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD;
+ }
+ }
+#endif
+
+ printf("i82365: GetStatus(%d) = %#4.4x\n", sock, *value);
+ return 0;
+} //i365_get_status
+*/
+
+/*static int i365_set_socket(u_short sock, socket_state_t *state)
+{
+ socket_info_t *t = &socket[sock];
+ u_char reg;
+
+ printf("i82365: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
+ state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+printf ("\nERROR:UNIMPLEMENTED\n" );
+return 0;
+ // First set global controller options
+ // set_bridge_state(sock); *TODO* check: need this here?
+
+ // IO card, RESET flag, IO interrupt
+ reg = t->intr;
+ if (state->io_irq != t->cap.pci_irq) reg |= state->io_irq;
+ reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET;
+ reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0;
+ i365_set(sock, I365_INTCTL, reg);
+
+ reg = I365_PWR_NORESET;
+ if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO;
+ if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT;
+
+ if (t->flags & IS_CIRRUS) {
+ if (state->Vpp != 0) {
+ if (state->Vpp == 120)
+ reg |= I365_VPP1_12V;
+ else if (state->Vpp == state->Vcc)
+ reg |= I365_VPP1_5V;
+ else return -EINVAL;
+ }
+ if (state->Vcc != 0) {
+ reg |= I365_VCC_5V;
+ if (state->Vcc == 33)
+ i365_bset(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
+ else if (state->Vcc == 50)
+ i365_bclr(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
+ else return -EINVAL;
+ }
+ } else if (t->flags & IS_VG_PWR) {
+ if (state->Vpp != 0) {
+ if (state->Vpp == 120)
+ reg |= I365_VPP1_12V;
+ else if (state->Vpp == state->Vcc)
+ reg |= I365_VPP1_5V;
+ else return -EINVAL;
+ }
+ if (state->Vcc != 0) {
+ reg |= I365_VCC_5V;
+ if (state->Vcc == 33)
+ i365_bset(sock, VG469_VSELECT, VG469_VSEL_VCC);
+ else if (state->Vcc == 50)
+ i365_bclr(sock, VG469_VSELECT, VG469_VSEL_VCC);
+ else return -EINVAL;
+ }
+ } else if (t->flags & IS_DF_PWR) {
+ switch (state->Vcc) {
+ case 0: break;
+ case 33: reg |= I365_VCC_3V; break;
+ case 50: reg |= I365_VCC_5V; break;
+ default: return -EINVAL;
+ }
+ switch (state->Vpp) {
+ case 0: break;
+ case 50: reg |= I365_VPP1_5V; break;
+ case 120: reg |= I365_VPP1_12V; break;
+ default: return -EINVAL;
+ }
+ } else {
+ switch (state->Vcc) {
+ case 0: break;
+ case 50: reg |= I365_VCC_5V; break;
+ default: return -EINVAL;
+ }
+ switch (state->Vpp) {
+ case 0: break;
+ case 50: reg |= I365_VPP1_5V | I365_VPP2_5V; break;
+ case 120: reg |= I365_VPP1_12V | I365_VPP2_12V; break;
+ default: return -EINVAL;
+ }
+ }
+
+ if (reg != i365_get(sock, I365_POWER))
+ i365_set(sock, I365_POWER, reg);
+
+ // Chipset-specific functions
+ if (t->flags & IS_CIRRUS) {
+ // Speaker control
+ i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA,
+ state->flags & SS_SPKR_ENA);
+ }
+
+ // Card status change interrupt mask
+ reg = t->cs_irq << 4;
+ if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
+ if (state->flags & SS_IOCARD) {
+ if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG;
+ } else {
+ if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1;
+ if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2;
+ if (state->csc_mask & SS_READY) reg |= I365_CSC_READY;
+ }
+ i365_set(sock, I365_CSCINT, reg);
+ i365_get(sock, I365_CSC);
+
+ return 0;
+} // i365_set_socket
+*/
+
+/*static int i365_get_io_map(u_short sock, struct pccard_io_map *io)
+{
+ u_char map, ioctl, addr;
+ printf ( "GETIOMAP unimplemented\n" ); return 0;
+ map = io->map;
+ if (map > 1) return -EINVAL;
+ io->start = i365_get_pair(sock, I365_IO(map)+I365_W_START);
+ io->stop = i365_get_pair(sock, I365_IO(map)+I365_W_STOP);
+ ioctl = i365_get(sock, I365_IOCTL);
+ addr = i365_get(sock, I365_ADDRWIN);
+ io->speed = to_ns(ioctl & I365_IOCTL_WAIT(map)) ? 1 : 0;
+ io->flags = (addr & I365_ENA_IO(map)) ? MAP_ACTIVE : 0;
+ io->flags |= (ioctl & I365_IOCTL_0WS(map)) ? MAP_0WS : 0;
+ io->flags |= (ioctl & I365_IOCTL_16BIT(map)) ? MAP_16BIT : 0;
+ io->flags |= (ioctl & I365_IOCTL_IOCS16(map)) ? MAP_AUTOSZ : 0;
+ printf("i82365: GetIOMap(%d, %d) = %#2.2x, %d ns, "
+ "%#4.4x-%#4.4x\n", sock, map, io->flags, io->speed,
+ io->start, io->stop);
+ return 0;
+} // i365_get_io_map
+*/
+
+/*====================================================================*/
+
+/*static int i365_set_io_map(u_short sock, struct pccard_io_map *io)
+{
+ u_char map, ioctl;
+
+ printf("i82365: SetIOMap(%d, %d, %#2.2x, %d ns, "
+ "%#4.4x-%#4.4x)\n", sock, io->map, io->flags,
+ io->speed, io->start, io->stop);
+printf ( "UNIMPLEMENTED\n" );
+ return 0;
+ map = io->map;
+ //if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
+ if ((map > 1) ||
+ (io->stop < io->start)) return -EINVAL;
+ // Turn off the window before changing anything
+ if (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(map))
+ i365_bclr(sock, I365_ADDRWIN, I365_ENA_IO(map));
+ i365_set_pair(sock, I365_IO(map)+I365_W_START, io->start);
+ i365_set_pair(sock, I365_IO(map)+I365_W_STOP, io->stop);
+ ioctl = i365_get(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map);
+ if (io->speed) ioctl |= I365_IOCTL_WAIT(map);
+ if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
+ if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
+ if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
+ i365_set(sock, I365_IOCTL, ioctl);
+ // Turn on the window if necessary
+ if (io->flags & MAP_ACTIVE)
+ i365_bset(sock, I365_ADDRWIN, I365_ENA_IO(map));
+ return 0;
+} // i365_set_io_map
+*/
+
+/*
+static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem)
+{
+ u_short base, i;
+ u_char map;
+
+ printf("i82365: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5"
+ "lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed,
+ mem->sys_start, mem->sys_stop, mem->card_start);
+
+printf ( "UNIMPLEMENTED\n" );
+ return 0;
+ map = mem->map;
+ if ((map > 4) || (mem->card_start > 0x3ffffff) ||
+ (mem->sys_start > mem->sys_stop) || (mem->speed > 1000))
+ return -EINVAL;
+ if (!(socket[sock].flags & IS_PCI) &&
+ ((mem->sys_start > 0xffffff) || (mem->sys_stop > 0xffffff)))
+ return -EINVAL;
+
+ // Turn off the window before changing anything
+ if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
+ i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+
+ base = I365_MEM(map);
+ i = (mem->sys_start >> 12) & 0x0fff;
+ if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT;
+ if (mem->flags & MAP_0WS) i |= I365_MEM_0WS;
+ i365_set_pair(sock, base+I365_W_START, i);
+
+ i = (mem->sys_stop >> 12) & 0x0fff;
+ switch (to_cycles(mem->speed)) {
+ case 0: break;
+ case 1: i |= I365_MEM_WS0; break;
+ case 2: i |= I365_MEM_WS1; break;
+ default: i |= I365_MEM_WS1 | I365_MEM_WS0; break;
+ }
+ i365_set_pair(sock, base+I365_W_STOP, i);
+
+ i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff;
+ if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT;
+ if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG;
+ i365_set_pair(sock, base+I365_W_OFF, i);
+
+ // Turn on the window if necessary
+ if (mem->flags & MAP_ACTIVE)
+ i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+ return 0;
+} // i365_set_mem_map
+*/
+
+
+int i82365_interfacer ( interface_func_t func, int sockno, int par1, int par2, void* par3 ) {
+ //int i, j, k;
+ //u_int ui;
+ u_char *upc;
+ struct pcc_config_t * pccc;
+ switch ( func ) {
+ case INIT:
+ mydriverid = par1;
+ return init_i82365();
+ case SHUTDOWN:
+ i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 );
+ i365_set(sockno, I365_INTCTL, 0x05 );
+ sleepticks(2);
+ i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card
+ break;
+ case MAPATTRMEM:
+ i365_set(sockno,I365_POWER, 0xb1 );
+ i365_set(sockno, I365_INTCTL, 0x05 );
+ sleepticks(2);
+ i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card
+ i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 );
+ //i365_bclr(sockno, I365_ADDRWIN, 1 );
+ i365_set(sockno, I365_MEM(0)+0, ( par1 >> 12 )& 0xff ); //start
+ i365_set(sockno, I365_MEM(0)+1, ( par1 >> 20 ) & 0x0f );
+ i365_set(sockno, I365_MEM(0)+2, ((par1 + par2 - 1 ) >> 12 ) & 0xff ); //end
+ i365_set(sockno, I365_MEM(0)+3, (( par1 + par2 - 1 ) >> 20 ) & 0x0f );
+ i365_set(sockno, I365_MEM(0)+4, ((0x4000000 - par1) >> 12) & 0xff ); //offset low
+ i365_set(sockno, I365_MEM(0)+5, 0x40 | (((0x40000000 - par1) >> 12) & 0x3f));
+ i365_bset(sockno, I365_ADDRWIN, 1 );
+ if ( ! ( 1 & i365_get ( sockno, I365_ADDRWIN ) ) ) return 1;
+ break;
+ case UNMAPATTRMEM:
+ i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 );
+ i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card
+ break;
+ case SELECTCONFIG: // Params: par1: config number; par3 config pointer pointer
+ if ( 0 > pccsock[sockno].configoffset ) return 1;
+ if ( NULL == (pccc = par3 ) ) return 2;
+ // write config number to
+ upc = ioremap ( MAP_ATTRMEM_TO, MAP_ATTRMEM_LEN );
+ if ( pccsock[sockno].configoffset > MAP_ATTRMEM_LEN ) return 3;
+ if ( ( par1 & 0x7fffffc0 ) ) return 4;
+ if ( pccc->index != par1 ) return 5;
+ upc[pccsock[sockno].configoffset] = ( upc[pccsock[sockno].configoffset] & 0xc0 ) | ( par1 & 0x3f );
+ i365_set(sockno, I365_IOCTL, (i365_get(sockno, I365_IOCTL) & 0xfe) | 0x20 ); // 16bit autosize
+ i365_set(sockno, I365_IO(0)+0, pccc->iowin & 0xff);
+ i365_set(sockno, I365_IO(0)+1, (pccc->iowin >> 8) & 0xff);
+ i365_set(sockno, I365_IO(0)+2, (pccc->iowin+pccc->iolen - 1) & 0xff);
+ i365_set(sockno, I365_IO(0)+3, ((pccc->iowin+pccc->iolen- 1) >> 8) & 0xff);
+ // Disable mem mapping
+ i365_bclr(sockno, I365_ADDRWIN, 1);
+ i365_set(sockno, I365_INTCTL, 0x65);
+ i365_bset(sockno, I365_ADDRWIN,0x40);
+ break;
+ default:
+ return -1; // ERROR: Unknown function called
+ }
+ return 0;
+}
+
+// get_mem_map[1320]
+// cirrus_get_state/set/opts...
+// vg46x_get_state/...
+// get_bridge_state/...
+
+#endif /* CONFIG_PCMCIA */
diff --git a/gpxe/src/core/ibft.c b/gpxe/src/core/ibft.c
new file mode 100644
index 00000000..fda14704
--- /dev/null
+++ b/gpxe/src/core/ibft.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright Fen Systems Ltd. 2007. Portions of this code are derived
+ * from IBM Corporation Sample Programs. Copyright IBM Corporation
+ * 2004, 2007. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <realmode.h>
+#include <gpxe/pci.h>
+#include <gpxe/acpi.h>
+#include <gpxe/in.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/iscsi.h>
+#include <gpxe/ibft.h>
+
+/** @file
+ *
+ * iSCSI boot firmware table
+ *
+ * The information in this file is derived from the document "iSCSI
+ * Boot Firmware Table (iBFT)" as published by IBM at
+ *
+ * ftp://ftp.software.ibm.com/systems/support/system_x_pdf/ibm_iscsi_boot_firmware_table_v1.02.pdf
+ *
+ */
+
+#define ibftab __use_data16 ( ibftab )
+/** The iBFT used by gPXE */
+struct gpxe_ibft __data16 ( ibftab ) = {
+ /* Table header */
+ .table = {
+ /* ACPI header */
+ .acpi = {
+ .signature = IBFT_SIG,
+ .length = sizeof ( ibftab ),
+ .revision = 1,
+ .oem_id = "FENSYS",
+ .oem_table_id = "gPXE",
+ },
+ /* Control block */
+ .control = {
+ .header = {
+ .structure_id = IBFT_STRUCTURE_ID_CONTROL,
+ .version = 1,
+ .length = sizeof ( ibftab.table.control ),
+ .flags = 0,
+ },
+ .initiator = offsetof ( typeof ( ibftab ), initiator ),
+ .nic_0 = offsetof ( typeof ( ibftab ), nic ),
+ .target_0 = offsetof ( typeof ( ibftab ), target ),
+ },
+ },
+ /* iSCSI initiator information */
+ .initiator = {
+ .header = {
+ .structure_id = IBFT_STRUCTURE_ID_INITIATOR,
+ .version = 1,
+ .length = sizeof ( ibftab.initiator ),
+ .flags = ( IBFT_FL_INITIATOR_BLOCK_VALID |
+ IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED ),
+ },
+ },
+ /* NIC information */
+ .nic = {
+ .header = {
+ .structure_id = IBFT_STRUCTURE_ID_NIC,
+ .version = 1,
+ .length = sizeof ( ibftab.nic ),
+ .flags = ( IBFT_FL_NIC_BLOCK_VALID |
+ IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED ),
+ },
+ },
+ /* iSCSI target information */
+ .target = {
+ .header = {
+ .structure_id = IBFT_STRUCTURE_ID_TARGET,
+ .version = 1,
+ .length = sizeof ( ibftab.target ),
+ .flags = ( IBFT_FL_TARGET_BLOCK_VALID |
+ IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED ),
+ },
+ },
+};
+
+/**
+ * Fill in an IP address field within iBFT
+ *
+ * @v ipaddr IP address field
+ * @v in IPv4 address
+ */
+static void ibft_set_ipaddr ( struct ibft_ipaddr *ipaddr, struct in_addr in ) {
+ memset ( ipaddr, 0, sizeof ( ipaddr ) );
+ if ( in.s_addr ) {
+ ipaddr->in = in;
+ ipaddr->ones = 0xffff;
+ }
+}
+
+/**
+ * Fill in an IP address within iBFT from configuration setting
+ *
+ * @v ipaddr IP address field
+ * @v setting Configuration setting
+ * @v tag DHCP option tag
+ */
+static void ibft_set_ipaddr_option ( struct ibft_ipaddr *ipaddr,
+ struct setting *setting ) {
+ struct in_addr in = { 0 };
+ fetch_ipv4_setting ( NULL, setting, &in );
+ ibft_set_ipaddr ( ipaddr, in );
+}
+
+/**
+ * Allocate a string within iBFT
+ *
+ * @v strings iBFT string block descriptor
+ * @v string String field to fill in
+ * @v len Length of string to allocate (excluding NUL)
+ * @ret rc Return status code
+ */
+static int ibft_alloc_string ( struct ibft_string_block *strings,
+ struct ibft_string *string, size_t len ) {
+ char *dest;
+ unsigned int remaining;
+
+ dest = ( ( ( char * ) strings->table ) + strings->offset );
+ remaining = ( strings->table->acpi.length - strings->offset );
+ if ( len >= remaining )
+ return -ENOMEM;
+
+ string->offset = strings->offset;
+ string->length = len;
+ strings->offset += ( len + 1 );
+ return 0;
+}
+
+/**
+ * Fill in a string field within iBFT
+ *
+ * @v strings iBFT string block descriptor
+ * @v string String field
+ * @v data String to fill in
+ * @ret rc Return status code
+ */
+static int ibft_set_string ( struct ibft_string_block *strings,
+ struct ibft_string *string, const char *data ) {
+ size_t len = strlen ( data );
+ char *dest;
+ int rc;
+
+ if ( ( rc = ibft_alloc_string ( strings, string, len ) ) != 0 )
+ return rc;
+ dest = ( ( ( char * ) strings->table ) + string->offset );
+ strcpy ( dest, data );
+
+ return 0;
+}
+
+/**
+ * Fill in a string field within iBFT from configuration setting
+ *
+ * @v strings iBFT string block descriptor
+ * @v string String field
+ * @v setting Configuration setting
+ * @ret rc Return status code
+ */
+static int ibft_set_string_option ( struct ibft_string_block *strings,
+ struct ibft_string *string,
+ struct setting *setting ) {
+ int len;
+ char *dest;
+ int rc;
+
+ len = fetch_setting_len ( NULL, setting );
+ if ( len < 0 ) {
+ string->offset = 0;
+ string->length = 0;
+ return 0;
+ }
+
+ if ( ( rc = ibft_alloc_string ( strings, string, len ) ) != 0 )
+ return rc;
+ dest = ( ( ( char * ) strings->table ) + string->offset );
+ fetch_string_setting ( NULL, setting, dest, ( len + 1 ) );
+ return 0;
+}
+
+/**
+ * Fill in NIC portion of iBFT
+ *
+ * @v nic NIC portion of iBFT
+ * @v strings iBFT string block descriptor
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int ibft_fill_nic ( struct ibft_nic *nic,
+ struct ibft_string_block *strings,
+ struct net_device *netdev ) {
+ struct in_addr netmask_addr = { 0 };
+ unsigned int netmask_count = 0;
+ int rc;
+
+ /* Extract values from DHCP configuration */
+ ibft_set_ipaddr_option ( &nic->ip_address, &ip_setting );
+ ibft_set_ipaddr_option ( &nic->gateway, &gateway_setting );
+ ibft_set_ipaddr_option ( &nic->dns[0], &dns_setting );
+ if ( ( rc = ibft_set_string_option ( strings, &nic->hostname,
+ &hostname_setting ) ) != 0 )
+ return rc;
+
+ /* Derive subnet mask prefix from subnet mask */
+ fetch_ipv4_setting ( NULL, &netmask_setting, &netmask_addr );
+ while ( netmask_addr.s_addr ) {
+ if ( netmask_addr.s_addr & 0x1 )
+ netmask_count++;
+ netmask_addr.s_addr >>= 1;
+ }
+ nic->subnet_mask_prefix = netmask_count;
+
+ /* Extract values from net-device configuration */
+ memcpy ( nic->mac_address, netdev->ll_addr,
+ sizeof ( nic->mac_address ) );
+ nic->pci_bus_dev_func = netdev->dev->desc.location;
+
+ return 0;
+}
+
+/**
+ * Fill in Initiator portion of iBFT
+ *
+ * @v initiator Initiator portion of iBFT
+ * @v strings iBFT string block descriptor
+ * @ret rc Return status code
+ */
+static int ibft_fill_initiator ( struct ibft_initiator *initiator,
+ struct ibft_string_block *strings ) {
+ const char *initiator_iqn = iscsi_initiator_iqn();
+ int rc;
+
+ if ( ( rc = ibft_set_string ( strings, &initiator->initiator_name,
+ initiator_iqn ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Fill in Target portion of iBFT
+ *
+ * @v target Target portion of iBFT
+ * @v strings iBFT string block descriptor
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ */
+static int ibft_fill_target ( struct ibft_target *target,
+ struct ibft_string_block *strings,
+ struct iscsi_session *iscsi ) {
+ struct sockaddr_in *sin_target =
+ ( struct sockaddr_in * ) &iscsi->target_sockaddr;
+ int rc;
+
+ /* Fill in Target values */
+ ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr );
+ target->socket = ntohs ( sin_target->sin_port );
+ if ( ( rc = ibft_set_string ( strings, &target->target_name,
+ iscsi->target_iqn ) ) != 0 )
+ return rc;
+ if ( iscsi->username ) {
+ if ( ( rc = ibft_set_string ( strings, &target->chap_name,
+ iscsi->username ) ) != 0 )
+ return rc;
+ }
+ if ( iscsi->password ) {
+ if ( ( rc = ibft_set_string ( strings, &target->chap_secret,
+ iscsi->password ) ) != 0 )
+ return rc;
+ target->chap_type = IBFT_CHAP_ONE_WAY;
+ }
+
+ return 0;
+}
+
+/**
+ * Fill in all variable portions of iBFT
+ *
+ * @v netdev Network device
+ * @v initiator_iqn Initiator IQN
+ * @v st_target Target socket address
+ * @v target_iqn Target IQN
+ * @ret rc Return status code
+ *
+ */
+int ibft_fill_data ( struct net_device *netdev,
+ struct iscsi_session *iscsi ) {
+ struct ibft_string_block strings = {
+ .table = &ibftab.table,
+ .offset = offsetof ( typeof ( ibftab ), strings ),
+ };
+ int rc;
+
+ /* Fill in NIC, Initiator and Target portions */
+ if ( ( rc = ibft_fill_nic ( &ibftab.nic, &strings, netdev ) ) != 0 )
+ return rc;
+ if ( ( rc = ibft_fill_initiator ( &ibftab.initiator,
+ &strings ) ) != 0 )
+ return rc;
+ if ( ( rc = ibft_fill_target ( &ibftab.target, &strings,
+ iscsi ) ) != 0 )
+ return rc;
+
+ /* Update checksum */
+ acpi_fix_checksum ( &ibftab.table.acpi );
+
+ return 0;
+}
diff --git a/gpxe/src/core/image.c b/gpxe/src/core/image.c
new file mode 100644
index 00000000..440a68c9
--- /dev/null
+++ b/gpxe/src/core/image.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <libgen.h>
+#include <gpxe/list.h>
+#include <gpxe/umalloc.h>
+#include <gpxe/uri.h>
+#include <gpxe/image.h>
+
+/** @file
+ *
+ * Executable/loadable images
+ *
+ */
+
+/** List of registered images */
+struct list_head images = LIST_HEAD_INIT ( images );
+
+/** List of image types */
+static struct image_type image_types[0]
+ __table_start ( struct image_type, image_types );
+static struct image_type image_types_end[0]
+ __table_end ( struct image_type, image_types );
+
+/**
+ * Free executable/loadable image
+ *
+ * @v refcnt Reference counter
+ */
+static void free_image ( struct refcnt *refcnt ) {
+ struct image *image = container_of ( refcnt, struct image, refcnt );
+
+ uri_put ( image->uri );
+ ufree ( image->data );
+ free ( image );
+ DBGC ( image, "IMAGE %p freed\n", image );
+}
+
+/**
+ * Allocate executable/loadable image
+ *
+ * @ret image Executable/loadable image
+ */
+struct image * alloc_image ( void ) {
+ struct image *image;
+
+ image = zalloc ( sizeof ( *image ) );
+ if ( image ) {
+ image->refcnt.free = free_image;
+ }
+ return image;
+}
+
+/**
+ * Set image URI
+ *
+ * @v image Image
+ * @v URI New image URI
+ * @ret rc Return status code
+ *
+ * If no name is set, the name will be updated to the base name of the
+ * URI path (if any).
+ */
+int image_set_uri ( struct image *image, struct uri *uri ) {
+ const char *path = uri->path;
+
+ /* Replace URI reference */
+ uri_put ( image->uri );
+ image->uri = uri_get ( uri );
+
+ /* Set name if none already specified */
+ if ( path && ( ! image->name[0] ) )
+ image_set_name ( image, basename ( ( char * ) path ) );
+
+ return 0;
+}
+
+/**
+ * Set image command line
+ *
+ * @v image Image
+ * @v cmdline New image command line
+ * @ret rc Return status code
+ */
+int image_set_cmdline ( struct image *image, const char *cmdline ) {
+ free ( image->cmdline );
+ image->cmdline = strdup ( cmdline );
+ if ( ! image->cmdline )
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * Register executable/loadable image
+ *
+ * @v image Executable/loadable image
+ * @ret rc Return status code
+ */
+int register_image ( struct image *image ) {
+ static unsigned int imgindex = 0;
+
+ /* Create image name if it doesn't already have one */
+ if ( ! image->name[0] ) {
+ snprintf ( image->name, sizeof ( image->name ), "img%d",
+ imgindex++ );
+ }
+
+ /* Add to image list */
+ image_get ( image );
+ list_add_tail ( &image->list, &images );
+ DBGC ( image, "IMAGE %p at [%lx,%lx) registered as %s\n",
+ image, user_to_phys ( image->data, 0 ),
+ user_to_phys ( image->data, image->len ), image->name );
+
+ return 0;
+}
+
+/**
+ * Unregister executable/loadable image
+ *
+ * @v image Executable/loadable image
+ */
+void unregister_image ( struct image *image ) {
+ list_del ( &image->list );
+ image_put ( image );
+ DBGC ( image, "IMAGE %p unregistered\n", image );
+}
+
+/**
+ * Find image by name
+ *
+ * @v name Image name
+ * @ret image Executable/loadable image, or NULL
+ */
+struct image * find_image ( const char *name ) {
+ struct image *image;
+
+ list_for_each_entry ( image, &images, list ) {
+ if ( strcmp ( image->name, name ) == 0 )
+ return image;
+ }
+
+ return NULL;
+}
+
+/**
+ * Load executable/loadable image into memory
+ *
+ * @v image Executable/loadable image
+ * @v type Executable/loadable image type
+ * @ret rc Return status code
+ */
+static int image_load_type ( struct image *image, struct image_type *type ) {
+ int rc;
+
+ /* Check image is actually loadable */
+ if ( ! type->load )
+ return -ENOEXEC;
+
+ /* Try the image loader */
+ if ( ( rc = type->load ( image ) ) != 0 ) {
+ DBGC ( image, "IMAGE %p could not load as %s: %s\n",
+ image, type->name, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Flag as loaded */
+ image->flags |= IMAGE_LOADED;
+ return 0;
+}
+
+/**
+ * Load executable/loadable image into memory
+ *
+ * @v image Executable/loadable image
+ * @ret rc Return status code
+ */
+int image_load ( struct image *image ) {
+
+ assert ( image->type != NULL );
+
+ return image_load_type ( image, image->type );
+}
+
+/**
+ * Autodetect image type and load executable/loadable image into memory
+ *
+ * @v image Executable/loadable image
+ * @ret rc Return status code
+ */
+int image_autoload ( struct image *image ) {
+ struct image_type *type;
+ int rc;
+
+ /* If image already has a type, use it */
+ if ( image->type )
+ return image_load ( image );
+
+ /* Otherwise probe for a suitable type */
+ for ( type = image_types ; type < image_types_end ; type++ ) {
+ DBGC ( image, "IMAGE %p trying type %s\n", image, type->name );
+ rc = image_load_type ( image, type );
+ if ( image->type == NULL )
+ continue;
+ return rc;
+ }
+
+ DBGC ( image, "IMAGE %p format not recognised\n", image );
+ return -ENOEXEC;
+}
+
+/**
+ * Execute loaded image
+ *
+ * @v image Loaded image
+ * @ret rc Return status code
+ */
+int image_exec ( struct image *image ) {
+ struct uri *old_cwuri;
+ int rc;
+
+ /* Image must be loaded first */
+ if ( ! ( image->flags & IMAGE_LOADED ) ) {
+ DBGC ( image, "IMAGE %p could not execute: not loaded\n",
+ image );
+ return -ENOTTY;
+ }
+
+ assert ( image->type != NULL );
+
+ /* Check that image is actually executable */
+ if ( ! image->type->exec )
+ return -ENOEXEC;
+
+ /* Switch current working directory to be that of the image itself */
+ old_cwuri = uri_get ( cwuri );
+ churi ( image->uri );
+
+ /* Try executing the image */
+ if ( ( rc = image->type->exec ( image ) ) != 0 ) {
+ DBGC ( image, "IMAGE %p could not execute: %s\n",
+ image, strerror ( rc ) );
+ goto done;
+ }
+
+ done:
+ /* Reset current working directory */
+ churi ( old_cwuri );
+ uri_put ( old_cwuri );
+
+ return rc;
+}
+
+/**
+ * Register and autoload an image
+ *
+ * @v image Image
+ * @ret rc Return status code
+ */
+int register_and_autoload_image ( struct image *image ) {
+ int rc;
+
+ if ( ( rc = register_image ( image ) ) != 0 )
+ return rc;
+
+ if ( ( rc = image_autoload ( image ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Register and autoexec an image
+ *
+ * @v image Image
+ * @ret rc Return status code
+ */
+int register_and_autoexec_image ( struct image *image ) {
+ int rc;
+
+ if ( ( rc = register_and_autoload_image ( image ) ) != 0 )
+ return rc;
+
+ if ( ( rc = image_exec ( image ) ) != 0 )
+ return rc;
+
+ return 0;
+}
diff --git a/gpxe/src/core/init.c b/gpxe/src/core/init.c
new file mode 100644
index 00000000..ed91bf36
--- /dev/null
+++ b/gpxe/src/core/init.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/device.h>
+#include <gpxe/init.h>
+
+/** @file
+ *
+ * Initialisation, startup and shutdown routines
+ *
+ */
+
+/** Registered initialisation functions */
+static struct init_fn init_fns[0]
+ __table_start ( struct init_fn, init_fns );
+static struct init_fn init_fns_end[0]
+ __table_end ( struct init_fn, init_fns );
+
+/** Registered startup/shutdown functions */
+static struct startup_fn startup_fns[0]
+ __table_start ( struct startup_fn, startup_fns );
+static struct startup_fn startup_fns_end[0]
+ __table_end ( struct startup_fn, startup_fns );
+
+/** "startup() has been called" flag */
+static int started = 0;
+
+/**
+ * Initialise gPXE
+ *
+ * This function performs the one-time-only and irreversible
+ * initialisation steps, such as initialising the heap. It must be
+ * called before (almost) any other function.
+ *
+ * There is, by definition, no counterpart to this function on the
+ * shutdown path.
+ */
+void initialise ( void ) {
+ struct init_fn *init_fn;
+
+ /* Call registered initialisation functions */
+ for ( init_fn = init_fns ; init_fn < init_fns_end ; init_fn++ ) {
+ init_fn->initialise ();
+ }
+}
+
+/**
+ * Start up gPXE
+ *
+ * This function performs the repeatable initialisation steps, such as
+ * probing devices. You may call startup() and shutdown() multiple
+ * times (as is done via the PXE API when PXENV_START_UNDI is used).
+ */
+void startup ( void ) {
+ struct startup_fn *startup_fn;
+
+ if ( started )
+ return;
+
+ /* Call registered startup functions */
+ for ( startup_fn = startup_fns ; startup_fn < startup_fns_end ;
+ startup_fn++ ) {
+ if ( startup_fn->startup )
+ startup_fn->startup();
+ }
+
+ /* Probe for all devices. Treated separately because nothing
+ * else will drag in device.o
+ */
+ probe_devices();
+
+ started = 1;
+}
+
+/**
+ * Shut down gPXE
+ *
+ * This function reverses the actions of startup(), and leaves gPXE in
+ * a state ready to be removed from memory. You may call startup()
+ * again after calling shutdown().
+
+ * Call this function only once, before either exiting main() or
+ * starting up a non-returnable image.
+ */
+void shutdown ( void ) {
+ struct startup_fn *startup_fn;
+
+ if ( ! started )
+ return;
+
+ /* Remove all devices */
+ remove_devices();
+
+ /* Call registered shutdown functions (in reverse order) */
+ for ( startup_fn = startup_fns_end - 1 ; startup_fn >= startup_fns ;
+ startup_fn-- ) {
+ if ( startup_fn->shutdown )
+ startup_fn->shutdown();
+ }
+
+ started = 0;
+}
diff --git a/gpxe/src/core/interface.c b/gpxe/src/core/interface.c
new file mode 100644
index 00000000..37aabfe0
--- /dev/null
+++ b/gpxe/src/core/interface.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/interface.h>
+
+/** @file
+ *
+ * Object communication interfaces
+ *
+ */
+
+/**
+ * Plug an interface into a new destination interface
+ *
+ * @v intf Interface
+ * @v dest New destination interface
+ *
+ * The reference to the existing destination interface is dropped, a
+ * reference to the new destination interface is obtained, and the
+ * interface is updated to point to the new destination interface.
+ *
+ * Note that there is no "unplug" call; instead you must plug the
+ * interface into a null interface.
+ */
+void plug ( struct interface *intf, struct interface *dest ) {
+ DBGC ( intf, "INTF %p moving from INTF %p to INTF %p\n",
+ intf, intf->dest, dest );
+ intf_put ( intf->dest );
+ intf->dest = intf_get ( dest );
+}
+
+/**
+ * Plug two interfaces together
+ *
+ * @v a Interface A
+ * @v b Interface B
+ *
+ * Plugs interface A into interface B, and interface B into interface
+ * A. (The basic plug() function is unidirectional; this function is
+ * merely a shorthand for two calls to plug(), hence the name.)
+ */
+void plug_plug ( struct interface *a, struct interface *b ) {
+ plug ( a, b );
+ plug ( b, a );
+}
diff --git a/gpxe/src/core/iobuf.c b/gpxe/src/core/iobuf.c
new file mode 100644
index 00000000..cc4aedea
--- /dev/null
+++ b/gpxe/src/core/iobuf.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <gpxe/malloc.h>
+#include <gpxe/iobuf.h>
+
+/** @file
+ *
+ * I/O buffers
+ *
+ */
+
+/**
+ * Allocate I/O buffer
+ *
+ * @v len Required length of buffer
+ * @ret iobuf I/O buffer, or NULL if none available
+ *
+ * The I/O buffer will be physically aligned to a multiple of
+ * @c IOBUF_SIZE.
+ */
+struct io_buffer * alloc_iob ( size_t len ) {
+ struct io_buffer *iobuf = NULL;
+ void *data;
+
+ /* Pad to minimum length */
+ if ( len < IOB_ZLEN )
+ len = IOB_ZLEN;
+
+ /* Align buffer length */
+ len = ( len + __alignof__( *iobuf ) - 1 ) &
+ ~( __alignof__( *iobuf ) - 1 );
+
+ /* Allocate memory for buffer plus descriptor */
+ data = malloc_dma ( len + sizeof ( *iobuf ), IOB_ALIGN );
+ if ( ! data )
+ return NULL;
+
+ iobuf = ( struct io_buffer * ) ( data + len );
+ iobuf->head = iobuf->data = iobuf->tail = data;
+ iobuf->end = iobuf;
+ return iobuf;
+}
+
+/**
+ * Free I/O buffer
+ *
+ * @v iobuf I/O buffer
+ */
+void free_iob ( struct io_buffer *iobuf ) {
+ if ( iobuf ) {
+ assert ( iobuf->head <= iobuf->data );
+ assert ( iobuf->data <= iobuf->tail );
+ assert ( iobuf->tail <= iobuf->end );
+ free_dma ( iobuf->head,
+ ( iobuf->end - iobuf->head ) + sizeof ( *iobuf ) );
+ }
+}
+
+/**
+ * Ensure I/O buffer has sufficient headroom
+ *
+ * @v iobuf I/O buffer
+ * @v len Required headroom
+ *
+ * This function currently only checks for the required headroom; it
+ * does not reallocate the I/O buffer if required. If we ever have a
+ * code path that requires this functionality, it's a fairly trivial
+ * change to make.
+ */
+int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len ) {
+
+ if ( iob_headroom ( iobuf ) >= len )
+ return 0;
+ return -ENOBUFS;
+}
+
diff --git a/gpxe/src/core/job.c b/gpxe/src/core/job.c
new file mode 100644
index 00000000..6c2faf30
--- /dev/null
+++ b/gpxe/src/core/job.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <gpxe/job.h>
+
+/** @file
+ *
+ * Job control interfaces
+ *
+ */
+
+void job_done ( struct job_interface *job, int rc ) {
+ struct job_interface *dest = job_get_dest ( job );
+
+ job_unplug ( job );
+ dest->op->done ( dest, rc );
+ job_put ( dest );
+}
+
+void job_kill ( struct job_interface *job ) {
+ struct job_interface *dest = job_get_dest ( job );
+
+ job_unplug ( job );
+ dest->op->kill ( dest );
+ job_put ( dest );
+}
+
+/****************************************************************************
+ *
+ * Helper methods
+ *
+ * These functions are designed to be used as methods in the
+ * job_interface_operations table.
+ *
+ */
+
+void ignore_job_done ( struct job_interface *job __unused, int rc __unused ) {
+ /* Nothing to do */
+}
+
+void ignore_job_kill ( struct job_interface *job __unused ) {
+ /* Nothing to do */
+}
+
+void ignore_job_progress ( struct job_interface *job __unused,
+ struct job_progress *progress ) {
+ memset ( progress, 0, sizeof ( *progress ) );
+}
+
+/** Null job control interface operations */
+struct job_interface_operations null_job_ops = {
+ .done = ignore_job_done,
+ .kill = ignore_job_kill,
+ .progress = ignore_job_progress,
+};
+
+/**
+ * Null job control interface
+ *
+ * This is the interface to which job control interfaces are connected
+ * when unplugged. It will never generate messages, and will silently
+ * absorb all received messages.
+ */
+struct job_interface null_job = {
+ .intf = {
+ .dest = &null_job.intf,
+ .refcnt = NULL,
+ },
+ .op = &null_job_ops,
+};
diff --git a/gpxe/src/core/linebuf.c b/gpxe/src/core/linebuf.c
new file mode 100644
index 00000000..d02f37c3
--- /dev/null
+++ b/gpxe/src/core/linebuf.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Line buffering
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <gpxe/linebuf.h>
+
+/**
+ * Retrieve buffered-up line
+ *
+ * @v linebuf Line buffer
+ * @ret line Buffered line, or NULL if no line ready to read
+ */
+char * buffered_line ( struct line_buffer *linebuf ) {
+ return ( linebuf->ready ? linebuf->data : NULL );
+}
+
+/**
+ * Discard line buffer contents
+ *
+ * @v linebuf Line buffer
+ */
+void empty_line_buffer ( struct line_buffer *linebuf ) {
+ free ( linebuf->data );
+ linebuf->data = NULL;
+ linebuf->len = 0;
+ linebuf->ready = 0;
+}
+
+/**
+ * Buffer up received data by lines
+ *
+ * @v linebuf Line buffer
+ * @v data New data to add
+ * @v len Length of new data to add
+ * @ret len Consumed length, or negative error number
+ *
+ * After calling line_buffer(), use buffered_line() to determine
+ * whether or not a complete line is available. Carriage returns and
+ * newlines will have been stripped, and the line will be
+ * NUL-terminated. This buffered line is valid only until the next
+ * call to line_buffer() (or to empty_line_buffer()).
+ *
+ * Note that line buffers use dynamically allocated storage; you
+ * should call empty_line_buffer() before freeing a @c struct @c
+ * line_buffer.
+ */
+ssize_t line_buffer ( struct line_buffer *linebuf,
+ const char *data, size_t len ) {
+ const char *eol;
+ size_t consume;
+ size_t new_len;
+ char *new_data;
+
+ /* Free any completed line from previous iteration */
+ if ( linebuf->ready )
+ empty_line_buffer ( linebuf );
+
+ /* Search for line terminator */
+ if ( ( eol = memchr ( data, '\n', len ) ) ) {
+ consume = ( eol - data + 1 );
+ } else {
+ consume = len;
+ }
+
+ /* Reallocate data buffer and copy in new data */
+ new_len = ( linebuf->len + consume );
+ new_data = realloc ( linebuf->data, ( new_len + 1 ) );
+ if ( ! new_data )
+ return -ENOMEM;
+ memcpy ( ( new_data + linebuf->len ), data, consume );
+ new_data[new_len] = '\0';
+ linebuf->data = new_data;
+ linebuf->len = new_len;
+
+ /* If we have reached end of line, trim the line and mark as ready */
+ if ( eol ) {
+ linebuf->data[--linebuf->len] = '\0'; /* trim NL */
+ if ( linebuf->data[linebuf->len - 1] == '\r' )
+ linebuf->data[--linebuf->len] = '\0'; /* trim CR */
+ linebuf->ready = 1;
+ }
+
+ return consume;
+}
diff --git a/gpxe/src/core/main.c b/gpxe/src/core/main.c
new file mode 100644
index 00000000..3295feaf
--- /dev/null
+++ b/gpxe/src/core/main.c
@@ -0,0 +1,40 @@
+/**************************************************************************
+gPXE - Network Bootstrap Program
+
+Literature dealing with the network protocols:
+ ARP - RFC826
+ RARP - RFC903
+ UDP - RFC768
+ BOOTP - RFC951, RFC2132 (vendor extensions)
+ DHCP - RFC2131, RFC2132 (options)
+ TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize)
+ RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper)
+ NFS - RFC1094, RFC1813 (v3, useful for clarifications, not implemented)
+ IGMP - RFC1112
+
+**************************************************************************/
+
+#include <gpxe/init.h>
+#include <gpxe/shell.h>
+#include <gpxe/shell_banner.h>
+#include <usr/autoboot.h>
+
+/**
+ * Main entry point
+ *
+ * @ret rc Return status code
+ */
+__cdecl int main ( void ) {
+
+ initialise();
+ startup();
+
+ if ( shell_banner() )
+ shell();
+ else
+ autoboot();
+
+ shutdown();
+
+ return 0;
+}
diff --git a/gpxe/src/core/malloc.c b/gpxe/src/core/malloc.c
new file mode 100644
index 00000000..2d892f42
--- /dev/null
+++ b/gpxe/src/core/malloc.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <io.h>
+#include <gpxe/list.h>
+#include <gpxe/init.h>
+#include <gpxe/malloc.h>
+
+/** @file
+ *
+ * Dynamic memory allocation
+ *
+ */
+
+/** A free block of memory */
+struct memory_block {
+ /** List of free blocks */
+ struct list_head list;
+ /** Size of this block */
+ size_t size;
+};
+
+#define MIN_MEMBLOCK_SIZE \
+ ( ( size_t ) ( 1 << ( fls ( sizeof ( struct memory_block ) - 1 ) ) ) )
+
+/** A block of allocated memory complete with size information */
+struct autosized_block {
+ /** Size of this block */
+ size_t size;
+ /** Remaining data */
+ char data[0];
+};
+
+/**
+ * Address for zero-length memory blocks
+ *
+ * @c malloc(0) or @c realloc(ptr,0) will return the special value @c
+ * NOWHERE. Calling @c free(NOWHERE) will have no effect.
+ *
+ * This is consistent with the ANSI C standards, which state that
+ * "either NULL or a pointer suitable to be passed to free()" must be
+ * returned in these cases. Using a special non-NULL value means that
+ * the caller can take a NULL return value to indicate failure,
+ * without first having to check for a requested size of zero.
+ *
+ * Code outside of malloc.c do not ever need to refer to the actual
+ * value of @c NOWHERE; this is an internal definition.
+ */
+#define NOWHERE ( ( void * ) ~( ( intptr_t ) 0 ) )
+
+/** List of free memory blocks */
+static LIST_HEAD ( free_blocks );
+
+/** Total amount of free memory */
+size_t freemem;
+
+/**
+ * Heap size
+ *
+ * Currently fixed at 128kB.
+ */
+#define HEAP_SIZE ( 128 * 1024 )
+
+/** The heap itself */
+static char heap[HEAP_SIZE] __attribute__ (( aligned ( __alignof__(void *) )));
+
+/**
+ * Allocate a memory block
+ *
+ * @v size Requested size
+ * @v align Physical alignment
+ * @ret ptr Memory block, or NULL
+ *
+ * Allocates a memory block @b physically aligned as requested. No
+ * guarantees are provided for the alignment of the virtual address.
+ *
+ * @c align must be a power of two. @c size may not be zero.
+ */
+void * alloc_memblock ( size_t size, size_t align ) {
+ struct memory_block *block;
+ size_t align_mask;
+ size_t pre_size;
+ ssize_t post_size;
+ struct memory_block *pre;
+ struct memory_block *post;
+
+ /* Round up size to multiple of MIN_MEMBLOCK_SIZE and
+ * calculate alignment mask.
+ */
+ size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 );
+ align_mask = ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 );
+
+ DBG ( "Allocating %#zx (aligned %#zx)\n", size, align );
+
+ /* Search through blocks for the first one with enough space */
+ list_for_each_entry ( block, &free_blocks, list ) {
+ pre_size = ( - virt_to_phys ( block ) ) & align_mask;
+ post_size = block->size - pre_size - size;
+ if ( post_size >= 0 ) {
+ /* Split block into pre-block, block, and
+ * post-block. After this split, the "pre"
+ * block is the one currently linked into the
+ * free list.
+ */
+ pre = block;
+ block = ( ( ( void * ) pre ) + pre_size );
+ post = ( ( ( void * ) block ) + size );
+ DBG ( "[%p,%p) -> [%p,%p) + [%p,%p)\n", pre,
+ ( ( ( void * ) pre ) + pre->size ), pre, block,
+ post, ( ( ( void * ) pre ) + pre->size ) );
+ /* If there is a "post" block, add it in to
+ * the free list. Leak it if it is too small
+ * (which can happen only at the very end of
+ * the heap).
+ */
+ if ( ( size_t ) post_size >= MIN_MEMBLOCK_SIZE ) {
+ post->size = post_size;
+ list_add ( &post->list, &pre->list );
+ }
+ /* Shrink "pre" block, leaving the main block
+ * isolated and no longer part of the free
+ * list.
+ */
+ pre->size = pre_size;
+ /* If there is no "pre" block, remove it from
+ * the list. Also remove it (i.e. leak it) if
+ * it is too small, which can happen only at
+ * the very start of the heap.
+ */
+ if ( pre_size < MIN_MEMBLOCK_SIZE )
+ list_del ( &pre->list );
+ /* Update total free memory */
+ freemem -= size;
+ /* Return allocated block */
+ DBG ( "Allocated [%p,%p)\n", block,
+ ( ( ( void * ) block ) + size ) );
+ return block;
+ }
+ }
+
+ DBG ( "Failed to allocate %#zx (aligned %#zx)\n", size, align );
+ return NULL;
+}
+
+/**
+ * Free a memory block
+ *
+ * @v ptr Memory allocated by alloc_memblock(), or NULL
+ * @v size Size of the memory
+ *
+ * If @c ptr is NULL, no action is taken.
+ */
+void free_memblock ( void *ptr, size_t size ) {
+ struct memory_block *freeing;
+ struct memory_block *block;
+ ssize_t gap_before;
+ ssize_t gap_after = -1;
+
+ /* Allow for ptr==NULL */
+ if ( ! ptr )
+ return;
+
+ /* Round up size to match actual size that alloc_memblock()
+ * would have used.
+ */
+ size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 );
+ freeing = ptr;
+ freeing->size = size;
+ DBG ( "Freeing [%p,%p)\n", freeing, ( ( ( void * ) freeing ) + size ));
+
+ /* Insert/merge into free list */
+ list_for_each_entry ( block, &free_blocks, list ) {
+ /* Calculate gaps before and after the "freeing" block */
+ gap_before = ( ( ( void * ) freeing ) -
+ ( ( ( void * ) block ) + block->size ) );
+ gap_after = ( ( ( void * ) block ) -
+ ( ( ( void * ) freeing ) + freeing->size ) );
+ /* Merge with immediately preceding block, if possible */
+ if ( gap_before == 0 ) {
+ DBG ( "[%p,%p) + [%p,%p) -> [%p,%p)\n", block,
+ ( ( ( void * ) block ) + block->size ), freeing,
+ ( ( ( void * ) freeing ) + freeing->size ),block,
+ ( ( ( void * ) freeing ) + freeing->size ) );
+ block->size += size;
+ list_del ( &block->list );
+ freeing = block;
+ }
+ /* Stop processing as soon as we reach a following block */
+ if ( gap_after >= 0 )
+ break;
+ }
+
+ /* Insert before the immediately following block. If
+ * possible, merge the following block into the "freeing"
+ * block.
+ */
+ DBG ( "[%p,%p)\n", freeing, ( ( ( void * ) freeing ) + freeing->size));
+ list_add_tail ( &freeing->list, &block->list );
+ if ( gap_after == 0 ) {
+ DBG ( "[%p,%p) + [%p,%p) -> [%p,%p)\n", freeing,
+ ( ( ( void * ) freeing ) + freeing->size ), block,
+ ( ( ( void * ) block ) + block->size ), freeing,
+ ( ( ( void * ) block ) + block->size ) );
+ freeing->size += block->size;
+ list_del ( &block->list );
+ }
+
+ /* Update free memory counter */
+ freemem += size;
+}
+
+/**
+ * Reallocate memory
+ *
+ * @v old_ptr Memory previously allocated by malloc(), or NULL
+ * @v new_size Requested size
+ * @ret new_ptr Allocated memory, or NULL
+ *
+ * Allocates memory with no particular alignment requirement. @c
+ * new_ptr will be aligned to at least a multiple of sizeof(void*).
+ * If @c old_ptr is non-NULL, then the contents of the newly allocated
+ * memory will be the same as the contents of the previously allocated
+ * memory, up to the minimum of the old and new sizes. The old memory
+ * will be freed.
+ *
+ * If allocation fails the previously allocated block is left
+ * untouched and NULL is returned.
+ *
+ * Calling realloc() with a new size of zero is a valid way to free a
+ * memory block.
+ */
+void * realloc ( void *old_ptr, size_t new_size ) {
+ struct autosized_block *old_block;
+ struct autosized_block *new_block;
+ size_t old_total_size;
+ size_t new_total_size;
+ size_t old_size;
+ void *new_ptr = NOWHERE;
+
+ /* Allocate new memory if necessary. If allocation fails,
+ * return without touching the old block.
+ */
+ if ( new_size ) {
+ new_total_size = ( new_size +
+ offsetof ( struct autosized_block, data ) );
+ new_block = alloc_memblock ( new_total_size, 1 );
+ if ( ! new_block )
+ return NULL;
+ new_block->size = new_total_size;
+ new_ptr = &new_block->data;
+ }
+
+ /* Copy across relevant part of the old data region (if any),
+ * then free it. Note that at this point either (a) new_ptr
+ * is valid, or (b) new_size is 0; either way, the memcpy() is
+ * valid.
+ */
+ if ( old_ptr && ( old_ptr != NOWHERE ) ) {
+ old_block = container_of ( old_ptr, struct autosized_block,
+ data );
+ old_total_size = old_block->size;
+ old_size = ( old_total_size -
+ offsetof ( struct autosized_block, data ) );
+ memcpy ( new_ptr, old_ptr,
+ ( ( old_size < new_size ) ? old_size : new_size ) );
+ free_memblock ( old_block, old_total_size );
+ }
+
+ return new_ptr;
+}
+
+/**
+ * Allocate memory
+ *
+ * @v size Requested size
+ * @ret ptr Memory, or NULL
+ *
+ * Allocates memory with no particular alignment requirement. @c ptr
+ * will be aligned to at least a multiple of sizeof(void*).
+ */
+void * malloc ( size_t size ) {
+ return realloc ( NULL, size );
+}
+
+/**
+ * Free memory
+ *
+ * @v ptr Memory allocated by malloc(), or NULL
+ *
+ * Memory allocated with malloc_dma() cannot be freed with free(); it
+ * must be freed with free_dma() instead.
+ *
+ * If @c ptr is NULL, no action is taken.
+ */
+void free ( void *ptr ) {
+ realloc ( ptr, 0 );
+}
+
+/**
+ * Allocate cleared memory
+ *
+ * @v size Requested size
+ * @ret ptr Allocated memory
+ *
+ * Allocate memory as per malloc(), and zero it.
+ *
+ * This function name is non-standard, but pretty intuitive.
+ * zalloc(size) is always equivalent to calloc(1,size)
+ */
+void * zalloc ( size_t size ) {
+ void *data;
+
+ data = malloc ( size );
+ if ( data )
+ memset ( data, 0, size );
+ return data;
+}
+
+/**
+ * Add memory to allocation pool
+ *
+ * @v start Start address
+ * @v end End address
+ *
+ * Adds a block of memory [start,end) to the allocation pool. This is
+ * a one-way operation; there is no way to reclaim this memory.
+ *
+ * @c start must be aligned to at least a multiple of sizeof(void*).
+ */
+void mpopulate ( void *start, size_t len ) {
+ /* Prevent free_memblock() from rounding up len beyond the end
+ * of what we were actually given...
+ */
+ free_memblock ( start, ( len & ~( MIN_MEMBLOCK_SIZE - 1 ) ) );
+}
+
+/**
+ * Initialise the heap
+ *
+ */
+static void init_heap ( void ) {
+ mpopulate ( heap, sizeof ( heap ) );
+}
+
+/** Memory allocator initialisation function */
+struct init_fn heap_init_fn __init_fn ( INIT_EARLY ) = {
+ .initialise = init_heap,
+};
+
+#if 0
+#include <stdio.h>
+/**
+ * Dump free block list
+ *
+ */
+void mdumpfree ( void ) {
+ struct memory_block *block;
+
+ printf ( "Free block list:\n" );
+ list_for_each_entry ( block, &free_blocks, list ) {
+ printf ( "[%p,%p] (size %#zx)\n", block,
+ ( ( ( void * ) block ) + block->size ), block->size );
+ }
+}
+#endif
diff --git a/gpxe/src/core/misc.c b/gpxe/src/core/misc.c
new file mode 100644
index 00000000..1f51272d
--- /dev/null
+++ b/gpxe/src/core/misc.c
@@ -0,0 +1,91 @@
+/**************************************************************************
+MISC Support Routines
+**************************************************************************/
+
+#include <stdlib.h>
+#include <byteswap.h>
+#include <gpxe/in.h>
+#include <gpxe/timer.h>
+
+/**************************************************************************
+INET_ATON - Convert an ascii x.x.x.x to binary form
+**************************************************************************/
+int inet_aton ( const char *cp, struct in_addr *inp ) {
+ const char *p = cp;
+ const char *digits_start;
+ unsigned long ip = 0;
+ unsigned long val;
+ int j;
+ for(j = 0; j <= 3; j++) {
+ digits_start = p;
+ val = strtoul(p, ( char ** ) &p, 10);
+ if ((p == digits_start) || (val > 255)) return 0;
+ if ( ( j < 3 ) && ( *(p++) != '.' ) ) return 0;
+ ip = (ip << 8) | val;
+ }
+ if ( *p == '\0' ) {
+ inp->s_addr = htonl(ip);
+ return 1;
+ }
+ return 0;
+}
+
+int isspace ( int c ) {
+ switch ( c ) {
+ case ' ':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\v':
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+unsigned long strtoul ( const char *p, char **endp, int base ) {
+ unsigned long ret = 0;
+ unsigned int charval;
+
+ while ( isspace ( *p ) )
+ p++;
+
+ if ( base == 0 ) {
+ base = 10;
+ if ( *p == '0' ) {
+ p++;
+ base = 8;
+ if ( ( *p | 0x20 ) == 'x' ) {
+ p++;
+ base = 16;
+ }
+ }
+ }
+
+ while ( 1 ) {
+ charval = *p;
+ if ( charval >= 'a' ) {
+ charval = ( charval - 'a' + 10 );
+ } else if ( charval >= 'A' ) {
+ charval = ( charval - 'A' + 10 );
+ } else if ( charval <= '9' ) {
+ charval = ( charval - '0' );
+ }
+ if ( charval >= ( unsigned int ) base )
+ break;
+ ret = ( ( ret * base ) + charval );
+ p++;
+ }
+
+ if ( endp )
+ *endp = ( char * ) p;
+
+ return ( ret );
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/gpxe/src/core/monojob.c b/gpxe/src/core/monojob.c
new file mode 100644
index 00000000..ea9bc834
--- /dev/null
+++ b/gpxe/src/core/monojob.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <gpxe/process.h>
+#include <console.h>
+#include <gpxe/keys.h>
+#include <gpxe/job.h>
+#include <gpxe/monojob.h>
+
+/** @file
+ *
+ * Single foreground job
+ *
+ */
+
+static int monojob_rc;
+
+static void monojob_done ( struct job_interface *job __unused, int rc ) {
+ monojob_rc = rc;
+}
+
+/** Single foreground job operations */
+static struct job_interface_operations monojob_operations = {
+ .done = monojob_done,
+ .kill = ignore_job_kill,
+ .progress = ignore_job_progress,
+};
+
+/** Single foreground job */
+struct job_interface monojob = {
+ .intf = {
+ .dest = &null_job.intf,
+ .refcnt = NULL,
+ },
+ .op = &monojob_operations,
+};
+
+/**
+ * Wait for single foreground job to complete
+ *
+ * @v string Job description to display
+ * @ret rc Job final status code
+ */
+int monojob_wait ( const char *string ) {
+ int key;
+ int rc;
+
+ printf ( "%s... ", string );
+ monojob_rc = -EINPROGRESS;
+ while ( monojob_rc == -EINPROGRESS ) {
+ step();
+ if ( iskey() ) {
+ key = getchar();
+ switch ( key ) {
+ case CTRL_C:
+ job_kill ( &monojob );
+ rc = -ECANCELED;
+ goto done;
+ default:
+ break;
+ }
+ }
+ }
+ rc = monojob_rc;
+
+done:
+ if ( rc ) {
+ printf ( "%s\n", strerror ( rc ) );
+ } else {
+ printf ( "ok\n" );
+ }
+ return rc;
+}
diff --git a/gpxe/src/core/nvo.c b/gpxe/src/core/nvo.c
new file mode 100644
index 00000000..13078022
--- /dev/null
+++ b/gpxe/src/core/nvo.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/nvs.h>
+#include <gpxe/nvo.h>
+
+/** @file
+ *
+ * Non-volatile stored options
+ *
+ */
+
+/**
+ * Calculate checksum over non-volatile stored options
+ *
+ * @v nvo Non-volatile options block
+ * @ret sum Checksum
+ */
+static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
+ uint8_t *data = nvo->data;
+ uint8_t sum = 0;
+ unsigned int i;
+
+ for ( i = 0 ; i < nvo->total_len ; i++ ) {
+ sum += *(data++);
+ }
+ return sum;
+}
+
+/**
+ * Load non-volatile stored options from non-volatile storage device
+ *
+ * @v nvo Non-volatile options block
+ * @ret rc Return status code
+ */
+static int nvo_load ( struct nvo_block *nvo ) {
+ void *data = nvo->data;
+ struct nvo_fragment *frag;
+ int rc;
+
+ /* Read data a fragment at a time */
+ for ( frag = nvo->fragments ; frag->len ; frag++ ) {
+ if ( ( rc = nvs_read ( nvo->nvs, frag->address, data,
+ frag->len ) ) != 0 ) {
+ DBGC ( nvo, "NVO %p could not read %zd bytes at "
+ "%#04x\n", nvo, frag->len, frag->address );
+ return rc;
+ }
+ data += frag->len;
+ }
+
+ DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo );
+ return 0;
+}
+
+/**
+ * Save non-volatile stored options back to non-volatile storage device
+ *
+ * @v nvo Non-volatile options block
+ * @ret rc Return status code
+ */
+static int nvo_save ( struct nvo_block *nvo ) {
+ void *data = nvo->data;
+ uint8_t *checksum = data;
+ struct nvo_fragment *frag;
+ int rc;
+
+ /* Recalculate checksum */
+ *checksum -= nvo_checksum ( nvo );
+
+ /* Write data a fragment at a time */
+ for ( frag = nvo->fragments ; frag->len ; frag++ ) {
+ if ( ( rc = nvs_write ( nvo->nvs, frag->address, data,
+ frag->len ) ) != 0 ) {
+ DBGC ( nvo, "NVO %p could not write %zd bytes at "
+ "%#04x\n", nvo, frag->len, frag->address );
+ return rc;
+ }
+ data += frag->len;
+ }
+
+ DBGC ( nvo, "NVO %p saved to non-volatile storage\n", nvo );
+ return 0;
+}
+
+/**
+ * Parse stored options
+ *
+ * @v nvo Non-volatile options block
+ *
+ * Verifies that the options data is valid, and configures the DHCP
+ * options block. If the data is not valid, it is replaced with an
+ * empty options block.
+ */
+static void nvo_init_dhcpopts ( struct nvo_block *nvo ) {
+ uint8_t *options_data;
+ size_t options_len;
+
+ /* Steal one byte for the checksum */
+ options_data = ( nvo->data + 1 );
+ options_len = ( nvo->total_len - 1 );
+
+ /* If checksum fails, or options data starts with a zero,
+ * assume the whole block is invalid. This should capture the
+ * case of random initial contents.
+ */
+ if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) {
+ DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; "
+ "assuming empty\n", nvo, nvo_checksum ( nvo ),
+ options_data[0] );
+ memset ( nvo->data, 0, nvo->total_len );
+ }
+
+ dhcpopt_init ( &nvo->dhcpopts, options_data, options_len );
+}
+
+/**
+ * Store value of NVO setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+static int nvo_store ( struct settings *settings, struct setting *setting,
+ const void *data, size_t len ) {
+ struct nvo_block *nvo =
+ container_of ( settings, struct nvo_block, settings );
+ int rc;
+
+ /* Update stored options */
+ if ( ( rc = dhcpopt_store ( &nvo->dhcpopts, setting->tag,
+ data, len ) ) != 0 ) {
+ DBGC ( nvo, "NVO %p could not store %zd bytes: %s\n",
+ nvo, len, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Save updated options to NVS */
+ if ( ( rc = nvo_save ( nvo ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Fetch value of NVO setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ *
+ * The actual length of the setting will be returned even if
+ * the buffer was too small.
+ */
+static int nvo_fetch ( struct settings *settings, struct setting *setting,
+ void *data, size_t len ) {
+ struct nvo_block *nvo =
+ container_of ( settings, struct nvo_block, settings );
+
+ return dhcpopt_fetch ( &nvo->dhcpopts, setting->tag, data, len );
+}
+
+/** NVO settings operations */
+static struct settings_operations nvo_settings_operations = {
+ .store = nvo_store,
+ .fetch = nvo_fetch,
+};
+
+/**
+ * Initialise non-volatile stored options
+ *
+ * @v nvo Non-volatile options block
+ * @v nvs Underlying non-volatile storage device
+ * @v fragments List of option-containing fragments
+ * @v refcnt Containing object reference counter, or NULL
+ */
+void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs,
+ struct nvo_fragment *fragments, struct refcnt *refcnt ) {
+ nvo->nvs = nvs;
+ nvo->fragments = fragments;
+ settings_init ( &nvo->settings, &nvo_settings_operations, refcnt,
+ "nvo" );
+}
+
+/**
+ * Register non-volatile stored options
+ *
+ * @v nvo Non-volatile options block
+ * @v parent Parent settings block, or NULL
+ * @ret rc Return status code
+ */
+int register_nvo ( struct nvo_block *nvo, struct settings *parent ) {
+ struct nvo_fragment *fragment = nvo->fragments;
+ int rc;
+
+ /* Calculate total length of all fragments */
+ for ( fragment = nvo->fragments ; fragment->len ; fragment++ )
+ nvo->total_len += fragment->len;
+
+ /* Allocate memory for options and read in from NVS */
+ nvo->data = malloc ( nvo->total_len );
+ if ( ! nvo->data ) {
+ DBGC ( nvo, "NVO %p could not allocate %zd bytes\n",
+ nvo, nvo->total_len );
+ rc = -ENOMEM;
+ goto err_malloc;
+ }
+ if ( ( rc = nvo_load ( nvo ) ) != 0 )
+ goto err_load;
+
+ /* Verify and register options */
+ nvo_init_dhcpopts ( nvo );
+ if ( ( rc = register_settings ( &nvo->settings, parent ) ) != 0 )
+ goto err_register;
+
+ DBGC ( nvo, "NVO %p registered\n", nvo );
+ return 0;
+
+ err_register:
+ err_load:
+ free ( nvo->data );
+ nvo->data = NULL;
+ err_malloc:
+ return rc;
+}
+
+/**
+ * Unregister non-volatile stored options
+ *
+ * @v nvo Non-volatile options block
+ */
+void unregister_nvo ( struct nvo_block *nvo ) {
+ unregister_settings ( &nvo->settings );
+ free ( nvo->data );
+ nvo->data = NULL;
+ DBGC ( nvo, "NVO %p unregistered\n", nvo );
+}
diff --git a/gpxe/src/core/open.c b/gpxe/src/core/open.c
new file mode 100644
index 00000000..db8d92e6
--- /dev/null
+++ b/gpxe/src/core/open.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/xfer.h>
+#include <gpxe/uri.h>
+#include <gpxe/socket.h>
+#include <gpxe/open.h>
+
+/** @file
+ *
+ * Data transfer interface opening
+ *
+ */
+
+/** Registered URI openers */
+static struct uri_opener uri_openers[0]
+ __table_start ( struct uri_opener, uri_openers );
+static struct uri_opener uri_openers_end[0]
+ __table_end ( struct uri_opener, uri_openers );
+
+/** Registered socket openers */
+static struct socket_opener socket_openers[0]
+ __table_start ( struct socket_opener, socket_openers );
+static struct socket_opener socket_openers_end[0]
+ __table_end ( struct socket_opener, socket_openers );
+
+/**
+ * Open URI
+ *
+ * @v xfer Data transfer interface
+ * @v uri URI
+ * @ret rc Return status code
+ *
+ * The URI will be regarded as being relative to the current working
+ * URI (see churi()).
+ */
+int xfer_open_uri ( struct xfer_interface *xfer, struct uri *uri ) {
+ struct uri_opener *opener;
+ struct uri *resolved_uri;
+ int rc = -ENOTSUP;
+
+ /* Resolve URI */
+ resolved_uri = resolve_uri ( cwuri, uri );
+ if ( ! resolved_uri )
+ return -ENOMEM;
+
+ /* Find opener which supports this URI scheme */
+ for ( opener = uri_openers ; opener < uri_openers_end ; opener++ ) {
+ if ( strcmp ( resolved_uri->scheme, opener->scheme ) == 0 ) {
+ rc = opener->open ( xfer, resolved_uri );
+ goto done;
+ }
+ }
+ DBGC ( xfer, "XFER %p attempted to open unsupported URI scheme "
+ "\"%s\"\n", xfer, resolved_uri->scheme );
+
+ done:
+ uri_put ( resolved_uri );
+ return rc;
+}
+
+/**
+ * Open URI string
+ *
+ * @v xfer Data transfer interface
+ * @v uri_string URI string (e.g. "http://etherboot.org/kernel")
+ * @ret rc Return status code
+ *
+ * The URI will be regarded as being relative to the current working
+ * URI (see churi()).
+ */
+int xfer_open_uri_string ( struct xfer_interface *xfer,
+ const char *uri_string ) {
+ struct uri *uri;
+ int rc;
+
+ DBGC ( xfer, "XFER %p opening URI %s\n", xfer, uri_string );
+
+ uri = parse_uri ( uri_string );
+ if ( ! uri )
+ return -ENOMEM;
+
+ rc = xfer_open_uri ( xfer, uri );
+
+ uri_put ( uri );
+ return rc;
+}
+
+/**
+ * Open socket
+ *
+ * @v xfer Data transfer interface
+ * @v semantics Communication semantics (e.g. SOCK_STREAM)
+ * @v peer Peer socket address
+ * @v local Local socket address, or NULL
+ * @ret rc Return status code
+ */
+int xfer_open_socket ( struct xfer_interface *xfer, int semantics,
+ struct sockaddr *peer, struct sockaddr *local ) {
+ struct socket_opener *opener;
+
+ DBGC ( xfer, "XFER %p opening (%s,%s) socket\n", xfer,
+ socket_semantics_name ( semantics ),
+ socket_family_name ( peer->sa_family ) );
+
+ for ( opener = socket_openers; opener < socket_openers_end; opener++ ){
+ if ( ( opener->semantics == semantics ) &&
+ ( opener->family == peer->sa_family ) ) {
+ return opener->open ( xfer, peer, local );
+ }
+ }
+
+ DBGC ( xfer, "XFER %p attempted to open unsupported socket type "
+ "(%s,%s)\n", xfer, socket_semantics_name ( semantics ),
+ socket_family_name ( peer->sa_family ) );
+ return -ENOTSUP;
+}
+
+/**
+ * Open location
+ *
+ * @v xfer Data transfer interface
+ * @v type Location type
+ * @v args Remaining arguments depend upon location type
+ * @ret rc Return status code
+ */
+int xfer_vopen ( struct xfer_interface *xfer, int type, va_list args ) {
+ switch ( type ) {
+ case LOCATION_URI_STRING: {
+ const char *uri_string = va_arg ( args, const char * );
+
+ return xfer_open_uri_string ( xfer, uri_string ); }
+ case LOCATION_URI: {
+ struct uri *uri = va_arg ( args, struct uri * );
+
+ return xfer_open_uri ( xfer, uri ); }
+ case LOCATION_SOCKET: {
+ int semantics = va_arg ( args, int );
+ struct sockaddr *peer = va_arg ( args, struct sockaddr * );
+ struct sockaddr *local = va_arg ( args, struct sockaddr * );
+
+ return xfer_open_socket ( xfer, semantics, peer, local ); }
+ default:
+ DBGC ( xfer, "XFER %p attempted to open unsupported location "
+ "type %d\n", xfer, type );
+ return -ENOTSUP;
+ }
+}
+
+/**
+ * Open location
+ *
+ * @v xfer Data transfer interface
+ * @v type Location type
+ * @v ... Remaining arguments depend upon location type
+ * @ret rc Return status code
+ */
+int xfer_open ( struct xfer_interface *xfer, int type, ... ) {
+ va_list args;
+ int rc;
+
+ va_start ( args, type );
+ rc = xfer_vopen ( xfer, type, args );
+ va_end ( args );
+ return rc;
+}
diff --git a/gpxe/src/core/pc_kbd.c b/gpxe/src/core/pc_kbd.c
new file mode 100644
index 00000000..d43357fe
--- /dev/null
+++ b/gpxe/src/core/pc_kbd.c
@@ -0,0 +1,112 @@
+/* Minimal polling PC keyboard driver
+ * - No interrupt
+ * - No LED
+ * - No special keys
+ *
+ * still Enough For Me to type a filename.
+ *
+ * 2003-07 by SONE Takesh
+ * 2004-04 moved by LYH From filo to Etherboot
+ * yhlu@tyan.com
+ */
+
+#include "io.h"
+#include "console.h"
+
+static char key_map[][128] = {
+ {
+ "\0\x1b""1234567890-=\b\t"
+ "qwertyuiop[]\r\0as"
+ "dfghjkl;'`\0\\zxcv"
+ "bnm,./\0*\0 \0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0""789-456+1"
+ "230."
+ },{
+ "\0\x1b""!@#$%^&*()_+\b\t"
+ "QWERTYUIOP{}\r\0AS"
+ "DFGHJKL:\"~\0|ZXCV"
+ "BNM<>?\0\0\0 \0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0""789-456+1"
+ "230."
+ }
+};
+
+static int cur_scan;
+static unsigned int shift_state;
+#define SHIFT 1
+#define CONTROL 2
+#define CAPS 4
+
+static int get_scancode(void)
+{
+ int scan;
+
+ if ((inb(0x64) & 1) == 0)
+ return 0;
+ scan = inb(0x60);
+
+ switch (scan) {
+ case 0x2a:
+ case 0x36:
+ shift_state |= SHIFT;
+ break;
+ case 0xaa:
+ case 0xb6:
+ shift_state &= ~SHIFT;
+ break;
+ case 0x1d:
+ shift_state |= CONTROL;
+ break;
+ case 0x9d:
+ shift_state &= ~CONTROL;
+ break;
+ case 0x3a:
+ shift_state ^= CAPS;
+ break;
+ }
+
+ if (scan & 0x80)
+ return 0; /* ignore break code or 0xe0 etc! */
+ return scan;
+}
+
+static int kbd_havekey(void)
+{
+ if (!cur_scan)
+ cur_scan = get_scancode();
+ return cur_scan != 0;
+}
+
+static int kbd_ischar(void)
+{
+ if (!kbd_havekey())
+ return 0;
+ if (!key_map[shift_state & SHIFT][cur_scan]) {
+ cur_scan = 0;
+ return 0;
+ }
+ return 1;
+}
+
+static int kbd_getc(void)
+{
+ int c;
+
+ while (!kbd_ischar())
+ ;
+ c = key_map[shift_state & SHIFT][cur_scan];
+ if (shift_state & (CONTROL | CAPS)) {
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
+ if (shift_state & CONTROL)
+ c &= 0x1f;
+ else if (shift_state & CAPS)
+ c ^= ('A' ^ 'a');
+ }
+ }
+ cur_scan = 0;
+ return c;
+}
+
+struct console_driver pc_kbd_console __console_driver = {
+ .getchar = kbd_getc,
+};
diff --git a/gpxe/src/core/pcmcia.c b/gpxe/src/core/pcmcia.c
new file mode 100644
index 00000000..631971ef
--- /dev/null
+++ b/gpxe/src/core/pcmcia.c
@@ -0,0 +1,267 @@
+#if 0
+
+/*
+ * pcmcia.c
+ *
+ * PCMCIA support routines for etherboot - generic stuff
+ *
+ * This code has partly be taken from the linux kernel sources, .../drivers/pcmcia/
+ * Started & put together by
+ * Anselm Martin Hoffmeister
+ * Stockholm Projekt Computer-Service
+ * Sankt Augustin / Bonn, Germany
+ *
+ * Distributed under GPL2
+ */
+
+/*
+ *
+ *
+ * ******************************
+ * PLEASE DO NOT YET WORK ON THIS
+ * ******************************
+ *
+ * I'm still fixing it up on every end, so we most probably would interfere
+ * at some point. If there's anything obvious or better, not-so-obvious,
+ * please contact me by e-mail: anselm (AT) hoffmeister (DOT) be *THANKS*
+ */
+#include <stdio.h>
+#include <pcmcia.h>
+#include <i82365.h>
+#define CODE_STATUS "alpha"
+#define CODE_VERSION "0.1.3"
+#include <pcmcia-opts.h>
+#include <console.h>
+#include <gpxe/init.h>
+
+int sockets; /* AHTODO: Phase this out! */
+u_int pccsocks;
+struct pccsock_t pccsock[MAXPCCSOCKS];
+int inited = -1;
+struct pcc_config_t pccconfig[MAXPCCCONFIGS];
+
+struct driver_interact_t driver[] = {
+#ifdef SUPPORT_I82365
+ { I82365, i82365_interfacer, "Intel_82365" },
+#endif
+};
+
+#define NUM_DRIVERS (sizeof(driver)/(sizeof(struct driver_interact_t)))
+
+void sleepticks(int numticks ) {
+ u_int tmo;
+ for (tmo = currticks()+numticks; currticks() < tmo; ) {
+ }
+ return;
+}
+
+static void pcmcia_init_all(void) {
+ u_int i, j, k, l, m, n, ui, configs = 0;
+ u_int multicard[8];
+ u_char *uc, upc;
+ if ( PDEBUG > 0 ) printf("Initializing PCMCIA subsystem (code-status: " CODE_STATUS ", Version " CODE_VERSION ")\n");
+ if ( PDEBUG > 2 ) {
+ printf ( "Supporting %d driver(s): ", NUM_DRIVERS );
+ for ( i = 0; i < NUM_DRIVERS; ++i ) {
+ printf ( "[%s] ", driver[i].name );
+ }
+ printf ( "\n" );
+ }
+ pccsocks = 0;
+ sockets = 0;
+ // Init all drivers in the driver[] array:
+ for ( i = 0; i < NUM_DRIVERS; ++i ) {
+ driver[i].f(INIT,0,i,0,0); // init needs no params. It uses pccsocks and pccsock[].
+ // Only i tells it which driver_id itself is.
+ }
+ for ( i = 0; i < pccsocks; ++i ) {
+ printf ( "Socket %d: ", i );
+ if ( pccsock[i].status != HASCARD ) {
+ printf ( "is %s: skipping\n", pccsock[i].status == EMPTY? "empty":"[status unknown]" );
+ continue;
+ }
+ if ( 0 != driver[pccsock[i].drivernum].f(MAPATTRMEM,pccsock[i].internalid,MAP_ATTRMEM_TO, MAP_ATTRMEM_LEN,0 ) ) {
+ printf ("PCMCIA controller failed to map attribute memory.\n**** SEVERE ERROR CONDITION. Skipping controller.\n" );
+ if ( PDEBUG > 2 ) {
+ printf ( "<press key. THIS CONDITION SHOULD BE REPORTED!>\n" ); getchar();
+ }
+ continue;
+ }
+ // parse configuration information
+ uc = ioremap ( MAP_ATTRMEM_TO, MAP_ATTRMEM_LEN );
+ pccsock[i].stringoffset = pccsock[i].configoffset = pccsock[i].stringlength = 0;
+ pccsock[i].type = 0xff;
+ for ( l = 0; l < 8; ++l ) multicard[l] = 0;
+ sleepticks(2);
+ for ( l = ui = 0; ui < 0x800; ui += uc[(2*ui)+2] + 2 ) {
+ if ( uc[(2*ui)] == 0xff ) {
+ break;
+ }
+ // This loop is complete rubbish AFAICS.
+ // But without it, my test system won't come up.
+ // It's too bad to develop on broken hardware
+ // - Anselm
+ }
+ sleepticks(2);
+ configs = 0;
+ inited = -1;
+ for ( l = ui = 0; ui < 0x800; ui += uc[(2*ui)+2] + 2 ) {
+ if ( uc[(2*ui)] == 0xff ) break;
+ else if ( uc[2*ui] == 0x15 ) {
+ for ( k = 2 * ( ui + 2 ); ( uc[k] <= ' ' ) && ( k < ( 2 * ( uc[2*(ui+1)] + ui + 2 ) ) ) ; k += 2 ) { ; }
+ pccsock[i].stringoffset = k;
+ pccsock[i].stringlength = ( 2 * ( ui + 2 + uc[(2*ui)+2] ) - k ) / 2;
+ } else if ( uc[2*ui] == 0x21 ) {
+ pccsock[i].type = uc[(2*ui)+4];
+ } else if ( uc[2*ui] == 0x1a ) { // Configuration map
+ printf ( "\nConfig map 0x1a found [" );
+ for ( k = 0; k < uc[2*(ui+1)]; ++k ) {
+ printf ( "%02x ", uc[2*(ui+k+2)] );
+ }
+ printf ( "]\nHighest config available is %d\n", uc[2*(ui+3)] );
+ m = uc[2*(ui+2)];
+ pccsock[i].configoffset = 0;
+ for ( j = 0; j <= (m & 3); ++j ) {
+ pccsock[i].configoffset += uc[2*(ui+4+j)] << (8*j);
+ }
+ pccsock[i].rmask0 = 0;
+ for ( j = 0; j <= ( ( ( m & 0x3c ) >> 2 ) & 3 ); ++j ) {
+ pccsock[i].rmask0 += uc[2*(ui+5+(m&3)+j)] << (8*j);
+ }
+ j = pccsock[i].rmask0;
+ printf ( "Config offset is %x, card has regs: < %s%s%s%s%s>\n", pccsock[i].configoffset,
+ j & 1 ? "COR ":"", j & 2 ? "CCSR ":"", j & 4 ? "PRR ":"", j & 8 ? "SCR ":"", j & 16? "ESR ":"" );
+ printf ( "COR + CCSR contents (si/du) %x %x/%x %x\n", uc[pccsock[i].configoffset+0],
+ uc[pccsock[i].configoffset+2],uc[pccsock[i].configoffset*2],uc[(pccsock[i].configoffset*2)+2] );
+ printf ( " " );
+ } else if ( uc[2*ui] == 0x1b ) { // Configuration data entry
+ //printf ( "Config data 0x1b found [\n" );getchar();
+ for ( k = 0; k < uc[2*(ui+1)]; ++k ) {
+ // printf ( "%02x ", uc[2*(ui+k+2)] );
+ }
+ // Parse this tuple into pccconfig[configs]
+ // printf ( "]\n" );
+ if ( configs == MAXPCCCONFIGS ) continue;
+ k = 2*ui+4;
+ pccconfig[configs].index = uc[k] & 0x3f;
+ if ( uc[k] & 0x80 ) {
+ // printf ( "Special config, unsupp. for now\n" );
+ continue;
+ }
+ k+=2;
+ // printf ( "Features: %2x\n", uc[k] );
+ if ( uc[k] & 0x7 ) {
+ // printf ( "Cannot work with Vcc/Timing configs right now\n" );
+ continue;
+ }
+ pccconfig[configs].iowin = pccconfig[configs].iolen = 0;
+ if ( 0 != ( uc[k] & 0x8 ) ) {
+ k+=2;
+ // printf ( "Reading IO config: " );
+ if ( 0 == ( uc[k] & 0x80 ) ) {
+ // printf ( "Cannot work with auto/io config\n" );
+ continue;
+ }
+ k+=2;
+ if ( 0 != ( uc[k] & 0x0f ) ) {
+ // printf ( "Don't support more than 1 iowin right now\n" );
+ continue;
+ }
+ j = (uc[k] & 0x30) >> 4;
+ m = (uc[k] & 0xc0) >> 6;
+ if ( 3 == j ) ++j;
+ if ( 3 == m ) ++m;
+ k += 2;
+ pccconfig[configs].iowin = 0;
+ pccconfig[configs].iolen = 1;
+ for ( n = 0; n < j; ++n, k+=2 ) {
+ pccconfig[configs].iowin += uc[k] << (n*8);
+ }
+ for ( n = 0; n < m; ++n, k+=2 ) {
+ pccconfig[configs].iolen += uc[k] << (n*8);
+ }
+ // printf ( "io %x len %d (%d)\n", pccconfig[configs].iowin, pccconfig[configs].iolen,configs );
+ }
+ for ( j = 0; j < (uc[k] & 3); ++j ) {
+ // pccconfig[configs].iowin += (uc[k+(2*j)+2]) << (8*j);
+ }
+ ++configs;
+ }
+ }
+ if ( pccsock[i].stringoffset > 0 ) { // If no identifier, it's not a valid CIS (as of documentation...)
+ printf ( "[" );
+ for ( k = 0; ( k < pccsock[i].stringlength ) && ( k < 64 ); ++k ) {
+ j = uc[pccsock[i].stringoffset + 2 * k];
+ printf ( "%c", (j>=' '? j:' ' ) );
+ }
+ printf ("]\n is type %d (", pccsock[i].type );
+ switch ( pccsock[i].type ) {
+ case 0x00:
+ printf ( "MULTI" ); break;
+ case 0x01:
+ printf ( "Memory" ); break;
+ case 0x02:
+ printf ( "Serial" ); break;
+ case 0x03:
+ printf ( "Parallel" ); break;
+ case 0x04:
+ printf ( "Fixed" ); break;
+ case 0x05:
+ printf ( "Video" ); break;
+ case 0x06:
+ printf ( "Network" ); break;
+ case 0x07:
+ printf ( "AIMS" ); break;
+ case 0x08:
+ printf ( "SCSI" ); break;
+ case 0x106: // Special / homebrew to say "Multi/network"
+ printf ( "MULTI, with Network" ); break; // AHTODO find a card for this
+ default:
+ printf ( "UNSUPPORTED/UNKNOWN" );
+ }
+ printf ( ") with %d possible configuration(s)\n", configs );
+ // Now set dependency: If it's Network or multi->network, accept
+ if ( (inited <= 0 ) && (6 == (0xff & pccsock[i].type) ) && (0 < configs ) ) {
+ printf ( "activating this device with ioport %x-%x (config #%d)\n",
+ pccconfig[0].iowin, pccconfig[0].iowin+pccconfig[0].iolen-1, pccconfig[0].index );
+ inited = i;
+ // And unmap attrmem ourselves!
+ printf ( "Activating config..." );
+ if ( m=driver[pccsock[i].drivernum].f(SELECTCONFIG,pccsock[i].internalid,pccconfig[0].index,0,&pccconfig[0]) ) {
+ printf ("Failure(%d)!",m); inited = -1;
+ driver[pccsock[i].drivernum].f(UNMAPATTRMEM,pccsock[i].internalid,0,0,0);
+ }
+ printf ( "done!\n" );
+ continue;
+ }
+ } else {
+ printf ( "unsupported - no identifier string found in CIS\n" );
+ }
+ // unmap the PCMCIA device
+ if ( i != inited ) {
+ if ( 0 != driver[pccsock[i].drivernum].f(UNMAPATTRMEM,pccsock[i].internalid,0,0,0) ) {
+ printf ("PCMCIA controller failed to unmap attribute memory.\n**** SEVERE ERROR CONDITION ****\n" );
+ if ( PDEBUG > 2 ) {
+ printf ( "<press key. THIS CONDITION SHOULD BE REPORTED!>\n" ); getchar();
+ }
+ continue;
+ }
+ }
+ }
+ if ( PDEBUG > 2 ) {
+ printf ( "<press key to exit the pcmcia_init_all routine>\n" );
+ getchar();
+ }
+
+}
+
+static void pcmcia_shutdown_all(void) {
+ int i;
+ //if ( PDEBUG > 2 ) {printf("<press key to continue>\n" ); getchar(); }
+ for ( i = 0; i < pccsocks; ++i ) {
+ driver[pccsock[i].drivernum].f(SHUTDOWN,pccsock[i].internalid,0,0,0);
+ }
+ printf("Shutdown of PCMCIA subsystem completed");
+}
+
+#endif
diff --git a/gpxe/src/core/posix_io.c b/gpxe/src/core/posix_io.c
new file mode 100644
index 00000000..e0459bdf
--- /dev/null
+++ b/gpxe/src/core/posix_io.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/list.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/process.h>
+#include <gpxe/posix_io.h>
+
+/** @file
+ *
+ * POSIX-like I/O
+ *
+ * These functions provide traditional blocking I/O semantics. They
+ * are designed to be used by the PXE TFTP API. Because they block,
+ * they may not be used by most other portions of the gPXE codebase.
+ */
+
+/** An open file */
+struct posix_file {
+ /** Reference count for this object */
+ struct refcnt refcnt;
+ /** List of open files */
+ struct list_head list;
+ /** File descriptor */
+ int fd;
+ /** Overall status
+ *
+ * Set to -EINPROGRESS while data transfer is in progress.
+ */
+ int rc;
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+ /** Current seek position */
+ size_t pos;
+ /** File size */
+ size_t filesize;
+ /** Received data queue */
+ struct list_head data;
+};
+
+/** List of open files */
+static LIST_HEAD ( posix_files );
+
+/**
+ * Free open file
+ *
+ * @v refcnt Reference counter
+ */
+static void posix_file_free ( struct refcnt *refcnt ) {
+ struct posix_file *file =
+ container_of ( refcnt, struct posix_file, refcnt );
+ struct io_buffer *iobuf;
+ struct io_buffer *tmp;
+
+ list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) {
+ list_del ( &iobuf->list );
+ free_iob ( iobuf );
+ }
+ free ( file );
+}
+
+/**
+ * Terminate file data transfer
+ *
+ * @v file POSIX file
+ * @v rc Reason for termination
+ */
+static void posix_file_finished ( struct posix_file *file, int rc ) {
+ xfer_nullify ( &file->xfer );
+ xfer_close ( &file->xfer, rc );
+ file->rc = rc;
+}
+
+/**
+ * Handle close() event
+ *
+ * @v xfer POSIX file data transfer interface
+ * @v rc Reason for close
+ */
+static void posix_file_xfer_close ( struct xfer_interface *xfer, int rc ) {
+ struct posix_file *file =
+ container_of ( xfer, struct posix_file, xfer );
+
+ posix_file_finished ( file, rc );
+}
+
+/**
+ * Handle deliver_iob() event
+ *
+ * @v xfer POSIX file data transfer interface
+ * @v iobuf I/O buffer
+ * @v meta Data transfer metadata
+ * @ret rc Return status code
+ */
+static int
+posix_file_xfer_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct posix_file *file =
+ container_of ( xfer, struct posix_file, xfer );
+
+ /* Keep track of file position solely for the filesize */
+ if ( meta->whence != SEEK_CUR )
+ file->pos = 0;
+ file->pos += meta->offset;
+ if ( file->filesize < file->pos )
+ file->filesize = file->pos;
+
+ if ( iob_len ( iobuf ) ) {
+ list_add_tail ( &iobuf->list, &file->data );
+ } else {
+ free_iob ( iobuf );
+ }
+
+ return 0;
+}
+
+/** POSIX file data transfer interface operations */
+static struct xfer_interface_operations posix_file_xfer_operations = {
+ .close = posix_file_xfer_close,
+ .vredirect = xfer_vopen,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = posix_file_xfer_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/**
+ * Identify file by file descriptor
+ *
+ * @v fd File descriptor
+ * @ret file Corresponding file, or NULL
+ */
+static struct posix_file * posix_fd_to_file ( int fd ) {
+ struct posix_file *file;
+
+ list_for_each_entry ( file, &posix_files, list ) {
+ if ( file->fd == fd )
+ return file;
+ }
+ return NULL;
+}
+
+/**
+ * Find an available file descriptor
+ *
+ * @ret fd File descriptor, or negative error number
+ */
+static int posix_find_free_fd ( void ) {
+ int fd;
+
+ for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
+ if ( ! posix_fd_to_file ( fd ) )
+ return fd;
+ }
+ DBG ( "POSIX could not find free file descriptor\n" );
+ return -ENFILE;
+}
+
+/**
+ * Open file
+ *
+ * @v uri_string URI string
+ * @ret fd File descriptor, or negative error number
+ */
+int open ( const char *uri_string ) {
+ struct posix_file *file;
+ int fd;
+ int rc;
+
+ /* Find a free file descriptor to use */
+ fd = posix_find_free_fd();
+ if ( fd < 0 )
+ return fd;
+
+ /* Allocate and initialise structure */
+ file = zalloc ( sizeof ( *file ) );
+ if ( ! file )
+ return -ENOMEM;
+ file->refcnt.free = posix_file_free;
+ file->fd = fd;
+ file->rc = -EINPROGRESS;
+ xfer_init ( &file->xfer, &posix_file_xfer_operations,
+ &file->refcnt );
+ INIT_LIST_HEAD ( &file->data );
+
+ /* Open URI on data transfer interface */
+ if ( ( rc = xfer_open_uri_string ( &file->xfer, uri_string ) ) != 0 )
+ goto err;
+
+ /* Wait for open to succeed or fail */
+ while ( list_empty ( &file->data ) ) {
+ step();
+ if ( file->rc == 0 )
+ break;
+ if ( file->rc != -EINPROGRESS ) {
+ rc = file->rc;
+ goto err;
+ }
+ }
+
+ /* Add to list of open files. List takes reference ownership. */
+ list_add ( &file->list, &posix_files );
+ DBG ( "POSIX opened %s as file %d\n", uri_string, fd );
+ return fd;
+
+ err:
+ posix_file_finished ( file, rc );
+ ref_put ( &file->refcnt );
+ return rc;
+}
+
+/**
+ * Check file descriptors for readiness
+ *
+ * @v readfds File descriptors to check
+ * @v wait Wait until data is ready
+ * @ret nready Number of ready file descriptors
+ */
+int select ( fd_set *readfds, int wait ) {
+ struct posix_file *file;
+ int fd;
+
+ do {
+ for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
+ if ( ! FD_ISSET ( fd, readfds ) )
+ continue;
+ file = posix_fd_to_file ( fd );
+ if ( ! file )
+ return -EBADF;
+ if ( ( list_empty ( &file->data ) ) &&
+ ( file->rc == -EINPROGRESS ) )
+ continue;
+ /* Data is available or status has changed */
+ FD_ZERO ( readfds );
+ FD_SET ( fd, readfds );
+ return 1;
+ }
+ step();
+ } while ( wait );
+
+ return 0;
+}
+
+/**
+ * Read data from file
+ *
+ * @v buffer Data buffer
+ * @v offset Starting offset within data buffer
+ * @v len Maximum length to read
+ * @ret len Actual length read, or negative error number
+ *
+ * This call is non-blocking; if no data is available to read then
+ * -EWOULDBLOCK will be returned.
+ */
+ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) {
+ struct posix_file *file;
+ struct io_buffer *iobuf;
+ size_t len;
+
+ /* Identify file */
+ file = posix_fd_to_file ( fd );
+ if ( ! file )
+ return -EBADF;
+
+ /* Try to fetch more data if none available */
+ if ( list_empty ( &file->data ) )
+ step();
+
+ /* Dequeue at most one received I/O buffer into user buffer */
+ list_for_each_entry ( iobuf, &file->data, list ) {
+ len = iob_len ( iobuf );
+ if ( len > max_len )
+ len = max_len;
+ copy_to_user ( buffer, offset, iobuf->data, len );
+ iob_pull ( iobuf, len );
+ if ( ! iob_len ( iobuf ) ) {
+ list_del ( &iobuf->list );
+ free_iob ( iobuf );
+ }
+ file->pos += len;
+ assert ( len != 0 );
+ return len;
+ }
+
+ /* If file has completed, return (after returning all data) */
+ if ( file->rc != -EINPROGRESS ) {
+ assert ( list_empty ( &file->data ) );
+ return file->rc;
+ }
+
+ /* No data ready and file still in progress; return -WOULDBLOCK */
+ return -EWOULDBLOCK;
+}
+
+/**
+ * Determine file size
+ *
+ * @v fd File descriptor
+ * @ret size File size, or negative error number
+ */
+ssize_t fsize ( int fd ) {
+ struct posix_file *file;
+
+ /* Identify file */
+ file = posix_fd_to_file ( fd );
+ if ( ! file )
+ return -EBADF;
+
+ return file->filesize;
+}
+
+/**
+ * Close file
+ *
+ * @v fd File descriptor
+ * @ret rc Return status code
+ */
+int close ( int fd ) {
+ struct posix_file *file;
+
+ /* Identify file */
+ file = posix_fd_to_file ( fd );
+ if ( ! file )
+ return -EBADF;
+
+ /* Terminate data transfer */
+ posix_file_finished ( file, 0 );
+
+ /* Remove from list of open files and drop reference */
+ list_del ( &file->list );
+ ref_put ( &file->refcnt );
+ return 0;
+}
diff --git a/gpxe/src/core/process.c b/gpxe/src/core/process.c
new file mode 100644
index 00000000..cf931acf
--- /dev/null
+++ b/gpxe/src/core/process.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/list.h>
+#include <gpxe/init.h>
+#include <gpxe/process.h>
+
+/** @file
+ *
+ * Processes
+ *
+ * We implement a trivial form of cooperative multitasking, in which
+ * all processes share a single stack and address space.
+ */
+
+/** Process run queue */
+static LIST_HEAD ( run_queue );
+
+/** Registered permanent processes */
+static struct process processes[0]
+ __table_start ( struct process, processes );
+static struct process processes_end[0]
+ __table_end ( struct process, processes );
+
+/**
+ * Add process to process list
+ *
+ * @v process Process
+ */
+void process_add ( struct process *process ) {
+ DBGC ( process, "PROCESS %p starting\n", process );
+ ref_get ( process->refcnt );
+ list_add_tail ( &process->list, &run_queue );
+}
+
+/**
+ * Remove process from process list
+ *
+ * @v process Process
+ *
+ * It is safe to call process_del() multiple times; further calls will
+ * have no effect.
+ */
+void process_del ( struct process *process ) {
+ if ( ! list_empty ( &process->list ) ) {
+ DBGC ( process, "PROCESS %p stopping\n", process );
+ list_del ( &process->list );
+ INIT_LIST_HEAD ( &process->list );
+ ref_put ( process->refcnt );
+ } else {
+ DBGC ( process, "PROCESS %p already stopped\n", process );
+ }
+}
+
+/**
+ * Single-step a single process
+ *
+ * This executes a single step of the first process in the run queue,
+ * and moves the process to the end of the run queue.
+ */
+void step ( void ) {
+ struct process *process;
+
+ list_for_each_entry ( process, &run_queue, list ) {
+ list_del ( &process->list );
+ list_add_tail ( &process->list, &run_queue );
+ process->step ( process );
+ break;
+ }
+}
+
+/**
+ * Initialise processes
+ *
+ */
+static void init_processes ( void ) {
+ struct process *process;
+
+ for ( process = processes ; process < processes_end ; process++ ) {
+ process_add ( process );
+ }
+}
+
+/** Process initialiser */
+struct init_fn process_init_fn __init_fn ( INIT_NORMAL ) = {
+ .initialise = init_processes,
+};
diff --git a/gpxe/src/core/proto_eth_slow.c b/gpxe/src/core/proto_eth_slow.c
new file mode 100644
index 00000000..b759a713
--- /dev/null
+++ b/gpxe/src/core/proto_eth_slow.c
@@ -0,0 +1,406 @@
+/* Copyright 2004 Linux Networx */
+#ifdef PROTO_LACP
+#if 0
+#include "nic.h"
+#include "timer.h"
+#endif
+
+#define LACP_DEBUG 0
+
+/* Structure definitions originally taken from the linux bond_3ad driver */
+
+#define SLOW_DST_MAC "\x01\x80\xc2\x00\x00\x02"
+static const char slow_dest[] = SLOW_DST_MAC;
+
+
+#define SLOW_SUBTYPE_LACP 1
+#define SLOW_SUBTYPE_MARKER 2
+
+struct slow_header {
+ uint8_t subtype;
+};
+
+struct lacp_info {
+ uint16_t system_priority;
+ uint8_t system[ETH_ALEN];
+ uint16_t key;
+ uint16_t port_priority;
+ uint16_t port;
+ uint8_t state;
+ uint8_t reserved[3];
+} PACKED;
+
+#define LACP_CMP_LEN (2 + 6 + 2 + 2 + 2)
+#define LACP_CP_LEN (2 + 6 + 2 + 2 + 2 + 1)
+
+/* Link Aggregation Control Protocol(LACP) data unit structure(43.4.2.2 in the 802.3ad standard) */
+struct slow_lacp {
+ uint8_t subtype; /* = LACP(= 0x01) */
+ uint8_t version_number;
+ uint8_t tlv_type_actor_info; /* = actor information(type/length/value) */
+#define LACP_TLV_TERMINATOR 0
+#define LACP_TLV_ACTOR 1
+#define LACP_TLV_PARTNER 2
+#define LACP_TLV_COLLECTOR 3
+ uint8_t actor_information_length; /* = 20 */
+ struct lacp_info actor;
+ uint8_t tlv_type_partner_info; /* = partner information */
+ uint8_t partner_information_length; /* = 20 */
+ struct lacp_info partner;
+ uint8_t tlv_type_collector_info; /* = collector information */
+ uint8_t collector_information_length; /* = 16 */
+ uint16_t collector_max_delay;
+ uint8_t reserved_12[12];
+ uint8_t tlv_type_terminator; /* = terminator */
+ uint8_t terminator_length; /* = 0 */
+ uint8_t reserved_50[50]; /* = 0 */
+} PACKED;
+
+/* Marker Protocol Data Unit(PDU) structure(43.5.3.2 in the 802.3ad standard) */
+struct slow_marker {
+ uint8_t subtype; /* = 0x02 (marker PDU) */
+ uint8_t version_number; /* = 0x01 */
+ uint8_t tlv_type;
+#define MARKER_TLV_TERMINATOR 0 /* marker terminator */
+#define MARKER_TLV_INFO 1 /* marker information */
+#define MARKER_TLV_RESPONSE 2 /* marker response information */
+ uint8_t marker_length; /* = 0x16 */
+ uint16_t requester_port; /* The number assigned to the port by the requester */
+ uint8_t requester_system[ETH_ALEN]; /* The requester's system id */
+ uint32_t requester_transaction_id; /* The transaction id allocated by the requester, */
+ uint16_t pad; /* = 0 */
+ uint8_t tlv_type_terminator; /* = 0x00 */
+ uint8_t terminator_length; /* = 0x00 */
+ uint8_t reserved_90[90]; /* = 0 */
+} PACKED;
+
+union slow_union {
+ struct slow_header header;
+ struct slow_lacp lacp;
+ struct slow_marker marker;
+};
+
+#define FAST_PERIODIC_TIME (1*TICKS_PER_SEC)
+#define SLOW_PERIODIC_TIME (30*TICKS_PER_SEC)
+#define SHORT_TIMEOUT_TIME (3*FAST_PERIODIC_TIME)
+#define LONG_TIMEOUT_TIME (3*SLOW_PERIODIC_TIME)
+#define CHURN_DETECTION_TIME (60*TICKS_PER_SEC)
+#define AGGREGATE_WAIT_TIME (2*TICKS_PER_SEC)
+
+#define LACP_ACTIVITY (1 << 0)
+#define LACP_TIMEOUT (1 << 1)
+#define LACP_AGGREGATION (1 << 2)
+#define LACP_SYNCHRONIZATION (1 << 3)
+#define LACP_COLLECTING (1 << 4)
+#define LACP_DISTRIBUTING (1 << 5)
+#define LACP_DEFAULTED (1 << 6)
+#define LACP_EXPIRED (1 << 7)
+
+#define UNSELECTED 0
+#define STANDBY 1
+#define SELECTED 2
+
+
+struct lacp_state {
+ struct slow_lacp pkt;
+ unsigned long current_while_timer; /* Time when the LACP information expires */
+ unsigned long periodic_timer; /* Time when I need to send my partner an update */
+};
+
+static struct lacp_state lacp;
+
+
+#if LACP_DEBUG > 0
+static void print_lacp_state(uint8_t state)
+{
+ printf("%hhx", state);
+ if (state & LACP_ACTIVITY) {
+ printf(" Activity");
+ }
+ if (state & LACP_TIMEOUT) {
+ printf(" Timeout");
+ }
+ if (state & LACP_AGGREGATION) {
+ printf(" Aggregation");
+ }
+ if (state & LACP_SYNCHRONIZATION) {
+ printf(" Syncronization");
+ }
+ if (state & LACP_COLLECTING) {
+ printf(" Collecting");
+ }
+ if (state & LACP_DISTRIBUTING) {
+ printf(" Distributing");
+ }
+ if (state & LACP_DEFAULTED) {
+ printf(" Defaulted");
+ }
+ if (state & LACP_EXPIRED) {
+ printf(" Expired");
+ }
+ printf("\n");
+}
+
+static inline void print_lacpdu(struct slow_lacp *pkt)
+{
+ printf("subtype version: %hhx %hhx\n",
+ pkt->subtype, pkt->version_number);
+ printf("actor_tlv %hhx", pkt->tlv_type_actor_info);
+ printf(" len: %hhx (\n", pkt->actor_information_length);
+ printf(" sys_pri: %hx", ntohs(pkt->actor.system_priority));
+ printf(" mac: %!", pkt->actor.system);
+ printf(" key: %hx", ntohs(pkt->actor.key));
+ printf(" port_pri: %hx", ntohs(pkt->actor.port_priority));
+ printf(" port: %hx\n", ntohs(pkt->actor.port));
+ printf(" state: ");
+ print_lacp_state(pkt->actor.state);
+#if LACP_DEBUG > 1
+ printf(" reserved: %hhx %hhx %hhx\n",
+ pkt->actor.reserved[0], pkt->actor.reserved[1], pkt->actor.reserved[2]);
+#endif
+ printf(")\n");
+ printf("partner_tlv: %hhx", pkt->tlv_type_partner_info);
+ printf(" len: %hhx (\n", pkt->partner_information_length);
+ printf(" sys_pri: %hx", ntohs(pkt->partner.system_priority));
+ printf(" mac: %!", pkt->partner.system);
+ printf(" key: %hx", ntohs(pkt->partner.key));
+ printf(" port_pri: %hx", ntohs(pkt->partner.port_priority));
+ printf(" port: %hx\n", ntohs(pkt->partner.port));
+ printf(" state: ");
+ print_lacp_state(pkt->partner.state);
+#if LACP_DEBUG > 1
+ printf(" reserved: %hhx %hhx %hhx\n",
+ pkt->partner.reserved[0], pkt->partner.reserved[1], pkt->partner.reserved[2]);
+#endif
+ printf(")\n");
+ printf("collector_tlv: %hhx ", pkt->tlv_type_collector_info);
+ printf(" len: %hhx (", pkt->collector_information_length);
+ printf(" max_delay: %hx", ntohs(pkt->collector_max_delay));
+#if LACP_DEBUG > 1
+ printf("reserved_12: %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\n",
+ pkt->reserved_12[0], pkt->reserved_12[1], pkt->reserved_12[2],
+ pkt->reserved_12[3], pkt->reserved_12[4], pkt->reserved_12[5],
+ pkt->reserved_12[6], pkt->reserved_12[7], pkt->reserved_12[8],
+ pkt->reserved_12[9], pkt->reserved_12[10], pkt->reserved_12[11]);
+#endif
+ printf(" )\n");
+ printf("terminator_tlv: %hhx", pkt->tlv_type_terminator);
+ printf(" len: %hhx ()\n", pkt->terminator_length);
+}
+
+static inline unsigned long lacp_timer_val(unsigned long now, unsigned long when)
+{
+ return when?(when - now)/TICKS_PER_SEC : 0;
+}
+static void print_lacp(const char *which, struct slow_lacp *pkt, unsigned long now)
+{
+ printf("%s\n", which);
+ print_lacpdu(pkt);
+ printf("timers: c %ds p %ds\n",
+ lacp_timer_val(now, lacp.current_while_timer),
+ lacp_timer_val(now, lacp.periodic_timer)
+ );
+ printf("\n");
+}
+#else /* LACP_DEBUG */
+#define print_lacp(which, pkt, now) do {} while(0)
+#endif /* LACP_DEBUG */
+
+static void lacp_init_state(const uint8_t *mac)
+{
+ memset(&lacp, 0, sizeof(lacp));
+
+ /* Initialize the packet constants */
+ lacp.pkt.subtype = 1;
+ lacp.pkt.version_number = 1;
+
+
+ /* The default state of my interface */
+ lacp.pkt.tlv_type_actor_info = LACP_TLV_ACTOR;
+ lacp.pkt.actor_information_length = 0x14;
+ lacp.pkt.actor.system_priority = htons(1);
+ memcpy(lacp.pkt.actor.system, mac, ETH_ALEN);
+ lacp.pkt.actor.key = htons(1);
+ lacp.pkt.actor.port = htons(1);
+ lacp.pkt.actor.port_priority = htons(1);
+ lacp.pkt.actor.state =
+ LACP_SYNCHRONIZATION |
+ LACP_COLLECTING |
+ LACP_DISTRIBUTING |
+ LACP_DEFAULTED;
+
+ /* Set my partner defaults */
+ lacp.pkt.tlv_type_partner_info = LACP_TLV_PARTNER;
+ lacp.pkt.partner_information_length = 0x14;
+ lacp.pkt.partner.system_priority = htons(1);
+ /* memset(lacp.pkt.parnter_system, 0, ETH_ALEN); */
+ lacp.pkt.partner.key = htons(1);
+ lacp.pkt.partner.port = htons(1);
+ lacp.pkt.partner.port_priority = htons(1);
+ lacp.pkt.partner.state =
+ LACP_ACTIVITY |
+ LACP_SYNCHRONIZATION |
+ LACP_COLLECTING |
+ LACP_DISTRIBUTING |
+ LACP_DEFAULTED;
+
+ lacp.pkt.tlv_type_collector_info = LACP_TLV_COLLECTOR;
+ lacp.pkt.collector_information_length = 0x10;
+ lacp.pkt.collector_max_delay = htons(0x8000); /* ???? */
+
+ lacp.pkt.tlv_type_terminator = LACP_TLV_TERMINATOR;
+ lacp.pkt.terminator_length = 0;
+}
+
+#define LACP_NTT_MASK (LACP_ACTIVITY | LACP_TIMEOUT | \
+ LACP_SYNCHRONIZATION | LACP_AGGREGATION)
+
+static inline int lacp_update_ntt(struct slow_lacp *pkt)
+{
+ int ntt = 0;
+ if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) != 0) ||
+ ((pkt->partner.state & LACP_NTT_MASK) !=
+ (lacp.pkt.actor.state & LACP_NTT_MASK)))
+ {
+ ntt = 1;
+ }
+ return ntt;
+}
+
+static inline void lacp_record_pdu(struct slow_lacp *pkt)
+{
+ memcpy(&lacp.pkt.partner, &pkt->actor, LACP_CP_LEN);
+
+ lacp.pkt.actor.state &= ~LACP_DEFAULTED;
+ lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION;
+ if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) == 0) &&
+ ((pkt->partner.state & LACP_AGGREGATION) ==
+ (lacp.pkt.actor.state & LACP_AGGREGATION)))
+ {
+ lacp.pkt.partner.state |= LACP_SYNCHRONIZATION;
+ }
+ if (!(pkt->actor.state & LACP_AGGREGATION)) {
+ lacp.pkt.partner.state |= LACP_SYNCHRONIZATION;
+ }
+
+ /* ACTIVITY? */
+}
+
+static inline int lacp_timer_expired(unsigned long now, unsigned long when)
+{
+ return when && (now > when);
+}
+
+static inline void lacp_start_periodic_timer(unsigned long now)
+{
+ if ((lacp.pkt.partner.state & LACP_ACTIVITY) ||
+ (lacp.pkt.actor.state & LACP_ACTIVITY)) {
+ lacp.periodic_timer = now +
+ (((lacp.pkt.partner.state & LACP_TIMEOUT)?
+ FAST_PERIODIC_TIME : SLOW_PERIODIC_TIME));
+ }
+}
+
+static inline void lacp_start_current_while_timer(unsigned long now)
+{
+ lacp.current_while_timer = now +
+ ((lacp.pkt.actor.state & LACP_TIMEOUT) ?
+ SHORT_TIMEOUT_TIME : LONG_TIMEOUT_TIME);
+
+ lacp.pkt.actor.state &= ~LACP_EXPIRED;
+}
+
+static void send_lacp_reports(unsigned long now, int ntt)
+{
+ if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) {
+ lacp_init_state(nic.node_addr);
+ }
+ /* If the remote information has expired I need to take action */
+ if (lacp_timer_expired(now, lacp.current_while_timer)) {
+ if (!(lacp.pkt.actor.state & LACP_EXPIRED)) {
+ lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION;
+ lacp.pkt.partner.state |= LACP_TIMEOUT;
+ lacp.pkt.actor.state |= LACP_EXPIRED;
+ lacp.current_while_timer = now + SHORT_TIMEOUT_TIME;
+ ntt = 1;
+ }
+ else {
+ lacp_init_state(nic.node_addr);
+ }
+ }
+ /* If the periodic timer has expired I need to transmit */
+ if (lacp_timer_expired(now, lacp.periodic_timer)) {
+ ntt = 1;
+ /* Reset by lacp_start_periodic_timer */
+ }
+ if (ntt) {
+ eth_transmit(slow_dest, ETH_P_SLOW, sizeof(lacp.pkt), &lacp.pkt);
+
+ /* Restart the periodic timer */
+ lacp_start_periodic_timer(now);
+
+ print_lacp("Trasmitted", &lacp.pkt, now);
+ }
+}
+
+static inline void send_eth_slow_reports(unsigned long now)
+{
+ send_lacp_reports(now, 0);
+}
+
+static inline void process_eth_slow(unsigned short ptype, unsigned long now)
+{
+ union slow_union *pkt;
+ if ((ptype != ETH_P_SLOW) ||
+ (nic.packetlen < (ETH_HLEN + sizeof(pkt->header)))) {
+ return;
+ }
+ pkt = (union slow_union *)&nic.packet[ETH_HLEN];
+ if ((pkt->header.subtype == SLOW_SUBTYPE_LACP) &&
+ (nic.packetlen >= ETH_HLEN + sizeof(pkt->lacp))) {
+ int ntt;
+ if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) {
+ lacp_init_state(nic.node_addr);
+ }
+ /* As long as nic.packet is 2 byte aligned all is good */
+ print_lacp("Received", &pkt->lacp, now);
+ /* I don't actually implement the MUX or SELECT
+ * machines.
+ *
+ * What logically happens when the client and I
+ * disagree about an aggregator is the current
+ * aggregtator is unselected. The MUX machine places
+ * me in DETACHED. The SELECT machine runs and
+ * reslects the same aggregator. If I go through
+ * these steps fast enough an outside observer can not
+ * notice this.
+ *
+ * Since the process will not generate any noticeable
+ * effect it does not need an implmenetation. This
+ * keeps the code simple and the code and binary
+ * size down.
+ */
+ /* lacp_update_selected(&pkt->lacp); */
+ ntt = lacp_update_ntt(&pkt->lacp);
+ lacp_record_pdu(&pkt->lacp);
+ lacp_start_current_while_timer(now);
+ send_lacp_reports(now, ntt);
+ }
+ /* If we receive a marker information packet return it */
+ else if ((pkt->header.subtype == SLOW_SUBTYPE_MARKER) &&
+ (nic.packetlen >= ETH_HLEN + sizeof(pkt->marker)) &&
+ (pkt->marker.tlv_type == MARKER_TLV_INFO) &&
+ (pkt->marker.marker_length == 0x16))
+ {
+ pkt->marker.tlv_type = MARKER_TLV_RESPONSE;
+ eth_transmit(slow_dest, ETH_P_SLOW,
+ sizeof(pkt->marker), &pkt->marker);
+ }
+
+ }
+#else
+
+#define send_eth_slow_reports(now) do {} while(0)
+#define process_eth_slow(ptype, now) do {} while(0)
+
+#endif
diff --git a/gpxe/src/core/random.c b/gpxe/src/core/random.c
new file mode 100644
index 00000000..d34e763a
--- /dev/null
+++ b/gpxe/src/core/random.c
@@ -0,0 +1,39 @@
+/** @file
+ *
+ * Random number generation
+ *
+ */
+
+#include <stdlib.h>
+#include <gpxe/timer.h>
+
+static int32_t rnd_seed = 0;
+
+/**
+ * Seed the pseudo-random number generator
+ *
+ * @v seed Seed value
+ */
+void srandom ( unsigned int seed ) {
+ rnd_seed = seed;
+}
+
+/**
+ * Generate a pseudo-random number between 0 and 2147483647L or 2147483562?
+ *
+ * @ret rand Pseudo-random number
+ */
+long int random ( void ) {
+ int32_t q;
+
+ if ( ! rnd_seed ) /* Initialize linear congruential generator */
+ srandom ( currticks() );
+
+ /* simplified version of the LCG given in Bruce Schneier's
+ "Applied Cryptography" */
+ q = ( rnd_seed / 53668 );
+ rnd_seed = ( 40014 * ( rnd_seed - 53668 * q ) - 12211 * q );
+ if ( rnd_seed < 0 )
+ rnd_seed += 2147483563L;
+ return rnd_seed;
+}
diff --git a/gpxe/src/core/refcnt.c b/gpxe/src/core/refcnt.c
new file mode 100644
index 00000000..30bb6dea
--- /dev/null
+++ b/gpxe/src/core/refcnt.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <gpxe/refcnt.h>
+
+/** @file
+ *
+ * Reference counting
+ *
+ */
+
+/**
+ * Increment reference count
+ *
+ * @v refcnt Reference counter, or NULL
+ * @ret refcnt Reference counter
+ *
+ * If @c refcnt is NULL, no action is taken.
+ */
+struct refcnt * ref_get ( struct refcnt *refcnt ) {
+
+ if ( refcnt ) {
+ refcnt->refcnt++;
+ DBGC2 ( refcnt, "REFCNT %p incremented to %d\n",
+ refcnt, refcnt->refcnt );
+ }
+ return refcnt;
+}
+
+/**
+ * Decrement reference count
+ *
+ * @v refcnt Reference counter, or NULL
+ *
+ * If the reference count decreases below zero, the object's free()
+ * method will be called.
+ *
+ * If @c refcnt is NULL, no action is taken.
+ */
+void ref_put ( struct refcnt *refcnt ) {
+
+ if ( ! refcnt )
+ return;
+
+ refcnt->refcnt--;
+ DBGC2 ( refcnt, "REFCNT %p decremented to %d\n",
+ refcnt, refcnt->refcnt );
+
+ if ( refcnt->refcnt >= 0 )
+ return;
+
+ if ( refcnt->free ) {
+ DBGC ( refcnt, "REFCNT %p being freed via method %p\n",
+ refcnt, refcnt->free );
+ refcnt->free ( refcnt );
+ } else {
+ DBGC ( refcnt, "REFCNT %p being freed\n", refcnt );
+ free ( refcnt );
+ }
+}
diff --git a/gpxe/src/core/resolv.c b/gpxe/src/core/resolv.c
new file mode 100644
index 00000000..f4a587f1
--- /dev/null
+++ b/gpxe/src/core/resolv.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/in.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/process.h>
+#include <gpxe/resolv.h>
+
+/** @file
+ *
+ * Name resolution
+ *
+ */
+
+/***************************************************************************
+ *
+ * Name resolution interfaces
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Name resolution completed
+ *
+ * @v resolv Name resolution interface
+ * @v sa Completed socket address (if successful)
+ * @v rc Final status code
+ */
+void resolv_done ( struct resolv_interface *resolv, struct sockaddr *sa,
+ int rc ) {
+ struct resolv_interface *dest = resolv_get_dest ( resolv );
+
+ resolv_unplug ( resolv );
+ dest->op->done ( dest, sa, rc );
+ resolv_put ( dest );
+}
+
+/**
+ * Ignore name resolution done() event
+ *
+ * @v resolv Name resolution interface
+ * @v sa Completed socket address (if successful)
+ * @v rc Final status code
+ */
+void ignore_resolv_done ( struct resolv_interface *resolv __unused,
+ struct sockaddr *sa __unused, int rc __unused ) {
+ /* Do nothing */
+}
+
+/** Null name resolution interface operations */
+struct resolv_interface_operations null_resolv_ops = {
+ .done = ignore_resolv_done,
+};
+
+/** Null name resolution interface */
+struct resolv_interface null_resolv = {
+ .intf = {
+ .dest = &null_resolv.intf,
+ .refcnt = NULL,
+ },
+ .op = &null_resolv_ops,
+};
+
+/***************************************************************************
+ *
+ * Numeric name resolver
+ *
+ ***************************************************************************
+ */
+
+/** A numeric name resolver */
+struct numeric_resolv {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** Name resolution interface */
+ struct resolv_interface resolv;
+ /** Process */
+ struct process process;
+ /** Completed socket address */
+ struct sockaddr sa;
+ /** Overall status code */
+ int rc;
+};
+
+static void numeric_step ( struct process *process ) {
+ struct numeric_resolv *numeric =
+ container_of ( process, struct numeric_resolv, process );
+
+ resolv_done ( &numeric->resolv, &numeric->sa, numeric->rc );
+ process_del ( process );
+}
+
+static int numeric_resolv ( struct resolv_interface *resolv,
+ const char *name, struct sockaddr *sa ) {
+ struct numeric_resolv *numeric;
+ struct sockaddr_in *sin;
+
+ /* Allocate and initialise structure */
+ numeric = zalloc ( sizeof ( *numeric ) );
+ if ( ! numeric )
+ return -ENOMEM;
+ resolv_init ( &numeric->resolv, &null_resolv_ops, &numeric->refcnt );
+ process_init ( &numeric->process, numeric_step, &numeric->refcnt );
+ memcpy ( &numeric->sa, sa, sizeof ( numeric->sa ) );
+
+ DBGC ( numeric, "NUMERIC %p attempting to resolve \"%s\"\n",
+ numeric, name );
+
+ /* Attempt to resolve name */
+ sin = ( ( struct sockaddr_in * ) &numeric->sa );
+ sin->sin_family = AF_INET;
+ if ( inet_aton ( name, &sin->sin_addr ) == 0 )
+ numeric->rc = -EINVAL;
+
+ /* Attach to parent interface, mortalise self, and return */
+ resolv_plug_plug ( &numeric->resolv, resolv );
+ ref_put ( &numeric->refcnt );
+ return 0;
+}
+
+struct resolver numeric_resolver __resolver ( RESOLV_NUMERIC ) = {
+ .name = "NUMERIC",
+ .resolv = numeric_resolv,
+};
+
+/***************************************************************************
+ *
+ * Name resolution multiplexer
+ *
+ ***************************************************************************
+ */
+
+/** Registered name resolvers */
+static struct resolver resolvers[0]
+ __table_start ( struct resolver, resolvers );
+static struct resolver resolvers_end[0]
+ __table_end ( struct resolver, resolvers );
+
+/** A name resolution multiplexer */
+struct resolv_mux {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** Parent name resolution interface */
+ struct resolv_interface parent;
+
+ /** Child name resolution interface */
+ struct resolv_interface child;
+ /** Current child resolver */
+ struct resolver *resolver;
+
+ /** Socket address to complete */
+ struct sockaddr sa;
+ /** Name to be resolved
+ *
+ * Must be at end of structure
+ */
+ char name[0];
+};
+
+/**
+ * Try current child name resolver
+ *
+ * @v mux Name resolution multiplexer
+ * @ret rc Return status code
+ */
+static int resolv_mux_try ( struct resolv_mux *mux ) {
+ struct resolver *resolver = mux->resolver;
+ int rc;
+
+ DBGC ( mux, "RESOLV %p trying method %s\n", mux, resolver->name );
+
+ if ( ( rc = resolver->resolv ( &mux->child, mux->name,
+ &mux->sa ) ) != 0 ) {
+ DBGC ( mux, "RESOLV %p could not use method %s: %s\n",
+ mux, resolver->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Handle done() event from child name resolver
+ *
+ * @v resolv Child name resolution interface
+ * @v sa Completed socket address (if successful)
+ * @v rc Final status code
+ */
+static void resolv_mux_done ( struct resolv_interface *resolv,
+ struct sockaddr *sa, int rc ) {
+ struct resolv_mux *mux =
+ container_of ( resolv, struct resolv_mux, child );
+
+ /* Unplug child */
+ resolv_unplug ( &mux->child );
+
+ /* If this resolution succeeded, stop now */
+ if ( rc == 0 ) {
+ DBGC ( mux, "RESOLV %p succeeded using method %s\n",
+ mux, mux->resolver->name );
+ goto finished;
+ }
+
+ /* Attempt next child resolver, if possible */
+ mux->resolver++;
+ if ( mux->resolver >= resolvers_end ) {
+ DBGC ( mux, "RESOLV %p failed to resolve name\n", mux );
+ goto finished;
+ }
+ if ( ( rc = resolv_mux_try ( mux ) ) != 0 )
+ goto finished;
+
+ /* Next resolver is now running */
+ return;
+
+ finished:
+ resolv_done ( &mux->parent, sa, rc );
+}
+
+/** Name resolution multiplexer operations */
+static struct resolv_interface_operations resolv_mux_child_ops = {
+ .done = resolv_mux_done,
+};
+
+/**
+ * Start name resolution
+ *
+ * @v resolv Name resolution interface
+ * @v name Name to resolve
+ * @v sa Socket address to complete
+ * @ret rc Return status code
+ */
+int resolv ( struct resolv_interface *resolv, const char *name,
+ struct sockaddr *sa ) {
+ struct resolv_mux *mux;
+ size_t name_len = ( strlen ( name ) + 1 );
+ int rc;
+
+ /* Allocate and initialise structure */
+ mux = zalloc ( sizeof ( *mux ) + name_len );
+ if ( ! mux )
+ return -ENOMEM;
+ resolv_init ( &mux->parent, &null_resolv_ops, &mux->refcnt );
+ resolv_init ( &mux->child, &resolv_mux_child_ops, &mux->refcnt );
+ mux->resolver = resolvers;
+ memcpy ( &mux->sa, sa, sizeof ( mux->sa ) );
+ memcpy ( mux->name, name, name_len );
+
+ DBGC ( mux, "RESOLV %p attempting to resolve \"%s\"\n", mux, name );
+
+ /* Start first resolver in chain. There will always be at
+ * least one resolver (the numeric resolver), so no need to
+ * check for the zero-resolvers-available case.
+ */
+ if ( ( rc = resolv_mux_try ( mux ) ) != 0 )
+ goto err;
+
+ /* Attach parent interface, mortalise self, and return */
+ resolv_plug_plug ( &mux->parent, resolv );
+ ref_put ( &mux->refcnt );
+ return 0;
+
+ err:
+ ref_put ( &mux->refcnt );
+ return rc;
+}
+
+/***************************************************************************
+ *
+ * Named socket opening
+ *
+ ***************************************************************************
+ */
+
+/** A named socket */
+struct named_socket {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+ /** Name resolution interface */
+ struct resolv_interface resolv;
+ /** Communication semantics (e.g. SOCK_STREAM) */
+ int semantics;
+ /** Stored local socket address, if applicable */
+ struct sockaddr local;
+ /** Stored local socket address exists */
+ int have_local;
+};
+
+/** Named socket opener data transfer interface operations */
+static struct xfer_interface_operations named_xfer_ops = {
+ .close = ignore_xfer_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = no_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = ignore_xfer_deliver_raw,
+};
+
+/**
+ * Handle done() event
+ *
+ * @v resolv Name resolution interface
+ * @v sa Completed socket address (if successful)
+ * @v rc Final status code
+ */
+static void named_resolv_done ( struct resolv_interface *resolv,
+ struct sockaddr *sa, int rc ) {
+ struct named_socket *named =
+ container_of ( resolv, struct named_socket, resolv );
+
+ /* Unplug resolver and nullify data transfer interface */
+ resolv_unplug ( &named->resolv );
+ xfer_nullify ( &named->xfer );
+
+ /* Redirect if name resolution was successful */
+ if ( rc == 0 ) {
+ rc = xfer_redirect ( &named->xfer, LOCATION_SOCKET,
+ named->semantics, sa,
+ ( named->have_local ?
+ &named->local : NULL ) );
+ }
+
+ /* Close data transfer interface if redirection failed */
+ if ( rc != 0 )
+ xfer_close ( &named->xfer, rc );
+
+ /* Unplug data transfer interface */
+ xfer_unplug ( &named->xfer );
+}
+
+/** Named socket opener name resolution interface operations */
+static struct resolv_interface_operations named_resolv_ops = {
+ .done = named_resolv_done,
+};
+
+/**
+ * Open named socket
+ *
+ * @v semantics Communication semantics (e.g. SOCK_STREAM)
+ * @v peer Peer socket address to complete
+ * @v name Name to resolve
+ * @v local Local socket address, or NULL
+ * @ret rc Return status code
+ */
+int xfer_open_named_socket ( struct xfer_interface *xfer, int semantics,
+ struct sockaddr *peer, const char *name,
+ struct sockaddr *local ) {
+ struct named_socket *named;
+ int rc;
+
+ /* Allocate and initialise structure */
+ named = zalloc ( sizeof ( *named ) );
+ if ( ! named )
+ return -ENOMEM;
+ xfer_init ( &named->xfer, &named_xfer_ops, &named->refcnt );
+ resolv_init ( &named->resolv, &named_resolv_ops, &named->refcnt );
+ named->semantics = semantics;
+ if ( local ) {
+ memcpy ( &named->local, local, sizeof ( named->local ) );
+ named->have_local = 1;
+ }
+
+ DBGC ( named, "RESOLV %p opening named socket \"%s\"\n",
+ named, name );
+
+ /* Start name resolution */
+ if ( ( rc = resolv ( &named->resolv, name, peer ) ) != 0 )
+ goto err;
+
+ /* Attach parent interface, mortalise self, and return */
+ xfer_plug_plug ( &named->xfer, xfer );
+ ref_put ( &named->refcnt );
+ return 0;
+
+ err:
+ ref_put ( &named->refcnt );
+ return rc;
+}
diff --git a/gpxe/src/core/serial.c b/gpxe/src/core/serial.c
new file mode 100644
index 00000000..a5b3f913
--- /dev/null
+++ b/gpxe/src/core/serial.c
@@ -0,0 +1,268 @@
+/*
+ * The serial port interface routines implement a simple polled i/o
+ * interface to a standard serial port. Due to the space restrictions
+ * for the boot blocks, no BIOS support is used (since BIOS requires
+ * expensive real/protected mode switches), instead the rudimentary
+ * BIOS support is duplicated here.
+ *
+ * The base address and speed for the i/o port are passed from the
+ * Makefile in the COMCONSOLE and CONSPEED preprocessor macros. The
+ * line control parameters are currently hard-coded to 8 bits, no
+ * parity, 1 stop bit (8N1). This can be changed in init_serial().
+ */
+
+#include "stddef.h"
+#include "console.h"
+#include <gpxe/init.h>
+#include "io.h"
+#include <unistd.h>
+#include "config/serial.h"
+
+/* Set default values if none specified */
+
+#ifndef COMCONSOLE
+#define COMCONSOLE 0x3f8
+#endif
+
+#ifndef COMSPEED
+#define COMSPEED 9600
+#endif
+
+#ifndef COMDATA
+#define COMDATA 8
+#endif
+
+#ifndef COMPARITY
+#define COMPARITY 0
+#endif
+
+#ifndef COMSTOP
+#define COMSTOP 1
+#endif
+
+#undef UART_BASE
+#define UART_BASE ( COMCONSOLE )
+
+#undef UART_BAUD
+#define UART_BAUD ( COMSPEED )
+
+#if ((115200%UART_BAUD) != 0)
+#error Bad ttys0 baud rate
+#endif
+
+#define COMBRD (115200/UART_BAUD)
+
+/* Line Control Settings */
+#define UART_LCS ( ( ( (COMDATA) - 5 ) << 0 ) | \
+ ( ( (COMPARITY) ) << 3 ) | \
+ ( ( (COMSTOP) - 1 ) << 2 ) )
+
+/* Data */
+#define UART_RBR 0x00
+#define UART_TBR 0x00
+
+/* Control */
+#define UART_IER 0x01
+#define UART_IIR 0x02
+#define UART_FCR 0x02
+#define UART_LCR 0x03
+#define UART_MCR 0x04
+#define UART_DLL 0x00
+#define UART_DLM 0x01
+
+/* Status */
+#define UART_LSR 0x05
+#define UART_LSR_TEMPT 0x40 /* Transmitter empty */
+#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
+#define UART_LSR_BI 0x10 /* Break interrupt indicator */
+#define UART_LSR_FE 0x08 /* Frame error indicator */
+#define UART_LSR_PE 0x04 /* Parity error indicator */
+#define UART_LSR_OE 0x02 /* Overrun error indicator */
+#define UART_LSR_DR 0x01 /* Receiver data ready */
+
+#define UART_MSR 0x06
+#define UART_SCR 0x07
+
+#if defined(UART_MEM)
+#define uart_readb(addr) readb((addr))
+#define uart_writeb(val,addr) writeb((val),(addr))
+#else
+#define uart_readb(addr) inb((addr))
+#define uart_writeb(val,addr) outb((val),(addr))
+#endif
+
+struct console_driver serial_console __console_driver;
+
+/*
+ * void serial_putc(int ch);
+ * Write character `ch' to port UART_BASE.
+ */
+static void serial_putc ( int ch ) {
+ int i;
+ int status;
+ i = 1000; /* timeout */
+ while(--i > 0) {
+ status = uart_readb(UART_BASE + UART_LSR);
+ if (status & UART_LSR_THRE) {
+ /* TX buffer emtpy */
+ uart_writeb(ch, UART_BASE + UART_TBR);
+ break;
+ }
+ mdelay(2);
+ }
+}
+
+/*
+ * int serial_getc(void);
+ * Read a character from port UART_BASE.
+ */
+static int serial_getc ( void ) {
+ int status;
+ int ch;
+ do {
+ status = uart_readb(UART_BASE + UART_LSR);
+ } while((status & 1) == 0);
+ ch = uart_readb(UART_BASE + UART_RBR); /* fetch (first) character */
+ ch &= 0x7f; /* remove any parity bits we get */
+ if (ch == 0x7f) { /* Make DEL... look like BS */
+ ch = 0x08;
+ }
+ return ch;
+}
+
+/*
+ * int serial_ischar(void);
+ * If there is a character in the input buffer of port UART_BASE,
+ * return nonzero; otherwise return 0.
+ */
+static int serial_ischar ( void ) {
+ int status;
+ status = uart_readb(UART_BASE + UART_LSR); /* line status reg; */
+ return status & 1; /* rx char available */
+}
+
+/*
+ * int serial_init(void);
+ * Initialize port UART_BASE to speed COMSPEED, line settings 8N1.
+ */
+static void serial_init ( void ) {
+ int status;
+ int divisor, lcs;
+
+ DBG ( "Serial port %#x initialising\n", UART_BASE );
+
+ divisor = COMBRD;
+ lcs = UART_LCS;
+
+
+#ifdef COMPRESERVE
+ lcs = uart_readb(UART_BASE + UART_LCR) & 0x7f;
+ uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
+ divisor = (uart_readb(UART_BASE + UART_DLM) << 8) | uart_readb(UART_BASE + UART_DLL);
+ uart_writeb(lcs, UART_BASE + UART_LCR);
+#endif
+
+ /* Set Baud Rate Divisor to COMSPEED, and test to see if the
+ * serial port appears to be present.
+ */
+ uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
+ uart_writeb(0xaa, UART_BASE + UART_DLL);
+ if (uart_readb(UART_BASE + UART_DLL) != 0xaa) {
+ DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE );
+ goto out;
+ }
+ uart_writeb(0x55, UART_BASE + UART_DLL);
+ if (uart_readb(UART_BASE + UART_DLL) != 0x55) {
+ DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE );
+ goto out;
+ }
+ uart_writeb(divisor & 0xff, UART_BASE + UART_DLL);
+ if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff)) {
+ DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE );
+ goto out;
+ }
+ uart_writeb(0xaa, UART_BASE + UART_DLM);
+ if (uart_readb(UART_BASE + UART_DLM) != 0xaa) {
+ DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE );
+ goto out;
+ }
+ uart_writeb(0x55, UART_BASE + UART_DLM);
+ if (uart_readb(UART_BASE + UART_DLM) != 0x55) {
+ DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE );
+ goto out;
+ }
+ uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM);
+ if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff)) {
+ DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE );
+ goto out;
+ }
+ uart_writeb(lcs, UART_BASE + UART_LCR);
+
+ /* disable interrupts */
+ uart_writeb(0x0, UART_BASE + UART_IER);
+
+ /* disable fifo's */
+ uart_writeb(0x00, UART_BASE + UART_FCR);
+
+ /* Set clear to send, so flow control works... */
+ uart_writeb((1<<1), UART_BASE + UART_MCR);
+
+
+ /* Flush the input buffer. */
+ do {
+ /* rx buffer reg
+ * throw away (unconditionally the first time)
+ */
+ (void) uart_readb(UART_BASE + UART_RBR);
+ /* line status reg */
+ status = uart_readb(UART_BASE + UART_LSR);
+ } while(status & UART_LSR_DR);
+ serial_console.disabled = 0;
+ out:
+ return;
+}
+
+/*
+ * void serial_fini(void);
+ * Cleanup our use of the serial port, in particular flush the
+ * output buffer so we don't accidentially lose characters.
+ */
+static void serial_fini ( void ) {
+ int i, status;
+ if (serial_console.disabled) {
+ /* no serial interface */
+ return;
+ }
+ /* Flush the output buffer to avoid dropping characters,
+ * if we are reinitializing the serial port.
+ */
+ i = 10000; /* timeout */
+ do {
+ status = uart_readb(UART_BASE + UART_LSR);
+ } while((--i > 0) && !(status & UART_LSR_TEMPT));
+ /* Don't mark it as disabled; it's still usable */
+}
+
+struct console_driver serial_console __console_driver = {
+ .putchar = serial_putc,
+ .getchar = serial_getc,
+ .iskey = serial_ischar,
+ .disabled = 1,
+};
+
+/** Serial console startup function */
+struct startup_fn serial_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
+ .startup = serial_init,
+ .shutdown = serial_fini,
+};
+
+/**
+ * Serial console initialisation function
+ *
+ * Initialise console early on so that it is available to capture
+ * early debug messages. It is safe to call serial_init() multiple
+ * times.
+ */
+struct init_fn serial_init_fn __init_fn ( INIT_CONSOLE ) = {
+ .initialise = serial_init,
+};
diff --git a/gpxe/src/core/settings.c b/gpxe/src/core/settings.c
new file mode 100644
index 00000000..b793ae68
--- /dev/null
+++ b/gpxe/src/core/settings.c
@@ -0,0 +1,1037 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <assert.h>
+#include <gpxe/in.h>
+#include <gpxe/vsprintf.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/settings.h>
+
+/** @file
+ *
+ * Configuration settings
+ *
+ */
+
+/** Registered settings */
+static struct setting settings[0]
+ __table_start ( struct setting, settings );
+static struct setting settings_end[0]
+ __table_end ( struct setting, settings );
+
+/** Registered setting types */
+static struct setting_type setting_types[0]
+ __table_start ( struct setting_type, setting_types );
+static struct setting_type setting_types_end[0]
+ __table_end ( struct setting_type, setting_types );
+
+/** Registered settings applicators */
+static struct settings_applicator settings_applicators[0]
+ __table_start ( struct settings_applicator, settings_applicators );
+static struct settings_applicator settings_applicators_end[0]
+ __table_end ( struct settings_applicator, settings_applicators );
+
+/******************************************************************************
+ *
+ * Registered settings blocks
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Store value of simple setting
+ *
+ * @v options DHCP option block
+ * @v setting Setting to store
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+int simple_settings_store ( struct settings *settings, struct setting *setting,
+ const void *data, size_t len ) {
+ struct simple_settings *simple =
+ container_of ( settings, struct simple_settings, settings );
+ return dhcpopt_extensible_store ( &simple->dhcpopts, setting->tag,
+ data, len );
+}
+
+/**
+ * Fetch value of simple setting
+ *
+ * @v options DHCP option block
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+int simple_settings_fetch ( struct settings *settings, struct setting *setting,
+ void *data, size_t len ) {
+ struct simple_settings *simple =
+ container_of ( settings, struct simple_settings, settings );
+ return dhcpopt_fetch ( &simple->dhcpopts, setting->tag, data, len );
+}
+
+/** Simple settings operations */
+struct settings_operations simple_settings_operations = {
+ .store = simple_settings_store,
+ .fetch = simple_settings_fetch,
+};
+
+/** Root simple settings block */
+struct simple_settings simple_settings_root = {
+ .settings = {
+ .refcnt = NULL,
+ .name = "",
+ .siblings =
+ LIST_HEAD_INIT ( simple_settings_root.settings.siblings ),
+ .children =
+ LIST_HEAD_INIT ( simple_settings_root.settings.children ),
+ .op = &simple_settings_operations,
+ },
+};
+
+/** Root settings block */
+#define settings_root simple_settings_root.settings
+
+/**
+ * Apply all settings
+ *
+ * @ret rc Return status code
+ */
+static int apply_settings ( void ) {
+ struct settings_applicator *applicator;
+ int rc;
+
+ /* Call all settings applicators */
+ for ( applicator = settings_applicators ;
+ applicator < settings_applicators_end ; applicator++ ) {
+ if ( ( rc = applicator->apply() ) != 0 ) {
+ DBG ( "Could not apply settings using applicator "
+ "%p: %s\n", applicator, strerror ( rc ) );
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Reprioritise settings
+ *
+ * @v settings Settings block
+ *
+ * Reorders the settings block amongst its siblings according to its
+ * priority.
+ */
+static void reprioritise_settings ( struct settings *settings ) {
+ struct settings *parent = settings->parent;
+ long priority;
+ struct settings *tmp;
+ long tmp_priority;
+
+ /* Stop when we reach the top of the tree */
+ if ( ! parent )
+ return;
+
+ /* Read priority, if present */
+ priority = fetch_intz_setting ( settings, &priority_setting );
+
+ /* Remove from siblings list */
+ list_del ( &settings->siblings );
+
+ /* Reinsert after any existing blocks which have a higher priority */
+ list_for_each_entry ( tmp, &parent->children, siblings ) {
+ tmp_priority = fetch_intz_setting ( tmp, &priority_setting );
+ if ( priority > tmp_priority )
+ break;
+ }
+ list_add_tail ( &settings->siblings, &tmp->siblings );
+
+ /* Recurse up the tree */
+ reprioritise_settings ( parent );
+}
+
+/**
+ * Register settings block
+ *
+ * @v settings Settings block
+ * @v parent Parent settings block, or NULL
+ * @ret rc Return status code
+ */
+int register_settings ( struct settings *settings, struct settings *parent ) {
+
+ /* NULL parent => add to settings root */
+ assert ( settings != NULL );
+ if ( parent == NULL )
+ parent = &settings_root;
+
+ /* Add to list of settings */
+ ref_get ( settings->refcnt );
+ ref_get ( parent->refcnt );
+ settings->parent = parent;
+ list_add_tail ( &settings->siblings, &parent->children );
+ DBGC ( settings, "Settings %p registered\n", settings );
+
+ /* Fix up settings priority */
+ reprioritise_settings ( settings );
+
+ /* Apply potentially-updated settings */
+ apply_settings();
+
+ return 0;
+}
+
+/**
+ * Unregister settings block
+ *
+ * @v settings Settings block
+ */
+void unregister_settings ( struct settings *settings ) {
+
+ /* Remove from list of settings */
+ ref_put ( settings->refcnt );
+ ref_put ( settings->parent->refcnt );
+ settings->parent = NULL;
+ list_del ( &settings->siblings );
+ DBGC ( settings, "Settings %p unregistered\n", settings );
+
+ /* Apply potentially-updated settings */
+ apply_settings();
+}
+
+/**
+ * Find child named settings block
+ *
+ * @v parent Parent settings block
+ * @v name Name within this parent
+ * @ret settings Settings block, or NULL
+ */
+struct settings * find_child_settings ( struct settings *parent,
+ const char *name ) {
+ struct settings *settings;
+ size_t len;
+
+ /* Look for a child whose name matches the initial component */
+ list_for_each_entry ( settings, &parent->children, siblings ) {
+ len = strlen ( settings->name );
+ if ( strncmp ( name, settings->name, len ) != 0 )
+ continue;
+ if ( name[len] == 0 )
+ return settings;
+ if ( name[len] == '.' )
+ return find_child_settings ( settings,
+ ( name + len + 1 ) );
+ }
+
+ return NULL;
+}
+
+/**
+ * Find named settings block
+ *
+ * @v name Name
+ * @ret settings Settings block, or NULL
+ */
+struct settings * find_settings ( const char *name ) {
+
+ /* If name is empty, use the root */
+ if ( ! *name )
+ return &settings_root;
+
+ return find_child_settings ( &settings_root, name );
+}
+
+/******************************************************************************
+ *
+ * Core settings routines
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Store value of setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+int store_setting ( struct settings *settings, struct setting *setting,
+ const void *data, size_t len ) {
+ int rc;
+
+ /* Sanity check */
+ if ( ! settings )
+ return -ENODEV;
+
+ /* Store setting */
+ if ( ( rc = settings->op->store ( settings, setting,
+ data, len ) ) != 0 )
+ return rc;
+
+ /* Reprioritise settings if necessary */
+ if ( setting_cmp ( setting, &priority_setting ) == 0 )
+ reprioritise_settings ( settings );
+
+ /* If these settings are registered, apply potentially-updated
+ * settings
+ */
+ for ( ; settings ; settings = settings->parent ) {
+ if ( settings == &settings_root ) {
+ if ( ( rc = apply_settings() ) != 0 )
+ return rc;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Fetch value of setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ *
+ * The actual length of the setting will be returned even if
+ * the buffer was too small.
+ */
+int fetch_setting ( struct settings *settings, struct setting *setting,
+ void *data, size_t len ) {
+ struct settings *child;
+ int ret;
+
+ /* NULL settings implies starting at the global settings root */
+ if ( ! settings )
+ settings = &settings_root;
+
+ /* Try this block first */
+ if ( ( ret = settings->op->fetch ( settings, setting,
+ data, len ) ) >= 0 )
+ return ret;
+
+ /* Recurse into each child block in turn */
+ list_for_each_entry ( child, &settings->children, siblings ) {
+ if ( ( ret = fetch_setting ( child, setting,
+ data, len ) ) >= 0 )
+ return ret;
+ }
+
+ return -ENOENT;
+}
+
+/**
+ * Fetch length of setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @ret len Length of setting data, or negative error
+ *
+ * This function can also be used as an existence check for the
+ * setting.
+ */
+int fetch_setting_len ( struct settings *settings, struct setting *setting ) {
+ return fetch_setting ( settings, setting, NULL, 0 );
+}
+
+/**
+ * Fetch value of string setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting string data
+ * @v len Length of buffer
+ * @ret len Length of string setting, or negative error
+ *
+ * The resulting string is guaranteed to be correctly NUL-terminated.
+ * The returned length will be the length of the underlying setting
+ * data.
+ */
+int fetch_string_setting ( struct settings *settings, struct setting *setting,
+ char *data, size_t len ) {
+ memset ( data, 0, len );
+ return fetch_setting ( settings, setting, data, ( len - 1 ) );
+}
+
+/**
+ * Fetch value of IPv4 address setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v inp IPv4 address to fill in
+ * @ret len Length of setting, or negative error
+ */
+int fetch_ipv4_setting ( struct settings *settings, struct setting *setting,
+ struct in_addr *inp ) {
+ int len;
+
+ len = fetch_setting ( settings, setting, inp, sizeof ( *inp ) );
+ if ( len < 0 )
+ return len;
+ if ( len < ( int ) sizeof ( *inp ) )
+ return -ERANGE;
+ return len;
+}
+
+/**
+ * Fetch value of signed integer setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v value Integer value to fill in
+ * @ret len Length of setting, or negative error
+ */
+int fetch_int_setting ( struct settings *settings, struct setting *setting,
+ long *value ) {
+ union {
+ long value;
+ uint8_t u8[ sizeof ( long ) ];
+ int8_t s8[ sizeof ( long ) ];
+ } buf;
+ int len;
+ int i;
+
+ buf.value = 0;
+ len = fetch_setting ( settings, setting, &buf, sizeof ( buf ) );
+ if ( len < 0 )
+ return len;
+ if ( len > ( int ) sizeof ( buf ) )
+ return -ERANGE;
+
+ *value = ( ( buf.s8[0] >= 0 ) ? 0 : -1L );
+ for ( i = 0 ; i < len ; i++ ) {
+ *value = ( ( *value << 8 ) | buf.u8[i] );
+ }
+
+ return len;
+}
+
+/**
+ * Fetch value of unsigned integer setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v value Integer value to fill in
+ * @ret len Length of setting, or negative error
+ */
+int fetch_uint_setting ( struct settings *settings, struct setting *setting,
+ unsigned long *value ) {
+ long svalue;
+ int len;
+
+ len = fetch_int_setting ( settings, setting, &svalue );
+ if ( len < 0 )
+ return len;
+
+ *value = ( svalue & ( -1UL >> ( sizeof ( long ) - len ) ) );
+
+ return len;
+}
+
+/**
+ * Fetch value of signed integer setting, or zero
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @ret value Setting value, or zero
+ */
+long fetch_intz_setting ( struct settings *settings, struct setting *setting ){
+ long value = 0;
+
+ fetch_int_setting ( settings, setting, &value );
+ return value;
+}
+
+/**
+ * Fetch value of unsigned integer setting, or zero
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @ret value Setting value, or zero
+ */
+unsigned long fetch_uintz_setting ( struct settings *settings,
+ struct setting *setting ) {
+ unsigned long value = 0;
+
+ fetch_uint_setting ( settings, setting, &value );
+ return value;
+}
+
+/**
+ * Compare two settings
+ *
+ * @v a Setting to compare
+ * @v b Setting to compare
+ * @ret 0 Settings are the same
+ * @ret non-zero Settings are not the same
+ */
+int setting_cmp ( struct setting *a, struct setting *b ) {
+
+ /* If the settings have tags, compare them */
+ if ( a->tag && ( a->tag == b->tag ) )
+ return 0;
+
+ /* Otherwise, compare the names */
+ return strcmp ( a->name, b->name );
+}
+
+/******************************************************************************
+ *
+ * Formatted setting routines
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Store value of typed setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v type Settings type
+ * @v value Formatted setting data, or NULL
+ * @ret rc Return status code
+ */
+int storef_setting ( struct settings *settings, struct setting *setting,
+ const char *value ) {
+
+ /* NULL value implies deletion. Avoid imposing the burden of
+ * checking for NULL values on each typed setting's storef()
+ * method.
+ */
+ if ( ! value )
+ return delete_setting ( settings, setting );
+
+ return setting->type->storef ( settings, setting, value );
+}
+
+/**
+ * Find named setting
+ *
+ * @v name Name
+ * @ret setting Named setting, or NULL
+ */
+static struct setting * find_setting ( const char *name ) {
+ struct setting *setting;
+
+ for ( setting = settings ; setting < settings_end ; setting++ ) {
+ if ( strcmp ( name, setting->name ) == 0 )
+ return setting;
+ }
+ return NULL;
+}
+
+/**
+ * Find setting type
+ *
+ * @v name Name
+ * @ret type Setting type, or NULL
+ */
+static struct setting_type * find_setting_type ( const char *name ) {
+ struct setting_type *type;
+
+ for ( type = setting_types ; type < setting_types_end ; type++ ) {
+ if ( strcmp ( name, type->name ) == 0 )
+ return type;
+ }
+ return NULL;
+}
+
+/**
+ * Parse setting name
+ *
+ * @v name Name of setting
+ * @v settings Settings block to fill in
+ * @v setting Setting to fill in
+ * @ret rc Return status code
+ *
+ * Interprets a name of the form
+ * "[settings_name/]tag_name[:type_name]" and fills in the appropriate
+ * fields.
+ */
+static int parse_setting_name ( const char *name, struct settings **settings,
+ struct setting *setting ) {
+ char tmp_name[ strlen ( name ) + 1 ];
+ char *settings_name;
+ char *setting_name;
+ char *type_name;
+ struct setting *named_setting;
+ char *tmp;
+
+ /* Set defaults */
+ *settings = &settings_root;
+ memset ( setting, 0, sizeof ( *setting ) );
+ setting->type = &setting_type_hex;
+
+ /* Split name into "[settings_name/]setting_name[:type_name]" */
+ memcpy ( tmp_name, name, sizeof ( tmp_name ) );
+ if ( ( setting_name = strchr ( tmp_name, '/' ) ) != NULL ) {
+ *(setting_name++) = 0;
+ settings_name = tmp_name;
+ } else {
+ setting_name = tmp_name;
+ settings_name = NULL;
+ }
+ if ( ( type_name = strchr ( setting_name, ':' ) ) != NULL )
+ *(type_name++) = 0;
+
+ /* Identify settings block, if specified */
+ if ( settings_name ) {
+ *settings = find_settings ( settings_name );
+ if ( *settings == NULL ) {
+ DBG ( "Unrecognised settings block \"%s\" in \"%s\"\n",
+ settings_name, name );
+ return -ENODEV;
+ }
+ }
+
+ /* Identify tag number */
+ if ( ( named_setting = find_setting ( setting_name ) ) != NULL ) {
+ memcpy ( setting, named_setting, sizeof ( *setting ) );
+ } else {
+ /* Unrecognised name: try to interpret as a tag number */
+ tmp = setting_name;
+ while ( 1 ) {
+ setting->tag = ( ( setting->tag << 8 ) |
+ strtoul ( tmp, &tmp, 0 ) );
+ if ( *tmp == 0 )
+ break;
+ if ( *tmp != '.' ) {
+ DBG ( "Invalid setting \"%s\" in \"%s\"\n",
+ setting_name, name );
+ return -ENOENT;
+ }
+ tmp++;
+ }
+ }
+
+ /* Identify setting type, if specified */
+ if ( type_name ) {
+ setting->type = find_setting_type ( type_name );
+ if ( setting->type == NULL ) {
+ DBG ( "Invalid setting type \"%s\" in \"%s\"\n",
+ type_name, name );
+ return -ENOTSUP;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Parse and store value of named setting
+ *
+ * @v name Name of setting
+ * @v value Formatted setting data, or NULL
+ * @ret rc Return status code
+ */
+int storef_named_setting ( const char *name, const char *value ) {
+ struct settings *settings;
+ struct setting setting;
+ int rc;
+
+ if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 )
+ return rc;
+ return storef_setting ( settings, &setting, value );
+}
+
+/**
+ * Fetch and format value of named setting
+ *
+ * @v name Name of setting
+ * @v buf Buffer to contain formatted value
+ * @v len Length of buffer
+ * @ret len Length of formatted value, or negative error
+ */
+int fetchf_named_setting ( const char *name, char *buf, size_t len ) {
+ struct settings *settings;
+ struct setting setting;
+ int rc;
+
+ if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 )
+ return rc;
+ return fetchf_setting ( settings, &setting, buf, len );
+}
+
+/******************************************************************************
+ *
+ * Setting types
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Parse and store value of string setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v value Formatted setting data
+ * @ret rc Return status code
+ */
+static int storef_string ( struct settings *settings, struct setting *setting,
+ const char *value ) {
+ return store_setting ( settings, setting, value, strlen ( value ) );
+}
+
+/**
+ * Fetch and format value of string setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v buf Buffer to contain formatted value
+ * @v len Length of buffer
+ * @ret len Length of formatted value, or negative error
+ */
+static int fetchf_string ( struct settings *settings, struct setting *setting,
+ char *buf, size_t len ) {
+ return fetch_string_setting ( settings, setting, buf, len );
+}
+
+/** A string setting type */
+struct setting_type setting_type_string __setting_type = {
+ .name = "string",
+ .storef = storef_string,
+ .fetchf = fetchf_string,
+};
+
+/**
+ * Parse and store value of IPv4 address setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v value Formatted setting data
+ * @ret rc Return status code
+ */
+static int storef_ipv4 ( struct settings *settings, struct setting *setting,
+ const char *value ) {
+ struct in_addr ipv4;
+
+ if ( inet_aton ( value, &ipv4 ) == 0 )
+ return -EINVAL;
+ return store_setting ( settings, setting, &ipv4, sizeof ( ipv4 ) );
+}
+
+/**
+ * Fetch and format value of IPv4 address setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v buf Buffer to contain formatted value
+ * @v len Length of buffer
+ * @ret len Length of formatted value, or negative error
+ */
+static int fetchf_ipv4 ( struct settings *settings, struct setting *setting,
+ char *buf, size_t len ) {
+ struct in_addr ipv4;
+ int rc;
+
+ if ( ( rc = fetch_ipv4_setting ( settings, setting, &ipv4 ) ) < 0 )
+ return rc;
+ return snprintf ( buf, len, inet_ntoa ( ipv4 ) );
+}
+
+/** An IPv4 address setting type */
+struct setting_type setting_type_ipv4 __setting_type = {
+ .name = "ipv4",
+ .storef = storef_ipv4,
+ .fetchf = fetchf_ipv4,
+};
+
+/**
+ * Parse and store value of integer setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v value Formatted setting data
+ * @v size Integer size, in bytes
+ * @ret rc Return status code
+ */
+static int storef_int ( struct settings *settings, struct setting *setting,
+ const char *value, unsigned int size ) {
+ union {
+ uint32_t num;
+ uint8_t bytes[4];
+ } u;
+ char *endp;
+
+ u.num = htonl ( strtoul ( value, &endp, 0 ) );
+ if ( *endp )
+ return -EINVAL;
+ return store_setting ( settings, setting,
+ &u.bytes[ sizeof ( u ) - size ], size );
+}
+
+/**
+ * Parse and store value of 8-bit integer setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v value Formatted setting data
+ * @v size Integer size, in bytes
+ * @ret rc Return status code
+ */
+static int storef_int8 ( struct settings *settings, struct setting *setting,
+ const char *value ) {
+ return storef_int ( settings, setting, value, 1 );
+}
+
+/**
+ * Parse and store value of 16-bit integer setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v value Formatted setting data
+ * @v size Integer size, in bytes
+ * @ret rc Return status code
+ */
+static int storef_int16 ( struct settings *settings, struct setting *setting,
+ const char *value ) {
+ return storef_int ( settings, setting, value, 2 );
+}
+
+/**
+ * Parse and store value of 32-bit integer setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v value Formatted setting data
+ * @v size Integer size, in bytes
+ * @ret rc Return status code
+ */
+static int storef_int32 ( struct settings *settings, struct setting *setting,
+ const char *value ) {
+ return storef_int ( settings, setting, value, 4 );
+}
+
+/**
+ * Fetch and format value of signed integer setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v buf Buffer to contain formatted value
+ * @v len Length of buffer
+ * @ret len Length of formatted value, or negative error
+ */
+static int fetchf_int ( struct settings *settings, struct setting *setting,
+ char *buf, size_t len ) {
+ long value;
+ int rc;
+
+ if ( ( rc = fetch_int_setting ( settings, setting, &value ) ) < 0 )
+ return rc;
+ return snprintf ( buf, len, "%ld", value );
+}
+
+/**
+ * Fetch and format value of unsigned integer setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v buf Buffer to contain formatted value
+ * @v len Length of buffer
+ * @ret len Length of formatted value, or negative error
+ */
+static int fetchf_uint ( struct settings *settings, struct setting *setting,
+ char *buf, size_t len ) {
+ unsigned long value;
+ int rc;
+
+ if ( ( rc = fetch_uint_setting ( settings, setting, &value ) ) < 0 )
+ return rc;
+ return snprintf ( buf, len, "%#lx", value );
+}
+
+/** A signed 8-bit integer setting type */
+struct setting_type setting_type_int8 __setting_type = {
+ .name = "int8",
+ .storef = storef_int8,
+ .fetchf = fetchf_int,
+};
+
+/** A signed 16-bit integer setting type */
+struct setting_type setting_type_int16 __setting_type = {
+ .name = "int16",
+ .storef = storef_int16,
+ .fetchf = fetchf_int,
+};
+
+/** A signed 32-bit integer setting type */
+struct setting_type setting_type_int32 __setting_type = {
+ .name = "int32",
+ .storef = storef_int32,
+ .fetchf = fetchf_int,
+};
+
+/** An unsigned 8-bit integer setting type */
+struct setting_type setting_type_uint8 __setting_type = {
+ .name = "uint8",
+ .storef = storef_int8,
+ .fetchf = fetchf_uint,
+};
+
+/** An unsigned 16-bit integer setting type */
+struct setting_type setting_type_uint16 __setting_type = {
+ .name = "uint16",
+ .storef = storef_int16,
+ .fetchf = fetchf_uint,
+};
+
+/** An unsigned 32-bit integer setting type */
+struct setting_type setting_type_uint32 __setting_type = {
+ .name = "uint32",
+ .storef = storef_int32,
+ .fetchf = fetchf_uint,
+};
+
+/**
+ * Parse and store value of hex string setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v value Formatted setting data
+ * @ret rc Return status code
+ */
+static int storef_hex ( struct settings *settings, struct setting *setting,
+ const char *value ) {
+ char *ptr = ( char * ) value;
+ uint8_t bytes[ strlen ( value ) ]; /* cannot exceed strlen(value) */
+ unsigned int len = 0;
+
+ while ( 1 ) {
+ bytes[len++] = strtoul ( ptr, &ptr, 16 );
+ switch ( *ptr ) {
+ case '\0' :
+ return store_setting ( settings, setting, bytes, len );
+ case ':' :
+ ptr++;
+ break;
+ default :
+ return -EINVAL;
+ }
+ }
+}
+
+/**
+ * Fetch and format value of hex string setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v buf Buffer to contain formatted value
+ * @v len Length of buffer
+ * @ret len Length of formatted value, or negative error
+ */
+static int fetchf_hex ( struct settings *settings, struct setting *setting,
+ char *buf, size_t len ) {
+ int raw_len;
+ int check_len;
+ int used = 0;
+ int i;
+
+ raw_len = fetch_setting_len ( settings, setting );
+ if ( raw_len < 0 )
+ return raw_len;
+
+ {
+ uint8_t raw[raw_len];
+
+ check_len = fetch_setting ( settings, setting, raw,
+ sizeof ( raw ) );
+ assert ( check_len == raw_len );
+
+ if ( len )
+ buf[0] = 0; /* Ensure that a terminating NUL exists */
+ for ( i = 0 ; i < raw_len ; i++ ) {
+ used += ssnprintf ( ( buf + used ), ( len - used ),
+ "%s%02x", ( used ? ":" : "" ),
+ raw[i] );
+ }
+ return used;
+ }
+}
+
+/** A hex-string setting */
+struct setting_type setting_type_hex __setting_type = {
+ .name = "hex",
+ .storef = storef_hex,
+ .fetchf = fetchf_hex,
+};
+
+/******************************************************************************
+ *
+ * Settings
+ *
+ ******************************************************************************
+ */
+
+/** Hostname setting */
+struct setting hostname_setting __setting = {
+ .name = "hostname",
+ .description = "Host name",
+ .tag = DHCP_HOST_NAME,
+ .type = &setting_type_string,
+};
+
+/** Filename setting */
+struct setting filename_setting __setting = {
+ .name = "filename",
+ .description = "Boot filename",
+ .tag = DHCP_BOOTFILE_NAME,
+ .type = &setting_type_string,
+};
+
+/** Root path setting */
+struct setting root_path_setting __setting = {
+ .name = "root-path",
+ .description = "NFS/iSCSI root path",
+ .tag = DHCP_ROOT_PATH,
+ .type = &setting_type_string,
+};
+
+/** Username setting */
+struct setting username_setting __setting = {
+ .name = "username",
+ .description = "User name",
+ .tag = DHCP_EB_USERNAME,
+ .type = &setting_type_string,
+};
+
+/** Password setting */
+struct setting password_setting __setting = {
+ .name = "password",
+ .description = "Password",
+ .tag = DHCP_EB_PASSWORD,
+ .type = &setting_type_string,
+};
+
+/** Priority setting */
+struct setting priority_setting __setting = {
+ .name = "priority",
+ .description = "Priority of these settings",
+ .tag = DHCP_EB_PRIORITY,
+ .type = &setting_type_int8,
+};
diff --git a/gpxe/src/core/string.c b/gpxe/src/core/string.c
new file mode 100644
index 00000000..2e17bdcb
--- /dev/null
+++ b/gpxe/src/core/string.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * string handling functions
+ * based on linux/lib/string.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * - Added strsep() which will replace strtok() soon (because strsep() is
+ * reentrant and should be faster). Use only strsep() in new code, please.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+/* *** FROM string.c *** */
+
+#ifndef __HAVE_ARCH_STRCPY
+/**
+ * strcpy - Copy a %NUL terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ */
+char * strcpy(char * dest,const char *src)
+{
+ char *tmp = dest;
+
+ while ((*dest++ = *src++) != '\0')
+ /* nothing */;
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCPY
+/**
+ * strncpy - Copy a length-limited, %NUL-terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @count: The maximum number of bytes to copy
+ *
+ * Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
+ * However, the result is not %NUL-terminated if the source exceeds
+ * @count bytes.
+ */
+char * strncpy(char * dest,const char *src,size_t count)
+{
+ char *tmp = dest;
+
+ while (count-- && (*dest++ = *src++) != '\0')
+ /* nothing */;
+
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCAT
+/**
+ * strcat - Append one %NUL-terminated string to another
+ * @dest: The string to be appended to
+ * @src: The string to append to it
+ */
+char * strcat(char * dest, const char * src)
+{
+ char *tmp = dest;
+
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++) != '\0')
+ ;
+
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCMP
+/**
+ * strcmp - Compare two strings
+ * @cs: One string
+ * @ct: Another string
+ */
+int strcmp(const char * cs,const char * ct)
+{
+ register signed char __res;
+
+ while (1) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ break;
+ }
+
+ return __res;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCMP
+/**
+ * strncmp - Compare two length-limited strings
+ * @cs: One string
+ * @ct: Another string
+ * @count: The maximum number of bytes to compare
+ */
+int strncmp(const char * cs,const char * ct,size_t count)
+{
+ register signed char __res = 0;
+
+ while (count) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ break;
+ count--;
+ }
+
+ return __res;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCASECMP
+int strcasecmp(const char *a, const char *b)
+{
+ while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; }
+ return((*a & ~0x20) - (*b & ~0x20));
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCHR
+/**
+ * strchr - Find the first occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strchr(const char * s, int c)
+{
+ for(; *s != (char) c; ++s)
+ if (*s == '\0')
+ return NULL;
+ return (char *) s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRRCHR
+/**
+ * strrchr - Find the last occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strrchr(const char * s, int c)
+{
+ const char *p = s + strlen(s);
+ do {
+ if (*p == (char)c)
+ return (char *)p;
+ } while (--p >= s);
+ return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRLEN
+/**
+ * strlen - Find the length of a string
+ * @s: The string to be sized
+ */
+size_t strlen(const char * s)
+{
+ const char *sc;
+
+ for (sc = s; *sc != '\0'; ++sc)
+ /* nothing */;
+ return sc - s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNLEN
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char * s, size_t count)
+{
+ const char *sc;
+
+ for (sc = s; count-- && *sc != '\0'; ++sc)
+ /* nothing */;
+ return sc - s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMSET
+/**
+ * memset - Fill a region of memory with the given value
+ * @s: Pointer to the start of the area.
+ * @c: The byte to fill the area with
+ * @count: The size of the area.
+ *
+ * Do not use memset() to access IO space, use memset_io() instead.
+ */
+void * memset(void * s,int c,size_t count)
+{
+ char *xs = (char *) s;
+
+ while (count--)
+ *xs++ = c;
+
+ return s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCPY
+/**
+ * memcpy - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * You should not use this function to access IO space, use memcpy_toio()
+ * or memcpy_fromio() instead.
+ */
+void * memcpy(void * dest,const void *src,size_t count)
+{
+ char *tmp = (char *) dest, *s = (char *) src;
+
+ while (count--)
+ *tmp++ = *s++;
+
+ return dest;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMMOVE
+/**
+ * memmove - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * Unlike memcpy(), memmove() copes with overlapping areas.
+ */
+void * memmove(void * dest,const void *src,size_t count)
+{
+ char *tmp, *s;
+
+ if (dest <= src) {
+ tmp = (char *) dest;
+ s = (char *) src;
+ while (count--)
+ *tmp++ = *s++;
+ }
+ else {
+ tmp = (char *) dest + count;
+ s = (char *) src + count;
+ while (count--)
+ *--tmp = *--s;
+ }
+
+ return dest;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCMP
+/**
+ * memcmp - Compare two areas of memory
+ * @cs: One area of memory
+ * @ct: Another area of memory
+ * @count: The size of the area.
+ */
+int memcmp(const void * cs,const void * ct,size_t count)
+{
+ const unsigned char *su1, *su2;
+ int res = 0;
+
+ for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
+ if ((res = *su1 - *su2) != 0)
+ break;
+ return res;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSTR
+/**
+ * strstr - Find the first substring in a %NUL terminated string
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ */
+char * strstr(const char * s1,const char * s2)
+{
+ int l1, l2;
+
+ l2 = strlen(s2);
+ if (!l2)
+ return (char *) s1;
+ l1 = strlen(s1);
+ while (l1 >= l2) {
+ l1--;
+ if (!memcmp(s1,s2,l2))
+ return (char *) s1;
+ s1++;
+ }
+ return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCHR
+/**
+ * memchr - Find a character in an area of memory.
+ * @s: The memory area
+ * @c: The byte to search for
+ * @n: The size of the area.
+ *
+ * returns the address of the first occurrence of @c, or %NULL
+ * if @c is not found
+ */
+void * memchr(const void *s, int c, size_t n)
+{
+ const unsigned char *p = s;
+ while (n-- != 0) {
+ if ((unsigned char)c == *p++) {
+ return (void *)(p-1);
+ }
+ }
+ return NULL;
+}
+
+#endif
+
+char * strndup(const char *s, size_t n)
+{
+ size_t len = strlen(s);
+ char *new;
+
+ if (len>n)
+ len = n;
+ new = malloc(len+1);
+ if (new) {
+ new[len] = '\0';
+ memcpy(new,s,len);
+ }
+ return new;
+}
+
+char * strdup(const char *s) {
+ return strndup(s, ~((size_t)0));
+}
diff --git a/gpxe/src/core/stringextra.c b/gpxe/src/core/stringextra.c
new file mode 100644
index 00000000..c2be4fc4
--- /dev/null
+++ b/gpxe/src/core/stringextra.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * string handling functions
+ * based on linux/lib/string.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * - Added strsep() which will replace strtok() soon (because strsep() is
+ * reentrant and should be faster). Use only strsep() in new code, please.
+ */
+
+/*
+ * these are the standard string functions that are currently not used by
+ * any code in etherboot. put into a separate file to avoid linking them in
+ * with the rest of string.o
+ * if anything ever does want to use a function of these, consider moving
+ * the function in question back into string.c
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+/* *** FROM string.c *** */
+
+#ifndef __HAVE_ARCH_STRNICMP
+/**
+ * strnicmp - Case insensitive, length-limited string comparison
+ * @s1: One string
+ * @s2: The other string
+ * @len: the maximum number of characters to compare
+ */
+int strnicmp(const char *s1, const char *s2, size_t len)
+{
+ /* Yes, Virginia, it had better be unsigned */
+ unsigned char c1, c2;
+
+ c1 = 0; c2 = 0;
+ if (len) {
+ do {
+ c1 = *s1; c2 = *s2;
+ s1++; s2++;
+ if (!c1)
+ break;
+ if (!c2)
+ break;
+ if (c1 == c2)
+ continue;
+ c1 = tolower(c1);
+ c2 = tolower(c2);
+ if (c1 != c2)
+ break;
+ } while (--len);
+ }
+ return (int)c1 - (int)c2;
+}
+#endif
+
+char * ___strtok;
+
+#ifndef __HAVE_ARCH_STRNCAT
+/**
+ * strncat - Append a length-limited, %NUL-terminated string to another
+ * @dest: The string to be appended to
+ * @src: The string to append to it
+ * @count: The maximum numbers of bytes to copy
+ *
+ * Note that in contrast to strncpy, strncat ensures the result is
+ * terminated.
+ */
+char * strncat(char *dest, const char *src, size_t count)
+{
+ char *tmp = dest;
+
+ if (count) {
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++)) {
+ if (--count == 0) {
+ *dest = '\0';
+ break;
+ }
+ }
+ }
+
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSPN
+/**
+ * strspn - Calculate the length of the initial substring of @s which only
+ * contain letters in @accept
+ * @s: The string to be searched
+ * @accept: The string to search for
+ */
+size_t strspn(const char *s, const char *accept)
+{
+ const char *p;
+ const char *a;
+ size_t count = 0;
+
+ for (p = s; *p != '\0'; ++p) {
+ for (a = accept; *a != '\0'; ++a) {
+ if (*p == *a)
+ break;
+ }
+ if (*a == '\0')
+ return count;
+ ++count;
+ }
+
+ return count;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCSPN
+/**
+ * strcspn - Calculate the length of the initial substring of @s which only
+ * contain letters not in @reject
+ * @s: The string to be searched
+ * @accept: The string to search for
+ */
+size_t strcspn(const char *s, const char *reject)
+{
+ const char *p;
+ const char *r;
+ size_t count = 0;
+
+ for (p = s; *p != '\0'; ++p) {
+ for (r = reject; *r != '\0'; ++r) {
+ if (*p == *r)
+ return count;
+ }
+ ++count;
+ }
+
+ return count;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRPBRK
+/**
+ * strpbrk - Find the first occurrence of a set of characters
+ * @cs: The string to be searched
+ * @ct: The characters to search for
+ */
+char * strpbrk(const char * cs,const char * ct)
+{
+ const char *sc1,*sc2;
+
+ for( sc1 = cs; *sc1 != '\0'; ++sc1) {
+ for( sc2 = ct; *sc2 != '\0'; ++sc2) {
+ if (*sc1 == *sc2)
+ return (char *) sc1;
+ }
+ }
+ return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRTOK
+/**
+ * strtok - Split a string into tokens
+ * @s: The string to be searched
+ * @ct: The characters to search for
+ *
+ * WARNING: strtok is deprecated, use strsep instead.
+ */
+char * strtok(char * s,const char * ct)
+{
+ char *sbegin, *send;
+
+ sbegin = s ? s : ___strtok;
+ if (!sbegin) {
+ return NULL;
+ }
+ sbegin += strspn(sbegin,ct);
+ if (*sbegin == '\0') {
+ ___strtok = NULL;
+ return( NULL );
+ }
+ send = strpbrk( sbegin, ct);
+ if (send && *send != '\0')
+ *send++ = '\0';
+ ___strtok = send;
+ return (sbegin);
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSEP
+/**
+ * strsep - Split a string into tokens
+ * @s: The string to be searched
+ * @ct: The characters to search for
+ *
+ * strsep() updates @s to point after the token, ready for the next call.
+ *
+ * It returns empty tokens, too, behaving exactly like the libc function
+ * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
+ * Same semantics, slimmer shape. ;)
+ */
+char * strsep(char **s, const char *ct)
+{
+ char *sbegin = *s, *end;
+
+ if (sbegin == NULL)
+ return NULL;
+
+ end = strpbrk(sbegin, ct);
+ if (end)
+ *end++ = '\0';
+ *s = end;
+
+ return sbegin;
+}
+#endif
+
+#ifndef __HAVE_ARCH_BCOPY
+/**
+ * bcopy - Copy one area of memory to another
+ * @src: Where to copy from
+ * @dest: Where to copy to
+ * @count: The size of the area.
+ *
+ * Note that this is the same as memcpy(), with the arguments reversed.
+ * memcpy() is the standard, bcopy() is a legacy BSD function.
+ *
+ * You should not use this function to access IO space, use memcpy_toio()
+ * or memcpy_fromio() instead.
+ */
+char * bcopy(const char * src, char * dest, int count)
+{
+ return memmove(dest,src,count);
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMSCAN
+/**
+ * memscan - Find a character in an area of memory.
+ * @addr: The memory area
+ * @c: The byte to search for
+ * @size: The size of the area.
+ *
+ * returns the address of the first occurrence of @c, or 1 byte past
+ * the area if @c is not found
+ */
+void * memscan(const void * addr, int c, size_t size)
+{
+ unsigned char * p = (unsigned char *) addr;
+
+ while (size) {
+ if (*p == c)
+ return (void *) p;
+ p++;
+ size--;
+ }
+ return (void *) p;
+}
+#endif
diff --git a/gpxe/src/core/timer.c b/gpxe/src/core/timer.c
new file mode 100644
index 00000000..4e047ea7
--- /dev/null
+++ b/gpxe/src/core/timer.c
@@ -0,0 +1,113 @@
+/*
+ * core/timer.c
+ *
+ * Copyright (C) 2007 Alexey Zaytsev <alexey.zaytsev@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <assert.h>
+#include <gpxe/timer.h>
+
+static struct timer ts_table[0]
+ __table_start ( struct timer, timers );
+static struct timer ts_table_end[0]
+ __table_end ( struct timer, timers );
+
+/*
+ * This function may be used in custom timer driver.
+ *
+ * This udelay implementation works well if you've got a
+ * fast currticks().
+ */
+void generic_currticks_udelay ( unsigned int usecs ) {
+ tick_t start;
+ tick_t elapsed;
+
+ start = currticks();
+ do {
+ /* xxx: Relax the cpu some way. */
+ elapsed = ( currticks() - start );
+ } while ( elapsed < usecs );
+}
+
+/**
+ * Identify timer source
+ *
+ * @ret timer Timer source
+ */
+static struct timer * timer ( void ) {
+ static struct timer *ts = NULL;
+
+ /* If we have a timer, use it */
+ if ( ts )
+ return ts;
+
+ /* Scan for a usable timer */
+ for ( ts = ts_table ; ts < ts_table_end ; ts++ ) {
+ if ( ts->init() == 0 )
+ return ts;
+ }
+
+ /* No timer found; we cannot continue */
+ assert ( 0 );
+ while ( 1 ) {};
+}
+
+/**
+ * Read current time
+ *
+ * @ret ticks Current time, in ticks
+ */
+tick_t currticks ( void ) {
+ tick_t ct;
+
+ ct = timer()->currticks();
+ DBG ( "currticks: %ld.%06ld seconds\n",
+ ct / USECS_IN_SEC, ct % USECS_IN_SEC );
+
+ return ct;
+}
+
+/**
+ * Delay
+ *
+ * @v usecs Time to delay, in microseconds
+ */
+void udelay ( unsigned int usecs ) {
+ timer()->udelay ( usecs );
+}
+
+/**
+ * Delay
+ *
+ * @v msecs Time to delay, in milliseconds
+ */
+void mdelay ( unsigned int msecs ) {
+ while ( msecs-- )
+ udelay ( USECS_IN_MSEC );
+}
+
+/**
+ * Delay
+ *
+ * @v secs Time to delay, in seconds
+ */
+unsigned int sleep ( unsigned int secs ) {
+ while ( secs-- )
+ mdelay ( MSECS_IN_SEC );
+ return 0;
+}
diff --git a/gpxe/src/core/uri.c b/gpxe/src/core/uri.c
new file mode 100644
index 00000000..3b3cf85b
--- /dev/null
+++ b/gpxe/src/core/uri.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/** @file
+ *
+ * Uniform Resource Identifiers
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <gpxe/vsprintf.h>
+#include <gpxe/uri.h>
+
+/**
+ * Dump URI for debugging
+ *
+ * @v uri URI
+ */
+static void dump_uri ( struct uri *uri ) {
+ if ( ! uri )
+ return;
+ if ( uri->scheme )
+ DBG ( " scheme \"%s\"", uri->scheme );
+ if ( uri->opaque )
+ DBG ( " opaque \"%s\"", uri->opaque );
+ if ( uri->user )
+ DBG ( " user \"%s\"", uri->user );
+ if ( uri->password )
+ DBG ( " password \"%s\"", uri->password );
+ if ( uri->host )
+ DBG ( " host \"%s\"", uri->host );
+ if ( uri->port )
+ DBG ( " port \"%s\"", uri->port );
+ if ( uri->path )
+ DBG ( " path \"%s\"", uri->path );
+ if ( uri->query )
+ DBG ( " query \"%s\"", uri->query );
+ if ( uri->fragment )
+ DBG ( " fragment \"%s\"", uri->fragment );
+}
+
+/**
+ * Parse URI
+ *
+ * @v uri_string URI as a string
+ * @ret uri URI
+ *
+ * Splits a URI into its component parts. The return URI structure is
+ * dynamically allocated and must eventually be freed by calling
+ * uri_put().
+ */
+struct uri * parse_uri ( const char *uri_string ) {
+ struct uri *uri;
+ char *raw;
+ char *tmp;
+ char *path = NULL;
+ char *authority = NULL;
+ size_t raw_len;
+
+ /* Allocate space for URI struct and a copy of the string */
+ raw_len = ( strlen ( uri_string ) + 1 /* NUL */ );
+ uri = zalloc ( sizeof ( *uri ) + raw_len );
+ if ( ! uri )
+ return NULL;
+ raw = ( ( ( char * ) uri ) + sizeof ( *uri ) );
+
+ /* Zero URI struct and copy in the raw string */
+ memcpy ( raw, uri_string, raw_len );
+
+ /* Start by chopping off the fragment, if it exists */
+ if ( ( tmp = strchr ( raw, '#' ) ) ) {
+ *(tmp++) = '\0';
+ uri->fragment = tmp;
+ }
+
+ /* Identify absolute/relative URI */
+ if ( ( tmp = strchr ( raw, ':' ) ) ) {
+ /* Absolute URI: identify hierarchical/opaque */
+ uri->scheme = raw;
+ *(tmp++) = '\0';
+ if ( *tmp == '/' ) {
+ /* Absolute URI with hierarchical part */
+ path = tmp;
+ } else {
+ /* Absolute URI with opaque part */
+ uri->opaque = tmp;
+ }
+ } else {
+ /* Relative URI */
+ path = raw;
+ }
+
+ /* If we don't have a path (i.e. we have an absolute URI with
+ * an opaque portion, we're already finished processing
+ */
+ if ( ! path )
+ goto done;
+
+ /* Chop off the query, if it exists */
+ if ( ( tmp = strchr ( path, '?' ) ) ) {
+ *(tmp++) = '\0';
+ uri->query = tmp;
+ }
+
+ /* Identify net/absolute/relative path */
+ if ( strncmp ( path, "//", 2 ) == 0 ) {
+ /* Net path. If this is terminated by the first '/'
+ * of an absolute path, then we have no space for a
+ * terminator after the authority field, so shuffle
+ * the authority down by one byte, overwriting one of
+ * the two slashes.
+ */
+ authority = ( path + 2 );
+ if ( ( tmp = strchr ( authority, '/' ) ) ) {
+ /* Shuffle down */
+ uri->path = tmp;
+ memmove ( ( authority - 1 ), authority,
+ ( tmp - authority ) );
+ authority--;
+ *(--tmp) = '\0';
+ }
+ } else {
+ /* Absolute/relative path */
+ uri->path = path;
+ }
+
+ /* Split authority into user[:password] and host[:port] portions */
+ if ( ( tmp = strchr ( authority, '@' ) ) ) {
+ /* Has user[:password] */
+ *(tmp++) = '\0';
+ uri->host = tmp;
+ uri->user = authority;
+ if ( ( tmp = strchr ( authority, ':' ) ) ) {
+ /* Has password */
+ *(tmp++) = '\0';
+ uri->password = tmp;
+ }
+ } else {
+ /* No user:password */
+ uri->host = authority;
+ }
+
+ /* Split host into host[:port] */
+ if ( ( tmp = strchr ( uri->host, ':' ) ) ) {
+ *(tmp++) = '\0';
+ uri->port = tmp;
+ }
+
+ done:
+ DBG ( "URI \"%s\" split into", uri_string );
+ dump_uri ( uri );
+ DBG ( "\n" );
+
+ return uri;
+}
+
+/**
+ * Get port from URI
+ *
+ * @v uri URI, or NULL
+ * @v default_port Default port to use if none specified in URI
+ * @ret port Port
+ */
+unsigned int uri_port ( struct uri *uri, unsigned int default_port ) {
+ if ( ( ! uri ) || ( ! uri->port ) )
+ return default_port;
+ return ( strtoul ( uri->port, NULL, 0 ) );
+}
+
+/**
+ * Unparse URI
+ *
+ * @v buf Buffer to fill with URI string
+ * @v size Size of buffer
+ * @v uri URI to write into buffer, or NULL
+ * @ret len Length of URI string
+ */
+int unparse_uri ( char *buf, size_t size, struct uri *uri ) {
+ int used = 0;
+
+ DBG ( "URI unparsing" );
+ dump_uri ( uri );
+ DBG ( "\n" );
+
+ /* Special-case NULL URI */
+ if ( ! uri ) {
+ if ( size )
+ buf[0] = '\0';
+ return 0;
+ }
+
+ /* Special-case opaque URIs */
+ if ( uri->opaque ) {
+ return ssnprintf ( ( buf + used ), ( size - used ),
+ "%s:%s", uri->scheme, uri->opaque );
+ }
+
+ /* scheme:// */
+ if ( uri->scheme ) {
+ used += ssnprintf ( ( buf + used ), ( size - used ),
+ "%s://", uri->scheme );
+ }
+
+ /* [user[:password]@]host[:port] */
+ if ( uri->host ) {
+ if ( uri->user ) {
+ used += ssnprintf ( ( buf + used ), ( size - used ),
+ "%s", uri->user );
+ if ( uri->password ) {
+ used += ssnprintf ( ( buf + used ),
+ ( size - used ),
+ ":%s", uri->password );
+ }
+ used += ssnprintf ( ( buf + used ), ( size - used ),
+ "@" );
+ }
+ used += ssnprintf ( ( buf + used ), ( size - used ), "%s",
+ uri->host );
+ if ( uri->port ) {
+ used += ssnprintf ( ( buf + used ), ( size - used ),
+ ":%s", uri->port );
+ }
+ }
+
+ /* /path */
+ if ( uri->path ) {
+ used += ssnprintf ( ( buf + used ), ( size - used ),
+ "%s", uri->path );
+ }
+
+ /* ?query */
+ if ( uri->query ) {
+ used += ssnprintf ( ( buf + used ), ( size - used ),
+ "?%s", uri->query );
+ }
+
+ /* #fragment */
+ if ( uri->fragment ) {
+ used += ssnprintf ( ( buf + used ), ( size - used ),
+ "#%s", uri->fragment );
+ }
+
+ return used;
+}
+
+/**
+ * Duplicate URI
+ *
+ * @v uri URI
+ * @ret uri Duplicate URI
+ *
+ * Creates a modifiable copy of a URI.
+ */
+struct uri * uri_dup ( struct uri *uri ) {
+ size_t len = ( unparse_uri ( NULL, 0, uri ) + 1 );
+ char buf[len];
+
+ unparse_uri ( buf, len, uri );
+ return parse_uri ( buf );
+}
+
+/**
+ * Resolve base+relative path
+ *
+ * @v base_uri Base path
+ * @v relative_uri Relative path
+ * @ret resolved_uri Resolved path
+ *
+ * Takes a base path (e.g. "/var/lib/tftpboot/vmlinuz" and a relative
+ * path (e.g. "initrd.gz") and produces a new path
+ * (e.g. "/var/lib/tftpboot/initrd.gz"). Note that any non-directory
+ * portion of the base path will automatically be stripped; this
+ * matches the semantics used when resolving the path component of
+ * URIs.
+ */
+char * resolve_path ( const char *base_path,
+ const char *relative_path ) {
+ size_t base_len = ( strlen ( base_path ) + 1 );
+ char base_path_copy[base_len];
+ char *base_tmp = base_path_copy;
+ char *resolved;
+
+ /* If relative path is absolute, just re-use it */
+ if ( relative_path[0] == '/' )
+ return strdup ( relative_path );
+
+ /* Create modifiable copy of path for dirname() */
+ memcpy ( base_tmp, base_path, base_len );
+ base_tmp = dirname ( base_tmp );
+
+ /* Process "./" and "../" elements */
+ while ( *relative_path == '.' ) {
+ relative_path++;
+ if ( *relative_path == 0 ) {
+ /* Do nothing */
+ } else if ( *relative_path == '/' ) {
+ relative_path++;
+ } else if ( *relative_path == '.' ) {
+ relative_path++;
+ if ( *relative_path == 0 ) {
+ base_tmp = dirname ( base_tmp );
+ } else if ( *relative_path == '/' ) {
+ base_tmp = dirname ( base_tmp );
+ relative_path++;
+ } else {
+ relative_path -= 2;
+ break;
+ }
+ } else {
+ relative_path--;
+ break;
+ }
+ }
+
+ /* Create and return new path */
+ if ( asprintf ( &resolved, "%s%s%s", base_tmp,
+ ( ( base_tmp[ strlen ( base_tmp ) - 1 ] == '/' ) ?
+ "" : "/" ), relative_path ) < 0 )
+ return NULL;
+
+ return resolved;
+}
+
+/**
+ * Resolve base+relative URI
+ *
+ * @v base_uri Base URI, or NULL
+ * @v relative_uri Relative URI
+ * @ret resolved_uri Resolved URI
+ *
+ * Takes a base URI (e.g. "http://etherboot.org/kernels/vmlinuz" and a
+ * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI
+ * (e.g. "http://etherboot.org/initrds/initrd.gz").
+ */
+struct uri * resolve_uri ( struct uri *base_uri,
+ struct uri *relative_uri ) {
+ struct uri tmp_uri;
+ char *tmp_path = NULL;
+ struct uri *new_uri;
+
+ /* If relative URI is absolute, just re-use it */
+ if ( uri_is_absolute ( relative_uri ) || ( ! base_uri ) )
+ return uri_get ( relative_uri );
+
+ /* Mangle URI */
+ memcpy ( &tmp_uri, base_uri, sizeof ( tmp_uri ) );
+ if ( relative_uri->path ) {
+ tmp_path = resolve_path ( ( base_uri->path ?
+ base_uri->path : "/" ),
+ relative_uri->path );
+ tmp_uri.path = tmp_path;
+ tmp_uri.query = relative_uri->query;
+ tmp_uri.fragment = relative_uri->fragment;
+ } else if ( relative_uri->query ) {
+ tmp_uri.query = relative_uri->query;
+ tmp_uri.fragment = relative_uri->fragment;
+ } else if ( relative_uri->fragment ) {
+ tmp_uri.fragment = relative_uri->fragment;
+ }
+
+ /* Create demangled URI */
+ new_uri = uri_dup ( &tmp_uri );
+ free ( tmp_path );
+ return new_uri;
+}
diff --git a/gpxe/src/core/uuid.c b/gpxe/src/core/uuid.c
new file mode 100644
index 00000000..dae26c16
--- /dev/null
+++ b/gpxe/src/core/uuid.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <byteswap.h>
+#include <gpxe/uuid.h>
+
+/** @file
+ *
+ * Universally unique IDs
+ *
+ */
+
+/**
+ * Convert UUID to printable string
+ *
+ * @v uuid UUID
+ * @ret string UUID in canonical form
+ */
+char * uuid_ntoa ( union uuid *uuid ) {
+ static char buf[37]; /* "00000000-0000-0000-0000-000000000000" */
+
+ sprintf ( buf, "%08lx-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
+ le32_to_cpu ( uuid->canonical.a ),
+ le16_to_cpu ( uuid->canonical.b ),
+ le16_to_cpu ( uuid->canonical.c ),
+ be16_to_cpu ( uuid->canonical.d ),
+ uuid->canonical.e[0], uuid->canonical.e[1],
+ uuid->canonical.e[2], uuid->canonical.e[3],
+ uuid->canonical.e[4], uuid->canonical.e[5] );
+ return buf;
+}
diff --git a/gpxe/src/core/vsprintf.c b/gpxe/src/core/vsprintf.c
new file mode 100644
index 00000000..4457fe4f
--- /dev/null
+++ b/gpxe/src/core/vsprintf.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <console.h>
+#include <errno.h>
+#include <gpxe/vsprintf.h>
+
+/** @file */
+
+#define CHAR_LEN 0 /**< "hh" length modifier */
+#define SHORT_LEN 1 /**< "h" length modifier */
+#define INT_LEN 2 /**< no length modifier */
+#define LONG_LEN 3 /**< "l" length modifier */
+#define LONGLONG_LEN 4 /**< "ll" length modifier */
+#define SIZE_T_LEN 5 /**< "z" length modifier */
+
+static uint8_t type_sizes[] = {
+ [CHAR_LEN] = sizeof ( char ),
+ [SHORT_LEN] = sizeof ( short ),
+ [INT_LEN] = sizeof ( int ),
+ [LONG_LEN] = sizeof ( long ),
+ [LONGLONG_LEN] = sizeof ( long long ),
+ [SIZE_T_LEN] = sizeof ( size_t ),
+};
+
+/**
+ * Use lower-case for hexadecimal digits
+ *
+ * Note that this value is set to 0x20 since that makes for very
+ * efficient calculations. (Bitwise-ORing with @c LCASE converts to a
+ * lower-case character, for example.)
+ */
+#define LCASE 0x20
+
+/**
+ * Use "alternate form"
+ *
+ * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
+ * the number.
+ */
+#define ALT_FORM 0x02
+
+/**
+ * Format a hexadecimal number
+ *
+ * @v end End of buffer to contain number
+ * @v num Number to format
+ * @v width Minimum field width
+ * @ret ptr End of buffer
+ *
+ * Fills a buffer in reverse order with a formatted hexadecimal
+ * number. The number will be zero-padded to the specified width.
+ * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
+ * set.
+ *
+ * There must be enough space in the buffer to contain the largest
+ * number that this function can format.
+ */
+static char * format_hex ( char *end, unsigned long long num, int width,
+ int flags ) {
+ char *ptr = end;
+ int case_mod;
+
+ /* Generate the number */
+ case_mod = flags & LCASE;
+ do {
+ *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
+ num >>= 4;
+ } while ( num );
+
+ /* Zero-pad to width */
+ while ( ( end - ptr ) < width )
+ *(--ptr) = '0';
+
+ /* Add "0x" or "0X" if alternate form specified */
+ if ( flags & ALT_FORM ) {
+ *(--ptr) = 'X' | case_mod;
+ *(--ptr) = '0';
+ }
+
+ return ptr;
+}
+
+/**
+ * Format a decimal number
+ *
+ * @v end End of buffer to contain number
+ * @v num Number to format
+ * @v width Minimum field width
+ * @ret ptr End of buffer
+ *
+ * Fills a buffer in reverse order with a formatted decimal number.
+ * The number will be space-padded to the specified width.
+ *
+ * There must be enough space in the buffer to contain the largest
+ * number that this function can format.
+ */
+static char * format_decimal ( char *end, signed long num, int width ) {
+ char *ptr = end;
+ int negative = 0;
+
+ /* Generate the number */
+ if ( num < 0 ) {
+ negative = 1;
+ num = -num;
+ }
+ do {
+ *(--ptr) = '0' + ( num % 10 );
+ num /= 10;
+ } while ( num );
+
+ /* Add "-" if necessary */
+ if ( negative )
+ *(--ptr) = '-';
+
+ /* Space-pad to width */
+ while ( ( end - ptr ) < width )
+ *(--ptr) = ' ';
+
+ return ptr;
+}
+
+/**
+ * Print character via a printf context
+ *
+ * @v ctx Context
+ * @v c Character
+ *
+ * Call's the printf_context::handler() method and increments
+ * printf_context::len.
+ */
+static inline void cputchar ( struct printf_context *ctx, unsigned int c ) {
+ ctx->handler ( ctx, c );
+ ++ctx->len;
+}
+
+/**
+ * Write a formatted string to a printf context
+ *
+ * @v ctx Context
+ * @v fmt Format string
+ * @v args Arguments corresponding to the format string
+ * @ret len Length of formatted string
+ */
+size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
+ int flags;
+ int width;
+ uint8_t *length;
+ char *ptr;
+ char tmp_buf[32]; /* 32 is enough for all numerical formats.
+ * Insane width fields could overflow this buffer. */
+
+ /* Initialise context */
+ ctx->len = 0;
+
+ for ( ; *fmt ; fmt++ ) {
+ /* Pass through ordinary characters */
+ if ( *fmt != '%' ) {
+ cputchar ( ctx, *fmt );
+ continue;
+ }
+ fmt++;
+ /* Process flag characters */
+ flags = 0;
+ for ( ; ; fmt++ ) {
+ if ( *fmt == '#' ) {
+ flags |= ALT_FORM;
+ } else if ( *fmt == '0' ) {
+ /* We always 0-pad hex and space-pad decimal */
+ } else {
+ /* End of flag characters */
+ break;
+ }
+ }
+ /* Process field width */
+ width = 0;
+ for ( ; ; fmt++ ) {
+ if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) {
+ width = ( width * 10 ) + ( *fmt - '0' );
+ } else {
+ break;
+ }
+ }
+ /* We don't do floating point */
+ /* Process length modifier */
+ length = &type_sizes[INT_LEN];
+ for ( ; ; fmt++ ) {
+ if ( *fmt == 'h' ) {
+ length--;
+ } else if ( *fmt == 'l' ) {
+ length++;
+ } else if ( *fmt == 'z' ) {
+ length = &type_sizes[SIZE_T_LEN];
+ } else {
+ break;
+ }
+ }
+ /* Process conversion specifier */
+ ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
+ *ptr = '\0';
+ if ( *fmt == 'c' ) {
+ cputchar ( ctx, va_arg ( args, unsigned int ) );
+ } else if ( *fmt == 's' ) {
+ ptr = va_arg ( args, char * );
+ if ( ! ptr )
+ ptr = "<NULL>";
+ } else if ( *fmt == 'p' ) {
+ intptr_t ptrval;
+
+ ptrval = ( intptr_t ) va_arg ( args, void * );
+ ptr = format_hex ( ptr, ptrval, width,
+ ( ALT_FORM | LCASE ) );
+ } else if ( ( *fmt & ~0x20 ) == 'X' ) {
+ unsigned long long hex;
+
+ flags |= ( *fmt & 0x20 ); /* LCASE */
+ if ( *length >= sizeof ( unsigned long long ) ) {
+ hex = va_arg ( args, unsigned long long );
+ } else if ( *length >= sizeof ( unsigned long ) ) {
+ hex = va_arg ( args, unsigned long );
+ } else {
+ hex = va_arg ( args, unsigned int );
+ }
+ ptr = format_hex ( ptr, hex, width, flags );
+ } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
+ signed long decimal;
+
+ if ( *length >= sizeof ( signed long ) ) {
+ decimal = va_arg ( args, signed long );
+ } else {
+ decimal = va_arg ( args, signed int );
+ }
+ ptr = format_decimal ( ptr, decimal, width );
+ } else {
+ *(--ptr) = *fmt;
+ }
+ /* Write out conversion result */
+ for ( ; *ptr ; ptr++ ) {
+ cputchar ( ctx, *ptr );
+ }
+ }
+
+ return ctx->len;
+}
+
+/** Context used by vsnprintf() and friends */
+struct sputc_context {
+ struct printf_context ctx;
+ /** Buffer for formatted string (used by printf_sputc()) */
+ char *buf;
+ /** Buffer length (used by printf_sputc()) */
+ size_t max_len;
+};
+
+/**
+ * Write character to buffer
+ *
+ * @v ctx Context
+ * @v c Character
+ */
+static void printf_sputc ( struct printf_context *ctx, unsigned int c ) {
+ struct sputc_context * sctx =
+ container_of ( ctx, struct sputc_context, ctx );
+
+ if ( ctx->len < sctx->max_len )
+ sctx->buf[ctx->len] = c;
+}
+
+/**
+ * Write a formatted string to a buffer
+ *
+ * @v buf Buffer into which to write the string
+ * @v size Size of buffer
+ * @v fmt Format string
+ * @v args Arguments corresponding to the format string
+ * @ret len Length of formatted string
+ *
+ * If the buffer is too small to contain the string, the returned
+ * length is the length that would have been written had enough space
+ * been available.
+ */
+int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) {
+ struct sputc_context sctx;
+ size_t len;
+ size_t end;
+
+ /* Hand off to vcprintf */
+ sctx.ctx.handler = printf_sputc;
+ sctx.buf = buf;
+ sctx.max_len = size;
+ len = vcprintf ( &sctx.ctx, fmt, args );
+
+ /* Add trailing NUL */
+ if ( size ) {
+ end = size - 1;
+ if ( len < end )
+ end = len;
+ buf[end] = '\0';
+ }
+
+ return len;
+}
+
+/**
+ * Write a formatted string to a buffer
+ *
+ * @v buf Buffer into which to write the string
+ * @v size Size of buffer
+ * @v fmt Format string
+ * @v ... Arguments corresponding to the format string
+ * @ret len Length of formatted string
+ */
+int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
+ va_list args;
+ int i;
+
+ va_start ( args, fmt );
+ i = vsnprintf ( buf, size, fmt, args );
+ va_end ( args );
+ return i;
+}
+
+/**
+ * Version of vsnprintf() that accepts a signed buffer size
+ *
+ * @v buf Buffer into which to write the string
+ * @v size Size of buffer
+ * @v fmt Format string
+ * @v args Arguments corresponding to the format string
+ * @ret len Length of formatted string
+ */
+int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) {
+
+ /* Treat negative buffer size as zero buffer size */
+ if ( ssize < 0 )
+ ssize = 0;
+
+ /* Hand off to vsnprintf */
+ return vsnprintf ( buf, ssize, fmt, args );
+}
+
+/**
+ * Version of vsnprintf() that accepts a signed buffer size
+ *
+ * @v buf Buffer into which to write the string
+ * @v size Size of buffer
+ * @v fmt Format string
+ * @v ... Arguments corresponding to the format string
+ * @ret len Length of formatted string
+ */
+int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
+ va_list args;
+ int len;
+
+ /* Hand off to vssnprintf */
+ va_start ( args, fmt );
+ len = vssnprintf ( buf, ssize, fmt, args );
+ va_end ( args );
+ return len;
+}
+
+/**
+ * Write character to console
+ *
+ * @v ctx Context
+ * @v c Character
+ */
+static void printf_putchar ( struct printf_context *ctx __unused,
+ unsigned int c ) {
+ putchar ( c );
+}
+
+/**
+ * Write a formatted string to the console
+ *
+ * @v fmt Format string
+ * @v args Arguments corresponding to the format string
+ * @ret len Length of formatted string
+ */
+int vprintf ( const char *fmt, va_list args ) {
+ struct printf_context ctx;
+
+ /* Hand off to vcprintf */
+ ctx.handler = printf_putchar;
+ return vcprintf ( &ctx, fmt, args );
+}
+
+/**
+ * Write a formatted string to the console.
+ *
+ * @v fmt Format string
+ * @v ... Arguments corresponding to the format string
+ * @ret len Length of formatted string
+ */
+int printf ( const char *fmt, ... ) {
+ va_list args;
+ int i;
+
+ va_start ( args, fmt );
+ i = vprintf ( fmt, args );
+ va_end ( args );
+ return i;
+}
diff --git a/gpxe/src/core/xfer.c b/gpxe/src/core/xfer.c
new file mode 100644
index 00000000..14c77d64
--- /dev/null
+++ b/gpxe/src/core/xfer.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <gpxe/xfer.h>
+
+/** @file
+ *
+ * Data transfer interfaces
+ *
+ */
+
+/**
+ * Close data transfer interface
+ *
+ * @v xfer Data transfer interface
+ * @v rc Reason for close
+ */
+void xfer_close ( struct xfer_interface *xfer, int rc ) {
+ struct xfer_interface *dest = xfer_get_dest ( xfer );
+
+ DBGC ( xfer, "XFER %p->%p close\n", xfer, dest );
+
+ xfer_unplug ( xfer );
+ dest->op->close ( dest, rc );
+ xfer_put ( dest );
+}
+
+/**
+ * Send redirection event
+ *
+ * @v xfer Data transfer interface
+ * @v type New location type
+ * @v args Remaining arguments depend upon location type
+ * @ret rc Return status code
+ */
+int xfer_vredirect ( struct xfer_interface *xfer, int type, va_list args ) {
+ struct xfer_interface *dest = xfer_get_dest ( xfer );
+ int rc;
+
+ DBGC ( xfer, "XFER %p->%p redirect\n", xfer, dest );
+
+ rc = dest->op->vredirect ( dest, type, args );
+
+ if ( rc != 0 ) {
+ DBGC ( xfer, "XFER %p<-%p redirect: %s\n", xfer, dest,
+ strerror ( rc ) );
+ }
+ xfer_put ( dest );
+ return rc;
+}
+
+/**
+ * Send redirection event
+ *
+ * @v xfer Data transfer interface
+ * @v type New location type
+ * @v ... Remaining arguments depend upon location type
+ * @ret rc Return status code
+ */
+int xfer_redirect ( struct xfer_interface *xfer, int type, ... ) {
+ va_list args;
+ int rc;
+
+ va_start ( args, type );
+ rc = xfer_vredirect ( xfer, type, args );
+ va_end ( args );
+ return rc;
+}
+
+/**
+ * Check flow control window
+ *
+ * @v xfer Data transfer interface
+ * @ret len Length of window
+ */
+size_t xfer_window ( struct xfer_interface *xfer ) {
+ struct xfer_interface *dest = xfer_get_dest ( xfer );
+ size_t len;
+
+ len = dest->op->window ( dest );
+
+ xfer_put ( dest );
+ return len;
+}
+
+/**
+ * Allocate I/O buffer
+ *
+ * @v xfer Data transfer interface
+ * @v len I/O buffer payload length
+ * @ret iobuf I/O buffer
+ */
+struct io_buffer * xfer_alloc_iob ( struct xfer_interface *xfer, size_t len ) {
+ struct xfer_interface *dest = xfer_get_dest ( xfer );
+ struct io_buffer *iobuf;
+
+ DBGC ( xfer, "XFER %p->%p alloc_iob %zd\n", xfer, dest, len );
+
+ iobuf = dest->op->alloc_iob ( dest, len );
+
+ if ( ! iobuf ) {
+ DBGC ( xfer, "XFER %p<-%p alloc_iob failed\n", xfer, dest );
+ }
+ xfer_put ( dest );
+ return iobuf;
+}
+
+/**
+ * Deliver datagram as I/O buffer with metadata
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf Datagram I/O buffer
+ * @v meta Data transfer metadata
+ * @ret rc Return status code
+ */
+int xfer_deliver_iob_meta ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct xfer_interface *dest = xfer_get_dest ( xfer );
+ int rc;
+
+ DBGC ( xfer, "XFER %p->%p deliver_iob %zd\n", xfer, dest,
+ iob_len ( iobuf ) );
+
+ rc = dest->op->deliver_iob ( dest, iobuf, meta );
+
+ if ( rc != 0 ) {
+ DBGC ( xfer, "XFER %p<-%p deliver_iob: %s\n", xfer, dest,
+ strerror ( rc ) );
+ }
+ xfer_put ( dest );
+ return rc;
+}
+
+/**
+ * Deliver datagram as I/O buffer with metadata
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf Datagram I/O buffer
+ * @ret rc Return status code
+ */
+int xfer_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf ) {
+ static struct xfer_metadata dummy_metadata;
+ return xfer_deliver_iob_meta ( xfer, iobuf, &dummy_metadata );
+}
+
+/**
+ * Deliver datagram as raw data
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf Datagram I/O buffer
+ * @ret rc Return status code
+ */
+int xfer_deliver_raw ( struct xfer_interface *xfer,
+ const void *data, size_t len ) {
+ struct xfer_interface *dest = xfer_get_dest ( xfer );
+ int rc;
+
+ DBGC ( xfer, "XFER %p->%p deliver_raw %p+%zd\n", xfer, dest,
+ data, len );
+
+ rc = dest->op->deliver_raw ( dest, data, len );
+
+ if ( rc != 0 ) {
+ DBGC ( xfer, "XFER %p<-%p deliver_raw: %s\n", xfer, dest,
+ strerror ( rc ) );
+ }
+ xfer_put ( dest );
+ return rc;
+}
+
+/**
+ * Deliver formatted string
+ *
+ * @v xfer Data transfer interface
+ * @v format Format string
+ * @v args Arguments corresponding to the format string
+ * @ret rc Return status code
+ */
+int xfer_vprintf ( struct xfer_interface *xfer, const char *format,
+ va_list args ) {
+ size_t len;
+ va_list args_tmp;
+
+ va_copy ( args_tmp, args );
+ len = vsnprintf ( NULL, 0, format, args );
+ {
+ char buf[len + 1];
+ vsnprintf ( buf, sizeof ( buf ), format, args_tmp );
+ va_end ( args_tmp );
+ return xfer_deliver_raw ( xfer, buf, len );
+ }
+}
+
+/**
+ * Deliver formatted string
+ *
+ * @v xfer Data transfer interface
+ * @v format Format string
+ * @v ... Arguments corresponding to the format string
+ * @ret rc Return status code
+ */
+int xfer_printf ( struct xfer_interface *xfer, const char *format, ... ) {
+ va_list args;
+ int rc;
+
+ va_start ( args, format );
+ rc = xfer_vprintf ( xfer, format, args );
+ va_end ( args );
+ return rc;
+}
+
+/**
+ * Seek to position
+ *
+ * @v xfer Data transfer interface
+ * @v offset Offset to new position
+ * @v whence Basis for new position
+ * @ret rc Return status code
+ */
+int xfer_seek ( struct xfer_interface *xfer, off_t offset, int whence ) {
+ struct io_buffer *iobuf;
+ struct xfer_metadata meta = {
+ .offset = offset,
+ .whence = whence,
+ };
+
+ DBGC ( xfer, "XFER %p seek %s+%ld\n", xfer,
+ whence_text ( whence ), offset );
+
+ /* Allocate and send a zero-length data buffer */
+ iobuf = xfer_alloc_iob ( xfer, 0 );
+ if ( ! iobuf )
+ return -ENOMEM;
+ return xfer_deliver_iob_meta ( xfer, iobuf, &meta );
+}
+
+/****************************************************************************
+ *
+ * Helper methods
+ *
+ * These functions are designed to be used as methods in the
+ * xfer_interface_operations table.
+ *
+ */
+
+/**
+ * Ignore close() event
+ *
+ * @v xfer Data transfer interface
+ * @v rc Reason for close
+ */
+void ignore_xfer_close ( struct xfer_interface *xfer __unused,
+ int rc __unused ) {
+ /* Nothing to do */
+}
+
+/**
+ * Ignore vredirect() event
+ *
+ * @v xfer Data transfer interface
+ * @v type New location type
+ * @v args Remaining arguments depend upon location type
+ * @ret rc Return status code
+ */
+int ignore_xfer_vredirect ( struct xfer_interface *xfer __unused,
+ int type __unused, va_list args __unused ) {
+ return 0;
+}
+
+/**
+ * Unlimited flow control window
+ *
+ * @v xfer Data transfer interface
+ * @ret len Length of window
+ *
+ * This handler indicates that the interface is always ready to accept
+ * data.
+ */
+size_t unlimited_xfer_window ( struct xfer_interface *xfer __unused ) {
+ return ~( ( size_t ) 0 );
+}
+
+/**
+ * No flow control window
+ *
+ * @v xfer Data transfer interface
+ * @ret len Length of window
+ *
+ * This handler indicates that the interface is never ready to accept
+ * data.
+ */
+size_t no_xfer_window ( struct xfer_interface *xfer __unused ) {
+ return 0;
+}
+
+/**
+ * Allocate I/O buffer
+ *
+ * @v xfer Data transfer interface
+ * @v len I/O buffer payload length
+ * @ret iobuf I/O buffer
+ */
+struct io_buffer *
+default_xfer_alloc_iob ( struct xfer_interface *xfer __unused, size_t len ) {
+ return alloc_iob ( len );
+}
+
+/**
+ * Deliver datagram as raw data
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf Datagram I/O buffer
+ * @v meta Data transfer metadata
+ * @ret rc Return status code
+ *
+ * This function is intended to be used as the deliver() method for
+ * data transfer interfaces that prefer to handle raw data.
+ */
+int xfer_deliver_as_raw ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta __unused ) {
+ int rc;
+
+ rc = xfer->op->deliver_raw ( xfer, iobuf->data, iob_len ( iobuf ) );
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * Deliver datagram as I/O buffer
+ *
+ * @v xfer Data transfer interface
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ *
+ * This function is intended to be used as the deliver_raw() method
+ * for data transfer interfaces that prefer to handle I/O buffers.
+ */
+int xfer_deliver_as_iob ( struct xfer_interface *xfer,
+ const void *data, size_t len ) {
+ struct io_buffer *iobuf;
+
+ iobuf = xfer->op->alloc_iob ( xfer, len );
+ if ( ! iobuf )
+ return -ENOMEM;
+
+ memcpy ( iob_put ( iobuf, len ), data, len );
+ return xfer->op->deliver_iob ( xfer, iobuf, NULL );
+}
+
+/**
+ * Ignore datagram as raw data event
+ *
+ * @v xfer Data transfer interface
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+int ignore_xfer_deliver_raw ( struct xfer_interface *xfer,
+ const void *data __unused, size_t len ) {
+ DBGC ( xfer, "XFER %p %zd bytes delivered %s\n", xfer, len,
+ ( ( xfer == &null_xfer ) ?
+ "before connection" : "after termination" ) );
+ return 0;
+}
+
+/** Null data transfer interface operations */
+struct xfer_interface_operations null_xfer_ops = {
+ .close = ignore_xfer_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = ignore_xfer_deliver_raw,
+};
+
+/**
+ * Null data transfer interface
+ *
+ * This is the interface to which data transfer interfaces are
+ * connected when unplugged. It will never generate messages, and
+ * will silently absorb all received messages.
+ */
+struct xfer_interface null_xfer = XFER_INIT ( &null_xfer_ops );
diff --git a/gpxe/src/crypto/asn1.c b/gpxe/src/crypto/asn1.c
new file mode 100644
index 00000000..0a69162a
--- /dev/null
+++ b/gpxe/src/crypto/asn1.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <errno.h>
+#include <gpxe/asn1.h>
+
+/** @file
+ *
+ * ASN.1 encoding
+ *
+ */
+
+/**
+ * Start parsing ASN.1 object
+ *
+ * @v cursor ASN.1 object cursor
+ * @v type Expected type
+ * @ret len Length of object body, or -1 on error
+ *
+ * The object cursor will be updated to point to the start of the
+ * object body (i.e. the first byte following the length byte(s)), and
+ * the length of the object body (i.e. the number of bytes until the
+ * following object tag, if any) is returned.
+ *
+ * If any error occurs (i.e. if the object is not of the expected
+ * type, or if we overflow beyond the end of the ASN.1 object), then
+ * the cursor will be invalidated and a negative value will be
+ * returned.
+ */
+static int asn1_start_object ( struct asn1_cursor *cursor,
+ unsigned int type ) {
+ unsigned int len_len;
+ unsigned int len;
+
+ /* Sanity check */
+ if ( cursor->len < 2 /* Tag byte and first length byte */ ) {
+ if ( cursor->len )
+ DBGC ( cursor, "ASN1 %p too short\n", cursor );
+ goto notfound;
+ }
+
+ /* Check the tag byte */
+ if ( cursor->data[0] != type ) {
+ DBGC ( cursor, "ASN1 %p type mismatch (expected %d, got %d)\n",
+ cursor, type, cursor->data[0] );
+ goto notfound;
+ }
+ cursor->data++;
+ cursor->len--;
+
+ /* Extract length of the length field and sanity check */
+ len_len = cursor->data[0];
+ if ( len_len & 0x80 ) {
+ len_len = ( len_len & 0x7f );
+ cursor->data++;
+ cursor->len--;
+ } else {
+ len_len = 1;
+ }
+ if ( cursor->len < len_len ) {
+ DBGC ( cursor, "ASN1 %p bad length field length %d (max "
+ "%zd)\n", cursor, len_len, cursor->len );
+ goto notfound;
+ }
+
+ /* Extract the length and sanity check */
+ for ( len = 0 ; len_len ; len_len-- ) {
+ len <<= 8;
+ len |= cursor->data[0];
+ cursor->data++;
+ cursor->len--;
+ }
+ if ( cursor->len < len ) {
+ DBGC ( cursor, "ASN1 %p bad length %d (max %zd)\n",
+ cursor, len, cursor->len );
+ goto notfound;
+ }
+
+ return len;
+
+ notfound:
+ cursor->data = NULL;
+ cursor->len = 0;
+ return -1;
+}
+
+/**
+ * Enter ASN.1 object
+ *
+ * @v cursor ASN.1 object cursor
+ * @v type Expected type
+ * @ret rc Return status code
+ *
+ * The object cursor will be updated to point to the body of the
+ * current ASN.1 object. If any error occurs, the object cursor will
+ * be invalidated.
+ */
+int asn1_enter_object ( struct asn1_cursor *cursor, unsigned int type ) {
+ int len;
+
+ len = asn1_start_object ( cursor, type );
+ if ( len < 0 )
+ return -ENOENT;
+
+ cursor->len = len;
+ DBGC ( cursor, "ASN1 %p entered object type %02x (len %x)\n",
+ cursor, type, len );
+
+ return 0;
+}
+
+/**
+ * Skip ASN.1 object
+ *
+ * @v cursor ASN.1 object cursor
+ * @v type Expected type
+ * @ret rc Return status code
+ *
+ * The object cursor will be updated to point to the next ASN.1
+ * object. If any error occurs, the object cursor will be
+ * invalidated.
+ */
+int asn1_skip_object ( struct asn1_cursor *cursor, unsigned int type ) {
+ int len;
+
+ len = asn1_start_object ( cursor, type );
+ if ( len < 0 )
+ return -ENOENT;
+
+ cursor->data += len;
+ cursor->len -= len;
+ DBGC ( cursor, "ASN1 %p skipped object type %02x (len %x)\n",
+ cursor, type, len );
+
+ if ( ! cursor->len ) {
+ DBGC ( cursor, "ASN1 %p reached end of object\n", cursor );
+ cursor->data = NULL;
+ return -ENOENT;
+ }
+
+ return 0;
+}
diff --git a/gpxe/src/crypto/axtls/aes.c b/gpxe/src/crypto/axtls/aes.c
new file mode 100644
index 00000000..9154a515
--- /dev/null
+++ b/gpxe/src/crypto/axtls/aes.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright(C) 2006 Cameron Rich
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * AES implementation - this is a small code version. There are much faster
+ * versions around but they are much larger in size (i.e. they use large
+ * submix tables).
+ */
+
+#include <string.h>
+#include "crypto.h"
+
+/* all commented out in skeleton mode */
+#ifndef CONFIG_SSL_SKELETON_MODE
+
+#define rot1(x) (((x) << 24) | ((x) >> 8))
+#define rot2(x) (((x) << 16) | ((x) >> 16))
+#define rot3(x) (((x) << 8) | ((x) >> 24))
+
+/*
+ * This cute trick does 4 'mul by two' at once. Stolen from
+ * Dr B. R. Gladman <brg@gladman.uk.net> but I'm sure the u-(u>>7) is
+ * a standard graphics trick
+ * The key to this is that we need to xor with 0x1b if the top bit is set.
+ * a 1xxx xxxx 0xxx 0xxx First we mask the 7bit,
+ * b 1000 0000 0000 0000 then we shift right by 7 putting the 7bit in 0bit,
+ * c 0000 0001 0000 0000 we then subtract (c) from (b)
+ * d 0111 1111 0000 0000 and now we and with our mask
+ * e 0001 1011 0000 0000
+ */
+#define mt 0x80808080
+#define ml 0x7f7f7f7f
+#define mh 0xfefefefe
+#define mm 0x1b1b1b1b
+#define mul2(x,t) ((t)=((x)&mt), \
+ ((((x)+(x))&mh)^(((t)-((t)>>7))&mm)))
+
+#define inv_mix_col(x,f2,f4,f8,f9) (\
+ (f2)=mul2(x,f2), \
+ (f4)=mul2(f2,f4), \
+ (f8)=mul2(f4,f8), \
+ (f9)=(x)^(f8), \
+ (f8)=((f2)^(f4)^(f8)), \
+ (f2)^=(f9), \
+ (f4)^=(f9), \
+ (f8)^=rot3(f2), \
+ (f8)^=rot2(f4), \
+ (f8)^rot1(f9))
+
+/* some macros to do endian independent byte extraction */
+#define n2l(c,l) l=ntohl(*c); c++
+#define l2n(l,c) *c++=htonl(l)
+
+/*
+ * AES S-box
+ */
+static const uint8_t aes_sbox[256] =
+{
+ 0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,
+ 0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76,
+ 0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,
+ 0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0,
+ 0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,
+ 0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15,
+ 0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,
+ 0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75,
+ 0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,
+ 0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84,
+ 0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,
+ 0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF,
+ 0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,
+ 0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8,
+ 0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,
+ 0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2,
+ 0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,
+ 0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73,
+ 0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,
+ 0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB,
+ 0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,
+ 0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79,
+ 0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,
+ 0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08,
+ 0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,
+ 0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A,
+ 0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,
+ 0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E,
+ 0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,
+ 0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF,
+ 0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,
+ 0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16,
+};
+
+/*
+ * AES is-box
+ */
+static const uint8_t aes_isbox[256] =
+{
+ 0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,
+ 0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,
+ 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,
+ 0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,
+ 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,
+ 0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,
+ 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,
+ 0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,
+ 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,
+ 0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,
+ 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,
+ 0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,
+ 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,
+ 0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,
+ 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,
+ 0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,
+ 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,
+ 0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,
+ 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,
+ 0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,
+ 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,
+ 0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,
+ 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,
+ 0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,
+ 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,
+ 0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,
+ 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,
+ 0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,
+ 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,
+ 0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,
+ 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,
+ 0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d
+};
+
+static const unsigned char Rcon[30]=
+{
+ 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,
+ 0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,
+ 0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,
+ 0xb3,0x7d,0xfa,0xef,0xc5,0x91,
+};
+
+/* ----- static functions ----- */
+static void AES_encrypt(const AES_CTX *ctx, uint32_t *data);
+static void AES_decrypt(const AES_CTX *ctx, uint32_t *data);
+
+/* Perform doubling in Galois Field GF(2^8) using the irreducible polynomial
+ x^8+x^4+x^3+x+1 */
+static unsigned char AES_xtime(uint32_t x)
+{
+ return x = (x&0x80) ? (x<<1)^0x1b : x<<1;
+}
+
+/**
+ * Set up AES with the key/iv and cipher size.
+ */
+void AES_set_key(AES_CTX *ctx, const uint8_t *key,
+ const uint8_t *iv, AES_MODE mode)
+{
+ int i, ii;
+ uint32_t *W, tmp, tmp2;
+ const unsigned char *ip;
+ int words;
+
+ switch (mode)
+ {
+ case AES_MODE_128:
+ i = 10;
+ words = 4;
+ break;
+
+ case AES_MODE_256:
+ i = 14;
+ words = 8;
+ break;
+
+ default: /* fail silently */
+ return;
+ }
+
+ ctx->rounds = i;
+ ctx->key_size = words;
+ W = ctx->ks;
+ for (i = 0; i < words; i+=2)
+ {
+ W[i+0]= ((uint32_t)key[ 0]<<24)|
+ ((uint32_t)key[ 1]<<16)|
+ ((uint32_t)key[ 2]<< 8)|
+ ((uint32_t)key[ 3] );
+ W[i+1]= ((uint32_t)key[ 4]<<24)|
+ ((uint32_t)key[ 5]<<16)|
+ ((uint32_t)key[ 6]<< 8)|
+ ((uint32_t)key[ 7] );
+ key += 8;
+ }
+
+ ip = Rcon;
+ ii = 4 * (ctx->rounds+1);
+ for (i = words; i<ii; i++)
+ {
+ tmp = W[i-1];
+
+ if ((i % words) == 0)
+ {
+ tmp2 =(uint32_t)aes_sbox[(tmp )&0xff]<< 8;
+ tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<<16;
+ tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<24;
+ tmp2|=(uint32_t)aes_sbox[(tmp>>24) ];
+ tmp=tmp2^(((unsigned int)*ip)<<24);
+ ip++;
+ }
+
+ if ((words == 8) && ((i % words) == 4))
+ {
+ tmp2 =(uint32_t)aes_sbox[(tmp )&0xff] ;
+ tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<< 8;
+ tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<16;
+ tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]<<24;
+ tmp=tmp2;
+ }
+
+ W[i]=W[i-words]^tmp;
+ }
+
+ /* copy the iv across */
+ memcpy(ctx->iv, iv, 16);
+}
+
+/**
+ * Change a key for decryption.
+ */
+void AES_convert_key(AES_CTX *ctx)
+{
+ int i;
+ uint32_t *k,w,t1,t2,t3,t4;
+
+ k = ctx->ks;
+ k += 4;
+
+ for (i=ctx->rounds*4; i>4; i--)
+ {
+ w= *k;
+ w = inv_mix_col(w,t1,t2,t3,t4);
+ *k++ =w;
+ }
+}
+
+/**
+ * Encrypt a byte sequence (with a block size 16) using the AES cipher.
+ */
+void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length)
+{
+ uint32_t tin0, tin1, tin2, tin3;
+ uint32_t tout0, tout1, tout2, tout3;
+ uint32_t tin[4];
+ uint32_t *iv = (uint32_t *)ctx->iv;
+ uint32_t *msg_32 = (uint32_t *)msg;
+ uint32_t *out_32 = (uint32_t *)out;
+
+ n2l(iv, tout0);
+ n2l(iv, tout1);
+ n2l(iv, tout2);
+ n2l(iv, tout3);
+ iv -= 4;
+
+ for (length -= 16; length >= 0; length -= 16)
+ {
+ n2l(msg_32, tin0);
+ n2l(msg_32, tin1);
+ n2l(msg_32, tin2);
+ n2l(msg_32, tin3);
+ tin[0] = tin0^tout0;
+ tin[1] = tin1^tout1;
+ tin[2] = tin2^tout2;
+ tin[3] = tin3^tout3;
+
+ AES_encrypt(ctx, tin);
+
+ tout0 = tin[0];
+ l2n(tout0, out_32);
+ tout1 = tin[1];
+ l2n(tout1, out_32);
+ tout2 = tin[2];
+ l2n(tout2, out_32);
+ tout3 = tin[3];
+ l2n(tout3, out_32);
+ }
+
+ l2n(tout0, iv);
+ l2n(tout1, iv);
+ l2n(tout2, iv);
+ l2n(tout3, iv);
+}
+
+/**
+ * Decrypt a byte sequence (with a block size 16) using the AES cipher.
+ */
+void AES_cbc_decrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length)
+{
+ uint32_t tin0, tin1, tin2, tin3;
+ uint32_t xor0,xor1,xor2,xor3;
+ uint32_t tout0,tout1,tout2,tout3;
+ uint32_t data[4];
+ uint32_t *iv = (uint32_t *)ctx->iv;
+ uint32_t *msg_32 = (uint32_t *)msg;
+ uint32_t *out_32 = (uint32_t *)out;
+
+ n2l(iv ,xor0);
+ n2l(iv, xor1);
+ n2l(iv, xor2);
+ n2l(iv, xor3);
+ iv -= 4;
+
+ for (length-=16; length >= 0; length -= 16)
+ {
+ n2l(msg_32, tin0);
+ n2l(msg_32, tin1);
+ n2l(msg_32, tin2);
+ n2l(msg_32, tin3);
+
+ data[0] = tin0;
+ data[1] = tin1;
+ data[2] = tin2;
+ data[3] = tin3;
+
+ AES_decrypt(ctx, data);
+
+ tout0 = data[0]^xor0;
+ tout1 = data[1]^xor1;
+ tout2 = data[2]^xor2;
+ tout3 = data[3]^xor3;
+
+ xor0 = tin0;
+ xor1 = tin1;
+ xor2 = tin2;
+ xor3 = tin3;
+
+ l2n(tout0, out_32);
+ l2n(tout1, out_32);
+ l2n(tout2, out_32);
+ l2n(tout3, out_32);
+ }
+
+ l2n(xor0, iv);
+ l2n(xor1, iv);
+ l2n(xor2, iv);
+ l2n(xor3, iv);
+}
+
+/**
+ * Encrypt a single block (16 bytes) of data
+ */
+static void AES_encrypt(const AES_CTX *ctx, uint32_t *data)
+{
+ /* To make this code smaller, generate the sbox entries on the fly.
+ * This will have a really heavy effect upon performance.
+ */
+ uint32_t tmp[4];
+ uint32_t tmp1, old_a0, a0, a1, a2, a3, row;
+ int curr_rnd;
+ int rounds = ctx->rounds;
+ const uint32_t *k = ctx->ks;
+
+ /* Pre-round key addition */
+ for (row = 0; row < 4; row++)
+ {
+ data[row] ^= *(k++);
+ }
+
+ /* Encrypt one block. */
+ for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++)
+ {
+ /* Perform ByteSub and ShiftRow operations together */
+ for (row = 0; row < 4; row++)
+ {
+ a0 = (uint32_t)aes_sbox[(data[row%4]>>24)&0xFF];
+ a1 = (uint32_t)aes_sbox[(data[(row+1)%4]>>16)&0xFF];
+ a2 = (uint32_t)aes_sbox[(data[(row+2)%4]>>8)&0xFF];
+ a3 = (uint32_t)aes_sbox[(data[(row+3)%4])&0xFF];
+
+ /* Perform MixColumn iff not last round */
+ if (curr_rnd < (rounds - 1))
+ {
+ tmp1 = a0 ^ a1 ^ a2 ^ a3;
+ old_a0 = a0;
+
+ a0 ^= tmp1 ^ AES_xtime(a0 ^ a1);
+ a1 ^= tmp1 ^ AES_xtime(a1 ^ a2);
+ a2 ^= tmp1 ^ AES_xtime(a2 ^ a3);
+ a3 ^= tmp1 ^ AES_xtime(a3 ^ old_a0);
+
+ }
+
+ tmp[row] = ((a0 << 24) | (a1 << 16) | (a2 << 8) | a3);
+ }
+
+ /* KeyAddition - note that it is vital that this loop is separate from
+ the MixColumn operation, which must be atomic...*/
+ for (row = 0; row < 4; row++)
+ {
+ data[row] = tmp[row] ^ *(k++);
+ }
+ }
+}
+
+/**
+ * Decrypt a single block (16 bytes) of data
+ */
+static void AES_decrypt(const AES_CTX *ctx, uint32_t *data)
+{
+ uint32_t tmp[4];
+ uint32_t xt0,xt1,xt2,xt3,xt4,xt5,xt6;
+ uint32_t a0, a1, a2, a3, row;
+ int curr_rnd;
+ int rounds = ctx->rounds;
+ uint32_t *k = (uint32_t*)ctx->ks + ((rounds+1)*4);
+
+ /* pre-round key addition */
+ for (row=4; row > 0;row--)
+ {
+ data[row-1] ^= *(--k);
+ }
+
+ /* Decrypt one block */
+ for (curr_rnd=0; curr_rnd < rounds; curr_rnd++)
+ {
+ /* Perform ByteSub and ShiftRow operations together */
+ for (row = 4; row > 0; row--)
+ {
+ a0 = aes_isbox[(data[(row+3)%4]>>24)&0xFF];
+ a1 = aes_isbox[(data[(row+2)%4]>>16)&0xFF];
+ a2 = aes_isbox[(data[(row+1)%4]>>8)&0xFF];
+ a3 = aes_isbox[(data[row%4])&0xFF];
+
+ /* Perform MixColumn iff not last round */
+ if (curr_rnd<(rounds-1))
+ {
+ /* The MDS cofefficients (0x09, 0x0B, 0x0D, 0x0E)
+ are quite large compared to encryption; this
+ operation slows decryption down noticeably. */
+ xt0 = AES_xtime(a0^a1);
+ xt1 = AES_xtime(a1^a2);
+ xt2 = AES_xtime(a2^a3);
+ xt3 = AES_xtime(a3^a0);
+ xt4 = AES_xtime(xt0^xt1);
+ xt5 = AES_xtime(xt1^xt2);
+ xt6 = AES_xtime(xt4^xt5);
+
+ xt0 ^= a1^a2^a3^xt4^xt6;
+ xt1 ^= a0^a2^a3^xt5^xt6;
+ xt2 ^= a0^a1^a3^xt4^xt6;
+ xt3 ^= a0^a1^a2^xt5^xt6;
+ tmp[row-1] = ((xt0<<24)|(xt1<<16)|(xt2<<8)|xt3);
+ }
+ else
+ tmp[row-1] = ((a0<<24)|(a1<<16)|(a2<<8)|a3);
+ }
+
+ for (row = 4; row > 0; row--)
+ {
+ data[row-1] = tmp[row-1] ^ *(--k);
+ }
+ }
+}
+
+#endif
diff --git a/gpxe/src/crypto/axtls/axtls_asn1.c b/gpxe/src/crypto/axtls/axtls_asn1.c
new file mode 100644
index 00000000..74411c70
--- /dev/null
+++ b/gpxe/src/crypto/axtls/axtls_asn1.c
@@ -0,0 +1,867 @@
+/*
+ * Copyright(C) 2006 Cameron Rich
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @file asn1.c
+ *
+ * Some primitive asn methods for extraction rsa modulus information. It also
+ * is used for retrieving information from X.509 certificates.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "crypto.h"
+
+#define SIG_OID_PREFIX_SIZE 8
+
+#define SIG_TYPE_MD2 0x02
+#define SIG_TYPE_MD5 0x04
+#define SIG_TYPE_SHA1 0x05
+
+/* Must be an RSA algorithm with either SHA1 or MD5 for verifying to work */
+static const uint8_t sig_oid_prefix[SIG_OID_PREFIX_SIZE] =
+{
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01
+};
+
+/* CN, O, OU */
+static const uint8_t g_dn_types[] = { 3, 10, 11 };
+
+static int get_asn1_length(const uint8_t *buf, int *offset)
+{
+ int len, i;
+
+ if (!(buf[*offset] & 0x80)) /* short form */
+ {
+ len = buf[(*offset)++];
+ }
+ else /* long form */
+ {
+ int length_bytes = buf[(*offset)++]&0x7f;
+ len = 0;
+ for (i = 0; i < length_bytes; i++)
+ {
+ len <<= 8;
+ len += buf[(*offset)++];
+ }
+ }
+
+ return len;
+}
+
+/**
+ * Skip the ASN1.1 object type and its length. Get ready to read the object's
+ * data.
+ */
+int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type)
+{
+ if (buf[*offset] != obj_type)
+ return X509_NOT_OK;
+ (*offset)++;
+ return get_asn1_length(buf, offset);
+}
+
+/**
+ * Skip over an ASN.1 object type completely. Get ready to read the next
+ * object.
+ */
+int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type)
+{
+ int len;
+
+ if (buf[*offset] != obj_type)
+ return X509_NOT_OK;
+ (*offset)++;
+ len = get_asn1_length(buf, offset);
+ *offset += len;
+ return 0;
+}
+
+/**
+ * Read an integer value for ASN.1 data
+ * Note: This function allocates memory which must be freed by the user.
+ */
+int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object)
+{
+ int len;
+
+ if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0)
+ goto end_int_array;
+
+ *object = (uint8_t *)malloc(len);
+ memcpy(*object, &buf[*offset], len);
+ *offset += len;
+
+end_int_array:
+ return len;
+}
+
+#if 0
+
+/**
+ * Get all the RSA private key specifics from an ASN.1 encoded file
+ */
+int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx)
+{
+ int offset = 7;
+ uint8_t *modulus, *priv_exp, *pub_exp;
+ int mod_len, priv_len, pub_len;
+#ifdef CONFIG_BIGINT_CRT
+ uint8_t *p, *q, *dP, *dQ, *qInv;
+ int p_len, q_len, dP_len, dQ_len, qInv_len;
+#endif
+
+ /* not in der format */
+ if (buf[0] != ASN1_SEQUENCE) /* basic sanity check */
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("Error: This is not a valid ASN.1 file\n");
+#endif
+ return X509_INVALID_PRIV_KEY;
+ }
+
+ /* initialise the RNG */
+ RNG_initialize(buf, len);
+
+ mod_len = asn1_get_int(buf, &offset, &modulus);
+ pub_len = asn1_get_int(buf, &offset, &pub_exp);
+ priv_len = asn1_get_int(buf, &offset, &priv_exp);
+
+ if (mod_len <= 0 || pub_len <= 0 || priv_len <= 0)
+ return X509_INVALID_PRIV_KEY;
+
+#ifdef CONFIG_BIGINT_CRT
+ p_len = asn1_get_int(buf, &offset, &p);
+ q_len = asn1_get_int(buf, &offset, &q);
+ dP_len = asn1_get_int(buf, &offset, &dP);
+ dQ_len = asn1_get_int(buf, &offset, &dQ);
+ qInv_len = asn1_get_int(buf, &offset, &qInv);
+
+ if (p_len <= 0 || q_len <= 0 || dP_len <= 0 || dQ_len <= 0 || qInv_len <= 0)
+ return X509_INVALID_PRIV_KEY;
+
+ RSA_priv_key_new(rsa_ctx,
+ modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len,
+ p, p_len, q, p_len, dP, dP_len, dQ, dQ_len, qInv, qInv_len);
+
+ free(p);
+ free(q);
+ free(dP);
+ free(dQ);
+ free(qInv);
+#else
+ RSA_priv_key_new(rsa_ctx,
+ modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len);
+#endif
+
+ free(modulus);
+ free(priv_exp);
+ free(pub_exp);
+ return X509_OK;
+}
+
+/**
+ * Get the time of a certificate. Ignore hours/minutes/seconds.
+ */
+static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t)
+{
+ int ret = X509_NOT_OK, len, t_offset;
+ struct tm tm;
+
+ if (buf[(*offset)++] != ASN1_UTC_TIME)
+ goto end_utc_time;
+ len = get_asn1_length(buf, offset);
+ t_offset = *offset;
+
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0');
+
+ if (tm.tm_year <= 50) /* 1951-2050 thing */
+ {
+ tm.tm_year += 100;
+ }
+
+ tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1;
+ tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0');
+ *t = mktime(&tm);
+ *offset += len;
+ ret = X509_OK;
+
+end_utc_time:
+ return ret;
+}
+
+/**
+ * Get the version type of a certificate (which we don't actually care about)
+ */
+static int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
+{
+ int ret = X509_NOT_OK;
+
+ (*offset) += 2; /* get past explicit tag */
+ if (asn1_skip_obj(cert, offset, ASN1_INTEGER))
+ goto end_version;
+
+ ret = X509_OK;
+end_version:
+ return ret;
+}
+
+/**
+ * Retrieve the notbefore and notafter certificate times.
+ */
+static int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
+{
+ return (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
+ asn1_get_utc_time(cert, offset, &x509_ctx->not_before) ||
+ asn1_get_utc_time(cert, offset, &x509_ctx->not_after));
+}
+
+/**
+ * Get the components of a distinguished name
+ */
+static int asn1_get_oid_x520(const uint8_t *buf, int *offset)
+{
+ int dn_type = 0;
+ int len;
+
+ if ((len = asn1_next_obj(buf, offset, ASN1_OID)) < 0)
+ goto end_oid;
+
+ /* expect a sequence of 2.5.4.[x] where x is a one of distinguished name
+ components we are interested in. */
+ if (len == 3 && buf[(*offset)++] == 0x55 && buf[(*offset)++] == 0x04)
+ dn_type = buf[(*offset)++];
+ else
+ {
+ *offset += len; /* skip over it */
+ }
+
+end_oid:
+ return dn_type;
+}
+
+/**
+ * Obtain an ASN.1 printable string type.
+ */
+static int asn1_get_printable_str(const uint8_t *buf, int *offset, char **str)
+{
+ int len = X509_NOT_OK;
+
+ /* some certs have this awful crud in them for some reason */
+ if (buf[*offset] != ASN1_PRINTABLE_STR &&
+ buf[*offset] != ASN1_TELETEX_STR && buf[*offset] != ASN1_IA5_STR)
+ goto end_pnt_str;
+
+ (*offset)++;
+ len = get_asn1_length(buf, offset);
+ *str = (char *)malloc(len+1); /* allow for null */
+ memcpy(*str, &buf[*offset], len);
+ (*str)[len] = 0; /* null terminate */
+ *offset += len;
+end_pnt_str:
+ return len;
+}
+
+/**
+ * Get the subject name (or the issuer) of a certificate.
+ */
+static int asn1_name(const uint8_t *cert, int *offset, char *dn[])
+{
+ int ret = X509_NOT_OK;
+ int dn_type;
+ char *tmp = NULL;
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0)
+ goto end_name;
+
+ while (asn1_next_obj(cert, offset, ASN1_SET) >= 0)
+ {
+ int i, found = 0;
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
+ (dn_type = asn1_get_oid_x520(cert, offset)) < 0)
+ goto end_name;
+
+ if (asn1_get_printable_str(cert, offset, &tmp) < 0)
+ {
+ free(tmp);
+ goto end_name;
+ }
+
+ /* find the distinguished named type */
+ for (i = 0; i < X509_NUM_DN_TYPES; i++)
+ {
+ if (dn_type == g_dn_types[i])
+ {
+ if (dn[i] == NULL)
+ {
+ dn[i] = tmp;
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ if (found == 0) /* not found so get rid of it */
+ {
+ free(tmp);
+ }
+ }
+
+ ret = X509_OK;
+end_name:
+ return ret;
+}
+
+/**
+ * Read the modulus and public exponent of a certificate.
+ */
+static int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
+{
+ int ret = X509_NOT_OK, mod_len, pub_len;
+ uint8_t *modulus, *pub_exp;
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(cert, offset, ASN1_SEQUENCE) ||
+ asn1_next_obj(cert, offset, ASN1_BIT_STRING) < 0)
+ goto end_pub_key;
+
+ (*offset)++;
+
+ if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0)
+ goto end_pub_key;
+
+ mod_len = asn1_get_int(cert, offset, &modulus);
+ pub_len = asn1_get_int(cert, offset, &pub_exp);
+
+ RSA_pub_key_new(&x509_ctx->rsa_ctx, modulus, mod_len, pub_exp, pub_len);
+
+ free(modulus);
+ free(pub_exp);
+ ret = X509_OK;
+
+end_pub_key:
+ return ret;
+}
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+/**
+ * Read the signature of the certificate.
+ */
+static int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx)
+{
+ int ret = X509_NOT_OK;
+
+ if (cert[(*offset)++] != ASN1_BIT_STRING)
+ goto end_sig;
+
+ x509_ctx->sig_len = get_asn1_length(cert, offset);
+ x509_ctx->signature = (uint8_t *)malloc(x509_ctx->sig_len);
+ memcpy(x509_ctx->signature, &cert[*offset], x509_ctx->sig_len);
+ *offset += x509_ctx->sig_len;
+ ret = X509_OK;
+
+end_sig:
+ return ret;
+}
+
+/*
+ * Compare 2 distinguished name components for equality
+ * @return 0 if a match
+ */
+static int asn1_compare_dn_comp(const char *dn1, const char *dn2)
+{
+ int ret = 1;
+
+ if ((dn1 && dn2 == NULL) || (dn1 == NULL && dn2)) goto err_no_match;
+
+ ret = (dn1 && dn2) ? strcmp(dn1, dn2) : 0;
+
+err_no_match:
+ return ret;
+}
+
+/**
+ * Clean up all of the CA certificates.
+ */
+void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx)
+{
+ int i = 0;
+
+ while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
+ {
+ x509_free(ca_cert_ctx->cert[i]);
+ ca_cert_ctx->cert[i++] = NULL;
+ }
+
+ free(ca_cert_ctx);
+}
+
+/*
+ * Compare 2 distinguished names for equality
+ * @return 0 if a match
+ */
+static int asn1_compare_dn(char * const dn1[], char * const dn2[])
+{
+ int i;
+
+ for (i = 0; i < X509_NUM_DN_TYPES; i++)
+ {
+ if (asn1_compare_dn_comp(dn1[i], dn2[i]))
+ {
+ return 1;
+ }
+ }
+
+ return 0; /* all good */
+}
+
+/**
+ * Retrieve the signature from a certificate.
+ */
+const uint8_t *x509_get_signature(const uint8_t *asn1_sig, int *len)
+{
+ int offset = 0;
+ const uint8_t *ptr = NULL;
+
+ if (asn1_next_obj(asn1_sig, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(asn1_sig, &offset, ASN1_SEQUENCE))
+ goto end_get_sig;
+
+ if (asn1_sig[offset++] != ASN1_OCTET_STRING)
+ goto end_get_sig;
+ *len = get_asn1_length(asn1_sig, &offset);
+ ptr = &asn1_sig[offset]; /* all ok */
+
+end_get_sig:
+ return ptr;
+}
+
+#endif
+
+/**
+ * Read the signature type of the certificate. We only support RSA-MD5 and
+ * RSA-SHA1 signature types.
+ */
+static int asn1_signature_type(const uint8_t *cert,
+ int *offset, X509_CTX *x509_ctx)
+{
+ int ret = X509_NOT_OK, len;
+
+ if (cert[(*offset)++] != ASN1_OID)
+ goto end_check_sig;
+
+ len = get_asn1_length(cert, offset);
+
+ if (memcmp(sig_oid_prefix, &cert[*offset], SIG_OID_PREFIX_SIZE))
+ goto end_check_sig; /* unrecognised cert type */
+
+ x509_ctx->sig_type = cert[*offset + SIG_OID_PREFIX_SIZE];
+
+ *offset += len;
+ if (asn1_skip_obj(cert, offset, ASN1_NULL))
+ goto end_check_sig;
+ ret = X509_OK;
+
+end_check_sig:
+ return ret;
+}
+
+/**
+ * Construct a new x509 object.
+ * @return 0 if ok. < 0 if there was a problem.
+ */
+int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx)
+{
+ int begin_tbs, end_tbs;
+ int ret = X509_NOT_OK, offset = 0, cert_size = 0;
+ X509_CTX *x509_ctx;
+ BI_CTX *bi_ctx;
+
+ *ctx = (X509_CTX *)calloc(1, sizeof(X509_CTX));
+ x509_ctx = *ctx;
+
+ /* get the certificate size */
+ asn1_skip_obj(cert, &cert_size, ASN1_SEQUENCE);
+
+ if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0)
+ goto end_cert;
+
+ begin_tbs = offset; /* start of the tbs */
+ end_tbs = begin_tbs; /* work out the end of the tbs */
+ asn1_skip_obj(cert, &end_tbs, ASN1_SEQUENCE);
+
+ if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0)
+ goto end_cert;
+
+ if (cert[offset] == ASN1_EXPLICIT_TAG) /* optional version */
+ {
+ if (asn1_version(cert, &offset, x509_ctx))
+ goto end_cert;
+ }
+
+ if (asn1_skip_obj(cert, &offset, ASN1_INTEGER) || /* serial number */
+ asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0)
+ goto end_cert;
+
+ /* make sure the signature is ok */
+ if (asn1_signature_type(cert, &offset, x509_ctx))
+ {
+ ret = X509_VFY_ERROR_UNSUPPORTED_DIGEST;
+ goto end_cert;
+ }
+
+ if (asn1_name(cert, &offset, x509_ctx->ca_cert_dn) ||
+ asn1_validity(cert, &offset, x509_ctx) ||
+ asn1_name(cert, &offset, x509_ctx->cert_dn) ||
+ asn1_public_key(cert, &offset, x509_ctx))
+ goto end_cert;
+
+ bi_ctx = x509_ctx->rsa_ctx->bi_ctx;
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */
+ /* use the appropriate signature algorithm (either SHA1 or MD5) */
+ if (x509_ctx->sig_type == SIG_TYPE_MD5)
+ {
+ MD5_CTX md5_ctx;
+ uint8_t md5_dgst[MD5_SIZE];
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, &cert[begin_tbs], end_tbs-begin_tbs);
+ MD5Final(&md5_ctx, md5_dgst);
+ x509_ctx->digest = bi_import(bi_ctx, md5_dgst, MD5_SIZE);
+ }
+ else if (x509_ctx->sig_type == SIG_TYPE_SHA1)
+ {
+ SHA1_CTX sha_ctx;
+ uint8_t sha_dgst[SHA1_SIZE];
+ SHA1Init(&sha_ctx);
+ SHA1Update(&sha_ctx, &cert[begin_tbs], end_tbs-begin_tbs);
+ SHA1Final(&sha_ctx, sha_dgst);
+ x509_ctx->digest = bi_import(bi_ctx, sha_dgst, SHA1_SIZE);
+ }
+
+ offset = end_tbs; /* skip the v3 data */
+ if (asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_signature(cert, &offset, x509_ctx))
+ goto end_cert;
+#endif
+
+ if (len)
+ {
+ *len = cert_size;
+ }
+
+ ret = X509_OK;
+end_cert:
+
+#ifdef CONFIG_SSL_FULL_MODE
+ if (ret)
+ {
+ printf("Error: Invalid X509 ASN.1 file\n");
+ }
+#endif
+
+ return ret;
+}
+
+/**
+ * Free an X.509 object's resources.
+ */
+void x509_free(X509_CTX *x509_ctx)
+{
+ X509_CTX *next;
+ int i;
+
+ if (x509_ctx == NULL) /* if already null, then don't bother */
+ return;
+
+ for (i = 0; i < X509_NUM_DN_TYPES; i++)
+ {
+ free(x509_ctx->ca_cert_dn[i]);
+ free(x509_ctx->cert_dn[i]);
+ }
+
+ free(x509_ctx->signature);
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ if (x509_ctx->digest)
+ {
+ bi_free(x509_ctx->rsa_ctx->bi_ctx, x509_ctx->digest);
+ }
+#endif
+
+ RSA_free(x509_ctx->rsa_ctx);
+
+ next = x509_ctx->next;
+ free(x509_ctx);
+ x509_free(next); /* clear the chain */
+}
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+/**
+ * Do some basic checks on the certificate chain.
+ *
+ * Certificate verification consists of a number of checks:
+ * - A root certificate exists in the certificate store.
+ * - The date of the certificate is after the start date.
+ * - The date of the certificate is before the finish date.
+ * - The certificate chain is valid.
+ * - That the certificate(s) are not self-signed.
+ * - The signature of the certificate is valid.
+ */
+int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert)
+{
+ int ret = X509_OK, i = 0;
+ bigint *cert_sig;
+ X509_CTX *next_cert = NULL;
+ BI_CTX *ctx;
+ bigint *mod, *expn;
+ struct timeval tv;
+ int match_ca_cert = 0;
+
+ if (cert == NULL || ca_cert_ctx == NULL)
+ {
+ ret = X509_VFY_ERROR_NO_TRUSTED_CERT;
+ goto end_verify;
+ }
+
+ /* last cert in the chain - look for a trusted cert */
+ if (cert->next == NULL)
+ {
+ while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
+ {
+ if (asn1_compare_dn(cert->ca_cert_dn,
+ ca_cert_ctx->cert[i]->cert_dn) == 0)
+ {
+ match_ca_cert = 1;
+ break;
+ }
+
+ i++;
+ }
+
+ if (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i])
+ {
+ next_cert = ca_cert_ctx->cert[i];
+ }
+ else /* trusted cert not found */
+ {
+ ret = X509_VFY_ERROR_NO_TRUSTED_CERT;
+ goto end_verify;
+ }
+ }
+ else
+ {
+ next_cert = cert->next;
+ }
+
+ gettimeofday(&tv, NULL);
+
+ /* check the not before date */
+ if (tv.tv_sec < cert->not_before)
+ {
+ ret = X509_VFY_ERROR_NOT_YET_VALID;
+ goto end_verify;
+ }
+
+ /* check the not after date */
+ if (tv.tv_sec > cert->not_after)
+ {
+ ret = X509_VFY_ERROR_EXPIRED;
+ goto end_verify;
+ }
+
+ /* check the chain integrity */
+ if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn))
+ {
+ ret = X509_VFY_ERROR_INVALID_CHAIN;
+ goto end_verify;
+ }
+
+ /* check for self-signing */
+ if (!match_ca_cert && asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0)
+ {
+ ret = X509_VFY_ERROR_SELF_SIGNED;
+ goto end_verify;
+ }
+
+ /* check the signature */
+ ctx = cert->rsa_ctx->bi_ctx;
+ mod = next_cert->rsa_ctx->m;
+ expn = next_cert->rsa_ctx->e;
+ cert_sig = RSA_sign_verify(ctx, cert->signature, cert->sig_len,
+ bi_clone(ctx, mod), bi_clone(ctx, expn));
+
+ if (cert_sig)
+ {
+ ret = cert->digest ? /* check the signature */
+ bi_compare(cert_sig, cert->digest) :
+ X509_VFY_ERROR_UNSUPPORTED_DIGEST;
+ bi_free(ctx, cert_sig);
+
+ if (ret)
+ goto end_verify;
+ }
+ else
+ {
+ ret = X509_VFY_ERROR_BAD_SIGNATURE;
+ goto end_verify;
+ }
+
+ /* go down the certificate chain using recursion. */
+ if (ret == 0 && cert->next)
+ {
+ ret = x509_verify(ca_cert_ctx, next_cert);
+ }
+
+end_verify:
+ return ret;
+}
+#endif
+
+#if defined (CONFIG_SSL_FULL_MODE)
+/**
+ * Used for diagnostics.
+ */
+void x509_print(CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert)
+{
+ if (cert == NULL)
+ return;
+
+ printf("---------------- CERT DEBUG ----------------\n");
+ printf("* CA Cert Distinguished Name\n");
+ if (cert->ca_cert_dn[X509_COMMON_NAME])
+ {
+ printf("Common Name (CN):\t%s\n", cert->ca_cert_dn[X509_COMMON_NAME]);
+ }
+
+ if (cert->ca_cert_dn[X509_ORGANIZATION])
+ {
+ printf("Organization (O):\t%s\n", cert->ca_cert_dn[X509_ORGANIZATION]);
+ }
+
+ if (cert->ca_cert_dn[X509_ORGANIZATIONAL_TYPE])
+ {
+ printf("Organizational Unit (OU): %s\n",
+ cert->ca_cert_dn[X509_ORGANIZATIONAL_TYPE]);
+ }
+
+ printf("* Cert Distinguished Name\n");
+ if (cert->cert_dn[X509_COMMON_NAME])
+ {
+ printf("Common Name (CN):\t%s\n", cert->cert_dn[X509_COMMON_NAME]);
+ }
+
+ if (cert->cert_dn[X509_ORGANIZATION])
+ {
+ printf("Organization (O):\t%s\n", cert->cert_dn[X509_ORGANIZATION]);
+ }
+
+ if (cert->cert_dn[X509_ORGANIZATIONAL_TYPE])
+ {
+ printf("Organizational Unit (OU): %s\n",
+ cert->cert_dn[X509_ORGANIZATIONAL_TYPE]);
+ }
+
+ printf("Not Before:\t\t%s", ctime(&cert->not_before));
+ printf("Not After:\t\t%s", ctime(&cert->not_after));
+ printf("RSA bitsize:\t\t%d\n", cert->rsa_ctx->num_octets*8);
+ printf("Sig Type:\t\t");
+ switch (cert->sig_type)
+ {
+ case SIG_TYPE_MD5:
+ printf("MD5\n");
+ break;
+ case SIG_TYPE_SHA1:
+ printf("SHA1\n");
+ break;
+ case SIG_TYPE_MD2:
+ printf("MD2\n");
+ break;
+ default:
+ printf("Unrecognized: %d\n", cert->sig_type);
+ break;
+ }
+
+ printf("Verify:\t\t\t");
+
+ if (ca_cert_ctx)
+ {
+ x509_display_error(x509_verify(ca_cert_ctx, cert));
+ }
+
+ printf("\n");
+#if 0
+ print_blob("Signature", cert->signature, cert->sig_len);
+ bi_print("Modulus", cert->rsa_ctx->m);
+ bi_print("Pub Exp", cert->rsa_ctx->e);
+#endif
+
+ if (ca_cert_ctx)
+ {
+ x509_print(ca_cert_ctx, cert->next);
+ }
+}
+
+void x509_display_error(int error)
+{
+ switch (error)
+ {
+ case X509_NOT_OK:
+ printf("X509 not ok");
+ break;
+
+ case X509_VFY_ERROR_NO_TRUSTED_CERT:
+ printf("No trusted cert is available");
+ break;
+
+ case X509_VFY_ERROR_BAD_SIGNATURE:
+ printf("Bad signature");
+ break;
+
+ case X509_VFY_ERROR_NOT_YET_VALID:
+ printf("Cert is not yet valid");
+ break;
+
+ case X509_VFY_ERROR_EXPIRED:
+ printf("Cert has expired");
+ break;
+
+ case X509_VFY_ERROR_SELF_SIGNED:
+ printf("Cert is self-signed");
+ break;
+
+ case X509_VFY_ERROR_INVALID_CHAIN:
+ printf("Chain is invalid (check order of certs)");
+ break;
+
+ case X509_VFY_ERROR_UNSUPPORTED_DIGEST:
+ printf("Unsupported digest");
+ break;
+
+ case X509_INVALID_PRIV_KEY:
+ printf("Invalid private key");
+ break;
+ }
+}
+#endif /* CONFIG_SSL_FULL_MODE */
+
+#endif
diff --git a/gpxe/src/crypto/axtls/bigint.c b/gpxe/src/crypto/axtls/bigint.c
new file mode 100644
index 00000000..49cad971
--- /dev/null
+++ b/gpxe/src/crypto/axtls/bigint.c
@@ -0,0 +1,1496 @@
+/*
+ * Copyright(C) 2006 Cameron Rich
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @defgroup bigint_api Big Integer API
+ * @brief The bigint implementation as used by the axTLS project.
+ *
+ * The bigint library is for RSA encryption/decryption as well as signing.
+ * This code tries to minimise use of malloc/free by maintaining a small
+ * cache. A bigint context may maintain state by being made "permanent".
+ * It be be later released with a bi_depermanent() and bi_free() call.
+ *
+ * It supports the following reduction techniques:
+ * - Classical
+ * - Barrett
+ * - Montgomery
+ *
+ * It also implements the following:
+ * - Karatsuba multiplication
+ * - Squaring
+ * - Sliding window exponentiation
+ * - Chinese Remainder Theorem (implemented in rsa.c).
+ *
+ * All the algorithms used are pretty standard, and designed for different
+ * data bus sizes. Negative numbers are not dealt with at all, so a subtraction
+ * may need to be tested for negativity.
+ *
+ * This library steals some ideas from Jef Poskanzer
+ * <http://cs.marlboro.edu/term/cs-fall02/algorithms/crypto/RSA/bigint>
+ * and GMP <http://www.swox.com/gmp>. It gets most of its implementation
+ * detail from "The Handbook of Applied Cryptography"
+ * <http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf>
+ * @{
+ */
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include "bigint.h"
+#include "crypto.h"
+
+static bigint *bi_int_multiply(BI_CTX *ctx, bigint *bi, comp i);
+static bigint *bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom);
+static bigint __malloc *alloc(BI_CTX *ctx, int size);
+static bigint *trim(bigint *bi);
+static void more_comps(bigint *bi, int n);
+#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \
+ defined(CONFIG_BIGINT_MONTGOMERY)
+static bigint *comp_right_shift(bigint *biR, int num_shifts);
+static bigint *comp_left_shift(bigint *biR, int num_shifts);
+#endif
+
+#ifdef CONFIG_BIGINT_CHECK_ON
+static void check(const bigint *bi);
+#endif
+
+/**
+ * @brief Start a new bigint context.
+ * @return A bigint context.
+ */
+BI_CTX *bi_initialize(void)
+{
+ /* calloc() sets everything to zero */
+ BI_CTX *ctx = (BI_CTX *)calloc(1, sizeof(BI_CTX));
+
+ /* the radix */
+ ctx->bi_radix = alloc(ctx, 2);
+ ctx->bi_radix->comps[0] = 0;
+ ctx->bi_radix->comps[1] = 1;
+ bi_permanent(ctx->bi_radix);
+ return ctx;
+}
+
+/**
+ * @brief Close the bigint context and free any resources.
+ *
+ * Free up any used memory - a check is done if all objects were not
+ * properly freed.
+ * @param ctx [in] The bigint session context.
+ */
+void bi_terminate(BI_CTX *ctx)
+{
+ bigint *p, *pn;
+
+ bi_depermanent(ctx->bi_radix);
+ bi_free(ctx, ctx->bi_radix);
+
+ if (ctx->active_count != 0)
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("bi_terminate: there were %d un-freed bigints\n",
+ ctx->active_count);
+#endif
+ abort();
+ }
+
+ for (p = ctx->free_list; p != NULL; p = pn)
+ {
+ pn = p->next;
+ free(p->comps);
+ free(p);
+ }
+
+ free(ctx);
+}
+
+/**
+ * @brief Increment the number of references to this object.
+ * It does not do a full copy.
+ * @param bi [in] The bigint to copy.
+ * @return A reference to the same bigint.
+ */
+bigint *bi_copy(bigint *bi)
+{
+ check(bi);
+ if (bi->refs != PERMANENT)
+ bi->refs++;
+ return bi;
+}
+
+/**
+ * @brief Simply make a bigint object "unfreeable" if bi_free() is called on it.
+ *
+ * For this object to be freed, bi_depermanent() must be called.
+ * @param bi [in] The bigint to be made permanent.
+ */
+void bi_permanent(bigint *bi)
+{
+ check(bi);
+ if (bi->refs != 1)
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("bi_permanent: refs was not 1\n");
+#endif
+ abort();
+ }
+
+ bi->refs = PERMANENT;
+}
+
+/**
+ * @brief Take a permanent object and make it eligible for freedom.
+ * @param bi [in] The bigint to be made back to temporary.
+ */
+void bi_depermanent(bigint *bi)
+{
+ check(bi);
+ if (bi->refs != PERMANENT)
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("bi_depermanent: bigint was not permanent\n");
+#endif
+ abort();
+ }
+
+ bi->refs = 1;
+}
+
+/**
+ * @brief Free a bigint object so it can be used again.
+ *
+ * The memory itself it not actually freed, just tagged as being available
+ * @param ctx [in] The bigint session context.
+ * @param bi [in] The bigint to be freed.
+ */
+void bi_free(BI_CTX *ctx, bigint *bi)
+{
+ check(bi);
+ if (bi->refs == PERMANENT)
+ {
+ return;
+ }
+
+ if (--bi->refs > 0)
+ {
+ return;
+ }
+
+ bi->next = ctx->free_list;
+ ctx->free_list = bi;
+ ctx->free_count++;
+
+ if (--ctx->active_count < 0)
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("bi_free: active_count went negative "
+ "- double-freed bigint?\n");
+#endif
+ abort();
+ }
+}
+
+/**
+ * @brief Convert an (unsigned) integer into a bigint.
+ * @param ctx [in] The bigint session context.
+ * @param i [in] The (unsigned) integer to be converted.
+ *
+ */
+bigint *int_to_bi(BI_CTX *ctx, comp i)
+{
+ bigint *biR = alloc(ctx, 1);
+ biR->comps[0] = i;
+ return biR;
+}
+
+/**
+ * @brief Do a full copy of the bigint object.
+ * @param ctx [in] The bigint session context.
+ * @param bi [in] The bigint object to be copied.
+ */
+bigint *bi_clone(BI_CTX *ctx, const bigint *bi)
+{
+ bigint *biR = alloc(ctx, bi->size);
+ check(bi);
+ memcpy(biR->comps, bi->comps, bi->size*COMP_BYTE_SIZE);
+ return biR;
+}
+
+/**
+ * @brief Perform an addition operation between two bigints.
+ * @param ctx [in] The bigint session context.
+ * @param bia [in] A bigint.
+ * @param bib [in] Another bigint.
+ * @return The result of the addition.
+ */
+bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib)
+{
+ int n;
+ comp carry = 0;
+ comp *pa, *pb;
+
+ check(bia);
+ check(bib);
+
+ n = max(bia->size, bib->size);
+ more_comps(bia, n+1);
+ more_comps(bib, n);
+ pa = bia->comps;
+ pb = bib->comps;
+
+ do
+ {
+ comp sl, rl, cy1;
+ sl = *pa + *pb++;
+ rl = sl + carry;
+ cy1 = sl < *pa;
+ carry = cy1 | (rl < sl);
+ *pa++ = rl;
+ } while (--n != 0);
+
+ *pa = carry; /* do overflow */
+ bi_free(ctx, bib);
+ return trim(bia);
+}
+
+/**
+ * @brief Perform a subtraction operation between two bigints.
+ * @param ctx [in] The bigint session context.
+ * @param bia [in] A bigint.
+ * @param bib [in] Another bigint.
+ * @param is_negative [out] If defined, indicates that the result was negative.
+ * is_negative may be null.
+ * @return The result of the subtraction. The result is always positive.
+ */
+bigint *bi_subtract(BI_CTX *ctx,
+ bigint *bia, bigint *bib, int *is_negative)
+{
+ int n = bia->size;
+ comp *pa, *pb, carry = 0;
+
+ check(bia);
+ check(bib);
+
+ more_comps(bib, n);
+ pa = bia->comps;
+ pb = bib->comps;
+
+ do
+ {
+ comp sl, rl, cy1;
+ sl = *pa - *pb++;
+ rl = sl - carry;
+ cy1 = sl > *pa;
+ carry = cy1 | (rl > sl);
+ *pa++ = rl;
+ } while (--n != 0);
+
+ if (is_negative) /* indicate a negative result */
+ {
+ *is_negative = carry;
+ }
+
+ bi_free(ctx, trim(bib)); /* put bib back to the way it was */
+ return trim(bia);
+}
+
+/**
+ * Perform a multiply between a bigint an an (unsigned) integer
+ */
+static bigint *bi_int_multiply(BI_CTX *ctx, bigint *bia, comp b)
+{
+ int j = 0, n = bia->size;
+ bigint *biR = alloc(ctx, n + 1);
+ comp carry = 0;
+ comp *r = biR->comps;
+ comp *a = bia->comps;
+
+ check(bia);
+
+ /* clear things to start with */
+ memset(r, 0, ((n+1)*COMP_BYTE_SIZE));
+
+ do
+ {
+ long_comp tmp = *r + (long_comp)a[j]*b + carry;
+ *r++ = (comp)tmp; /* downsize */
+ carry = (comp)(tmp >> COMP_BIT_SIZE);
+ } while (++j < n);
+
+ *r = carry;
+ bi_free(ctx, bia);
+ return trim(biR);
+}
+
+/**
+ * @brief Does both division and modulo calculations.
+ *
+ * Used extensively when doing classical reduction.
+ * @param ctx [in] The bigint session context.
+ * @param u [in] A bigint which is the numerator.
+ * @param v [in] Either the denominator or the modulus depending on the mode.
+ * @param is_mod [n] Determines if this is a normal division (0) or a reduction
+ * (1).
+ * @return The result of the division/reduction.
+ */
+bigint *bi_divide(BI_CTX *ctx, bigint *u, bigint *v, int is_mod)
+{
+ int n = v->size, m = u->size-n;
+ int j = 0, orig_u_size = u->size;
+ uint8_t mod_offset = ctx->mod_offset;
+ comp d;
+ bigint *quotient, *tmp_u;
+ comp q_dash;
+
+ check(u);
+ check(v);
+
+ /* if doing reduction and we are < mod, then return mod */
+ if (is_mod && bi_compare(v, u) > 0)
+ {
+ bi_free(ctx, v);
+ return u;
+ }
+
+ quotient = alloc(ctx, m+1);
+ tmp_u = alloc(ctx, n+1);
+ v = trim(v); /* make sure we have no leading 0's */
+ d = (comp)((long_comp)COMP_RADIX/(V1+1));
+
+ /* clear things to start with */
+ memset(quotient->comps, 0, ((quotient->size)*COMP_BYTE_SIZE));
+
+ /* normalise */
+ if (d > 1)
+ {
+ u = bi_int_multiply(ctx, u, d);
+
+ if (is_mod)
+ {
+ v = ctx->bi_normalised_mod[mod_offset];
+ }
+ else
+ {
+ v = bi_int_multiply(ctx, v, d);
+ }
+ }
+
+ if (orig_u_size == u->size) /* new digit position u0 */
+ {
+ more_comps(u, orig_u_size + 1);
+ }
+
+ do
+ {
+ /* get a temporary short version of u */
+ memcpy(tmp_u->comps, &u->comps[u->size-n-1-j], (n+1)*COMP_BYTE_SIZE);
+
+ /* calculate q' */
+ if (U(0) == V1)
+ {
+ q_dash = COMP_RADIX-1;
+ }
+ else
+ {
+ q_dash = (comp)(((long_comp)U(0)*COMP_RADIX + U(1))/V1);
+ }
+
+ if (v->size > 1 && V2)
+ {
+ /* we are implementing the following:
+ if (V2*q_dash > (((U(0)*COMP_RADIX + U(1) -
+ q_dash*V1)*COMP_RADIX) + U(2))) ... */
+ comp inner = (comp)((long_comp)COMP_RADIX*U(0) + U(1) -
+ (long_comp)q_dash*V1);
+ if ((long_comp)V2*q_dash > (long_comp)inner*COMP_RADIX + U(2))
+ {
+ q_dash--;
+ }
+ }
+
+ /* multiply and subtract */
+ if (q_dash)
+ {
+ int is_negative;
+ tmp_u = bi_subtract(ctx, tmp_u,
+ bi_int_multiply(ctx, bi_copy(v), q_dash), &is_negative);
+ more_comps(tmp_u, n+1);
+
+ Q(j) = q_dash;
+
+ /* add back */
+ if (is_negative)
+ {
+ Q(j)--;
+ tmp_u = bi_add(ctx, tmp_u, bi_copy(v));
+
+ /* lop off the carry */
+ tmp_u->size--;
+ v->size--;
+ }
+ }
+ else
+ {
+ Q(j) = 0;
+ }
+
+ /* copy back to u */
+ memcpy(&u->comps[u->size-n-1-j], tmp_u->comps, (n+1)*COMP_BYTE_SIZE);
+ } while (++j <= m);
+
+ bi_free(ctx, tmp_u);
+ bi_free(ctx, v);
+
+ if (is_mod) /* get the remainder */
+ {
+ bi_free(ctx, quotient);
+ return bi_int_divide(ctx, trim(u), d);
+ }
+ else /* get the quotient */
+ {
+ bi_free(ctx, u);
+ return trim(quotient);
+ }
+}
+
+/*
+ * Perform an integer divide on a bigint.
+ */
+static bigint *bi_int_divide(BI_CTX *ctx __unused, bigint *biR, comp denom)
+{
+ int i = biR->size - 1;
+ long_comp r = 0;
+
+ check(biR);
+
+ do
+ {
+ r = (r<<COMP_BIT_SIZE) + biR->comps[i];
+ biR->comps[i] = (comp)(r / denom);
+ r %= denom;
+ } while (--i != 0);
+
+ return trim(biR);
+}
+
+#ifdef CONFIG_BIGINT_MONTGOMERY
+/**
+ * There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1,
+ * where B^-1(B-1) mod N=1. Actually, only the least significant part of
+ * N' is needed, hence the definition N0'=N' mod b. We reproduce below the
+ * simple algorithm from an article by Dusse and Kaliski to efficiently
+ * find N0' from N0 and b */
+static comp modular_inverse(bigint *bim)
+{
+ int i;
+ comp t = 1;
+ comp two_2_i_minus_1 = 2; /* 2^(i-1) */
+ long_comp two_2_i = 4; /* 2^i */
+ comp N = bim->comps[0];
+
+ for (i = 2; i <= COMP_BIT_SIZE; i++)
+ {
+ if ((long_comp)N*t%two_2_i >= two_2_i_minus_1)
+ {
+ t += two_2_i_minus_1;
+ }
+
+ two_2_i_minus_1 <<= 1;
+ two_2_i <<= 1;
+ }
+
+ return (comp)(COMP_RADIX-t);
+}
+#endif
+
+#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \
+ defined(CONFIG_BIGINT_MONTGOMERY)
+/**
+ * Take each component and shift down (in terms of components)
+ */
+static bigint *comp_right_shift(bigint *biR, int num_shifts)
+{
+ int i = biR->size-num_shifts;
+ comp *x = biR->comps;
+ comp *y = &biR->comps[num_shifts];
+
+ check(biR);
+
+ if (i <= 0) /* have we completely right shifted? */
+ {
+ biR->comps[0] = 0; /* return 0 */
+ biR->size = 1;
+ return biR;
+ }
+
+ do
+ {
+ *x++ = *y++;
+ } while (--i > 0);
+
+ biR->size -= num_shifts;
+ return biR;
+}
+
+/**
+ * Take each component and shift it up (in terms of components)
+ */
+static bigint *comp_left_shift(bigint *biR, int num_shifts)
+{
+ int i = biR->size-1;
+ comp *x, *y;
+
+ check(biR);
+
+ if (num_shifts <= 0)
+ {
+ return biR;
+ }
+
+ more_comps(biR, biR->size + num_shifts);
+
+ x = &biR->comps[i+num_shifts];
+ y = &biR->comps[i];
+
+ do
+ {
+ *x-- = *y--;
+ } while (i--);
+
+ memset(biR->comps, 0, num_shifts*COMP_BYTE_SIZE); /* zero LS comps */
+ return biR;
+}
+#endif
+
+/**
+ * @brief Allow a binary sequence to be imported as a bigint.
+ * @param ctx [in] The bigint session context.
+ * @param data [in] The data to be converted.
+ * @param size [in] The number of bytes of data.
+ * @return A bigint representing this data.
+ */
+bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int size)
+{
+ bigint *biR = alloc(ctx, (size+COMP_BYTE_SIZE-1)/COMP_BYTE_SIZE);
+ int i, j = 0, offset = 0;
+
+ memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE);
+
+ for (i = size-1; i >= 0; i--)
+ {
+ biR->comps[offset] += data[i] << (j*8);
+
+ if (++j == COMP_BYTE_SIZE)
+ {
+ j = 0;
+ offset ++;
+ }
+ }
+
+ return trim(biR);
+}
+
+#ifdef CONFIG_SSL_FULL_MODE
+/**
+ * @brief The testharness uses this code to import text hex-streams and
+ * convert them into bigints.
+ * @param ctx [in] The bigint session context.
+ * @param data [in] A string consisting of hex characters. The characters must
+ * be in upper case.
+ * @return A bigint representing this data.
+ */
+bigint *bi_str_import(BI_CTX *ctx, const char *data)
+{
+ int size = strlen(data);
+ bigint *biR = alloc(ctx, (size+COMP_NUM_NIBBLES-1)/COMP_NUM_NIBBLES);
+ int i, j = 0, offset = 0;
+ memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE);
+
+ for (i = size-1; i >= 0; i--)
+ {
+ int num = (data[i] <= '9') ? (data[i] - '0') : (data[i] - 'A' + 10);
+ biR->comps[offset] += num << (j*4);
+
+ if (++j == COMP_NUM_NIBBLES)
+ {
+ j = 0;
+ offset ++;
+ }
+ }
+
+ return biR;
+}
+
+void bi_print(const char *label, bigint *x)
+{
+ int i, j;
+
+ if (x == NULL)
+ {
+ printf("%s: (null)\n", label);
+ return;
+ }
+
+ printf("%s: (size %d)\n", label, x->size);
+ for (i = x->size-1; i >= 0; i--)
+ {
+ for (j = COMP_NUM_NIBBLES-1; j >= 0; j--)
+ {
+ comp mask = 0x0f << (j*4);
+ comp num = (x->comps[i] & mask) >> (j*4);
+ putc((num <= 9) ? (num + '0') : (num + 'A' - 10), stdout);
+ }
+ }
+
+ printf("\n");
+}
+#endif
+
+/**
+ * @brief Take a bigint and convert it into a byte sequence.
+ *
+ * This is useful after a decrypt operation.
+ * @param ctx [in] The bigint session context.
+ * @param x [in] The bigint to be converted.
+ * @param data [out] The converted data as a byte stream.
+ * @param size [in] The maximum size of the byte stream. Unused bytes will be
+ * zeroed.
+ */
+void bi_export(BI_CTX *ctx, bigint *x, uint8_t *data, int size)
+{
+ int i, j, k = size-1;
+
+ check(x);
+ memset(data, 0, size); /* ensure all leading 0's are cleared */
+
+ for (i = 0; i < x->size; i++)
+ {
+ for (j = 0; j < COMP_BYTE_SIZE; j++)
+ {
+ comp mask = 0xff << (j*8);
+ int num = (x->comps[i] & mask) >> (j*8);
+ data[k--] = num;
+
+ if (k < 0)
+ {
+ break;
+ }
+ }
+ }
+
+ bi_free(ctx, x);
+}
+
+/**
+ * @brief Pre-calculate some of the expensive steps in reduction.
+ *
+ * This function should only be called once (normally when a session starts).
+ * When the session is over, bi_free_mod() should be called. bi_mod_power()
+ * relies on this function being called.
+ * @param ctx [in] The bigint session context.
+ * @param bim [in] The bigint modulus that will be used.
+ * @param mod_offset [in] There are three moduluii that can be stored - the
+ * standard modulus, and its two primes p and q. This offset refers to which
+ * modulus we are referring to.
+ * @see bi_free_mod(), bi_mod_power().
+ */
+void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset)
+{
+ int k = bim->size;
+ comp d = (comp)((long_comp)COMP_RADIX/(bim->comps[k-1]+1));
+#ifdef CONFIG_BIGINT_MONTGOMERY
+ bigint *R, *R2;
+#endif
+
+ ctx->bi_mod[mod_offset] = bim;
+ bi_permanent(ctx->bi_mod[mod_offset]);
+ ctx->bi_normalised_mod[mod_offset] = bi_int_multiply(ctx, bim, d);
+ bi_permanent(ctx->bi_normalised_mod[mod_offset]);
+
+#if defined(CONFIG_BIGINT_MONTGOMERY)
+ /* set montgomery variables */
+ R = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k-1); /* R */
+ R2 = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k*2-1); /* R^2 */
+ ctx->bi_RR_mod_m[mod_offset] = bi_mod(ctx, R2); /* R^2 mod m */
+ ctx->bi_R_mod_m[mod_offset] = bi_mod(ctx, R); /* R mod m */
+
+ bi_permanent(ctx->bi_RR_mod_m[mod_offset]);
+ bi_permanent(ctx->bi_R_mod_m[mod_offset]);
+
+ ctx->N0_dash[mod_offset] = modular_inverse(ctx->bi_mod[mod_offset]);
+
+#elif defined (CONFIG_BIGINT_BARRETT)
+ ctx->bi_mu[mod_offset] =
+ bi_divide(ctx, comp_left_shift(
+ bi_clone(ctx, ctx->bi_radix), k*2-1), ctx->bi_mod[mod_offset], 0);
+ bi_permanent(ctx->bi_mu[mod_offset]);
+#endif
+}
+
+/**
+ * @brief Used when cleaning various bigints at the end of a session.
+ * @param ctx [in] The bigint session context.
+ * @param mod_offset [in] The offset to use.
+ * @see bi_set_mod().
+ */
+void bi_free_mod(BI_CTX *ctx, int mod_offset)
+{
+ bi_depermanent(ctx->bi_mod[mod_offset]);
+ bi_free(ctx, ctx->bi_mod[mod_offset]);
+#if defined (CONFIG_BIGINT_MONTGOMERY)
+ bi_depermanent(ctx->bi_RR_mod_m[mod_offset]);
+ bi_depermanent(ctx->bi_R_mod_m[mod_offset]);
+ bi_free(ctx, ctx->bi_RR_mod_m[mod_offset]);
+ bi_free(ctx, ctx->bi_R_mod_m[mod_offset]);
+#elif defined(CONFIG_BIGINT_BARRETT)
+ bi_depermanent(ctx->bi_mu[mod_offset]);
+ bi_free(ctx, ctx->bi_mu[mod_offset]);
+#endif
+ bi_depermanent(ctx->bi_normalised_mod[mod_offset]);
+ bi_free(ctx, ctx->bi_normalised_mod[mod_offset]);
+}
+
+/**
+ * Perform a standard multiplication between two bigints.
+ */
+static bigint *regular_multiply(BI_CTX *ctx, bigint *bia, bigint *bib)
+{
+ int i, j, i_plus_j;
+ int n = bia->size;
+ int t = bib->size;
+ bigint *biR = alloc(ctx, n + t);
+ comp *sr = biR->comps;
+ comp *sa = bia->comps;
+ comp *sb = bib->comps;
+
+ check(bia);
+ check(bib);
+
+ /* clear things to start with */
+ memset(biR->comps, 0, ((n+t)*COMP_BYTE_SIZE));
+ i = 0;
+
+ do
+ {
+ comp carry = 0;
+ comp b = *sb++;
+ i_plus_j = i;
+ j = 0;
+
+ do
+ {
+ long_comp tmp = sr[i_plus_j] + (long_comp)sa[j]*b + carry;
+ sr[i_plus_j++] = (comp)tmp; /* downsize */
+ carry = (comp)(tmp >> COMP_BIT_SIZE);
+ } while (++j < n);
+
+ sr[i_plus_j] = carry;
+ } while (++i < t);
+
+ bi_free(ctx, bia);
+ bi_free(ctx, bib);
+ return trim(biR);
+}
+
+#ifdef CONFIG_BIGINT_KARATSUBA
+/*
+ * Karatsuba improves on regular multiplication due to only 3 multiplications
+ * being done instead of 4. The additional additions/subtractions are O(N)
+ * rather than O(N^2) and so for big numbers it saves on a few operations
+ */
+static bigint *karatsuba(BI_CTX *ctx, bigint *bia, bigint *bib, int is_square)
+{
+ bigint *x0, *x1;
+ bigint *p0, *p1, *p2;
+ int m;
+
+ if (is_square)
+ {
+ m = (bia->size + 1)/2;
+ }
+ else
+ {
+ m = (max(bia->size, bib->size) + 1)/2;
+ }
+
+ x0 = bi_clone(ctx, bia);
+ x0->size = m;
+ x1 = bi_clone(ctx, bia);
+ comp_right_shift(x1, m);
+ bi_free(ctx, bia);
+
+ /* work out the 3 partial products */
+ if (is_square)
+ {
+ p0 = bi_square(ctx, bi_copy(x0));
+ p2 = bi_square(ctx, bi_copy(x1));
+ p1 = bi_square(ctx, bi_add(ctx, x0, x1));
+ }
+ else /* normal multiply */
+ {
+ bigint *y0, *y1;
+ y0 = bi_clone(ctx, bib);
+ y0->size = m;
+ y1 = bi_clone(ctx, bib);
+ comp_right_shift(y1, m);
+ bi_free(ctx, bib);
+
+ p0 = bi_multiply(ctx, bi_copy(x0), bi_copy(y0));
+ p2 = bi_multiply(ctx, bi_copy(x1), bi_copy(y1));
+ p1 = bi_multiply(ctx, bi_add(ctx, x0, x1), bi_add(ctx, y0, y1));
+ }
+
+ p1 = bi_subtract(ctx,
+ bi_subtract(ctx, p1, bi_copy(p2), NULL), bi_copy(p0), NULL);
+
+ comp_left_shift(p1, m);
+ comp_left_shift(p2, 2*m);
+ return bi_add(ctx, p1, bi_add(ctx, p0, p2));
+}
+#endif
+
+/**
+ * @brief Perform a multiplication operation between two bigints.
+ * @param ctx [in] The bigint session context.
+ * @param bia [in] A bigint.
+ * @param bib [in] Another bigint.
+ * @return The result of the multiplication.
+ */
+bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib)
+{
+ check(bia);
+ check(bib);
+
+#ifdef CONFIG_BIGINT_KARATSUBA
+ if (min(bia->size, bib->size) < MUL_KARATSUBA_THRESH)
+ {
+ return regular_multiply(ctx, bia, bib);
+ }
+
+ return karatsuba(ctx, bia, bib, 0);
+#else
+ return regular_multiply(ctx, bia, bib);
+#endif
+}
+
+#ifdef CONFIG_BIGINT_SQUARE
+/*
+ * Perform the actual square operion. It takes into account overflow.
+ */
+static bigint *regular_square(BI_CTX *ctx, bigint *bi)
+{
+ int t = bi->size;
+ int i = 0, j;
+ bigint *biR = alloc(ctx, t*2);
+ comp *w = biR->comps;
+ comp *x = bi->comps;
+ comp carry;
+
+ memset(w, 0, biR->size*COMP_BYTE_SIZE);
+
+ do
+ {
+ long_comp tmp = w[2*i] + (long_comp)x[i]*x[i];
+ comp u = 0;
+ w[2*i] = (comp)tmp;
+ carry = (comp)(tmp >> COMP_BIT_SIZE);
+
+ for (j = i+1; j < t; j++)
+ {
+ long_comp xx = (long_comp)x[i]*x[j];
+ long_comp blob = (long_comp)w[i+j]+carry;
+
+ if (u) /* previous overflow */
+ {
+ blob += COMP_RADIX;
+ }
+
+ u = 0;
+ if (xx & COMP_BIG_MSB) /* check for overflow */
+ {
+ u = 1;
+ }
+
+ tmp = 2*xx + blob;
+ w[i+j] = (comp)tmp;
+ carry = (comp)(tmp >> COMP_BIT_SIZE);
+ }
+
+ w[i+t] += carry;
+
+ if (u)
+ {
+ w[i+t+1] = 1; /* add carry */
+ }
+ } while (++i < t);
+
+ bi_free(ctx, bi);
+ return trim(biR);
+}
+
+/**
+ * @brief Perform a square operation on a bigint.
+ * @param ctx [in] The bigint session context.
+ * @param bia [in] A bigint.
+ * @return The result of the multiplication.
+ */
+bigint *bi_square(BI_CTX *ctx, bigint *bia)
+{
+ check(bia);
+
+#ifdef CONFIG_BIGINT_KARATSUBA
+ if (bia->size < SQU_KARATSUBA_THRESH)
+ {
+ return regular_square(ctx, bia);
+ }
+
+ return karatsuba(ctx, bia, NULL, 1);
+#else
+ return regular_square(ctx, bia);
+#endif
+}
+#endif
+
+/**
+ * @brief Compare two bigints.
+ * @param bia [in] A bigint.
+ * @param bib [in] Another bigint.
+ * @return -1 if smaller, 1 if larger and 0 if equal.
+ */
+int bi_compare(bigint *bia, bigint *bib)
+{
+ int r, i;
+
+ check(bia);
+ check(bib);
+
+ if (bia->size > bib->size)
+ r = 1;
+ else if (bia->size < bib->size)
+ r = -1;
+ else
+ {
+ comp *a = bia->comps;
+ comp *b = bib->comps;
+
+ /* Same number of components. Compare starting from the high end
+ * and working down. */
+ r = 0;
+ i = bia->size - 1;
+
+ do
+ {
+ if (a[i] > b[i])
+ {
+ r = 1;
+ break;
+ }
+ else if (a[i] < b[i])
+ {
+ r = -1;
+ break;
+ }
+ } while (--i >= 0);
+ }
+
+ return r;
+}
+
+/*
+ * Allocate and zero more components. Does not consume bi.
+ */
+static void more_comps(bigint *bi, int n)
+{
+ if (n > bi->max_comps)
+ {
+ bi->max_comps = max(bi->max_comps * 2, n);
+ bi->comps = (comp*)realloc(bi->comps, bi->max_comps * COMP_BYTE_SIZE);
+ }
+
+ if (n > bi->size)
+ {
+ memset(&bi->comps[bi->size], 0, (n-bi->size)*COMP_BYTE_SIZE);
+ }
+
+ bi->size = n;
+}
+
+/*
+ * Make a new empty bigint. It may just use an old one if one is available.
+ * Otherwise get one off the heap.
+ */
+static bigint *alloc(BI_CTX *ctx, int size)
+{
+ bigint *biR;
+
+ /* Can we recycle an old bigint? */
+ if (ctx->free_list != NULL)
+ {
+ biR = ctx->free_list;
+ ctx->free_list = biR->next;
+ ctx->free_count--;
+
+ if (biR->refs != 0)
+ {
+#ifdef CONFIG_SSL_FULL_MODE
+ printf("alloc: refs was not 0\n");
+#endif
+ abort(); /* create a stack trace from a core dump */
+ }
+
+ more_comps(biR, size);
+ }
+ else
+ {
+ /* No free bigints available - create a new one. */
+ biR = (bigint *)malloc(sizeof(bigint));
+ biR->comps = (comp*)malloc(size * COMP_BYTE_SIZE);
+ biR->max_comps = size; /* give some space to spare */
+ }
+
+ biR->size = size;
+ biR->refs = 1;
+ biR->next = NULL;
+ ctx->active_count++;
+ return biR;
+}
+
+/*
+ * Work out the highest '1' bit in an exponent. Used when doing sliding-window
+ * exponentiation.
+ */
+static int find_max_exp_index(bigint *biexp)
+{
+ int i = COMP_BIT_SIZE-1;
+ comp shift = COMP_RADIX/2;
+ comp test = biexp->comps[biexp->size-1]; /* assume no leading zeroes */
+
+ check(biexp);
+
+ do
+ {
+ if (test & shift)
+ {
+ return i+(biexp->size-1)*COMP_BIT_SIZE;
+ }
+
+ shift >>= 1;
+ } while (--i != 0);
+
+ return -1; /* error - must have been a leading 0 */
+}
+
+/*
+ * Is a particular bit is an exponent 1 or 0? Used when doing sliding-window
+ * exponentiation.
+ */
+static int exp_bit_is_one(bigint *biexp, int offset)
+{
+ comp test = biexp->comps[offset / COMP_BIT_SIZE];
+ int num_shifts = offset % COMP_BIT_SIZE;
+ comp shift = 1;
+ int i;
+
+ check(biexp);
+
+ for (i = 0; i < num_shifts; i++)
+ {
+ shift <<= 1;
+ }
+
+ return test & shift;
+}
+
+#ifdef CONFIG_BIGINT_CHECK_ON
+/*
+ * Perform a sanity check on bi.
+ */
+static void check(const bigint *bi)
+{
+ if (bi->refs <= 0)
+ {
+ printf("check: zero or negative refs in bigint\n");
+ abort();
+ }
+
+ if (bi->next != NULL)
+ {
+ printf("check: attempt to use a bigint from "
+ "the free list\n");
+ abort();
+ }
+}
+#endif
+
+/*
+ * Delete any leading 0's (and allow for 0).
+ */
+static bigint *trim(bigint *bi)
+{
+ check(bi);
+
+ while (bi->comps[bi->size-1] == 0 && bi->size > 1)
+ {
+ bi->size--;
+ }
+
+ return bi;
+}
+
+#if defined(CONFIG_BIGINT_MONTGOMERY)
+/**
+ * @brief Perform a single montgomery reduction.
+ * @param ctx [in] The bigint session context.
+ * @param bixy [in] A bigint.
+ * @return The result of the montgomery reduction.
+ */
+bigint *bi_mont(BI_CTX *ctx, bigint *bixy)
+{
+ int i = 0, n;
+ uint8_t mod_offset = ctx->mod_offset;
+ bigint *bim = ctx->bi_mod[mod_offset];
+ comp mod_inv = ctx->N0_dash[mod_offset];
+
+ check(bixy);
+
+ if (ctx->use_classical) /* just use classical instead */
+ {
+ return bi_mod(ctx, bixy);
+ }
+
+ n = bim->size;
+
+ do
+ {
+ bixy = bi_add(ctx, bixy, comp_left_shift(
+ bi_int_multiply(ctx, bim, bixy->comps[i]*mod_inv), i));
+ } while (++i < n);
+
+ comp_right_shift(bixy, n);
+
+ if (bi_compare(bixy, bim) >= 0)
+ {
+ bixy = bi_subtract(ctx, bixy, bim, NULL);
+ }
+
+ return bixy;
+}
+
+#elif defined(CONFIG_BIGINT_BARRETT)
+/*
+ * Stomp on the most significant components to give the illusion of a "mod base
+ * radix" operation
+ */
+static bigint *comp_mod(bigint *bi, int mod)
+{
+ check(bi);
+
+ if (bi->size > mod)
+ {
+ bi->size = mod;
+ }
+
+ return bi;
+}
+
+/*
+ * Barrett reduction has no need for some parts of the product, so ignore bits
+ * of the multiply. This routine gives Barrett its big performance
+ * improvements over Classical/Montgomery reduction methods.
+ */
+static bigint *partial_multiply(BI_CTX *ctx, bigint *bia, bigint *bib,
+ int inner_partial, int outer_partial)
+{
+ int i = 0, j, n = bia->size, t = bib->size;
+ bigint *biR;
+ comp carry;
+ comp *sr, *sa, *sb;
+
+ check(bia);
+ check(bib);
+
+ biR = alloc(ctx, n + t);
+ sa = bia->comps;
+ sb = bib->comps;
+ sr = biR->comps;
+
+ if (inner_partial)
+ {
+ memset(sr, 0, inner_partial*COMP_BYTE_SIZE);
+ }
+ else /* outer partial */
+ {
+ if (n < outer_partial || t < outer_partial) /* should we bother? */
+ {
+ bi_free(ctx, bia);
+ bi_free(ctx, bib);
+ biR->comps[0] = 0; /* return 0 */
+ biR->size = 1;
+ return biR;
+ }
+
+ memset(&sr[outer_partial], 0, (n+t-outer_partial)*COMP_BYTE_SIZE);
+ }
+
+ do
+ {
+ comp *a = sa;
+ comp b = *sb++;
+ long_comp tmp;
+ int i_plus_j = i;
+ carry = 0;
+ j = n;
+
+ if (outer_partial && i_plus_j < outer_partial)
+ {
+ i_plus_j = outer_partial;
+ a = &sa[outer_partial-i];
+ j = n-(outer_partial-i);
+ }
+
+ do
+ {
+ if (inner_partial && i_plus_j >= inner_partial)
+ {
+ break;
+ }
+
+ tmp = sr[i_plus_j] + ((long_comp)*a++)*b + carry;
+ sr[i_plus_j++] = (comp)tmp; /* downsize */
+ carry = (comp)(tmp >> COMP_BIT_SIZE);
+ } while (--j != 0);
+
+ sr[i_plus_j] = carry;
+ } while (++i < t);
+
+ bi_free(ctx, bia);
+ bi_free(ctx, bib);
+ return trim(biR);
+}
+
+/**
+ * @brief Perform a single Barrett reduction.
+ * @param ctx [in] The bigint session context.
+ * @param bi [in] A bigint.
+ * @return The result of the Barrett reduction.
+ */
+bigint *bi_barrett(BI_CTX *ctx, bigint *bi)
+{
+ bigint *q1, *q2, *q3, *r1, *r2, *r;
+ uint8_t mod_offset = ctx->mod_offset;
+ bigint *bim = ctx->bi_mod[mod_offset];
+ int k = bim->size;
+
+ check(bi);
+ check(bim);
+
+ /* use Classical method instead - Barrett cannot help here */
+ if (bi->size > k*2)
+ {
+ return bi_mod(ctx, bi);
+ }
+
+ q1 = comp_right_shift(bi_clone(ctx, bi), k-1);
+
+ /* do outer partial multiply */
+ q2 = partial_multiply(ctx, q1, ctx->bi_mu[mod_offset], 0, k-1);
+ q3 = comp_right_shift(q2, k+1);
+ r1 = comp_mod(bi, k+1);
+
+ /* do inner partial multiply */
+ r2 = comp_mod(partial_multiply(ctx, q3, bim, k+1, 0), k+1);
+ r = bi_subtract(ctx, r1, r2, NULL);
+
+ /* if (r >= m) r = r - m; */
+ if (bi_compare(r, bim) >= 0)
+ {
+ r = bi_subtract(ctx, r, bim, NULL);
+ }
+
+ return r;
+}
+#endif /* CONFIG_BIGINT_BARRETT */
+
+#ifdef CONFIG_BIGINT_SLIDING_WINDOW
+/*
+ * Work out g1, g3, g5, g7... etc for the sliding-window algorithm
+ */
+static void precompute_slide_window(BI_CTX *ctx, int window, bigint *g1)
+{
+ int k = 1, i;
+ bigint *g2;
+
+ for (i = 0; i < window-1; i++) /* compute 2^(window-1) */
+ {
+ k <<= 1;
+ }
+
+ ctx->g = (bigint **)malloc(k*sizeof(bigint *));
+ ctx->g[0] = bi_clone(ctx, g1);
+ bi_permanent(ctx->g[0]);
+ g2 = bi_residue(ctx, bi_square(ctx, ctx->g[0])); /* g^2 */
+
+ for (i = 1; i < k; i++)
+ {
+ ctx->g[i] = bi_residue(ctx, bi_multiply(ctx, ctx->g[i-1], bi_copy(g2)));
+ bi_permanent(ctx->g[i]);
+ }
+
+ bi_free(ctx, g2);
+ ctx->window = k;
+}
+#endif
+
+/**
+ * @brief Perform a modular exponentiation.
+ *
+ * This function requires bi_set_mod() to have been called previously. This is
+ * one of the optimisations used for performance.
+ * @param ctx [in] The bigint session context.
+ * @param bi [in] The bigint on which to perform the mod power operation.
+ * @param biexp [in] The bigint exponent.
+ * @see bi_set_mod().
+ */
+bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp)
+{
+ int i = find_max_exp_index(biexp), j, window_size = 1;
+ bigint *biR = int_to_bi(ctx, 1);
+
+#if defined(CONFIG_BIGINT_MONTGOMERY)
+ uint8_t mod_offset = ctx->mod_offset;
+ if (!ctx->use_classical)
+ {
+ /* preconvert */
+ bi = bi_mont(ctx,
+ bi_multiply(ctx, bi, ctx->bi_RR_mod_m[mod_offset])); /* x' */
+ bi_free(ctx, biR);
+ biR = ctx->bi_R_mod_m[mod_offset]; /* A */
+ }
+#endif
+
+ check(bi);
+ check(biexp);
+
+#ifdef CONFIG_BIGINT_SLIDING_WINDOW
+ for (j = i; j > 32; j /= 5) /* work out an optimum size */
+ window_size++;
+
+ /* work out the slide constants */
+ precompute_slide_window(ctx, window_size, bi);
+#else /* just one constant */
+ ctx->g = (bigint **)malloc(sizeof(bigint *));
+ ctx->g[0] = bi_clone(ctx, bi);
+ ctx->window = 1;
+ bi_permanent(ctx->g[0]);
+#endif
+
+ /* if sliding-window is off, then only one bit will be done at a time and
+ * will reduce to standard left-to-right exponentiation */
+ do
+ {
+ if (exp_bit_is_one(biexp, i))
+ {
+ int l = i-window_size+1;
+ int part_exp = 0;
+
+ if (l < 0) /* LSB of exponent will always be 1 */
+ l = 0;
+ else
+ {
+ while (exp_bit_is_one(biexp, l) == 0)
+ l++; /* go back up */
+ }
+
+ /* build up the section of the exponent */
+ for (j = i; j >= l; j--)
+ {
+ biR = bi_residue(ctx, bi_square(ctx, biR));
+ if (exp_bit_is_one(biexp, j))
+ part_exp++;
+
+ if (j != l)
+ part_exp <<= 1;
+ }
+
+ part_exp = (part_exp-1)/2; /* adjust for array */
+ biR = bi_residue(ctx, bi_multiply(ctx, biR, ctx->g[part_exp]));
+ i = l-1;
+ }
+ else /* square it */
+ {
+ biR = bi_residue(ctx, bi_square(ctx, biR));
+ i--;
+ }
+ } while (i >= 0);
+
+ /* cleanup */
+ for (i = 0; i < ctx->window; i++)
+ {
+ bi_depermanent(ctx->g[i]);
+ bi_free(ctx, ctx->g[i]);
+ }
+
+ free(ctx->g);
+ bi_free(ctx, bi);
+ bi_free(ctx, biexp);
+#if defined CONFIG_BIGINT_MONTGOMERY
+ return ctx->use_classical ? biR : bi_mont(ctx, biR); /* convert back */
+#else /* CONFIG_BIGINT_CLASSICAL or CONFIG_BIGINT_BARRETT */
+ return biR;
+#endif
+}
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+/**
+ * @brief Perform a modular exponentiation using a temporary modulus.
+ *
+ * We need this function to check the signatures of certificates. The modulus
+ * of this function is temporary as it's just used for authentication.
+ * @param ctx [in] The bigint session context.
+ * @param bi [in] The bigint to perform the exp/mod.
+ * @param bim [in] The temporary modulus.
+ * @param biexp [in] The bigint exponent.
+ * @see bi_set_mod().
+ */
+bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp)
+{
+ bigint *biR, *tmp_biR;
+
+ /* Set up a temporary bigint context and transfer what we need between
+ * them. We need to do this since we want to keep the original modulus
+ * which is already in this context. This operation is only called when
+ * doing peer verification, and so is not expensive :-) */
+ BI_CTX *tmp_ctx = bi_initialize();
+ bi_set_mod(tmp_ctx, bi_clone(tmp_ctx, bim), BIGINT_M_OFFSET);
+ tmp_biR = bi_mod_power(tmp_ctx,
+ bi_clone(tmp_ctx, bi),
+ bi_clone(tmp_ctx, biexp));
+ biR = bi_clone(ctx, tmp_biR);
+ bi_free(tmp_ctx, tmp_biR);
+ bi_free_mod(tmp_ctx, BIGINT_M_OFFSET);
+ bi_terminate(tmp_ctx);
+
+ bi_free(ctx, bi);
+ bi_free(ctx, bim);
+ bi_free(ctx, biexp);
+ return biR;
+}
+#endif
+/** @} */
diff --git a/gpxe/src/crypto/axtls/bigint.h b/gpxe/src/crypto/axtls/bigint.h
new file mode 100644
index 00000000..5a13c5ae
--- /dev/null
+++ b/gpxe/src/crypto/axtls/bigint.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright(C) 2006 Cameron Rich
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BIGINT_HEADER
+#define BIGINT_HEADER
+
+#include "config.h"
+
+/* enable features based on a 'super-set' capbaility. */
+#if defined(CONFIG_SSL_FULL_MODE)
+#define CONFIG_SSL_ENABLE_CLIENT
+#define CONFIG_SSL_CERT_VERIFICATION
+#elif defined(CONFIG_SSL_ENABLE_CLIENT)
+#define CONFIG_SSL_CERT_VERIFICATION
+#endif
+
+#include "os_port.h"
+#include "bigint_impl.h"
+
+#ifndef CONFIG_BIGINT_CHECK_ON
+#define check(A) /**< disappears in normal production mode */
+#endif
+BI_CTX *bi_initialize(void);
+void bi_terminate(BI_CTX *ctx);
+void bi_permanent(bigint *bi);
+void bi_depermanent(bigint *bi);
+void bi_free(BI_CTX *ctx, bigint *bi);
+bigint *bi_copy(bigint *bi);
+bigint *bi_clone(BI_CTX *ctx, const bigint *bi);
+void bi_export(BI_CTX *ctx, bigint *bi, uint8_t *data, int size);
+bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int len);
+bigint *int_to_bi(BI_CTX *ctx, comp i);
+
+/* the functions that actually do something interesting */
+bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib);
+bigint *bi_subtract(BI_CTX *ctx, bigint *bia,
+ bigint *bib, int *is_negative);
+bigint *bi_divide(BI_CTX *ctx, bigint *bia, bigint *bim, int is_mod);
+bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib);
+bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp);
+bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp);
+int bi_compare(bigint *bia, bigint *bib);
+void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset);
+void bi_free_mod(BI_CTX *ctx, int mod_offset);
+
+#ifdef CONFIG_SSL_FULL_MODE
+void bi_print(const char *label, bigint *bi);
+bigint *bi_str_import(BI_CTX *ctx, const char *data);
+#endif
+
+/**
+ * @def bi_mod
+ * Find the residue of B. bi_set_mod() must be called before hand.
+ */
+#define bi_mod(A, B) bi_divide(A, B, ctx->bi_mod[ctx->mod_offset], 1)
+
+/**
+ * bi_residue() is technically the same as bi_mod(), but it uses the
+ * appropriate reduction technique (which is bi_mod() when doing classical
+ * reduction).
+ */
+#if defined(CONFIG_BIGINT_MONTGOMERY)
+#define bi_residue(A, B) bi_mont(A, B)
+bigint *bi_mont(BI_CTX *ctx, bigint *bixy);
+#elif defined(CONFIG_BIGINT_BARRETT)
+#define bi_residue(A, B) bi_barrett(A, B)
+bigint *bi_barrett(BI_CTX *ctx, bigint *bi);
+#else /* if defined(CONFIG_BIGINT_CLASSICAL) */
+#define bi_residue(A, B) bi_mod(A, B)
+#endif
+
+#ifdef CONFIG_BIGINT_SQUARE
+bigint *bi_square(BI_CTX *ctx, bigint *bi);
+#else
+#define bi_square(A, B) bi_multiply(A, bi_copy(B), B)
+#endif
+
+#endif
diff --git a/gpxe/src/crypto/axtls/bigint_impl.h b/gpxe/src/crypto/axtls/bigint_impl.h
new file mode 100644
index 00000000..762a7ccb
--- /dev/null
+++ b/gpxe/src/crypto/axtls/bigint_impl.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright(C) 2006 Cameron Rich
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BIGINT_IMPL_HEADER
+#define BIGINT_IMPL_HEADER
+
+/* Maintain a number of precomputed variables when doing reduction */
+#define BIGINT_M_OFFSET 0 /**< Normal modulo offset. */
+#ifdef CONFIG_BIGINT_CRT
+#define BIGINT_P_OFFSET 1 /**< p modulo offset. */
+#define BIGINT_Q_OFFSET 2 /**< q module offset. */
+#define BIGINT_NUM_MODS 3 /**< The number of modulus constants used. */
+#else
+#define BIGINT_NUM_MODS 1
+#endif
+
+/* Architecture specific functions for big ints */
+#ifdef WIN32
+#define COMP_RADIX 4294967296i64
+#define COMP_BIG_MSB 0x8000000000000000i64
+#else
+#define COMP_RADIX 4294967296ULL /**< Max component + 1 */
+#define COMP_BIG_MSB 0x8000000000000000ULL /**< (Max dbl comp + 1)/ 2 */
+#endif
+#define COMP_BIT_SIZE 32 /**< Number of bits in a component. */
+#define COMP_BYTE_SIZE 4 /**< Number of bytes in a component. */
+#define COMP_NUM_NIBBLES 8 /**< Used For diagnostics only. */
+
+typedef uint32_t comp; /**< A single precision component. */
+typedef uint64_t long_comp; /**< A double precision component. */
+typedef int64_t slong_comp; /**< A signed double precision component. */
+
+/**
+ * @struct _bigint
+ * @brief A big integer basic object
+ */
+struct _bigint
+{
+ struct _bigint* next; /**< The next bigint in the cache. */
+ short size; /**< The number of components in this bigint. */
+ short max_comps; /**< The heapsize allocated for this bigint */
+ int refs; /**< An internal reference count. */
+ comp* comps; /**< A ptr to the actual component data */
+};
+
+typedef struct _bigint bigint; /**< An alias for _bigint */
+
+/**
+ * Maintains the state of the cache, and a number of variables used in
+ * reduction.
+ */
+typedef struct /**< A big integer "session" context. */
+{
+ bigint *active_list; /**< Bigints currently used. */
+ bigint *free_list; /**< Bigints not used. */
+ bigint *bi_radix; /**< The radix used. */
+ bigint *bi_mod[BIGINT_NUM_MODS]; /**< modulus */
+
+#if defined(CONFIG_BIGINT_MONTGOMERY)
+ bigint *bi_RR_mod_m[BIGINT_NUM_MODS]; /**< R^2 mod m */
+ bigint *bi_R_mod_m[BIGINT_NUM_MODS]; /**< R mod m */
+ comp N0_dash[BIGINT_NUM_MODS];
+#elif defined(CONFIG_BIGINT_BARRETT)
+ bigint *bi_mu[BIGINT_NUM_MODS]; /**< Storage for mu */
+#endif
+ bigint *bi_normalised_mod[BIGINT_NUM_MODS]; /**< Normalised mod storage. */
+ bigint **g; /**< Used by sliding-window. */
+ int window; /**< The size of the sliding window */
+ int active_count; /**< Number of active bigints. */
+ int free_count; /**< Number of free bigints. */
+
+#ifdef CONFIG_BIGINT_MONTGOMERY
+ uint8_t use_classical; /**< Use classical reduction. */
+#endif
+ uint8_t mod_offset; /**< The mod offset we are using */
+} BI_CTX;
+
+#ifndef WIN32
+#define max(a,b) ((a)>(b)?(a):(b)) /**< Find the maximum of 2 numbers. */
+#define min(a,b) ((a)<(b)?(a):(b)) /**< Find the minimum of 2 numbers. */
+#endif
+
+#define PERMANENT 0x7FFF55AA /**< A magic number for permanents. */
+
+#define V1 v->comps[v->size-1] /**< v1 for division */
+#define V2 v->comps[v->size-2] /**< v2 for division */
+#define U(j) tmp_u->comps[tmp_u->size-j-1] /**< uj for division */
+#define Q(j) quotient->comps[quotient->size-j-1] /**< qj for division */
+
+#endif
diff --git a/gpxe/src/crypto/axtls/crypto.h b/gpxe/src/crypto/axtls/crypto.h
new file mode 100644
index 00000000..de1dbeb4
--- /dev/null
+++ b/gpxe/src/crypto/axtls/crypto.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright(C) 2006 Cameron Rich
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * @file crypto.h
+ */
+
+#ifndef HEADER_CRYPTO_H
+#define HEADER_CRYPTO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "bigint.h"
+
+/**************************************************************************
+ * AES declarations
+ **************************************************************************/
+
+#define AES_MAXROUNDS 14
+
+typedef struct aes_key_st
+{
+ uint16_t rounds;
+ uint16_t key_size;
+ uint32_t ks[(AES_MAXROUNDS+1)*8];
+ uint8_t iv[16];
+} AES_CTX;
+
+typedef enum
+{
+ AES_MODE_128,
+ AES_MODE_256
+} AES_MODE;
+
+void AES_set_key(AES_CTX *ctx, const uint8_t *key,
+ const uint8_t *iv, AES_MODE mode);
+void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg,
+ uint8_t *out, int length);
+void AES_cbc_decrypt(AES_CTX *ks, const uint8_t *in, uint8_t *out, int length);
+void AES_convert_key(AES_CTX *ctx);
+
+/**************************************************************************
+ * RC4 declarations
+ **************************************************************************/
+
+typedef struct
+{
+ int x, y, m[256];
+} RC4_CTX;
+
+void RC4_setup(RC4_CTX *s, const uint8_t *key, int length);
+void RC4_crypt(RC4_CTX *s, const uint8_t *msg, uint8_t *data, int length);
+
+/**************************************************************************
+ * SHA1 declarations
+ **************************************************************************/
+
+#define SHA1_SIZE 20
+
+/*
+ * This structure will hold context information for the SHA-1
+ * hashing operation
+ */
+typedef struct
+{
+ uint32_t Intermediate_Hash[SHA1_SIZE/4]; /* Message Digest */
+ uint32_t Length_Low; /* Message length in bits */
+ uint32_t Length_High; /* Message length in bits */
+ uint16_t Message_Block_Index; /* Index into message block array */
+ uint8_t Message_Block[64]; /* 512-bit message blocks */
+} SHA1_CTX;
+
+void SHA1Init(SHA1_CTX *);
+void SHA1Update(SHA1_CTX *, const uint8_t * msg, int len);
+void SHA1Final(SHA1_CTX *, uint8_t *digest);
+
+/**************************************************************************
+ * MD5 declarations
+ **************************************************************************/
+
+/* MD5 context. */
+
+#define MD5_SIZE 16
+
+typedef struct
+{
+ uint32_t state[4]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint8_t buffer[64]; /* input buffer */
+} MD5_CTX;
+
+void MD5Init(MD5_CTX *);
+void MD5Update(MD5_CTX *, const uint8_t *msg, int len);
+void MD5Final(MD5_CTX *, uint8_t *digest);
+
+/**************************************************************************
+ * HMAC declarations
+ **************************************************************************/
+void hmac_md5(const uint8_t *msg, int length, const uint8_t *key,
+ int key_len, uint8_t *digest);
+void hmac_sha1(const uint8_t *msg, int length, const uint8_t *key,
+ int key_len, uint8_t *digest);
+
+/**************************************************************************
+ * RNG declarations
+ **************************************************************************/
+void RNG_initialize(const uint8_t *seed_buf, int size);
+void RNG_terminate(void);
+void get_random(int num_rand_bytes, uint8_t *rand_data);
+//void get_random_NZ(int num_rand_bytes, uint8_t *rand_data);
+
+#include <string.h>
+static inline void get_random_NZ(int num_rand_bytes, uint8_t *rand_data) {
+ memset ( rand_data, 0x01, num_rand_bytes );
+}
+
+/**************************************************************************
+ * RSA declarations
+ **************************************************************************/
+
+typedef struct
+{
+ bigint *m; /* modulus */
+ bigint *e; /* public exponent */
+ bigint *d; /* private exponent */
+#ifdef CONFIG_BIGINT_CRT
+ bigint *p; /* p as in m = pq */
+ bigint *q; /* q as in m = pq */
+ bigint *dP; /* d mod (p-1) */
+ bigint *dQ; /* d mod (q-1) */
+ bigint *qInv; /* q^-1 mod p */
+#endif
+ int num_octets;
+ bigint *sig_m; /* signature modulus */
+ BI_CTX *bi_ctx;
+} RSA_CTX;
+
+void RSA_priv_key_new(RSA_CTX **rsa_ctx,
+ const uint8_t *modulus, int mod_len,
+ const uint8_t *pub_exp, int pub_len,
+ const uint8_t *priv_exp, int priv_len
+#ifdef CONFIG_BIGINT_CRT
+ , const uint8_t *p, int p_len,
+ const uint8_t *q, int q_len,
+ const uint8_t *dP, int dP_len,
+ const uint8_t *dQ, int dQ_len,
+ const uint8_t *qInv, int qInv_len
+#endif
+ );
+void RSA_pub_key_new(RSA_CTX **rsa_ctx,
+ const uint8_t *modulus, int mod_len,
+ const uint8_t *pub_exp, int pub_len);
+void RSA_free(RSA_CTX *ctx);
+int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data,
+ int is_decryption);
+bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg);
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+bigint *RSA_raw_sign_verify(RSA_CTX *c, bigint *bi_msg);
+bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len,
+ bigint *modulus, bigint *pub_exp);
+bigint *RSA_public(const RSA_CTX *c, bigint *bi_msg);
+int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len,
+ uint8_t *out_data, int is_signing);
+void RSA_print(const RSA_CTX *ctx);
+#endif
+
+/**************************************************************************
+ * ASN1 declarations
+ **************************************************************************/
+#define X509_OK 0
+#define X509_NOT_OK -1
+#define X509_VFY_ERROR_NO_TRUSTED_CERT -2
+#define X509_VFY_ERROR_BAD_SIGNATURE -3
+#define X509_VFY_ERROR_NOT_YET_VALID -4
+#define X509_VFY_ERROR_EXPIRED -5
+#define X509_VFY_ERROR_SELF_SIGNED -6
+#define X509_VFY_ERROR_INVALID_CHAIN -7
+#define X509_VFY_ERROR_UNSUPPORTED_DIGEST -8
+#define X509_INVALID_PRIV_KEY -9
+
+/*
+ * The Distinguished Name
+ */
+#define X509_NUM_DN_TYPES 3
+#define X509_COMMON_NAME 0
+#define X509_ORGANIZATION 1
+#define X509_ORGANIZATIONAL_TYPE 2
+
+#define ASN1_INTEGER 0x02
+#define ASN1_BIT_STRING 0x03
+#define ASN1_OCTET_STRING 0x04
+#define ASN1_NULL 0x05
+#define ASN1_OID 0x06
+#define ASN1_PRINTABLE_STR 0x13
+#define ASN1_TELETEX_STR 0x14
+#define ASN1_IA5_STR 0x16
+#define ASN1_UTC_TIME 0x17
+#define ASN1_SEQUENCE 0x30
+#define ASN1_SET 0x31
+#define ASN1_IMPLICIT_TAG 0x80
+#define ASN1_EXPLICIT_TAG 0xa0
+
+#define SALT_SIZE 8
+
+struct _x509_ctx
+{
+ char *ca_cert_dn[X509_NUM_DN_TYPES];
+ char *cert_dn[X509_NUM_DN_TYPES];
+#if defined(_WIN32_WCE)
+ long not_before;
+ long not_after;
+#else
+ time_t not_before;
+ time_t not_after;
+#endif
+ uint8_t *signature;
+ uint16_t sig_len;
+ uint8_t sig_type;
+ RSA_CTX *rsa_ctx;
+ bigint *digest;
+ struct _x509_ctx *next;
+};
+
+typedef struct _x509_ctx X509_CTX;
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+typedef struct
+{
+ X509_CTX *cert[CONFIG_X509_MAX_CA_CERTS];
+} CA_CERT_CTX;
+#endif
+
+int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx);
+int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type);
+int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type);
+int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object);
+int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx);
+void x509_free(X509_CTX *x509_ctx);
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert);
+const uint8_t *x509_get_signature(const uint8_t *asn1_signature, int *len);
+#endif
+#ifdef CONFIG_SSL_FULL_MODE
+void x509_print(CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert);
+void x509_display_error(int error);
+#endif
+
+/**************************************************************************
+ * MISC declarations
+ **************************************************************************/
+
+extern const char * const unsupported_str;
+
+typedef void (*crypt_func)(void *, const uint8_t *, uint8_t *, int);
+typedef void (*hmac_func)(const uint8_t *msg, int length, const uint8_t *key,
+ int key_len, uint8_t *digest);
+
+typedef struct
+{
+ uint8_t *pre_data; /* include the ssl record bytes */
+ uint8_t *data; /* the regular ssl data */
+ int max_len;
+ int index;
+} BUF_MEM;
+
+BUF_MEM buf_new(void);
+void buf_grow(BUF_MEM *bm, int len);
+void buf_free(BUF_MEM *bm);
+int get_file(const char *filename, uint8_t **buf);
+
+#if defined(CONFIG_SSL_FULL_MODE) || defined(WIN32) || defined(CONFIG_DEBUG)
+void print_blob(const char *format, const uint8_t *data, int size, ...);
+#else
+ #define print_blob(...)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/gpxe/src/crypto/axtls/os_port.h b/gpxe/src/crypto/axtls/os_port.h
new file mode 100644
index 00000000..babdbfad
--- /dev/null
+++ b/gpxe/src/crypto/axtls/os_port.h
@@ -0,0 +1,61 @@
+/**
+ * @file os_port.h
+ *
+ * Trick the axtls code into building within our build environment.
+ */
+
+#ifndef HEADER_OS_PORT_H
+#define HEADER_OS_PORT_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <byteswap.h>
+
+#define STDCALL
+#define EXP_FUNC
+#define TTY_FLUSH()
+
+/** We can't actually abort, since we are effectively a kernel... */
+#define abort() assert ( 0 )
+
+/** crypto_misc.c has a bad #ifdef */
+static inline void close ( int fd __unused ) {
+ /* Do nothing */
+}
+
+typedef void FILE;
+
+static inline FILE * fopen ( const char *filename __unused,
+ const char *mode __unused ) {
+ return NULL;
+}
+
+static inline int fseek ( FILE *stream __unused, long offset __unused,
+ int whence __unused ) {
+ return -1;
+}
+
+static inline long ftell ( FILE *stream __unused ) {
+ return -1;
+}
+
+static inline size_t fread ( void *ptr __unused, size_t size __unused,
+ size_t nmemb __unused, FILE *stream __unused ) {
+ return -1;
+}
+
+static inline int fclose ( FILE *stream __unused ) {
+ return -1;
+}
+
+#define CONFIG_SSL_CERT_VERIFICATION 1
+#define CONFIG_SSL_MAX_CERTS 1
+#define CONFIG_X509_MAX_CA_CERTS 1
+#define CONFIG_SSL_EXPIRY_TIME 24
+#define CONFIG_SSL_ENABLE_CLIENT 1
+#define CONFIG_BIGINT_CLASSICAL 1
+
+#endif
diff --git a/gpxe/src/crypto/axtls/rsa.c b/gpxe/src/crypto/axtls/rsa.c
new file mode 100644
index 00000000..389eda57
--- /dev/null
+++ b/gpxe/src/crypto/axtls/rsa.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright(C) 2006 Cameron Rich
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * Implements the RSA public encryption algorithm. Uses the bigint library to
+ * perform its calculations.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include "crypto.h"
+
+#ifdef CONFIG_BIGINT_CRT
+static bigint *bi_crt(const RSA_CTX *rsa, bigint *bi);
+#endif
+
+void RSA_priv_key_new(RSA_CTX **ctx,
+ const uint8_t *modulus, int mod_len,
+ const uint8_t *pub_exp, int pub_len,
+ const uint8_t *priv_exp, int priv_len
+#if CONFIG_BIGINT_CRT
+ , const uint8_t *p, int p_len,
+ const uint8_t *q, int q_len,
+ const uint8_t *dP, int dP_len,
+ const uint8_t *dQ, int dQ_len,
+ const uint8_t *qInv, int qInv_len
+#endif
+ )
+{
+ RSA_CTX *rsa_ctx;
+ BI_CTX *bi_ctx;
+ RSA_pub_key_new(ctx, modulus, mod_len, pub_exp, pub_len);
+ rsa_ctx = *ctx;
+ bi_ctx = rsa_ctx->bi_ctx;
+ rsa_ctx->d = bi_import(bi_ctx, priv_exp, priv_len);
+ bi_permanent(rsa_ctx->d);
+
+#ifdef CONFIG_BIGINT_CRT
+ rsa_ctx->p = bi_import(bi_ctx, p, p_len);
+ rsa_ctx->q = bi_import(bi_ctx, q, q_len);
+ rsa_ctx->dP = bi_import(bi_ctx, dP, dP_len);
+ rsa_ctx->dQ = bi_import(bi_ctx, dQ, dQ_len);
+ rsa_ctx->qInv = bi_import(bi_ctx, qInv, qInv_len);
+ bi_permanent(rsa_ctx->dP);
+ bi_permanent(rsa_ctx->dQ);
+ bi_permanent(rsa_ctx->qInv);
+ bi_set_mod(bi_ctx, rsa_ctx->p, BIGINT_P_OFFSET);
+ bi_set_mod(bi_ctx, rsa_ctx->q, BIGINT_Q_OFFSET);
+#endif
+}
+
+void RSA_pub_key_new(RSA_CTX **ctx,
+ const uint8_t *modulus, int mod_len,
+ const uint8_t *pub_exp, int pub_len)
+{
+ RSA_CTX *rsa_ctx;
+ BI_CTX *bi_ctx = bi_initialize();
+ *ctx = (RSA_CTX *)calloc(1, sizeof(RSA_CTX));
+ rsa_ctx = *ctx;
+ rsa_ctx->bi_ctx = bi_ctx;
+ rsa_ctx->num_octets = (mod_len & 0xFFF0);
+ rsa_ctx->m = bi_import(bi_ctx, modulus, mod_len);
+ bi_set_mod(bi_ctx, rsa_ctx->m, BIGINT_M_OFFSET);
+ rsa_ctx->e = bi_import(bi_ctx, pub_exp, pub_len);
+ bi_permanent(rsa_ctx->e);
+}
+
+/**
+ * Free up any RSA context resources.
+ */
+void RSA_free(RSA_CTX *rsa_ctx)
+{
+ BI_CTX *bi_ctx;
+ if (rsa_ctx == NULL) /* deal with ptrs that are null */
+ return;
+
+ bi_ctx = rsa_ctx->bi_ctx;
+
+ bi_depermanent(rsa_ctx->e);
+ bi_free(bi_ctx, rsa_ctx->e);
+ bi_free_mod(rsa_ctx->bi_ctx, BIGINT_M_OFFSET);
+
+ if (rsa_ctx->d)
+ {
+ bi_depermanent(rsa_ctx->d);
+ bi_free(bi_ctx, rsa_ctx->d);
+#ifdef CONFIG_BIGINT_CRT
+ bi_depermanent(rsa_ctx->dP);
+ bi_depermanent(rsa_ctx->dQ);
+ bi_depermanent(rsa_ctx->qInv);
+ bi_free(bi_ctx, rsa_ctx->dP);
+ bi_free(bi_ctx, rsa_ctx->dQ);
+ bi_free(bi_ctx, rsa_ctx->qInv);
+ bi_free_mod(rsa_ctx->bi_ctx, BIGINT_P_OFFSET);
+ bi_free_mod(rsa_ctx->bi_ctx, BIGINT_Q_OFFSET);
+#endif
+ }
+
+ bi_terminate(bi_ctx);
+ free(rsa_ctx);
+}
+
+/**
+ * @brief Use PKCS1.5 for decryption/verification.
+ * @param ctx [in] The context
+ * @param in_data [in] The data to encrypt (must be < modulus size-11)
+ * @param out_data [out] The encrypted data.
+ * @param is_decryption [in] Decryption or verify operation.
+ * @return The number of bytes that were originally encrypted. -1 on error.
+ * @see http://www.rsasecurity.com/rsalabs/node.asp?id=2125
+ */
+int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data,
+ uint8_t *out_data, int is_decryption)
+{
+ int byte_size = ctx->num_octets;
+ uint8_t *block;
+ int i, size;
+ bigint *decrypted_bi, *dat_bi;
+
+ memset(out_data, 0, byte_size); /* initialise */
+
+ /* decrypt */
+ dat_bi = bi_import(ctx->bi_ctx, in_data, byte_size);
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ decrypted_bi = is_decryption ? /* decrypt or verify? */
+ RSA_private(ctx, dat_bi) : RSA_public(ctx, dat_bi);
+#else /* always a decryption */
+ decrypted_bi = RSA_private(ctx, dat_bi);
+#endif
+
+ /* convert to a normal block */
+ block = (uint8_t *)malloc(byte_size);
+ bi_export(ctx->bi_ctx, decrypted_bi, block, byte_size);
+
+ i = 10; /* start at the first possible non-padded byte */
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+ if (is_decryption == 0) /* PKCS1.5 signing pads with "0xff"s */
+ {
+ while (block[i++] == 0xff && i < byte_size);
+
+ if (block[i-2] != 0xff)
+ i = byte_size; /*ensure size is 0 */
+ }
+ else /* PKCS1.5 encryption padding is random */
+#endif
+ {
+ while (block[i++] && i < byte_size);
+ }
+ size = byte_size - i;
+
+ /* get only the bit we want */
+ if (size > 0)
+ memcpy(out_data, &block[i], size);
+
+ free(block);
+ return size ? size : -1;
+}
+
+/**
+ * Performs m = c^d mod n
+ */
+bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg)
+{
+#ifdef CONFIG_BIGINT_CRT
+ return bi_crt(c, bi_msg);
+#else
+ BI_CTX *ctx = c->bi_ctx;
+ ctx->mod_offset = BIGINT_M_OFFSET;
+ return bi_mod_power(ctx, bi_msg, c->d);
+#endif
+}
+
+#ifdef CONFIG_BIGINT_CRT
+/**
+ * Use the Chinese Remainder Theorem to quickly perform RSA decrypts.
+ * This should really be in bigint.c (and was at one stage), but needs
+ * access to the RSA_CTX context...
+ */
+static bigint *bi_crt(const RSA_CTX *rsa, bigint *bi)
+{
+ BI_CTX *ctx = rsa->bi_ctx;
+ bigint *m1, *m2, *h;
+
+ /* Montgomery has a condition the 0 < x, y < m and these products violate
+ * that condition. So disable Montgomery when using CRT */
+#if defined(CONFIG_BIGINT_MONTGOMERY)
+ ctx->use_classical = 1;
+#endif
+ ctx->mod_offset = BIGINT_P_OFFSET;
+ m1 = bi_mod_power(ctx, bi_copy(bi), rsa->dP);
+
+ ctx->mod_offset = BIGINT_Q_OFFSET;
+ m2 = bi_mod_power(ctx, bi, rsa->dQ);
+
+ h = bi_subtract(ctx, bi_add(ctx, m1, rsa->p), bi_copy(m2), NULL);
+ h = bi_multiply(ctx, h, rsa->qInv);
+ ctx->mod_offset = BIGINT_P_OFFSET;
+ h = bi_residue(ctx, h);
+#if defined(CONFIG_BIGINT_MONTGOMERY)
+ ctx->use_classical = 0; /* reset for any further operation */
+#endif
+ return bi_add(ctx, m2, bi_multiply(ctx, rsa->q, h));
+}
+#endif
+
+#ifdef CONFIG_SSL_FULL_MODE
+/**
+ * Used for diagnostics.
+ */
+void RSA_print(const RSA_CTX *rsa_ctx)
+{
+ if (rsa_ctx == NULL)
+ return;
+
+ printf("----------------- RSA DEBUG ----------------\n");
+ printf("Size:\t%d\n", rsa_ctx->num_octets);
+ bi_print("Modulus", rsa_ctx->m);
+ bi_print("Public Key", rsa_ctx->e);
+ bi_print("Private Key", rsa_ctx->d);
+}
+#endif
+
+#ifdef CONFIG_SSL_CERT_VERIFICATION
+/**
+ * Performs c = m^e mod n
+ */
+bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg)
+{
+ c->bi_ctx->mod_offset = BIGINT_M_OFFSET;
+ return bi_mod_power(c->bi_ctx, bi_msg, c->e);
+}
+
+/**
+ * Use PKCS1.5 for encryption/signing.
+ * see http://www.rsasecurity.com/rsalabs/node.asp?id=2125
+ */
+int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len,
+ uint8_t *out_data, int is_signing)
+{
+ int byte_size = ctx->num_octets;
+ int num_pads_needed = byte_size-in_len-3;
+ bigint *dat_bi, *encrypt_bi;
+
+ /* note: in_len+11 must be > byte_size */
+ out_data[0] = 0; /* ensure encryption block is < modulus */
+
+ if (is_signing)
+ {
+ out_data[1] = 1; /* PKCS1.5 signing pads with "0xff"'s */
+ memset(&out_data[2], 0xff, num_pads_needed);
+ }
+ else /* randomize the encryption padding with non-zero bytes */
+ {
+ out_data[1] = 2;
+ get_random_NZ(num_pads_needed, &out_data[2]);
+ }
+
+ out_data[2+num_pads_needed] = 0;
+ memcpy(&out_data[3+num_pads_needed], in_data, in_len);
+
+ /* now encrypt it */
+ dat_bi = bi_import(ctx->bi_ctx, out_data, byte_size);
+ encrypt_bi = is_signing ? RSA_private(ctx, dat_bi) :
+ RSA_public(ctx, dat_bi);
+ bi_export(ctx->bi_ctx, encrypt_bi, out_data, byte_size);
+ return byte_size;
+}
+
+#if 0
+/**
+ * Take a signature and decrypt it.
+ */
+bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len,
+ bigint *modulus, bigint *pub_exp)
+{
+ uint8_t *block;
+ int i, size;
+ bigint *decrypted_bi, *dat_bi;
+ bigint *bir = NULL;
+
+ block = (uint8_t *)malloc(sig_len);
+
+ /* decrypt */
+ dat_bi = bi_import(ctx, sig, sig_len);
+ ctx->mod_offset = BIGINT_M_OFFSET;
+
+ /* convert to a normal block */
+ decrypted_bi = bi_mod_power2(ctx, dat_bi, modulus, pub_exp);
+
+ bi_export(ctx, decrypted_bi, block, sig_len);
+ ctx->mod_offset = BIGINT_M_OFFSET;
+
+ i = 10; /* start at the first possible non-padded byte */
+ while (block[i++] && i < sig_len);
+ size = sig_len - i;
+
+ /* get only the bit we want */
+ if (size > 0)
+ {
+ int len;
+ const uint8_t *sig_ptr = x509_get_signature(&block[i], &len);
+
+ if (sig_ptr)
+ {
+ bir = bi_import(ctx, sig_ptr, len);
+ }
+ }
+
+ free(block);
+ return bir;
+}
+#endif
+
+#endif /* CONFIG_SSL_CERT_VERIFICATION */
diff --git a/gpxe/src/crypto/axtls/sha1.c b/gpxe/src/crypto/axtls/sha1.c
new file mode 100644
index 00000000..9a42801f
--- /dev/null
+++ b/gpxe/src/crypto/axtls/sha1.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright(C) 2006 Cameron Rich
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * SHA1 implementation - as defined in FIPS PUB 180-1 published April 17, 1995.
+ * This code was originally taken from RFC3174
+ */
+
+#include <string.h>
+#include "crypto.h"
+
+/*
+ * Define the SHA1 circular left shift macro
+ */
+#define SHA1CircularShift(bits,word) \
+ (((word) << (bits)) | ((word) >> (32-(bits))))
+
+/* ----- static functions ----- */
+static void SHA1PadMessage(SHA1_CTX *ctx);
+static void SHA1ProcessMessageBlock(SHA1_CTX *ctx);
+
+/**
+ * Initialize the SHA1 context
+ */
+void SHA1Init(SHA1_CTX *ctx)
+{
+ ctx->Length_Low = 0;
+ ctx->Length_High = 0;
+ ctx->Message_Block_Index = 0;
+ ctx->Intermediate_Hash[0] = 0x67452301;
+ ctx->Intermediate_Hash[1] = 0xEFCDAB89;
+ ctx->Intermediate_Hash[2] = 0x98BADCFE;
+ ctx->Intermediate_Hash[3] = 0x10325476;
+ ctx->Intermediate_Hash[4] = 0xC3D2E1F0;
+}
+
+/**
+ * Accepts an array of octets as the next portion of the message.
+ */
+void SHA1Update(SHA1_CTX *ctx, const uint8_t *msg, int len)
+{
+ while (len--)
+ {
+ ctx->Message_Block[ctx->Message_Block_Index++] = (*msg & 0xFF);
+
+ ctx->Length_Low += 8;
+ if (ctx->Length_Low == 0)
+ {
+ ctx->Length_High++;
+ }
+
+ if (ctx->Message_Block_Index == 64)
+ {
+ SHA1ProcessMessageBlock(ctx);
+ }
+
+ msg++;
+ }
+}
+
+/**
+ * Return the 160-bit message digest into the user's array
+ */
+void SHA1Final(SHA1_CTX *ctx, uint8_t *digest)
+{
+ int i;
+
+ SHA1PadMessage(ctx);
+ memset(ctx->Message_Block, 0, 64);
+ ctx->Length_Low = 0; /* and clear length */
+ ctx->Length_High = 0;
+
+ for (i = 0; i < SHA1_SIZE; i++)
+ {
+ digest[i] = ctx->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) );
+ }
+}
+
+/**
+ * Process the next 512 bits of the message stored in the array.
+ */
+static void SHA1ProcessMessageBlock(SHA1_CTX *ctx)
+{
+ const uint32_t K[] = { /* Constants defined in SHA-1 */
+ 0x5A827999,
+ 0x6ED9EBA1,
+ 0x8F1BBCDC,
+ 0xCA62C1D6
+ };
+ int t; /* Loop counter */
+ uint32_t temp; /* Temporary word value */
+ uint32_t W[80]; /* Word sequence */
+ uint32_t A, B, C, D, E; /* Word buffers */
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for (t = 0; t < 16; t++)
+ {
+ W[t] = ctx->Message_Block[t * 4] << 24;
+ W[t] |= ctx->Message_Block[t * 4 + 1] << 16;
+ W[t] |= ctx->Message_Block[t * 4 + 2] << 8;
+ W[t] |= ctx->Message_Block[t * 4 + 3];
+ }
+
+ for (t = 16; t < 80; t++)
+ {
+ W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+ }
+
+ A = ctx->Intermediate_Hash[0];
+ B = ctx->Intermediate_Hash[1];
+ C = ctx->Intermediate_Hash[2];
+ D = ctx->Intermediate_Hash[3];
+ E = ctx->Intermediate_Hash[4];
+
+ for (t = 0; t < 20; t++)
+ {
+ temp = SHA1CircularShift(5,A) +
+ ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+
+ B = A;
+ A = temp;
+ }
+
+ for (t = 20; t < 40; t++)
+ {
+ temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 40; t < 60; t++)
+ {
+ temp = SHA1CircularShift(5,A) +
+ ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 60; t < 80; t++)
+ {
+ temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ ctx->Intermediate_Hash[0] += A;
+ ctx->Intermediate_Hash[1] += B;
+ ctx->Intermediate_Hash[2] += C;
+ ctx->Intermediate_Hash[3] += D;
+ ctx->Intermediate_Hash[4] += E;
+ ctx->Message_Block_Index = 0;
+}
+
+/*
+ * According to the standard, the message must be padded to an even
+ * 512 bits. The first padding bit must be a '1'. The last 64
+ * bits represent the length of the original message. All bits in
+ * between should be 0. This function will pad the message
+ * according to those rules by filling the Message_Block array
+ * accordingly. It will also call the ProcessMessageBlock function
+ * provided appropriately. When it returns, it can be assumed that
+ * the message digest has been computed.
+ *
+ * @param ctx [in, out] The SHA1 context
+ */
+static void SHA1PadMessage(SHA1_CTX *ctx)
+{
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second
+ * block.
+ */
+ if (ctx->Message_Block_Index > 55)
+ {
+ ctx->Message_Block[ctx->Message_Block_Index++] = 0x80;
+ while(ctx->Message_Block_Index < 64)
+ {
+ ctx->Message_Block[ctx->Message_Block_Index++] = 0;
+ }
+
+ SHA1ProcessMessageBlock(ctx);
+
+ while (ctx->Message_Block_Index < 56)
+ {
+ ctx->Message_Block[ctx->Message_Block_Index++] = 0;
+ }
+ }
+ else
+ {
+ ctx->Message_Block[ctx->Message_Block_Index++] = 0x80;
+ while(ctx->Message_Block_Index < 56)
+ {
+
+ ctx->Message_Block[ctx->Message_Block_Index++] = 0;
+ }
+ }
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ ctx->Message_Block[56] = ctx->Length_High >> 24;
+ ctx->Message_Block[57] = ctx->Length_High >> 16;
+ ctx->Message_Block[58] = ctx->Length_High >> 8;
+ ctx->Message_Block[59] = ctx->Length_High;
+ ctx->Message_Block[60] = ctx->Length_Low >> 24;
+ ctx->Message_Block[61] = ctx->Length_Low >> 16;
+ ctx->Message_Block[62] = ctx->Length_Low >> 8;
+ ctx->Message_Block[63] = ctx->Length_Low;
+ SHA1ProcessMessageBlock(ctx);
+}
diff --git a/gpxe/src/crypto/axtls_aes.c b/gpxe/src/crypto/axtls_aes.c
new file mode 100644
index 00000000..ac7e921d
--- /dev/null
+++ b/gpxe/src/crypto/axtls_aes.c
@@ -0,0 +1,54 @@
+#include "crypto/axtls/crypto.h"
+#include <string.h>
+#include <errno.h>
+#include <gpxe/crypto.h>
+#include <gpxe/aes.h>
+
+static int aes_setkey ( void *ctx, const void *key, size_t keylen ) {
+ AES_CTX *aesctx = ctx;
+ AES_MODE mode;
+
+ switch ( keylen ) {
+ case ( 128 / 8 ):
+ mode = AES_MODE_128;
+ break;
+ case ( 256 / 8 ):
+ mode = AES_MODE_256;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ AES_set_key ( aesctx, key, aesctx->iv, mode );
+ return 0;
+}
+
+static void aes_setiv ( void *ctx, const void *iv ) {
+ AES_CTX *aesctx = ctx;
+
+ memcpy ( aesctx->iv, iv, sizeof ( aesctx->iv ) );
+}
+
+static void aes_encrypt ( void *ctx, const void *data, void *dst,
+ size_t len ) {
+ AES_CTX *aesctx = ctx;
+
+ AES_cbc_encrypt ( aesctx, data, dst, len );
+}
+
+static void aes_decrypt ( void *ctx, const void *data, void *dst,
+ size_t len ) {
+ AES_CTX *aesctx = ctx;
+
+ AES_cbc_decrypt ( aesctx, data, dst, len );
+}
+
+struct crypto_algorithm aes_algorithm = {
+ .name = "aes",
+ .ctxsize = sizeof ( AES_CTX ),
+ .blocksize = 16,
+ .setkey = aes_setkey,
+ .setiv = aes_setiv,
+ .encode = aes_encrypt,
+ .decode = aes_decrypt,
+};
diff --git a/gpxe/src/crypto/axtls_sha1.c b/gpxe/src/crypto/axtls_sha1.c
new file mode 100644
index 00000000..62ff878a
--- /dev/null
+++ b/gpxe/src/crypto/axtls_sha1.c
@@ -0,0 +1,26 @@
+#include "crypto/axtls/crypto.h"
+#include <gpxe/crypto.h>
+#include <gpxe/sha1.h>
+
+static void sha1_init ( void *ctx ) {
+ SHA1Init ( ctx );
+}
+
+static void sha1_update ( void *ctx, const void *data, void *dst __unused,
+ size_t len ) {
+ SHA1Update ( ctx, data, len );
+}
+
+static void sha1_final ( void *ctx, void *out ) {
+ SHA1Final ( ctx, out );
+}
+
+struct crypto_algorithm sha1_algorithm = {
+ .name = "sha1",
+ .ctxsize = SHA1_CTX_SIZE,
+ .blocksize = 64,
+ .digestsize = SHA1_DIGEST_SIZE,
+ .init = sha1_init,
+ .encode = sha1_update,
+ .final = sha1_final,
+};
diff --git a/gpxe/src/crypto/chap.c b/gpxe/src/crypto/chap.c
new file mode 100644
index 00000000..13b8fda2
--- /dev/null
+++ b/gpxe/src/crypto/chap.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <gpxe/crypto.h>
+#include <gpxe/chap.h>
+
+/** @file
+ *
+ * CHAP protocol
+ *
+ */
+
+/**
+ * Initialise CHAP challenge/response
+ *
+ * @v chap CHAP challenge/response
+ * @v digest Digest algorithm to use
+ * @ret rc Return status code
+ *
+ * Initialises a CHAP challenge/response structure. This routine
+ * allocates memory, and so may fail. The allocated memory must
+ * eventually be freed by a call to chap_finish().
+ */
+int chap_init ( struct chap_challenge *chap,
+ struct crypto_algorithm *digest ) {
+ size_t state_len;
+ void *state;
+
+ assert ( chap->digest == NULL );
+ assert ( chap->digest_context == NULL );
+ assert ( chap->response == NULL );
+
+ DBG ( "CHAP %p initialising with %s digest\n", chap, digest->name );
+
+ state_len = ( digest->ctxsize + digest->digestsize );
+ state = malloc ( state_len );
+ if ( ! state ) {
+ DBG ( "CHAP %p could not allocate %zd bytes for state\n",
+ chap, state_len );
+ return -ENOMEM;
+ }
+
+ chap->digest = digest;
+ chap->digest_context = state;
+ chap->response = ( state + digest->ctxsize );
+ chap->response_len = digest->digestsize;
+ digest_init ( chap->digest, chap->digest_context );
+ return 0;
+}
+
+/**
+ * Add data to the CHAP challenge
+ *
+ * @v chap CHAP challenge/response
+ * @v data Data to add
+ * @v len Length of data to add
+ */
+void chap_update ( struct chap_challenge *chap, const void *data,
+ size_t len ) {
+ assert ( chap->digest != NULL );
+ assert ( chap->digest_context != NULL );
+
+ if ( ! chap->digest )
+ return;
+
+ digest_update ( chap->digest, chap->digest_context, data, len );
+}
+
+/**
+ * Respond to the CHAP challenge
+ *
+ * @v chap CHAP challenge/response
+ *
+ * Calculates the final CHAP response value, and places it in @c
+ * chap->response, with a length of @c chap->response_len.
+ */
+void chap_respond ( struct chap_challenge *chap ) {
+ assert ( chap->digest != NULL );
+ assert ( chap->digest_context != NULL );
+ assert ( chap->response != NULL );
+
+ DBG ( "CHAP %p responding to challenge\n", chap );
+
+ if ( ! chap->digest )
+ return;
+
+ digest_final ( chap->digest, chap->digest_context, chap->response );
+}
+
+/**
+ * Free resources used by a CHAP challenge/response
+ *
+ * @v chap CHAP challenge/response
+ */
+void chap_finish ( struct chap_challenge *chap ) {
+ void *state = chap->digest_context;
+
+ DBG ( "CHAP %p finished\n", chap );
+
+ free ( state );
+ memset ( chap, 0, sizeof ( *chap ) );
+}
diff --git a/gpxe/src/crypto/cipher.c b/gpxe/src/crypto/cipher.c
new file mode 100644
index 00000000..9c392009
--- /dev/null
+++ b/gpxe/src/crypto/cipher.c
@@ -0,0 +1,24 @@
+#include <stdint.h>
+#include <errno.h>
+#include <gpxe/crypto.h>
+
+int cipher_encrypt ( struct crypto_algorithm *crypto,
+ void *ctx, const void *src, void *dst,
+ size_t len ) {
+ if ( ( len & ( crypto->blocksize - 1 ) ) ) {
+ return -EINVAL;
+ }
+ crypto->encode ( ctx, src, dst, len );
+ return 0;
+}
+
+int cipher_decrypt ( struct crypto_algorithm *crypto,
+ void *ctx, const void *src, void *dst,
+ size_t len ) {
+ if ( ( len & ( crypto->blocksize - 1 ) ) ) {
+ return -EINVAL;
+ }
+ crypto->decode ( ctx, src, dst, len );
+ return 0;
+}
+
diff --git a/gpxe/src/crypto/cryptoLayer.h b/gpxe/src/crypto/cryptoLayer.h
new file mode 100644
index 00000000..28ce97bc
--- /dev/null
+++ b/gpxe/src/crypto/cryptoLayer.h
@@ -0,0 +1,120 @@
+#ifndef _MATRIXSSL_CRYPTOLAYER_H
+#define _MATRIXSSL_CRYPTOLAYER_H
+
+/** @file
+ *
+ * Compatibility layer for MatrixSSL
+ *
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <gpxe/bitops.h>
+#include <gpxe/crypto.h>
+
+/* Drag in pscrypto.h */
+typedef uint64_t ulong64;
+typedef void psPool_t;
+#define SMALL_CODE
+#define USE_INT64
+#define USE_RSA
+#define USE_RSA_PUBLIC_ENCRYPT
+#define CRYPT
+#include "matrixssl/pscrypto.h"
+#define SMALL_CODE
+#undef CLEAN_STACK
+
+#define sslAssert( ... ) assert ( __VA_ARGS__ )
+
+static inline __attribute__ (( always_inline )) void * __malloc
+psMalloc ( psPool_t *pool __unused, size_t len ) {
+ return malloc ( len );
+}
+
+static inline __attribute__ (( always_inline )) void *
+psRealloc ( void *ptr, size_t len ) {
+ return realloc ( ptr, len );
+}
+
+static inline __attribute__ (( always_inline )) void psFree ( void *ptr ) {
+ free ( ptr );
+}
+
+#define matrixStrDebugMsg( ... ) DBG ( __VA_ARGS__ )
+#define matrixIntDebugMsg( ... ) DBG ( __VA_ARGS__ )
+
+/* Use our standard cpu_to_leXX etc. macros */
+
+#undef LOAD32L
+#define LOAD32L( cpu32, ptr ) do { \
+ uint32_t *le32 = ( ( uint32_t * ) ptr ); \
+ cpu32 = le32_to_cpu ( *le32 ); \
+ } while ( 0 )
+
+#undef LOAD32H
+#define LOAD32H( cpu32, ptr ) do { \
+ uint32_t *be32 = ( ( uint32_t * ) ptr ); \
+ cpu32 = be32_to_cpu ( *be32 ); \
+ } while ( 0 )
+
+#undef LOAD64L
+#define LOAD64L( cpu64, ptr ) do { \
+ uint64_t *le64 = ( ( uint64_t * ) ptr ); \
+ cpu64 = le64_to_cpu ( *le64 ); \
+ } while ( 0 )
+
+#undef LOAD64H
+#define LOAD64H( cpu64, ptr ) do { \
+ uint64_t *be64 = ( ( uint64_t * ) ptr ); \
+ cpu64 = be64_to_cpu ( *be64 ); \
+ } while ( 0 )
+
+#undef STORE32L
+#define STORE32L( cpu32, ptr ) do { \
+ uint32_t *le32 = ( ( uint32_t * ) ptr ); \
+ *le32 = cpu_to_le32 ( cpu32 ); \
+ } while ( 0 )
+
+#undef STORE32H
+#define STORE32H( cpu32, ptr ) do { \
+ uint32_t *be32 = ( ( uint32_t * ) ptr ); \
+ *be32 = cpu_to_be32 ( cpu32 ); \
+ } while ( 0 )
+
+#undef STORE64L
+#define STORE64L( cpu64, ptr ) do { \
+ uint64_t *le64 = ( ( uint64_t * ) ptr ); \
+ *le64 = cpu_to_le64 ( cpu64 ); \
+ } while ( 0 )
+
+#undef STORE64H
+#define STORE64H( cpu64, ptr ) do { \
+ uint64_t *be64 = ( ( uint64_t * ) ptr ); \
+ *be64 = cpu_to_be64 ( cpu64 ); \
+ } while ( 0 )
+
+/* Use rolXX etc. from bitops.h */
+
+#undef ROL
+#define ROL( data, rotation ) rol32 ( (data), (rotation) )
+#undef ROLc
+#define ROLc( data, rotation ) rol32 ( (data), (rotation) )
+#undef ROR
+#define ROR( data, rotation ) ror32 ( (data), (rotation) )
+#undef RORc
+#define RORc( data, rotation ) ror32 ( (data), (rotation) )
+#undef ROL64
+#define ROL64( data, rotation ) rol64 ( (data), (rotation) )
+#undef ROL64c
+#define ROL64c( data, rotation ) rol64 ( (data), (rotation) )
+#undef ROR64
+#define ROR64( data, rotation ) ror64 ( (data), (rotation) )
+#undef ROR64c
+#define ROR64c( data, rotation ) ror64 ( (data), (rotation) )
+
+#endif /* _MATRIXSSL_CRYPTOLAYER_H */
diff --git a/gpxe/src/crypto/crypto_null.c b/gpxe/src/crypto/crypto_null.c
new file mode 100644
index 00000000..120ef0a6
--- /dev/null
+++ b/gpxe/src/crypto/crypto_null.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Null crypto algorithm
+ */
+
+#include <string.h>
+#include <gpxe/crypto.h>
+
+static void null_init ( void *ctx __unused ) {
+ /* Do nothing */
+}
+
+static int null_setkey ( void *ctx __unused, const void *key __unused,
+ size_t keylen __unused ) {
+ /* Do nothing */
+ return 0;
+}
+
+static void null_setiv ( void *ctx __unused, const void *iv __unused ) {
+ /* Do nothing */
+}
+
+static void null_encode ( void *ctx __unused, const void *src,
+ void *dst, size_t len ) {
+ if ( dst )
+ memcpy ( dst, src, len );
+}
+
+static void null_decode ( void *ctx __unused, const void *src,
+ void *dst, size_t len ) {
+ if ( dst )
+ memcpy ( dst, src, len );
+}
+
+static void null_final ( void *ctx __unused, void *out __unused ) {
+ /* Do nothing */
+}
+
+struct crypto_algorithm crypto_null = {
+ .name = "null",
+ .ctxsize = 0,
+ .blocksize = 1,
+ .digestsize = 0,
+ .init = null_init,
+ .setkey = null_setkey,
+ .setiv = null_setiv,
+ .encode = null_encode,
+ .decode = null_decode,
+ .final = null_final,
+};
diff --git a/gpxe/src/crypto/framework.c b/gpxe/src/crypto/framework.c
new file mode 100644
index 00000000..0da2cbe3
--- /dev/null
+++ b/gpxe/src/crypto/framework.c
@@ -0,0 +1,86 @@
+/* mcb - this file breaks the build process; temporarily deactivating */
+#if 0
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include "ssl.h"
+
+int main(int argc, char *argv[])
+{
+ SSL_t ssl;
+ int sockfd, portno, rc;
+ struct sockaddr_in serv_addr;
+ struct hostent *server;
+
+ portno = 443;
+ sockfd = socket(AF_INET,SOCK_STREAM,0);
+ if(sockfd<0){
+ fprintf(stderr,"Error creating socket\n");
+ exit(sockfd);
+ }
+
+ server = gethostbyname(argv[1]);
+ if(server==NULL){
+ fprintf(stderr,"Error looking up host %s\n",argv[1]);
+ exit(1);
+ }
+
+ /**
+ *matrixSslOpen()
+ *matrixSslReadKeys()
+ **/
+ printf("Calling CreateSSLHello()\n");
+ rc = CreateSSLHello(&ssl);
+ printf("Finished calling CreateSSLHello()\n");
+
+ bzero((char *) &serv_addr, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ bcopy((char *)server->h_addr,(char *)&serv_addr.sin_addr.s_addr,server->h_length);
+ serv_addr.sin_port = htons(portno);
+ if(connect(sockfd,(struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
+ fprintf(stderr,"ERROR connecting to server\n");
+ exit(1);
+ }
+
+ PrintSSLPacket(&ssl);
+
+ printf("Write ssl.buffer\n");
+ write(sockfd,ssl.buffer,ssl.length);
+ printf("Finished writing\n");
+ ssl.length = read(sockfd,ssl.buffer,ssl.max_size);
+ ReadSSLHello(&ssl);
+
+ /**
+ *matrixSslNewSession()
+ *matrixSslSetCetValidator()
+ *encodeSslHandshake()
+
+ *write handshake buffer
+
+ *readSslResponse() <-+
+ |
+ *read return code |-- similar/same function??
+ |
+ *sslEncode() |
+ *sslDecode() <-------+
+
+ *encodeSslCloseAlert()
+
+ *write close alert buffer
+ **/
+ close(sockfd);
+
+ /**
+ *sslClose()
+ * -free connection
+ * -free keys
+ * -close pki interface
+ **/
+
+ return 0;
+}
+
+#endif
diff --git a/gpxe/src/crypto/hmac.c b/gpxe/src/crypto/hmac.c
new file mode 100644
index 00000000..6884bde9
--- /dev/null
+++ b/gpxe/src/crypto/hmac.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Keyed-Hashing for Message Authentication
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <gpxe/crypto.h>
+#include <gpxe/hmac.h>
+
+/**
+ * Reduce HMAC key length
+ *
+ * @v digest Digest algorithm to use
+ * @v digest_ctx Digest context
+ * @v key Key
+ * @v key_len Length of key
+ */
+static void hmac_reduce_key ( struct crypto_algorithm *digest,
+ void *key, size_t *key_len ) {
+ uint8_t digest_ctx[digest->ctxsize];
+
+ digest_init ( digest, digest_ctx );
+ digest_update ( digest, digest_ctx, key, *key_len );
+ digest_final ( digest, digest_ctx, key );
+ *key_len = digest->digestsize;
+}
+
+/**
+ * Initialise HMAC
+ *
+ * @v digest Digest algorithm to use
+ * @v digest_ctx Digest context
+ * @v key Key
+ * @v key_len Length of key
+ *
+ * The length of the key should be less than the block size of the
+ * digest algorithm being used. (If the key length is greater, it
+ * will be replaced with its own digest, and key_len will be updated
+ * accordingly).
+ */
+void hmac_init ( struct crypto_algorithm *digest, void *digest_ctx,
+ void *key, size_t *key_len ) {
+ unsigned char k_ipad[digest->blocksize];
+ unsigned int i;
+
+ /* Reduce key if necessary */
+ if ( *key_len > sizeof ( k_ipad ) )
+ hmac_reduce_key ( digest, key, key_len );
+
+ /* Construct input pad */
+ memset ( k_ipad, 0, sizeof ( k_ipad ) );
+ memcpy ( k_ipad, key, *key_len );
+ for ( i = 0 ; i < sizeof ( k_ipad ) ; i++ ) {
+ k_ipad[i] ^= 0x36;
+ }
+
+ /* Start inner hash */
+ digest_init ( digest, digest_ctx );
+ digest_update ( digest, digest_ctx, k_ipad, sizeof ( k_ipad ) );
+}
+
+/**
+ * Finalise HMAC
+ *
+ * @v digest Digest algorithm to use
+ * @v digest_ctx Digest context
+ * @v key Key
+ * @v key_len Length of key
+ * @v hmac HMAC digest to fill in
+ *
+ * The length of the key should be less than the block size of the
+ * digest algorithm being used. (If the key length is greater, it
+ * will be replaced with its own digest, and key_len will be updated
+ * accordingly).
+ */
+void hmac_final ( struct crypto_algorithm *digest, void *digest_ctx,
+ void *key, size_t *key_len, void *hmac ) {
+ unsigned char k_opad[digest->blocksize];
+ unsigned int i;
+
+ /* Reduce key if necessary */
+ if ( *key_len > sizeof ( k_opad ) )
+ hmac_reduce_key ( digest, key, key_len );
+
+ /* Construct output pad */
+ memset ( k_opad, 0, sizeof ( k_opad ) );
+ memcpy ( k_opad, key, *key_len );
+ for ( i = 0 ; i < sizeof ( k_opad ) ; i++ ) {
+ k_opad[i] ^= 0x5c;
+ }
+
+ /* Finish inner hash */
+ digest_final ( digest, digest_ctx, hmac );
+
+ /* Perform outer hash */
+ digest_init ( digest, digest_ctx );
+ digest_update ( digest, digest_ctx, k_opad, sizeof ( k_opad ) );
+ digest_update ( digest, digest_ctx, hmac, digest->digestsize );
+ digest_final ( digest, digest_ctx, hmac );
+}
diff --git a/gpxe/src/crypto/matrixssl/mpi.h b/gpxe/src/crypto/matrixssl/mpi.h
new file mode 100644
index 00000000..bb2c9c57
--- /dev/null
+++ b/gpxe/src/crypto/matrixssl/mpi.h
@@ -0,0 +1,487 @@
+/*
+ * mpi.h
+ * Release $Name$
+ *
+ * multiple-precision integer library
+ */
+/*
+ * Copyright (c) PeerSec Networks, 2002-2006. All Rights Reserved.
+ * The latest version of this code is available at http://www.matrixssl.org
+ *
+ * This software is open source; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This General Public License does NOT permit incorporating this software
+ * into proprietary programs. If you are unable to comply with the GPL, a
+ * commercial license for this software may be purchased from PeerSec Networks
+ * at http://www.peersec.com
+ *
+ * This program is distributed in WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+/******************************************************************************/
+
+#ifndef _h_MPI
+#define _h_MPI
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+
+#undef MIN
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#undef MAX
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+#ifdef __cplusplus
+extern "C" {
+
+
+/*
+ C++ compilers don't like assigning void * to mp_digit *
+ */
+#define OPT_CAST(x) (x *)
+
+#else
+
+/*
+ C on the other hand doesn't care
+ */
+#define OPT_CAST(x)
+
+#endif /* __cplusplus */
+
+/******************************************************************************/
+/*
+ some default configurations.
+
+ A "mp_digit" must be able to hold DIGIT_BIT + 1 bits
+ A "mp_word" must be able to hold 2*DIGIT_BIT + 1 bits
+
+ At the very least a mp_digit must be able to hold 7 bits
+ [any size beyond that is ok provided it doesn't overflow the data type]
+ */
+#ifdef MP_8BIT
+ typedef unsigned char mp_digit;
+ typedef unsigned short mp_word;
+#elif defined(MP_16BIT)
+ typedef unsigned short mp_digit;
+ typedef unsigned long mp_word;
+#elif defined(MP_64BIT)
+/*
+ for GCC only on supported platforms
+ */
+ #ifndef CRYPT
+ typedef unsigned long long ulong64;
+ typedef signed long long long64;
+ #endif /* CRYPT */
+
+ typedef ulong64 mp_digit;
+ typedef unsigned long mp_word __attribute__ ((mode(TI)));
+
+ #define DIGIT_BIT 60
+#else /* MP_8BIT */
+/*
+ this is the default case, 28-bit digits
+ */
+ #ifndef CRYPT
+ #if defined(_MSC_VER) || defined(__BORLANDC__)
+ typedef unsigned __int64 ulong64;
+ typedef signed __int64 long64;
+ #else
+ typedef unsigned long long ulong64;
+ typedef signed long long long64;
+ #endif
+ #endif /* CRYPT */
+
+ typedef unsigned long mp_digit;
+ typedef ulong64 mp_word;
+
+ #ifdef MP_31BIT
+/*
+ this is an extension that uses 31-bit digits
+ */
+ #define DIGIT_BIT 31
+ #else /* MP_31BIT */
+/*
+ default case is 28-bit digits, defines MP_28BIT as a handy macro to test
+ */
+ #define DIGIT_BIT 28
+ #define MP_28BIT
+ #endif /* MP_31BIT */
+#endif /* MP_8BIT */
+
+/*
+ otherwise the bits per digit is calculated automatically from the size of
+ a mp_digit
+ */
+#ifndef DIGIT_BIT
+ #define DIGIT_BIT ((int32)((CHAR_BIT * sizeof(mp_digit) - 1))) /* bits per digit */
+#endif /* DIGIT_BIT */
+
+#define MP_DIGIT_BIT DIGIT_BIT
+#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1))
+#define MP_DIGIT_MAX MP_MASK
+
+/******************************************************************************/
+/*
+ equalities
+ */
+#define MP_LT -1 /* less than */
+#define MP_EQ 0 /* equal to */
+#define MP_GT 1 /* greater than */
+
+#define MP_ZPOS 0 /* positive integer */
+#define MP_NEG 1 /* negative */
+
+#define MP_OKAY 0 /* ok result */
+#define MP_MEM -2 /* out of mem */
+#define MP_VAL -3 /* invalid input */
+#define MP_RANGE MP_VAL
+
+#define MP_YES 1 /* yes response */
+#define MP_NO 0 /* no response */
+
+typedef int32 mp_err;
+
+/******************************************************************************/
+/*
+ various build options
+ */
+#define MP_PREC 64 /* default digits of precision */
+
+/*
+ define this to use lower memory usage routines (exptmods mostly)
+ */
+#define MP_LOW_MEM
+
+/*
+ size of comba arrays, should be at least
+ 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2)
+ */
+#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1))
+
+typedef struct {
+ int32 used, alloc, sign;
+ mp_digit *dp;
+} mp_int;
+
+#define USED(m) ((m)->used)
+#define DIGIT(m,k) ((m)->dp[(k)])
+#define SIGN(m) ((m)->sign)
+
+/******************************************************************************/
+/*
+ init and deinit bignum functions
+ */
+
+/*
+ init a bignum
+ */
+extern int32 mp_init(psPool_t *pool, mp_int *a);
+
+/*
+ free a bignum
+ */
+extern void mp_clear(mp_int *a);
+
+/*
+ init a series of arguments
+ */
+extern int32 _mp_init_multi(psPool_t *pool, mp_int *mp0, mp_int *mp1, mp_int *mp2,
+ mp_int *mp3, mp_int *mp4, mp_int *mp5, mp_int *mp6,
+ mp_int *mp7);
+
+/*
+ clear a series of arguments
+ */
+extern void _mp_clear_multi(mp_int *mp0, mp_int *mp1, mp_int *mp2, mp_int *mp3,
+ mp_int *mp4, mp_int *mp5, mp_int *mp6, mp_int *mp7);
+
+/*
+ exchange two ints
+ */
+extern void mp_exch(mp_int *a, mp_int *b);
+
+/*
+ shrink ram required for a bignum
+ */
+extern int32 mp_shrink(mp_int *a);
+
+/*
+ grow an int32 to a given size
+ */
+extern int32 mp_grow(mp_int *a, int32 size);
+
+/*
+ init to a given number of digits
+ */
+extern int32 mp_init_size(psPool_t *pool, mp_int *a, int32 size);
+
+/******************************************************************************/
+/*
+ Basic Manipulations
+ */
+#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO)
+#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO)
+#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO)
+
+extern int32 mp_add_d (mp_int * a, mp_digit b, mp_int * c);
+extern int32 mp_sub_d (mp_int * a, mp_digit b, mp_int * c);
+/*
+ set to zero
+ */
+extern void mp_zero(mp_int *a);
+
+/*
+ set to a digit
+ */
+extern void mp_set(mp_int *a, mp_digit b);
+
+/*
+ copy, b = a
+ */
+extern int32 mp_copy(mp_int *a, mp_int *b);
+
+/*
+ inits and copies, a = b
+ */
+extern int32 mp_init_copy(psPool_t *pool, mp_int *a, mp_int *b);
+
+/*
+ trim unused digits
+ */
+extern void mp_clamp(mp_int *a);
+
+/******************************************************************************/
+/*
+ digit manipulation
+*/
+
+/*
+ right shift by "b" digits
+ */
+extern void mp_rshd(mp_int *a, int32 b);
+
+/*
+ left shift by "b" digits
+ */
+extern int32 mp_lshd(mp_int *a, int32 b);
+
+/*
+ c = a / 2**b
+ */
+extern int32 mp_div_2d(psPool_t *pool, mp_int *a, int32 b, mp_int *c, mp_int *d);
+
+/*
+ b = a/2
+ */
+extern int32 mp_div_2(mp_int *a, mp_int *b);
+
+/*
+ c = a * 2**b
+ */
+extern int32 mp_mul_2d(mp_int *a, int32 b, mp_int *c);
+
+/*
+ c = a mod 2**d
+ */
+extern int32 mp_mod_2d(mp_int *a, int32 b, mp_int *c);
+
+/*
+ computes a = 2**b
+ */
+extern int32 mp_2expt(mp_int *a, int32 b);
+
+/******************************************************************************/
+/*
+ Basic arithmetic
+ */
+
+/*
+ b = |a|
+ */
+extern int32 mp_abs(mp_int *a, mp_int *b);
+
+/*
+ compare a to b
+ */
+extern int32 mp_cmp(mp_int *a, mp_int *b);
+
+/*
+ compare |a| to |b|
+ */
+extern int32 mp_cmp_mag(mp_int *a, mp_int *b);
+
+/*
+ c = a + b
+ */
+extern int32 mp_add(mp_int *a, mp_int *b, mp_int *c);
+
+/*
+ c = a - b
+ */
+extern int32 mp_sub(mp_int *a, mp_int *b, mp_int *c);
+
+/*
+ c = a * b
+ b = a*a
+ */
+/* STEVE - moved mp_mul out of SLOW case */
+extern int32 mp_mul(psPool_t *pool, mp_int *a, mp_int *b, mp_int *c);
+#ifdef USE_SMALL_WORD
+extern int32 mp_sqr(psPool_t *pool, mp_int *a, mp_int *b);
+#endif
+
+/*
+ a/b => cb + d == a
+ */
+extern int32 mp_div(psPool_t *pool, mp_int *a, mp_int *b, mp_int *c, mp_int *d);
+
+/*
+ c = a mod b, 0 <= c < b
+ */
+extern int32 mp_mod(psPool_t *pool, mp_int *a, mp_int *b, mp_int *c);
+
+/******************************************************************************/
+/*
+ single digit functions
+ */
+
+/*
+ compare against a single digit
+ */
+extern int32 mp_cmp_d(mp_int *a, mp_digit b);
+
+/*
+ c = a * b
+ */
+extern int32 mp_mul_d(mp_int *a, mp_digit b, mp_int *c);
+
+/******************************************************************************/
+/*
+ number theory
+ */
+
+/*
+ d = a + b (mod c)
+ */
+extern int32 mp_addmod(psPool_t *pool, mp_int *a, mp_int *b, mp_int *c, mp_int *d);
+
+/*
+ d = a * b (mod c)
+ */
+extern int32 mp_mulmod(psPool_t *pool, mp_int *a, mp_int *b, mp_int *c, mp_int *d);
+
+/*
+ c = 1/a (mod b)
+ */
+#ifdef USE_SMALL_WORD
+extern int32 mp_invmod(psPool_t *pool, mp_int *a, mp_int *b, mp_int *c);
+#endif
+
+/*
+ setups the montgomery reduction
+ */
+extern int32 mp_montgomery_setup(mp_int *a, mp_digit *mp);
+
+/*
+ computes a = B**n mod b without division or multiplication useful for
+ normalizing numbers in a Montgomery system.
+ */
+extern int32 mp_montgomery_calc_normalization(mp_int *a, mp_int *b);
+
+/*
+ computes x/R == x (mod N) via Montgomery Reduction
+ */
+#ifdef USE_SMALL_WORD
+extern int32 mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp);
+#endif
+
+/*
+ d = a**b (mod c)
+ */
+/* TODO - we never define this */
+extern int32 mp_exptmod(psPool_t *pool, mp_int *a, mp_int *b, mp_int *c, mp_int *d);
+
+/******************************************************************************/
+/*
+ If we're using 1024 or 2048 bit keys and 28 bit digits, we only need the
+ fast_ versions of these functions, removing the others to save space.
+ Otherwise, we include the slow versions as well and which version to use
+ is done at runtime.
+*/
+#ifdef USE_SMALL_WORD
+extern int32 s_mp_mul_digs(psPool_t *pool, mp_int *a, mp_int *b, mp_int *c,
+ int32 digs);
+extern int32 s_mp_sqr(psPool_t *pool, mp_int *a, mp_int *b);
+#else
+#define mp_montgomery_reduce fast_mp_montgomery_reduce
+#define mp_sqr fast_s_mp_sqr
+#if STEVE
+#define mp_mul(P, A, B, C) fast_s_mp_mul_digs(P, A, B, C, (A)->used + (B)->used + 1)
+#endif
+#define s_mp_mul_digs fast_s_mp_mul_digs
+#define mp_invmod fast_mp_invmod
+#endif
+
+/******************************************************************************/
+/*
+ radix conversion
+ */
+extern int32 mp_count_bits(mp_int *a);
+
+extern int32 mp_unsigned_bin_size(mp_int *a);
+extern int32 mp_read_unsigned_bin(mp_int *a, unsigned char *b, int32 c);
+extern int32 mp_to_unsigned_bin(psPool_t *pool, mp_int *a, unsigned char *b);
+
+extern int32 mp_signed_bin_size(mp_int *a);
+
+/*
+ lowlevel functions, do not call!
+ */
+#if STEVE
+#ifdef USE_SMALL_WORD
+#define s_mp_mul(P, A, B, C) s_mp_mul_digs(P, A, B, C, (A)->used + (B)->used + 1)
+#else
+#define s_mp_mul(P, A, B, C) sslAssert();
+#endif
+#endif /* STEVE */
+/* define this in all cases for now STEVE */
+#define s_mp_mul(P, A, B, C) s_mp_mul_digs(P, A, B, C, (A)->used + (B)->used + 1)
+
+
+/*
+ b = a*2
+ */
+extern int32 mp_mul_2(mp_int *a, mp_int *b);
+
+extern int32 s_mp_add(mp_int *a, mp_int *b, mp_int *c);
+extern int32 s_mp_sub(mp_int *a, mp_int *b, mp_int *c);
+
+extern int32 fast_s_mp_mul_digs(psPool_t *pool, mp_int *a, mp_int *b, mp_int *c,
+ int32 digs);
+extern int32 fast_s_mp_sqr(psPool_t *pool, mp_int *a, mp_int *b);
+
+extern int32 fast_mp_invmod(psPool_t *pool, mp_int *a, mp_int *b, mp_int *c);
+extern int32 fast_mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp);
+
+extern void bn_reverse(unsigned char *s, int32 len);
+
+
+#ifdef __cplusplus
+ }
+#endif /* __cplusplus */
+
+#endif /* _h_MPI */
+
diff --git a/gpxe/src/crypto/matrixssl/pscrypto.h b/gpxe/src/crypto/matrixssl/pscrypto.h
new file mode 100644
index 00000000..4d684327
--- /dev/null
+++ b/gpxe/src/crypto/matrixssl/pscrypto.h
@@ -0,0 +1,661 @@
+/*
+ * pscrypto.h
+ * Release $Name$
+ *
+ * Internal definitions for PeerSec Networks MatrixSSL cryptography provider
+ */
+/*
+ * Copyright (c) PeerSec Networks, 2002-2006. All Rights Reserved.
+ * The latest version of this code is available at http://www.matrixssl.org
+ *
+ * This software is open source; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This General Public License does NOT permit incorporating this software
+ * into proprietary programs. If you are unable to comply with the GPL, a
+ * commercial license for this software may be purchased from PeerSec Networks
+ * at http://www.peersec.com
+ *
+ * This program is distributed in WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+/******************************************************************************/
+
+#ifndef _h_PSCRYPTO
+#define _h_PSCRYPTO
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ PeerSec crypto-specific defines.
+ */
+#define SMALL_CODE
+#define CLEAN_STACK
+/*
+ If Native 64 bit integers are not supported, we must set the 16 bit flag
+ to produce 32 bit mp_words in mpi.h
+ We must also include the slow MPI functions because the fast ones only
+ work with larger (28 bit) digit sizes.
+*/
+#ifndef USE_INT64
+#define MP_16BIT
+#define USE_SMALL_WORD
+#endif /* USE_INT64 */
+
+/******************************************************************************/
+
+#ifdef USE_RSA
+
+#include "mpi.h"
+
+#if LINUX
+ #define _stat stat
+#endif
+
+/* this is the "32-bit at least" data type
+ * Re-define it to suit your platform but it must be at least 32-bits
+ */
+typedef unsigned long ulong32;
+
+/*
+ Primary RSA Key struct. Define here for crypto
+*/
+typedef struct {
+ mp_int e, d, N, qP, dP, dQ, p, q;
+ int32 size; /* Size of the key in bytes */
+ int32 optimized; /* 1 for optimized */
+} sslRsaKey_t;
+
+#endif /* USE_RSA */
+
+
+/*
+ * Private
+ */
+extern int32 ps_base64_decode(const unsigned char *in, uint32 len,
+ unsigned char *out, uint32 *outlen);
+
+/*
+ * Memory routines
+ */
+extern void psZeromem(void *dst, size_t len);
+extern void psBurnStack(unsigned long len);
+
+
+/* max size of either a cipher/hash block or symmetric key [largest of the two] */
+#define MAXBLOCKSIZE 24
+
+/* ch1-01-1 */
+/* error codes [will be expanded in future releases] */
+enum {
+ CRYPT_OK=0, /* Result OK */
+ CRYPT_ERROR, /* Generic Error */
+ CRYPT_NOP, /* Not a failure but no operation was performed */
+
+ CRYPT_INVALID_KEYSIZE, /* Invalid key size given */
+ CRYPT_INVALID_ROUNDS, /* Invalid number of rounds */
+ CRYPT_FAIL_TESTVECTOR, /* Algorithm failed test vectors */
+
+ CRYPT_BUFFER_OVERFLOW, /* Not enough space for output */
+ CRYPT_INVALID_PACKET, /* Invalid input packet given */
+
+ CRYPT_INVALID_PRNGSIZE, /* Invalid number of bits for a PRNG */
+ CRYPT_ERROR_READPRNG, /* Could not read enough from PRNG */
+
+ CRYPT_INVALID_CIPHER, /* Invalid cipher specified */
+ CRYPT_INVALID_HASH, /* Invalid hash specified */
+ CRYPT_INVALID_PRNG, /* Invalid PRNG specified */
+
+ CRYPT_MEM, /* Out of memory */
+
+ CRYPT_PK_TYPE_MISMATCH, /* Not equivalent types of PK keys */
+ CRYPT_PK_NOT_PRIVATE, /* Requires a private PK key */
+
+ CRYPT_INVALID_ARG, /* Generic invalid argument */
+ CRYPT_FILE_NOTFOUND, /* File Not Found */
+
+ CRYPT_PK_INVALID_TYPE, /* Invalid type of PK key */
+ CRYPT_PK_INVALID_SYSTEM, /* Invalid PK system specified */
+ CRYPT_PK_DUP, /* Duplicate key already in key ring */
+ CRYPT_PK_NOT_FOUND, /* Key not found in keyring */
+ CRYPT_PK_INVALID_SIZE, /* Invalid size input for PK parameters */
+
+ CRYPT_INVALID_PRIME_SIZE /* Invalid size of prime requested */
+};
+
+/******************************************************************************/
+/*
+ hash defines
+ */
+struct sha1_state {
+#ifdef USE_INT64
+ ulong64 length;
+#else
+ ulong32 lengthHi;
+ ulong32 lengthLo;
+#endif /* USE_INT64 */
+ ulong32 state[5], curlen;
+ unsigned char buf[64];
+};
+
+struct md5_state {
+#ifdef USE_INT64
+ ulong64 length;
+#else
+ ulong32 lengthHi;
+ ulong32 lengthLo;
+#endif /* USE_INT64 */
+ ulong32 state[4], curlen;
+ unsigned char buf[64];
+};
+
+#ifdef USE_MD2
+struct md2_state {
+ unsigned char chksum[16], X[48], buf[16];
+ unsigned long curlen;
+};
+#endif /* USE_MD2 */
+
+#ifdef USE_SHA256
+struct sha256_state {
+ ulong64 length;
+ ulong32 state[8], curlen;
+ unsigned char buf[64];
+};
+#endif /* USE_SHA256 */
+
+typedef union {
+ struct sha1_state sha1;
+ struct md5_state md5;
+#ifdef USE_MD2
+ struct md2_state md2;
+#endif /* USE_MD2 */
+#ifdef USE_SHA256
+ struct sha256_state sha256;
+#endif
+} hash_state;
+
+typedef hash_state sslSha1Context_t;
+typedef hash_state sslMd5Context_t;
+#ifdef USE_MD2
+typedef hash_state sslMd2Context_t;
+#endif /* USE_MD2 */
+#ifdef USE_SHA256
+typedef hash_state sslSha256Context_t;
+#endif /* USE_SHA256 */
+
+typedef struct {
+ unsigned char pad[64];
+ union {
+ sslMd5Context_t md5;
+ sslSha1Context_t sha1;
+ } u;
+} sslHmacContext_t;
+
+/******************************************************************************/
+/*
+ RC4
+ */
+#ifdef USE_ARC4
+typedef struct {
+ unsigned char state[256];
+ uint32 byteCount;
+ unsigned char x;
+ unsigned char y;
+} rc4_key;
+#endif /* USE_ARC4 */
+
+#define SSL_DES3_KEY_LEN 24
+#define SSL_DES3_IV_LEN 8
+#ifdef USE_3DES
+
+typedef struct {
+ ulong32 ek[3][32], dk[3][32];
+} des3_key;
+
+/*
+ A block cipher CBC structure
+ */
+typedef struct {
+ int32 blocklen;
+ unsigned char IV[8];
+ des3_key key;
+ int32 explicitIV; /* 1 if yes */
+} des3_CBC;
+
+extern int32 des3_setup(const unsigned char *key, int32 keylen, int32 num_rounds,
+ des3_CBC *skey);
+extern void des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct,
+ des3_CBC *key);
+extern void des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt,
+ des3_CBC *key);
+extern int32 des3_keysize(int32 *desired_keysize);
+
+extern int32 des_setup(const unsigned char *key, int32 keylen, int32 num_rounds,
+ des3_CBC *skey);
+extern void des_ecb_encrypt(const unsigned char *pt, unsigned char *ct,
+ des3_CBC *key);
+extern void des_ecb_decrypt(const unsigned char *ct, unsigned char *pt,
+ des3_CBC *key);
+
+#endif /* USE_3DES */
+
+
+typedef union {
+#ifdef USE_ARC4
+ rc4_key arc4;
+#endif
+#ifdef USE_3DES
+ des3_CBC des3;
+#endif
+} sslCipherContext_t;
+
+
+/*
+ Controls endianess and size of registers. Leave uncommented to get
+ platform neutral [slower] code detect x86-32 machines somewhat
+ */
+#if (defined(_MSC_VER) && defined(WIN32)) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__)))
+ #define ENDIAN_LITTLE
+ #define ENDIAN_32BITWORD
+#endif
+
+
+/* #define ENDIAN_LITTLE */
+/* #define ENDIAN_BIG */
+
+/* #define ENDIAN_32BITWORD */
+/* #define ENDIAN_64BITWORD */
+
+#if (defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) && !(defined(ENDIAN_32BITWORD) || defined(ENDIAN_64BITWORD))
+ #error You must specify a word size as well as endianess
+#endif
+
+#if !(defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE))
+ #define ENDIAN_NEUTRAL
+#endif
+
+/*
+ helper macros
+ */
+#if defined (ENDIAN_NEUTRAL)
+
+#define STORE32L(x, y) \
+ { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
+ (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }
+
+#define LOAD32L(x, y) \
+ { x = ((unsigned long)((y)[3] & 255)<<24) | \
+ ((unsigned long)((y)[2] & 255)<<16) | \
+ ((unsigned long)((y)[1] & 255)<<8) | \
+ ((unsigned long)((y)[0] & 255)); }
+
+#define STORE64L(x, y) \
+ { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \
+ (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \
+ (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
+ (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }
+
+#define LOAD64L(x, y) \
+ { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \
+ (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \
+ (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \
+ (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); }
+
+#define STORE32H(x, y) \
+ { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \
+ (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); }
+
+#define LOAD32H(x, y) \
+ { x = ((unsigned long)((y)[0] & 255)<<24) | \
+ ((unsigned long)((y)[1] & 255)<<16) | \
+ ((unsigned long)((y)[2] & 255)<<8) | \
+ ((unsigned long)((y)[3] & 255)); }
+
+#define STORE64H(x, y) \
+ { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
+ (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
+ (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
+ (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
+
+#define LOAD64H(x, y) \
+ { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \
+ (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \
+ (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \
+ (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); }
+
+#endif /* ENDIAN_NEUTRAL */
+
+#ifdef ENDIAN_LITTLE
+
+#define STORE32H(x, y) \
+ { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \
+ (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); }
+
+#define LOAD32H(x, y) \
+ { x = ((unsigned long)((y)[0] & 255)<<24) | \
+ ((unsigned long)((y)[1] & 255)<<16) | \
+ ((unsigned long)((y)[2] & 255)<<8) | \
+ ((unsigned long)((y)[3] & 255)); }
+
+#define STORE64H(x, y) \
+ { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
+ (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
+ (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
+ (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
+
+#define LOAD64H(x, y) \
+ { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \
+ (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \
+ (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \
+ (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); }
+
+#ifdef ENDIAN_32BITWORD
+
+#define STORE32L(x, y) \
+ { unsigned long __t = (x); memcpy(y, &__t, 4); }
+
+#define LOAD32L(x, y) \
+ memcpy(&(x), y, 4);
+
+#define STORE64L(x, y) \
+ { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \
+ (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \
+ (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
+ (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }
+
+#define LOAD64L(x, y) \
+ { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \
+ (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \
+ (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \
+ (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); }
+
+#else /* 64-bit words then */
+
+#define STORE32L(x, y) \
+ { unsigned long __t = (x); memcpy(y, &__t, 4); }
+
+#define LOAD32L(x, y) \
+ { memcpy(&(x), y, 4); x &= 0xFFFFFFFF; }
+
+#define STORE64L(x, y) \
+ { ulong64 __t = (x); memcpy(y, &__t, 8); }
+
+#define LOAD64L(x, y) \
+ { memcpy(&(x), y, 8); }
+
+#endif /* ENDIAN_64BITWORD */
+#endif /* ENDIAN_LITTLE */
+
+#ifdef ENDIAN_BIG
+#define STORE32L(x, y) \
+ { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
+ (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }
+
+#define LOAD32L(x, y) \
+ { x = ((unsigned long)((y)[3] & 255)<<24) | \
+ ((unsigned long)((y)[2] & 255)<<16) | \
+ ((unsigned long)((y)[1] & 255)<<8) | \
+ ((unsigned long)((y)[0] & 255)); }
+
+#define STORE64L(x, y) \
+ { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \
+ (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \
+ (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
+ (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }
+
+#define LOAD64L(x, y) \
+ { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48) | \
+ (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32) | \
+ (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16) | \
+ (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); }
+
+#ifdef ENDIAN_32BITWORD
+
+#define STORE32H(x, y) \
+ { unsigned long __t = (x); memcpy(y, &__t, 4); }
+
+#define LOAD32H(x, y) \
+ memcpy(&(x), y, 4);
+
+#define STORE64H(x, y) \
+ { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
+ (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
+ (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
+ (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
+
+#define LOAD64H(x, y) \
+ { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48)| \
+ (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32)| \
+ (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16)| \
+ (((ulong64)((y)[6] & 255))<<8)| (((ulong64)((y)[7] & 255))); }
+
+#else /* 64-bit words then */
+
+#define STORE32H(x, y) \
+ { unsigned long __t = (x); memcpy(y, &__t, 4); }
+
+#define LOAD32H(x, y) \
+ { memcpy(&(x), y, 4); x &= 0xFFFFFFFF; }
+
+#define STORE64H(x, y) \
+ { ulong64 __t = (x); memcpy(y, &__t, 8); }
+
+#define LOAD64H(x, y) \
+ { memcpy(&(x), y, 8); }
+
+#endif /* ENDIAN_64BITWORD */
+#endif /* ENDIAN_BIG */
+
+/*
+ packet code */
+#if defined(USE_RSA) || defined(MDH) || defined(MECC)
+ #define PACKET
+
+/*
+ size of a packet header in bytes */
+ #define PACKET_SIZE 4
+
+/*
+ Section tags
+ */
+ #define PACKET_SECT_RSA 0
+ #define PACKET_SECT_DH 1
+ #define PACKET_SECT_ECC 2
+ #define PACKET_SECT_DSA 3
+
+/*
+ Subsection Tags for the first three sections
+ */
+ #define PACKET_SUB_KEY 0
+ #define PACKET_SUB_ENCRYPTED 1
+ #define PACKET_SUB_SIGNED 2
+ #define PACKET_SUB_ENC_KEY 3
+#endif
+
+/*
+ fix for MSVC ...evil!
+ */
+#ifdef WIN32
+#ifdef _MSC_VER
+ #define CONST64(n) n ## ui64
+ typedef unsigned __int64 ulong64;
+#else
+ #define CONST64(n) n ## ULL
+ typedef unsigned long long ulong64;
+#endif
+#endif /* WIN32 */
+
+
+#define BSWAP(x) ( ((x>>24)&0x000000FFUL) | ((x<<24)&0xFF000000UL) | \
+ ((x>>8)&0x0000FF00UL) | ((x<<8)&0x00FF0000UL) )
+
+#ifdef _MSC_VER
+
+/*
+ instrinsic rotate
+ */
+#include <stdlib.h>
+#pragma intrinsic(_lrotr,_lrotl)
+#define ROR(x,n) _lrotr(x,n)
+#define ROL(x,n) _lrotl(x,n)
+#define RORc(x,n) _lrotr(x,n)
+#define ROLc(x,n) _lrotl(x,n)
+
+/*
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && !defined(INTEL_CC) && !defined(PS_NO_ASM)
+
+static inline unsigned ROL(unsigned word, int32 i)
+{
+ asm ("roll %%cl,%0"
+ :"0" (word),"c" (i));
+ return word;
+}
+
+static inline unsigned ROR(unsigned word, int32 i)
+{
+ asm ("rorl %%cl,%0"
+ :"=r" (word)
+ :"0" (word),"c" (i));
+ return word;
+}
+*/
+/*
+#ifndef PS_NO_ROLC
+
+static inline unsigned ROLc(unsigned word, const int32 i)
+{
+ asm ("roll %2,%0"
+ :"=r" (word)
+ :"0" (word),"I" (i));
+ return word;
+}
+
+static inline unsigned RORc(unsigned word, const int32 i)
+{
+ asm ("rorl %2,%0"
+ :"=r" (word)
+ :"0" (word),"I" (i));
+ return word;
+}
+
+#else
+
+#define ROLc ROL
+#define RORc ROR
+
+#endif
+*/
+
+#else /* _MSC_VER */
+
+/*
+ rotates the hard way
+ */
+#define ROL(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#define ROR(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#define ROLc(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#define RORc(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+
+#endif /* _MSC_VER */
+
+/* 64-bit Rotates */
+#if 0
+
+#if defined(__GNUC__) && defined(__x86_64__) && !defined(PS_NO_ASM)
+
+static inline unsigned long ROL64(unsigned long word, int32 i)
+{
+ asm("rolq %%cl,%0"
+ :"=r" (word)
+ :"0" (word),"c" (i));
+ return word;
+}
+
+static inline unsigned long ROR64(unsigned long word, int32 i)
+{
+ asm("rorq %%cl,%0"
+ :"=r" (word)
+ :"0" (word),"c" (i));
+ return word;
+}
+
+#ifndef PS_NO_ROLC
+
+static inline unsigned long ROL64c(unsigned long word, const int32 i)
+{
+ asm("rolq %2,%0"
+ :"=r" (word)
+ :"0" (word),"J" (i));
+ return word;
+}
+
+static inline unsigned long ROR64c(unsigned long word, const int32 i)
+{
+ asm("rorq %2,%0"
+ :"=r" (word)
+ :"0" (word),"J" (i));
+ return word;
+}
+
+#else /* PS_NO_ROLC */
+
+#define ROL64c ROL
+#define ROR64c ROR
+
+#endif /* PS_NO_ROLC */
+#endif
+#endif /* commented out */
+
+#define ROL64(x, y) \
+ ( (((x)<<((ulong64)(y)&63)) | \
+ (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)64-((y)&63)))) & CONST64(0xFFFFFFFFFFFFFFFF))
+
+#define ROR64(x, y) \
+ ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \
+ ((x)<<((ulong64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF))
+
+#define ROL64c(x, y) \
+ ( (((x)<<((ulong64)(y)&63)) | \
+ (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)64-((y)&63)))) & CONST64(0xFFFFFFFFFFFFFFFF))
+
+#define ROR64c(x, y) \
+ ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \
+ ((x)<<((ulong64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF))
+
+#undef MAX
+#undef MIN
+#define MAX(x, y) ( ((x)>(y))?(x):(y) )
+#define MIN(x, y) ( ((x)<(y))?(x):(y) )
+
+/*
+ extract a byte portably This MSC code causes runtime errors in VS.NET,
+ always use the other
+ */
+/*
+#ifdef _MSC_VER
+ #define byte(x, n) ((unsigned char)((x) >> (8 * (n))))
+#else
+*/
+ #define byte(x, n) (((x) >> (8 * (n))) & 255)
+/*
+#endif
+*/
+#ifdef __cplusplus
+ }
+#endif /* __cplusplus */
+
+#endif /* _h_PSCRYPTO */
+
+/******************************************************************************/
+
diff --git a/gpxe/src/crypto/md5.c b/gpxe/src/crypto/md5.c
new file mode 100644
index 00000000..1fed24fc
--- /dev/null
+++ b/gpxe/src/crypto/md5.c
@@ -0,0 +1,235 @@
+/*
+ * Cryptographic API.
+ *
+ * MD5 Message Digest Algorithm (RFC1321).
+ *
+ * Derived from cryptoapi implementation, originally based on the
+ * public domain implementation written by Colin Plumb in 1993.
+ *
+ * Reduced object size by around 50% compared to the original Linux
+ * version for use in Etherboot by Michael Brown.
+ *
+ * Copyright (c) Cryptoapi developers.
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ * Copyright (c) 2006 Michael Brown <mbrown@fensystems.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <byteswap.h>
+#include <gpxe/crypto.h>
+#include <gpxe/md5.h>
+
+#define __md5step __attribute__ (( regparm ( 3 ) ))
+
+struct md5_step {
+ u32 __md5step ( * f ) ( u32 b, u32 c, u32 d );
+ u8 coefficient;
+ u8 constant;
+};
+
+static u32 __md5step f1(u32 b, u32 c, u32 d)
+{
+ return ( d ^ ( b & ( c ^ d ) ) );
+}
+
+static u32 __md5step f2(u32 b, u32 c, u32 d)
+{
+ return ( c ^ ( d & ( b ^ c ) ) );
+}
+
+static u32 __md5step f3(u32 b, u32 c, u32 d)
+{
+ return ( b ^ c ^ d );
+}
+
+static u32 __md5step f4(u32 b, u32 c, u32 d)
+{
+ return ( c ^ ( b | ~d ) );
+}
+
+static struct md5_step md5_steps[4] = {
+ {
+ .f = f1,
+ .coefficient = 1,
+ .constant = 0,
+ },
+ {
+ .f = f2,
+ .coefficient = 5,
+ .constant = 1,
+ },
+ {
+ .f = f3,
+ .coefficient = 3,
+ .constant = 5,
+ },
+ {
+ .f = f4,
+ .coefficient = 7,
+ .constant = 0,
+ }
+};
+
+static const u8 r[64] = {
+ 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
+ 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
+ 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
+ 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
+};
+
+static const u32 k[64] = {
+ 0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL,
+ 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL,
+ 0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL,
+ 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL,
+ 0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL,
+ 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL,
+ 0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL,
+ 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL,
+ 0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL,
+ 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL,
+ 0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL,
+ 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL,
+ 0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL,
+ 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL,
+ 0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL,
+ 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL,
+};
+
+static void md5_transform(u32 *hash, const u32 *in)
+{
+ u32 a, b, c, d, f, g, temp;
+ int i;
+ struct md5_step *step;
+
+ a = hash[0];
+ b = hash[1];
+ c = hash[2];
+ d = hash[3];
+
+ for ( i = 0 ; i < 64 ; i++ ) {
+ step = &md5_steps[i >> 4];
+ f = step->f ( b, c, d );
+ g = ( ( i * step->coefficient + step->constant ) & 0xf );
+ temp = d;
+ d = c;
+ c = b;
+ a += ( f + k[i] + in[g] );
+ a = ( ( a << r[i] ) | ( a >> ( 32-r[i] ) ) );
+ b += a;
+ a = temp;
+ }
+
+ hash[0] += a;
+ hash[1] += b;
+ hash[2] += c;
+ hash[3] += d;
+}
+
+/* XXX: this stuff can be optimized */
+static inline void le32_to_cpu_array(u32 *buf, unsigned int words)
+{
+ while (words--) {
+ le32_to_cpus(buf);
+ buf++;
+ }
+}
+
+static inline void cpu_to_le32_array(u32 *buf, unsigned int words)
+{
+ while (words--) {
+ cpu_to_le32s(buf);
+ buf++;
+ }
+}
+
+static inline void md5_transform_helper(struct md5_ctx *ctx)
+{
+ le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(u32));
+ md5_transform(ctx->hash, ctx->block);
+}
+
+static void md5_init(void *context)
+{
+ struct md5_ctx *mctx = context;
+
+ mctx->hash[0] = 0x67452301;
+ mctx->hash[1] = 0xefcdab89;
+ mctx->hash[2] = 0x98badcfe;
+ mctx->hash[3] = 0x10325476;
+ mctx->byte_count = 0;
+}
+
+static void md5_update(void *context, const void *data, void *dst __unused,
+ size_t len)
+{
+ struct md5_ctx *mctx = context;
+ const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
+
+ mctx->byte_count += len;
+
+ if (avail > len) {
+ memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
+ data, len);
+ return;
+ }
+
+ memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
+ data, avail);
+
+ md5_transform_helper(mctx);
+ data += avail;
+ len -= avail;
+
+ while (len >= sizeof(mctx->block)) {
+ memcpy(mctx->block, data, sizeof(mctx->block));
+ md5_transform_helper(mctx);
+ data += sizeof(mctx->block);
+ len -= sizeof(mctx->block);
+ }
+
+ memcpy(mctx->block, data, len);
+}
+
+static void md5_final(void *context, void *out)
+{
+ struct md5_ctx *mctx = context;
+ const unsigned int offset = mctx->byte_count & 0x3f;
+ char *p = (char *)mctx->block + offset;
+ int padding = 56 - (offset + 1);
+
+ *p++ = 0x80;
+ if (padding < 0) {
+ memset(p, 0x00, padding + sizeof (u64));
+ md5_transform_helper(mctx);
+ p = (char *)mctx->block;
+ padding = 56;
+ }
+
+ memset(p, 0, padding);
+ mctx->block[14] = mctx->byte_count << 3;
+ mctx->block[15] = mctx->byte_count >> 29;
+ le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
+ sizeof(u64)) / sizeof(u32));
+ md5_transform(mctx->hash, mctx->block);
+ cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(u32));
+ memcpy(out, mctx->hash, sizeof(mctx->hash));
+ memset(mctx, 0, sizeof(*mctx));
+}
+
+struct crypto_algorithm md5_algorithm = {
+ .name = "md5",
+ .ctxsize = MD5_CTX_SIZE,
+ .blocksize = ( MD5_BLOCK_WORDS * 4 ),
+ .digestsize = MD5_DIGEST_SIZE,
+ .init = md5_init,
+ .encode = md5_update,
+ .final = md5_final,
+};
diff --git a/gpxe/src/crypto/ssl.c b/gpxe/src/crypto/ssl.c
new file mode 100644
index 00000000..8abd7af8
--- /dev/null
+++ b/gpxe/src/crypto/ssl.c
@@ -0,0 +1,136 @@
+#if 0
+
+#include "ssl.h"
+#include "ssl_constructs.h"
+#include <string.h> // for bcopy()
+#include <time.h> // for time()
+#include <stdlib.h> // for rand(), htons?, htonl?
+// note net byte order is big-endian
+// Need to set error codes
+
+int CreateSSLHello(SSL_t *ssl)
+{
+ printf("In CreateSSLHello()\n",ssl);
+
+ // Initalize the structure
+ bzero(ssl,sizeof(SSL_t));
+ //ssl->max_size = sizeof(ssl->buffer);
+ ssl->max_size = 18456;
+
+ // Declare variables
+ int i; void *ptr;
+
+ // Set pointers into buffer
+ SSLPlaintext *record = (SSLPlaintext *)ssl->buffer;
+ Handshake *handshake = (Handshake *)record->fragment;
+ // the body starts right after the handshake
+ printf("sizeof(Handshake) = %d\n",sizeof(Handshake));
+ ClientHello *hello = (ClientHello *)(handshake + 1);
+
+ printf("record->%#x, handshake->%#x, hello->%#x\n",record,handshake,hello);
+
+ // Construct ClientHello Message
+ hello->client_version = version;
+ i = htonl(time(NULL));
+ bcopy(&i,hello->random.gmt_unix_time,4);
+ for(i=0;i<28;i++){ hello->random.random_bytes[i] = (uint8)rand(); }
+ hello->session_id_length = 0;
+ hello->session_id = &hello->session_id_length;
+ hello->session_id_end = hello->session_id;
+ hello->cipher_suites_length = (CipherSuiteLength *)(hello->session_id_end + 1);
+ hello->cipher_suites = (hello->cipher_suites_length + 1);
+ hello->cipher_suites_end = hello->cipher_suites;
+ i = htons(2*5); // 2 bytes per Suite * 5 Suites
+ bcopy(&i,hello->cipher_suites_length,2);
+ bcopy(SSL_NULL_WITH_NULL_NULL,hello->cipher_suites_end,sizeof(CipherSuite));
+ *hello->cipher_suites_end++;
+ bcopy(SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,hello->cipher_suites_end,sizeof(CipherSuite));
+ *hello->cipher_suites_end++;
+ bcopy(SSL_DH_DSS_WITH_DES_CBC_SHA,hello->cipher_suites_end,sizeof(CipherSuite));
+ *hello->cipher_suites_end++;
+ bcopy(SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,hello->cipher_suites_end,sizeof(CipherSuite));
+ *hello->cipher_suites_end++;
+ bcopy(SSL_DH_anon_WITH_RC4_128_MD5,hello->cipher_suites_end,sizeof(CipherSuite));
+ hello->compression_methods_length = (CompressionMethodLength *)(hello->cipher_suites_end + 1);
+ hello->compression_methods = (hello->compression_methods_length + 1);
+ hello->compression_methods_end = hello->compression_methods;
+ *hello->compression_methods_length = 1;
+ *hello->compression_methods_end = compression_method_null;
+
+ // Construct Handshake Message
+ handshake->msg_type = handshake_type_client_hello;
+ i = (void *)(hello->compression_methods_end + 1) - (void *)hello;
+ printf("Handshake.length = %d\n", i);
+ handshake->length[0] = (char)*(&i+8);
+ handshake->length[1] = (char)*(&i+8);
+ handshake->length[2] = (char)i;
+ //bcopy((&i+1),handshake->length,3); // +1 so we copy 3 bytes
+
+ // Construct SSL Record
+ printf("sizeof(ContentType)=%d\n",sizeof(ContentType));
+ printf("sizeof(uint8)=%d\n",sizeof(uint8));
+ record->type = content_type_handshake;
+ record->version = version;
+ i += sizeof(Handshake);
+ printf("SSLPlaintext.length = %d\n",i);
+ record->length[0] = (char)*(&i+8);
+ record->length[1] = (char)i;
+ //bcopy(&i,record->length,4); // length of handshake
+
+ // Set total size of message
+ i += sizeof(ContentType) + sizeof(ProtocolVersion) + sizeof(uint16);
+ ssl->length = i;
+ printf("End of CreateSSLHello\n");
+ return 0;
+}
+
+void PrintSSLPacket(SSL_t *ssl)
+{
+ printf("Printing packet with length:%d\n", ssl->length);
+ char *ptr = ssl->buffer;
+ char *begin = ptr;
+ char *tmp;
+ char *end = ssl->buffer + ssl->length;
+ printf("Record Layer:\n");
+ printf("\tContentType: %2hhX\n",(char)*ptr++);
+ printf("\tVersion: %2hhX %2hhX\n", (char)*ptr++, (char)*ptr++);
+ printf("\tLength: %2hhX %2hhX\n", (char)*ptr++, (char)*ptr++);
+
+ printf("Handshake:\n");
+ printf("\tType: %2hhX\n", (char)*ptr++);
+ printf("\tLength: %2hhX %2hhX %2hhX\n", (char)*ptr++, (char)*ptr++, (char)*ptr++);
+ printf("\tVersion: %2hhX %2hhX\n", (char)*ptr++, (char)*ptr++);
+ printf("\tgmt_unix_time: %2hhX %2hhX %2hhX %2hhX\n", (char)*ptr++, (char)*ptr++, (char)*ptr++, (char)*ptr++);
+ printf("\trandom: ");
+ tmp = ptr + 28;
+ for(;ptr<tmp;ptr++){printf("%2hhX ", (char)*ptr);}
+
+ printf("\n\nHexDump:\n");
+
+ int ctr = 0;
+ for(;begin<end;begin++){printf("%2hhX ",(char)*begin);if(++ctr%10==0){printf("\n");}}
+ printf("\n\n");
+}
+
+int ReadSSLHello(SSL_t *ssl)
+{
+ SSLCiphertext *ct = (SSLCiphertext *)ssl->buffer;
+
+ if(ct->type == content_type_alert){
+ // assuming text is still plaintext
+ Alert *a = (Alert *)&ct->fragment;
+ if(a->level == alert_level_fatal){
+ printf("Fatal Alert %d, connection terminated\n",a->description);
+ return (1);
+ }else if(a->level == alert_level_warning){
+ printf("Warning Alert %d\n", a->description);
+ }else{
+ printf("Unknown alert level %d\n", a->level);
+ }
+ }else{
+ printf("SSL type %d\n",ct->type);
+ }
+ return (0);
+}
+
+#endif
diff --git a/gpxe/src/crypto/ssl.h b/gpxe/src/crypto/ssl.h
new file mode 100644
index 00000000..06d43008
--- /dev/null
+++ b/gpxe/src/crypto/ssl.h
@@ -0,0 +1,19 @@
+// At the moment I have hard coded one buffer. The size
+// is the max size of SSLCiphertext.length (so, actually it should
+// be increased to include the other information in the struct)
+// I might need to make a new, or split the current, buffer because
+// I have to have space to read in and write out, as well as keep
+// any data that has not been translated.
+// It works for now.
+typedef struct _ssl_t{
+ char buffer[18456];
+ int length;
+ int max_size; // can't define const here
+ // Current CipherSuite
+ // Client random / Server random ???
+ // pointers to different crypto functions
+} SSL_t;
+
+int CreateSSLHello(SSL_t *ssl);
+int ReadSSLHello(SSL_t *ssl);
+void PrintSSLPacket(SSL_t *ssl);
diff --git a/gpxe/src/crypto/ssl_constructs.h b/gpxe/src/crypto/ssl_constructs.h
new file mode 100644
index 00000000..ab3aa703
--- /dev/null
+++ b/gpxe/src/crypto/ssl_constructs.h
@@ -0,0 +1,342 @@
+// Note: This file still needs some work.
+// Note: I had to redefine the enums to a set of const values,
+// so that the size of the variable would be correct.
+
+// Typedefs
+// (As defined by the SSL v3.0 RFC Draft)
+// URL: http://wp.netscape.com/eng/ssl3/draft302.txt
+typedef unsigned char uint8;
+typedef uint8 uint16[2];
+typedef uint8 uint24[3];
+typedef uint8 uint32[4];
+typedef uint8 uint64[8];
+
+// Record layers
+typedef struct _ProtocolVersion{
+ uint8 major, minor;
+} ProtocolVersion;
+
+const ProtocolVersion version = { 3, 0 };
+
+typedef uint8 ContentType;
+const ContentType content_type_change_cipher_spec_type = 20;
+const ContentType content_type_alert = 21;
+const ContentType content_type_handshake = 22;
+const ContentType content_type_application_data = 23;
+
+typedef struct _SSLPlaintext{
+ ContentType type;
+ ProtocolVersion version;
+ uint16 length; // can not exceed 2^14 bytes
+ uint8 fragment[16384]; // 2^14 = 16,384 bytes
+} SSLPlaintext;
+
+typedef struct _SSLCompressed{
+ ContentType type;
+ ProtocolVersion version;
+ uint16 length; // can not exceed 2^14 + 1024
+ uint8 fragment[17408]; // SSLCompressed.length
+} SSLCompressed;
+
+typedef struct _SSLCiphertext{
+ ContentType type;
+ ProtocolVersion version;
+ uint16 length;
+ uint8 fragment; // so we have a pointer to the data, and don't have to do math
+ // fragment; type GenericStreamCipher or GenericBlockCipher
+} SSLCiphertext; // recast to get fragment
+
+typedef struct _GenericStreamCipher{
+ uint8 content[17408]; // SSLCompressed.length
+ uint8 MAC[]; // CipherSpec.hash_size
+} GenericStreamCipher;
+
+typedef struct _SSLStreamCiphertext{
+ ContentType type;
+ ProtocolVersion version;
+ uint16 length; // can not exceed 2^14 + 2048 = 18,456
+ GenericStreamCipher fragment;
+} SSLStreamCiphertext;
+
+typedef struct _GenericBlockCipher{
+ uint8 content[17408]; // SSLConpressed.length
+ uint8 MAC[0]; // CipherSpec.hash_size
+ // padding is used to bring the plaintext to
+ // a multiple of the block cipher's block length.
+ uint8 padding[0]; // GenericBlockCipher.padding_length
+ uint8 padding_length;
+} GenericBlockCipher;
+
+typedef struct _SSLBlockCiphertext{
+ ContentType type;
+ ProtocolVersion version;
+ uint16 length; // can not exceed 2^14 + 2048 = 18,456
+ GenericBlockCipher fragment;
+} SSLBlockCiphertext;
+
+// Change cipher specs message
+typedef struct _ChangeCipherSpec{
+ enum { type_change_cipher_spec=1, type_size=255 } type;
+} ChangeCipherSpec;
+
+// Alert messages
+typedef uint8 AlertLevel;
+const AlertLevel alert_level_warning = 1;
+const AlertLevel alert_level_fatal=2;
+
+typedef uint8 AlertDescription;
+const AlertDescription alert_description_close_notify = 0;
+const AlertDescription alert_description_unexpected_message = 10;
+const AlertDescription alert_description_bad_record_mac = 20;
+const AlertDescription alert_description_decompression_failure = 30;
+const AlertDescription alert_description_handshake_failure = 40;
+const AlertDescription alert_description_no_certificate = 41;
+const AlertDescription alert_description_bad_certificate = 42;
+const AlertDescription alert_description_unsupported_certificate = 43;
+const AlertDescription alert_description_certificate_revoked = 44;
+const AlertDescription alert_description_certificate_expired = 45;
+const AlertDescription alert_description_certificate_unknown = 46;
+const AlertDescription alert_description_illegal_parameter = 47;
+
+typedef struct _Alert{
+ AlertLevel level;
+ AlertDescription description;
+} Alert;
+
+// Handshake protocol
+// What is the best way to have a generic pointer to the body struct??
+typedef uint8 HandshakeType;
+const HandshakeType handshake_type_hello_request = 0;
+const HandshakeType handshake_type_client_hello = 1;
+const HandshakeType handshake_type_server_hello = 2;
+const HandshakeType handshake_type_certificate = 11;
+const HandshakeType handshake_type_server_key_exchange = 12;
+const HandshakeType handshake_type_certificate_request = 13;
+const HandshakeType handshake_type_server_done = 14;
+const HandshakeType handshake_type_certificate_verify = 15;
+const HandshakeType handshake_type_client_key_exchange = 16;
+const HandshakeType handshake_type_finished = 20;
+
+typedef struct _Handshake{
+ HandshakeType msg_type;
+ uint24 length;
+ // body; // one of HandshakeType structs
+} Handshake; // generic Handshake, need to recast to get body
+
+// Hello messages
+typedef struct _HelloRequest{} HelloRequest;
+
+typedef struct _HelloRequestHandshake{
+ HandshakeType msg_type;
+ uint24 length;
+ HelloRequest body;
+} HelloRequestHandshake;
+
+typedef struct _Random{
+ uint32 gmt_unix_time;
+ uint8 random_bytes[28];
+} Random;
+
+//typedef uint8 SessionID[32]; // <0..32>
+typedef uint8 SessionIDLength;
+typedef uint8 SessionID;
+
+typedef uint16 CipherSuiteLength;
+typedef uint8 CipherSuite[2];
+
+typedef uint8 CompressionMethodLength;
+typedef uint8 CompressionMethod;
+const CompressionMethod compression_method_null = 0;
+
+
+typedef struct _ClientHello{
+ ProtocolVersion client_version;
+ Random random;
+ SessionIDLength session_id_length;
+ SessionID *session_id;
+ SessionID *session_id_end;
+ CipherSuiteLength *cipher_suites_length;
+ CipherSuite *cipher_suites; // min size is one entry
+ CipherSuite *cipher_suites_end;
+ //CipherSuite cipher_suites[32768]; // <2..2^16-1> = 65,536 bytes and CipherSuite is 2 bytes
+ CompressionMethodLength *compression_methods_length;
+ CompressionMethod *compression_methods;
+ CompressionMethod *compression_methods_end;
+ //CompressionMethod *compression_methods; // min size is zero
+ //CompressionMethod compression_methods[256]; // <0..2^8-1> = 256 bytes and CompressionMethod is 1 byte
+} ClientHello;
+
+typedef struct _ClientHelloHandshake{
+ //HandshakeType msg_type;
+ uint8 msg_type;
+ uint24 length;
+ ClientHello body;
+} ClientHelloHandshake;
+
+typedef struct _ServerHello{
+ ProtocolVersion server_version;
+ Random random;
+ SessionID session_id;
+ CipherSuite cipher_suite;
+ CompressionMethod compression_method;
+} ServerHello;
+
+typedef struct _ServerHelloHandshake{
+ HandshakeType msg_type;
+ uint24 length;
+ ServerHello body;
+} ServerHelloHandshake;
+
+// Server authentication and key exchange messages
+typedef uint8 ASN1Cert[16777216]; // <1..2^24-1> = 16,777,216 bytes
+
+typedef struct _Certificate{
+ ASN1Cert certificate_list[1]; // <1..2^24-1> / ANS1Cert = 1
+ // for some reason the size of certificate_list and ASN1Cert is the same, so only one certificate in the list
+} Certificate;
+
+typedef uint8 KeyExchangeAlgorithm;
+const KeyExchangeAlgorithm key_exchange_algorithm_rsa = 0;
+const KeyExchangeAlgorithm key_exchange_algorithm_diffie_hellman = 1;
+const KeyExchangeAlgorithm key_exchange_algorithm_fortezza_kea = 2;
+
+typedef struct _AnonSignature{
+ struct {};
+} AnonSignature;
+
+typedef struct _RSASignature{
+ uint8 md5_hash[16];
+ uint8 sha_hash[20];
+} RSASignature;
+
+typedef struct _DSASignature{
+ uint8 sha_hash[20];
+} DSASignature;
+
+// use union??, make a mess to reference, but easy to make Signature type.
+typedef union _Signature{ AnonSignature anon; RSASignature rsa; DSASignature dsa; } Signature;
+
+typedef struct _ServerRSAParams{
+ uint8 RSA_modulus[65536]; // <1..2^16-1> = 65,536
+ uint8 RSA_exponent[65536]; // <1..2^16-1> = 65,536
+} ServerRSAParams;
+
+typedef struct _ServerDHParams{
+ uint8 DH_p[65536]; // <1..2^16-1>
+ uint8 DH_g[65536]; // <1..2^16-1>
+ uint8 DH_Ys[65536]; // <1..2^16-1>
+} ServerDHParams;
+
+typedef struct _ServerDHKeyExchange{
+ ServerDHParams params;
+ Signature signed_params;
+} ServerDHKeyExchange;
+
+typedef struct _ServerRSAKeyExchange{
+ ServerRSAParams params;
+ Signature signed_params;
+} ServerRSAKeyExchange;
+
+typedef uint8 SignatureAlgorithm;
+const SignatureAlgorithm signature_algorithm_anonymous = 0;
+const SignatureAlgorithm signature_algorithm_rsa = 1;
+const SignatureAlgorithm signature_algorithm_dsa = 2;
+
+typedef uint8 CertificateType;
+const CertificateType certificate_type_RSA_sign = 1;
+const CertificateType certificate_type_DSS_sign = 2;
+const CertificateType certificate_type_RSA_fixed_DH = 3;
+const CertificateType certificate_type_DSS_fixed_DH = 4;
+const CertificateType certificate_type_RSA_ephemeral_DH = 5;
+const CertificateType certificate_type_DSS_ephemeral_DH = 6;
+const CertificateType certificate_type_FORTEZZA_MISSI = 20;
+
+typedef uint8 DistinguishedName[65536]; // <1..2^16-1> = 65,536
+
+typedef struct _CertificateRequest{
+ CertificateType certificate_types[256]; // <1..2^8-1>
+ DistinguishedName certificate_authorities[1]; // <3...2^16-1> / DistinguishedName
+ // this is another one that is odd with a list size of 1
+} CertificateRequest;
+
+typedef struct _ServerHelloDone{} ServerHelloDone;
+
+// Client authentication and key exchange messages
+typedef struct _PreMasterSecret{
+ ProtocolVersion client_version;
+ uint8 random[46];
+} PreMasterSecret;
+
+typedef struct _EncryptedPreMasterSecret{
+ PreMasterSecret pre_master_secret;
+} EncryptedPreMasterSecret;
+
+typedef struct _RSAClientKeyExchange{
+ EncryptedPreMasterSecret exchange_keys;
+} RSAClientKeyExchange;
+
+typedef uint8 PublicValueEncoding;
+const PublicValueEncoding public_value_encoding_implicit = 0;
+const PublicValueEncoding public_value_encoding_explicit = 1;
+
+typedef struct _ClientDiffieHellmanPublic{
+ // This is a select on PublicValueEncoding, and I chose the larger size
+ uint8 dh_public[65536]; // DH_Yc<1..2^16-1>, the dh public value
+} ClientDiffieHellmanPublic;
+
+typedef struct _DHClientKeyExhange{
+ ClientDiffieHellmanPublic exchange_keys;
+} DHClientKeyExchange;
+
+typedef struct _CertificateVerify{
+ Signature signature;
+} CertificateVerify;
+
+// Handshake finalization message
+typedef struct _Finished{
+ uint8 md5_hash[16];
+ uint8 sha_hash[20];
+} Finished;
+
+// The CipherSuite
+CipherSuite SSL_NULL_WITH_NULL_NULL = { 0x00, 0x13 };
+CipherSuite SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x0B };
+CipherSuite SSL_DH_DSS_WITH_DES_CBC_SHA = { 0x00, 0x0C };
+CipherSuite SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x11 };
+CipherSuite SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 = { 0x00, 0x17 };
+CipherSuite SSL_DH_anon_WITH_RC4_128_MD5 = { 0x00, 0x18 };
+
+// The CipherSpec
+typedef uint8 CipherType;
+const CipherType cipher_type_stream = 0;
+const CipherType cipher_type_block = 1;
+
+typedef uint8 IsExportable;
+const IsExportable is_exportable_true = 0;
+const IsExportable is_exportable_false = 1;
+
+typedef uint8 BulkCipherAlgorithm;
+const BulkCipherAlgorithm bulk_cipher_algorithm_null = 0;
+const BulkCipherAlgorithm bulk_cipher_algorithm_rc4 = 1;
+const BulkCipherAlgorithm bulk_cipher_algorithm_rc2 = 2;
+const BulkCipherAlgorithm bulk_cipher_algorithm_des = 3;
+const BulkCipherAlgorithm bulk_cipher_algorithm_3des = 4;
+const BulkCipherAlgorithm bulk_cipher_algorithm_des40 = 5;
+const BulkCipherAlgorithm bulk_cipher_algorithm_fortezza = 6;
+
+typedef uint8 MACAlgorithm;
+const MACAlgorithm mac_algorithm_null = 0;
+const MACAlgorithm mac_algorithm_md5 = 1;
+const MACAlgorithm mac_algorithm_sha = 2;
+
+typedef struct _CipherSpec{
+ BulkCipherAlgorithm bulk_cipher_algorithm;
+ MACAlgorithm mac_algorithm;
+ CipherType cipher_type;
+ IsExportable is_exportable;
+ uint8 hash_size;
+ uint8 key_material;
+ uint8 IV_size;
+} CipherSpec;
+
+
diff --git a/gpxe/src/doc/build_sys.dox b/gpxe/src/doc/build_sys.dox
new file mode 100644
index 00000000..9466f662
--- /dev/null
+++ b/gpxe/src/doc/build_sys.dox
@@ -0,0 +1,419 @@
+/** @page build_sys Build system
+
+@section overview Overview
+
+Building an Etherboot image consists of three stages:
+
+ -# @ref compilation : Compiling all the source files into object files
+ -# @ref linking : Linking a particular image from selected object files
+ -# @ref finalisation : Producing the final output binary
+
+Though this is a remarkably complex process, it is important to note
+that it all happens automatically. Whatever state your build tree is
+in, you can always type, for example
+
+@code
+
+ make bin/rtl8139.dsk
+
+@endcode
+
+and know that you will get a floppy disk image with an RTL8139 driver
+built from the current sources.
+
+@section compilation Compilation
+
+@subsection comp_overview Overview
+
+Each source file (a @c .c or a @c .S file) is compiled into a @c .o
+file in the @c bin/ directory. Etherboot makes minimal use of
+conditional compilation (see @ref ifdef_harmful), and so you will find
+that all objects get built, even the objects that correspond to
+features that you are not intending to include in your image. For
+example, all network card drivers will be compiled even if you are
+just building a ROM for a 3c509 card. This is a deliberate design
+decision; please do @b not attempt to "fix" the build system to avoid
+doing this.
+
+Source files are defined to be any @c .c or @c .S files found in a
+directory listed in the Makefile variable #SRCDIRS. You therefore do
+@b not need to edit the Makefile just because you have added a new
+source file (although you will need to edit the Makefile if you have
+added a new source directory). To see a list of all source
+directories and source files that the build system currently knows
+about, you can use the commands
+
+@code
+
+ make srcdirs
+ make srcs
+
+@endcode
+
+Rules for compiling @c .c and @c .S files are defined in the Makefile
+variables #RULE_c and #RULE_S. Makefile rules are automatically
+generated for each source file using these rules. The generated rules
+can be found in the @c .d file corresponding to each source file;
+these are located in <tt>bin/deps/</tt>. For example, the rules
+generated for <tt>drivers/net/rtl8139.c</tt> can be found in
+<tt>bin/deps/drivers/net/rtl8139.c.d</tt>. These rules allow you to
+type, for example
+
+@code
+
+ make bin/rtl8139.o
+
+@endcode
+
+and have <tt>rtl8139.o</tt> be built from
+<tt>drivers/net/rtl8139.c</tt> using the generic rule #RULE_c for
+compiling @c .c files.
+
+You can see the full list of object files that will be built using
+
+@code
+
+ make bobjs
+
+@endcode
+
+@subsection comp_ar After compilation
+
+Once all objects have been compiled, they will be collected into a
+build library ("blib") in <tt>bin/blib.a</tt>.
+
+@subsection comp_custom Customising compilation
+
+The Makefile rules for a particular object can be customised to a
+certain extent by defining the Makefile variable CFLAGS_@<object@>.
+For example, if you were to set
+
+@code
+
+ CFLAGS_rtl8139 = -DFOO
+
+@endcode
+
+then <tt>bin/rtl8139.o</tt> would be compiled with the additional
+flags <tt>-DFOO</tt>. To see the flags that will be used when
+compiling a particular object, you can use e.g.
+
+@code
+
+ make bin/rtl8139.flags
+
+@endcode
+
+If you need more flexibility than the CFLAGS_@<object@> mechanism
+provides, then you can exclude source files from the automatic rule
+generation process by listing them in the Makefile variable
+#NON_AUTO_SRCS. The command
+
+@code
+
+ make autosrcs
+
+@endcode
+
+will show you which files are currently part of the automatic rule
+generation process.
+
+@subsection comp_multiobj Multiple objects
+
+A single source file can be used to generate multiple object files.
+This is used, for example, to generate the decompressing and the
+non-decompressing prefixes from the same source files.
+
+By default, a single object will be built from each source file. To
+override the list of objects for a source file, you can define the
+Makefile variable OBJS_@<object@>. For example, the
+<tt>arch/i386/prefix/dskprefix.S</tt> source file is built into two
+objects, <tt>bin/dskprefix.o</tt> and <tt>zdskprefix.o</tt> by
+defining the Makefile variable
+
+@code
+
+ OBJS_dskprefix = dskprefix zdskprefix
+
+@endcode
+
+Since there would be little point in building two identical objects,
+customised compilation flags (see @ref comp_custom) are defined as
+
+@code
+
+ CFLAGS_zdskprefix = -DCOMPRESS
+
+@endcode
+
+Thus, <tt>arch/i386/prefix/dskprefix.S</tt> is built into @c
+dskprefix.o using the normal set of flags, and into @c zdskprefix.o
+using the normal set of flags plus <tt>-DCOMPRESS</tt>.
+
+@subsection comp_debug Special debugging targets
+
+In addition to the basic rules #RULE_c and #RULE_S for compiling
+source files into object files, there are various other rules that can
+be useful for debugging.
+
+@subsubsection comp_debug_c_to_c Preprocessed C
+
+You can see the results of preprocessing a @c .c file (including the
+per-object flags defined via CFLAGS_@<object@> if applicable) using
+e.g.
+
+@code
+
+ make bin/rtl8139.c
+
+@endcode
+
+and examining the resulting file (<tt>bin/rtl8139.c</tt> in this
+case).
+
+@subsubsection comp_debug_x_to_s Assembler
+
+You can see the results of assembling a @c .c file, or of
+preprocessing a @c .S file, using e.g.
+
+@code
+
+ make bin/rtl8139.s
+ make bin/zdskprefix.s
+
+@endcode
+
+@subsubsection comp_debug_dbg Debugging-enabled targets
+
+You can build targets with debug messages (DBG()) enabled using e.g.
+
+@code
+
+ make bin/rtl8139.dbg.o
+ make bin/rtl8139.dbg2.o
+
+@endcode
+
+You will probably not need to use these targets directly, since a
+mechanism exists to select debugging levels at build time; see @ref
+debug.
+
+@section linking Linking
+
+@subsection link_overview Overview
+
+Etherboot is designed to be small and extremely customisable. This is
+achieved by linking in only the features that are really wanted in any
+particular build.
+
+There are two places from which the list of desired features is
+obtained:
+
+ -# @ref link_config_h
+ -# @ref link_cmdline
+
+@subsection link_config_h config.h
+
+The config.h file is used to define global build options that are
+likely to apply to all images that you build, such as the console
+types, supported download protocols etc. See the documentation for
+config.h for more details.
+
+@subsection link_cmdline The make command line
+
+When you type a command such as
+
+@code
+
+ make bin/dfe538.zrom
+
+@endcode
+
+it is used to derive the following information:
+
+ - We are building a compressed ROM image
+ - The DFE538 is a PCI NIC, so we need the decompressing PCI ROM prefix
+ - The PCI IDs for the DFE538 are 1186:1300
+ - The DFE538 is an rtl8139-based card, therefore we need the rtl8139 driver
+
+You can see this process in action using the command
+
+@code
+
+ make bin/dfe538.zrom.info
+
+@endcode
+
+which will print
+
+@code
+
+ Elements : dfe538
+ Prefix : zrom
+ Drivers : rtl8139
+ ROM name : dfe538
+ Media : rom
+
+ ROM type : pci
+ PCI vendor : 0x1186
+ PCI device : 0x1300
+
+ LD driver symbols : obj_rtl8139
+ LD prefix symbols : obj_zpciprefix
+ LD ID symbols : pci_vendor_id=0x1186 pci_device_id=0x1300
+
+ LD target flags : -u obj_zpciprefix --defsym check_obj_zpciprefix=obj_zpciprefix -u obj_rtl8139 --defsym check_obj_rtl8139=obj_rtl8139 -u obj_config --defsym check_obj_config=obj_config --defsym pci_vendor_id=0x1186 --defsym pci_device_id=0x1300
+
+@endcode
+
+This should be interpreted as follows:
+
+@code
+
+ Elements : dfe538
+ Prefix : zrom
+
+@endcode
+
+"Elements" is the list of components preceding the first dot in the
+target name. "Prefix" is the component following the first dot in the
+target name. (It's called a "prefix" because the code that makes it a
+@c .zrom (rather than a @c .dsk, @c .zpxe or any other type of target)
+usually ends up at the start of the resulting binary image.)
+
+@code
+
+ Drivers : rtl8139
+
+@endcode
+
+"Drivers" is the list of drivers corresponding to the "Elements".
+Most drivers support several network cards. The PCI_ROM() and
+ISA_ROM() macros are used in the driver source files to list the cards
+that a particular driver can support.
+
+@code
+
+ ROM name : dfe538
+
+@endcode
+
+"ROM name" is the first element in the "Elements" list. It is used to
+select the PCI IDs for a PCI ROM.
+
+@code
+
+ Media : rom
+
+@endcode
+
+"Media" is the "Prefix" minus the leading @c z, if any.
+
+@code
+
+ ROM type : pci
+ PCI vendor : 0x1186
+ PCI device : 0x1300
+
+@endcode
+
+These are derived from the "ROM name" and the PCI_ROM() or ISA_ROM()
+macros in the driver source files.
+
+@code
+
+ LD driver symbols : obj_rtl8139
+ LD prefix symbols : obj_zpciprefix
+
+@endcode
+
+This is the interesting part. At this point, we have established that
+we need the rtl8139 driver (i.e. @c rtl8139.o) and the decompressing
+PCI prefix (i.e. @c zpciprefix.o). Our build system (via the
+compiler.h header file) arranges that every object exports a symbol
+obj_@<object@>; this can be seen by e.g.
+
+@code
+
+ objdump -t bin/rtl8139.o
+
+@endcode
+
+which will show the line
+
+@code
+
+ 00000000 g *ABS* 00000000 obj_rtl8139
+
+@endcode
+
+By instructing the linker that we need the symbols @c obj_rtl8139 and
+@c obj_zpciprefix, we can therefore ensure that these two objects are
+included in our build. (The linker will also include any objects that
+these two objects require, since that's the whole purpose of the
+linker.)
+
+In a similar way, we always instruct the linker that we need the
+symbol @c obj_config, in order to include the object @c config.o. @c
+config.o is used to drag in the objects that were specified via
+config.h; see @ref link_config_h.
+
+@code
+
+ LD target flags : -u obj_zpciprefix --defsym check_obj_zpciprefix=obj_zpciprefix -u obj_rtl8139 --defsym check_obj_rtl8139=obj_rtl8139 -u obj_config --defsym check_obj_config=obj_config --defsym pci_vendor_id=0x1186 --defsym pci_device_id=0x1300
+
+@endcode
+
+These are the flags that we pass to the linker in order to include the
+objects that we want in our build, and to check that they really get
+included. (This latter check is needed to work around what seems to
+be a bug in @c ld).
+
+The linker does its job of linking all the required objects together
+into a coherent build. The best way to see what is happening is to
+look at one of the resulting linker maps; try, for example
+
+@code
+
+ make bin/dfe538.dsk.map
+
+@endcode
+
+The linker map includes, amongst others:
+
+ - A list of which objects are included in the build, and why.
+ - The results of processing the linker script, line-by-line.
+ - A complete symbol table of the resulting build.
+
+It is worth spending time examining the linker map to see how an
+Etherboot image is assembled.
+
+Whatever format is selected, the Etherboot image is built into an ELF
+file, simply because this is the default format used by @c ld.
+
+@section finalisation Finalisation
+
+@subsection final_overview Overview
+
+The ELF file resulting from @ref linking "linking" needs to be
+converted into the final binary image. Usually, this is just a case
+of running
+
+@code
+
+ objcopy -O binary <elf file> <output file>
+
+@endcode
+
+to convert the ELF file into a raw binary image. Certain image
+formats require special additional treatment.
+
+@subsection final_rom ROM images
+
+ROM images must be rounded up to a suitable ROM size (e.g. 16kB or
+32kB), and certain header information such as checksums needs to be
+filled in. This is done by the @c makerom.pl program.
+
+@section debug Debugging-enabled builds
+
+*/
diff --git a/gpxe/src/doc/pxe_extensions b/gpxe/src/doc/pxe_extensions
new file mode 100644
index 00000000..92269cfc
--- /dev/null
+++ b/gpxe/src/doc/pxe_extensions
@@ -0,0 +1,279 @@
+FILE OPEN
+
+Op-Code: PXENV_FILE_OPEN (00e0h)
+
+Input: Far pointer to a t_PXENV_FILE_OPEN parameter structure
+ that has been initialised by the caller.
+
+Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be
+ returned in AX. The status field in the parameter
+ structure must be set to one of the values represented
+ by the PXENV_STATUS_xxx constants.
+
+Description: Opens a file specified by a URL for reading. Multiple
+ files may be opened and used concurrently.
+
+
+typedef struct s_PXENV_FILE_OPEN {
+ PXENV_STATUS Status;
+ UINT16 FileHandle;
+ SEGOFF16 FileName;
+ UINT32 Reserved;
+} t_PXENV_FILE_OPEN;
+
+
+Set before calling API service:
+
+FileName: URL of file to be opened. Null terminated.
+
+Reserved: Must be zero.
+
+
+Returned from API service:
+
+FileHandle: Handle for use in subsequent PXE FILE API calls.
+
+Status: See PXENV_STATUS_xxx constants.
+
+
+
+
+FILE CLOSE
+
+Op-Code: PXENV_FILE_CLOSE (00e1h)
+
+Input: Far pointer to a t_PXENV_FILE_CLOSE parameter structure
+ that has been initialised by the caller.
+
+Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be
+ returned in AX. The status field in the parameter
+ structure must be set to one of the values represented
+ by the PXENV_STATUS_xxx constants.
+
+Description: Closes a previously opened file.
+
+
+typedef struct s_PXENV_FILE_CLOSE {
+ PXENV_STATUS Status;
+ UINT16 FileHandle;
+} t_PXENV_FILE_CLOSE;
+
+
+Set before calling API service:
+
+FileHandle: Handle obtained when file was opened.
+
+
+Returned from API service:
+
+Status: See PXENV_STATUS_xxx constants.
+
+
+
+
+FILE SELECT
+
+Op-Code: PXENV_FILE_SELECT (00e2h)
+
+Input: Far pointer to a t_PXENV_FILE_SELECT parameter structure
+ that has been initialised by the caller.
+
+Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be
+ returned in AX. The status field in the parameter
+ structure must be set to one of the values represented
+ by the PXENV_STATUS_xxx constants.
+
+Description: Check a previously opened file's readiness for I/O.
+
+
+typedef struct s_PXENV_FILE_SELECT {
+ PXENV_STATUS Status;
+ UINT16 FileHandle;
+ UINT16 Ready;
+#define RDY_READ 0x0001
+} t_PXENV_FILE_SELECT;
+
+
+Set before calling API service:
+
+FileHandle: Handle obtained when file was opened.
+
+
+Returned from API service:
+
+Ready: Indication of readiness. This can be zero, or more,
+ of the RDY_xxx constants. Multiple values are
+ arithmetically or-ed together.
+
+Status: See PXENV_STATUS_xxx constants.
+
+
+
+
+FILE READ
+
+Op-Code: PXENV_FILE_READ (00e3h)
+
+Input: Far pointer to a t_PXENV_FILE_READ parameter structure
+ that has been initialised by the caller.
+
+Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be
+ returned in AX. The status field in the parameter
+ structure must be set to one of the values represented
+ by the PXENV_STATUS_xxx constants.
+
+ This API function is non-blocking. PXENV_EXIT_SUCCESS
+ and PXENV_STATUS_SUCCESS is returned if a data block
+ has been transferred into the caller's buffer.
+ PXENV_EXIT_FAILURE and PXENV_STATUS_TFTP_OPEN is
+ returned if no data is available to transfer; any
+ other status code reflects an error.
+
+Description: Read from a previously opened file.
+
+
+typedef struct s_PXENV_FILE_READ {
+ PXENV_STATUS Status;
+ UINT16 FileHandle;
+ UINT16 BufferSize;
+ SEGOFF16 Buffer;
+} t_PXENV_FILE_READ;
+
+
+Set before calling API service:
+
+FileHandle: Handle obtained when file was opened.
+
+BufferSize: Maximum number of data bytes that can be copied into
+ Buffer.
+
+Buffer: Segment:Offset address of data buffer.
+
+
+Returned from API service:
+
+BufferSize: Number of bytes written to the data buffer. End of
+ file if this is zero.
+
+Status: See PXENV_STATUS_xxx constants.
+
+
+
+
+GET FILE SIZE
+
+Op-Code: PXENV_GET_FILE_SIZE (00e4h)
+
+Input: Far pointer to a t_PXENV_GET_FILE_SIZE parameter
+ structure that has been initialised by the caller.
+
+Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be
+ returned in AX. The status field in the parameter
+ structure must be set to one of the values represented
+ by the PXENV_STATUS_xxx constants.
+
+Description: Determine size of a previously opened file.
+
+
+typedef struct s_PXENV_GET_FILE_SIZE {
+ PXENV_STATUS Status;
+ UINT16 FileHandle;
+ UINT32 FileSize;
+} t_PXENV_GET_FILE_SIZE;
+
+
+Set before calling API service:
+
+FileHandle: Handle obtained when file was opened.
+
+
+Returned from API service:
+
+FileSize: Size of the file in bytes.
+
+Status: See PXENV_STATUS_xxx constants.
+
+
+
+
+FILE EXEC
+
+Op-Code: PXENV_FILE_EXEC (00e5h)
+
+Input: Far pointer to a t_PXENV_FILE_EXEC parameter
+ structure that has been initialized by the caller.
+
+Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be
+ returned in AX. The Status field in the parameter
+ structure must be set to one of the values represented
+ by the PXENV_STATUS_xxx constants.
+
+Description: Execute a gPXE command.
+
+typedef struct s_PXENV_FILE_EXEC {
+ PXENV_STATUS_t Status;
+ SEGOFF16_t Command;
+} t_PXENV_FILE_EXEC;
+
+
+Set before calling API service:
+
+Command: Command to execute. Null terminated.
+
+
+Returned from API service:
+
+Status: See PXENV_STATUS_xxx constants.
+
+
+
+
+FILE API CHECK
+
+Op-Code: PXENV_FILE_API_CHECK (00e6h)
+
+Input: Far pointer to a t_PXENV_FILE_CHECK_API parameter
+ structure that has been initialized by the caller.
+
+ On entry, the Magic field should contain the number
+ 0x91d447b2 or the call will fail.
+
+Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be
+ returned in AX. The Status field in the parameter
+ structure must be set to one of the values represented
+ by the PXENV_STATUS_xxx constants.
+
+ If this API is present and the Magic field contains the
+ proper value on entry, AX will contain PXENV_EXIT_SUCCESS,
+ the Status field PXENV_STATUS_SUCCESS, and the Magic field
+ the number 0xe9c17b20. Any other combination should be
+ considered a failure.
+
+Description: Detect presence of this API.
+
+
+typedef struct s_PXENV_FILE_CHECK_API {
+ PXENV_STATUS Status;
+ UINT16 Size;
+ UINT32 Magic;
+ UINT32 Provider;
+ UINT32 APIMask;
+ UINT32 Flags;
+} t_PXENV_FILE_CHECK_API;
+
+Set before calling API service:
+
+Size: Set to sizeof(t_PXENV_FILE_CHECK_API) (20).
+Magic: Set to 0x91d447b2.
+
+
+Returned from API service:
+
+Size: Set to the number of bytes filled in (20).
+Magic: Set to 0xe9c17b20.
+Provider: Set to 0x45585067 ("gPXE"). Another implementation of this
+ API can use another value, e.g. to indicate a different
+ command set supported by FILE EXEC.
+APIMask: Bitmask of supported API functions (one bit for each function
+ in the range 00e0h to 00ffh).
+Flags: Set to zero, reserved for future use.
diff --git a/gpxe/src/doxygen.cfg b/gpxe/src/doxygen.cfg
new file mode 100644
index 00000000..5c429094
--- /dev/null
+++ b/gpxe/src/doxygen.cfg
@@ -0,0 +1,1245 @@
+# Doxyfile 1.4.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = Etherboot
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = @BIN@/doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish,
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese,
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish,
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES = v=@param \
+ ret=@retval \
+ err=@exception
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
+# only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is YES.
+
+SHOW_DIRECTORIES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the progam writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT =
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @SRCDIRS@ \
+ include \
+ include/gpxe \
+ arch/@ARCH@/include \
+ doc
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+
+FILE_PATTERNS = *.c \
+ *.h \
+ *.S \
+ *.dox
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT =
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION =
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT =
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = YES
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = YES
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT =
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT =
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION =
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH = include \
+ arch/@ARCH@/include
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = DOXYGEN=1
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED = __attribute__ \
+ PACKED \
+ __unused \
+ __used \
+ __aligned \
+ __table \
+ __table_start \
+ __table_end
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that a graph may be further truncated if the graph's
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default),
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/gpxe/src/drivers/bitbash/bitbash.c b/gpxe/src/drivers/bitbash/bitbash.c
new file mode 100644
index 00000000..c6f93520
--- /dev/null
+++ b/gpxe/src/drivers/bitbash/bitbash.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/bitbash.h>
+
+/** @file
+ *
+ * Bit-bashing interfaces
+ *
+ */
+
+/**
+ * Set/clear output bit
+ *
+ * @v basher Bit-bashing interface
+ * @v bit_id Bit number
+ * @v data Value to write
+ *
+ * If @c data is 0, a logic 0 will be written. If @c data is
+ * non-zero, a logic 1 will be written.
+ */
+void write_bit ( struct bit_basher *basher, unsigned int bit_id,
+ unsigned long data ) {
+ basher->op->write ( basher, bit_id, ( data ? -1UL : 0 ) );
+}
+
+/**
+ * Read input bit
+ *
+ * @v basher Bit-bashing interface
+ * @v bit_id Bit number
+ * @ret data Value read
+ *
+ * @c data will always be either 0 or -1UL. The idea is that the
+ * caller can simply binary-AND the returned value with whatever mask
+ * it needs to apply.
+ */
+int read_bit ( struct bit_basher *basher, unsigned int bit_id ) {
+ return ( basher->op->read ( basher, bit_id ) ? -1UL : 0 );
+}
diff --git a/gpxe/src/drivers/bitbash/i2c_bit.c b/gpxe/src/drivers/bitbash/i2c_bit.c
new file mode 100644
index 00000000..a3af610b
--- /dev/null
+++ b/gpxe/src/drivers/bitbash/i2c_bit.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <gpxe/bitbash.h>
+#include <gpxe/i2c.h>
+
+/** @file
+ *
+ * I2C bit-bashing interface
+ *
+ * This implements a simple I2C master via a bit-bashing interface
+ * that provides two lines: SCL (clock) and SDA (data).
+ */
+
+/**
+ * Delay between output state changes
+ *
+ * Max rated i2c speed (for the basic i2c protocol) is 100kbps,
+ * i.e. 200k clock transitions per second.
+ */
+static void i2c_delay ( void ) {
+ udelay ( I2C_UDELAY );
+}
+
+/**
+ * Set state of I2C SCL line
+ *
+ * @v basher Bit-bashing interface
+ * @v state New state of SCL
+ */
+static void setscl ( struct bit_basher *basher, int state ) {
+ write_bit ( basher, I2C_BIT_SCL, state );
+ i2c_delay();
+}
+
+/**
+ * Set state of I2C SDA line
+ *
+ * @v basher Bit-bashing interface
+ * @v state New state of SDA
+ */
+static void setsda ( struct bit_basher *basher, int state ) {
+ write_bit ( basher, I2C_BIT_SDA, state );
+ i2c_delay();
+}
+
+/**
+ * Get state of I2C SDA line
+ *
+ * @v basher Bit-bashing interface
+ * @ret state State of SDA
+ */
+static int getsda ( struct bit_basher *basher ) {
+ return read_bit ( basher, I2C_BIT_SDA );
+}
+
+/**
+ * Send an I2C start condition
+ *
+ * @v basher Bit-bashing interface
+ */
+static void i2c_start ( struct bit_basher *basher ) {
+ setscl ( basher, 1 );
+ setsda ( basher, 0 );
+ setscl ( basher, 0 );
+ setsda ( basher, 1 );
+}
+
+/**
+ * Send an I2C data bit
+ *
+ * @v basher Bit-bashing interface
+ * @v bit Bit to send
+ */
+static void i2c_send_bit ( struct bit_basher *basher, int bit ) {
+ setsda ( basher, bit );
+ setscl ( basher, 1 );
+ setscl ( basher, 0 );
+ setsda ( basher, 1 );
+}
+
+/**
+ * Receive an I2C data bit
+ *
+ * @v basher Bit-bashing interface
+ * @ret bit Received bit
+ */
+static int i2c_recv_bit ( struct bit_basher *basher ) {
+ int bit;
+
+ setscl ( basher, 1 );
+ bit = getsda ( basher );
+ setscl ( basher, 0 );
+ return bit;
+}
+
+/**
+ * Send an I2C stop condition
+ *
+ * @v basher Bit-bashing interface
+ */
+static void i2c_stop ( struct bit_basher *basher ) {
+ setsda ( basher, 0 );
+ setscl ( basher, 1 );
+ setsda ( basher, 1 );
+}
+
+/**
+ * Send byte via I2C bus and check for acknowledgement
+ *
+ * @v basher Bit-bashing interface
+ * @v byte Byte to send
+ * @ret rc Return status code
+ *
+ * Sends a byte via the I2C bus and checks for an acknowledgement from
+ * the slave device.
+ */
+static int i2c_send_byte ( struct bit_basher *basher, uint8_t byte ) {
+ int i;
+
+ /* Send byte */
+ for ( i = 8 ; i ; i-- ) {
+ i2c_send_bit ( basher, byte & 0x80 );
+ byte <<= 1;
+ }
+
+ /* Check for acknowledgement from slave */
+ return ( i2c_recv_bit ( basher ) == 0 ? 0 : -EIO );
+}
+
+/**
+ * Receive byte via I2C bus
+ *
+ * @v basher Bit-bashing interface
+ * @ret byte Received byte
+ *
+ * Receives a byte via the I2C bus and sends NACK to the slave device.
+ */
+static uint8_t i2c_recv_byte ( struct bit_basher *basher ) {
+ uint8_t value = 0;
+ int i;
+
+ /* Receive byte */
+ for ( i = 8 ; i ; i-- ) {
+ value <<= 1;
+ value |= ( i2c_recv_bit ( basher ) & 0x1 );
+ }
+
+ /* Send NACK */
+ i2c_send_bit ( basher, 1 );
+
+ return value;
+}
+
+/**
+ * Select I2C device for reading or writing
+ *
+ * @v basher Bit-bashing interface
+ * @v i2cdev I2C device
+ * @v direction I2C_READ or I2C_WRITE
+ * @ret rc Return status code
+ */
+static int i2c_select ( struct bit_basher *basher, struct i2c_device *i2cdev,
+ unsigned int direction ) {
+ unsigned int address;
+ int rc;
+
+ i2c_start ( basher );
+
+ /* First byte of the address */
+ address = i2cdev->address;
+ if ( i2cdev->tenbit ) {
+ address |= I2C_TENBIT_ADDRESS;
+ address >>= 8;
+ }
+ if ( ( rc = i2c_send_byte ( basher,
+ ( ( address << 1 ) | direction ) ) ) != 0 )
+ return rc;
+
+ /* Second byte of the address (10-bit addresses only) */
+ if ( i2cdev->tenbit ) {
+ if ( ( rc = i2c_send_byte ( basher,
+ ( i2cdev->address & 0xff ) ) ) !=0)
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Read data from I2C device via bit-bashing interface
+ *
+ * @v i2c I2C interface
+ * @v i2cdev I2C device
+ * @v offset Starting offset within the device
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ *
+ * Note that attempting to read zero bytes of data is a valid way to
+ * check for I2C device presence.
+ */
+static int i2c_bit_read ( struct i2c_interface *i2c,
+ struct i2c_device *i2cdev, unsigned int offset,
+ uint8_t *data, unsigned int len ) {
+ struct i2c_bit_basher *i2cbit
+ = container_of ( i2c, struct i2c_bit_basher, i2c );
+ struct bit_basher *basher = &i2cbit->basher;
+ int rc = 0;
+
+ DBG ( "Reading from I2C device %x: ", i2cdev->address );
+
+ while ( 1 ) {
+
+ /* Select device for writing */
+ if ( ( rc = i2c_select ( basher, i2cdev, I2C_WRITE ) ) != 0 )
+ break;
+
+ /* Abort at end of data */
+ if ( ! ( len-- ) )
+ break;
+
+ /* Select offset */
+ if ( ( rc = i2c_send_byte ( basher, offset++ ) ) != 0 )
+ break;
+
+ /* Select device for reading */
+ if ( ( rc = i2c_select ( basher, i2cdev, I2C_READ ) ) != 0 )
+ break;
+
+ /* Read byte */
+ *data++ = i2c_recv_byte ( basher );
+ DBG ( "%02x ", *(data - 1) );
+ }
+
+ DBG ( "%s\n", ( rc ? "failed" : "" ) );
+ i2c_stop ( basher );
+ return rc;
+}
+
+/**
+ * Write data to I2C device via bit-bashing interface
+ *
+ * @v i2c I2C interface
+ * @v i2cdev I2C device
+ * @v offset Starting offset within the device
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ *
+ * Note that attempting to write zero bytes of data is a valid way to
+ * check for I2C device presence.
+ */
+static int i2c_bit_write ( struct i2c_interface *i2c,
+ struct i2c_device *i2cdev, unsigned int offset,
+ const uint8_t *data, unsigned int len ) {
+ struct i2c_bit_basher *i2cbit
+ = container_of ( i2c, struct i2c_bit_basher, i2c );
+ struct bit_basher *basher = &i2cbit->basher;
+ int rc = 0;
+
+ DBG ( "Writing to I2C device %x: ", i2cdev->address );
+
+ while ( 1 ) {
+
+ /* Select device for writing */
+ if ( ( rc = i2c_select ( basher, i2cdev, I2C_WRITE ) ) != 0 )
+ break;
+
+ /* Abort at end of data */
+ if ( ! ( len-- ) )
+ break;
+
+ /* Select offset */
+ if ( ( rc = i2c_send_byte ( basher, offset++ ) ) != 0 )
+ break;
+
+ /* Write data to device */
+ DBG ( "%02x ", *data );
+ if ( ( rc = i2c_send_byte ( basher, *data++ ) ) != 0 )
+ break;
+ }
+
+ DBG ( "%s\n", ( rc ? "failed" : "" ) );
+ i2c_stop ( basher );
+ return rc;
+}
+
+/**
+ * Initialise I2C bit-bashing interface
+ *
+ * @v i2cbit I2C bit-bashing interface
+ */
+void init_i2c_bit_basher ( struct i2c_bit_basher *i2cbit ) {
+ struct bit_basher *basher = &i2cbit->basher;
+
+ assert ( basher->op->read != NULL );
+ assert ( basher->op->write != NULL );
+ i2cbit->i2c.read = i2c_bit_read;
+ i2cbit->i2c.write = i2c_bit_write;
+ i2c_stop ( basher );
+}
diff --git a/gpxe/src/drivers/bitbash/spi_bit.c b/gpxe/src/drivers/bitbash/spi_bit.c
new file mode 100644
index 00000000..ef87b5a2
--- /dev/null
+++ b/gpxe/src/drivers/bitbash/spi_bit.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <gpxe/bitbash.h>
+#include <gpxe/spi_bit.h>
+
+/** @file
+ *
+ * SPI bit-bashing interface
+ *
+ */
+
+/** Delay between SCLK changes and around SS changes */
+static void spi_bit_delay ( void ) {
+ udelay ( SPI_BIT_UDELAY );
+}
+
+/** Chip select line will be asserted */
+#define SELECT_SLAVE 0
+
+/** Chip select line will be deasserted */
+#define DESELECT_SLAVE SPI_MODE_SSPOL
+
+/**
+ * Select/deselect slave
+ *
+ * @v spibit SPI bit-bashing interface
+ * @v slave Slave number
+ * @v state Slave select state
+ *
+ * @c state must be @c SELECT_SLAVE or @c DESELECT_SLAVE.
+ */
+static void spi_bit_set_slave_select ( struct spi_bit_basher *spibit,
+ unsigned int slave,
+ unsigned int state ) {
+ struct bit_basher *basher = &spibit->basher;
+
+ state ^= ( spibit->bus.mode & SPI_MODE_SSPOL );
+ DBG ( "Setting slave %d select %s\n", slave,
+ ( state ? "high" : "low" ) );
+
+ spi_bit_delay();
+ write_bit ( basher, SPI_BIT_SS ( slave ), state );
+ spi_bit_delay();
+}
+
+/**
+ * Transfer bits over SPI bit-bashing bus
+ *
+ * @v bus SPI bus
+ * @v data_out TX data buffer (or NULL)
+ * @v data_in RX data buffer (or NULL)
+ * @v len Length of transfer (in @b bits)
+ * @v endianness Endianness of this data transfer
+ *
+ * This issues @c len clock cycles on the SPI bus, shifting out data
+ * from the @c data_out buffer to the MOSI line and shifting in data
+ * from the MISO line to the @c data_in buffer. If @c data_out is
+ * NULL, then the data sent will be all zeroes. If @c data_in is
+ * NULL, then the incoming data will be discarded.
+ */
+static void spi_bit_transfer ( struct spi_bit_basher *spibit,
+ const void *data_out, void *data_in,
+ unsigned int len, int endianness ) {
+ struct spi_bus *bus = &spibit->bus;
+ struct bit_basher *basher = &spibit->basher;
+ unsigned int sclk = ( ( bus->mode & SPI_MODE_CPOL ) ? 1 : 0 );
+ unsigned int cpha = ( ( bus->mode & SPI_MODE_CPHA ) ? 1 : 0 );
+ unsigned int bit_offset;
+ unsigned int byte_offset;
+ unsigned int byte_mask;
+ unsigned int bit;
+ unsigned int step;
+
+ DBG ( "Transferring %d bits in mode %x\n", len, bus->mode );
+
+ for ( step = 0 ; step < ( len * 2 ) ; step++ ) {
+ /* Calculate byte offset and byte mask */
+ bit_offset = ( ( endianness == SPI_BIT_BIG_ENDIAN ) ?
+ ( len - ( step / 2 ) - 1 ) : ( step / 2 ) );
+ byte_offset = ( bit_offset / 8 );
+ byte_mask = ( 1 << ( bit_offset % 8 ) );
+
+ /* Shift data in or out */
+ if ( sclk == cpha ) {
+ const uint8_t *byte;
+
+ /* Shift data out */
+ if ( data_out ) {
+ byte = ( data_out + byte_offset );
+ bit = ( *byte & byte_mask );
+ } else {
+ bit = 0;
+ }
+ write_bit ( basher, SPI_BIT_MOSI, bit );
+ } else {
+ uint8_t *byte;
+
+ /* Shift data in */
+ bit = read_bit ( basher, SPI_BIT_MISO );
+ if ( data_in ) {
+ byte = ( data_in + byte_offset );
+ *byte &= ~byte_mask;
+ *byte |= ( bit & byte_mask );
+ }
+ }
+
+ /* Toggle clock line */
+ spi_bit_delay();
+ sclk = ~sclk;
+ write_bit ( basher, SPI_BIT_SCLK, sclk );
+ }
+}
+
+/**
+ * Read/write data via SPI bit-bashing bus
+ *
+ * @v bus SPI bus
+ * @v device SPI device
+ * @v command Command
+ * @v address Address to read/write (<0 for no address)
+ * @v data_out TX data buffer (or NULL)
+ * @v data_in RX data buffer (or NULL)
+ * @v len Length of transfer
+ * @ret rc Return status code
+ */
+static int spi_bit_rw ( struct spi_bus *bus, struct spi_device *device,
+ unsigned int command, int address,
+ const void *data_out, void *data_in, size_t len ) {
+ struct spi_bit_basher *spibit
+ = container_of ( bus, struct spi_bit_basher, bus );
+ uint32_t tmp;
+
+ /* Set clock line to idle state */
+ write_bit ( &spibit->basher, SPI_BIT_SCLK,
+ ( bus->mode & SPI_MODE_CPOL ) );
+
+ /* Assert chip select on specified slave */
+ spi_bit_set_slave_select ( spibit, device->slave, SELECT_SLAVE );
+
+ /* Transmit command */
+ assert ( device->command_len <= ( 8 * sizeof ( tmp ) ) );
+ tmp = cpu_to_le32 ( command );
+ spi_bit_transfer ( spibit, &tmp, NULL, device->command_len,
+ SPI_BIT_BIG_ENDIAN );
+
+ /* Transmit address, if present */
+ if ( address >= 0 ) {
+ assert ( device->address_len <= ( 8 * sizeof ( tmp ) ) );
+ tmp = cpu_to_le32 ( address );
+ spi_bit_transfer ( spibit, &tmp, NULL, device->address_len,
+ SPI_BIT_BIG_ENDIAN );
+ }
+
+ /* Transmit/receive data */
+ spi_bit_transfer ( spibit, data_out, data_in, ( len * 8 ),
+ spibit->endianness );
+
+ /* Deassert chip select on specified slave */
+ spi_bit_set_slave_select ( spibit, device->slave, DESELECT_SLAVE );
+
+ return 0;
+}
+
+/**
+ * Initialise SPI bit-bashing interface
+ *
+ * @v spibit SPI bit-bashing interface
+ */
+void init_spi_bit_basher ( struct spi_bit_basher *spibit ) {
+ assert ( &spibit->basher.op->read != NULL );
+ assert ( &spibit->basher.op->write != NULL );
+ spibit->bus.rw = spi_bit_rw;
+}
diff --git a/gpxe/src/drivers/block/ata.c b/gpxe/src/drivers/block/ata.c
new file mode 100644
index 00000000..555a5f6e
--- /dev/null
+++ b/gpxe/src/drivers/block/ata.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <gpxe/blockdev.h>
+#include <gpxe/ata.h>
+
+/** @file
+ *
+ * ATA block device
+ *
+ */
+
+static inline __attribute__ (( always_inline )) struct ata_device *
+block_to_ata ( struct block_device *blockdev ) {
+ return container_of ( blockdev, struct ata_device, blockdev );
+}
+
+/**
+ * Issue ATA command
+ *
+ * @v ata ATA device
+ * @v command ATA command
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+ata_command ( struct ata_device *ata, struct ata_command *command ) {
+ DBG ( "ATA cmd %02x dev %02x LBA%s %llx count %04x\n",
+ command->cb.cmd_stat, command->cb.device,
+ ( command->cb.lba48 ? "48" : "" ),
+ ( unsigned long long ) command->cb.lba.native,
+ command->cb.count.native );
+
+ return ata->command ( ata, command );
+}
+
+/**
+ * Read block from ATA device
+ *
+ * @v blockdev Block device
+ * @v block LBA block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int ata_read ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer ) {
+ struct ata_device *ata = block_to_ata ( blockdev );
+ struct ata_command command;
+
+ memset ( &command, 0, sizeof ( command ) );
+ command.cb.lba.native = block;
+ command.cb.count.native = count;
+ command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
+ command.cb.lba48 = ata->lba48;
+ if ( ! ata->lba48 )
+ command.cb.device |= command.cb.lba.bytes.low_prev;
+ command.cb.cmd_stat = ( ata->lba48 ? ATA_CMD_READ_EXT : ATA_CMD_READ );
+ command.data_in = buffer;
+ return ata_command ( ata, &command );
+}
+
+/**
+ * Write block to ATA device
+ *
+ * @v blockdev Block device
+ * @v block LBA block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int ata_write ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer ) {
+ struct ata_device *ata = block_to_ata ( blockdev );
+ struct ata_command command;
+
+ memset ( &command, 0, sizeof ( command ) );
+ command.cb.lba.native = block;
+ command.cb.count.native = count;
+ command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
+ command.cb.lba48 = ata->lba48;
+ if ( ! ata->lba48 )
+ command.cb.device |= command.cb.lba.bytes.low_prev;
+ command.cb.cmd_stat = ( ata->lba48 ?
+ ATA_CMD_WRITE_EXT : ATA_CMD_WRITE );
+ command.data_out = buffer;
+ return ata_command ( ata, &command );
+}
+
+/**
+ * Identify ATA device
+ *
+ * @v blockdev Block device
+ * @ret rc Return status code
+ */
+static int ata_identify ( struct block_device *blockdev ) {
+ struct ata_device *ata = block_to_ata ( blockdev );
+ struct ata_command command;
+ struct ata_identity identity;
+ int rc;
+
+ /* Issue IDENTIFY */
+ memset ( &command, 0, sizeof ( command ) );
+ command.cb.count.native = 1;
+ command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
+ command.cb.cmd_stat = ATA_CMD_IDENTIFY;
+ command.data_in = virt_to_user ( &identity );
+ linker_assert ( sizeof ( identity ) == ATA_SECTOR_SIZE,
+ __ata_identity_bad_size__ );
+ if ( ( rc = ata_command ( ata, &command ) ) != 0 )
+ return rc;
+
+ /* Fill in block device parameters */
+ blockdev->blksize = ATA_SECTOR_SIZE;
+ if ( identity.supports_lba48 & cpu_to_le16 ( ATA_SUPPORTS_LBA48 ) ) {
+ ata->lba48 = 1;
+ blockdev->blocks = le64_to_cpu ( identity.lba48_sectors );
+ } else {
+ blockdev->blocks = le32_to_cpu ( identity.lba_sectors );
+ }
+ return 0;
+}
+
+/**
+ * Initialise ATA device
+ *
+ * @v ata ATA device
+ * @ret rc Return status code
+ *
+ * Initialises an ATA device. The ata_device::command field and the
+ * @c ATA_FL_SLAVE portion of the ata_device::flags field must already
+ * be filled in. This function will configure ata_device::blockdev,
+ * including issuing an IDENTIFY DEVICE call to determine the block
+ * size and total device size.
+ */
+int init_atadev ( struct ata_device *ata ) {
+ /** Fill in read and write methods, and get device capacity */
+ ata->blockdev.read = ata_read;
+ ata->blockdev.write = ata_write;
+ return ata_identify ( &ata->blockdev );
+}
diff --git a/gpxe/src/drivers/block/ramdisk.c b/gpxe/src/drivers/block/ramdisk.c
new file mode 100644
index 00000000..b5324bf1
--- /dev/null
+++ b/gpxe/src/drivers/block/ramdisk.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/blockdev.h>
+#include <gpxe/ramdisk.h>
+
+/**
+ * @file
+ *
+ * RAM disks
+ *
+ */
+
+static inline __attribute__ (( always_inline )) struct ramdisk *
+block_to_ramdisk ( struct block_device *blockdev ) {
+ return container_of ( blockdev, struct ramdisk, blockdev );
+}
+
+/**
+ * Read block
+ *
+ * @v blockdev Block device
+ * @v block Block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int ramdisk_read ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer ) {
+ struct ramdisk *ramdisk = block_to_ramdisk ( blockdev );
+ unsigned long offset = ( block * blockdev->blksize );
+ unsigned long length = ( count * blockdev->blksize );
+
+ DBGC ( ramdisk, "RAMDISK %p reading [%lx,%lx)\n",
+ ramdisk, offset, length );
+
+ memcpy_user ( buffer, 0, ramdisk->data, offset, length );
+ return 0;
+}
+
+/**
+ * Write block
+ *
+ * @v blockdev Block device
+ * @v block Block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int ramdisk_write ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer ) {
+ struct ramdisk *ramdisk = block_to_ramdisk ( blockdev );
+ unsigned long offset = ( block * blockdev->blksize );
+ unsigned long length = ( count * blockdev->blksize );
+
+ DBGC ( ramdisk, "RAMDISK %p writing [%lx,%lx)\n",
+ ramdisk, offset, length );
+
+ memcpy_user ( ramdisk->data, offset, buffer, 0, length );
+ return 0;
+}
+
+int init_ramdisk ( struct ramdisk *ramdisk, userptr_t data, size_t len,
+ unsigned int blksize ) {
+
+ if ( ! blksize )
+ blksize = 512;
+
+ ramdisk->data = data;
+ ramdisk->blockdev.read = ramdisk_read;
+ ramdisk->blockdev.write = ramdisk_write;
+ ramdisk->blockdev.blksize = blksize;
+ ramdisk->blockdev.blocks = ( len / blksize );
+
+ return 0;
+}
diff --git a/gpxe/src/drivers/block/scsi.c b/gpxe/src/drivers/block/scsi.c
new file mode 100644
index 00000000..9651583a
--- /dev/null
+++ b/gpxe/src/drivers/block/scsi.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/blockdev.h>
+#include <gpxe/scsi.h>
+
+/** @file
+ *
+ * SCSI block device
+ *
+ */
+
+static inline __attribute__ (( always_inline )) struct scsi_device *
+block_to_scsi ( struct block_device *blockdev ) {
+ return container_of ( blockdev, struct scsi_device, blockdev );
+}
+
+/**
+ * Issue SCSI command
+ *
+ * @v scsi SCSI device
+ * @v command SCSI command
+ * @ret rc Return status code
+ */
+static int scsi_command ( struct scsi_device *scsi,
+ struct scsi_command *command ) {
+ int rc;
+
+ /* Clear sense response code before issuing command */
+ command->sense_response = 0;
+
+ /* Issue SCSI command */
+ if ( ( rc = scsi->command ( scsi, command ) ) != 0 ) {
+ /* Something went wrong with the issuing mechanism,
+ * (rather than with the command itself)
+ */
+ DBG ( "SCSI %p " SCSI_CDB_FORMAT " err %d\n",
+ scsi, SCSI_CDB_DATA ( command->cdb ), rc );
+ return rc;
+ }
+
+ /* Check for SCSI errors */
+ if ( command->status != 0 ) {
+ DBG ( "SCSI %p " SCSI_CDB_FORMAT " status %02x sense %02x\n",
+ scsi, SCSI_CDB_DATA ( command->cdb ),
+ command->status, command->sense_response );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * Read block from SCSI device using READ (10)
+ *
+ * @v blockdev Block device
+ * @v block LBA block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int scsi_read_10 ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer ) {
+ struct scsi_device *scsi = block_to_scsi ( blockdev );
+ struct scsi_command command;
+ struct scsi_cdb_read_10 *cdb = &command.cdb.read10;
+
+ /* Issue READ (10) */
+ memset ( &command, 0, sizeof ( command ) );
+ cdb->opcode = SCSI_OPCODE_READ_10;
+ cdb->lba = cpu_to_be32 ( block );
+ cdb->len = cpu_to_be16 ( count );
+ command.data_in = buffer;
+ command.data_in_len = ( count * blockdev->blksize );
+ return scsi_command ( scsi, &command );
+}
+
+/**
+ * Read block from SCSI device using READ (16)
+ *
+ * @v blockdev Block device
+ * @v block LBA block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int scsi_read_16 ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer ) {
+ struct scsi_device *scsi = block_to_scsi ( blockdev );
+ struct scsi_command command;
+ struct scsi_cdb_read_16 *cdb = &command.cdb.read16;
+
+ /* Issue READ (16) */
+ memset ( &command, 0, sizeof ( command ) );
+ cdb->opcode = SCSI_OPCODE_READ_16;
+ cdb->lba = cpu_to_be64 ( block );
+ cdb->len = cpu_to_be32 ( count );
+ command.data_in = buffer;
+ command.data_in_len = ( count * blockdev->blksize );
+ return scsi_command ( scsi, &command );
+}
+
+/**
+ * Write block to SCSI device using WRITE (10)
+ *
+ * @v blockdev Block device
+ * @v block LBA block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int scsi_write_10 ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer ) {
+ struct scsi_device *scsi = block_to_scsi ( blockdev );
+ struct scsi_command command;
+ struct scsi_cdb_write_10 *cdb = &command.cdb.write10;
+
+ /* Issue WRITE (10) */
+ memset ( &command, 0, sizeof ( command ) );
+ cdb->opcode = SCSI_OPCODE_WRITE_10;
+ cdb->lba = cpu_to_be32 ( block );
+ cdb->len = cpu_to_be16 ( count );
+ command.data_out = buffer;
+ command.data_out_len = ( count * blockdev->blksize );
+ return scsi_command ( scsi, &command );
+}
+
+/**
+ * Write block to SCSI device using WRITE (16)
+ *
+ * @v blockdev Block device
+ * @v block LBA block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int scsi_write_16 ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer ) {
+ struct scsi_device *scsi = block_to_scsi ( blockdev );
+ struct scsi_command command;
+ struct scsi_cdb_write_16 *cdb = &command.cdb.write16;
+
+ /* Issue WRITE (16) */
+ memset ( &command, 0, sizeof ( command ) );
+ cdb->opcode = SCSI_OPCODE_WRITE_16;
+ cdb->lba = cpu_to_be64 ( block );
+ cdb->len = cpu_to_be32 ( count );
+ command.data_out = buffer;
+ command.data_out_len = ( count * blockdev->blksize );
+ return scsi_command ( scsi, &command );
+}
+
+/**
+ * Read capacity of SCSI device via READ CAPACITY (10)
+ *
+ * @v blockdev Block device
+ * @ret rc Return status code
+ */
+static int scsi_read_capacity_10 ( struct block_device *blockdev ) {
+ struct scsi_device *scsi = block_to_scsi ( blockdev );
+ struct scsi_command command;
+ struct scsi_cdb_read_capacity_10 *cdb = &command.cdb.readcap10;
+ struct scsi_capacity_10 capacity;
+ int rc;
+
+ /* Issue READ CAPACITY (10) */
+ memset ( &command, 0, sizeof ( command ) );
+ cdb->opcode = SCSI_OPCODE_READ_CAPACITY_10;
+ command.data_in = virt_to_user ( &capacity );
+ command.data_in_len = sizeof ( capacity );
+
+ if ( ( rc = scsi_command ( scsi, &command ) ) != 0 )
+ return rc;
+
+ /* Fill in block device fields */
+ blockdev->blksize = be32_to_cpu ( capacity.blksize );
+ blockdev->blocks = ( be32_to_cpu ( capacity.lba ) + 1 );
+
+ return 0;
+}
+
+/**
+ * Read capacity of SCSI device via READ CAPACITY (16)
+ *
+ * @v blockdev Block device
+ * @ret rc Return status code
+ */
+static int scsi_read_capacity_16 ( struct block_device *blockdev ) {
+ struct scsi_device *scsi = block_to_scsi ( blockdev );
+ struct scsi_command command;
+ struct scsi_cdb_read_capacity_16 *cdb = &command.cdb.readcap16;
+ struct scsi_capacity_16 capacity;
+ int rc;
+
+ /* Issue READ CAPACITY (16) */
+ memset ( &command, 0, sizeof ( command ) );
+ cdb->opcode = SCSI_OPCODE_SERVICE_ACTION_IN;
+ cdb->service_action = SCSI_SERVICE_ACTION_READ_CAPACITY_16;
+ cdb->len = cpu_to_be32 ( sizeof ( capacity ) );
+ command.data_in = virt_to_user ( &capacity );
+ command.data_in_len = sizeof ( capacity );
+
+ if ( ( rc = scsi_command ( scsi, &command ) ) != 0 )
+ return rc;
+
+ /* Fill in block device fields */
+ blockdev->blksize = be32_to_cpu ( capacity.blksize );
+ blockdev->blocks = ( be64_to_cpu ( capacity.lba ) + 1 );
+ return 0;
+}
+
+/**
+ * Initialise SCSI device
+ *
+ * @v scsi SCSI device
+ * @ret rc Return status code
+ *
+ * Initialises a SCSI device. The scsi_device::command and
+ * scsi_device::lun fields must already be filled in. This function
+ * will configure scsi_device::blockdev, including issuing a READ
+ * CAPACITY call to determine the block size and total device size.
+ */
+int init_scsidev ( struct scsi_device *scsi ) {
+ int rc;
+
+ /* Issue a theoretically extraneous READ CAPACITY (10)
+ * command, solely in order to draw out the "CHECK CONDITION
+ * (power-on occurred)" that some dumb targets insist on
+ * sending as an error at start of day.
+ */
+ scsi_read_capacity_10 ( &scsi->blockdev );
+
+ /* Try READ CAPACITY (10), which is a mandatory command, first. */
+ scsi->blockdev.read = scsi_read_10;
+ scsi->blockdev.write = scsi_write_10;
+ if ( ( rc = scsi_read_capacity_10 ( &scsi->blockdev ) ) != 0 )
+ return rc;
+
+ /* If capacity range was exceeded (i.e. capacity.lba was
+ * 0xffffffff, meaning that blockdev->blocks is now zero), use
+ * READ CAPACITY (16) instead. READ CAPACITY (16) is not
+ * mandatory, so we can't just use it straight off.
+ */
+ if ( scsi->blockdev.blocks == 0 ) {
+ scsi->blockdev.read = scsi_read_16;
+ scsi->blockdev.write = scsi_write_16;
+ if ( ( rc = scsi_read_capacity_16 ( &scsi->blockdev ) ) != 0 )
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/gpxe/src/drivers/bus/eisa.c b/gpxe/src/drivers/bus/eisa.c
new file mode 100644
index 00000000..ee03df3a
--- /dev/null
+++ b/gpxe/src/drivers/bus/eisa.c
@@ -0,0 +1,185 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <io.h>
+#include <unistd.h>
+#include <gpxe/eisa.h>
+
+static struct eisa_driver eisa_drivers[0]
+ __table_start ( struct eisa_driver, eisa_drivers );
+static struct eisa_driver eisa_drivers_end[0]
+ __table_end ( struct eisa_driver, eisa_drivers );
+
+static void eisabus_remove ( struct root_device *rootdev );
+
+/**
+ * Reset and enable/disable an EISA device
+ *
+ * @v eisa EISA device
+ * @v enabled 1=enable, 0=disable
+ */
+void eisa_device_enabled ( struct eisa_device *eisa, int enabled ) {
+ /* Set reset line high for 1000 µs. Spec says 500 µs, but
+ * this doesn't work for all cards, so we are conservative.
+ */
+ outb ( EISA_CMD_RESET, eisa->ioaddr + EISA_GLOBAL_CONFIG );
+ udelay ( 1000 ); /* Must wait 800 */
+
+ /* Set reset low and write a 1 to ENABLE. Delay again, in
+ * case the card takes a while to wake up.
+ */
+ outb ( enabled ? EISA_CMD_ENABLE : 0,
+ eisa->ioaddr + EISA_GLOBAL_CONFIG );
+ udelay ( 1000 ); /* Must wait 800 */
+
+ DBG ( "EISA %s device %02x\n", ( enabled ? "enabled" : "disabled" ),
+ eisa->slot );
+}
+
+/**
+ * Probe an EISA device
+ *
+ * @v eisa EISA device
+ * @ret rc Return status code
+ *
+ * Searches for a driver for the EISA device. If a driver is found,
+ * its probe() routine is called.
+ */
+static int eisa_probe ( struct eisa_device *eisa ) {
+ struct eisa_driver *driver;
+ struct eisa_device_id *id;
+ unsigned int i;
+ int rc;
+
+ DBG ( "Adding EISA device %02x (%04x:%04x (\"%s\") io %x)\n",
+ eisa->slot, eisa->vendor_id, eisa->prod_id,
+ isa_id_string ( eisa->vendor_id, eisa->prod_id ), eisa->ioaddr );
+
+ for ( driver = eisa_drivers; driver < eisa_drivers_end; driver++ ) {
+ for ( i = 0 ; i < driver->id_count ; i++ ) {
+ id = &driver->ids[i];
+ if ( id->vendor_id != eisa->vendor_id )
+ continue;
+ if ( ISA_PROD_ID ( id->prod_id ) !=
+ ISA_PROD_ID ( eisa->prod_id ) )
+ continue;
+ eisa->driver = driver;
+ eisa->driver_name = id->name;
+ DBG ( "...using driver %s\n", eisa->driver_name );
+ if ( ( rc = driver->probe ( eisa, id ) ) != 0 ) {
+ DBG ( "......probe failed\n" );
+ continue;
+ }
+ return 0;
+ }
+ }
+
+ DBG ( "...no driver found\n" );
+ return -ENOTTY;
+}
+
+/**
+ * Remove an EISA device
+ *
+ * @v eisa EISA device
+ */
+static void eisa_remove ( struct eisa_device *eisa ) {
+ eisa->driver->remove ( eisa );
+ DBG ( "Removed EISA device %02x\n", eisa->slot );
+}
+
+/**
+ * Probe EISA root bus
+ *
+ * @v rootdev EISA bus root device
+ *
+ * Scans the EISA bus for devices and registers all devices it can
+ * find.
+ */
+static int eisabus_probe ( struct root_device *rootdev ) {
+ struct eisa_device *eisa = NULL;
+ unsigned int slot;
+ int rc;
+
+ for ( slot = EISA_MIN_SLOT ; slot <= EISA_MAX_SLOT ; slot++ ) {
+ /* Allocate struct eisa_device */
+ if ( ! eisa )
+ eisa = malloc ( sizeof ( *eisa ) );
+ if ( ! eisa ) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset ( eisa, 0, sizeof ( *eisa ) );
+ eisa->slot = slot;
+ eisa->ioaddr = EISA_SLOT_BASE ( eisa->slot );
+
+ /* Test for board present */
+ outb ( 0xff, eisa->ioaddr + EISA_VENDOR_ID );
+ eisa->vendor_id =
+ le16_to_cpu ( inw ( eisa->ioaddr + EISA_VENDOR_ID ) );
+ eisa->prod_id =
+ le16_to_cpu ( inw ( eisa->ioaddr + EISA_PROD_ID ) );
+ if ( eisa->vendor_id & 0x80 ) {
+ /* No board present */
+ continue;
+ }
+
+ /* Add to device hierarchy */
+ snprintf ( eisa->dev.name, sizeof ( eisa->dev.name ),
+ "EISA%02x", slot );
+ eisa->dev.desc.bus_type = BUS_TYPE_EISA;
+ eisa->dev.desc.vendor = eisa->vendor_id;
+ eisa->dev.desc.device = eisa->prod_id;
+ eisa->dev.parent = &rootdev->dev;
+ list_add ( &eisa->dev.siblings, &rootdev->dev.children );
+ INIT_LIST_HEAD ( &eisa->dev.children );
+
+ /* Look for a driver */
+ if ( eisa_probe ( eisa ) == 0 ) {
+ /* eisadev registered, we can drop our ref */
+ eisa = NULL;
+ } else {
+ /* Not registered; re-use struct */
+ list_del ( &eisa->dev.siblings );
+ }
+ }
+
+ free ( eisa );
+ return 0;
+
+ err:
+ free ( eisa );
+ eisabus_remove ( rootdev );
+ return rc;
+}
+
+/**
+ * Remove EISA root bus
+ *
+ * @v rootdev EISA bus root device
+ */
+static void eisabus_remove ( struct root_device *rootdev ) {
+ struct eisa_device *eisa;
+ struct eisa_device *tmp;
+
+ list_for_each_entry_safe ( eisa, tmp, &rootdev->dev.children,
+ dev.siblings ) {
+ eisa_remove ( eisa );
+ list_del ( &eisa->dev.siblings );
+ free ( eisa );
+ }
+}
+
+/** EISA bus root device driver */
+static struct root_driver eisa_root_driver = {
+ .probe = eisabus_probe,
+ .remove = eisabus_remove,
+};
+
+/** EISA bus root device */
+struct root_device eisa_root_device __root_device = {
+ .dev = { .name = "EISA" },
+ .driver = &eisa_root_driver,
+};
diff --git a/gpxe/src/drivers/bus/isa.c b/gpxe/src/drivers/bus/isa.c
new file mode 100644
index 00000000..a4105fd0
--- /dev/null
+++ b/gpxe/src/drivers/bus/isa.c
@@ -0,0 +1,175 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <io.h>
+#include <gpxe/isa.h>
+
+/*
+ * isa.c implements a "classical" port-scanning method of ISA device
+ * detection. The driver must provide a list of probe addresses
+ * (probe_addrs), together with a function (probe_addr) that can be
+ * used to test for the physical presence of a device at any given
+ * address.
+ *
+ * Note that this should probably be considered the "last resort" for
+ * device probing. If the card supports ISAPnP or EISA, use that
+ * instead. Some cards (e.g. the 3c509) implement a proprietary
+ * ISAPnP-like mechanism.
+ *
+ * The ISA probe address list can be overridden by config.h; if the
+ * user specifies ISA_PROBE_ADDRS then that list will be used first.
+ * (If ISA_PROBE_ONLY is defined, the driver's own list will never be
+ * used).
+ */
+
+/*
+ * User-supplied probe address list
+ *
+ */
+static isa_probe_addr_t isa_extra_probe_addrs[] = {
+#ifdef ISA_PROBE_ADDRS
+ ISA_PROBE_ADDRS
+#endif
+};
+#define ISA_EXTRA_PROBE_ADDR_COUNT \
+ ( sizeof ( isa_extra_probe_addrs ) / sizeof ( isa_extra_probe_addrs[0] ) )
+
+#define ISA_IOIDX_MIN( driver ) ( -ISA_EXTRA_PROBE_ADDR_COUNT )
+#ifdef ISA_PROBE_ONLY
+#define ISA_IOIDX_MAX( driver ) ( -1 )
+#else
+#define ISA_IOIDX_MAX( driver ) ( (int) (driver)->addr_count - 1 )
+#endif
+
+#define ISA_IOADDR( driver, ioidx ) \
+ ( ( (ioidx) < 0 ) ? \
+ isa_extra_probe_addrs[ (ioidx) + ISA_EXTRA_PROBE_ADDR_COUNT ] : \
+ (driver)->probe_addrs[(ioidx)] )
+
+static struct isa_driver isa_drivers[0]
+ __table_start ( struct isa_driver, isa_driver );
+static struct isa_driver isa_drivers_end[0]
+ __table_end ( struct isa_driver, isa_driver );
+
+static void isabus_remove ( struct root_device *rootdev );
+
+/**
+ * Probe an ISA device
+ *
+ * @v isa ISA device
+ * @ret rc Return status code
+ */
+static int isa_probe ( struct isa_device *isa ) {
+ int rc;
+
+ DBG ( "Trying ISA driver %s at I/O %04x\n",
+ isa->driver->name, isa->ioaddr );
+
+ if ( ( rc = isa->driver->probe ( isa ) ) != 0 ) {
+ DBG ( "...probe failed\n" );
+ return rc;
+ }
+
+ DBG ( "...device found\n" );
+ return 0;
+}
+
+/**
+ * Remove an ISA device
+ *
+ * @v isa ISA device
+ */
+static void isa_remove ( struct isa_device *isa ) {
+ isa->driver->remove ( isa );
+ DBG ( "Removed ISA%04x\n", isa->ioaddr );
+}
+
+/**
+ * Probe ISA root bus
+ *
+ * @v rootdev ISA bus root device
+ *
+ * Scans the ISA bus for devices and registers all devices it can
+ * find.
+ */
+static int isabus_probe ( struct root_device *rootdev ) {
+ struct isa_device *isa = NULL;
+ struct isa_driver *driver;
+ int ioidx;
+ int rc;
+
+ for ( driver = isa_drivers ; driver < isa_drivers_end ; driver++ ) {
+ for ( ioidx = ISA_IOIDX_MIN ( driver ) ;
+ ioidx <= ISA_IOIDX_MAX ( driver ) ; ioidx++ ) {
+ /* Allocate struct isa_device */
+ if ( ! isa )
+ isa = malloc ( sizeof ( *isa ) );
+ if ( ! isa ) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset ( isa, 0, sizeof ( *isa ) );
+ isa->driver = driver;
+ isa->ioaddr = ISA_IOADDR ( driver, ioidx );
+
+ /* Add to device hierarchy */
+ snprintf ( isa->dev.name, sizeof ( isa->dev.name ),
+ "ISA%04x", isa->ioaddr );
+ isa->dev.desc.bus_type = BUS_TYPE_ISA;
+ isa->dev.desc.vendor = driver->vendor_id;
+ isa->dev.desc.device = driver->prod_id;
+ isa->dev.parent = &rootdev->dev;
+ list_add ( &isa->dev.siblings,
+ &rootdev->dev.children );
+ INIT_LIST_HEAD ( &isa->dev.children );
+
+ /* Try probing at this I/O address */
+ if ( isa_probe ( isa ) == 0 ) {
+ /* isadev registered, we can drop our ref */
+ isa = NULL;
+ } else {
+ /* Not registered; re-use struct */
+ list_del ( &isa->dev.siblings );
+ }
+ }
+ }
+
+ free ( isa );
+ return 0;
+
+ err:
+ free ( isa );
+ isabus_remove ( rootdev );
+ return rc;
+}
+
+/**
+ * Remove ISA root bus
+ *
+ * @v rootdev ISA bus root device
+ */
+static void isabus_remove ( struct root_device *rootdev ) {
+ struct isa_device *isa;
+ struct isa_device *tmp;
+
+ list_for_each_entry_safe ( isa, tmp, &rootdev->dev.children,
+ dev.siblings ) {
+ isa_remove ( isa );
+ list_del ( &isa->dev.siblings );
+ free ( isa );
+ }
+}
+
+/** ISA bus root device driver */
+static struct root_driver isa_root_driver = {
+ .probe = isabus_probe,
+ .remove = isabus_remove,
+};
+
+/** ISA bus root device */
+struct root_device isa_root_device __root_device = {
+ .dev = { .name = "ISA" },
+ .driver = &isa_root_driver,
+};
diff --git a/gpxe/src/drivers/bus/isa_ids.c b/gpxe/src/drivers/bus/isa_ids.c
new file mode 100644
index 00000000..73101584
--- /dev/null
+++ b/gpxe/src/drivers/bus/isa_ids.c
@@ -0,0 +1,26 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <byteswap.h>
+#include <gpxe/isa_ids.h>
+
+/*
+ * EISA and ISAPnP IDs are actually mildly human readable, though in a
+ * somewhat brain-damaged way.
+ *
+ */
+char * isa_id_string ( unsigned int vendor, unsigned int product ) {
+ static char buf[7];
+ int i;
+
+ /* Vendor ID is a compressed ASCII string */
+ vendor = bswap_16 ( vendor );
+ for ( i = 2 ; i >= 0 ; i-- ) {
+ buf[i] = ( 'A' - 1 + ( vendor & 0x1f ) );
+ vendor >>= 5;
+ }
+
+ /* Product ID is a 4-digit hex string */
+ sprintf ( &buf[3], "%04x", bswap_16 ( product ) );
+
+ return buf;
+}
diff --git a/gpxe/src/drivers/bus/isapnp.c b/gpxe/src/drivers/bus/isapnp.c
new file mode 100644
index 00000000..f4968eb1
--- /dev/null
+++ b/gpxe/src/drivers/bus/isapnp.c
@@ -0,0 +1,758 @@
+/**************************************************************************
+*
+* isapnp.c -- Etherboot isapnp support for the 3Com 3c515
+* Written 2002-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code:
+* Copyright (C) 2001 P.J.H.Fox (fox@roestock.demon.co.uk)
+*
+*
+* REVISION HISTORY:
+* ================
+* Version 0.1 April 26, 2002 TJL
+* Version 0.2 01/08/2003 TJL Moved outside the 3c515.c driver file
+* Version 0.3 Sept 23, 2003 timlegge Change delay to currticks
+*
+*
+* Generalised into an ISAPnP bus that can be used by more than just
+* the 3c515 by Michael Brown <mbrown@fensystems.co.uk>
+*
+***************************************************************************/
+
+/** @file
+ *
+ * ISAPnP bus support
+ *
+ * Etherboot orignally gained ISAPnP support in a very limited way for
+ * the 3c515 NIC. The current implementation is almost a complete
+ * rewrite based on the ISAPnP specification, with passing reference
+ * to the Linux ISAPnP code.
+ *
+ * There can be only one ISAPnP bus in a system. Once the read port
+ * is known and all cards have been allocated CSNs, there's nothing to
+ * be gained by re-scanning for cards.
+ *
+ * External code (e.g. the ISAPnP ROM prefix) may already know the
+ * read port address, in which case it can store it in
+ * #isapnp_read_port. Note that setting the read port address in this
+ * way will prevent further isolation from taking place; you should
+ * set the read port address only if you know that devices have
+ * already been allocated CSNs.
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <io.h>
+#include <unistd.h>
+#include <gpxe/isapnp.h>
+
+/**
+ * ISAPnP Read Port address.
+ *
+ * ROM prefix may be able to set this address, which is why this is
+ * non-static.
+ */
+uint16_t isapnp_read_port;
+
+static struct isapnp_driver isapnp_drivers[0]
+ __table_start ( struct isapnp_driver, isapnp_drivers );
+static struct isapnp_driver isapnp_drivers_end[0]
+ __table_end ( struct isapnp_driver, isapnp_drivers );
+
+static void isapnpbus_remove ( struct root_device *rootdev );
+
+/*
+ * ISAPnP utility functions
+ *
+ */
+
+#define ISAPNP_CARD_ID_FMT "ID %04x:%04x (\"%s\") serial %lx"
+#define ISAPNP_CARD_ID_DATA(identifier) \
+ (identifier)->vendor_id, (identifier)->prod_id, \
+ isa_id_string ( (identifier)->vendor_id, (identifier)->prod_id ), \
+ (identifier)->serial
+#define ISAPNP_DEV_ID_FMT "ID %04x:%04x (\"%s\")"
+#define ISAPNP_DEV_ID_DATA(isapnp) \
+ (isapnp)->vendor_id, (isapnp)->prod_id, \
+ isa_id_string ( (isapnp)->vendor_id, (isapnp)->prod_id )
+
+static inline void isapnp_write_address ( unsigned int address ) {
+ outb ( address, ISAPNP_ADDRESS );
+}
+
+static inline void isapnp_write_data ( unsigned int data ) {
+ outb ( data, ISAPNP_WRITE_DATA );
+}
+
+static inline unsigned int isapnp_read_data ( void ) {
+ return inb ( isapnp_read_port );
+}
+
+static inline void isapnp_write_byte ( unsigned int address,
+ unsigned int value ) {
+ isapnp_write_address ( address );
+ isapnp_write_data ( value );
+}
+
+static inline unsigned int isapnp_read_byte ( unsigned int address ) {
+ isapnp_write_address ( address );
+ return isapnp_read_data ();
+}
+
+static inline unsigned int isapnp_read_word ( unsigned int address ) {
+ /* Yes, they're in big-endian order */
+ return ( ( isapnp_read_byte ( address ) << 8 )
+ | isapnp_read_byte ( address + 1 ) );
+}
+
+/** Inform cards of a new read port address */
+static inline void isapnp_set_read_port ( void ) {
+ isapnp_write_byte ( ISAPNP_READPORT, ( isapnp_read_port >> 2 ) );
+}
+
+/**
+ * Enter the Isolation state.
+ *
+ * Only cards currently in the Sleep state will respond to this
+ * command.
+ */
+static inline void isapnp_serialisolation ( void ) {
+ isapnp_write_address ( ISAPNP_SERIALISOLATION );
+}
+
+/**
+ * Enter the Wait for Key state.
+ *
+ * All cards will respond to this command, regardless of their current
+ * state.
+ */
+static inline void isapnp_wait_for_key ( void ) {
+ isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_WAIT_FOR_KEY );
+}
+
+/**
+ * Reset (i.e. remove) Card Select Number.
+ *
+ * Only cards currently in the Sleep state will respond to this
+ * command.
+ */
+static inline void isapnp_reset_csn ( void ) {
+ isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_RESET_CSN );
+}
+
+/**
+ * Place a specified card into the Config state.
+ *
+ * @v csn Card Select Number
+ * @ret None -
+ * @err None -
+ *
+ * Only cards currently in the Sleep, Isolation, or Config states will
+ * respond to this command. The card that has the specified CSN will
+ * enter the Config state, all other cards will enter the Sleep state.
+ */
+static inline void isapnp_wake ( uint8_t csn ) {
+ isapnp_write_byte ( ISAPNP_WAKE, csn );
+}
+
+static inline unsigned int isapnp_read_resourcedata ( void ) {
+ return isapnp_read_byte ( ISAPNP_RESOURCEDATA );
+}
+
+static inline unsigned int isapnp_read_status ( void ) {
+ return isapnp_read_byte ( ISAPNP_STATUS );
+}
+
+/**
+ * Assign a Card Select Number to a card, and enter the Config state.
+ *
+ * @v csn Card Select Number
+ *
+ * Only cards in the Isolation state will respond to this command.
+ * The isolation protocol is designed so that only one card will
+ * remain in the Isolation state by the time the isolation protocol
+ * completes.
+ */
+static inline void isapnp_write_csn ( unsigned int csn ) {
+ isapnp_write_byte ( ISAPNP_CARDSELECTNUMBER, csn );
+}
+
+static inline void isapnp_logicaldevice ( unsigned int logdev ) {
+ isapnp_write_byte ( ISAPNP_LOGICALDEVICENUMBER, logdev );
+}
+
+static inline void isapnp_activate ( unsigned int logdev ) {
+ isapnp_logicaldevice ( logdev );
+ isapnp_write_byte ( ISAPNP_ACTIVATE, 1 );
+}
+
+static inline void isapnp_deactivate ( unsigned int logdev ) {
+ isapnp_logicaldevice ( logdev );
+ isapnp_write_byte ( ISAPNP_ACTIVATE, 0 );
+}
+
+static inline unsigned int isapnp_read_iobase ( unsigned int index ) {
+ return isapnp_read_word ( ISAPNP_IOBASE ( index ) );
+}
+
+static inline unsigned int isapnp_read_irqno ( unsigned int index ) {
+ return isapnp_read_byte ( ISAPNP_IRQNO ( index ) );
+}
+
+static void isapnp_delay ( void ) {
+ udelay ( 1000 );
+}
+
+/**
+ * Linear feedback shift register.
+ *
+ * @v lfsr Current value of the LFSR
+ * @v input_bit Current input bit to the LFSR
+ * @ret lfsr Next value of the LFSR
+ *
+ * This routine implements the linear feedback shift register as
+ * described in Appendix B of the PnP ISA spec. The hardware
+ * implementation uses eight D-type latches and two XOR gates. I
+ * think this is probably the smallest possible implementation in
+ * software. Six instructions when input_bit is a constant 0 (for
+ * isapnp_send_key). :)
+ */
+static inline unsigned int isapnp_lfsr_next ( unsigned int lfsr,
+ unsigned int input_bit ) {
+ register uint8_t lfsr_next;
+
+ lfsr_next = lfsr >> 1;
+ lfsr_next |= ( ( ( lfsr ^ lfsr_next ) ^ input_bit ) ) << 7;
+ return lfsr_next;
+}
+
+/**
+ * Send the ISAPnP initiation key.
+ *
+ * Sending the key causes all ISAPnP cards that are currently in the
+ * Wait for Key state to transition into the Sleep state.
+ */
+static void isapnp_send_key ( void ) {
+ unsigned int i;
+ unsigned int lfsr;
+
+ isapnp_delay();
+ isapnp_write_address ( 0x00 );
+ isapnp_write_address ( 0x00 );
+
+ lfsr = ISAPNP_LFSR_SEED;
+ for ( i = 0 ; i < 32 ; i++ ) {
+ isapnp_write_address ( lfsr );
+ lfsr = isapnp_lfsr_next ( lfsr, 0 );
+ }
+}
+
+/**
+ * Compute ISAPnP identifier checksum
+ *
+ * @v identifier ISAPnP identifier
+ * @ret checksum Expected checksum value
+ */
+static unsigned int isapnp_checksum ( struct isapnp_identifier *identifier ) {
+ unsigned int i, j;
+ unsigned int lfsr;
+ unsigned int byte;
+
+ lfsr = ISAPNP_LFSR_SEED;
+ for ( i = 0 ; i < 8 ; i++ ) {
+ byte = * ( ( ( uint8_t * ) identifier ) + i );
+ for ( j = 0 ; j < 8 ; j++ ) {
+ lfsr = isapnp_lfsr_next ( lfsr, byte );
+ byte >>= 1;
+ }
+ }
+ return lfsr;
+}
+
+/*
+ * Read a byte of resource data from the current location
+ *
+ * @ret byte Byte of resource data
+ */
+static inline unsigned int isapnp_peek_byte ( void ) {
+ unsigned int i;
+
+ /* Wait for data to be ready */
+ for ( i = 0 ; i < 20 ; i++ ) {
+ if ( isapnp_read_status() & 0x01 ) {
+ /* Byte ready - read it */
+ return isapnp_read_resourcedata();
+ }
+ isapnp_delay();
+ }
+ /* Data never became ready - return 0xff */
+ return 0xff;
+}
+
+/**
+ * Read resource data.
+ *
+ * @v buf Buffer in which to store data, or NULL
+ * @v bytes Number of bytes to read
+ *
+ * Resource data is read from the current location. If #buf is NULL,
+ * the data is discarded.
+ */
+static void isapnp_peek ( void *buf, size_t len ) {
+ unsigned int i;
+ unsigned int byte;
+
+ for ( i = 0 ; i < len ; i++) {
+ byte = isapnp_peek_byte();
+ if ( buf )
+ * ( ( uint8_t * ) buf + i ) = byte;
+ }
+}
+
+/**
+ * Find a tag within the resource data.
+ *
+ * @v wanted_tag The tag that we're looking for
+ * @v buf Buffer in which to store the tag's contents
+ * @v len Length of buffer
+ * @ret rc Return status code
+ *
+ * Scan through the resource data until we find a particular tag, and
+ * read its contents into a buffer.
+ */
+static int isapnp_find_tag ( unsigned int wanted_tag, void *buf, size_t len ) {
+ unsigned int tag;
+ unsigned int tag_len;
+
+ DBG2 ( "ISAPnP read tag" );
+ do {
+ tag = isapnp_peek_byte();
+ if ( ISAPNP_IS_SMALL_TAG ( tag ) ) {
+ tag_len = ISAPNP_SMALL_TAG_LEN ( tag );
+ tag = ISAPNP_SMALL_TAG_NAME ( tag );
+ } else {
+ tag_len = ( isapnp_peek_byte() +
+ ( isapnp_peek_byte() << 8 ) );
+ tag = ISAPNP_LARGE_TAG_NAME ( tag );
+ }
+ DBG2 ( " %02x (%02x)", tag, tag_len );
+ if ( tag == wanted_tag ) {
+ if ( len > tag_len )
+ len = tag_len;
+ isapnp_peek ( buf, len );
+ DBG2 ( "\n" );
+ return 0;
+ } else {
+ isapnp_peek ( NULL, tag_len );
+ }
+ } while ( tag != ISAPNP_TAG_END );
+ DBG2 ( "\n" );
+ return -ENOENT;
+}
+
+/**
+ * Find specified Logical Device ID tag
+ *
+ * @v logdev Logical device ID
+ * @v logdevid Logical device ID structure to fill in
+ * @ret rc Return status code
+ */
+static int isapnp_find_logdevid ( unsigned int logdev,
+ struct isapnp_logdevid *logdevid ) {
+ unsigned int i;
+ int rc;
+
+ for ( i = 0 ; i <= logdev ; i++ ) {
+ if ( ( rc = isapnp_find_tag ( ISAPNP_TAG_LOGDEVID, logdevid,
+ sizeof ( *logdevid ) ) ) != 0 )
+ return rc;
+ }
+ return 0;
+}
+
+/**
+ * Try isolating ISAPnP cards at the current read port.
+ *
+ * @ret \>0 Number of ISAPnP cards found
+ * @ret 0 There are no ISAPnP cards in the system
+ * @ret \<0 A conflict was detected; try a new read port
+ * @err None -
+ *
+ * The state diagram on page 18 (PDF page 24) of the PnP ISA spec
+ * gives the best overview of what happens here.
+ */
+static int isapnp_try_isolate ( void ) {
+ struct isapnp_identifier identifier;
+ unsigned int i, j;
+ unsigned int seen_55aa, seen_life;
+ unsigned int csn = 0;
+ unsigned int data;
+ unsigned int byte;
+
+ DBG ( "ISAPnP attempting isolation at read port %04x\n",
+ isapnp_read_port );
+
+ /* Place all cards into the Sleep state, whatever state
+ * they're currently in.
+ */
+ isapnp_wait_for_key();
+ isapnp_send_key();
+
+ /* Reset all assigned CSNs */
+ isapnp_reset_csn();
+ isapnp_delay();
+ isapnp_delay();
+
+ /* Place all cards into the Isolation state */
+ isapnp_wait_for_key ();
+ isapnp_send_key();
+ isapnp_wake ( 0x00 );
+
+ /* Set the read port */
+ isapnp_set_read_port();
+ isapnp_delay();
+
+ while ( 1 ) {
+
+ /* All cards that do not have assigned CSNs are
+ * currently in the Isolation state, each time we go
+ * through this loop.
+ */
+
+ /* Initiate serial isolation */
+ isapnp_serialisolation();
+ isapnp_delay();
+
+ /* Read identifier serially via the ISAPnP read port. */
+ memset ( &identifier, 0, sizeof ( identifier ) );
+ seen_55aa = seen_life = 0;
+ for ( i = 0 ; i < 9 ; i++ ) {
+ byte = 0;
+ for ( j = 0 ; j < 8 ; j++ ) {
+ data = isapnp_read_data();
+ isapnp_delay();
+ data = ( ( data << 8 ) | isapnp_read_data() );
+ isapnp_delay();
+ byte >>= 1;
+ if ( data != 0xffff ) {
+ seen_life++;
+ if ( data == 0x55aa ) {
+ byte |= 0x80;
+ seen_55aa++;
+ }
+ }
+ }
+ *( ( ( uint8_t * ) &identifier ) + i ) = byte;
+ }
+
+ /* If we didn't see any 55aa patterns, stop here */
+ if ( ! seen_55aa ) {
+ if ( csn ) {
+ DBG ( "ISAPnP found no more cards\n" );
+ } else {
+ if ( seen_life ) {
+ DBG ( "ISAPnP saw life but no cards, "
+ "trying new read port\n" );
+ csn = -1;
+ } else {
+ DBG ( "ISAPnP saw no signs of life, "
+ "abandoning isolation\n" );
+ }
+ }
+ break;
+ }
+
+ /* If the checksum was invalid stop here */
+ if ( identifier.checksum != isapnp_checksum ( &identifier) ) {
+ DBG ( "ISAPnP found malformed card "
+ ISAPNP_CARD_ID_FMT "\n with checksum %02x "
+ "(should be %02x), trying new read port\n",
+ ISAPNP_CARD_ID_DATA ( &identifier ),
+ identifier.checksum,
+ isapnp_checksum ( &identifier) );
+ csn = -1;
+ break;
+ }
+
+ /* Give the device a CSN */
+ csn++;
+ DBG ( "ISAPnP found card " ISAPNP_CARD_ID_FMT
+ ", assigning CSN %02x\n",
+ ISAPNP_CARD_ID_DATA ( &identifier ), csn );
+
+ isapnp_write_csn ( csn );
+ isapnp_delay();
+
+ /* Send this card back to Sleep and force all cards
+ * without a CSN into Isolation state
+ */
+ isapnp_wake ( 0x00 );
+ isapnp_delay();
+ }
+
+ /* Place all cards in Wait for Key state */
+ isapnp_wait_for_key();
+
+ /* Return number of cards found */
+ if ( csn > 0 ) {
+ DBG ( "ISAPnP found %d cards at read port %04x\n",
+ csn, isapnp_read_port );
+ }
+ return csn;
+}
+
+/**
+ * Find a valid read port and isolate all ISAPnP cards.
+ *
+ */
+static void isapnp_isolate ( void ) {
+ for ( isapnp_read_port = ISAPNP_READ_PORT_START ;
+ isapnp_read_port <= ISAPNP_READ_PORT_MAX ;
+ isapnp_read_port += ISAPNP_READ_PORT_STEP ) {
+ /* Avoid problematic locations such as the NE2000
+ * probe space
+ */
+ if ( ( isapnp_read_port >= 0x280 ) &&
+ ( isapnp_read_port <= 0x380 ) )
+ continue;
+
+ /* If we detect any ISAPnP cards at this location, stop */
+ if ( isapnp_try_isolate() >= 0 )
+ return;
+ }
+}
+
+/**
+ * Activate or deactivate an ISAPnP device.
+ *
+ * @v isapnp ISAPnP device
+ * @v activation True to enable, False to disable the device
+ * @ret None -
+ * @err None -
+ *
+ * This routine simply activates the device in its current
+ * configuration, or deactivates the device. It does not attempt any
+ * kind of resource arbitration.
+ *
+ */
+void isapnp_device_activation ( struct isapnp_device *isapnp,
+ int activation ) {
+ /* Wake the card and select the logical device */
+ isapnp_wait_for_key ();
+ isapnp_send_key ();
+ isapnp_wake ( isapnp->csn );
+ isapnp_logicaldevice ( isapnp->logdev );
+
+ /* Activate/deactivate the logical device */
+ isapnp_activate ( activation );
+ isapnp_delay();
+
+ /* Return all cards to Wait for Key state */
+ isapnp_wait_for_key ();
+
+ DBG ( "ISAPnP %s device %02x:%02x\n",
+ ( activation ? "activated" : "deactivated" ),
+ isapnp->csn, isapnp->logdev );
+}
+
+/**
+ * Probe an ISAPnP device
+ *
+ * @v isapnp ISAPnP device
+ * @ret rc Return status code
+ *
+ * Searches for a driver for the ISAPnP device. If a driver is found,
+ * its probe() routine is called.
+ */
+static int isapnp_probe ( struct isapnp_device *isapnp ) {
+ struct isapnp_driver *driver;
+ struct isapnp_device_id *id;
+ unsigned int i;
+ int rc;
+
+ DBG ( "Adding ISAPnP device %02x:%02x (%04x:%04x (\"%s\") "
+ "io %x irq %d)\n", isapnp->csn, isapnp->logdev,
+ isapnp->vendor_id, isapnp->prod_id,
+ isa_id_string ( isapnp->vendor_id, isapnp->prod_id ),
+ isapnp->ioaddr, isapnp->irqno );
+
+ for ( driver = isapnp_drivers; driver < isapnp_drivers_end; driver++ ){
+ for ( i = 0 ; i < driver->id_count ; i++ ) {
+ id = &driver->ids[i];
+ if ( id->vendor_id != isapnp->vendor_id )
+ continue;
+ if ( ISA_PROD_ID ( id->prod_id ) !=
+ ISA_PROD_ID ( isapnp->prod_id ) )
+ continue;
+ isapnp->driver = driver;
+ isapnp->driver_name = id->name;
+ DBG ( "...using driver %s\n", isapnp->driver_name );
+ if ( ( rc = driver->probe ( isapnp, id ) ) != 0 ) {
+ DBG ( "......probe failed\n" );
+ continue;
+ }
+ return 0;
+ }
+ }
+
+ DBG ( "...no driver found\n" );
+ return -ENOTTY;
+}
+
+/**
+ * Remove an ISAPnP device
+ *
+ * @v isapnp ISAPnP device
+ */
+static void isapnp_remove ( struct isapnp_device *isapnp ) {
+ isapnp->driver->remove ( isapnp );
+ DBG ( "Removed ISAPnP device %02x:%02x\n",
+ isapnp->csn, isapnp->logdev );
+}
+
+/**
+ * Probe ISAPnP root bus
+ *
+ * @v rootdev ISAPnP bus root device
+ *
+ * Scans the ISAPnP bus for devices and registers all devices it can
+ * find.
+ */
+static int isapnpbus_probe ( struct root_device *rootdev ) {
+ struct isapnp_device *isapnp = NULL;
+ struct isapnp_identifier identifier;
+ struct isapnp_logdevid logdevid;
+ unsigned int csn;
+ unsigned int logdev;
+ int rc;
+
+ /* Perform isolation if it hasn't yet been done */
+ if ( ! isapnp_read_port )
+ isapnp_isolate();
+
+ for ( csn = 1 ; csn <= 0xff ; csn++ ) {
+ for ( logdev = 0 ; logdev <= 0xff ; logdev++ ) {
+
+ /* Allocate struct isapnp_device */
+ if ( ! isapnp )
+ isapnp = malloc ( sizeof ( *isapnp ) );
+ if ( ! isapnp ) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset ( isapnp, 0, sizeof ( *isapnp ) );
+ isapnp->csn = csn;
+ isapnp->logdev = logdev;
+
+ /* Wake the card */
+ isapnp_wait_for_key();
+ isapnp_send_key();
+ isapnp_wake ( csn );
+
+ /* Read the card identifier */
+ isapnp_peek ( &identifier, sizeof ( identifier ) );
+
+ /* No card with this CSN; stop here */
+ if ( identifier.vendor_id & 0x80 )
+ goto done;
+
+ /* Find the Logical Device ID tag */
+ if ( ( rc = isapnp_find_logdevid ( logdev,
+ &logdevid ) ) != 0){
+ /* No more logical devices; go to next CSN */
+ break;
+ }
+
+ /* Select the logical device */
+ isapnp_logicaldevice ( logdev );
+
+ /* Populate struct isapnp_device */
+ isapnp->vendor_id = logdevid.vendor_id;
+ isapnp->prod_id = logdevid.prod_id;
+ isapnp->ioaddr = isapnp_read_iobase ( 0 );
+ isapnp->irqno = isapnp_read_irqno ( 0 );
+
+ /* Return all cards to Wait for Key state */
+ isapnp_wait_for_key();
+
+ /* Add to device hierarchy */
+ snprintf ( isapnp->dev.name,
+ sizeof ( isapnp->dev.name ),
+ "ISAPnP%02x:%02x", csn, logdev );
+ isapnp->dev.desc.bus_type = BUS_TYPE_ISAPNP;
+ isapnp->dev.desc.vendor = isapnp->vendor_id;
+ isapnp->dev.desc.device = isapnp->prod_id;
+ isapnp->dev.desc.ioaddr = isapnp->ioaddr;
+ isapnp->dev.desc.irq = isapnp->irqno;
+ isapnp->dev.parent = &rootdev->dev;
+ list_add ( &isapnp->dev.siblings,
+ &rootdev->dev.children );
+ INIT_LIST_HEAD ( &isapnp->dev.children );
+
+ /* Look for a driver */
+ if ( isapnp_probe ( isapnp ) == 0 ) {
+ /* isapnpdev registered, we can drop our ref */
+ isapnp = NULL;
+ } else {
+ /* Not registered; re-use struct */
+ list_del ( &isapnp->dev.siblings );
+ }
+ }
+ }
+
+ done:
+ free ( isapnp );
+ return 0;
+
+ err:
+ free ( isapnp );
+ isapnpbus_remove ( rootdev );
+ return rc;
+}
+
+/**
+ * Remove ISAPnP root bus
+ *
+ * @v rootdev ISAPnP bus root device
+ */
+static void isapnpbus_remove ( struct root_device *rootdev ) {
+ struct isapnp_device *isapnp;
+ struct isapnp_device *tmp;
+
+ list_for_each_entry_safe ( isapnp, tmp, &rootdev->dev.children,
+ dev.siblings ) {
+ isapnp_remove ( isapnp );
+ list_del ( &isapnp->dev.siblings );
+ free ( isapnp );
+ }
+}
+
+/** ISAPnP bus root device driver */
+static struct root_driver isapnp_root_driver = {
+ .probe = isapnpbus_probe,
+ .remove = isapnpbus_remove,
+};
+
+/** ISAPnP bus root device */
+struct root_device isapnp_root_device __root_device = {
+ .dev = { .name = "ISAPnP" },
+ .driver = &isapnp_root_driver,
+};
diff --git a/gpxe/src/drivers/bus/mca.c b/gpxe/src/drivers/bus/mca.c
new file mode 100644
index 00000000..eb7b7e39
--- /dev/null
+++ b/gpxe/src/drivers/bus/mca.c
@@ -0,0 +1,180 @@
+/*
+ * MCA bus driver code
+ *
+ * Abstracted from 3c509.c.
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <io.h>
+#include <gpxe/mca.h>
+
+static struct mca_driver mca_drivers[0]
+ __table_start ( struct mca_driver, mca_drivers );
+static struct mca_driver mca_drivers_end[0]
+ __table_end ( struct mca_driver, mca_drivers );
+
+static void mcabus_remove ( struct root_device *rootdev );
+
+/**
+ * Probe an MCA device
+ *
+ * @v mca MCA device
+ * @ret rc Return status code
+ *
+ * Searches for a driver for the MCA device. If a driver is found,
+ * its probe() routine is called.
+ */
+static int mca_probe ( struct mca_device *mca ) {
+ struct mca_driver *driver;
+ struct mca_device_id *id;
+ unsigned int i;
+ int rc;
+
+ DBG ( "Adding MCA slot %02x (ID %04x POS "
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x)\n",
+ mca->slot, MCA_ID ( mca ),
+ mca->pos[0], mca->pos[1], mca->pos[2], mca->pos[3],
+ mca->pos[4], mca->pos[5], mca->pos[6], mca->pos[7] );
+
+ for ( driver = mca_drivers; driver < mca_drivers_end; driver++ ){
+ for ( i = 0 ; i < driver->id_count ; i++ ) {
+ id = &driver->ids[i];
+ if ( id->id != MCA_ID ( mca ) )
+ continue;
+ mca->driver = driver;
+ mca->driver_name = id->name;
+ DBG ( "...using driver %s\n", mca->driver_name );
+ if ( ( rc = driver->probe ( mca, id ) ) != 0 ) {
+ DBG ( "......probe failed\n" );
+ continue;
+ }
+ return 0;
+ }
+ }
+
+ DBG ( "...no driver found\n" );
+ return -ENOTTY;
+}
+
+/**
+ * Remove an MCA device
+ *
+ * @v mca MCA device
+ */
+static void mca_remove ( struct mca_device *mca ) {
+ mca->driver->remove ( mca );
+ DBG ( "Removed MCA device %02x\n", mca->slot );
+}
+
+/**
+ * Probe MCA root bus
+ *
+ * @v rootdev MCA bus root device
+ *
+ * Scans the MCA bus for devices and registers all devices it can
+ * find.
+ */
+static int mcabus_probe ( struct root_device *rootdev ) {
+ struct mca_device *mca = NULL;
+ unsigned int slot;
+ int seen_non_ff;
+ unsigned int i;
+ int rc;
+
+ for ( slot = 0 ; slot <= MCA_MAX_SLOT_NR ; slot++ ) {
+ /* Allocate struct mca_device */
+ if ( ! mca )
+ mca = malloc ( sizeof ( *mca ) );
+ if ( ! mca ) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset ( mca, 0, sizeof ( *mca ) );
+ mca->slot = slot;
+
+ /* Make sure motherboard setup is off */
+ outb_p ( 0xff, MCA_MOTHERBOARD_SETUP_REG );
+
+ /* Select the slot */
+ outb_p ( 0x8 | ( mca->slot & 0xf ), MCA_ADAPTER_SETUP_REG );
+
+ /* Read the POS registers */
+ seen_non_ff = 0;
+ for ( i = 0 ; i < ( sizeof ( mca->pos ) /
+ sizeof ( mca->pos[0] ) ) ; i++ ) {
+ mca->pos[i] = inb_p ( MCA_POS_REG ( i ) );
+ if ( mca->pos[i] != 0xff )
+ seen_non_ff = 1;
+ }
+
+ /* Kill all setup modes */
+ outb_p ( 0, MCA_ADAPTER_SETUP_REG );
+
+ /* If all POS registers are 0xff, this means there's no device
+ * present
+ */
+ if ( ! seen_non_ff )
+ continue;
+
+ /* Add to device hierarchy */
+ snprintf ( mca->dev.name, sizeof ( mca->dev.name ),
+ "MCA%02x", slot );
+ mca->dev.desc.bus_type = BUS_TYPE_MCA;
+ mca->dev.desc.vendor = GENERIC_MCA_VENDOR;
+ mca->dev.desc.device = MCA_ID ( mca );
+ mca->dev.parent = &rootdev->dev;
+ list_add ( &mca->dev.siblings, &rootdev->dev.children );
+ INIT_LIST_HEAD ( &mca->dev.children );
+
+ /* Look for a driver */
+ if ( mca_probe ( mca ) == 0 ) {
+ /* mcadev registered, we can drop our ref */
+ mca = NULL;
+ } else {
+ /* Not registered; re-use struct */
+ list_del ( &mca->dev.siblings );
+ }
+ }
+
+ free ( mca );
+ return 0;
+
+ err:
+ free ( mca );
+ mcabus_remove ( rootdev );
+ return rc;
+}
+
+/**
+ * Remove MCA root bus
+ *
+ * @v rootdev MCA bus root device
+ */
+static void mcabus_remove ( struct root_device *rootdev ) {
+ struct mca_device *mca;
+ struct mca_device *tmp;
+
+ list_for_each_entry_safe ( mca, tmp, &rootdev->dev.children,
+ dev.siblings ) {
+ mca_remove ( mca );
+ list_del ( &mca->dev.siblings );
+ free ( mca );
+ }
+}
+
+/** MCA bus root device driver */
+static struct root_driver mca_root_driver = {
+ .probe = mcabus_probe,
+ .remove = mcabus_remove,
+};
+
+/** MCA bus root device */
+struct root_device mca_root_device __root_device = {
+ .dev = { .name = "MCA" },
+ .driver = &mca_root_driver,
+};
diff --git a/gpxe/src/drivers/bus/pci.c b/gpxe/src/drivers/bus/pci.c
new file mode 100644
index 00000000..967441ac
--- /dev/null
+++ b/gpxe/src/drivers/bus/pci.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * Based in part on pci.c from Etherboot 5.4, by Ken Yap and David
+ * Munro, in turn based on the Linux kernel's PCI implementation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/tables.h>
+#include <gpxe/device.h>
+#include <gpxe/pci.h>
+
+/** @file
+ *
+ * PCI bus
+ *
+ */
+
+static struct pci_driver pci_drivers[0]
+ __table_start ( struct pci_driver, pci_drivers );
+static struct pci_driver pci_drivers_end[0]
+ __table_end ( struct pci_driver, pci_drivers );
+
+static void pcibus_remove ( struct root_device *rootdev );
+
+/**
+ * Read PCI BAR
+ *
+ * @v pci PCI device
+ * @v reg PCI register number
+ * @ret bar Base address register
+ *
+ * Reads the specified PCI base address register, including the flags
+ * portion. 64-bit BARs will be handled automatically. If the value
+ * of the 64-bit BAR exceeds the size of an unsigned long (i.e. if the
+ * high dword is non-zero on a 32-bit platform), then the value
+ * returned will be zero plus the flags for a 64-bit BAR. Unreachable
+ * 64-bit BARs are therefore returned as uninitialised 64-bit BARs.
+ */
+static unsigned long pci_bar ( struct pci_device *pci, unsigned int reg ) {
+ uint32_t low;
+ uint32_t high;
+
+ pci_read_config_dword ( pci, reg, &low );
+ if ( ( low & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK) )
+ == (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64) ){
+ pci_read_config_dword ( pci, reg + 4, &high );
+ if ( high ) {
+ if ( sizeof ( unsigned long ) > sizeof ( uint32_t ) ) {
+ return ( ( ( uint64_t ) high << 32 ) | low );
+ } else {
+ DBG ( "Unhandled 64-bit BAR %08lx%08lx\n",
+ high, low );
+ return PCI_BASE_ADDRESS_MEM_TYPE_64;
+ }
+ }
+ }
+ return low;
+}
+
+/**
+ * Find the start of a PCI BAR
+ *
+ * @v pci PCI device
+ * @v reg PCI register number
+ * @ret start BAR start address
+ *
+ * Reads the specified PCI base address register, and returns the
+ * address portion of the BAR (i.e. without the flags).
+ *
+ * If the address exceeds the size of an unsigned long (i.e. if a
+ * 64-bit BAR has a non-zero high dword on a 32-bit machine), the
+ * return value will be zero.
+ */
+unsigned long pci_bar_start ( struct pci_device *pci, unsigned int reg ) {
+ unsigned long bar;
+
+ bar = pci_bar ( pci, reg );
+ if ( (bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY ){
+ return ( bar & PCI_BASE_ADDRESS_MEM_MASK );
+ } else {
+ return ( bar & PCI_BASE_ADDRESS_IO_MASK );
+ }
+}
+
+/**
+ * Read membase and ioaddr for a PCI device
+ *
+ * @v pci PCI device
+ *
+ * This scans through all PCI BARs on the specified device. The first
+ * valid memory BAR is recorded as pci_device::membase, and the first
+ * valid IO BAR is recorded as pci_device::ioaddr.
+ *
+ * 64-bit BARs are handled automatically. On a 32-bit platform, if a
+ * 64-bit BAR has a non-zero high dword, it will be regarded as
+ * invalid.
+ */
+static void pci_read_bases ( struct pci_device *pci ) {
+ unsigned long bar;
+ int reg;
+
+ for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4 ) {
+ bar = pci_bar ( pci, reg );
+ if ( bar & PCI_BASE_ADDRESS_SPACE_IO ) {
+ if ( ! pci->ioaddr )
+ pci->ioaddr =
+ ( bar & PCI_BASE_ADDRESS_IO_MASK );
+ } else {
+ if ( ! pci->membase )
+ pci->membase =
+ ( bar & PCI_BASE_ADDRESS_MEM_MASK );
+ /* Skip next BAR if 64-bit */
+ if ( bar & PCI_BASE_ADDRESS_MEM_TYPE_64 )
+ reg += 4;
+ }
+ }
+}
+
+/**
+ * Enable PCI device
+ *
+ * @v pci PCI device
+ *
+ * Set device to be a busmaster in case BIOS neglected to do so. Also
+ * adjust PCI latency timer to a reasonable value, 32.
+ */
+void adjust_pci_device ( struct pci_device *pci ) {
+ unsigned short new_command, pci_command;
+ unsigned char pci_latency;
+
+ pci_read_config_word ( pci, PCI_COMMAND, &pci_command );
+ new_command = pci_command | PCI_COMMAND_MASTER | PCI_COMMAND_IO;
+ if ( pci_command != new_command ) {
+ DBG ( "PCI BIOS has not enabled device %02x:%02x.%x! "
+ "Updating PCI command %04x->%04x\n", pci->bus,
+ PCI_SLOT ( pci->devfn ), PCI_FUNC ( pci->devfn ),
+ pci_command, new_command );
+ pci_write_config_word ( pci, PCI_COMMAND, new_command );
+ }
+
+ pci_read_config_byte ( pci, PCI_LATENCY_TIMER, &pci_latency);
+ if ( pci_latency < 32 ) {
+ DBG ( "PCI device %02x:%02x.%x latency timer is unreasonably "
+ "low at %d. Setting to 32.\n", pci->bus,
+ PCI_SLOT ( pci->devfn ), PCI_FUNC ( pci->devfn ),
+ pci_latency );
+ pci_write_config_byte ( pci, PCI_LATENCY_TIMER, 32);
+ }
+}
+
+/**
+ * Probe a PCI device
+ *
+ * @v pci PCI device
+ * @ret rc Return status code
+ *
+ * Searches for a driver for the PCI device. If a driver is found,
+ * its probe() routine is called.
+ */
+static int pci_probe ( struct pci_device *pci ) {
+ struct pci_driver *driver;
+ struct pci_device_id *id;
+ unsigned int i;
+ int rc;
+
+ DBG ( "Adding PCI device %02x:%02x.%x (%04x:%04x mem %lx io %lx "
+ "irq %d)\n", pci->bus, PCI_SLOT ( pci->devfn ),
+ PCI_FUNC ( pci->devfn ), pci->vendor, pci->device,
+ pci->membase, pci->ioaddr, pci->irq );
+
+ for ( driver = pci_drivers ; driver < pci_drivers_end ; driver++ ) {
+ for ( i = 0 ; i < driver->id_count ; i++ ) {
+ id = &driver->ids[i];
+ if ( ( id->vendor != PCI_ANY_ID ) &&
+ ( id->vendor != pci->vendor ) )
+ continue;
+ if ( ( id->device != PCI_ANY_ID ) &&
+ ( id->device != pci->device ) )
+ continue;
+ pci->driver = driver;
+ pci->driver_name = id->name;
+ DBG ( "...using driver %s\n", pci->driver_name );
+ if ( ( rc = driver->probe ( pci, id ) ) != 0 ) {
+ DBG ( "......probe failed\n" );
+ continue;
+ }
+ return 0;
+ }
+ }
+
+ DBG ( "...no driver found\n" );
+ return -ENOTTY;
+}
+
+/**
+ * Remove a PCI device
+ *
+ * @v pci PCI device
+ */
+static void pci_remove ( struct pci_device *pci ) {
+ pci->driver->remove ( pci );
+ DBG ( "Removed PCI device %02x:%02x.%x\n", pci->bus,
+ PCI_SLOT ( pci->devfn ), PCI_FUNC ( pci->devfn ) );
+}
+
+/**
+ * Probe PCI root bus
+ *
+ * @v rootdev PCI bus root device
+ *
+ * Scans the PCI bus for devices and registers all devices it can
+ * find.
+ */
+static int pcibus_probe ( struct root_device *rootdev ) {
+ struct pci_device *pci = NULL;
+ unsigned int max_bus;
+ unsigned int bus;
+ unsigned int devfn;
+ uint8_t hdrtype = 0;
+ uint32_t tmp;
+ int rc;
+
+ max_bus = pci_max_bus();
+ for ( bus = 0 ; bus <= max_bus ; bus++ ) {
+ for ( devfn = 0 ; devfn <= 0xff ; devfn++ ) {
+
+ /* Allocate struct pci_device */
+ if ( ! pci )
+ pci = malloc ( sizeof ( *pci ) );
+ if ( ! pci ) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset ( pci, 0, sizeof ( *pci ) );
+ pci->bus = bus;
+ pci->devfn = devfn;
+
+ /* Skip all but the first function on
+ * non-multifunction cards
+ */
+ if ( PCI_FUNC ( devfn ) == 0 ) {
+ pci_read_config_byte ( pci, PCI_HEADER_TYPE,
+ &hdrtype );
+ } else if ( ! ( hdrtype & 0x80 ) ) {
+ continue;
+ }
+
+ /* Check for physical device presence */
+ pci_read_config_dword ( pci, PCI_VENDOR_ID, &tmp );
+ if ( ( tmp == 0xffffffff ) || ( tmp == 0 ) )
+ continue;
+
+ /* Populate struct pci_device */
+ pci->vendor = ( tmp & 0xffff );
+ pci->device = ( tmp >> 16 );
+ pci_read_config_dword ( pci, PCI_REVISION, &tmp );
+ pci->class = ( tmp >> 8 );
+ pci_read_config_byte ( pci, PCI_INTERRUPT_LINE,
+ &pci->irq );
+ pci_read_bases ( pci );
+
+ /* Add to device hierarchy */
+ snprintf ( pci->dev.name, sizeof ( pci->dev.name ),
+ "PCI%02x:%02x.%x", bus,
+ PCI_SLOT ( devfn ), PCI_FUNC ( devfn ) );
+ pci->dev.desc.bus_type = BUS_TYPE_PCI;
+ pci->dev.desc.location = PCI_BUSDEVFN (bus, devfn);
+ pci->dev.desc.vendor = pci->vendor;
+ pci->dev.desc.device = pci->device;
+ pci->dev.desc.class = pci->class;
+ pci->dev.desc.ioaddr = pci->ioaddr;
+ pci->dev.desc.irq = pci->irq;
+ pci->dev.parent = &rootdev->dev;
+ list_add ( &pci->dev.siblings, &rootdev->dev.children);
+ INIT_LIST_HEAD ( &pci->dev.children );
+
+ /* Look for a driver */
+ if ( pci_probe ( pci ) == 0 ) {
+ /* pcidev registered, we can drop our ref */
+ pci = NULL;
+ } else {
+ /* Not registered; re-use struct pci_device */
+ list_del ( &pci->dev.siblings );
+ }
+ }
+ }
+
+ free ( pci );
+ return 0;
+
+ err:
+ free ( pci );
+ pcibus_remove ( rootdev );
+ return rc;
+}
+
+/**
+ * Remove PCI root bus
+ *
+ * @v rootdev PCI bus root device
+ */
+static void pcibus_remove ( struct root_device *rootdev ) {
+ struct pci_device *pci;
+ struct pci_device *tmp;
+
+ list_for_each_entry_safe ( pci, tmp, &rootdev->dev.children,
+ dev.siblings ) {
+ pci_remove ( pci );
+ list_del ( &pci->dev.siblings );
+ free ( pci );
+ }
+}
+
+/** PCI bus root device driver */
+static struct root_driver pci_root_driver = {
+ .probe = pcibus_probe,
+ .remove = pcibus_remove,
+};
+
+/** PCI bus root device */
+struct root_device pci_root_device __root_device = {
+ .dev = { .name = "PCI" },
+ .driver = &pci_root_driver,
+};
diff --git a/gpxe/src/drivers/bus/pciextra.c b/gpxe/src/drivers/bus/pciextra.c
new file mode 100644
index 00000000..4603bcb9
--- /dev/null
+++ b/gpxe/src/drivers/bus/pciextra.c
@@ -0,0 +1,79 @@
+#include <stdint.h>
+#include <gpxe/pci.h>
+
+/**
+ * Look for a PCI capability
+ *
+ * @v pci PCI device to query
+ * @v cap Capability code
+ * @ret address Address of capability, or 0 if not found
+ *
+ * Determine whether or not a device supports a given PCI capability.
+ * Returns the address of the requested capability structure within
+ * the device's PCI configuration space, or 0 if the device does not
+ * support it.
+ */
+int pci_find_capability ( struct pci_device *pci, int cap ) {
+ uint16_t status;
+ uint8_t pos, id;
+ uint8_t hdr_type;
+ int ttl = 48;
+
+ pci_read_config_word ( pci, PCI_STATUS, &status );
+ if ( ! ( status & PCI_STATUS_CAP_LIST ) )
+ return 0;
+
+ pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdr_type );
+ switch ( hdr_type & 0x7F ) {
+ case PCI_HEADER_TYPE_NORMAL:
+ case PCI_HEADER_TYPE_BRIDGE:
+ default:
+ pci_read_config_byte ( pci, PCI_CAPABILITY_LIST, &pos );
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ pci_read_config_byte ( pci, PCI_CB_CAPABILITY_LIST, &pos );
+ break;
+ }
+ while ( ttl-- && pos >= 0x40 ) {
+ pos &= ~3;
+ pci_read_config_byte ( pci, pos + PCI_CAP_LIST_ID, &id );
+ DBG ( "PCI Capability: %d\n", id );
+ if ( id == 0xff )
+ break;
+ if ( id == cap )
+ return pos;
+ pci_read_config_byte ( pci, pos + PCI_CAP_LIST_NEXT, &pos );
+ }
+ return 0;
+}
+
+/**
+ * Find the size of a PCI BAR
+ *
+ * @v pci PCI device
+ * @v reg PCI register number
+ * @ret size BAR size
+ *
+ * It should not be necessary for any Etherboot code to call this
+ * function.
+ */
+unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) {
+ uint32_t start, size;
+
+ /* Save the original bar */
+ pci_read_config_dword ( pci, reg, &start );
+ /* Compute which bits can be set */
+ pci_write_config_dword ( pci, reg, ~0 );
+ pci_read_config_dword ( pci, reg, &size );
+ /* Restore the original size */
+ pci_write_config_dword ( pci, reg, start );
+ /* Find the significant bits */
+ if ( start & PCI_BASE_ADDRESS_SPACE_IO ) {
+ size &= PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ size &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ /* Find the lowest bit set */
+ size = size & ~( size - 1 );
+ return size;
+}
diff --git a/gpxe/src/drivers/infiniband/MT25218_PRM.h b/gpxe/src/drivers/infiniband/MT25218_PRM.h
new file mode 100644
index 00000000..19ca92cd
--- /dev/null
+++ b/gpxe/src/drivers/infiniband/MT25218_PRM.h
@@ -0,0 +1,3460 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+/***
+ *** This file was generated at "Tue Nov 22 15:21:23 2005"
+ *** by:
+ *** % csp_bf -copyright=/mswg/misc/license-header.txt -prefix arbelprm_ -bits -fixnames MT25218_PRM.csp
+ ***/
+
+#ifndef H_prefix_arbelprm_bits_fixnames_MT25218_PRM_csp_H
+#define H_prefix_arbelprm_bits_fixnames_MT25218_PRM_csp_H
+
+/* UD Address Vector */
+
+struct arbelprm_ud_address_vector_st { /* Little Endian */
+ pseudo_bit_t pd[0x00018]; /* Protection Domain */
+ pseudo_bit_t port_number[0x00002]; /* Port number
+ 1 - Port 1
+ 2 - Port 2
+ other - reserved */
+ pseudo_bit_t reserved0[0x00006];
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */
+ pseudo_bit_t my_lid_path_bits[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */
+ pseudo_bit_t g[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */
+ pseudo_bit_t max_stat_rate[0x00003];/* Maximum static rate control.
+ 0 - 4X injection rate
+ 1 - 1X injection rate
+ other - reserved
+ */
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t msg[0x00002]; /* Max Message size, size is 256*2^MSG bytes */
+ pseudo_bit_t reserved3[0x00002];
+ pseudo_bit_t mgid_index[0x00006]; /* Index to port GID table
+ mgid_index = (port_number-1) * 2^log_max_gid + gid_index
+ Where:
+ 1. log_max_gid is taken from QUERY_DEV_LIM command
+ 2. gid_index is the index to the GID table */
+ pseudo_bit_t reserved4[0x0000a];
+/* -------------- */
+ pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */
+ pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */
+ pseudo_bit_t sl[0x00004]; /* InfiniBand Service Level (SL) */
+/* -------------- */
+ pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */
+/* -------------- */
+ pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */
+/* -------------- */
+ pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */
+/* -------------- */
+ pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] if G bit is set. Must be set to 0x2 if G bit is cleared. */
+/* -------------- */
+};
+
+/* Send doorbell */
+
+struct arbelprm_send_doorbell_st { /* Little Endian */
+ pseudo_bit_t nopcode[0x00005]; /* Opcode of descriptor to be executed */
+ pseudo_bit_t f[0x00001]; /* Fence bit. If set, descriptor is fenced */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t wqe_counter[0x00010]; /* Modulo-64K counter of WQEs posted to the QP since its creation excluding the newly posted WQEs in this doorbell. Should be zero for the first doorbell on the QP */
+ pseudo_bit_t wqe_cnt[0x00008]; /* Number of WQEs posted with this doorbell. Must be grater then zero. */
+/* -------------- */
+ pseudo_bit_t nds[0x00006]; /* Next descriptor size (in 16-byte chunks) */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */
+/* -------------- */
+};
+
+/* ACCESS_LAM_inject_errors_input_modifier */
+
+struct arbelprm_access_lam_inject_errors_input_modifier_st { /* Little Endian */
+ pseudo_bit_t index3[0x00007];
+ pseudo_bit_t q3[0x00001];
+ pseudo_bit_t index2[0x00007];
+ pseudo_bit_t q2[0x00001];
+ pseudo_bit_t index1[0x00007];
+ pseudo_bit_t q1[0x00001];
+ pseudo_bit_t index0[0x00007];
+ pseudo_bit_t q0[0x00001];
+/* -------------- */
+};
+
+/* ACCESS_LAM_inject_errors_input_parameter */
+
+struct arbelprm_access_lam_inject_errors_input_parameter_st { /* Little Endian */
+ pseudo_bit_t ba[0x00002]; /* Bank Address */
+ pseudo_bit_t da[0x00002]; /* Dimm Address */
+ pseudo_bit_t reserved0[0x0001c];
+/* -------------- */
+ pseudo_bit_t ra[0x00010]; /* Row Address */
+ pseudo_bit_t ca[0x00010]; /* Column Address */
+/* -------------- */
+};
+
+/* */
+
+struct arbelprm_recv_wqe_segment_next_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00006];
+ pseudo_bit_t nda_31_6[0x0001a]; /* Next WQE address, low 32 bit. WQE address must be aligned to 64-byte boundary (6 LSB are forced ZERO). */
+/* -------------- */
+ pseudo_bit_t nds[0x00006]; /* Next WQE size in OctoWords (16 bytes).
+ Zero value in NDS field signals end of WQEs? chain.
+ */
+ pseudo_bit_t reserved1[0x0001a];
+/* -------------- */
+};
+
+/* Send wqe segment data inline */
+
+struct arbelprm_wqe_segment_data_inline_st { /* Little Endian */
+ pseudo_bit_t byte_count[0x0000a]; /* Not including padding for 16Byte chunks */
+ pseudo_bit_t reserved0[0x00015];
+ pseudo_bit_t always1[0x00001];
+/* -------------- */
+ pseudo_bit_t data[0x00018]; /* Data may be more this segment size - in 16Byte chunks */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+};
+
+/* Send wqe segment data ptr */
+
+struct arbelprm_wqe_segment_data_ptr_st { /* Little Endian */
+ pseudo_bit_t byte_count[0x0001f];
+ pseudo_bit_t always0[0x00001];
+/* -------------- */
+ pseudo_bit_t l_key[0x00020];
+/* -------------- */
+ pseudo_bit_t local_address_h[0x00020];
+/* -------------- */
+ pseudo_bit_t local_address_l[0x00020];
+/* -------------- */
+};
+
+/* Send wqe segment rd */
+
+struct arbelprm_local_invalidate_segment_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t mem_key[0x00018];
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved2[0x000a0];
+/* -------------- */
+};
+
+/* Fast_Registration_Segment */
+
+struct arbelprm_fast_registration_segment_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x0001b];
+ pseudo_bit_t lr[0x00001]; /* If set - Local Read access will be enabled */
+ pseudo_bit_t lw[0x00001]; /* If set - Local Write access will be enabled */
+ pseudo_bit_t rr[0x00001]; /* If set - Remote Read access will be enabled */
+ pseudo_bit_t rw[0x00001]; /* If set - Remote Write access will be enabled */
+ pseudo_bit_t a[0x00001]; /* If set - Remote Atomic access will be enabled */
+/* -------------- */
+ pseudo_bit_t pbl_ptr_63_32[0x00020];/* Physical address pointer [63:32] to the physical buffer list */
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* Memory Key on which the fast registration is executed on. */
+/* -------------- */
+ pseudo_bit_t page_size[0x00005]; /* Page size used for the region. Actual size is [4K]*2^Page_size bytes.
+ page_size should be less than 20. */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t zb[0x00001]; /* Zero Based Region */
+ pseudo_bit_t pbl_ptr_31_8[0x00018]; /* Physical address pointer [31:8] to the physical buffer list */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start Address[63:32] - Virtual Address where this region starts */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start Address[31:0] - Virtual Address where this region starts */
+/* -------------- */
+ pseudo_bit_t reg_len_h[0x00020]; /* Region Length[63:32] */
+/* -------------- */
+ pseudo_bit_t reg_len_l[0x00020]; /* Region Length[31:0] */
+/* -------------- */
+};
+
+/* Send wqe segment atomic */
+
+struct arbelprm_wqe_segment_atomic_st { /* Little Endian */
+ pseudo_bit_t swap_add_h[0x00020];
+/* -------------- */
+ pseudo_bit_t swap_add_l[0x00020];
+/* -------------- */
+ pseudo_bit_t compare_h[0x00020];
+/* -------------- */
+ pseudo_bit_t compare_l[0x00020];
+/* -------------- */
+};
+
+/* Send wqe segment remote address */
+
+struct arbelprm_wqe_segment_remote_address_st { /* Little Endian */
+ pseudo_bit_t remote_virt_addr_h[0x00020];
+/* -------------- */
+ pseudo_bit_t remote_virt_addr_l[0x00020];
+/* -------------- */
+ pseudo_bit_t rkey[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* end wqe segment bind */
+
+struct arbelprm_wqe_segment_bind_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x0001d];
+ pseudo_bit_t rr[0x00001]; /* If set, Remote Read Enable for bound window. */
+ pseudo_bit_t rw[0x00001]; /* If set, Remote Write Enable for bound window.
+ */
+ pseudo_bit_t a[0x00001]; /* If set, Atomic Enable for bound window. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x0001e];
+ pseudo_bit_t zb[0x00001]; /* If set, Window is Zero Based. */
+ pseudo_bit_t type[0x00001]; /* Window type.
+ 0 - Type one window
+ 1 - Type two window
+ */
+/* -------------- */
+ pseudo_bit_t new_rkey[0x00020]; /* The new RKey of window to bind */
+/* -------------- */
+ pseudo_bit_t region_lkey[0x00020]; /* Local key of region, which window will be bound to */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];
+/* -------------- */
+ pseudo_bit_t length_h[0x00020];
+/* -------------- */
+ pseudo_bit_t length_l[0x00020];
+/* -------------- */
+};
+
+/* Send wqe segment ud */
+
+struct arbelprm_wqe_segment_ud_st { /* Little Endian */
+ struct arbelprm_ud_address_vector_st ud_address_vector;/* UD Address Vector */
+/* -------------- */
+ pseudo_bit_t destination_qp[0x00018];
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t q_key[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+};
+
+/* Send wqe segment rd */
+
+struct arbelprm_wqe_segment_rd_st { /* Little Endian */
+ pseudo_bit_t destination_qp[0x00018];
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t q_key[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+};
+
+/* Send wqe segment ctrl */
+
+struct arbelprm_wqe_segment_ctrl_send_st { /* Little Endian */
+ pseudo_bit_t always1[0x00001];
+ pseudo_bit_t s[0x00001]; /* Solicited Event bit. If set, SE (Solicited Event) bit is set in the (last packet of) message. */
+ pseudo_bit_t e[0x00001]; /* Event bit. If set, event is generated upon WQE?s completion, if QP is allowed to generate an event. Every WQE with E-bit set generates an event. The C bit must be set on unsignalled QPs if the E bit is set. */
+ pseudo_bit_t c[0x00001]; /* Completion Queue bit. Valid for unsignalled QPs only. If set, the CQ is updated upon WQE?s completion */
+ pseudo_bit_t ip[0x00001]; /* When set, InfiniHost III Ex will calculate the IP checksum of the IP header that is present immediately after the IPoverIB encapsulation header. In the case of multiple headers (encapsulation), InfiniHost III Ex will calculate the checksum only for the first IP header following the IPoverIB encapsulation header. Not Valid for IPv6 packets */
+ pseudo_bit_t tcp_udp[0x00001]; /* When set, InfiniHost III Ex will calculate the TCP/UDP checksum of the packet that is present immediately after the IP header. In the case of multiple headers (encapsulation), InfiniHost III Ex will calculate the checksum only for the first TCP header following the IP header. This bit may be set only if the entire TCP/UDP segment is present in one IB packet */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t so[0x00001]; /* Strong Ordering - when set, the WQE will be executed only after all previous WQEs have been executed. Can be set for RC WQEs only. This bit must be set in type two BIND, Fast Registration and Local invalidate operations. */
+ pseudo_bit_t reserved1[0x00018];
+/* -------------- */
+ pseudo_bit_t immediate[0x00020]; /* If the OpCode encodes an operation with Immediate (RDMA-write/SEND), This field will hold the Immediate data to be sent. If the OpCode encodes send and invalidate operations, this field holds the Invalidation key to be inserted into the packet; otherwise, this field is reserved. */
+/* -------------- */
+};
+
+/* Send wqe segment next */
+
+struct arbelprm_wqe_segment_next_st { /* Little Endian */
+ pseudo_bit_t nopcode[0x00005]; /* Next Opcode: OpCode to be used in the next WQE. Encodes the type of operation to be executed on the QP:
+ ?00000? - NOP. WQE with this opcode creates a completion, but does nothing else
+ ?01000? - RDMA-write
+ ?01001? - RDMA-Write with Immediate
+ ?10000? - RDMA-read
+ ?10001? - Atomic Compare & swap
+ ?10010? - Atomic Fetch & Add
+ ?11000? - Bind memory window
+
+ The encoding for the following operations depends on the QP type:
+ For RC, UC and RD QP:
+ ?01010? - SEND
+ ?01011? - SEND with Immediate
+
+ For UD QP:
+ the encoding depends on the values of bit[31] of the Q_key field in the Datagram Segment (see Table 39, ?Unreliable Datagram Segment Format - Pointers,? on page 101) of
+ both the current WQE and the next WQE, as follows:
+
+ If the last WQE Q_Key bit[31] is clear and the next WQE Q_key bit[31] is set :
+ ?01000? - SEND
+ ?01001? - SEND with Immediate
+
+ otherwise (if the next WQE Q_key bit[31] is cleared, or the last WQE Q_Key bit[31] is set):
+ ?01010? - SEND
+ ?01011? - SEND with Immediate
+
+ All other opcode values are RESERVED, and will result in invalid operation execution. */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t nda_31_6[0x0001a]; /* Next WQE address, low 32 bit. WQE address must be aligned to 64-byte boundary (6 LSB are forced ZERO). */
+/* -------------- */
+ pseudo_bit_t nds[0x00006]; /* Next WQE size in OctoWords (16 bytes).
+ Zero value in NDS field signals end of WQEs? chain.
+ */
+ pseudo_bit_t f[0x00001]; /* Fence bit. If set, next WQE will start execution only after all previous Read/Atomic WQEs complete. */
+ pseudo_bit_t always1[0x00001];
+ pseudo_bit_t reserved1[0x00018];
+/* -------------- */
+};
+
+/* Address Path */
+
+struct arbelprm_address_path_st { /* Little Endian */
+ pseudo_bit_t pkey_index[0x00007]; /* PKey table index */
+ pseudo_bit_t reserved0[0x00011];
+ pseudo_bit_t port_number[0x00002]; /* Specific port associated with this QP/EE.
+ 1 - Port 1
+ 2 - Port 2
+ other - reserved */
+ pseudo_bit_t reserved1[0x00006];
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */
+ pseudo_bit_t my_lid_path_bits[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */
+ pseudo_bit_t g[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */
+ pseudo_bit_t reserved2[0x00005];
+ pseudo_bit_t rnr_retry[0x00003]; /* RNR retry count (see C9-132 in IB spec Vol 1)
+ 0-6 - number of retries
+ 7 - infinite */
+/* -------------- */
+ pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */
+ pseudo_bit_t max_stat_rate[0x00003];/* Maximum static rate control.
+ 0 - 100% injection rate
+ 1 - 25% injection rate
+ 2 - 12.5% injection rate
+ 3 - 50% injection rate
+ other - reserved */
+ pseudo_bit_t reserved3[0x00005];
+ pseudo_bit_t mgid_index[0x00006]; /* Index to port GID table */
+ pseudo_bit_t reserved4[0x00005];
+ pseudo_bit_t ack_timeout[0x00005]; /* Local ACK timeout - Transport timer for activation of retransmission mechanism. Refer to IB spec Vol1 9.7.6.1.3 for further details.
+ The transport timer is set to 4.096us*2^ack_timeout, if ack_timeout is 0 then transport timer is disabled. */
+/* -------------- */
+ pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */
+ pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */
+ pseudo_bit_t sl[0x00004]; /* InfiniBand Service Level (SL) */
+/* -------------- */
+ pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */
+/* -------------- */
+ pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */
+/* -------------- */
+ pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */
+/* -------------- */
+ pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] */
+/* -------------- */
+};
+
+/* HCA Command Register (HCR) */
+
+struct arbelprm_hca_command_register_st { /* Little Endian */
+ pseudo_bit_t in_param_h[0x00020]; /* Input Parameter: parameter[63:32] or pointer[63:32] to input mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t in_param_l[0x00020]; /* Input Parameter: parameter[31:0] or pointer[31:0] to input mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t input_modifier[0x00020];/* Input Parameter Modifier */
+/* -------------- */
+ pseudo_bit_t out_param_h[0x00020]; /* Output Parameter: parameter[63:32] or pointer[63:32] to output mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t out_param_l[0x00020]; /* Output Parameter: parameter[31:0] or pointer[31:0] to output mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00010];
+ pseudo_bit_t token[0x00010]; /* Software assigned token to the command, to uniquely identify it. The token is returned to the software in the EQE reported. */
+/* -------------- */
+ pseudo_bit_t opcode[0x0000c]; /* Command opcode */
+ pseudo_bit_t opcode_modifier[0x00004];/* Opcode Modifier, see specific description for each command. */
+ pseudo_bit_t reserved1[0x00006];
+ pseudo_bit_t e[0x00001]; /* Event Request
+ 0 - Don't report event (software will poll the GO bit)
+ 1 - Report event to EQ when the command completes */
+ pseudo_bit_t go[0x00001]; /* Go (0=Software ownership for the HCR, 1=Hardware ownership for the HCR)
+ Software can write to the HCR only if Go bit is cleared.
+ Software must set the Go bit to trigger the HW to execute the command. Software must not write to this register value other than 1 for the Go bit. */
+ pseudo_bit_t status[0x00008]; /* Command execution status report. Valid only if command interface in under SW ownership (Go bit is cleared)
+ 0 - command completed without error. If different than zero, command execution completed with error. Syndrom encoding is depended on command executed and is defined for each command */
+/* -------------- */
+};
+
+/* CQ Doorbell */
+
+struct arbelprm_cq_cmd_doorbell_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number accessed */
+ pseudo_bit_t cmd[0x00003]; /* Command to be executed on CQ
+ 0x0 - Reserved
+ 0x1 - Request notification for next Solicited completion event. CQ_param specifies the current CQ Consumer Counter.
+ 0x2 - Request notification for next Solicited or Unsolicited completion event. CQ_param specifies the current CQ Consumer Counter.
+ 0x3 - Request notification for multiple completions (Arm-N). CQ_param specifies the value of the CQ Counter that when reached by HW (i.e. HW generates a CQE into this Counter) Event will be generated
+ Other - Reserved */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t cmd_sn[0x00002]; /* Command Sequence Number - This field should be incremented upon receiving completion notification of the respective CQ.
+ This transition is done by ringing Request notification for next Solicited, Request notification for next Solicited or Unsolicited
+ completion or Request notification for multiple completions doorbells after receiving completion notification.
+ This field is initialized to Zero */
+ pseudo_bit_t reserved1[0x00002];
+/* -------------- */
+ pseudo_bit_t cq_param[0x00020]; /* parameter to be used by CQ command */
+/* -------------- */
+};
+
+/* RD-send doorbell */
+
+struct arbelprm_rd_send_doorbell_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t een[0x00018]; /* End-to-end context number (reliable datagram)
+ Must be zero for Nop and Bind operations */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00008];
+ pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */
+/* -------------- */
+ struct arbelprm_send_doorbell_st send_doorbell;/* Send Parameters */
+/* -------------- */
+};
+
+/* Multicast Group Member QP */
+
+struct arbelprm_mgmqp_st { /* Little Endian */
+ pseudo_bit_t qpn_i[0x00018]; /* QPN_i: QP number which is a member in this multicast group. Valid only if Qi bit is set. Length of the QPN_i list is set in INIT_HCA */
+ pseudo_bit_t reserved0[0x00007];
+ pseudo_bit_t qi[0x00001]; /* Qi: QPN_i is valid */
+/* -------------- */
+};
+
+/* vsd */
+
+struct arbelprm_vsd_st { /* Little Endian */
+ pseudo_bit_t vsd_dw0[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw1[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw2[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw3[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw4[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw5[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw6[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw7[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw8[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw9[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw10[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw11[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw12[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw13[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw14[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw15[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw16[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw17[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw18[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw19[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw20[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw21[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw22[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw23[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw24[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw25[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw26[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw27[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw28[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw29[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw30[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw31[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw32[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw33[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw34[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw35[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw36[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw37[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw38[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw39[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw40[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw41[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw42[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw43[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw44[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw45[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw46[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw47[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw48[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw49[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw50[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw51[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw52[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw53[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw54[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw55[0x00020];
+/* -------------- */
+};
+
+/* ACCESS_LAM_inject_errors */
+
+struct arbelprm_access_lam_inject_errors_st { /* Little Endian */
+ struct arbelprm_access_lam_inject_errors_input_parameter_st access_lam_inject_errors_input_parameter;
+/* -------------- */
+ struct arbelprm_access_lam_inject_errors_input_modifier_st access_lam_inject_errors_input_modifier;
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* Logical DIMM Information */
+
+struct arbelprm_dimminfo_st { /* Little Endian */
+ pseudo_bit_t dimmsize[0x00010]; /* Size of DIMM in units of 2^20 Bytes. This value is valid only when DIMMStatus is 0. */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t dimmstatus[0x00001]; /* DIMM Status
+ 0 - Enabled
+ 1 - Disabled
+ */
+ pseudo_bit_t dh[0x00001]; /* When set, the DIMM is Hidden and can not be accessed from the PCI bus. */
+ pseudo_bit_t wo[0x00001]; /* When set, the DIMM is write only.
+ If data integrity is configured (other than none), the DIMM must be
+ only targeted by write transactions where the address and size are multiples of 16 bytes. */
+ pseudo_bit_t reserved1[0x00005];
+/* -------------- */
+ pseudo_bit_t spd[0x00001]; /* 0 - DIMM SPD was read from DIMM
+ 1 - DIMM SPD was read from InfiniHost-III-EX NVMEM */
+ pseudo_bit_t sladr[0x00003]; /* SPD Slave Address 3 LSBits.
+ Valid only if spd bit is 0. */
+ pseudo_bit_t sock_num[0x00002]; /* DIMM socket number (for double sided DIMM one of the two numbers will be reported) */
+ pseudo_bit_t syn[0x00004]; /* Error syndrome (valid regardless of status value)
+ 0 - DIMM has no error
+ 1 - SPD error (e.g. checksum error, no response, error while reading)
+ 2 - DIMM out of bounds (e.g. DIMM rows number is not between 7 and 14, DIMM type is not 2)
+ 3 - DIMM conflict (e.g. mix of registered and unbuffered DIMMs, CAS latency conflict)
+ 5 - DIMM size trimmed due to configuration (size exceeds)
+ other - Error, reserved
+ */
+ pseudo_bit_t reserved2[0x00016];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00040];
+/* -------------- */
+ pseudo_bit_t dimm_start_adr_h[0x00020];/* DIMM memory start address [63:32]. This value is valid only when DIMMStatus is 0. */
+/* -------------- */
+ pseudo_bit_t dimm_start_adr_l[0x00020];/* DIMM memory start address [31:0]. This value is valid only when DIMMStatus is 0. */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00040];
+/* -------------- */
+};
+
+/* UAR Parameters */
+
+struct arbelprm_uar_params_st { /* Little Endian */
+ pseudo_bit_t uar_base_addr_h[0x00020];/* UAR Base (pyhsical) Address [63:32] (QUERY_HCA only) */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00014];
+ pseudo_bit_t uar_base_addr_l[0x0000c];/* UAR Base (pyhsical) Address [31:20] (QUERY_HCA only) */
+/* -------------- */
+ pseudo_bit_t uar_page_sz[0x00008]; /* This field defines the size of each UAR page.
+ Size of UAR Page is 4KB*2^UAR_Page_Size */
+ pseudo_bit_t log_max_uars[0x00004]; /* Number of UARs supported is 2^log_max_UARs */
+ pseudo_bit_t reserved1[0x00004];
+ pseudo_bit_t log_uar_entry_sz[0x00006];/* Size of UAR Context entry is 2^log_uar_sz in 4KByte pages */
+ pseudo_bit_t reserved2[0x0000a];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t uar_scratch_base_addr_h[0x00020];/* Base address of UAR scratchpad [63:32].
+ Number of entries in table is 2^log_max_uars.
+ Table must be aligned to its size */
+/* -------------- */
+ pseudo_bit_t uar_scratch_base_addr_l[0x00020];/* Base address of UAR scratchpad [31:0].
+ Number of entries in table is 2^log_max_uars.
+ Table must be aligned to its size. */
+/* -------------- */
+ pseudo_bit_t uar_context_base_addr_h[0x00020];/* Base address of UAR Context [63:32].
+ Number of entries in table is 2^log_max_uars.
+ Table must be aligned to its size. */
+/* -------------- */
+ pseudo_bit_t uar_context_base_addr_l[0x00020];/* Base address of UAR Context [31:0].
+ Number of entries in table is 2^log_max_uars.
+ Table must be aligned to its size. */
+/* -------------- */
+};
+
+/* Translation and Protection Tables Parameters */
+
+struct arbelprm_tptparams_st { /* Little Endian */
+ pseudo_bit_t mpt_base_adr_h[0x00020];/* MPT - Memory Protection Table base physical address [63:32].
+ Entry size is 64 bytes.
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t mpt_base_adr_l[0x00020];/* MPT - Memory Protection Table base physical address [31:0].
+ Entry size is 64 bytes.
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t log_mpt_sz[0x00006]; /* Log (base 2) of the number of region/windows entries in the MPT table. */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t pfto[0x00005]; /* Page Fault RNR Timeout -
+ The field returned in RNR Naks generated when a page fault is detected.
+ It has no effect when on-demand-paging is not used. */
+ pseudo_bit_t reserved1[0x00013];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00020];
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_h[0x00020];/* MTT - Memory Translation table base physical address [63:32].
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_l[0x00020];/* MTT - Memory Translation table base physical address [31:0].
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00040];
+/* -------------- */
+};
+
+/* Multicast Support Parameters */
+
+struct arbelprm_multicastparam_st { /* Little Endian */
+ pseudo_bit_t mc_base_addr_h[0x00020];/* Base Address of the Multicast Table [63:32].
+ The base address must be aligned to the entry size.
+ Address may be set to 0xFFFFFFFF if multicast is not supported. */
+/* -------------- */
+ pseudo_bit_t mc_base_addr_l[0x00020];/* Base Address of the Multicast Table [31:0].
+ The base address must be aligned to the entry size.
+ Address may be set to 0xFFFFFFFF if multicast is not supported. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t log_mc_table_entry_sz[0x00010];/* Log2 of the Size of multicast group member (MGM) entry.
+ Must be greater than 5 (to allow CTRL and GID sections).
+ That implies the number of QPs per MC table entry. */
+ pseudo_bit_t reserved1[0x00010];
+/* -------------- */
+ pseudo_bit_t mc_table_hash_sz[0x00011];/* Number of entries in multicast DGID hash table (must be power of 2)
+ INIT_HCA - the required number of entries
+ QUERY_HCA - the actual number of entries assigned by firmware (will be less than or equal to the amount required in INIT_HCA) */
+ pseudo_bit_t reserved2[0x0000f];
+/* -------------- */
+ pseudo_bit_t log_mc_table_sz[0x00005];/* Log2 of the overall number of MC entries in the MCG table (includes both hash and auxiliary tables) */
+ pseudo_bit_t reserved3[0x00013];
+ pseudo_bit_t mc_hash_fn[0x00003]; /* Multicast hash function
+ 0 - Default hash function
+ other - reserved */
+ pseudo_bit_t reserved4[0x00005];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+};
+
+/* QPC/EEC/CQC/EQC/RDB Parameters */
+
+struct arbelprm_qpcbaseaddr_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t qpc_base_addr_h[0x00020];/* QPC Base Address [63:32]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t log_num_of_qp[0x00005];/* Log base 2 of number of supported QPs */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t qpc_base_addr_l[0x00019];/* QPC Base Address [31:7]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t eec_base_addr_h[0x00020];/* EEC Base Address [63:32]
+ Table must be aligned on its size.
+ Address may be set to 0xFFFFFFFF if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t log_num_of_ee[0x00005];/* Log base 2 of number of supported EEs. */
+ pseudo_bit_t reserved3[0x00002];
+ pseudo_bit_t eec_base_addr_l[0x00019];/* EEC Base Address [31:7]
+ Table must be aligned on its size
+ Address may be set to 0xFFFFFFFF if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t srqc_base_addr_h[0x00020];/* SRQ Context Base Address [63:32]
+ Table must be aligned on its size
+ Address may be set to 0xFFFFFFFF if SRQ is not supported. */
+/* -------------- */
+ pseudo_bit_t log_num_of_srq[0x00005];/* Log base 2 of number of supported SRQs. */
+ pseudo_bit_t srqc_base_addr_l[0x0001b];/* SRQ Context Base Address [31:5]
+ Table must be aligned on its size
+ Address may be set to 0xFFFFFFFF if SRQ is not supported. */
+/* -------------- */
+ pseudo_bit_t cqc_base_addr_h[0x00020];/* CQC Base Address [63:32]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t log_num_of_cq[0x00005];/* Log base 2 of number of supported CQs. */
+ pseudo_bit_t reserved4[0x00001];
+ pseudo_bit_t cqc_base_addr_l[0x0001a];/* CQC Base Address [31:6]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00040];
+/* -------------- */
+ pseudo_bit_t eqpc_base_addr_h[0x00020];/* Extended QPC Base Address [63:32]
+ Table has same number of entries as QPC table.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t eqpc_base_addr_l[0x00020];/* Extended QPC Base Address [31:0]
+ Table has same number of entries as QPC table.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00040];
+/* -------------- */
+ pseudo_bit_t eeec_base_addr_h[0x00020];/* Extended EEC Base Address [63:32]
+ Table has same number of entries as EEC table.
+ Table must be aligned to entry size.
+ Address may be set to 0xFFFFFFFF if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t eeec_base_addr_l[0x00020];/* Extended EEC Base Address [31:0]
+ Table has same number of entries as EEC table.
+ Table must be aligned to entry size.
+ Address may be set to 0xFFFFFFFF if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00040];
+/* -------------- */
+ pseudo_bit_t eqc_base_addr_h[0x00020];/* EQC Base Address [63:32]
+ Address may be set to 0xFFFFFFFF if EQs are not supported.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t log_num_eq[0x00004]; /* Log base 2 of number of supported EQs.
+ Must be 6 or less in InfiniHost-III-EX. */
+ pseudo_bit_t reserved8[0x00002];
+ pseudo_bit_t eqc_base_addr_l[0x0001a];/* EQC Base Address [31:6]
+ Address may be set to 0xFFFFFFFF if EQs are not supported.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00040];
+/* -------------- */
+ pseudo_bit_t rdb_base_addr_h[0x00020];/* Base address of table that holds remote read and remote atomic requests [63:32].
+ Address may be set to 0xFFFFFFFF if remote RDMA reads are not supported.
+ Please refer to QP and EE chapter for further explanation on RDB allocation. */
+/* -------------- */
+ pseudo_bit_t rdb_base_addr_l[0x00020];/* Base address of table that holds remote read and remote atomic requests [31:0].
+ Table must be aligned to RDB entry size (32 bytes).
+ Address may be set to zero if remote RDMA reads are not supported.
+ Please refer to QP and EE chapter for further explanation on RDB allocation. */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00040];
+/* -------------- */
+};
+
+/* Header_Log_Register */
+
+struct arbelprm_header_log_register_st { /* Little Endian */
+ pseudo_bit_t place_holder[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved0[0x00060];
+/* -------------- */
+};
+
+/* Performance Monitors */
+
+struct arbelprm_performance_monitors_st { /* Little Endian */
+ pseudo_bit_t e0[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t e1[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t e2[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t r0[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t r1[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t r2[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t reserved1[0x00001];
+ pseudo_bit_t i0[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t i1[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t i2[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t f0[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t f1[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t f2[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t reserved3[0x00001];
+ pseudo_bit_t ev_cnt1[0x00005]; /* Specifies event to be counted by Event_counter1 See XXX for events' definition. */
+ pseudo_bit_t reserved4[0x00003];
+ pseudo_bit_t ev_cnt2[0x00005]; /* Specifies event to be counted by Event_counter2 See XXX for events' definition. */
+ pseudo_bit_t reserved5[0x00003];
+/* -------------- */
+ pseudo_bit_t clock_counter[0x00020];
+/* -------------- */
+ pseudo_bit_t event_counter1[0x00020];
+/* -------------- */
+ pseudo_bit_t event_counter2[0x00020];/* Read/write event counter, counting events specified by EvCntl and EvCnt2 fields repsectively. When the event counter reaches is maximum value of 0xFFFFFF, the next event will cause it to roll over to zero, set F1 or F2 bit respectively and generate interrupt by I1 I2 bit respectively. */
+/* -------------- */
+};
+
+/* Receive segment format */
+
+struct arbelprm_wqe_segment_ctrl_recv_st { /* Little Endian */
+ struct arbelprm_recv_wqe_segment_next_st wqe_segment_next;
+/* -------------- */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t reserved1[0x00001];
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t reserved3[0x0001c];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+};
+
+/* MLX WQE segment format */
+
+struct arbelprm_wqe_segment_ctrl_mlx_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t e[0x00001]; /* WQE event */
+ pseudo_bit_t c[0x00001]; /* Create CQE (for "requested signalling" QP) */
+ pseudo_bit_t icrc[0x00002]; /* icrc field detemines what to do with the last dword of the packet: 0 - Calculate ICRC and put it instead of last dword. Last dword must be 0x0. 1,2 - reserved. 3 - Leave last dword as is. Last dword must not be 0x0. */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t sl[0x00004];
+ pseudo_bit_t max_statrate[0x00004];
+ pseudo_bit_t slr[0x00001]; /* 0= take slid from port. 1= take slid from given headers */
+ pseudo_bit_t v15[0x00001]; /* Send packet over VL15 */
+ pseudo_bit_t reserved2[0x0000e];
+/* -------------- */
+ pseudo_bit_t vcrc[0x00010]; /* Packet's VCRC (if not 0 - otherwise computed by HW) */
+ pseudo_bit_t rlid[0x00010]; /* Destination LID (must match given headers) */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00040];
+/* -------------- */
+};
+
+/* Send WQE segment format */
+
+struct arbelprm_send_wqe_segment_st { /* Little Endian */
+ struct arbelprm_wqe_segment_next_st wqe_segment_next;/* Send wqe segment next */
+/* -------------- */
+ struct arbelprm_wqe_segment_ctrl_send_st wqe_segment_ctrl_send;/* Send wqe segment ctrl */
+/* -------------- */
+ struct arbelprm_wqe_segment_rd_st wqe_segment_rd;/* Send wqe segment rd */
+/* -------------- */
+ struct arbelprm_wqe_segment_ud_st wqe_segment_ud;/* Send wqe segment ud */
+/* -------------- */
+ struct arbelprm_wqe_segment_bind_st wqe_segment_bind;/* Send wqe segment bind */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00180];
+/* -------------- */
+ struct arbelprm_wqe_segment_remote_address_st wqe_segment_remote_address;/* Send wqe segment remote address */
+/* -------------- */
+ struct arbelprm_wqe_segment_atomic_st wqe_segment_atomic;/* Send wqe segment atomic */
+/* -------------- */
+ struct arbelprm_fast_registration_segment_st fast_registration_segment;/* Fast Registration Segment */
+/* -------------- */
+ struct arbelprm_local_invalidate_segment_st local_invalidate_segment;/* local invalidate segment */
+/* -------------- */
+ struct arbelprm_wqe_segment_data_ptr_st wqe_segment_data_ptr;/* Send wqe segment data ptr */
+/* -------------- */
+ struct arbelprm_wqe_segment_data_inline_st wqe_segment_data_inline;/* Send wqe segment data inline */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00200];
+/* -------------- */
+};
+
+/* QP and EE Context Entry */
+
+struct arbelprm_queue_pair_ee_context_entry_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t de[0x00001]; /* Send/Receive Descriptor Event enable - if set, events can be generated upon descriptors' completion on send/receive queue (controlled by E bit in WQE). Invalid in EE context */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t pm_state[0x00002]; /* Path migration state (Migrated, Armed or Rearm)
+ 11-Migrated
+ 00-Armed
+ 01-Rearm
+ 10-Reserved
+ Should be set to 11 for UD QPs and for QPs which do not support APM */
+ pseudo_bit_t reserved2[0x00003];
+ pseudo_bit_t st[0x00003]; /* Service type (invalid in EE context):
+ 000-Reliable Connection
+ 001-Unreliable Connection
+ 010-Reliable Datagram
+ 011-Unreliable Datagram
+ 111-MLX transport (raw bits injection). Used for management QPs and RAW */
+ pseudo_bit_t reserved3[0x00009];
+ pseudo_bit_t state[0x00004]; /* QP/EE state:
+ 0 - RST
+ 1 - INIT
+ 2 - RTR
+ 3 - RTS
+ 4 - SQEr
+ 5 - SQD (Send Queue Drained)
+ 6 - ERR
+ 7 - Send Queue Draining
+ 8 - Reserved
+ 9 - Suspended
+ A- F - Reserved
+ (Valid for QUERY_QPEE and ERR2RST_QPEE commands only) */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t sched_queue[0x00004]; /* Schedule queue to be used for WQE scheduling to execution. Determines QOS for this QP. */
+ pseudo_bit_t rlky[0x00001]; /* When set this QP can use the Reserved L_Key */
+ pseudo_bit_t reserved5[0x00003];
+ pseudo_bit_t log_sq_stride[0x00003];/* Stride on the send queue. WQ entry is 16*(2^log_SQ_stride) bytes.
+ Stride must be equal or bigger then 64 bytes (minimum log_RQ_stride value allowed is 2). */
+ pseudo_bit_t log_sq_size[0x00004]; /* Log2 of the Number of WQEs in the Send Queue. */
+ pseudo_bit_t reserved6[0x00001];
+ pseudo_bit_t log_rq_stride[0x00003];/* Stride on the receive queue. WQ entry is 16*(2^log_RQ_stride) bytes.
+ Stride must be equal or bigger then 64 bytes (minimum log_RQ_stride value allowed is 2). */
+ pseudo_bit_t log_rq_size[0x00004]; /* Log2 of the Number of WQEs in the Receive Queue. */
+ pseudo_bit_t reserved7[0x00001];
+ pseudo_bit_t msg_max[0x00005]; /* Max message size allowed on the QP. Maximum message size is 2^msg_Max.
+ Must be equal to MTU for UD and MLX QPs. */
+ pseudo_bit_t mtu[0x00003]; /* MTU of the QP (Must be the same for both paths: primary and alternative):
+ 0x1 - 256 bytes
+ 0x2 - 512
+ 0x3 - 1024
+ 0x4 - 2048
+ other - reserved
+
+ Should be configured to 0x4 for UD and MLX QPs. */
+/* -------------- */
+ pseudo_bit_t usr_page[0x00018]; /* QP (see "non_privileged Access to the HCA Hardware"). Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved8[0x00008];
+/* -------------- */
+ pseudo_bit_t local_qpn_een[0x00018];/* Local QP/EE number Lower bits determine position of this record in QPC table, and - thus - constrained
+ This field is valid for QUERY and ERR2RST commands only. */
+ pseudo_bit_t reserved9[0x00008];
+/* -------------- */
+ pseudo_bit_t remote_qpn_een[0x00018];/* Remote QP/EE number */
+ pseudo_bit_t reserved10[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved11[0x00040];
+/* -------------- */
+ struct arbelprm_address_path_st primary_address_path;/* Primary address path for the QP/EE */
+/* -------------- */
+ struct arbelprm_address_path_st alternative_address_path;/* Alternate address path for the QP/EE */
+/* -------------- */
+ pseudo_bit_t rdd[0x00018]; /* Reliable Datagram Domain */
+ pseudo_bit_t reserved12[0x00008];
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* QP protection domain. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved13[0x00008];
+/* -------------- */
+ pseudo_bit_t wqe_base_adr_h[0x00020];/* Bits 63:32 of WQE address for both SQ and RQ.
+ Reserved for EE context. */
+/* -------------- */
+ pseudo_bit_t wqe_lkey[0x00020]; /* memory key (L-Key) to be used to access WQEs. Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t reserved14[0x00003];
+ pseudo_bit_t ssc[0x00001]; /* Send Signaled Completion
+ 1 - all send WQEs generate CQEs.
+ 0 - only send WQEs with C bit set generate completion.
+ Not valid (reserved) in EE context. */
+ pseudo_bit_t sic[0x00001]; /* If set - Ignore end to end credits on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t cur_retry_cnt[0x00003];/* Current transport retry counter (QUERY_QPEE only).
+ The current transport retry counter can vary from retry_count down to 1, where 1 means that the last retry attempt is currently executing. */
+ pseudo_bit_t cur_rnr_retry[0x00003];/* Current RNR retry counter (QUERY_QPEE only).
+ The current RNR retry counter can vary from rnr_retry to 1, where 1 means that the last retry attempt is currently executing. */
+ pseudo_bit_t fre[0x00001]; /* Fast Registration Work Request Enabled. (Reserved for EE) */
+ pseudo_bit_t reserved15[0x00001];
+ pseudo_bit_t sae[0x00001]; /* If set - Atomic operations enabled on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t swe[0x00001]; /* If set - RDMA - write enabled on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t sre[0x00001]; /* If set - RDMA - read enabled on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t retry_count[0x00003]; /* Transport timeout Retry count */
+ pseudo_bit_t reserved16[0x00002];
+ pseudo_bit_t sra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed in the send queue. Maximum number is 2^SRA_Max. Must be zero in EE context. */
+ pseudo_bit_t flight_lim[0x00004]; /* Number of outstanding (in-flight) messages on the wire allowed for this send queue.
+ Number of outstanding messages is 2^Flight_Lim.
+ Use 0xF for unlimited number of outstanding messages. */
+ pseudo_bit_t ack_req_freq[0x00004]; /* ACK required frequency. ACK required bit will be set in every 2^AckReqFreq packets at least. Not valid for RD QP. */
+/* -------------- */
+ pseudo_bit_t reserved17[0x00020];
+/* -------------- */
+ pseudo_bit_t next_send_psn[0x00018];/* Next PSN to be sent */
+ pseudo_bit_t reserved18[0x00008];
+/* -------------- */
+ pseudo_bit_t cqn_snd[0x00018]; /* CQ number completions from the send queue to be reported to. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved19[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved20[0x00006];
+ pseudo_bit_t snd_wqe_base_adr_l[0x0001a];/* While opening (creating) the WQ, this field should contain the address of first descriptor to be posted. Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t snd_db_record_index[0x00020];/* Index in the UAR Context Table Entry.
+ HW uses this index as an offset from the UAR Context Table Entry in order to read this SQ doorbell record.
+ The entry is obtained via the usr_page field.
+ Not valid for EE. */
+/* -------------- */
+ pseudo_bit_t last_acked_psn[0x00018];/* The last acknowledged PSN for the requester (QUERY_QPEE only) */
+ pseudo_bit_t reserved21[0x00008];
+/* -------------- */
+ pseudo_bit_t ssn[0x00018]; /* Requester Send Sequence Number (QUERY_QPEE only) */
+ pseudo_bit_t reserved22[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved23[0x00003];
+ pseudo_bit_t rsc[0x00001]; /* 1 - all receive WQEs generate CQEs.
+ 0 - only receive WQEs with C bit set generate completion.
+ Not valid (reserved) in EE context.
+ */
+ pseudo_bit_t ric[0x00001]; /* Invalid Credits.
+ 1 - place "Invalid Credits" to ACKs sent from this queue.
+ 0 - ACKs report the actual number of end to end credits on the connection.
+ Not valid (reserved) in EE context.
+ Must be set to 1 on QPs which are attached to SRQ. */
+ pseudo_bit_t reserved24[0x00008];
+ pseudo_bit_t rae[0x00001]; /* If set - Atomic operations enabled. on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t rwe[0x00001]; /* If set - RDMA - write enabled on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t rre[0x00001]; /* If set - RDMA - read enabled on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved25[0x00005];
+ pseudo_bit_t rra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed on receive queue is 2^RRA_Max.
+ Must be 0 for EE context. */
+ pseudo_bit_t reserved26[0x00008];
+/* -------------- */
+ pseudo_bit_t next_rcv_psn[0x00018]; /* Next (expected) PSN on receive */
+ pseudo_bit_t min_rnr_nak[0x00005]; /* Minimum RNR NAK timer value (TTTTT field encoding according to the IB spec Vol1 9.7.5.2.8).
+ Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved27[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved28[0x00005];
+ pseudo_bit_t ra_buff_indx[0x0001b]; /* Index to outstanding read/atomic buffer.
+ This field constructs the address to the RDB for maintaining the incoming RDMA read and atomic requests. */
+/* -------------- */
+ pseudo_bit_t cqn_rcv[0x00018]; /* CQ number completions from receive queue to be reported to. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved29[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved30[0x00006];
+ pseudo_bit_t rcv_wqe_base_adr_l[0x0001a];/* While opening (creating) the WQ, this field should contain the address of first descriptor to be posted. Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t rcv_db_record_index[0x00020];/* Index in the UAR Context Table Entry containing the doorbell record for the receive queue.
+ HW uses this index as an offset from the UAR Context Table Entry in order to read this RQ doorbell record.
+ The entry is obtained via the usr_page field.
+ Not valid for EE. */
+/* -------------- */
+ pseudo_bit_t q_key[0x00020]; /* Q_Key to be validated against received datagrams.
+ On send datagrams, if Q_Key[31] specified in the WQE is set, then this Q_Key will be transmitted in the outgoing message.
+ Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t srqn[0x00018]; /* SRQN - Shared Receive Queue Number - specifies the SRQ number from which the QP dequeues receive descriptors.
+ SRQN is valid only if SRQ bit is set. Not valid (reserved) in EE context. */
+ pseudo_bit_t srq[0x00001]; /* SRQ - Shared Receive Queue. If this bit is set, then the QP is associated with a SRQ. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved31[0x00007];
+/* -------------- */
+ pseudo_bit_t rmsn[0x00018]; /* Responder current message sequence number (QUERY_QPEE only) */
+ pseudo_bit_t reserved32[0x00008];
+/* -------------- */
+ pseudo_bit_t sq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the SQ.
+ Must be 0x0 in SQ initialization.
+ (QUERY_QPEE only). */
+ pseudo_bit_t rq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the RQ.
+ Must be 0x0 in RQ initialization.
+ (QUERY_QPEE only). */
+/* -------------- */
+ pseudo_bit_t reserved33[0x00040];
+/* -------------- */
+};
+
+/* Clear Interrupt [63:0] */
+
+struct arbelprm_clr_int_st { /* Little Endian */
+ pseudo_bit_t clr_int_h[0x00020]; /* Clear Interrupt [63:32]
+ Write transactions to this register will clear (de-assert) the virtual interrupt output pins of InfiniHost-III-EX. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot.
+ This register is write-only. Reading from this register will cause undefined result
+ */
+/* -------------- */
+ pseudo_bit_t clr_int_l[0x00020]; /* Clear Interrupt [31:0]
+ Write transactions to this register will clear (de-assert) the virtual interrupt output pins of InfiniHost-III-EX. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot.
+ This register is write-only. Reading from this register will cause undefined result */
+/* -------------- */
+};
+
+/* EQ_Arm_DB_Region */
+
+struct arbelprm_eq_arm_db_region_st { /* Little Endian */
+ pseudo_bit_t eq_x_arm_h[0x00020]; /* EQ[63:32] X state.
+ This register is used to Arm EQs when setting the appropriate bits. */
+/* -------------- */
+ pseudo_bit_t eq_x_arm_l[0x00020]; /* EQ[31:0] X state.
+ This register is used to Arm EQs when setting the appropriate bits. */
+/* -------------- */
+};
+
+/* EQ Set CI DBs Table */
+
+struct arbelprm_eq_set_ci_table_st { /* Little Endian */
+ pseudo_bit_t eq0_set_ci[0x00020]; /* EQ0_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t eq1_set_ci[0x00020]; /* EQ1_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t eq2_set_ci[0x00020]; /* EQ2_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00020];
+/* -------------- */
+ pseudo_bit_t eq3_set_ci[0x00020]; /* EQ3_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t eq4_set_ci[0x00020]; /* EQ4_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t eq5_set_ci[0x00020]; /* EQ5_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t eq6_set_ci[0x00020]; /* EQ6_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00020];
+/* -------------- */
+ pseudo_bit_t eq7_set_ci[0x00020]; /* EQ7_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00020];
+/* -------------- */
+ pseudo_bit_t eq8_set_ci[0x00020]; /* EQ8_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00020];
+/* -------------- */
+ pseudo_bit_t eq9_set_ci[0x00020]; /* EQ9_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00020];
+/* -------------- */
+ pseudo_bit_t eq10_set_ci[0x00020]; /* EQ10_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00020];
+/* -------------- */
+ pseudo_bit_t eq11_set_ci[0x00020]; /* EQ11_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved11[0x00020];
+/* -------------- */
+ pseudo_bit_t eq12_set_ci[0x00020]; /* EQ12_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved12[0x00020];
+/* -------------- */
+ pseudo_bit_t eq13_set_ci[0x00020]; /* EQ13_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved13[0x00020];
+/* -------------- */
+ pseudo_bit_t eq14_set_ci[0x00020]; /* EQ14_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved14[0x00020];
+/* -------------- */
+ pseudo_bit_t eq15_set_ci[0x00020]; /* EQ15_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved15[0x00020];
+/* -------------- */
+ pseudo_bit_t eq16_set_ci[0x00020]; /* EQ16_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved16[0x00020];
+/* -------------- */
+ pseudo_bit_t eq17_set_ci[0x00020]; /* EQ17_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved17[0x00020];
+/* -------------- */
+ pseudo_bit_t eq18_set_ci[0x00020]; /* EQ18_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved18[0x00020];
+/* -------------- */
+ pseudo_bit_t eq19_set_ci[0x00020]; /* EQ19_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved19[0x00020];
+/* -------------- */
+ pseudo_bit_t eq20_set_ci[0x00020]; /* EQ20_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved20[0x00020];
+/* -------------- */
+ pseudo_bit_t eq21_set_ci[0x00020]; /* EQ21_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved21[0x00020];
+/* -------------- */
+ pseudo_bit_t eq22_set_ci[0x00020]; /* EQ22_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved22[0x00020];
+/* -------------- */
+ pseudo_bit_t eq23_set_ci[0x00020]; /* EQ23_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved23[0x00020];
+/* -------------- */
+ pseudo_bit_t eq24_set_ci[0x00020]; /* EQ24_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved24[0x00020];
+/* -------------- */
+ pseudo_bit_t eq25_set_ci[0x00020]; /* EQ25_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved25[0x00020];
+/* -------------- */
+ pseudo_bit_t eq26_set_ci[0x00020]; /* EQ26_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved26[0x00020];
+/* -------------- */
+ pseudo_bit_t eq27_set_ci[0x00020]; /* EQ27_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved27[0x00020];
+/* -------------- */
+ pseudo_bit_t eq28_set_ci[0x00020]; /* EQ28_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved28[0x00020];
+/* -------------- */
+ pseudo_bit_t eq29_set_ci[0x00020]; /* EQ29_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved29[0x00020];
+/* -------------- */
+ pseudo_bit_t eq30_set_ci[0x00020]; /* EQ30_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved30[0x00020];
+/* -------------- */
+ pseudo_bit_t eq31_set_ci[0x00020]; /* EQ31_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved31[0x00020];
+/* -------------- */
+ pseudo_bit_t eq32_set_ci[0x00020]; /* EQ32_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved32[0x00020];
+/* -------------- */
+ pseudo_bit_t eq33_set_ci[0x00020]; /* EQ33_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved33[0x00020];
+/* -------------- */
+ pseudo_bit_t eq34_set_ci[0x00020]; /* EQ34_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved34[0x00020];
+/* -------------- */
+ pseudo_bit_t eq35_set_ci[0x00020]; /* EQ35_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved35[0x00020];
+/* -------------- */
+ pseudo_bit_t eq36_set_ci[0x00020]; /* EQ36_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved36[0x00020];
+/* -------------- */
+ pseudo_bit_t eq37_set_ci[0x00020]; /* EQ37_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved37[0x00020];
+/* -------------- */
+ pseudo_bit_t eq38_set_ci[0x00020]; /* EQ38_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved38[0x00020];
+/* -------------- */
+ pseudo_bit_t eq39_set_ci[0x00020]; /* EQ39_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved39[0x00020];
+/* -------------- */
+ pseudo_bit_t eq40_set_ci[0x00020]; /* EQ40_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved40[0x00020];
+/* -------------- */
+ pseudo_bit_t eq41_set_ci[0x00020]; /* EQ41_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved41[0x00020];
+/* -------------- */
+ pseudo_bit_t eq42_set_ci[0x00020]; /* EQ42_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved42[0x00020];
+/* -------------- */
+ pseudo_bit_t eq43_set_ci[0x00020]; /* EQ43_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved43[0x00020];
+/* -------------- */
+ pseudo_bit_t eq44_set_ci[0x00020]; /* EQ44_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved44[0x00020];
+/* -------------- */
+ pseudo_bit_t eq45_set_ci[0x00020]; /* EQ45_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved45[0x00020];
+/* -------------- */
+ pseudo_bit_t eq46_set_ci[0x00020]; /* EQ46_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved46[0x00020];
+/* -------------- */
+ pseudo_bit_t eq47_set_ci[0x00020]; /* EQ47_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved47[0x00020];
+/* -------------- */
+ pseudo_bit_t eq48_set_ci[0x00020]; /* EQ48_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved48[0x00020];
+/* -------------- */
+ pseudo_bit_t eq49_set_ci[0x00020]; /* EQ49_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved49[0x00020];
+/* -------------- */
+ pseudo_bit_t eq50_set_ci[0x00020]; /* EQ50_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved50[0x00020];
+/* -------------- */
+ pseudo_bit_t eq51_set_ci[0x00020]; /* EQ51_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved51[0x00020];
+/* -------------- */
+ pseudo_bit_t eq52_set_ci[0x00020]; /* EQ52_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved52[0x00020];
+/* -------------- */
+ pseudo_bit_t eq53_set_ci[0x00020]; /* EQ53_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved53[0x00020];
+/* -------------- */
+ pseudo_bit_t eq54_set_ci[0x00020]; /* EQ54_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved54[0x00020];
+/* -------------- */
+ pseudo_bit_t eq55_set_ci[0x00020]; /* EQ55_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved55[0x00020];
+/* -------------- */
+ pseudo_bit_t eq56_set_ci[0x00020]; /* EQ56_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved56[0x00020];
+/* -------------- */
+ pseudo_bit_t eq57_set_ci[0x00020]; /* EQ57_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved57[0x00020];
+/* -------------- */
+ pseudo_bit_t eq58_set_ci[0x00020]; /* EQ58_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved58[0x00020];
+/* -------------- */
+ pseudo_bit_t eq59_set_ci[0x00020]; /* EQ59_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved59[0x00020];
+/* -------------- */
+ pseudo_bit_t eq60_set_ci[0x00020]; /* EQ60_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved60[0x00020];
+/* -------------- */
+ pseudo_bit_t eq61_set_ci[0x00020]; /* EQ61_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved61[0x00020];
+/* -------------- */
+ pseudo_bit_t eq62_set_ci[0x00020]; /* EQ62_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved62[0x00020];
+/* -------------- */
+ pseudo_bit_t eq63_set_ci[0x00020]; /* EQ63_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved63[0x00020];
+/* -------------- */
+};
+
+/* InfiniHost-III-EX Configuration Registers */
+
+struct arbelprm_configuration_registers_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x403400];
+/* -------------- */
+ struct arbelprm_hca_command_register_st hca_command_interface_register;/* HCA Command Register */
+/* -------------- */
+ pseudo_bit_t reserved1[0x3fcb20];
+/* -------------- */
+};
+
+/* QP_DB_Record */
+
+struct arbelprm_qp_db_record_st { /* Little Endian */
+ pseudo_bit_t counter[0x00010]; /* Modulo-64K counter of WQEs posted to the QP since its creation. Should be initialized to zero. */
+ pseudo_bit_t reserved0[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00005];
+ pseudo_bit_t res[0x00003]; /* 0x3 for SQ
+ 0x4 for RQ
+ 0x5 for SRQ */
+ pseudo_bit_t qp_number[0x00018]; /* QP number */
+/* -------------- */
+};
+
+/* CQ_ARM_DB_Record */
+
+struct arbelprm_cq_arm_db_record_st { /* Little Endian */
+ pseudo_bit_t counter[0x00020]; /* CQ counter for the arming request */
+/* -------------- */
+ pseudo_bit_t cmd[0x00003]; /* 0x0 - No command
+ 0x1 - Request notification for next Solicited completion event. Counter filed specifies the current CQ Consumer Counter.
+ 0x2 - Request notification for next Solicited or Unsolicited completion event. Counter filed specifies the current CQ Consumer counter.
+ 0x3 - Request notification for multiple completions (Arm-N). Counter filed specifies the value of the CQ Index that when reached by HW (i.e. HW generates a CQE into this Index) Event will be generated
+ Other - Reserved */
+ pseudo_bit_t cmd_sn[0x00002]; /* Command Sequence Number - See Table 35, "CQ Doorbell Layout" for definition of this filed */
+ pseudo_bit_t res[0x00003]; /* Must be 0x2 */
+ pseudo_bit_t cq_number[0x00018]; /* CQ number */
+/* -------------- */
+};
+
+/* CQ_CI_DB_Record */
+
+struct arbelprm_cq_ci_db_record_st { /* Little Endian */
+ pseudo_bit_t counter[0x00020]; /* CQ counter */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00005];
+ pseudo_bit_t res[0x00003]; /* Must be 0x1 */
+ pseudo_bit_t cq_number[0x00018]; /* CQ number */
+/* -------------- */
+};
+
+/* Virtual_Physical_Mapping */
+
+struct arbelprm_virtual_physical_mapping_st { /* Little Endian */
+ pseudo_bit_t va_h[0x00020]; /* Virtual Address[63:32]. Valid only for MAP_ICM command. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x0000c];
+ pseudo_bit_t va_l[0x00014]; /* Virtual Address[31:12]. Valid only for MAP_ICM command. */
+/* -------------- */
+ pseudo_bit_t pa_h[0x00020]; /* Physical Address[63:32] */
+/* -------------- */
+ pseudo_bit_t log2size[0x00006]; /* Log2 of the size in 4KB pages of the physical and virtual contiguous memory that starts at PA_L/H and VA_L/H */
+ pseudo_bit_t reserved1[0x00006];
+ pseudo_bit_t pa_l[0x00014]; /* Physical Address[31:12] */
+/* -------------- */
+};
+
+/* MOD_STAT_CFG */
+
+struct arbelprm_mod_stat_cfg_st { /* Little Endian */
+ pseudo_bit_t log_max_srqs[0x00005]; /* Log (base 2) of the number of SRQs to allocate (0 if no SRQs are required), valid only if srq bit is set. */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t srq[0x00001]; /* When set SRQs are supported */
+ pseudo_bit_t srq_m[0x00001]; /* Modify SRQ parameters */
+ pseudo_bit_t reserved1[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved2[0x007e0];
+/* -------------- */
+};
+
+/* SRQ Context */
+
+struct arbelprm_srq_context_st { /* Little Endian */
+ pseudo_bit_t srqn[0x00018]; /* SRQ number */
+ pseudo_bit_t log_srq_size[0x00004]; /* Log2 of the Number of WQEs in the Receive Queue.
+ Maximum value is 0x10, i.e. 16M WQEs. */
+ pseudo_bit_t state[0x00004]; /* SRQ State:
+ 1111 - SW Ownership
+ 0000 - HW Ownership
+ 0001 - Error
+ Valid only on QUERY_SRQ and HW2SW_SRQ commands. */
+/* -------------- */
+ pseudo_bit_t l_key[0x00020]; /* memory key (L-Key) to be used to access WQEs. */
+/* -------------- */
+ pseudo_bit_t srq_db_record_index[0x00020];/* Index in the UAR Context Table Entry containing the doorbell record for the receive queue.
+ HW uses this index as an offset from the UAR Context Table Entry in order to read this SRQ doorbell record.
+ The entry is obtained via the usr_page field. */
+/* -------------- */
+ pseudo_bit_t usr_page[0x00018]; /* Index (offset) of user page allocated for this SRQ (see "non_privileged Access to the HCA Hardware"). Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved0[0x00005];
+ pseudo_bit_t log_rq_stride[0x00003];/* Stride (max WQE size) on the receive queue. WQ entry is 16*(2^log_RQ_stride) bytes. */
+/* -------------- */
+ pseudo_bit_t wqe_addr_h[0x00020]; /* Bits 63:32 of WQE address (WQE base address) */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00006];
+ pseudo_bit_t srq_wqe_base_adr_l[0x0001a];/* While opening (creating) the SRQ, this field should contain the address of first descriptor to be posted. */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* SRQ protection domain. */
+ pseudo_bit_t reserved2[0x00008];
+/* -------------- */
+ pseudo_bit_t wqe_cnt[0x00010]; /* WQE count on the SRQ.
+ Valid only on QUERY_SRQ and HW2SW_SRQ commands. */
+ pseudo_bit_t lwm[0x00010]; /* Limit Water Mark - if the LWM is not zero, and the wqe_cnt drops below LWM when a WQE is dequeued from the SRQ, then a SRQ limit event is fired and the LWM is set to zero. */
+/* -------------- */
+ pseudo_bit_t srq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the SQ.
+ Must be 0x0 in SRQ initialization.
+ (QUERY_SRQ only). */
+ pseudo_bit_t reserved3[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00060];
+/* -------------- */
+};
+
+/* PBL */
+
+struct arbelprm_pbl_st { /* Little Endian */
+ pseudo_bit_t mtt_0_h[0x00020]; /* First MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_0_l[0x00020]; /* First MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_1_h[0x00020]; /* Second MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_1_l[0x00020]; /* Second MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_2_h[0x00020]; /* Third MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_2_l[0x00020]; /* Third MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_3_h[0x00020]; /* Fourth MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_3_l[0x00020]; /* Fourth MTT[31:0] */
+/* -------------- */
+};
+
+/* Performance Counters */
+
+struct arbelprm_performance_counters_st { /* Little Endian */
+ pseudo_bit_t sqpc_access_cnt[0x00020];/* SQPC cache access count */
+/* -------------- */
+ pseudo_bit_t sqpc_miss_cnt[0x00020];/* SQPC cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t rqpc_access_cnt[0x00020];/* RQPC cache access count */
+/* -------------- */
+ pseudo_bit_t rqpc_miss_cnt[0x00020];/* RQPC cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+ pseudo_bit_t cqc_access_cnt[0x00020];/* CQC cache access count */
+/* -------------- */
+ pseudo_bit_t cqc_miss_cnt[0x00020]; /* CQC cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t tpt_access_cnt[0x00020];/* TPT cache access count */
+/* -------------- */
+ pseudo_bit_t mpt_miss_cnt[0x00020]; /* MPT cache miss count */
+/* -------------- */
+ pseudo_bit_t mtt_miss_cnt[0x00020]; /* MTT cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00620];
+/* -------------- */
+};
+
+/* Transport and CI Error Counters */
+
+struct arbelprm_transport_and_ci_error_counters_st { /* Little Endian */
+ pseudo_bit_t rq_num_lle[0x00020]; /* Responder - number of local length errors */
+/* -------------- */
+ pseudo_bit_t sq_num_lle[0x00020]; /* Requester - number of local length errors */
+/* -------------- */
+ pseudo_bit_t rq_num_lqpoe[0x00020]; /* Responder - number local QP operation error */
+/* -------------- */
+ pseudo_bit_t sq_num_lqpoe[0x00020]; /* Requester - number local QP operation error */
+/* -------------- */
+ pseudo_bit_t rq_num_leeoe[0x00020]; /* Responder - number local EE operation error */
+/* -------------- */
+ pseudo_bit_t sq_num_leeoe[0x00020]; /* Requester - number local EE operation error */
+/* -------------- */
+ pseudo_bit_t rq_num_lpe[0x00020]; /* Responder - number of local protection errors */
+/* -------------- */
+ pseudo_bit_t sq_num_lpe[0x00020]; /* Requester - number of local protection errors */
+/* -------------- */
+ pseudo_bit_t rq_num_wrfe[0x00020]; /* Responder - number of CQEs with error.
+ Incremented each time a CQE with error is generated */
+/* -------------- */
+ pseudo_bit_t sq_num_wrfe[0x00020]; /* Requester - number of CQEs with error.
+ Incremented each time a CQE with error is generated */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_mwbe[0x00020]; /* Requester - number of memory window bind errors */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_bre[0x00020]; /* Requester - number of bad response errors */
+/* -------------- */
+ pseudo_bit_t rq_num_lae[0x00020]; /* Responder - number of local access errors */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t sq_num_rire[0x00020]; /* Requester - number of remote invalid request errors
+ NAK-Invalid Request on:
+ 1. Unsupported OpCode: Responder detected an unsupported OpCode.
+ 2. Unexpected OpCode: Responder detected an error in the sequence of OpCodes, such
+ as a missing "Last" packet.
+ Note: there is no PSN error, thus this does not indicate a dropped packet. */
+/* -------------- */
+ pseudo_bit_t rq_num_rire[0x00020]; /* Responder - number of remote invalid request errors.
+ NAK may or may not be sent.
+ 1. QP Async Affiliated Error: Unsupported or Reserved OpCode (RC,RD only):
+ Inbound request OpCode was either reserved, or was for a function not supported by this
+ QP. (E.g. RDMA or ATOMIC on QP not set up for this).
+ 2. Misaligned ATOMIC: VA does not point to an aligned address on an atomic opera-tion.
+ 3. Too many RDMA READ or ATOMIC Requests: There were more requests received
+ and not ACKed than allowed for the connection.
+ 4. Out of Sequence OpCode, current packet is "First" or "Only": The Responder
+ detected an error in the sequence of OpCodes; a missing "Last" packet
+ 5. Out of Sequence OpCode, current packet is not "First" or "Only": The Responder
+ detected an error in the sequence of OpCodes; a missing "First" packet
+ 6. Local Length Error: Inbound "Send" request message exceeded the responder.s avail-able
+ buffer space.
+ 7. Length error: RDMA WRITE request message contained too much or too little pay-load
+ data compared to the DMA length advertised in the first or only packet.
+ 8. Length error: Payload length was not consistent with the opcode:
+ a: 0 byte <= "only" <= PMTU bytes
+ b: ("first" or "middle") == PMTU bytes
+ c: 1byte <= "last" <= PMTU bytes
+ 9. Length error: Inbound message exceeded the size supported by the CA port. */
+/* -------------- */
+ pseudo_bit_t sq_num_rae[0x00020]; /* Requester - number of remote access errors.
+ NAK-Remote Access Error on:
+ R_Key Violation: Responder detected an invalid R_Key while executing an RDMA
+ Request. */
+/* -------------- */
+ pseudo_bit_t rq_num_rae[0x00020]; /* Responder - number of remote access errors.
+ R_Key Violation Responder detected an R_Key violation while executing an RDMA
+ request.
+ NAK may or may not be sent. */
+/* -------------- */
+ pseudo_bit_t sq_num_roe[0x00020]; /* Requester - number of remote operation errors.
+ NAK-Remote Operation Error on:
+ Remote Operation Error: Responder encountered an error, (local to the responder),
+ which prevented it from completing the request. */
+/* -------------- */
+ pseudo_bit_t rq_num_roe[0x00020]; /* Responder - number of remote operation errors.
+ NAK-Remote Operation Error on:
+ 1. Malformed WQE: Responder detected a malformed Receive Queue WQE while pro-cessing
+ the packet.
+ 2. Remote Operation Error: Responder encountered an error, (local to the responder),
+ which prevented it from completing the request. */
+/* -------------- */
+ pseudo_bit_t sq_num_tree[0x00020]; /* Requester - number of transport retries exceeded errors */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_rree[0x00020]; /* Requester - number of RNR nak retries exceeded errors */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_lrdve[0x00020]; /* Requester - number of local RDD violation errors */
+/* -------------- */
+ pseudo_bit_t rq_num_rirdre[0x00020];/* Responder - number of remote invalid RD request errors */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00040];
+/* -------------- */
+ pseudo_bit_t sq_num_rabrte[0x00020];/* Requester - number of remote aborted errors */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_ieecne[0x00020];/* Requester - number of invalid EE context number errors */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_ieecse[0x00020];/* Requester - invalid EE context state errors */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00380];
+/* -------------- */
+ pseudo_bit_t rq_num_oos[0x00020]; /* Responder - number of out of sequence requests received */
+/* -------------- */
+ pseudo_bit_t sq_num_oos[0x00020]; /* Requester - number of out of sequence Naks received */
+/* -------------- */
+ pseudo_bit_t rq_num_mce[0x00020]; /* Responder - number of bad multicast packets received */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00020];
+/* -------------- */
+ pseudo_bit_t rq_num_rsync[0x00020]; /* Responder - number of RESYNC operations */
+/* -------------- */
+ pseudo_bit_t sq_num_rsync[0x00020]; /* Requester - number of RESYNC operations */
+/* -------------- */
+ pseudo_bit_t rq_num_udsdprd[0x00020];/* The number of UD packets silently discarded on the receive queue due to lack of receive descriptor. */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00020];
+/* -------------- */
+ pseudo_bit_t rq_num_ucsdprd[0x00020];/* The number of UC packets silently discarded on the receive queue due to lack of receive descriptor. */
+/* -------------- */
+ pseudo_bit_t reserved11[0x003e0];
+/* -------------- */
+ pseudo_bit_t num_cqovf[0x00020]; /* Number of CQ overflows */
+/* -------------- */
+ pseudo_bit_t num_eqovf[0x00020]; /* Number of EQ overflows */
+/* -------------- */
+ pseudo_bit_t num_baddb[0x00020]; /* Number of bad doorbells */
+/* -------------- */
+ pseudo_bit_t reserved12[0x002a0];
+/* -------------- */
+};
+
+/* Event_data Field - HCR Completion Event */
+
+struct arbelprm_hcr_completion_event_st { /* Little Endian */
+ pseudo_bit_t token[0x00010]; /* HCR Token */
+ pseudo_bit_t reserved0[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t status[0x00008]; /* HCR Status */
+ pseudo_bit_t reserved2[0x00018];
+/* -------------- */
+ pseudo_bit_t out_param_h[0x00020]; /* HCR Output Parameter [63:32] */
+/* -------------- */
+ pseudo_bit_t out_param_l[0x00020]; /* HCR Output Parameter [31:0] */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+};
+
+/* Completion with Error CQE */
+
+struct arbelprm_completion_with_error_st { /* Little Endian */
+ pseudo_bit_t myqpn[0x00018]; /* Indicates the QP for which completion is being reported */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00060];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00010];
+ pseudo_bit_t vendor_code[0x00008];
+ pseudo_bit_t syndrome[0x00008]; /* Completion with error syndrome:
+ 0x01 - Local Length Error
+ 0x02 - Local QP Operation Error
+ 0x03 - Local EE Context Operation Error
+ 0x04 - Local Protection Error
+ 0x05 - Work Request Flushed Error
+ 0x06 - Memory Window Bind Error
+ 0x10 - Bad Response Error
+ 0x11 - Local Access Error
+ 0x12 - Remote Invalid Request Error
+ 0x13 - Remote Access Error
+ 0x14 - Remote Operation Error
+ 0x15 - Transport Retry Counter Exceeded
+ 0x16 - RNR Retry Counter Exceeded
+ 0x20 - Local RDD Violation Error
+ 0x21 - Remote Invalid RD Request
+ 0x22 - Remote Aborted Error
+ 0x23 - Invalid EE Context Number
+ 0x24 - Invalid EE Context State
+ other - Reserved
+ Syndrome is defined according to the IB specification volume 1. For detailed explanation of the syndromes, refer to chapters 10-11 of the IB specification rev 1.1. */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00006];
+ pseudo_bit_t wqe_addr[0x0001a]; /* Bits 31:6 of WQE virtual address completion is reported for. The 6 least significant bits are zero. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00007];
+ pseudo_bit_t owner[0x00001]; /* Owner field. Zero value of this field means SW ownership of CQE. */
+ pseudo_bit_t reserved6[0x00010];
+ pseudo_bit_t opcode[0x00008]; /* The opcode of WQE completion is reported for.
+
+ The following values are reported in case of completion with error:
+ 0xFE - For completion with error on Receive Queues
+ 0xFF - For completion with error on Send Queues */
+/* -------------- */
+};
+
+/* Resize CQ Input Mailbox */
+
+struct arbelprm_resize_cq_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t start_addr_h[0x00020]; /* Start address of CQ[63:32].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t start_addr_l[0x00020]; /* Start address of CQ[31:0].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00018];
+ pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries) */
+ pseudo_bit_t reserved2[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+ pseudo_bit_t l_key[0x00020]; /* Memory key (L_Key) to be used to access CQ */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00100];
+/* -------------- */
+};
+
+/* MAD_IFC Input Modifier */
+
+struct arbelprm_mad_ifc_input_modifier_st { /* Little Endian */
+ pseudo_bit_t port_number[0x00008]; /* The packet reception port number (1 or 2). */
+ pseudo_bit_t mad_extended_info[0x00001];/* Mad_Extended_Info valid bit (MAD_IFC Input Mailbox data from offset 00100h and down). MAD_Extended_Info is read only if this bit is set.
+ Required for trap generation when BKey check is enabled and for global routed packets. */
+ pseudo_bit_t reserved0[0x00007];
+ pseudo_bit_t rlid[0x00010]; /* Remote (source) LID from the received MAD.
+ This field is required for trap generation upon MKey/BKey validation. */
+/* -------------- */
+};
+
+/* MAD_IFC Input Mailbox */
+
+struct arbelprm_mad_ifc_st { /* Little Endian */
+ pseudo_bit_t request_mad_packet[64][0x00020];/* Request MAD Packet (256bytes) */
+/* -------------- */
+ pseudo_bit_t my_qpn[0x00018]; /* Destination QP number from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t rqpn[0x00018]; /* Remote (source) QP number from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (source) LID from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t ml_path[0x00007]; /* My (destination) LID path bits from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t g[0x00001]; /* If set, the GRH field in valid.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved2[0x00004];
+ pseudo_bit_t sl[0x00004]; /* Service Level of the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+/* -------------- */
+ pseudo_bit_t pkey_indx[0x00010]; /* Index in PKey table that matches PKey of the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved3[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00180];
+/* -------------- */
+ pseudo_bit_t grh[10][0x00020]; /* The GRH field of the MAD packet that was scattered to the first 40 bytes pointed to by the scatter list.
+ Valid if Mad_extended_info bit (in the input modifier) and g bit are set.
+ Otherwise this field is reserved. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x004c0];
+/* -------------- */
+};
+
+/* Query Debug Message */
+
+struct arbelprm_query_debug_msg_st { /* Little Endian */
+ pseudo_bit_t phy_addr_h[0x00020]; /* Translation of the address in firmware area. High 32 bits. */
+/* -------------- */
+ pseudo_bit_t v[0x00001]; /* Physical translation is valid */
+ pseudo_bit_t reserved0[0x0000b];
+ pseudo_bit_t phy_addr_l[0x00014]; /* Translation of the address in firmware area. Low 32 bits. */
+/* -------------- */
+ pseudo_bit_t fw_area_base[0x00020]; /* Firmware area base address. The format strings and the trace buffers may be located starting from this address. */
+/* -------------- */
+ pseudo_bit_t fw_area_size[0x00020]; /* Firmware area size */
+/* -------------- */
+ pseudo_bit_t trc_hdr_sz[0x00020]; /* Trace message header size in dwords. */
+/* -------------- */
+ pseudo_bit_t trc_arg_num[0x00020]; /* The number of arguments per trace message. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x000c0];
+/* -------------- */
+ pseudo_bit_t dbg_msk_h[0x00020]; /* Debug messages mask [63:32] */
+/* -------------- */
+ pseudo_bit_t dbg_msk_l[0x00020]; /* Debug messages mask [31:0] */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t buff0_addr[0x00020]; /* Address in firmware area of Trace Buffer 0 */
+/* -------------- */
+ pseudo_bit_t buff0_size[0x00020]; /* Size of Trace Buffer 0 */
+/* -------------- */
+ pseudo_bit_t buff1_addr[0x00020]; /* Address in firmware area of Trace Buffer 1 */
+/* -------------- */
+ pseudo_bit_t buff1_size[0x00020]; /* Size of Trace Buffer 1 */
+/* -------------- */
+ pseudo_bit_t buff2_addr[0x00020]; /* Address in firmware area of Trace Buffer 2 */
+/* -------------- */
+ pseudo_bit_t buff2_size[0x00020]; /* Size of Trace Buffer 2 */
+/* -------------- */
+ pseudo_bit_t buff3_addr[0x00020]; /* Address in firmware area of Trace Buffer 3 */
+/* -------------- */
+ pseudo_bit_t buff3_size[0x00020]; /* Size of Trace Buffer 3 */
+/* -------------- */
+ pseudo_bit_t buff4_addr[0x00020]; /* Address in firmware area of Trace Buffer 4 */
+/* -------------- */
+ pseudo_bit_t buff4_size[0x00020]; /* Size of Trace Buffer 4 */
+/* -------------- */
+ pseudo_bit_t buff5_addr[0x00020]; /* Address in firmware area of Trace Buffer 5 */
+/* -------------- */
+ pseudo_bit_t buff5_size[0x00020]; /* Size of Trace Buffer 5 */
+/* -------------- */
+ pseudo_bit_t buff6_addr[0x00020]; /* Address in firmware area of Trace Buffer 6 */
+/* -------------- */
+ pseudo_bit_t buff6_size[0x00020]; /* Size of Trace Buffer 6 */
+/* -------------- */
+ pseudo_bit_t buff7_addr[0x00020]; /* Address in firmware area of Trace Buffer 7 */
+/* -------------- */
+ pseudo_bit_t buff7_size[0x00020]; /* Size of Trace Buffer 7 */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00400];
+/* -------------- */
+};
+
+/* User Access Region */
+
+struct arbelprm_uar_st { /* Little Endian */
+ struct arbelprm_rd_send_doorbell_st rd_send_doorbell;/* Reliable Datagram send doorbell */
+/* -------------- */
+ struct arbelprm_send_doorbell_st send_doorbell;/* Send doorbell */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ struct arbelprm_cq_cmd_doorbell_st cq_command_doorbell;/* CQ Doorbell */
+/* -------------- */
+ pseudo_bit_t reserved1[0x03ec0];
+/* -------------- */
+};
+
+/* Receive doorbell */
+
+struct arbelprm_receive_doorbell_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t wqe_counter[0x00010]; /* Modulo-64K counter of WQEs posted on this queue since its creation. Should be zero for the first doorbell on the QP */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00005];
+ pseudo_bit_t srq[0x00001]; /* If set, this is a Shared Receive Queue */
+ pseudo_bit_t reserved3[0x00002];
+ pseudo_bit_t qpn[0x00018]; /* QP number or SRQ number this doorbell is rung on */
+/* -------------- */
+};
+
+/* SET_IB Parameters */
+
+struct arbelprm_set_ib_st { /* Little Endian */
+ pseudo_bit_t rqk[0x00001]; /* Reset QKey Violation Counter */
+ pseudo_bit_t reserved0[0x00011];
+ pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified.
+ system_image_guid and sig must be the same for all ports. */
+ pseudo_bit_t reserved1[0x0000d];
+/* -------------- */
+ pseudo_bit_t capability_mask[0x00020];/* PortInfo Capability Mask */
+/* -------------- */
+ pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00180];
+/* -------------- */
+};
+
+/* Multicast Group Member */
+
+struct arbelprm_mgm_entry_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00006];
+ pseudo_bit_t next_gid_index[0x0001a];/* Index of next Multicast Group Member whose GID maps to same MGID_HASH number.
+ The index is into the Multicast Group Table, which is the comprised the MGHT and AMGM tables.
+ next_gid_index=0 means end of the chain. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00060];
+/* -------------- */
+ pseudo_bit_t mgid_128_96[0x00020]; /* Multicast group GID[128:96] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_95_64[0x00020]; /* Multicast group GID[95:64] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_63_32[0x00020]; /* Multicast group GID[63:32] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_31_0[0x00020]; /* Multicast group GID[31:0] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_0; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_1; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_2; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_3; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_4; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_5; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_6; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_7; /* Multicast Group Member QP */
+/* -------------- */
+};
+
+/* INIT_IB Parameters */
+
+struct arbelprm_init_ib_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00004];
+ pseudo_bit_t vl_cap[0x00004]; /* Maximum VLs supported on the port, excluding VL15.
+ Legal values are 1,2,4 and 8. */
+ pseudo_bit_t port_width_cap[0x00004];/* IB Port Width
+ 1 - 1x
+ 3 - 1x, 4x
+ 11 - 1x, 4x or 12x (must not be used in InfiniHost-III-EX MT25208)
+ else - Reserved */
+ pseudo_bit_t mtu_cap[0x00004]; /* Maximum MTU Supported
+ 0x0 - Reserved
+ 0x1 - 256
+ 0x2 - 512
+ 0x3 - 1024
+ 0x4 - 2048
+ 0x5 - 0xF Reserved */
+ pseudo_bit_t g0[0x00001]; /* Set port GUID0 to GUID0 specified */
+ pseudo_bit_t ng[0x00001]; /* Set node GUID to node_guid specified.
+ node_guid and ng must be the same for all ports. */
+ pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified.
+ system_image_guid and sig must be the same for all ports. */
+ pseudo_bit_t reserved1[0x0000d];
+/* -------------- */
+ pseudo_bit_t max_gid[0x00010]; /* Maximum number of GIDs for the port */
+ pseudo_bit_t reserved2[0x00010];
+/* -------------- */
+ pseudo_bit_t max_pkey[0x00010]; /* Maximum pkeys for the port.
+ Must be the same for both ports. */
+ pseudo_bit_t reserved3[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t guid0_h[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 63:32) */
+/* -------------- */
+ pseudo_bit_t guid0_l[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 31:0) */
+/* -------------- */
+ pseudo_bit_t node_guid_h[0x00020]; /* Node GUID[63:32], takes effect only if the NG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t node_guid_l[0x00020]; /* Node GUID[31:0], takes effect only if the NG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x006c0];
+/* -------------- */
+};
+
+/* Query Device Limitations */
+
+struct arbelprm_query_dev_lim_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t log_max_qp[0x00005]; /* Log2 of the Maximum number of QPs supported */
+ pseudo_bit_t reserved1[0x00003];
+ pseudo_bit_t log2_rsvd_qps[0x00004];/* Log (base 2) of the number of QPs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_qps-1 */
+ pseudo_bit_t reserved2[0x00004];
+ pseudo_bit_t log_max_qp_sz[0x00008];/* The maximum number of WQEs allowed on the RQ or the SQ is 2^log_max_qp_sz-1 */
+ pseudo_bit_t log_max_srq_sz[0x00008];/* The maximum number of WQEs allowed on the SRQ is 2^log_max_srq_sz-1 */
+/* -------------- */
+ pseudo_bit_t log_max_ee[0x00005]; /* Log2 of the Maximum number of EE contexts supported */
+ pseudo_bit_t reserved3[0x00003];
+ pseudo_bit_t log2_rsvd_ees[0x00004];/* Log (base 2) of the number of EECs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_ees-1 */
+ pseudo_bit_t reserved4[0x00004];
+ pseudo_bit_t log_max_srqs[0x00005]; /* Log base 2 of the maximum number of SRQs supported, valid only if SRQ bit is set.
+ */
+ pseudo_bit_t reserved5[0x00007];
+ pseudo_bit_t log2_rsvd_srqs[0x00004];/* Log (base 2) of the number of reserved SRQs for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_srqs-1
+ This parameter is valid only if the SRQ bit is set. */
+/* -------------- */
+ pseudo_bit_t log_max_cq[0x00005]; /* Log2 of the Maximum number of CQs supported */
+ pseudo_bit_t reserved6[0x00003];
+ pseudo_bit_t log2_rsvd_cqs[0x00004];/* Log (base 2) of the number of CQs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsrvd_cqs-1 */
+ pseudo_bit_t reserved7[0x00004];
+ pseudo_bit_t log_max_cq_sz[0x00008];/* Log2 of the Maximum CQEs allowed in a CQ */
+ pseudo_bit_t reserved8[0x00008];
+/* -------------- */
+ pseudo_bit_t log_max_eq[0x00003]; /* Log2 of the Maximum number of EQs */
+ pseudo_bit_t reserved9[0x00005];
+ pseudo_bit_t num_rsvd_eqs[0x00004]; /* The number of EQs reserved for firmware use
+ The reserved resources are numbered from 0 to num_rsvd_eqs-1
+ If 0 - no resources are reserved. */
+ pseudo_bit_t reserved10[0x00004];
+ pseudo_bit_t log_max_mpts[0x00006]; /* Log (base 2) of the maximum number of MPT entries (the number of Regions/Windows) */
+ pseudo_bit_t reserved11[0x00002];
+ pseudo_bit_t log_max_eq_sz[0x00008];/* Log2 of the Maximum EQEs allowed in a EQ */
+/* -------------- */
+ pseudo_bit_t log_max_mtts[0x00006]; /* Log2 of the Maximum number of MTT entries */
+ pseudo_bit_t reserved12[0x00002];
+ pseudo_bit_t log2_rsvd_mrws[0x00004];/* Log (base 2) of the number of MPTs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_mrws-1 */
+ pseudo_bit_t reserved13[0x00004];
+ pseudo_bit_t log_max_mrw_sz[0x00008];/* Log2 of the Maximum Size of Memory Region/Window */
+ pseudo_bit_t reserved14[0x00004];
+ pseudo_bit_t log2_rsvd_mtts[0x00004];/* Log (base 2) of the number of MTT entries reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_mtts-1
+ */
+/* -------------- */
+ pseudo_bit_t reserved15[0x00020];
+/* -------------- */
+ pseudo_bit_t log_max_ra_res_qp[0x00006];/* Log2 of the Maximum number of outstanding RDMA read/Atomic per QP as a responder */
+ pseudo_bit_t reserved16[0x0000a];
+ pseudo_bit_t log_max_ra_req_qp[0x00006];/* Log2 of the maximum number of outstanding RDMA read/Atomic per QP as a requester */
+ pseudo_bit_t reserved17[0x0000a];
+/* -------------- */
+ pseudo_bit_t log_max_ra_res_global[0x00006];/* Log2 of the maximum number of RDMA read/atomic operations the HCA responder can support globally. That implies the RDB table size. */
+ pseudo_bit_t reserved18[0x00016];
+ pseudo_bit_t log2_rsvd_rdbs[0x00004];/* Log (base 2) of the number of RDB entries reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_rdbs-1 */
+/* -------------- */
+ pseudo_bit_t rsz_srq[0x00001]; /* Ability to modify the maximum number of WRs per SRQ. */
+ pseudo_bit_t reserved19[0x0001f];
+/* -------------- */
+ pseudo_bit_t num_ports[0x00004]; /* Number of IB ports. */
+ pseudo_bit_t max_vl[0x00004]; /* Maximum VLs supported on each port, excluding VL15 */
+ pseudo_bit_t max_port_width[0x00004];/* IB Port Width
+ 1 - 1x
+ 3 - 1x, 4x
+ 11 - 1x, 4x or 12x
+ else - Reserved */
+ pseudo_bit_t max_mtu[0x00004]; /* Maximum MTU Supported
+ 0x0 - Reserved
+ 0x1 - 256
+ 0x2 - 512
+ 0x3 - 1024
+ 0x4 - 2048
+ 0x5 - 0xF Reserved */
+ pseudo_bit_t local_ca_ack_delay[0x00005];/* The Local CA ACK Delay. This is the value recommended to be returned in Query HCA verb.
+ The delay value in microseconds is computed using 4.096us * 2^(local_ca_ack_delay). */
+ pseudo_bit_t reserved20[0x0000b];
+/* -------------- */
+ pseudo_bit_t log_max_gid[0x00004]; /* Log2 of the maximum number of GIDs per port */
+ pseudo_bit_t reserved21[0x0001c];
+/* -------------- */
+ pseudo_bit_t log_max_pkey[0x00004]; /* Log2 of the max PKey Table Size (per IB port) */
+ pseudo_bit_t reserved22[0x0000c];
+ pseudo_bit_t stat_rate_support[0x00010];/* bit mask of stat rate supported
+ bit 0 - full bw
+ bit 1 - 1/4 bw
+ bit 2 - 1/8 bw
+ bit 3 - 1/2 bw; */
+/* -------------- */
+ pseudo_bit_t reserved23[0x00020];
+/* -------------- */
+ pseudo_bit_t rc[0x00001]; /* RC Transport supported */
+ pseudo_bit_t uc[0x00001]; /* UC Transport Supported */
+ pseudo_bit_t ud[0x00001]; /* UD Transport Supported */
+ pseudo_bit_t rd[0x00001]; /* RD Transport Supported */
+ pseudo_bit_t raw_ipv6[0x00001]; /* Raw IPv6 Transport Supported */
+ pseudo_bit_t raw_ether[0x00001]; /* Raw Ethertype Transport Supported */
+ pseudo_bit_t srq[0x00001]; /* SRQ is supported
+ */
+ pseudo_bit_t ipo_ib_checksum[0x00001];/* IP over IB checksum is supported */
+ pseudo_bit_t pkv[0x00001]; /* PKey Violation Counter Supported */
+ pseudo_bit_t qkv[0x00001]; /* QKey Violation Coutner Supported */
+ pseudo_bit_t reserved24[0x00006];
+ pseudo_bit_t mw[0x00001]; /* Memory windows supported */
+ pseudo_bit_t apm[0x00001]; /* Automatic Path Migration Supported */
+ pseudo_bit_t atm[0x00001]; /* Atomic operations supported (atomicity is guaranteed between QPs on this HCA) */
+ pseudo_bit_t rm[0x00001]; /* Raw Multicast Supported */
+ pseudo_bit_t avp[0x00001]; /* Address Vector Port checking supported */
+ pseudo_bit_t udm[0x00001]; /* UD Multicast Supported */
+ pseudo_bit_t reserved25[0x00002];
+ pseudo_bit_t pg[0x00001]; /* Paging on demand supported */
+ pseudo_bit_t r[0x00001]; /* Router mode supported */
+ pseudo_bit_t reserved26[0x00006];
+/* -------------- */
+ pseudo_bit_t log_pg_sz[0x00008]; /* Minimum system page size supported (log2).
+ For proper operation it must be less than or equal the hosting platform (CPU) minimum page size. */
+ pseudo_bit_t reserved27[0x00008];
+ pseudo_bit_t uar_sz[0x00006]; /* UAR Area Size = 1MB * 2^uar_sz */
+ pseudo_bit_t reserved28[0x00006];
+ pseudo_bit_t num_rsvd_uars[0x00004];/* The number of UARs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_uars-1
+ Note that UAR number num_reserved_uars is always for the kernel. */
+/* -------------- */
+ pseudo_bit_t reserved29[0x00020];
+/* -------------- */
+ pseudo_bit_t max_desc_sz_sq[0x00010];/* Max descriptor size in bytes for the send queue */
+ pseudo_bit_t max_sg_sq[0x00008]; /* The maximum S/G list elements in a SQ WQE (max_desc_sz/16 - 3) */
+ pseudo_bit_t reserved30[0x00008];
+/* -------------- */
+ pseudo_bit_t max_desc_sz_rq[0x00010];/* Max descriptor size in bytes for the receive queue */
+ pseudo_bit_t max_sg_rq[0x00008]; /* The maximum S/G list elements in a RQ WQE (max_desc_sz/16 - 3) */
+ pseudo_bit_t reserved31[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved32[0x00040];
+/* -------------- */
+ pseudo_bit_t log_max_mcg[0x00008]; /* Log2 of the maximum number of multicast groups */
+ pseudo_bit_t num_rsvd_mcgs[0x00004];/* The number of MGMs reserved for firmware use in the MGHT.
+ The reserved resources are numbered from 0 to num_reserved_mcgs-1
+ If 0 - no resources are reserved. */
+ pseudo_bit_t reserved33[0x00004];
+ pseudo_bit_t log_max_qp_mcg[0x00008];/* Log2 of the maximum number of QPs per multicast group */
+ pseudo_bit_t reserved34[0x00008];
+/* -------------- */
+ pseudo_bit_t log_max_rdds[0x00006]; /* Log2 of the maximum number of RDDs */
+ pseudo_bit_t reserved35[0x00006];
+ pseudo_bit_t num_rsvd_rdds[0x00004];/* The number of RDDs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_rdds-1.
+ If 0 - no resources are reserved. */
+ pseudo_bit_t log_max_pd[0x00006]; /* Log2 of the maximum number of PDs */
+ pseudo_bit_t reserved36[0x00006];
+ pseudo_bit_t num_rsvd_pds[0x00004]; /* The number of PDs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_pds-1
+ If 0 - no resources are reserved. */
+/* -------------- */
+ pseudo_bit_t reserved37[0x000c0];
+/* -------------- */
+ pseudo_bit_t qpc_entry_sz[0x00010]; /* QPC Entry Size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 256 bytes */
+ pseudo_bit_t eec_entry_sz[0x00010]; /* EEC Entry Size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 256 bytes */
+/* -------------- */
+ pseudo_bit_t eqpc_entry_sz[0x00010];/* Extended QPC entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 32 bytes */
+ pseudo_bit_t eeec_entry_sz[0x00010];/* Extended EEC entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 32 bytes */
+/* -------------- */
+ pseudo_bit_t cqc_entry_sz[0x00010]; /* CQC entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 64 bytes */
+ pseudo_bit_t eqc_entry_sz[0x00010]; /* EQ context entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 64 bytes */
+/* -------------- */
+ pseudo_bit_t uar_scratch_entry_sz[0x00010];/* UAR Scratchpad Entry Size
+ For the InfiniHost-III-EX MT25208 entry size is 32 bytes */
+ pseudo_bit_t srq_entry_sz[0x00010]; /* SRQ context entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 32 bytes */
+/* -------------- */
+ pseudo_bit_t mpt_entry_sz[0x00010]; /* MPT entry size in Bytes for the device.
+ For the InfiniHost-III-EX MT25208 entry size is 64 bytes */
+ pseudo_bit_t mtt_entry_sz[0x00010]; /* MTT entry size in Bytes for the device.
+ For the InfiniHost-III-EX MT25208 entry size is 8 bytes */
+/* -------------- */
+ pseudo_bit_t bmme[0x00001]; /* Base Memory Management Extension Support */
+ pseudo_bit_t win_type[0x00001]; /* Bound Type 2 Memory Window Association mechanism:
+ 0 - Type 2A - QP Number Association; or
+ 1 - Type 2B - QP Number and PD Association. */
+ pseudo_bit_t mps[0x00001]; /* Ability of this HCA to support multiple page sizes per Memory Region. */
+ pseudo_bit_t bl[0x00001]; /* Ability of this HCA to support Block List Physical Buffer Lists. (The device does not supports Block List) */
+ pseudo_bit_t zb[0x00001]; /* Zero Based region/windows supported */
+ pseudo_bit_t lif[0x00001]; /* Ability of this HCA to support Local Invalidate Fencing. */
+ pseudo_bit_t reserved38[0x00002];
+ pseudo_bit_t log_pbl_sz[0x00006]; /* Log2 of the Maximum Physical Buffer List size in Bytes supported by this HCA when invoking the Allocate L_Key verb.
+ */
+ pseudo_bit_t reserved39[0x00012];
+/* -------------- */
+ pseudo_bit_t resd_lkey[0x00020]; /* The value of the reserved Lkey for Base Memory Management Extension */
+/* -------------- */
+ pseudo_bit_t lamr[0x00001]; /* When set the device requires local attached memory in order to operate.
+ When set, ICM pages, Firmware Area and ICM auxiliary pages must be allocated in the local attached memory. */
+ pseudo_bit_t reserved40[0x0001f];
+/* -------------- */
+ pseudo_bit_t max_icm_size_h[0x00020];/* Bits [63:32] of maximum ICM size InfiniHost III Ex support in bytes. */
+/* -------------- */
+ pseudo_bit_t max_icm_size_l[0x00020];/* Bits [31:0] of maximum ICM size InfiniHost III Ex support in bytes. */
+/* -------------- */
+ pseudo_bit_t reserved41[0x002c0];
+/* -------------- */
+};
+
+/* QUERY_ADAPTER Parameters Block */
+
+struct arbelprm_query_adapter_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00018];
+ pseudo_bit_t intapin[0x00008]; /* Driver should set this field to INTR value in the event queue in order to get Express interrupt messages. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00060];
+/* -------------- */
+ struct arbelprm_vsd_st vsd;
+/* -------------- */
+};
+
+/* QUERY_FW Parameters Block */
+
+struct arbelprm_query_fw_st { /* Little Endian */
+ pseudo_bit_t fw_rev_major[0x00010]; /* Firmware Revision - Major */
+ pseudo_bit_t fw_pages[0x00010]; /* Amount of physical memory to be allocated for FW usage is in 4KByte pages. */
+/* -------------- */
+ pseudo_bit_t fw_rev_minor[0x00010]; /* Firmware Revision - Minor */
+ pseudo_bit_t fw_rev_subminor[0x00010];/* Firmware Sub-minor version (Patch level). */
+/* -------------- */
+ pseudo_bit_t cmd_interface_rev[0x00010];/* Command Interface Interpreter Revision ID */
+ pseudo_bit_t reserved0[0x0000e];
+ pseudo_bit_t wqe_h_mode[0x00001]; /* Hermon mode. If '1', then WQE and AV format is the advanced format */
+ pseudo_bit_t zb_wq_cq[0x00001]; /* If '1', then ZB mode of WQ and CQ are enabled (i.e. real Memfree PRM is supported) */
+/* -------------- */
+ pseudo_bit_t log_max_outstanding_cmd[0x00008];/* Log2 of the maximum number of commands the HCR can support simultaneously */
+ pseudo_bit_t reserved1[0x00017];
+ pseudo_bit_t dt[0x00001]; /* Debug Trace Support
+ 0 - Debug trace is not supported
+ 1 - Debug trace is supported */
+/* -------------- */
+ pseudo_bit_t cmd_interface_db[0x00001];/* Set if the device accepts commands by means of special doorbells */
+ pseudo_bit_t reserved2[0x0001f];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+ pseudo_bit_t clr_int_base_addr_h[0x00020];/* Bits [63:32] of Clear interrupt register physical address.
+ Points to 64 bit register. */
+/* -------------- */
+ pseudo_bit_t clr_int_base_addr_l[0x00020];/* Bits [31:0] of Clear interrupt register physical address.
+ Points to 64 bit register. */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00040];
+/* -------------- */
+ pseudo_bit_t error_buf_start_h[0x00020];/* Read Only buffer for catastrophic error reports (physical address) */
+/* -------------- */
+ pseudo_bit_t error_buf_start_l[0x00020];/* Read Only buffer for catastrophic error reports (physical address) */
+/* -------------- */
+ pseudo_bit_t error_buf_size[0x00020];/* Size in words */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t eq_arm_base_addr_h[0x00020];/* Bits [63:32] of EQ Arm DBs physical address.
+ Points to 64 bit register.
+ Setting bit x in the offset, arms EQ number x.
+ */
+/* -------------- */
+ pseudo_bit_t eq_arm_base_addr_l[0x00020];/* Bits [31:0] of EQ Arm DBs physical address.
+ Points to 64 bit register.
+ Setting bit x in the offset, arms EQ number x. */
+/* -------------- */
+ pseudo_bit_t eq_set_ci_base_addr_h[0x00020];/* Bits [63:32] of EQ Set CI DBs Table physical address.
+ Points to a the EQ Set CI DBs Table base address. */
+/* -------------- */
+ pseudo_bit_t eq_set_ci_base_addr_l[0x00020];/* Bits [31:0] of EQ Set CI DBs Table physical address.
+ Points to a the EQ Set CI DBs Table base address. */
+/* -------------- */
+ pseudo_bit_t cmd_db_dw1[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 1 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw0[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 0 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_dw3[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 3 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw2[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 2 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_dw5[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 5 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw4[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 4 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_dw7[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 7 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw6[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 6 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_addr_base_h[0x00020];/* High bits of cmd_db_addr_base, which cmd_db_dw offsets refer to. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_addr_base_l[0x00020];/* Low bits of cmd_db_addr_base, which cmd_db_dw offsets refer to. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t reserved6[0x004c0];
+/* -------------- */
+};
+
+/* ACCESS_LAM */
+
+struct arbelprm_access_lam_st { /* Little Endian */
+ struct arbelprm_access_lam_inject_errors_st access_lam_inject_errors;
+/* -------------- */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+};
+
+/* ENABLE_LAM Parameters Block */
+
+struct arbelprm_enable_lam_st { /* Little Endian */
+ pseudo_bit_t lam_start_adr_h[0x00020];/* LAM start address [63:32] */
+/* -------------- */
+ pseudo_bit_t lam_start_adr_l[0x00020];/* LAM start address [31:0] */
+/* -------------- */
+ pseudo_bit_t lam_end_adr_h[0x00020];/* LAM end address [63:32] */
+/* -------------- */
+ pseudo_bit_t lam_end_adr_l[0x00020];/* LAM end address [31:0] */
+/* -------------- */
+ pseudo_bit_t di[0x00002]; /* Data Integrity Configuration:
+ 00 - none
+ 01 - Parity
+ 10 - ECC Detection Only
+ 11 - ECC With Correction */
+ pseudo_bit_t ap[0x00002]; /* Auto Precharge Mode
+ 00 - No auto precharge
+ 01 - Auto precharge per transaction
+ 10 - Auto precharge per 64 bytes
+ 11 - reserved */
+ pseudo_bit_t dh[0x00001]; /* When set, LAM is Hidden and can not be accessed directly from the PCI bus. */
+ pseudo_bit_t reserved0[0x0001b];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00160];
+/* -------------- */
+ struct arbelprm_dimminfo_st dimm0; /* Logical DIMM 0 Parameters */
+/* -------------- */
+ struct arbelprm_dimminfo_st dimm1; /* Logical DIMM 1 Parameters */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00400];
+/* -------------- */
+};
+
+/* Memory Access Parameters for UD Address Vector Table */
+
+struct arbelprm_udavtable_memory_parameters_st { /* Little Endian */
+ pseudo_bit_t l_key[0x00020]; /* L_Key used to access TPT */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* PD used by TPT for matching against PD of region entry being accessed. */
+ pseudo_bit_t reserved0[0x00005];
+ pseudo_bit_t xlation_en[0x00001]; /* When cleared, address is physical address and no translation will be done. When set, address is virtual. */
+ pseudo_bit_t reserved1[0x00002];
+/* -------------- */
+};
+
+/* INIT_HCA & QUERY_HCA Parameters Block */
+
+struct arbelprm_init_hca_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00060];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00010];
+ pseudo_bit_t time_stamp_granularity[0x00008];/* This field controls the granularity in which CQE Timestamp counter is incremented.
+ The TimeStampGranularity units is 1/4 of a microseconds. (e.g is TimeStampGranularity is configured to 0x2, CQE Timestamp will be incremented every one microsecond)
+ When sets to Zero, timestamp reporting in the CQE is disabled.
+ This feature is currently not supported.
+ */
+ pseudo_bit_t hca_core_clock[0x00008];/* Internal Clock Period (in units of 1/16 ns) (QUERY_HCA only) */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00008];
+ pseudo_bit_t router_qp[0x00010]; /* Upper 16 bit to be used as a QP number for router mode. Low order 8 bits are taken from the TClass field of the incoming packet.
+ Valid only if RE bit is set */
+ pseudo_bit_t reserved3[0x00007];
+ pseudo_bit_t re[0x00001]; /* Router Mode Enable
+ If this bit is set, entire packet (including all headers and ICRC) will be considered as a data payload and will be scattered to memory as specified in the descriptor that is posted on the QP matching the TClass field of packet. */
+/* -------------- */
+ pseudo_bit_t udp[0x00001]; /* UD Port Check Enable
+ 0 - Port field in Address Vector is ignored
+ 1 - HCA will check the port field in AV entry (fetched for UD descriptor) against the Port of the UD QP executing the descriptor. */
+ pseudo_bit_t he[0x00001]; /* Host Endianess - Used for Atomic Operations
+ 0 - Host is Little Endian
+ 1 - Host is Big endian
+ */
+ pseudo_bit_t reserved4[0x00001];
+ pseudo_bit_t ce[0x00001]; /* Checksum Enabled - when Set IPoverIB checksum generation & checking is enabled */
+ pseudo_bit_t sph[0x00001]; /* 0 - SW calculates TCP/UDP Pseudo-Header checksum and inserts it into the TCP/UDP checksum field when sending a packet
+ 1 - HW calculates TCP/UDP Pseudo-Header checksum when sending a packet
+ */
+ pseudo_bit_t rph[0x00001]; /* 0 - Not HW calculation of TCP/UDP Pseudo-Header checksum are done when receiving a packet
+ 1 - HW calculates TCP/UDP Pseudo-Header checksum when receiving a packet
+ */
+ pseudo_bit_t reserved5[0x00002];
+ pseudo_bit_t responder_exu[0x00004];/* Indicate the relation between the execution enegines allocation dedicated for responder versus the engines dedicated for reqvester .
+ responder_exu/16 = (number of responder exu engines)/(total number of engines)
+ Legal values are 0x0-0xF. 0 is "auto".
+
+ */
+ pseudo_bit_t reserved6[0x00004];
+ pseudo_bit_t wqe_quota[0x0000f]; /* Maximum number of WQEs that are executed prior to preemption of execution unit. 0 - reserved. */
+ pseudo_bit_t wqe_quota_en[0x00001]; /* If set - wqe_quota field is used. If cleared - WQE quota is set to "auto" value */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00040];
+/* -------------- */
+ struct arbelprm_qpcbaseaddr_st qpc_eec_cqc_eqc_rdb_parameters;
+/* -------------- */
+ pseudo_bit_t reserved8[0x00100];
+/* -------------- */
+ struct arbelprm_multicastparam_st multicast_parameters;
+/* -------------- */
+ pseudo_bit_t reserved9[0x00080];
+/* -------------- */
+ struct arbelprm_tptparams_st tpt_parameters;
+/* -------------- */
+ pseudo_bit_t reserved10[0x00080];
+/* -------------- */
+ struct arbelprm_uar_params_st uar_parameters;/* UAR Parameters */
+/* -------------- */
+ pseudo_bit_t reserved11[0x00600];
+/* -------------- */
+};
+
+/* Event Queue Context Table Entry */
+
+struct arbelprm_eqc_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t st[0x00004]; /* Event delivery state machine
+ 0x9 - Armed
+ 0xA - Fired
+ 0xB - Always_Armed (auto-rearm)
+ other - reserved */
+ pseudo_bit_t reserved1[0x00005];
+ pseudo_bit_t oi[0x00001]; /* Oerrun ignore.
+ If set, HW will not check EQ full condition when writing new EQEs. */
+ pseudo_bit_t tr[0x00001]; /* Translation Required. If set - EQ access undergo address translation. */
+ pseudo_bit_t reserved2[0x00005];
+ pseudo_bit_t owner[0x00004]; /* 0 - SW ownership
+ 1 - HW ownership
+ Valid for the QUERY_EQ and HW2SW_EQ commands only */
+ pseudo_bit_t status[0x00004]; /* EQ status:
+ 0000 - OK
+ 1010 - EQ write failure
+ Valid for the QUERY_EQ and HW2SW_EQ commands only */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start Address of Event Queue[63:32]. */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start Address of Event Queue[31:0].
+ Must be aligned on 32-byte boundary */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00018];
+ pseudo_bit_t log_eq_size[0x00005]; /* Amount of entries in this EQ is 2^log_eq_size.
+ Log_eq_size must be bigger than 1.
+ Maximum EQ size is 2^17 EQEs (max Log_eq_size is 17). */
+ pseudo_bit_t reserved4[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t intr[0x00008]; /* Interrupt (message) to be generated to report event to INT layer.
+ 00iiiiii - set to INTA given in QUERY_ADAPTER in order to generate INTA messages on Express.
+ 10jjjjjj - specificies type of interrupt message to be generated (total 64 different messages supported).
+ All other values are reserved and should not be used.
+
+ If interrupt generation is not required, ST field must be set upon creation to Fired state. No EQ arming doorbell should be performed. In this case hardware will not generate any interrupt. */
+ pseudo_bit_t reserved6[0x00018];
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* PD to be used to access EQ */
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t lkey[0x00020]; /* Memory key (L-Key) to be used to access EQ */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00040];
+/* -------------- */
+ pseudo_bit_t consumer_indx[0x00020];/* Contains next entry to be read upon polling the event queue.
+ Must be initalized to zero while opening EQ */
+/* -------------- */
+ pseudo_bit_t producer_indx[0x00020];/* Contains next entry in EQ to be written by the HCA.
+ Must be initalized to zero while opening EQ. */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00080];
+/* -------------- */
+};
+
+/* Memory Translation Table (MTT) Entry */
+
+struct arbelprm_mtt_st { /* Little Endian */
+ pseudo_bit_t ptag_h[0x00020]; /* High-order bits of physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */
+/* -------------- */
+ pseudo_bit_t p[0x00001]; /* Present bit. If set, page entry is valid. If cleared, access to this page will generate non-present page access fault. */
+ pseudo_bit_t reserved0[0x0000b];
+ pseudo_bit_t ptag_l[0x00014]; /* Low-order bits of Physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */
+/* -------------- */
+};
+
+/* Memory Protection Table (MPT) Entry */
+
+struct arbelprm_mpt_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t r_w[0x00001]; /* Defines whether this entry is Region (1) or Window (0) */
+ pseudo_bit_t pa[0x00001]; /* Physical address. If set, no virtual-to-physical address translation will be performed for this region */
+ pseudo_bit_t lr[0x00001]; /* If set - local read access enabled */
+ pseudo_bit_t lw[0x00001]; /* If set - local write access enabled */
+ pseudo_bit_t rr[0x00001]; /* If set - remote read access enabled. */
+ pseudo_bit_t rw[0x00001]; /* If set - remote write access enabled */
+ pseudo_bit_t a[0x00001]; /* If set - remote Atomic access is enabled */
+ pseudo_bit_t eb[0x00001]; /* If set - Bind is enabled. Valid for region entry only. */
+ pseudo_bit_t reserved1[0x0000c];
+ pseudo_bit_t status[0x00004]; /* Region/Window Status
+ 0xF - not valid (SW ownership)
+ 0x3 - FREE state
+ else - HW ownership
+ Unbound Type I windows are doneted reg_wnd_len field equals zero.
+ Unbound Type II windows are donated by Status=FREE. */
+/* -------------- */
+ pseudo_bit_t page_size[0x00005]; /* Page size used for the region. Actual size is [4K]*2^Page_size bytes.
+ page_size should be less than 20. */
+ pseudo_bit_t reserved2[0x00002];
+ pseudo_bit_t type[0x00001]; /* Applicable for windows only, must be zero for regions
+ 0 - Type one window
+ 1 - Type two window */
+ pseudo_bit_t qpn[0x00018]; /* QP number this MW is attached to. Valid for type2 memory windows and on QUERY_MPT only */
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* The memory Key. The field holds the mem_key field in the following semantics: {key[7:0],key[31:8]}.
+ */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* Protection Domain */
+ pseudo_bit_t reserved3[0x00001];
+ pseudo_bit_t ei[0x00001]; /* Enable Invalidation - When set, Local/Remote invalidation can be executed on this window/region.
+ Must be set for type2 windows and non-shared physical memory regions.
+ Must be clear for regions that are used to access Work Queues, Completion Queues and Event Queues */
+ pseudo_bit_t zb[0x00001]; /* When set, this region is Zero Based Region */
+ pseudo_bit_t fre[0x00001]; /* When set, Fast Registration Operations can be executed on this region */
+ pseudo_bit_t rae[0x00001]; /* When set, remote access can be enabled on this region.
+ Used when executing Fast Registration Work Request to validate that remote access rights can be granted to this MPT.
+ If the bit is cleared, Fast Registration Work Request requesting remote access rights will fail.
+ */
+ pseudo_bit_t reserved4[0x00003];
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start Address[63:32] - Virtual Address where this region/window starts */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start Address[31:0] - Virtual Address where this region/window starts */
+/* -------------- */
+ pseudo_bit_t reg_wnd_len_h[0x00020];/* Region/Window Length[63:32] */
+/* -------------- */
+ pseudo_bit_t reg_wnd_len_l[0x00020];/* Region/Window Length[31:0] */
+/* -------------- */
+ pseudo_bit_t lkey[0x00020]; /* Must be 0 for SW2HW_MPT.
+ On QUERY_MPT and HW2SW_MPT commands for Memory Window it reflects the LKey of the Region that the Window is bound to.
+ The field holds the lkey field in the following semantics: {key[7:0],key[31:8]}. */
+/* -------------- */
+ pseudo_bit_t win_cnt[0x00020]; /* Number of windows bound to this region. Valid for regions only.
+ The field is valid only for the QUERY_MPT and HW2SW_MPT commands. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t mtt_adr_h[0x00006]; /* Base (first) address of the MTT relative to MTT base in the ICM */
+ pseudo_bit_t reserved6[0x0001a];
+/* -------------- */
+ pseudo_bit_t reserved7[0x00003];
+ pseudo_bit_t mtt_adr_l[0x0001d]; /* Base (first) address of the MTT relative to MTT base address in the ICM. Must be aligned on 8 bytes. */
+/* -------------- */
+ pseudo_bit_t mtt_sz[0x00020]; /* Number of MTT entries allocated for this MR.
+ When Fast Registration Operations can not be executed on this region (FRE bit is zero) this field is reserved.
+ When Fast Registration Operation is enabled (FRE bit is set) this field indicates the number of MTTs allocated for this MR. If mtt_sz value is zero, there is no limit for the numbers of MTTs and the HCA does not check this field when executing fast register WQE. */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00040];
+/* -------------- */
+};
+
+/* Completion Queue Context Table Entry */
+
+struct arbelprm_completion_queue_context_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t st[0x00004]; /* Event delivery state machine
+ 0x0 - reserved
+ 0x9 - ARMED (Request for Notification)
+ 0x6 - ARMED SOLICITED (Request Solicited Notification)
+ 0xA - FIRED
+ other - reserved
+
+ Must be 0x0 in CQ initialization.
+ Valid for the QUERY_CQ and HW2SW_CQ commands only. */
+ pseudo_bit_t reserved1[0x00005];
+ pseudo_bit_t oi[0x00001]; /* When set, overrun ignore is enabled.
+ When set, Updates of CQ consumer counter (poll for completion) or Request completion notifications (Arm CQ) doorbells should not be rang on that CQ. */
+ pseudo_bit_t reserved2[0x0000a];
+ pseudo_bit_t status[0x00004]; /* CQ status
+ 0000 - OK
+ 1001 - CQ overflow
+ 1010 - CQ write failure
+ Valid for the QUERY_CQ and HW2SW_CQ commands only */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start address of CQ[63:32].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start address of CQ[31:0].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t usr_page[0x00018]; /* UAR page this CQ can be accessed through (ringinig CQ doorbells) */
+ pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries).
+ Maximum CQ size is 2^17 CQEs (max log_cq_size is 17) */
+ pseudo_bit_t reserved3[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t c_eqn[0x00008]; /* Event Queue this CQ reports completion events to.
+ Valid values are 0 to 63
+ If configured to value other than 0-63, completion events will not be reported on the CQ. */
+ pseudo_bit_t reserved5[0x00018];
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* Protection Domain to be used to access CQ.
+ Must be the same PD of the CQ L_Key. */
+ pseudo_bit_t reserved6[0x00008];
+/* -------------- */
+ pseudo_bit_t l_key[0x00020]; /* Memory key (L_Key) to be used to access CQ */
+/* -------------- */
+ pseudo_bit_t last_notified_indx[0x00020];/* Maintained by HW.
+ Valid for QUERY_CQ and HW2SW_CQ commands only. */
+/* -------------- */
+ pseudo_bit_t solicit_producer_indx[0x00020];/* Maintained by HW.
+ Valid for QUERY_CQ and HW2SW_CQ commands only.
+ */
+/* -------------- */
+ pseudo_bit_t consumer_counter[0x00020];/* Consumer counter is a 32bits counter that is incremented for each CQE pooled from the CQ.
+ Must be 0x0 in CQ initialization.
+ Valid for the QUERY_CQ and HW2SW_CQ commands only. */
+/* -------------- */
+ pseudo_bit_t producer_counter[0x00020];/* Producer counter is a 32bits counter that is incremented for each CQE that is written by the HW to the CQ.
+ CQ overrun is reported if Producer_counter + 1 equals to Consumer_counter and a CQE needs to be added..
+ Maintained by HW (valid for the QUERY_CQ and HW2SW_CQ commands only) */
+/* -------------- */
+ pseudo_bit_t cqn[0x00018]; /* CQ number. Least significant bits are constrained by the position of this CQ in CQC table
+ Valid for the QUERY_CQ and HW2SW_CQ commands only */
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t cq_ci_db_record[0x00020];/* Index in the UAR Context Table Entry.
+ HW uses this index as an offset from the UAR Context Table Entry in order to read this CQ Consumer Counter doorbell record.
+ This value can be retrieved from the HW in the QUERY_CQ command. */
+/* -------------- */
+ pseudo_bit_t cq_state_db_record[0x00020];/* Index in the UAR Context Table Entry.
+ HW uses this index as an offset from the UAR Context Table Entry in order to read this CQ state doorbell record.
+ This value can be retrieved from the HW in the QUERY_CQ command. */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00020];
+/* -------------- */
+};
+
+/* GPIO_event_data */
+
+struct arbelprm_gpio_event_data_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00060];
+/* -------------- */
+ pseudo_bit_t gpio_event_hi[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */
+/* -------------- */
+ pseudo_bit_t gpio_event_lo[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+};
+
+/* Event_data Field - QP/EE Events */
+
+struct arbelprm_qp_ee_event_st { /* Little Endian */
+ pseudo_bit_t qpn_een[0x00018]; /* QP/EE/SRQ number event is reported for */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved2[0x0001c];
+ pseudo_bit_t e_q[0x00001]; /* If set - EEN if cleared - QP in the QPN/EEN field
+ Not valid on SRQ events */
+ pseudo_bit_t reserved3[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00060];
+/* -------------- */
+};
+
+/* InfiniHost-III-EX Type0 Configuration Header */
+
+struct arbelprm_mt25208_type0_st { /* Little Endian */
+ pseudo_bit_t vendor_id[0x00010]; /* Hardwired to 0x15B3 */
+ pseudo_bit_t device_id[0x00010]; /* 25208 (decimal) - InfiniHost-III compatible mode
+ 25218 (decimal) - InfiniHost-III EX mode (the mode described in this manual)
+ 25209 (decimal) - Flash burner mode - see Flash burning application note for further details on this mode
+ */
+/* -------------- */
+ pseudo_bit_t command[0x00010]; /* PCI Command Register */
+ pseudo_bit_t status[0x00010]; /* PCI Status Register */
+/* -------------- */
+ pseudo_bit_t revision_id[0x00008];
+ pseudo_bit_t class_code_hca_class_code[0x00018];
+/* -------------- */
+ pseudo_bit_t cache_line_size[0x00008];/* Cache Line Size */
+ pseudo_bit_t latency_timer[0x00008];
+ pseudo_bit_t header_type[0x00008]; /* hardwired to zero */
+ pseudo_bit_t bist[0x00008];
+/* -------------- */
+ pseudo_bit_t bar0_ctrl[0x00004]; /* hard-wired to 0100 */
+ pseudo_bit_t reserved0[0x00010];
+ pseudo_bit_t bar0_l[0x0000c]; /* Lower bits of BAR0 (Device Configuration Space) */
+/* -------------- */
+ pseudo_bit_t bar0_h[0x00020]; /* Upper 32 bits of BAR0 (Device Configuration Space) */
+/* -------------- */
+ pseudo_bit_t bar1_ctrl[0x00004]; /* Hardwired to 1100 */
+ pseudo_bit_t reserved1[0x00010];
+ pseudo_bit_t bar1_l[0x0000c]; /* Lower bits of BAR1 (User Access Region - UAR - space) */
+/* -------------- */
+ pseudo_bit_t bar1_h[0x00020]; /* upper 32 bits of BAR1 (User Access Region - UAR - space) */
+/* -------------- */
+ pseudo_bit_t bar2_ctrl[0x00004]; /* Hardwired to 1100 */
+ pseudo_bit_t reserved2[0x00010];
+ pseudo_bit_t bar2_l[0x0000c]; /* Lower bits of BAR2 - Local Attached Memory if present and enabled. Else zeroed. */
+/* -------------- */
+ pseudo_bit_t bar2_h[0x00020]; /* Upper 32 bits of BAR2 - Local Attached Memory if present and enabled. Else zeroed. */
+/* -------------- */
+ pseudo_bit_t cardbus_cis_pointer[0x00020];
+/* -------------- */
+ pseudo_bit_t subsystem_vendor_id[0x00010];/* Specified by the device NVMEM configuration */
+ pseudo_bit_t subsystem_id[0x00010]; /* Specified by the device NVMEM configuration */
+/* -------------- */
+ pseudo_bit_t expansion_rom_enable[0x00001];/* Expansion ROM Enable. Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */
+ pseudo_bit_t reserved3[0x0000a];
+ pseudo_bit_t expansion_rom_base_address[0x00015];/* Expansion ROM Base Address (upper 21 bit). Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */
+/* -------------- */
+ pseudo_bit_t capabilities_pointer[0x00008];/* Specified by the device NVMEM configuration */
+ pseudo_bit_t reserved4[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t interrupt_line[0x00008];
+ pseudo_bit_t interrupt_pin[0x00008];
+ pseudo_bit_t min_gnt[0x00008];
+ pseudo_bit_t max_latency[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved6[0x00100];
+/* -------------- */
+ pseudo_bit_t msi_cap_id[0x00008];
+ pseudo_bit_t msi_next_cap_ptr[0x00008];
+ pseudo_bit_t msi_en[0x00001];
+ pseudo_bit_t multiple_msg_cap[0x00003];
+ pseudo_bit_t multiple_msg_en[0x00003];
+ pseudo_bit_t cap_64_bit_addr[0x00001];
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t msg_addr_l[0x00020];
+/* -------------- */
+ pseudo_bit_t msg_addr_h[0x00020];
+/* -------------- */
+ pseudo_bit_t msg_data[0x00010];
+ pseudo_bit_t reserved8[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved9[0x00080];
+/* -------------- */
+ pseudo_bit_t pm_cap_id[0x00008]; /* Power management capability ID - 01h */
+ pseudo_bit_t pm_next_cap_ptr[0x00008];
+ pseudo_bit_t pm_cap[0x00010]; /* [2:0] Version - 02h
+ [3] PME clock - 0h
+ [4] RsvP
+ [5] Device specific initialization - 0h
+ [8:6] AUX current - 0h
+ [9] D1 support - 0h
+ [10] D2 support - 0h
+ [15:11] PME support - 0h */
+/* -------------- */
+ pseudo_bit_t pm_status_control[0x00010];/* [14:13] - Data scale - 0h */
+ pseudo_bit_t pm_control_status_brdg_ext[0x00008];
+ pseudo_bit_t data[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved10[0x00040];
+/* -------------- */
+ pseudo_bit_t vpd_cap_id[0x00008]; /* 03h */
+ pseudo_bit_t vpd_next_cap_id[0x00008];
+ pseudo_bit_t vpd_address[0x0000f];
+ pseudo_bit_t f[0x00001];
+/* -------------- */
+ pseudo_bit_t vpd_data[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved11[0x00040];
+/* -------------- */
+ pseudo_bit_t pciex_cap_id[0x00008]; /* PCI-Express capability ID - 10h */
+ pseudo_bit_t pciex_next_cap_ptr[0x00008];
+ pseudo_bit_t pciex_cap[0x00010]; /* [3:0] Capability version - 1h
+ [7:4] Device/Port Type - 0h
+ [8] Slot implemented - 0h
+ [13:9] Interrupt message number
+ */
+/* -------------- */
+ pseudo_bit_t device_cap[0x00020]; /* [2:0] Max_Payload_Size supported - 2h
+ [4:3] Phantom Function supported - 0h
+ [5] Extended Tag Filed supported - 0h
+ [8:6] Endpoint L0s Acceptable Latency - TBD
+ [11:9] Endpoint L1 Acceptable Latency - TBD
+ [12] Attention Button Present - configured through InfiniBurn
+ [13] Attention Indicator Present - configured through InfiniBurn
+ [14] Power Indicator Present - configured through InfiniBurn
+ [25:18] Captured Slot Power Limit Value
+ [27:26] Captured Slot Power Limit Scale */
+/* -------------- */
+ pseudo_bit_t device_control[0x00010];
+ pseudo_bit_t device_status[0x00010];
+/* -------------- */
+ pseudo_bit_t link_cap[0x00020]; /* [3:0] Maximum Link Speed - 1h
+ [9:4] Maximum Link Width - 8h
+ [11:10] Active State Power Management Support - 3h
+ [14:12] L0s Exit Latency - TBD
+ [17:15] L1 Exit Latency - TBD
+ [31:24] Port Number - 0h */
+/* -------------- */
+ pseudo_bit_t link_control[0x00010];
+ pseudo_bit_t link_status[0x00010]; /* [3:0] Link Speed - 1h
+ [9:4] Negotiated Link Width
+ [12] Slot clock configuration - 1h */
+/* -------------- */
+ pseudo_bit_t reserved12[0x00260];
+/* -------------- */
+ pseudo_bit_t advanced_error_reporting_cap_id[0x00010];/* 0001h. */
+ pseudo_bit_t capability_version[0x00004];/* 1h */
+ pseudo_bit_t next_capability_offset[0x0000c];/* 0h */
+/* -------------- */
+ pseudo_bit_t uncorrectable_error_status_register[0x00020];/* 0 Training Error Status
+ 4 Data Link Protocol Error Status
+ 12 Poisoned TLP Status
+ 13 Flow Control Protocol Error Status
+ 14 Completion Timeout Status
+ 15 Completer Abort Status
+ 16 Unexpected Completion Status
+ 17 Receiver Overflow Status
+ 18 Malformed TLP Status
+ 19 ECRC Error Status
+ 20 Unsupported Request Error Status */
+/* -------------- */
+ pseudo_bit_t uncorrectable_error_mask_register[0x00020];/* 0 Training Error Mask
+ 4 Data Link Protocol Error Mask
+ 12 Poisoned TLP Mask
+ 13 Flow Control Protocol Error Mask
+ 14 Completion Timeout Mask
+ 15 Completer Abort Mask
+ 16 Unexpected Completion Mask
+ 17 Receiver Overflow Mask
+ 18 Malformed TLP Mask
+ 19 ECRC Error Mask
+ 20 Unsupported Request Error Mask */
+/* -------------- */
+ pseudo_bit_t uncorrectable_severity_mask_register[0x00020];/* 0 Training Error Severity
+ 4 Data Link Protocol Error Severity
+ 12 Poisoned TLP Severity
+ 13 Flow Control Protocol Error Severity
+ 14 Completion Timeout Severity
+ 15 Completer Abort Severity
+ 16 Unexpected Completion Severity
+ 17 Receiver Overflow Severity
+ 18 Malformed TLP Severity
+ 19 ECRC Error Severity
+ 20 Unsupported Request Error Severity */
+/* -------------- */
+ pseudo_bit_t correctable_error_status_register[0x00020];/* 0 Receiver Error Status
+ 6 Bad TLP Status
+ 7 Bad DLLP Status
+ 8 REPLAY_NUM Rollover Status
+ 12 Replay Timer Timeout Status */
+/* -------------- */
+ pseudo_bit_t correctable_error_mask_register[0x00020];/* 0 Receiver Error Mask
+ 6 Bad TLP Mask
+ 7 Bad DLLP Mask
+ 8 REPLAY_NUM Rollover Mask
+ 12 Replay Timer Timeout Mask */
+/* -------------- */
+ pseudo_bit_t advance_error_capabilities_and_control_register[0x00020];
+/* -------------- */
+ struct arbelprm_header_log_register_st header_log_register;
+/* -------------- */
+ pseudo_bit_t reserved13[0x006a0];
+/* -------------- */
+};
+
+/* Event Data Field - Performance Monitor */
+
+struct arbelprm_performance_monitor_event_st { /* Little Endian */
+ struct arbelprm_performance_monitors_st performance_monitor_snapshot;/* Performance monitor snapshot */
+/* -------------- */
+ pseudo_bit_t monitor_number[0x00008];/* 0x01 - SQPC
+ 0x02 - RQPC
+ 0x03 - CQC
+ 0x04 - Rkey
+ 0x05 - TLB
+ 0x06 - port0
+ 0x07 - port1 */
+ pseudo_bit_t reserved0[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+};
+
+/* Event_data Field - Page Faults */
+
+struct arbelprm_page_fault_event_data_st { /* Little Endian */
+ pseudo_bit_t va_h[0x00020]; /* Virtual Address[63:32] this page fault is reported on */
+/* -------------- */
+ pseudo_bit_t va_l[0x00020]; /* Virtual Address[63:32] this page fault is reported on */
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* Memory Key this page fault is reported on */
+/* -------------- */
+ pseudo_bit_t qp[0x00018]; /* QP this page fault is reported on */
+ pseudo_bit_t reserved0[0x00003];
+ pseudo_bit_t a[0x00001]; /* If set the memory access that caused the page fault was atomic */
+ pseudo_bit_t lw[0x00001]; /* If set the memory access that caused the page fault was local write */
+ pseudo_bit_t lr[0x00001]; /* If set the memory access that caused the page fault was local read */
+ pseudo_bit_t rw[0x00001]; /* If set the memory access that caused the page fault was remote write */
+ pseudo_bit_t rr[0x00001]; /* If set the memory access that caused the page fault was remote read */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* PD this page fault is reported on */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t prefetch_len[0x00020]; /* Indicates how many subsequent pages in the same memory region/window will be accessed by the following transaction after this page fault is resolved. measured in bytes. SW can use this information in order to page-in the subsequent pages if they are not present. */
+/* -------------- */
+};
+
+/* WQE segments format */
+
+struct arbelprm_wqe_segment_st { /* Little Endian */
+ struct arbelprm_send_wqe_segment_st send_wqe_segment;/* Send WQE segment format */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00280];
+/* -------------- */
+ struct arbelprm_wqe_segment_ctrl_mlx_st mlx_wqe_segment_ctrl;/* MLX WQE segment format */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00100];
+/* -------------- */
+ struct arbelprm_wqe_segment_ctrl_recv_st recv_wqe_segment_ctrl;/* Receive segment format */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00080];
+/* -------------- */
+};
+
+/* Event_data Field - Port State Change */
+
+struct arbelprm_port_state_change_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t reserved1[0x0001c];
+ pseudo_bit_t p[0x00002]; /* Port number (1 or 2) */
+ pseudo_bit_t reserved2[0x00002];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+};
+
+/* Event_data Field - Completion Queue Error */
+
+struct arbelprm_completion_queue_error_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t syndrome[0x00008]; /* Error syndrome
+ 0x01 - CQ overrun
+ 0x02 - CQ access violation error */
+ pseudo_bit_t reserved2[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+};
+
+/* Event_data Field - Completion Event */
+
+struct arbelprm_completion_event_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x000a0];
+/* -------------- */
+};
+
+/* Event Queue Entry */
+
+struct arbelprm_event_queue_entry_st { /* Little Endian */
+ pseudo_bit_t event_sub_type[0x00008];/* Event Sub Type.
+ Defined for events which have sub types, zero elsewhere. */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t event_type[0x00008]; /* Event Type */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t event_data[6][0x00020];/* Delivers auxilary data to handle event. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00007];
+ pseudo_bit_t owner[0x00001]; /* Owner of the entry
+ 0 SW
+ 1 HW */
+ pseudo_bit_t reserved3[0x00018];
+/* -------------- */
+};
+
+/* QP/EE State Transitions Command Parameters */
+
+struct arbelprm_qp_ee_state_transitions_st { /* Little Endian */
+ pseudo_bit_t opt_param_mask[0x00020];/* This field defines which optional parameters are passed. Each bit specifies whether optional parameter is passed (set) or not (cleared). The optparammask is defined for each QP/EE command. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ struct arbelprm_queue_pair_ee_context_entry_st qpc_eec_data;/* QPC/EEC data */
+/* -------------- */
+ pseudo_bit_t reserved1[0x009c0];
+/* -------------- */
+};
+
+/* Completion Queue Entry Format */
+
+struct arbelprm_completion_queue_entry_st { /* Little Endian */
+ pseudo_bit_t my_qpn[0x00018]; /* Indicates the QP for which completion is being reported */
+ pseudo_bit_t reserved0[0x00004];
+ pseudo_bit_t ver[0x00004]; /* CQE version.
+ 0 for InfiniHost-III-EX */
+/* -------------- */
+ pseudo_bit_t my_ee[0x00018]; /* EE context (for RD only).
+ Invalid for Bind and Nop operation on RD.
+ For non RD services this filed reports the CQE timestamp. The Timestamp is a free running counter that is incremented every TimeStampGranularity tick. The counter rolls-over when it reaches saturation. TimeStampGranularity is configured in the INIT_HCA command. This feature is currently not supported.
+ */
+ pseudo_bit_t checksum_15_8[0x00008];/* Checksum[15:8] - See IPoverIB checksum offloading chapter */
+/* -------------- */
+ pseudo_bit_t rqpn[0x00018]; /* Remote (source) QP number. Valid in Responder CQE only for Datagram QP. */
+ pseudo_bit_t checksum_7_0[0x00008]; /* Checksum[7:0] - See IPoverIB checksum offloading chapter */
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (source) LID of the message. Valid in Responder of UD QP CQE only. */
+ pseudo_bit_t ml_path[0x00007]; /* My (destination) LID path bits - these are the lowemost LMC bits of the DLID in an incoming UD packet, higher bits of this field, that are not part of the LMC bits are zeroed by HW.
+ Valid in responder of UD QP CQE only.
+ Invalid if incoming message DLID is the permissive LID or incoming message is multicast. */
+ pseudo_bit_t g[0x00001]; /* GRH present indicator. Valid in Responder of UD QP CQE only. */
+ pseudo_bit_t ipok[0x00001]; /* IP OK - See IPoverIB checksum offloading chapter */
+ pseudo_bit_t reserved1[0x00003];
+ pseudo_bit_t sl[0x00004]; /* Service Level of the message. Valid in Responder of UD QP CQE only. */
+/* -------------- */
+ pseudo_bit_t immediate_ethertype_pkey_indx_eecredits[0x00020];/* Valid for receive queue completion only.
+ If Opcode field indicates that this was send/write with immediate, this field contains immediate field of the packet.
+ If completion corresponds to RAW receive queue, bits 15:0 contain Ethertype field of the packet.
+ If completion corresponds to GSI receive queue, bits 31:16 contain index in PKey table that matches PKey of the message arrived.
+ If Opcode field indicates that this was send and invalidate, this field contains the key that was invalidated.
+ For CQE of send queue of the reliable connection service (but send and invalide), bits [4:0] of this field contain the encoded EEcredits received in last ACK of the message. */
+/* -------------- */
+ pseudo_bit_t byte_cnt[0x00020]; /* Byte count of data actually transferred (valid for receive queue completions only) */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00006];
+ pseudo_bit_t wqe_adr[0x0001a]; /* Bits 31:6 of WQE virtual address completion is reported for. The 6 least significant bits are zero. */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00007];
+ pseudo_bit_t owner[0x00001]; /* Owner field. Zero value of this field means SW ownership of CQE. */
+ pseudo_bit_t reserved4[0x0000f];
+ pseudo_bit_t s[0x00001]; /* If set, completion is reported for Send queue, if cleared - receive queue. */
+ pseudo_bit_t opcode[0x00008]; /* The opcode of WQE completion is reported for.
+ For CQEs corresponding to send completion, NOPCODE field of the WQE is copied to this field.
+ For CQEs corresponding to receive completions, opcode field of last packet in the message copied to this field.
+ For CQEs corresponding to the receive queue of QPs mapped to QP1, the opcode will be SEND with Immediate (messages are guaranteed to be SEND only)
+
+ The following values are reported in case of completion with error:
+ 0xFE - For completion with error on Receive Queues
+ 0xFF - For completion with error on Send Queues */
+/* -------------- */
+};
+
+/* */
+
+struct arbelprm_ecc_detect_event_data_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t cause_lsb[0x00001];
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t cause_msb[0x00001];
+ pseudo_bit_t reserved2[0x00002];
+ pseudo_bit_t err_rmw[0x00001];
+ pseudo_bit_t err_src_id[0x00003];
+ pseudo_bit_t err_da[0x00002];
+ pseudo_bit_t err_ba[0x00002];
+ pseudo_bit_t reserved3[0x00011];
+ pseudo_bit_t overflow[0x00001];
+/* -------------- */
+ pseudo_bit_t err_ra[0x00010];
+ pseudo_bit_t err_ca[0x00010];
+/* -------------- */
+};
+
+/* Event_data Field - ECC Detection Event */
+
+struct arbelprm_scrubbing_event_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t cause_lsb[0x00001]; /* data integrity error cause:
+ single ECC error in the 64bit lsb data, on the rise edge of the clock */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t cause_msb[0x00001]; /* data integrity error cause:
+ single ECC error in the 64bit msb data, on the fall edge of the clock */
+ pseudo_bit_t reserved2[0x00002];
+ pseudo_bit_t err_rmw[0x00001]; /* transaction type:
+ 0 - read
+ 1 - read/modify/write */
+ pseudo_bit_t err_src_id[0x00003]; /* source of the transaction: 0x4 - PCI, other - internal or IB */
+ pseudo_bit_t err_da[0x00002]; /* Error DIMM address */
+ pseudo_bit_t err_ba[0x00002]; /* Error bank address */
+ pseudo_bit_t reserved3[0x00011];
+ pseudo_bit_t overflow[0x00001]; /* Fatal: ECC error FIFO overflow - ECC errors were detected, which may or may not have been corrected by InfiniHost-III-EX */
+/* -------------- */
+ pseudo_bit_t err_ra[0x00010]; /* Error row address */
+ pseudo_bit_t err_ca[0x00010]; /* Error column address */
+/* -------------- */
+};
+
+/* Miscellaneous Counters */
+
+struct arbelprm_misc_counters_st { /* Little Endian */
+ pseudo_bit_t ddr_scan_cnt[0x00020]; /* Number of times whole of LAM was scanned */
+/* -------------- */
+ pseudo_bit_t reserved0[0x007e0];
+/* -------------- */
+};
+
+/* LAM_EN Output Parameter */
+
+struct arbelprm_lam_en_out_param_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+};
+
+/* Extended_Completion_Queue_Entry */
+
+struct arbelprm_extended_completion_queue_entry_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* */
+
+struct arbelprm_eq_cmd_doorbell_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* 0 */
+
+struct arbelprm_arbel_prm_st { /* Little Endian */
+ struct arbelprm_completion_queue_entry_st completion_queue_entry;/* Completion Queue Entry Format */
+/* -------------- */
+ pseudo_bit_t reserved0[0x7ff00];
+/* -------------- */
+ struct arbelprm_qp_ee_state_transitions_st qp_ee_state_transitions;/* QP/EE State Transitions Command Parameters */
+/* -------------- */
+ pseudo_bit_t reserved1[0x7f000];
+/* -------------- */
+ struct arbelprm_event_queue_entry_st event_queue_entry;/* Event Queue Entry */
+/* -------------- */
+ pseudo_bit_t reserved2[0x7ff00];
+/* -------------- */
+ struct arbelprm_completion_event_st completion_event;/* Event_data Field - Completion Event */
+/* -------------- */
+ pseudo_bit_t reserved3[0x7ff40];
+/* -------------- */
+ struct arbelprm_completion_queue_error_st completion_queue_error;/* Event_data Field - Completion Queue Error */
+/* -------------- */
+ pseudo_bit_t reserved4[0x7ff40];
+/* -------------- */
+ struct arbelprm_port_state_change_st port_state_change;/* Event_data Field - Port State Change */
+/* -------------- */
+ pseudo_bit_t reserved5[0x7ff40];
+/* -------------- */
+ struct arbelprm_wqe_segment_st wqe_segment;/* WQE segments format */
+/* -------------- */
+ pseudo_bit_t reserved6[0x7f000];
+/* -------------- */
+ struct arbelprm_page_fault_event_data_st page_fault_event_data;/* Event_data Field - Page Faults */
+/* -------------- */
+ pseudo_bit_t reserved7[0x7ff40];
+/* -------------- */
+ struct arbelprm_performance_monitor_event_st performance_monitor_event;/* Event Data Field - Performance Monitor */
+/* -------------- */
+ pseudo_bit_t reserved8[0xfff20];
+/* -------------- */
+ struct arbelprm_mt25208_type0_st mt25208_type0;/* InfiniHost-III-EX Type0 Configuration Header */
+/* -------------- */
+ pseudo_bit_t reserved9[0x7f000];
+/* -------------- */
+ struct arbelprm_qp_ee_event_st qp_ee_event;/* Event_data Field - QP/EE Events */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00040];
+/* -------------- */
+ struct arbelprm_gpio_event_data_st gpio_event_data;
+/* -------------- */
+ pseudo_bit_t reserved11[0x7fe40];
+/* -------------- */
+ struct arbelprm_ud_address_vector_st ud_address_vector;/* UD Address Vector */
+/* -------------- */
+ pseudo_bit_t reserved12[0x7ff00];
+/* -------------- */
+ struct arbelprm_queue_pair_ee_context_entry_st queue_pair_ee_context_entry;/* QP and EE Context Entry */
+/* -------------- */
+ pseudo_bit_t reserved13[0x7fa00];
+/* -------------- */
+ struct arbelprm_address_path_st address_path;/* Address Path */
+/* -------------- */
+ pseudo_bit_t reserved14[0x7ff00];
+/* -------------- */
+ struct arbelprm_completion_queue_context_st completion_queue_context;/* Completion Queue Context Table Entry */
+/* -------------- */
+ pseudo_bit_t reserved15[0x7fe00];
+/* -------------- */
+ struct arbelprm_mpt_st mpt; /* Memory Protection Table (MPT) Entry */
+/* -------------- */
+ pseudo_bit_t reserved16[0x7fe00];
+/* -------------- */
+ struct arbelprm_mtt_st mtt; /* Memory Translation Table (MTT) Entry */
+/* -------------- */
+ pseudo_bit_t reserved17[0x7ffc0];
+/* -------------- */
+ struct arbelprm_eqc_st eqc; /* Event Queue Context Table Entry */
+/* -------------- */
+ pseudo_bit_t reserved18[0x7fe00];
+/* -------------- */
+ struct arbelprm_performance_monitors_st performance_monitors;/* Performance Monitors */
+/* -------------- */
+ pseudo_bit_t reserved19[0x7ff80];
+/* -------------- */
+ struct arbelprm_hca_command_register_st hca_command_register;/* HCA Command Register (HCR) */
+/* -------------- */
+ pseudo_bit_t reserved20[0xfff20];
+/* -------------- */
+ struct arbelprm_init_hca_st init_hca;/* INIT_HCA & QUERY_HCA Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved21[0x7f000];
+/* -------------- */
+ struct arbelprm_qpcbaseaddr_st qpcbaseaddr;/* QPC/EEC/CQC/EQC/RDB Parameters */
+/* -------------- */
+ pseudo_bit_t reserved22[0x7fc00];
+/* -------------- */
+ struct arbelprm_udavtable_memory_parameters_st udavtable_memory_parameters;/* Memory Access Parameters for UD Address Vector Table */
+/* -------------- */
+ pseudo_bit_t reserved23[0x7ffc0];
+/* -------------- */
+ struct arbelprm_multicastparam_st multicastparam;/* Multicast Support Parameters */
+/* -------------- */
+ pseudo_bit_t reserved24[0x7ff00];
+/* -------------- */
+ struct arbelprm_tptparams_st tptparams;/* Translation and Protection Tables Parameters */
+/* -------------- */
+ pseudo_bit_t reserved25[0x7ff00];
+/* -------------- */
+ struct arbelprm_enable_lam_st enable_lam;/* ENABLE_LAM Parameters Block */
+/* -------------- */
+ struct arbelprm_access_lam_st access_lam;
+/* -------------- */
+ pseudo_bit_t reserved26[0x7f700];
+/* -------------- */
+ struct arbelprm_dimminfo_st dimminfo;/* Logical DIMM Information */
+/* -------------- */
+ pseudo_bit_t reserved27[0x7ff00];
+/* -------------- */
+ struct arbelprm_query_fw_st query_fw;/* QUERY_FW Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved28[0x7f800];
+/* -------------- */
+ struct arbelprm_query_adapter_st query_adapter;/* QUERY_ADAPTER Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved29[0x7f800];
+/* -------------- */
+ struct arbelprm_query_dev_lim_st query_dev_lim;/* Query Device Limitations */
+/* -------------- */
+ pseudo_bit_t reserved30[0x7f800];
+/* -------------- */
+ struct arbelprm_uar_params_st uar_params;/* UAR Parameters */
+/* -------------- */
+ pseudo_bit_t reserved31[0x7ff00];
+/* -------------- */
+ struct arbelprm_init_ib_st init_ib; /* INIT_IB Parameters */
+/* -------------- */
+ pseudo_bit_t reserved32[0x7f800];
+/* -------------- */
+ struct arbelprm_mgm_entry_st mgm_entry;/* Multicast Group Member */
+/* -------------- */
+ pseudo_bit_t reserved33[0x7fe00];
+/* -------------- */
+ struct arbelprm_set_ib_st set_ib; /* SET_IB Parameters */
+/* -------------- */
+ pseudo_bit_t reserved34[0x7fe00];
+/* -------------- */
+ struct arbelprm_rd_send_doorbell_st rd_send_doorbell;/* RD-send doorbell */
+/* -------------- */
+ pseudo_bit_t reserved35[0x7ff80];
+/* -------------- */
+ struct arbelprm_send_doorbell_st send_doorbell;/* Send doorbell */
+/* -------------- */
+ pseudo_bit_t reserved36[0x7ffc0];
+/* -------------- */
+ struct arbelprm_receive_doorbell_st receive_doorbell;/* Receive doorbell */
+/* -------------- */
+ pseudo_bit_t reserved37[0x7ffc0];
+/* -------------- */
+ struct arbelprm_cq_cmd_doorbell_st cq_cmd_doorbell;/* CQ Doorbell */
+/* -------------- */
+ pseudo_bit_t reserved38[0xfffc0];
+/* -------------- */
+ struct arbelprm_uar_st uar; /* User Access Region */
+/* -------------- */
+ pseudo_bit_t reserved39[0x7c000];
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp; /* Multicast Group Member QP */
+/* -------------- */
+ pseudo_bit_t reserved40[0x7ffe0];
+/* -------------- */
+ struct arbelprm_query_debug_msg_st query_debug_msg;/* Query Debug Message */
+/* -------------- */
+ pseudo_bit_t reserved41[0x7f800];
+/* -------------- */
+ struct arbelprm_mad_ifc_st mad_ifc; /* MAD_IFC Input Mailbox */
+/* -------------- */
+ pseudo_bit_t reserved42[0x00900];
+/* -------------- */
+ struct arbelprm_mad_ifc_input_modifier_st mad_ifc_input_modifier;/* MAD_IFC Input Modifier */
+/* -------------- */
+ pseudo_bit_t reserved43[0x7e6e0];
+/* -------------- */
+ struct arbelprm_resize_cq_st resize_cq;/* Resize CQ Input Mailbox */
+/* -------------- */
+ pseudo_bit_t reserved44[0x7fe00];
+/* -------------- */
+ struct arbelprm_completion_with_error_st completion_with_error;/* Completion with Error CQE */
+/* -------------- */
+ pseudo_bit_t reserved45[0x7ff00];
+/* -------------- */
+ struct arbelprm_hcr_completion_event_st hcr_completion_event;/* Event_data Field - HCR Completion Event */
+/* -------------- */
+ pseudo_bit_t reserved46[0x7ff40];
+/* -------------- */
+ struct arbelprm_transport_and_ci_error_counters_st transport_and_ci_error_counters;/* Transport and CI Error Counters */
+/* -------------- */
+ pseudo_bit_t reserved47[0x7f000];
+/* -------------- */
+ struct arbelprm_performance_counters_st performance_counters;/* Performance Counters */
+/* -------------- */
+ pseudo_bit_t reserved48[0x9ff800];
+/* -------------- */
+ struct arbelprm_fast_registration_segment_st fast_registration_segment;/* Fast Registration Segment */
+/* -------------- */
+ pseudo_bit_t reserved49[0x7ff00];
+/* -------------- */
+ struct arbelprm_pbl_st pbl; /* Physical Buffer List */
+/* -------------- */
+ pseudo_bit_t reserved50[0x7ff00];
+/* -------------- */
+ struct arbelprm_srq_context_st srq_context;/* SRQ Context */
+/* -------------- */
+ pseudo_bit_t reserved51[0x7fe80];
+/* -------------- */
+ struct arbelprm_mod_stat_cfg_st mod_stat_cfg;/* MOD_STAT_CFG */
+/* -------------- */
+ pseudo_bit_t reserved52[0x7f800];
+/* -------------- */
+ struct arbelprm_virtual_physical_mapping_st virtual_physical_mapping;/* Virtual and Physical Mapping */
+/* -------------- */
+ pseudo_bit_t reserved53[0x7ff80];
+/* -------------- */
+ struct arbelprm_cq_ci_db_record_st cq_ci_db_record;/* CQ_CI_DB_Record */
+/* -------------- */
+ pseudo_bit_t reserved54[0x7ffc0];
+/* -------------- */
+ struct arbelprm_cq_arm_db_record_st cq_arm_db_record;/* CQ_ARM_DB_Record */
+/* -------------- */
+ pseudo_bit_t reserved55[0x7ffc0];
+/* -------------- */
+ struct arbelprm_qp_db_record_st qp_db_record;/* QP_DB_Record */
+/* -------------- */
+ pseudo_bit_t reserved56[0x1fffc0];
+/* -------------- */
+ struct arbelprm_configuration_registers_st configuration_registers;/* InfiniHost III EX Configuration Registers */
+/* -------------- */
+ struct arbelprm_eq_set_ci_table_st eq_set_ci_table;/* EQ Set CI DBs Table */
+/* -------------- */
+ pseudo_bit_t reserved57[0x01000];
+/* -------------- */
+ struct arbelprm_eq_arm_db_region_st eq_arm_db_region;/* EQ Arm Doorbell Region */
+/* -------------- */
+ pseudo_bit_t reserved58[0x00fc0];
+/* -------------- */
+ struct arbelprm_clr_int_st clr_int; /* Clear Interrupt Register */
+/* -------------- */
+ pseudo_bit_t reserved59[0xffcfc0];
+/* -------------- */
+};
+#endif /* H_prefix_arbelprm_bits_fixnames_MT25218_PRM_csp_H */
diff --git a/gpxe/src/drivers/infiniband/MT25408_PRM.h b/gpxe/src/drivers/infiniband/MT25408_PRM.h
new file mode 100644
index 00000000..17882ed7
--- /dev/null
+++ b/gpxe/src/drivers/infiniband/MT25408_PRM.h
@@ -0,0 +1,3313 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+/***
+ *** This file was generated at "Mon Apr 16 23:22:02 2007"
+ *** by:
+ *** % csp_bf -copyright=/mswg/misc/license-header.txt -prefix hermonprm_ -bits -fixnames MT25408_PRM.csp
+ ***/
+
+#ifndef H_prefix_hermonprm_bits_fixnames_MT25408_PRM_csp_H
+#define H_prefix_hermonprm_bits_fixnames_MT25408_PRM_csp_H
+
+/* UD Address Vector */
+
+struct hermonprm_ud_address_vector_st { /* Little Endian */
+ pseudo_bit_t pd[0x00018]; /* Protection Domain */
+ pseudo_bit_t port_number[0x00002]; /* Port number
+ 1 - Port 1
+ 2 - Port 2
+ other - reserved */
+ pseudo_bit_t reserved0[0x00005];
+ pseudo_bit_t fl[0x00001]; /* force loopback */
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */
+ pseudo_bit_t my_lid_path_bits[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */
+ pseudo_bit_t g[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */
+ pseudo_bit_t max_stat_rate[0x00004];/* Maximum static rate control.
+ 0 - 4X injection rate
+ 1 - 1X injection rate
+ other - reserved
+ */
+ pseudo_bit_t reserved2[0x00004];
+ pseudo_bit_t mgid_index[0x00007]; /* Index to port GID table
+ mgid_index = (port_number-1) * 2^log_max_gid + gid_index
+ Where:
+ 1. log_max_gid is taken from QUERY_DEV_CAP command
+ 2. gid_index is the index to the GID table */
+ pseudo_bit_t reserved3[0x00009];
+/* -------------- */
+ pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */
+ pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */
+ pseudo_bit_t sl[0x00004]; /* InfiniBand Service Level (SL) */
+/* -------------- */
+ pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */
+/* -------------- */
+ pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */
+/* -------------- */
+ pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */
+/* -------------- */
+ pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] if G bit is set. Must be set to 0x2 if G bit is cleared. */
+/* -------------- */
+};
+
+/* Send doorbell */
+
+struct hermonprm_send_doorbell_st { /* Little Endian */
+ pseudo_bit_t nopcode[0x00005]; /* Opcode of descriptor to be executed */
+ pseudo_bit_t f[0x00001]; /* Fence bit. If set, descriptor is fenced */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t wqe_counter[0x00010]; /* Modulo-64K counter of WQEs posted to the QP since its creation excluding the newly posted WQEs in this doorbell. Should be zero for the first doorbell on the QP */
+ pseudo_bit_t wqe_cnt[0x00008]; /* Number of WQEs posted with this doorbell. Must be grater then zero. */
+/* -------------- */
+ pseudo_bit_t nds[0x00006]; /* Next descriptor size (in 16-byte chunks) */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */
+/* -------------- */
+};
+
+/* Send wqe segment data inline */
+
+struct hermonprm_wqe_segment_data_inline_st { /* Little Endian */
+ pseudo_bit_t byte_count[0x0000a]; /* Not including padding for 16Byte chunks */
+ pseudo_bit_t reserved0[0x00015];
+ pseudo_bit_t always1[0x00001];
+/* -------------- */
+ pseudo_bit_t data[0x00018]; /* Data may be more this segment size - in 16Byte chunks */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+};
+
+/* Send wqe segment data ptr */
+
+struct hermonprm_wqe_segment_data_ptr_st { /* Little Endian */
+ pseudo_bit_t byte_count[0x0001f];
+ pseudo_bit_t always0[0x00001];
+/* -------------- */
+ pseudo_bit_t l_key[0x00020];
+/* -------------- */
+ pseudo_bit_t local_address_h[0x00020];
+/* -------------- */
+ pseudo_bit_t local_address_l[0x00020];
+/* -------------- */
+};
+
+/* Send wqe segment rd */
+
+struct hermonprm_local_invalidate_segment_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t mem_key[0x00018];
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved2[0x000a0];
+/* -------------- */
+};
+
+/* Fast_Registration_Segment ####michal - doesn't match PRM (fields were added, see below) new table size in bytes - 0x30 */
+
+struct hermonprm_fast_registration_segment_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x0001b];
+ pseudo_bit_t lr[0x00001]; /* If set - Local Read access will be enabled */
+ pseudo_bit_t lw[0x00001]; /* If set - Local Write access will be enabled */
+ pseudo_bit_t rr[0x00001]; /* If set - Remote Read access will be enabled */
+ pseudo_bit_t rw[0x00001]; /* If set - Remote Write access will be enabled */
+ pseudo_bit_t a[0x00001]; /* If set - Remote Atomic access will be enabled */
+/* -------------- */
+ pseudo_bit_t pbl_ptr_63_32[0x00020];/* Physical address pointer [63:32] to the physical buffer list ### michal - this field is replaced with mem_key .32 */
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* Memory Key on which the fast registration is executed on. ###michal-this field is replaced with pbl_ptr_63_32 */
+/* -------------- */
+ pseudo_bit_t page_size[0x00005]; /* Page size used for the region. Actual size is [4K]*2^Page_size bytes.
+ page_size should be less than 20. ###michal - field doesn't exsist (see replacement above) */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t zb[0x00001]; /* Zero Based Region ###michal - field doesn't exsist (see replacement above) */
+ pseudo_bit_t pbl_ptr_31_8[0x00018]; /* Physical address pointer [31:8] to the physical buffer list ###michal - field doesn't exsist (see replacement above) */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start Address[63:32] - Virtual Address where this region starts */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start Address[31:0] - Virtual Address where this region starts */
+/* -------------- */
+ pseudo_bit_t reg_len_h[0x00020]; /* Region Length[63:32] */
+/* -------------- */
+ pseudo_bit_t reg_len_l[0x00020]; /* Region Length[31:0] */
+/* -------------- */
+};
+
+/* Send wqe segment atomic */
+
+struct hermonprm_wqe_segment_atomic_st { /* Little Endian */
+ pseudo_bit_t swap_add_h[0x00020];
+/* -------------- */
+ pseudo_bit_t swap_add_l[0x00020];
+/* -------------- */
+ pseudo_bit_t compare_h[0x00020];
+/* -------------- */
+ pseudo_bit_t compare_l[0x00020];
+/* -------------- */
+};
+
+/* Send wqe segment remote address */
+
+struct hermonprm_wqe_segment_remote_address_st { /* Little Endian */
+ pseudo_bit_t remote_virt_addr_h[0x00020];
+/* -------------- */
+ pseudo_bit_t remote_virt_addr_l[0x00020];
+/* -------------- */
+ pseudo_bit_t rkey[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* end wqe segment bind */
+
+struct hermonprm_wqe_segment_bind_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x0001d];
+ pseudo_bit_t rr[0x00001]; /* If set, Remote Read Enable for bound window. */
+ pseudo_bit_t rw[0x00001]; /* If set, Remote Write Enable for bound window.
+ */
+ pseudo_bit_t a[0x00001]; /* If set, Atomic Enable for bound window. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x0001e];
+ pseudo_bit_t zb[0x00001]; /* If set, Window is Zero Based. */
+ pseudo_bit_t type[0x00001]; /* Window type.
+ 0 - Type one window
+ 1 - Type two window
+ */
+/* -------------- */
+ pseudo_bit_t new_rkey[0x00020]; /* The new RKey of window to bind */
+/* -------------- */
+ pseudo_bit_t region_lkey[0x00020]; /* Local key of region, which window will be bound to */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];
+/* -------------- */
+ pseudo_bit_t length_h[0x00020];
+/* -------------- */
+ pseudo_bit_t length_l[0x00020];
+/* -------------- */
+};
+
+/* Send wqe segment ud */
+
+struct hermonprm_wqe_segment_ud_st { /* Little Endian */
+ struct hermonprm_ud_address_vector_st ud_address_vector;/* UD Address Vector */
+/* -------------- */
+ pseudo_bit_t destination_qp[0x00018];
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t q_key[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+};
+
+/* Send wqe segment rd */
+
+struct hermonprm_wqe_segment_rd_st { /* Little Endian */
+ pseudo_bit_t destination_qp[0x00018];
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t q_key[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+};
+
+/* Send wqe segment ctrl */
+
+struct hermonprm_wqe_segment_ctrl_send_st { /* Little Endian */
+ pseudo_bit_t opcode[0x00005];
+ pseudo_bit_t reserved0[0x0001a];
+ pseudo_bit_t owner[0x00001];
+/* -------------- */
+ pseudo_bit_t ds[0x00006]; /* descriptor (wqe) size in 16bytes chunk */
+ pseudo_bit_t f[0x00001]; /* fence */
+ pseudo_bit_t reserved1[0x00019];
+/* -------------- */
+ pseudo_bit_t fl[0x00001]; /* Force LoopBack */
+ pseudo_bit_t s[0x00001]; /* Remote Solicited Event */
+ pseudo_bit_t c[0x00002]; /* completion required: 0b00 - no 0b11 - yes */
+ pseudo_bit_t ip[0x00001]; /* When set, InfiniHost III Ex will calculate the IP checksum of the IP header that is present immediately after the IPoverIB encapsulation header. In the case of multiple headers (encapsulation), InfiniHost III Ex will calculate the checksum only for the first IP header following the IPoverIB encapsulation header. Not Valid for IPv6 packets */
+ pseudo_bit_t tcp_udp[0x00001]; /* When set, InfiniHost III Ex will calculate the TCP/UDP checksum of the packet that is present immediately after the IP header. In the case of multiple headers (encapsulation), InfiniHost III Ex will calculate the checksum only for the first TCP header following the IP header. This bit may be set only if the entire TCP/UDP segment is present in one IB packet */
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t so[0x00001]; /* Strong Ordering - when set, the WQE will be executed only after all previous WQEs have been executed. Can be set for RC WQEs only. This bit must be set in type two BIND, Fast Registration and Local invalidate operations. */
+ pseudo_bit_t src_remote_buf[0x00018];
+/* -------------- */
+ pseudo_bit_t immediate[0x00020]; /* If the OpCode encodes an operation with Immediate (RDMA-write/SEND), This field will hold the Immediate data to be sent. If the OpCode encodes send and invalidate operations, this field holds the Invalidation key to be inserted into the packet; otherwise, this field is reserved. */
+/* -------------- */
+};
+
+/* Address Path # ###michal - match to PRM */
+
+struct hermonprm_address_path_st { /* Little Endian */
+ pseudo_bit_t pkey_index[0x00007]; /* PKey table index */
+ pseudo_bit_t reserved0[0x00016];
+ pseudo_bit_t sv[0x00001]; /* Service VLAN on QP */
+ pseudo_bit_t cv[0x00001]; /* Customer VLAN in QP */
+ pseudo_bit_t fl[0x00001]; /* Force LoopBack */
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */
+ pseudo_bit_t my_lid_smac_idx[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */
+ pseudo_bit_t grh_ip[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */
+ pseudo_bit_t max_stat_rate[0x00004];/* Maximum static rate control.
+ 0 - 100% injection rate
+ 1 - 25% injection rate
+ 2 - 12.5% injection rate
+ 3 - 50% injection rate
+ 7: 2.5 Gb/s.
+ 8: 10 Gb/s.
+ 9: 30 Gb/s.
+ 10: 5 Gb/s.
+ 11: 20 Gb/s.
+ 12: 40 Gb/s.
+ 13: 60 Gb/s.
+ 14: 80 Gb/s.
+ 15: 120 Gb/s. */
+ pseudo_bit_t reserved2[0x00004];
+ pseudo_bit_t mgid_index[0x00007]; /* Index to port GID table */
+ pseudo_bit_t reserved3[0x00004];
+ pseudo_bit_t ack_timeout[0x00005]; /* Local ACK timeout - Transport timer for activation of retransmission mechanism. Refer to IB spec Vol1 9.7.6.1.3 for further details.
+ The transport timer is set to 4.096us*2^ack_timeout, if ack_timeout is 0 then transport timer is disabled. */
+/* -------------- */
+ pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */
+ pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */
+ pseudo_bit_t reserved4[0x00004];
+/* -------------- */
+ pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */
+/* -------------- */
+ pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */
+/* -------------- */
+ pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */
+/* -------------- */
+ pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00008];
+ pseudo_bit_t sp[0x00001]; /* if set, spoofing protection is enforced on this QP and Ethertype headers are restricted */
+ pseudo_bit_t reserved6[0x00002];
+ pseudo_bit_t fvl[0x00001]; /* force VLAN */
+ pseudo_bit_t fsip[0x00001]; /* force source IP */
+ pseudo_bit_t fsm[0x00001]; /* force source MAC */
+ pseudo_bit_t reserved7[0x0000a];
+ pseudo_bit_t sched_queue[0x00008];
+/* -------------- */
+ pseudo_bit_t dmac_47_32[0x00010];
+ pseudo_bit_t vlan_index[0x00007];
+ pseudo_bit_t reserved8[0x00001];
+ pseudo_bit_t counter_index[0x00008];/* Index to a table of counters that counts egress packets and bytes, 0xFF not valid */
+/* -------------- */
+ pseudo_bit_t dmac_31_0[0x00020];
+/* -------------- */
+};
+
+/* HCA Command Register (HCR) #### michal - match PRM */
+
+struct hermonprm_hca_command_register_st { /* Little Endian */
+ pseudo_bit_t in_param_h[0x00020]; /* Input Parameter: parameter[63:32] or pointer[63:32] to input mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t in_param_l[0x00020]; /* Input Parameter: parameter[31:0] or pointer[31:0] to input mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t input_modifier[0x00020];/* Input Parameter Modifier */
+/* -------------- */
+ pseudo_bit_t out_param_h[0x00020]; /* Output Parameter: parameter[63:32] or pointer[63:32] to output mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t out_param_l[0x00020]; /* Output Parameter: parameter[31:0] or pointer[31:0] to output mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00010];
+ pseudo_bit_t token[0x00010]; /* Software assigned token to the command, to uniquely identify it. The token is returned to the software in the EQE reported. */
+/* -------------- */
+ pseudo_bit_t opcode[0x0000c]; /* Command opcode */
+ pseudo_bit_t opcode_modifier[0x00004];/* Opcode Modifier, see specific description for each command. */
+ pseudo_bit_t reserved1[0x00005];
+ pseudo_bit_t t[0x00001]; /* Toggle */
+ pseudo_bit_t e[0x00001]; /* Event Request
+ 0 - Don't report event (software will poll the GO bit)
+ 1 - Report event to EQ when the command completes */
+ pseudo_bit_t go[0x00001]; /* Go (0=Software ownership for the HCR, 1=Hardware ownership for the HCR)
+ Software can write to the HCR only if Go bit is cleared.
+ Software must set the Go bit to trigger the HW to execute the command. Software must not write to this register value other than 1 for the Go bit. */
+ pseudo_bit_t status[0x00008]; /* Command execution status report. Valid only if command interface in under SW ownership (Go bit is cleared)
+ 0 - command completed without error. If different than zero, command execution completed with error. Syndrom encoding is depended on command executed and is defined for each command */
+/* -------------- */
+};
+
+/* CQ Doorbell */
+
+struct hermonprm_cq_cmd_doorbell_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number accessed */
+ pseudo_bit_t cmd[0x00003]; /* Command to be executed on CQ
+ 0x0 - Reserved
+ 0x1 - Request notification for next Solicited completion event. CQ_param specifies the current CQ Consumer Counter.
+ 0x2 - Request notification for next Solicited or Unsolicited completion event. CQ_param specifies the current CQ Consumer Counter.
+ 0x3 - Request notification for multiple completions (Arm-N). CQ_param specifies the value of the CQ Counter that when reached by HW (i.e. HW generates a CQE into this Counter) Event will be generated
+ Other - Reserved */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t cmd_sn[0x00002]; /* Command Sequence Number - This field should be incremented upon receiving completion notification of the respective CQ.
+ This transition is done by ringing Request notification for next Solicited, Request notification for next Solicited or Unsolicited
+ completion or Request notification for multiple completions doorbells after receiving completion notification.
+ This field is initialized to Zero */
+ pseudo_bit_t reserved1[0x00002];
+/* -------------- */
+ pseudo_bit_t cq_param[0x00020]; /* parameter to be used by CQ command */
+/* -------------- */
+};
+
+/* RD-send doorbell */
+
+struct hermonprm_rd_send_doorbell_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t een[0x00018]; /* End-to-end context number (reliable datagram)
+ Must be zero for Nop and Bind operations */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00008];
+ pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */
+/* -------------- */
+ struct hermonprm_send_doorbell_st send_doorbell;/* Send Parameters */
+/* -------------- */
+};
+
+/* Multicast Group Member QP #### michal - match PRM */
+
+struct hermonprm_mgmqp_st { /* Little Endian */
+ pseudo_bit_t qpn_i[0x00018]; /* QPN_i: QP number which is a member in this multicast group. Valid only if Qi bit is set. Length of the QPN_i list is set in INIT_HCA */
+ pseudo_bit_t reserved0[0x00006];
+ pseudo_bit_t blck_lb[0x00001]; /* Block self-loopback messages arriving to this qp */
+ pseudo_bit_t qi[0x00001]; /* Qi: QPN_i is valid */
+/* -------------- */
+};
+
+/* vsd */
+
+struct hermonprm_vsd_st { /* Little Endian */
+ pseudo_bit_t vsd_dw0[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw1[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw2[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw3[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw4[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw5[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw6[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw7[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw8[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw9[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw10[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw11[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw12[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw13[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw14[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw15[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw16[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw17[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw18[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw19[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw20[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw21[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw22[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw23[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw24[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw25[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw26[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw27[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw28[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw29[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw30[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw31[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw32[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw33[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw34[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw35[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw36[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw37[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw38[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw39[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw40[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw41[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw42[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw43[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw44[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw45[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw46[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw47[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw48[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw49[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw50[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw51[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw52[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw53[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw54[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw55[0x00020];
+/* -------------- */
+};
+
+/* UAR Parameters */
+
+struct hermonprm_uar_params_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t uar_page_sz[0x00008]; /* This field defines the size of each UAR page.
+ Size of UAR Page is 4KB*2^UAR_Page_Size */
+ pseudo_bit_t log_max_uars[0x00004]; /* Number of UARs supported is 2^log_max_UARs */
+ pseudo_bit_t reserved1[0x00014];
+/* -------------- */
+ pseudo_bit_t reserved2[0x000a0];
+/* -------------- */
+};
+
+/* Translation and Protection Tables Parameters */
+
+struct hermonprm_tptparams_st { /* Little Endian */
+ pseudo_bit_t dmpt_base_adr_h[0x00020];/* dMPT - Memory Protection Table base physical address [63:32].
+ Entry size is 64 bytes.
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t dmpt_base_adr_l[0x00020];/* dMPT - Memory Protection Table base physical address [31:0].
+ Entry size is 64 bytes.
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t log_dmpt_sz[0x00006]; /* Log (base 2) of the number of region/windows entries in the dMPT table. */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t pfto[0x00005]; /* Page Fault RNR Timeout -
+ The field returned in RNR Naks generated when a page fault is detected.
+ It has no effect when on-demand-paging is not used. */
+ pseudo_bit_t reserved1[0x00013];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00020];
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_h[0x00020];/* MTT - Memory Translation table base physical address [63:32].
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_l[0x00020];/* MTT - Memory Translation table base physical address [31:0].
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t cmpt_base_adr_h[0x00020];/* cMPT - Memory Protection Table base physical address [63:32].
+ Entry size is 64 bytes.
+ Table must be aligned to its size. */
+/* -------------- */
+ pseudo_bit_t cmpt_base_adr_l[0x00020];/* cMPT - Memory Protection Table base physical address [31:0].
+ Entry size is 64 bytes.
+ Table must be aligned to its size. */
+/* -------------- */
+};
+
+/* Multicast Support Parameters #### michal - match PRM */
+
+struct hermonprm_multicastparam_st { /* Little Endian */
+ pseudo_bit_t mc_base_addr_h[0x00020];/* Base Address of the Multicast Table [63:32].
+ The base address must be aligned to the entry size.
+ Address may be set to 0xFFFFFFFF if multicast is not supported. */
+/* -------------- */
+ pseudo_bit_t mc_base_addr_l[0x00020];/* Base Address of the Multicast Table [31:0].
+ The base address must be aligned to the entry size.
+ Address may be set to 0xFFFFFFFF if multicast is not supported. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t log_mc_table_entry_sz[0x00005];/* Log2 of the Size of multicast group member (MGM) entry.
+ Must be greater than 5 (to allow CTRL and GID sections).
+ That implies the number of QPs per MC table entry. */
+ pseudo_bit_t reserved1[0x0000b];
+ pseudo_bit_t reserved2[0x00010];
+/* -------------- */
+ pseudo_bit_t log_mc_table_hash_sz[0x00005];/* Number of entries in multicast DGID hash table (must be power of 2)
+ INIT_HCA - the required number of entries
+ QUERY_HCA - the actual number of entries assigned by firmware (will be less than or equal to the amount required in INIT_HCA) */
+ pseudo_bit_t reserved3[0x0001b];
+/* -------------- */
+ pseudo_bit_t log_mc_table_sz[0x00005];/* Log2 of the overall number of MC entries in the MCG table (includes both hash and auxiliary tables) */
+ pseudo_bit_t reserved4[0x00013];
+ pseudo_bit_t mc_hash_fn[0x00003]; /* Multicast hash function
+ 0 - Default hash function
+ other - reserved */
+ pseudo_bit_t reserved5[0x00005];
+/* -------------- */
+ pseudo_bit_t reserved6[0x00020];
+/* -------------- */
+};
+
+/* QPC/EEC/CQC/EQC/RDB Parameters #### michal - doesn't match PRM (field name are differs. see below) */
+
+struct hermonprm_qpcbaseaddr_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t qpc_base_addr_h[0x00020];/* QPC Base Address [63:32]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t log_num_of_qp[0x00005];/* Log base 2 of number of supported QPs */
+ pseudo_bit_t qpc_base_addr_l[0x0001b];/* QPC Base Address [31:7]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t srqc_base_addr_h[0x00020];/* SRQ Context Base Address [63:32]
+ Table must be aligned on its size
+ Address may be set to 0xFFFFFFFF if SRQ is not supported. */
+/* -------------- */
+ pseudo_bit_t log_num_of_srq[0x00005];/* Log base 2 of number of supported SRQs. */
+ pseudo_bit_t srqc_base_addr_l[0x0001b];/* SRQ Context Base Address [31:5]
+ Table must be aligned on its size
+ Address may be set to 0xFFFFFFFF if SRQ is not supported. */
+/* -------------- */
+ pseudo_bit_t cqc_base_addr_h[0x00020];/* CQC Base Address [63:32]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t log_num_of_cq[0x00005];/* Log base 2 of number of supported CQs. */
+ pseudo_bit_t cqc_base_addr_l[0x0001b];/* CQC Base Address [31:6]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00040];
+/* -------------- */
+ pseudo_bit_t altc_base_addr_h[0x00020];/* AltC Base Address (altc_base_addr_h) [63:32]
+ Table has same number of entries as QPC table.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t altc_base_addr_l[0x00020];/* AltC Base Address (altc_base_addr_l) [31:0]
+ Table has same number of entries as QPC table.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00040];
+/* -------------- */
+ pseudo_bit_t auxc_base_addr_h[0x00020];
+/* -------------- */
+ pseudo_bit_t auxc_base_addr_l[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00040];
+/* -------------- */
+ pseudo_bit_t eqc_base_addr_h[0x00020];/* EQC Base Address [63:32]
+ Address may be set to 0xFFFFFFFF if EQs are not supported.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t log_num_of_eq[0x00005];/* Log base 2 of number of supported EQs.
+ Must be 6 or less in InfiniHost-III-EX. */
+ pseudo_bit_t eqc_base_addr_l[0x0001b];/* EQC Base Address [31:6]
+ Address may be set to 0xFFFFFFFF if EQs are not supported.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00040];
+/* -------------- */
+ pseudo_bit_t rdmardc_base_addr_h[0x00020];/* rdmardc_base_addr_h: Base address of table that holds remote read and remote atomic requests [63:32]. */
+/* -------------- */
+ pseudo_bit_t log_num_rd[0x00003]; /* Log (base 2) of the maximum number of RdmaRdC entries per QP. This denotes the maximum number of outstanding reads/atomics as a responder. */
+ pseudo_bit_t reserved7[0x00002];
+ pseudo_bit_t rdmardc_base_addr_l[0x0001b];/* rdmardc_base_addr_l: Base address of table that holds remote read and remote atomic requests [31:0].
+ Table must be aligned to RDB entry size (32 bytes). */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00040];
+/* -------------- */
+};
+
+/* Header_Log_Register */
+
+struct hermonprm_header_log_register_st { /* Little Endian */
+ pseudo_bit_t place_holder[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved0[0x00060];
+/* -------------- */
+};
+
+/* Performance Monitors */
+
+struct hermonprm_performance_monitors_st { /* Little Endian */
+ pseudo_bit_t e0[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t e1[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t e2[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t r0[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t r1[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t r2[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t reserved1[0x00001];
+ pseudo_bit_t i0[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t i1[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t i2[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t f0[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t f1[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t f2[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t reserved3[0x00001];
+ pseudo_bit_t ev_cnt1[0x00005]; /* Specifies event to be counted by Event_counter1 See XXX for events' definition. */
+ pseudo_bit_t reserved4[0x00003];
+ pseudo_bit_t ev_cnt2[0x00005]; /* Specifies event to be counted by Event_counter2 See XXX for events' definition. */
+ pseudo_bit_t reserved5[0x00003];
+/* -------------- */
+ pseudo_bit_t clock_counter[0x00020];
+/* -------------- */
+ pseudo_bit_t event_counter1[0x00020];
+/* -------------- */
+ pseudo_bit_t event_counter2[0x00020];/* Read/write event counter, counting events specified by EvCntl and EvCnt2 fields repsectively. When the event counter reaches is maximum value of 0xFFFFFF, the next event will cause it to roll over to zero, set F1 or F2 bit respectively and generate interrupt by I1 I2 bit respectively. */
+/* -------------- */
+};
+
+/* MLX WQE segment format */
+
+struct hermonprm_wqe_segment_ctrl_mlx_st { /* Little Endian */
+ pseudo_bit_t opcode[0x00005]; /* must be 0xA = SEND */
+ pseudo_bit_t reserved0[0x0001a];
+ pseudo_bit_t owner[0x00001];
+/* -------------- */
+ pseudo_bit_t ds[0x00006]; /* Descriptor Size */
+ pseudo_bit_t reserved1[0x0001a];
+/* -------------- */
+ pseudo_bit_t fl[0x00001]; /* Force LoopBack */
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t c[0x00002]; /* Create CQE (for "requested signalling" QP) */
+ pseudo_bit_t icrc[0x00001]; /* last dword of the packet: 0 - Calculate ICRC and put it instead of last dword. 1 - Leave last dword as is. */
+ pseudo_bit_t reserved3[0x00003];
+ pseudo_bit_t sl[0x00004];
+ pseudo_bit_t max_statrate[0x00004];
+ pseudo_bit_t slr[0x00001]; /* 0= take slid from port. 1= take slid from given headers */
+ pseudo_bit_t v15[0x00001]; /* Send packet over VL15 */
+ pseudo_bit_t reserved4[0x0000e];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00010];
+ pseudo_bit_t rlid[0x00010]; /* Destination LID (must match given headers) */
+/* -------------- */
+};
+
+/* Send WQE segment format */
+
+struct hermonprm_send_wqe_segment_st { /* Little Endian */
+ struct hermonprm_wqe_segment_ctrl_send_st wqe_segment_ctrl_send;/* Send wqe segment ctrl */
+/* -------------- */
+ struct hermonprm_wqe_segment_rd_st wqe_segment_rd;/* Send wqe segment rd */
+/* -------------- */
+ struct hermonprm_wqe_segment_ud_st wqe_segment_ud;/* Send wqe segment ud */
+/* -------------- */
+ struct hermonprm_wqe_segment_bind_st wqe_segment_bind;/* Send wqe segment bind */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00180];
+/* -------------- */
+ struct hermonprm_wqe_segment_remote_address_st wqe_segment_remote_address;/* Send wqe segment remote address */
+/* -------------- */
+ struct hermonprm_wqe_segment_atomic_st wqe_segment_atomic;/* Send wqe segment atomic */
+/* -------------- */
+ struct hermonprm_fast_registration_segment_st fast_registration_segment;/* Fast Registration Segment */
+/* -------------- */
+ struct hermonprm_local_invalidate_segment_st local_invalidate_segment;/* local invalidate segment */
+/* -------------- */
+ struct hermonprm_wqe_segment_data_ptr_st wqe_segment_data_ptr;/* Send wqe segment data ptr */
+/* -------------- */
+ struct hermonprm_wqe_segment_data_inline_st wqe_segment_data_inline;/* Send wqe segment data inline */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00200];
+/* -------------- */
+};
+
+/* QP and EE Context Entry */
+
+struct hermonprm_queue_pair_ee_context_entry_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t reserved1[0x00001];
+ pseudo_bit_t reserved2[0x00002];
+ pseudo_bit_t pm_state[0x00002]; /* Path migration state (Migrated, Armed or Rearm)
+ 11-Migrated
+ 00-Armed
+ 01-Rearm
+ 10-Reserved
+ Should be set to 11 for UD QPs and for QPs which do not support APM */
+ pseudo_bit_t reserved3[0x00003];
+ pseudo_bit_t st[0x00004]; /* Transport Service Type: RC: 0, UC: 1, RD: 2, UD: 3, FCMND:4, FEXCH:5, SRC:6, MLX 7, Raw Eth 11 */
+ pseudo_bit_t reserved4[0x00008];
+ pseudo_bit_t state[0x00004]; /* QP/EE state:
+ 0 - RST
+ 1 - INIT
+ 2 - RTR
+ 3 - RTS
+ 4 - SQEr
+ 5 - SQD (Send Queue Drained)
+ 6 - ERR
+ 7 - Send Queue Draining
+ 8 - Reserved
+ 9 - Suspended
+ A- F - Reserved
+ (Valid for QUERY_QPEE and ERR2RST_QPEE commands only) */
+/* -------------- */
+ pseudo_bit_t pd[0x00018];
+ pseudo_bit_t reserved5[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved6[0x00004];
+ pseudo_bit_t rlky[0x00001]; /* When set this QP can use the Reserved L_Key */
+ pseudo_bit_t reserved7[0x00003];
+ pseudo_bit_t log_sq_stride[0x00003];/* Stride on the send queue. WQ entry is 16*(2^log_SQ_stride) bytes.
+ Stride must be equal or bigger then 64 bytes (minimum log_RQ_stride value allowed is 2). */
+ pseudo_bit_t log_sq_size[0x00004]; /* Log2 of the Number of WQEs in the Send Queue. */
+ pseudo_bit_t reserved8[0x00001];
+ pseudo_bit_t log_rq_stride[0x00003];/* Stride on the receive queue. WQ entry is 16*(2^log_RQ_stride) bytes.
+ Stride must be equal or bigger then 64 bytes (minimum log_RQ_stride value allowed is 2). */
+ pseudo_bit_t log_rq_size[0x00004]; /* Log2 of the Number of WQEs in the Receive Queue. */
+ pseudo_bit_t reserved9[0x00001];
+ pseudo_bit_t msg_max[0x00005]; /* Max message size allowed on the QP. Maximum message size is 2^msg_Max.
+ Must be equal to MTU for UD and MLX QPs. */
+ pseudo_bit_t mtu[0x00003]; /* MTU of the QP (Must be the same for both paths: primary and alternative):
+ 0x1 - 256 bytes
+ 0x2 - 512
+ 0x3 - 1024
+ 0x4 - 2048
+ other - reserved
+
+ Should be configured to 0x4 for UD and MLX QPs. */
+/* -------------- */
+ pseudo_bit_t usr_page[0x00018]; /* UAR number to ring doorbells for this QP (aliased to doorbell and Blue Flame pages) */
+ pseudo_bit_t reserved10[0x00008];
+/* -------------- */
+ pseudo_bit_t local_qpn_een[0x00018];/* Local QP/EE number Lower bits determine position of this record in QPC table, and - thus - constrained
+ This field is valid for QUERY and ERR2RST commands only. */
+ pseudo_bit_t reserved11[0x00008];
+/* -------------- */
+ pseudo_bit_t remote_qpn_een[0x00018];/* Remote QP/EE number */
+ pseudo_bit_t reserved12[0x00008];
+/* -------------- */
+ struct hermonprm_address_path_st primary_address_path;/* Primary address path for the QP/EE */
+/* -------------- */
+ struct hermonprm_address_path_st alternative_address_path;/* Alternate address path for the QP/EE */
+/* -------------- */
+ pseudo_bit_t reserved13[0x00003];
+ pseudo_bit_t reserved14[0x00001];
+ pseudo_bit_t reserved15[0x00001];
+ pseudo_bit_t cur_retry_cnt[0x00003];/* Current transport retry counter (QUERY_QPEE only).
+ The current transport retry counter can vary from retry_count down to 1, where 1 means that the last retry attempt is currently executing. */
+ pseudo_bit_t cur_rnr_retry[0x00003];/* Current RNR retry counter (QUERY_QPEE only).
+ The current RNR retry counter can vary from rnr_retry to 1, where 1 means that the last retry attempt is currently executing. */
+ pseudo_bit_t fre[0x00001]; /* Fast Registration Work Request Enabled. (Reserved for EE) */
+ pseudo_bit_t reserved16[0x00001];
+ pseudo_bit_t rnr_retry[0x00003];
+ pseudo_bit_t retry_count[0x00003]; /* Transport timeout Retry count */
+ pseudo_bit_t reserved17[0x00002];
+ pseudo_bit_t sra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed in the send queue. Maximum number is 2^SRA_Max. Must be zero in EE context. */
+ pseudo_bit_t reserved18[0x00004];
+ pseudo_bit_t ack_req_freq[0x00004]; /* ACK required frequency. ACK required bit will be set in every 2^AckReqFreq packets at least. Not valid for RD QP. */
+/* -------------- */
+ pseudo_bit_t reserved19[0x00020];
+/* -------------- */
+ pseudo_bit_t next_send_psn[0x00018];/* Next PSN to be sent */
+ pseudo_bit_t reserved20[0x00008];
+/* -------------- */
+ pseudo_bit_t cqn_snd[0x00018]; /* CQ number completions from the send queue to be reported to. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved21[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved22[0x00040];
+/* -------------- */
+ pseudo_bit_t last_acked_psn[0x00018];/* The last acknowledged PSN for the requester (QUERY_QPEE only) */
+ pseudo_bit_t reserved23[0x00008];
+/* -------------- */
+ pseudo_bit_t ssn[0x00018]; /* Requester Send Sequence Number (QUERY_QPEE only) */
+ pseudo_bit_t reserved24[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved25[0x00004];
+ pseudo_bit_t ric[0x00001]; /* Invalid Credits.
+ 1 - place "Invalid Credits" to ACKs sent from this queue.
+ 0 - ACKs report the actual number of end to end credits on the connection.
+ Not valid (reserved) in EE context.
+ Must be set to 1 on QPs which are attached to SRQ. */
+ pseudo_bit_t reserved26[0x00001];
+ pseudo_bit_t page_offset[0x00006]; /* start address of wqes in first page (11:6), bits [5:0] reserved */
+ pseudo_bit_t reserved27[0x00001];
+ pseudo_bit_t rae[0x00001]; /* If set - Atomic operations enabled. on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t rwe[0x00001]; /* If set - RDMA - write enabled on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t rre[0x00001]; /* If set - RDMA - read enabled on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved28[0x00005];
+ pseudo_bit_t rra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed on receive queue is 2^RRA_Max.
+ Must be 0 for EE context. */
+ pseudo_bit_t reserved29[0x00008];
+/* -------------- */
+ pseudo_bit_t next_rcv_psn[0x00018]; /* Next (expected) PSN on receive */
+ pseudo_bit_t min_rnr_nak[0x00005]; /* Minimum RNR NAK timer value (TTTTT field encoding according to the IB spec Vol1 9.7.5.2.8).
+ Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved30[0x00003];
+/* -------------- */
+ pseudo_bit_t srcd[0x00010]; /* Scalable Reliable Connection Domain. Valid for SRC transport service */
+ pseudo_bit_t reserved31[0x00010];
+/* -------------- */
+ pseudo_bit_t cqn_rcv[0x00018]; /* CQ number completions from receive queue to be reported to. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved32[0x00008];
+/* -------------- */
+ pseudo_bit_t db_record_addr_h[0x00020];/* QP DB Record physical address */
+/* -------------- */
+ pseudo_bit_t reserved33[0x00002];
+ pseudo_bit_t db_record_addr_l[0x0001e];/* QP DB Record physical address */
+/* -------------- */
+ pseudo_bit_t q_key[0x00020]; /* Q_Key to be validated against received datagrams.
+ On send datagrams, if Q_Key[31] specified in the WQE is set, then this Q_Key will be transmitted in the outgoing message.
+ Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t srqn[0x00018]; /* SRQN - Shared Receive Queue Number - specifies the SRQ number from which the QP dequeues receive descriptors.
+ SRQN is valid only if SRQ bit is set. Not valid (reserved) in EE context. */
+ pseudo_bit_t srq[0x00001]; /* SRQ - Shared Receive Queue. If this bit is set, then the QP is associated with a SRQ. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved34[0x00007];
+/* -------------- */
+ pseudo_bit_t rmsn[0x00018]; /* Responder current message sequence number (QUERY_QPEE only) */
+ pseudo_bit_t reserved35[0x00008];
+/* -------------- */
+ pseudo_bit_t sq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the SQ.
+ Must be 0x0 in SQ initialization.
+ (QUERY_QPEE only). */
+ pseudo_bit_t rq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the RQ.
+ Must be 0x0 in RQ initialization.
+ (QUERY_QPEE only). */
+/* -------------- */
+ pseudo_bit_t reserved36[0x00040];
+/* -------------- */
+ pseudo_bit_t rmc_parent_qpn[0x00018];/* reliable multicast parent queue number */
+ pseudo_bit_t hs[0x00001]; /* Header Separation. If set, the byte count of the first scatter entry will be ignored. The buffer specified by the first scatter entry will contain packet headers (up to TCP). CQE will report number of bytes scattered to the first scatter entry. Intended for use on IPoverIB on UD QP or Raw Ethernet QP. */
+ pseudo_bit_t is[0x00001]; /* when set - inline scatter is enabled for this RQ */
+ pseudo_bit_t reserved37[0x00001];
+ pseudo_bit_t rme[0x00002]; /* Reliable Multicast
+ 00 - disabled
+ 01 - parent QP (requester)
+ 10 - child QP (requester)
+ 11 - responder QP
+ Note that Reliable Multicast is a preliminary definition which can be subject to change. */
+ pseudo_bit_t reserved38[0x00002];
+ pseudo_bit_t mkey_rmp[0x00001]; /* If set, MKey used to access TPT for incoming RDMA-write request is calculated by adding MKey from the packet to base_MKey field in the QPC. Can be set only for QPs that are not target for RDMA-read request. */
+/* -------------- */
+ pseudo_bit_t base_mkey[0x00018]; /* Base Mkey bits [31:8]. Lower 8 bits must be zero. */
+ pseudo_bit_t num_rmc_peers[0x00008];/* Number of remote peers in Reliable Multicast group */
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_h[0x00008];/* MTT Base Address [39:32] in ICM relative to INIT_HCA.mtt_base_addr */
+ pseudo_bit_t reserved39[0x00010];
+ pseudo_bit_t log2_page_size[0x00006];/* Log (base 2) of MTT page size in units of 4KByte */
+ pseudo_bit_t reserved40[0x00002];
+/* -------------- */
+ pseudo_bit_t reserved41[0x00003];
+ pseudo_bit_t mtt_base_addr_l[0x0001d];/* MTT Base Address [31:3] in ICM relative to INIT_HCA.mtt_base_addr */
+/* -------------- */
+ pseudo_bit_t vft_lan[0x0000c];
+ pseudo_bit_t vft_prio[0x00003]; /* The Priority filed in the VFT header for FCP */
+ pseudo_bit_t reserved42[0x00001];
+ pseudo_bit_t cs_ctl[0x00009]; /* The Priority filed in the VFT header for FCP */
+ pseudo_bit_t reserved43[0x00006];
+ pseudo_bit_t ve[0x00001]; /* Should we add/check the VFT header */
+/* -------------- */
+ pseudo_bit_t exch_base[0x00010]; /* For init QP only - The base exchanges */
+ pseudo_bit_t reserved44[0x00008];
+ pseudo_bit_t exch_size[0x00004]; /* For CMMD QP only - The size (from base) exchanges is 2exchanges_size */
+ pseudo_bit_t reserved45[0x00003];
+ pseudo_bit_t fc[0x00001]; /* When set it mean that this QP is used for FIBRE CHANNEL. */
+/* -------------- */
+ pseudo_bit_t remote_id[0x00018]; /* Peer NX port ID */
+ pseudo_bit_t reserved46[0x00008];
+/* -------------- */
+ pseudo_bit_t fcp_mtu[0x0000a]; /* In 4*Bytes units. The MTU Size */
+ pseudo_bit_t reserved47[0x00006];
+ pseudo_bit_t my_id_indx[0x00008]; /* Index to My NX port ID table */
+ pseudo_bit_t vft_hop_count[0x00008];/* HopCnt value for the VFT header */
+/* -------------- */
+ pseudo_bit_t reserved48[0x000c0];
+/* -------------- */
+};
+
+/* */
+
+struct hermonprm_mcg_qp_dw_st { /* Little Endian */
+ pseudo_bit_t qpn[0x00018];
+ pseudo_bit_t reserved0[0x00006];
+ pseudo_bit_t blck_lb[0x00001];
+ pseudo_bit_t reserved1[0x00001];
+/* -------------- */
+};
+
+/* Clear Interrupt [63:0] #### michal - match to PRM */
+
+struct hermonprm_clr_int_st { /* Little Endian */
+ pseudo_bit_t clr_int_h[0x00020]; /* Clear Interrupt [63:32]
+ Write transactions to this register will clear (de-assert) the virtual interrupt output pins of InfiniHost-III-EX. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot.
+ This register is write-only. Reading from this register will cause undefined result
+ */
+/* -------------- */
+ pseudo_bit_t clr_int_l[0x00020]; /* Clear Interrupt [31:0]
+ Write transactions to this register will clear (de-assert) the virtual interrupt output pins of InfiniHost-III-EX. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot.
+ This register is write-only. Reading from this register will cause undefined result */
+/* -------------- */
+};
+
+/* EQ Set CI DBs Table */
+
+struct hermonprm_eq_set_ci_table_st { /* Little Endian */
+ pseudo_bit_t eq0_set_ci[0x00020]; /* EQ0_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t eq1_set_ci[0x00020]; /* EQ1_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t eq2_set_ci[0x00020]; /* EQ2_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00020];
+/* -------------- */
+ pseudo_bit_t eq3_set_ci[0x00020]; /* EQ3_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t eq4_set_ci[0x00020]; /* EQ4_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t eq5_set_ci[0x00020]; /* EQ5_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t eq6_set_ci[0x00020]; /* EQ6_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00020];
+/* -------------- */
+ pseudo_bit_t eq7_set_ci[0x00020]; /* EQ7_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00020];
+/* -------------- */
+ pseudo_bit_t eq8_set_ci[0x00020]; /* EQ8_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00020];
+/* -------------- */
+ pseudo_bit_t eq9_set_ci[0x00020]; /* EQ9_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00020];
+/* -------------- */
+ pseudo_bit_t eq10_set_ci[0x00020]; /* EQ10_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00020];
+/* -------------- */
+ pseudo_bit_t eq11_set_ci[0x00020]; /* EQ11_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved11[0x00020];
+/* -------------- */
+ pseudo_bit_t eq12_set_ci[0x00020]; /* EQ12_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved12[0x00020];
+/* -------------- */
+ pseudo_bit_t eq13_set_ci[0x00020]; /* EQ13_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved13[0x00020];
+/* -------------- */
+ pseudo_bit_t eq14_set_ci[0x00020]; /* EQ14_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved14[0x00020];
+/* -------------- */
+ pseudo_bit_t eq15_set_ci[0x00020]; /* EQ15_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved15[0x00020];
+/* -------------- */
+ pseudo_bit_t eq16_set_ci[0x00020]; /* EQ16_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved16[0x00020];
+/* -------------- */
+ pseudo_bit_t eq17_set_ci[0x00020]; /* EQ17_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved17[0x00020];
+/* -------------- */
+ pseudo_bit_t eq18_set_ci[0x00020]; /* EQ18_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved18[0x00020];
+/* -------------- */
+ pseudo_bit_t eq19_set_ci[0x00020]; /* EQ19_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved19[0x00020];
+/* -------------- */
+ pseudo_bit_t eq20_set_ci[0x00020]; /* EQ20_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved20[0x00020];
+/* -------------- */
+ pseudo_bit_t eq21_set_ci[0x00020]; /* EQ21_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved21[0x00020];
+/* -------------- */
+ pseudo_bit_t eq22_set_ci[0x00020]; /* EQ22_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved22[0x00020];
+/* -------------- */
+ pseudo_bit_t eq23_set_ci[0x00020]; /* EQ23_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved23[0x00020];
+/* -------------- */
+ pseudo_bit_t eq24_set_ci[0x00020]; /* EQ24_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved24[0x00020];
+/* -------------- */
+ pseudo_bit_t eq25_set_ci[0x00020]; /* EQ25_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved25[0x00020];
+/* -------------- */
+ pseudo_bit_t eq26_set_ci[0x00020]; /* EQ26_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved26[0x00020];
+/* -------------- */
+ pseudo_bit_t eq27_set_ci[0x00020]; /* EQ27_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved27[0x00020];
+/* -------------- */
+ pseudo_bit_t eq28_set_ci[0x00020]; /* EQ28_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved28[0x00020];
+/* -------------- */
+ pseudo_bit_t eq29_set_ci[0x00020]; /* EQ29_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved29[0x00020];
+/* -------------- */
+ pseudo_bit_t eq30_set_ci[0x00020]; /* EQ30_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved30[0x00020];
+/* -------------- */
+ pseudo_bit_t eq31_set_ci[0x00020]; /* EQ31_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved31[0x00020];
+/* -------------- */
+ pseudo_bit_t eq32_set_ci[0x00020]; /* EQ32_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved32[0x00020];
+/* -------------- */
+ pseudo_bit_t eq33_set_ci[0x00020]; /* EQ33_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved33[0x00020];
+/* -------------- */
+ pseudo_bit_t eq34_set_ci[0x00020]; /* EQ34_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved34[0x00020];
+/* -------------- */
+ pseudo_bit_t eq35_set_ci[0x00020]; /* EQ35_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved35[0x00020];
+/* -------------- */
+ pseudo_bit_t eq36_set_ci[0x00020]; /* EQ36_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved36[0x00020];
+/* -------------- */
+ pseudo_bit_t eq37_set_ci[0x00020]; /* EQ37_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved37[0x00020];
+/* -------------- */
+ pseudo_bit_t eq38_set_ci[0x00020]; /* EQ38_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved38[0x00020];
+/* -------------- */
+ pseudo_bit_t eq39_set_ci[0x00020]; /* EQ39_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved39[0x00020];
+/* -------------- */
+ pseudo_bit_t eq40_set_ci[0x00020]; /* EQ40_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved40[0x00020];
+/* -------------- */
+ pseudo_bit_t eq41_set_ci[0x00020]; /* EQ41_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved41[0x00020];
+/* -------------- */
+ pseudo_bit_t eq42_set_ci[0x00020]; /* EQ42_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved42[0x00020];
+/* -------------- */
+ pseudo_bit_t eq43_set_ci[0x00020]; /* EQ43_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved43[0x00020];
+/* -------------- */
+ pseudo_bit_t eq44_set_ci[0x00020]; /* EQ44_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved44[0x00020];
+/* -------------- */
+ pseudo_bit_t eq45_set_ci[0x00020]; /* EQ45_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved45[0x00020];
+/* -------------- */
+ pseudo_bit_t eq46_set_ci[0x00020]; /* EQ46_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved46[0x00020];
+/* -------------- */
+ pseudo_bit_t eq47_set_ci[0x00020]; /* EQ47_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved47[0x00020];
+/* -------------- */
+ pseudo_bit_t eq48_set_ci[0x00020]; /* EQ48_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved48[0x00020];
+/* -------------- */
+ pseudo_bit_t eq49_set_ci[0x00020]; /* EQ49_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved49[0x00020];
+/* -------------- */
+ pseudo_bit_t eq50_set_ci[0x00020]; /* EQ50_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved50[0x00020];
+/* -------------- */
+ pseudo_bit_t eq51_set_ci[0x00020]; /* EQ51_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved51[0x00020];
+/* -------------- */
+ pseudo_bit_t eq52_set_ci[0x00020]; /* EQ52_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved52[0x00020];
+/* -------------- */
+ pseudo_bit_t eq53_set_ci[0x00020]; /* EQ53_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved53[0x00020];
+/* -------------- */
+ pseudo_bit_t eq54_set_ci[0x00020]; /* EQ54_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved54[0x00020];
+/* -------------- */
+ pseudo_bit_t eq55_set_ci[0x00020]; /* EQ55_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved55[0x00020];
+/* -------------- */
+ pseudo_bit_t eq56_set_ci[0x00020]; /* EQ56_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved56[0x00020];
+/* -------------- */
+ pseudo_bit_t eq57_set_ci[0x00020]; /* EQ57_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved57[0x00020];
+/* -------------- */
+ pseudo_bit_t eq58_set_ci[0x00020]; /* EQ58_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved58[0x00020];
+/* -------------- */
+ pseudo_bit_t eq59_set_ci[0x00020]; /* EQ59_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved59[0x00020];
+/* -------------- */
+ pseudo_bit_t eq60_set_ci[0x00020]; /* EQ60_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved60[0x00020];
+/* -------------- */
+ pseudo_bit_t eq61_set_ci[0x00020]; /* EQ61_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved61[0x00020];
+/* -------------- */
+ pseudo_bit_t eq62_set_ci[0x00020]; /* EQ62_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved62[0x00020];
+/* -------------- */
+ pseudo_bit_t eq63_set_ci[0x00020]; /* EQ63_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved63[0x00020];
+/* -------------- */
+};
+
+/* InfiniHost-III-EX Configuration Registers #### michal - match to PRM */
+
+struct hermonprm_configuration_registers_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x403400];
+/* -------------- */
+ struct hermonprm_hca_command_register_st hca_command_interface_register;/* HCA Command Register */
+/* -------------- */
+ pseudo_bit_t reserved1[0x3fcb20];
+/* -------------- */
+};
+
+/* QP_DB_Record ### michal = gdror fixed */
+
+struct hermonprm_qp_db_record_st { /* Little Endian */
+ pseudo_bit_t receive_wqe_counter[0x00010];/* Modulo-64K counter of WQEs posted to the QP since its creation. Should be initialized to zero. */
+ pseudo_bit_t reserved0[0x00010];
+/* -------------- */
+};
+
+/* CQ_ARM_DB_Record */
+
+struct hermonprm_cq_arm_db_record_st { /* Little Endian */
+ pseudo_bit_t counter[0x00020]; /* CQ counter for the arming request */
+/* -------------- */
+ pseudo_bit_t cmd[0x00003]; /* 0x0 - No command
+ 0x1 - Request notification for next Solicited completion event. Counter filed specifies the current CQ Consumer Counter.
+ 0x2 - Request notification for next Solicited or Unsolicited completion event. Counter filed specifies the current CQ Consumer counter.
+ 0x3 - Request notification for multiple completions (Arm-N). Counter filed specifies the value of the CQ Index that when reached by HW (i.e. HW generates a CQE into this Index) Event will be generated
+ Other - Reserved */
+ pseudo_bit_t cmd_sn[0x00002]; /* Command Sequence Number - See Table 35, "CQ Doorbell Layout" for definition of this filed */
+ pseudo_bit_t res[0x00003]; /* Must be 0x2 */
+ pseudo_bit_t cq_number[0x00018]; /* CQ number */
+/* -------------- */
+};
+
+/* CQ_CI_DB_Record */
+
+struct hermonprm_cq_ci_db_record_st { /* Little Endian */
+ pseudo_bit_t counter[0x00020]; /* CQ counter */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00005];
+ pseudo_bit_t res[0x00003]; /* Must be 0x1 */
+ pseudo_bit_t cq_number[0x00018]; /* CQ number */
+/* -------------- */
+};
+
+/* Virtual_Physical_Mapping */
+
+struct hermonprm_virtual_physical_mapping_st { /* Little Endian */
+ pseudo_bit_t va_h[0x00020]; /* Virtual Address[63:32]. Valid only for MAP_ICM command. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x0000c];
+ pseudo_bit_t va_l[0x00014]; /* Virtual Address[31:12]. Valid only for MAP_ICM command. */
+/* -------------- */
+ pseudo_bit_t pa_h[0x00020]; /* Physical Address[63:32] */
+/* -------------- */
+ pseudo_bit_t log2size[0x00006]; /* Log2 of the size in 4KB pages of the physical and virtual contiguous memory that starts at PA_L/H and VA_L/H */
+ pseudo_bit_t reserved1[0x00006];
+ pseudo_bit_t pa_l[0x00014]; /* Physical Address[31:12] */
+/* -------------- */
+};
+
+/* MOD_STAT_CFG #### michal - gdror fix */
+
+struct hermonprm_mod_stat_cfg_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00010];
+ pseudo_bit_t rx_options[0x00004]; /* number of RX options to sweep when doing SerDes parameters AutoNegotiation. */
+ pseudo_bit_t reserved1[0x00003];
+ pseudo_bit_t rx_options_m[0x00001]; /* Modify rx_options */
+ pseudo_bit_t tx_options[0x00004]; /* number of TX options to sweep when doing SerDes parameters AutoNegotiation. */
+ pseudo_bit_t reserved2[0x00003];
+ pseudo_bit_t tx_options_m[0x00001]; /* Modify tx_options */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t pre_amp[0x00004]; /* Pre Amplitude */
+ pseudo_bit_t pre_emp_pre_amp[0x00004];
+ pseudo_bit_t pre_emp_out[0x00004]; /* Pre Emphasis Out */
+ pseudo_bit_t voltage[0x00004];
+ pseudo_bit_t equ[0x00004]; /* Equalization */
+ pseudo_bit_t reserved4[0x0000b];
+ pseudo_bit_t serdes_m[0x00001]; /* Modify serdes parameters */
+/* -------------- */
+ pseudo_bit_t lid[0x00010]; /* default LID */
+ pseudo_bit_t lid_m[0x00001]; /* Modify default LID */
+ pseudo_bit_t reserved5[0x00003];
+ pseudo_bit_t port_en[0x00001]; /* enable port (E_Key) */
+ pseudo_bit_t port_en_m[0x00001]; /* Modify port_en */
+ pseudo_bit_t reserved6[0x0000a];
+/* -------------- */
+ pseudo_bit_t reserved7[0x0001f];
+ pseudo_bit_t guid_hi_m[0x00001]; /* Modify guid_hi */
+/* -------------- */
+ pseudo_bit_t guid_hi[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved8[0x0001f];
+ pseudo_bit_t guid_lo_m[0x00001]; /* Modify guid_lo */
+/* -------------- */
+ pseudo_bit_t guid_lo[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved9[0x0001f];
+ pseudo_bit_t nodeguid_hi_m[0x00001];
+/* -------------- */
+ pseudo_bit_t nodeguid_hi[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved10[0x0001f];
+ pseudo_bit_t nodeguid_lo_m[0x00001];
+/* -------------- */
+ pseudo_bit_t nodeguid_lo[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved11[0x00680];
+/* -------------- */
+};
+
+/* SRQ Context */
+
+struct hermonprm_srq_context_st { /* Little Endian */
+ pseudo_bit_t srqn[0x00018]; /* SRQ number */
+ pseudo_bit_t log_srq_size[0x00004]; /* Log2 of the Number of WQEs in the Receive Queue.
+ Maximum value is 0x10, i.e. 16M WQEs. */
+ pseudo_bit_t state[0x00004]; /* SRQ State:
+ 1111 - SW Ownership
+ 0000 - HW Ownership
+ 0001 - Error
+ Valid only on QUERY_SRQ and HW2SW_SRQ commands. */
+/* -------------- */
+ pseudo_bit_t src_domain[0x00010]; /* The Scalable RC Domain. Messages coming to receive ports specifying this SRQ as receive queue will be served only if SRC_Domain of the SRQ matches SRC_Domain of the transport QP of this message. */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t log_srq_stride[0x00003];/* Stride (max WQE size) on the receive queue. WQ entry is 16*(2^log_RQ_stride) bytes. */
+ pseudo_bit_t reserved1[0x00005];
+/* -------------- */
+ pseudo_bit_t cqn[0x00018]; /* Completion Queue to report SRC messages directed to this SRQ. */
+ pseudo_bit_t page_offset[0x00006]; /* The offset of the first WQE from the beginning of 4Kbyte page (Figure 52,“Work Queue Buffer Structure”) */
+ pseudo_bit_t reserved2[0x00002];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_h[0x00008];/* MTT Base Address [39:32] in ICM relative to INIT_HCA.mtt_base_addr */
+ pseudo_bit_t reserved4[0x00010];
+ pseudo_bit_t log2_page_size[0x00006];/* Log (base 2) of MTT page size in units of 4KByte */
+ pseudo_bit_t reserved5[0x00002];
+/* -------------- */
+ pseudo_bit_t reserved6[0x00003];
+ pseudo_bit_t mtt_base_addr_l[0x0001d];/* MTT Base Address [31:3] in ICM relative to INIT_HCA.mtt_base_addr */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* SRQ protection domain */
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t wqe_cnt[0x00010]; /* WQE count on the SRQ. Valid only upon QUERY_SRQ and HW2SW_SRQ commands. */
+ pseudo_bit_t lwm[0x00010]; /* Limit Water Mark - if the LWM is not zero, and the wqe_cnt drops below LWM when a WQE is dequeued from the SRQ, then an SRQ limit event is fired and the LWM is set to zero. Valid only upon QUERY_SRQ and HW2SW_SRQ commands. */
+/* -------------- */
+ pseudo_bit_t srq_wqe_counter[0x00010];/* A 16-bit counter incremented for each WQE posted to the SRQ. Must be 0x0 in SRQ initialization. Valid only upon the QUERY_SRQ command. */
+ pseudo_bit_t reserved8[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved9[0x00020];
+/* -------------- */
+ pseudo_bit_t db_record_addr_h[0x00020];/* SRQ DB Record physical address [63:32] */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00002];
+ pseudo_bit_t db_record_addr_l[0x0001e];/* SRQ DB Record physical address [31:2] */
+/* -------------- */
+};
+
+/* PBL */
+
+struct hermonprm_pbl_st { /* Little Endian */
+ pseudo_bit_t mtt_0_h[0x00020]; /* First MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_0_l[0x00020]; /* First MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_1_h[0x00020]; /* Second MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_1_l[0x00020]; /* Second MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_2_h[0x00020]; /* Third MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_2_l[0x00020]; /* Third MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_3_h[0x00020]; /* Fourth MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_3_l[0x00020]; /* Fourth MTT[31:0] */
+/* -------------- */
+};
+
+/* Performance Counters #### michal - gdror fixed */
+
+struct hermonprm_performance_counters_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00080];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00080];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00620];
+/* -------------- */
+};
+
+/* Transport and CI Error Counters */
+
+struct hermonprm_transport_and_ci_error_counters_st { /* Little Endian */
+ pseudo_bit_t rq_num_lle[0x00020]; /* Responder - number of local length errors */
+/* -------------- */
+ pseudo_bit_t sq_num_lle[0x00020]; /* Requester - number of local length errors */
+/* -------------- */
+ pseudo_bit_t rq_num_lqpoe[0x00020]; /* Responder - number local QP operation error */
+/* -------------- */
+ pseudo_bit_t sq_num_lqpoe[0x00020]; /* Requester - number local QP operation error */
+/* -------------- */
+ pseudo_bit_t rq_num_leeoe[0x00020]; /* Responder - number local EE operation error */
+/* -------------- */
+ pseudo_bit_t sq_num_leeoe[0x00020]; /* Requester - number local EE operation error */
+/* -------------- */
+ pseudo_bit_t rq_num_lpe[0x00020]; /* Responder - number of local protection errors */
+/* -------------- */
+ pseudo_bit_t sq_num_lpe[0x00020]; /* Requester - number of local protection errors */
+/* -------------- */
+ pseudo_bit_t rq_num_wrfe[0x00020]; /* Responder - number of CQEs with error.
+ Incremented each time a CQE with error is generated */
+/* -------------- */
+ pseudo_bit_t sq_num_wrfe[0x00020]; /* Requester - number of CQEs with error.
+ Incremented each time a CQE with error is generated */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_mwbe[0x00020]; /* Requester - number of memory window bind errors */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_bre[0x00020]; /* Requester - number of bad response errors */
+/* -------------- */
+ pseudo_bit_t rq_num_lae[0x00020]; /* Responder - number of local access errors */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t sq_num_rire[0x00020]; /* Requester - number of remote invalid request errors
+ NAK-Invalid Request on:
+ 1. Unsupported OpCode: Responder detected an unsupported OpCode.
+ 2. Unexpected OpCode: Responder detected an error in the sequence of OpCodes, such
+ as a missing "Last" packet.
+ Note: there is no PSN error, thus this does not indicate a dropped packet. */
+/* -------------- */
+ pseudo_bit_t rq_num_rire[0x00020]; /* Responder - number of remote invalid request errors.
+ NAK may or may not be sent.
+ 1. QP Async Affiliated Error: Unsupported or Reserved OpCode (RC,RD only):
+ Inbound request OpCode was either reserved, or was for a function not supported by this
+ QP. (E.g. RDMA or ATOMIC on QP not set up for this).
+ 2. Misaligned ATOMIC: VA does not point to an aligned address on an atomic opera-tion.
+ 3. Too many RDMA READ or ATOMIC Requests: There were more requests received
+ and not ACKed than allowed for the connection.
+ 4. Out of Sequence OpCode, current packet is "First" or "Only": The Responder
+ detected an error in the sequence of OpCodes; a missing "Last" packet
+ 5. Out of Sequence OpCode, current packet is not "First" or "Only": The Responder
+ detected an error in the sequence of OpCodes; a missing "First" packet
+ 6. Local Length Error: Inbound "Send" request message exceeded the responder.s avail-able
+ buffer space.
+ 7. Length error: RDMA WRITE request message contained too much or too little pay-load
+ data compared to the DMA length advertised in the first or only packet.
+ 8. Length error: Payload length was not consistent with the opcode:
+ a: 0 byte <= "only" <= PMTU bytes
+ b: ("first" or "middle") == PMTU bytes
+ c: 1byte <= "last" <= PMTU bytes
+ 9. Length error: Inbound message exceeded the size supported by the CA port. */
+/* -------------- */
+ pseudo_bit_t sq_num_rae[0x00020]; /* Requester - number of remote access errors.
+ NAK-Remote Access Error on:
+ R_Key Violation: Responder detected an invalid R_Key while executing an RDMA
+ Request. */
+/* -------------- */
+ pseudo_bit_t rq_num_rae[0x00020]; /* Responder - number of remote access errors.
+ R_Key Violation Responder detected an R_Key violation while executing an RDMA
+ request.
+ NAK may or may not be sent. */
+/* -------------- */
+ pseudo_bit_t sq_num_roe[0x00020]; /* Requester - number of remote operation errors.
+ NAK-Remote Operation Error on:
+ Remote Operation Error: Responder encountered an error, (local to the responder),
+ which prevented it from completing the request. */
+/* -------------- */
+ pseudo_bit_t rq_num_roe[0x00020]; /* Responder - number of remote operation errors.
+ NAK-Remote Operation Error on:
+ 1. Malformed WQE: Responder detected a malformed Receive Queue WQE while pro-cessing
+ the packet.
+ 2. Remote Operation Error: Responder encountered an error, (local to the responder),
+ which prevented it from completing the request. */
+/* -------------- */
+ pseudo_bit_t sq_num_tree[0x00020]; /* Requester - number of transport retries exceeded errors */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_rree[0x00020]; /* Requester - number of RNR nak retries exceeded errors */
+/* -------------- */
+ pseudo_bit_t rq_num_rnr[0x00020]; /* Responder - the number of RNR Naks sent */
+/* -------------- */
+ pseudo_bit_t sq_num_rnr[0x00020]; /* Requester - the number of RNR Naks received */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00040];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_rabrte[0x00020];/* Requester - number of remote aborted errors */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_ieecne[0x00020];/* Requester - number of invalid EE context number errors */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_ieecse[0x00020];/* Requester - invalid EE context state errors */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00380];
+/* -------------- */
+ pseudo_bit_t rq_num_oos[0x00020]; /* Responder - number of out of sequence requests received */
+/* -------------- */
+ pseudo_bit_t sq_num_oos[0x00020]; /* Requester - number of out of sequence Naks received */
+/* -------------- */
+ pseudo_bit_t rq_num_mce[0x00020]; /* Responder - number of bad multicast packets received */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00020];
+/* -------------- */
+ pseudo_bit_t rq_num_rsync[0x00020]; /* Responder - number of RESYNC operations */
+/* -------------- */
+ pseudo_bit_t sq_num_rsync[0x00020]; /* Requester - number of RESYNC operations */
+/* -------------- */
+ pseudo_bit_t rq_num_udsdprd[0x00020];/* The number of UD packets silently discarded on the receive queue due to lack of receive descriptor. */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00020];
+/* -------------- */
+ pseudo_bit_t rq_num_ucsdprd[0x00020];/* The number of UC packets silently discarded on the receive queue due to lack of receive descriptor. */
+/* -------------- */
+ pseudo_bit_t reserved11[0x003e0];
+/* -------------- */
+ pseudo_bit_t num_cqovf[0x00020]; /* Number of CQ overflows */
+/* -------------- */
+ pseudo_bit_t num_eqovf[0x00020]; /* Number of EQ overflows */
+/* -------------- */
+ pseudo_bit_t num_baddb[0x00020]; /* Number of bad doorbells */
+/* -------------- */
+ pseudo_bit_t reserved12[0x002a0];
+/* -------------- */
+};
+
+/* Event_data Field - HCR Completion Event #### michal - match PRM */
+
+struct hermonprm_hcr_completion_event_st { /* Little Endian */
+ pseudo_bit_t token[0x00010]; /* HCR Token */
+ pseudo_bit_t reserved0[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t status[0x00008]; /* HCR Status */
+ pseudo_bit_t reserved2[0x00018];
+/* -------------- */
+ pseudo_bit_t out_param_h[0x00020]; /* HCR Output Parameter [63:32] */
+/* -------------- */
+ pseudo_bit_t out_param_l[0x00020]; /* HCR Output Parameter [31:0] */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+};
+
+/* Completion with Error CQE #### michal - gdror fixed */
+
+struct hermonprm_completion_with_error_st { /* Little Endian */
+ pseudo_bit_t qpn[0x00018]; /* Indicates the QP for which completion is being reported */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x000a0];
+/* -------------- */
+ pseudo_bit_t syndrome[0x00008]; /* Completion with error syndrome:
+ 0x01 - Local Length Error
+ 0x02 - Local QP Operation Error
+ 0x03 - Local EE Context Operation Error
+ 0x04 - Local Protection Error
+ 0x05 - Work Request Flushed Error
+ 0x06 - Memory Window Bind Error
+ 0x10 - Bad Response Error
+ 0x11 - Local Access Error
+ 0x12 - Remote Invalid Request Error
+ 0x13 - Remote Access Error
+ 0x14 - Remote Operation Error
+ 0x15 - Transport Retry Counter Exceeded
+ 0x16 - RNR Retry Counter Exceeded
+ 0x20 - Local RDD Violation Error
+ 0x21 - Remote Invalid RD Request
+ 0x22 - Remote Aborted Error
+ 0x23 - Invalid EE Context Number
+ 0x24 - Invalid EE Context State
+ other - Reserved
+ Syndrome is defined according to the IB specification volume 1. For detailed explanation of the syndromes, refer to chapters 10-11 of the IB specification rev 1.1. */
+ pseudo_bit_t vendor_error_syndrome[0x00008];
+ pseudo_bit_t wqe_counter[0x00010];
+/* -------------- */
+ pseudo_bit_t opcode[0x00005]; /* The opcode of WQE completion is reported for.
+
+ The following values are reported in case of completion with error:
+ 0xFE - For completion with error on Receive Queues
+ 0xFF - For completion with error on Send Queues */
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t s_r[0x00001]; /* send 1 / receive 0 */
+ pseudo_bit_t owner[0x00001]; /* HW Flips this bit for every CQ warp around. Initialized to Zero. */
+ pseudo_bit_t reserved3[0x00018];
+/* -------------- */
+};
+
+/* Resize CQ Input Mailbox */
+
+struct hermonprm_resize_cq_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00006];
+ pseudo_bit_t page_offset[0x00006];
+ pseudo_bit_t reserved2[0x00014];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00018];
+ pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries) */
+ pseudo_bit_t reserved4[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_h[0x00008];
+ pseudo_bit_t reserved6[0x00010];
+ pseudo_bit_t log2_page_size[0x00006];
+ pseudo_bit_t reserved7[0x00002];
+/* -------------- */
+ pseudo_bit_t reserved8[0x00003];
+ pseudo_bit_t mtt_base_addr_l[0x0001d];
+/* -------------- */
+ pseudo_bit_t reserved9[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved10[0x00100];
+/* -------------- */
+};
+
+/* MAD_IFC Input Modifier */
+
+struct hermonprm_mad_ifc_input_modifier_st { /* Little Endian */
+ pseudo_bit_t port_number[0x00008]; /* The packet reception port number (1 or 2). */
+ pseudo_bit_t mad_extended_info[0x00001];/* Mad_Extended_Info valid bit (MAD_IFC Input Mailbox data from offset 00100h and down). MAD_Extended_Info is read only if this bit is set.
+ Required for trap generation when BKey check is enabled and for global routed packets. */
+ pseudo_bit_t reserved0[0x00007];
+ pseudo_bit_t rlid[0x00010]; /* Remote (source) LID from the received MAD.
+ This field is required for trap generation upon MKey/BKey validation. */
+/* -------------- */
+};
+
+/* MAD_IFC Input Mailbox ###michal -gdror fixed */
+
+struct hermonprm_mad_ifc_st { /* Little Endian */
+ pseudo_bit_t request_mad_packet[64][0x00020];/* Request MAD Packet (256bytes) */
+/* -------------- */
+ pseudo_bit_t my_qpn[0x00018]; /* Destination QP number from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t rqpn[0x00018]; /* Remote (source) QP number from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved2[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00010];
+ pseudo_bit_t ml_path[0x00007]; /* My (destination) LID path bits from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t g[0x00001]; /* If set, the GRH field in valid.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved4[0x00004];
+ pseudo_bit_t sl[0x00004]; /* Service Level of the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+/* -------------- */
+ pseudo_bit_t pkey_indx[0x00010]; /* Index in PKey table that matches PKey of the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved5[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved6[0x00160];
+/* -------------- */
+ pseudo_bit_t grh[10][0x00020]; /* The GRH field of the MAD packet that was scattered to the first 40 bytes pointed to by the scatter list.
+ Valid if Mad_extended_info bit (in the input modifier) and g bit are set.
+ Otherwise this field is reserved. */
+/* -------------- */
+ pseudo_bit_t reserved7[0x004c0];
+/* -------------- */
+};
+
+/* Query Debug Message #### michal - gdror fixed */
+
+struct hermonprm_query_debug_msg_st { /* Little Endian */
+ pseudo_bit_t phy_addr_h[0x00020]; /* Translation of the address in firmware area. High 32 bits. */
+/* -------------- */
+ pseudo_bit_t v[0x00001]; /* Physical translation is valid */
+ pseudo_bit_t reserved0[0x0000b];
+ pseudo_bit_t phy_addr_l[0x00014]; /* Translation of the address in firmware area. Low 32 bits. */
+/* -------------- */
+ pseudo_bit_t fw_area_base[0x00020]; /* Firmware area base address. The format strings and the trace buffers may be located starting from this address. */
+/* -------------- */
+ pseudo_bit_t fw_area_size[0x00020]; /* Firmware area size */
+/* -------------- */
+ pseudo_bit_t trc_hdr_sz[0x00020]; /* Trace message header size in dwords. */
+/* -------------- */
+ pseudo_bit_t trc_arg_num[0x00020]; /* The number of arguments per trace message. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x000c0];
+/* -------------- */
+ pseudo_bit_t dbg_msk_h[0x00020]; /* Debug messages mask [63:32] */
+/* -------------- */
+ pseudo_bit_t dbg_msk_l[0x00020]; /* Debug messages mask [31:0] */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t buff0_addr[0x00020]; /* Address in firmware area of Trace Buffer 0 */
+/* -------------- */
+ pseudo_bit_t buff0_size[0x00020]; /* Size of Trace Buffer 0 */
+/* -------------- */
+ pseudo_bit_t buff1_addr[0x00020]; /* Address in firmware area of Trace Buffer 1 */
+/* -------------- */
+ pseudo_bit_t buff1_size[0x00020]; /* Size of Trace Buffer 1 */
+/* -------------- */
+ pseudo_bit_t buff2_addr[0x00020]; /* Address in firmware area of Trace Buffer 2 */
+/* -------------- */
+ pseudo_bit_t buff2_size[0x00020]; /* Size of Trace Buffer 2 */
+/* -------------- */
+ pseudo_bit_t buff3_addr[0x00020]; /* Address in firmware area of Trace Buffer 3 */
+/* -------------- */
+ pseudo_bit_t buff3_size[0x00020]; /* Size of Trace Buffer 3 */
+/* -------------- */
+ pseudo_bit_t buff4_addr[0x00020]; /* Address in firmware area of Trace Buffer 4 */
+/* -------------- */
+ pseudo_bit_t buff4_size[0x00020]; /* Size of Trace Buffer 4 */
+/* -------------- */
+ pseudo_bit_t buff5_addr[0x00020]; /* Address in firmware area of Trace Buffer 5 */
+/* -------------- */
+ pseudo_bit_t buff5_size[0x00020]; /* Size of Trace Buffer 5 */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00080];
+/* -------------- */
+ pseudo_bit_t hw_buff_addr[0x00020]; /* Dror Mux Bohrer tracer */
+/* -------------- */
+ pseudo_bit_t hw_buff_size[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved4[0x003c0];
+/* -------------- */
+};
+
+/* User Access Region */
+
+struct hermonprm_uar_st { /* Little Endian */
+ struct hermonprm_rd_send_doorbell_st rd_send_doorbell;/* Reliable Datagram send doorbell */
+/* -------------- */
+ struct hermonprm_send_doorbell_st send_doorbell;/* Send doorbell */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ struct hermonprm_cq_cmd_doorbell_st cq_command_doorbell;/* CQ Doorbell */
+/* -------------- */
+ pseudo_bit_t reserved1[0x03ec0];
+/* -------------- */
+};
+
+/* Receive doorbell */
+
+struct hermonprm_receive_doorbell_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t wqe_counter[0x00010]; /* Modulo-64K counter of WQEs posted on this queue since its creation. Should be zero for the first doorbell on the QP */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00005];
+ pseudo_bit_t srq[0x00001]; /* If set, this is a Shared Receive Queue */
+ pseudo_bit_t reserved3[0x00002];
+ pseudo_bit_t qpn[0x00018]; /* QP number or SRQ number this doorbell is rung on */
+/* -------------- */
+};
+
+/* SET_IB Parameters */
+
+struct hermonprm_set_ib_st { /* Little Endian */
+ pseudo_bit_t rqk[0x00001]; /* Reset QKey Violation Counter */
+ pseudo_bit_t reserved0[0x00011];
+ pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified.
+ system_image_guid and sig must be the same for all ports. */
+ pseudo_bit_t reserved1[0x0000d];
+/* -------------- */
+ pseudo_bit_t capability_mask[0x00020];/* PortInfo Capability Mask */
+/* -------------- */
+ pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00180];
+/* -------------- */
+};
+
+/* Multicast Group Member #### michal - gdror fixed */
+
+struct hermonprm_mgm_entry_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00006];
+ pseudo_bit_t next_gid_index[0x0001a];/* Index of next Multicast Group Member whose GID maps to same MGID_HASH number.
+ The index is into the Multicast Group Table, which is the comprised the MGHT and AMGM tables.
+ next_gid_index=0 means end of the chain. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00060];
+/* -------------- */
+ pseudo_bit_t mgid_128_96[0x00020]; /* Multicast group GID[128:96] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_95_64[0x00020]; /* Multicast group GID[95:64] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_63_32[0x00020]; /* Multicast group GID[63:32] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_31_0[0x00020]; /* Multicast group GID[31:0] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ struct hermonprm_mgmqp_st mgmqp_0; /* Multicast Group Member QP */
+/* -------------- */
+ struct hermonprm_mgmqp_st mgmqp_1; /* Multicast Group Member QP */
+/* -------------- */
+ struct hermonprm_mgmqp_st mgmqp_2; /* Multicast Group Member QP */
+/* -------------- */
+ struct hermonprm_mgmqp_st mgmqp_3; /* Multicast Group Member QP */
+/* -------------- */
+ struct hermonprm_mgmqp_st mgmqp_4; /* Multicast Group Member QP */
+/* -------------- */
+ struct hermonprm_mgmqp_st mgmqp_5; /* Multicast Group Member QP */
+/* -------------- */
+ struct hermonprm_mgmqp_st mgmqp_6; /* Multicast Group Member QP */
+/* -------------- */
+ struct hermonprm_mgmqp_st mgmqp_7; /* Multicast Group Member QP */
+/* -------------- */
+};
+
+/* INIT_PORT Parameters #### michal - match PRM */
+
+struct hermonprm_init_port_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00004];
+ pseudo_bit_t vl_cap[0x00004]; /* Maximum VLs supported on the port, excluding VL15.
+ Legal values are 1,2,4 and 8. */
+ pseudo_bit_t port_width_cap[0x00004];/* IB Port Width
+ 1 - 1x
+ 3 - 1x, 4x
+ 11 - 1x, 4x or 12x (must not be used in InfiniHost-III-EX MT25208)
+ else - Reserved */
+ pseudo_bit_t reserved1[0x00004];
+ pseudo_bit_t g0[0x00001]; /* Set port GUID0 to GUID0 specified */
+ pseudo_bit_t ng[0x00001]; /* Set node GUID to node_guid specified.
+ node_guid and ng must be the same for all ports. */
+ pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified.
+ system_image_guid and sig must be the same for all ports. */
+ pseudo_bit_t reserved2[0x0000d];
+/* -------------- */
+ pseudo_bit_t max_gid[0x00010]; /* Maximum number of GIDs for the port */
+ pseudo_bit_t mtu[0x00010]; /* Maximum MTU Supported in bytes
+ must be: 256, 512, 1024, 2048 or 4096
+ For Eth port, can be any
+ Field must not cross device capabilities as reported
+ */
+/* -------------- */
+ pseudo_bit_t max_pkey[0x00010]; /* Maximum pkeys for the port.
+ Must be the same for both ports. */
+ pseudo_bit_t reserved3[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t guid0_h[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 63:32) */
+/* -------------- */
+ pseudo_bit_t guid0_l[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 31:0) */
+/* -------------- */
+ pseudo_bit_t node_guid_h[0x00020]; /* Node GUID[63:32], takes effect only if the NG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t node_guid_l[0x00020]; /* Node GUID[31:0], takes effect only if the NG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x006c0];
+/* -------------- */
+};
+
+/* Query Device Capablities #### michal - gdror fixed */
+
+struct hermonprm_query_dev_cap_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t log_max_qp[0x00005]; /* Log2 of the Maximum number of QPs supported */
+ pseudo_bit_t reserved1[0x00003];
+ pseudo_bit_t log2_rsvd_qps[0x00004];/* Log (base 2) of the number of QPs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_qps-1 */
+ pseudo_bit_t reserved2[0x00004];
+ pseudo_bit_t log_max_qp_sz[0x00008];/* The maximum number of WQEs allowed on the RQ or the SQ is 2^log_max_qp_sz-1 */
+ pseudo_bit_t log_max_srq_sz[0x00008];/* The maximum number of WQEs allowed on the SRQ is 2^log_max_srq_sz-1 */
+/* -------------- */
+ pseudo_bit_t log_max_scqs[0x00004]; /* log base 2 of number of supported schedule queues */
+ pseudo_bit_t reserved3[0x00004];
+ pseudo_bit_t num_rsvd_scqs[0x00006];
+ pseudo_bit_t reserved4[0x00002];
+ pseudo_bit_t log_max_srqs[0x00005];
+ pseudo_bit_t reserved5[0x00007];
+ pseudo_bit_t log2_rsvd_srqs[0x00004];
+/* -------------- */
+ pseudo_bit_t log_max_cq[0x00005]; /* Log2 of the Maximum number of CQs supported */
+ pseudo_bit_t reserved6[0x00003];
+ pseudo_bit_t log2_rsvd_cqs[0x00004];/* Log (base 2) of the number of CQs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsrvd_cqs-1 */
+ pseudo_bit_t reserved7[0x00004];
+ pseudo_bit_t log_max_cq_sz[0x00008];/* Log2 of the Maximum CQEs allowed in a CQ */
+ pseudo_bit_t reserved8[0x00008];
+/* -------------- */
+ pseudo_bit_t log_max_eq[0x00004]; /* Log2 of the Maximum number of EQs */
+ pseudo_bit_t reserved9[0x00004];
+ pseudo_bit_t num_rsvd_eqs[0x00004]; /* The number of EQs reserved for firmware use
+ The reserved resources are numbered from 0 to num_rsvd_eqs-1
+ If 0 - no resources are reserved. */
+ pseudo_bit_t reserved10[0x00004];
+ pseudo_bit_t log_max_d_mpts[0x00006];/* Log (base 2) of the maximum number of data MPT entries (the number of Regions/Windows) */
+ pseudo_bit_t reserved11[0x00002];
+ pseudo_bit_t log_max_eq_sz[0x00008];/* Log2 of the Maximum EQEs allowed in a EQ */
+/* -------------- */
+ pseudo_bit_t log_max_mtts[0x00006]; /* Log2 of the Maximum number of MTT entries */
+ pseudo_bit_t reserved12[0x00002];
+ pseudo_bit_t log2_rsvd_mrws[0x00004];/* Log (base 2) of the number of MPTs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_mrws-1 */
+ pseudo_bit_t reserved13[0x00004];
+ pseudo_bit_t log_max_mrw_sz[0x00007];/* Log2 of the Maximum Size of Memory Region/Window. is it in PRM layout? */
+ pseudo_bit_t reserved14[0x00005];
+ pseudo_bit_t log2_rsvd_mtts[0x00004];/* Log (base 2) of the number of MTT entries reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_mtts-1
+ */
+/* -------------- */
+ pseudo_bit_t reserved15[0x00020];
+/* -------------- */
+ pseudo_bit_t log_max_ra_res_qp[0x00006];/* Log2 of the Maximum number of outstanding RDMA read/Atomic per QP as a responder */
+ pseudo_bit_t reserved16[0x0000a];
+ pseudo_bit_t log_max_ra_req_qp[0x00006];/* Log2 of the maximum number of outstanding RDMA read/Atomic per QP as a requester */
+ pseudo_bit_t reserved17[0x0000a];
+/* -------------- */
+ pseudo_bit_t log_max_ra_res_global[0x00006];/* Log2 of the maximum number of RDMA read/atomic operations the HCA responder can support globally. That implies the RDB table size. */
+ pseudo_bit_t reserved18[0x0001a];
+/* -------------- */
+ pseudo_bit_t rsz_srq[0x00001]; /* Ability to modify the maximum number of WRs per SRQ. */
+ pseudo_bit_t reserved19[0x0001f];
+/* -------------- */
+ pseudo_bit_t num_ports[0x00004]; /* Number of IB ports. */
+ pseudo_bit_t max_vl_ib[0x00004]; /* Maximum VLs supported on each port, excluding VL15 */
+ pseudo_bit_t ib_port_width[0x00004];/* IB Port Width
+ 1 - 1x
+ 3 - 1x, 4x
+ 11 - 1x, 4x or 12x
+ else - Reserved */
+ pseudo_bit_t ib_mtu[0x00004]; /* Maximum MTU Supported
+ 0x0 - Reserved
+ 0x1 - 256
+ 0x2 - 512
+ 0x3 - 1024
+ 0x4 - 2048
+ 0x5 - 4096
+ 0x6-0xF Reserved */
+ pseudo_bit_t local_ca_ack_delay[0x00005];/* The Local CA ACK Delay. This is the value recommended to be returned in Query HCA verb.
+ The delay value in microseconds is computed using 4.096us * 2^(local_ca_ack_delay). */
+ pseudo_bit_t port_type[0x00004]; /* Hermon New. bit per port. bit0 is first port. value '1' is ehternet. '0' is IB */
+ pseudo_bit_t reserved20[0x00004];
+ pseudo_bit_t w[0x00001]; /* Hermon New. 10GB eth support */
+ pseudo_bit_t j[0x00001]; /* Hermon New. Jumbo frame support */
+ pseudo_bit_t reserved21[0x00001];
+/* -------------- */
+ pseudo_bit_t log_max_gid[0x00004]; /* Log2 of the maximum number of GIDs per port */
+ pseudo_bit_t reserved22[0x00004];
+ pseudo_bit_t log_ethtype[0x00004]; /* Hermon New. log2 eth type table size */
+ pseudo_bit_t reserved23[0x00004];
+ pseudo_bit_t log_drain_size[0x00008];/* Log (base 2) of minimum size of the NoDropVLDrain buffer, specified in 4Kpages units */
+ pseudo_bit_t log_max_msg[0x00005]; /* Log (base 2) of the maximum message size supported by the device */
+ pseudo_bit_t reserved24[0x00003];
+/* -------------- */
+ pseudo_bit_t log_max_pkey[0x00004]; /* Log2 of the max PKey Table Size (per IB port) */
+ pseudo_bit_t reserved25[0x0000c];
+ pseudo_bit_t stat_rate_support[0x00010];/* bit mask of stat rate supported
+ bit 0 - full bw
+ bit 1 - 1/4 bw
+ bit 2 - 1/8 bw
+ bit 3 - 1/2 bw; */
+/* -------------- */
+ pseudo_bit_t reserved26[0x00020];
+/* -------------- */
+ pseudo_bit_t rc[0x00001]; /* RC Transport supported */
+ pseudo_bit_t uc[0x00001]; /* UC Transport Supported */
+ pseudo_bit_t ud[0x00001]; /* UD Transport Supported */
+ pseudo_bit_t src[0x00001]; /* SRC Transport Supported. Hermon New instead of RD. */
+ pseudo_bit_t rcm[0x00001]; /* Reliable Multicast support. Hermon New instead of IPv6 Transport Supported */
+ pseudo_bit_t fcoib[0x00001]; /* Hermon New */
+ pseudo_bit_t srq[0x00001]; /* SRQ is supported
+ */
+ pseudo_bit_t checksum[0x00001]; /* IP over IB checksum is supported */
+ pseudo_bit_t pkv[0x00001]; /* PKey Violation Counter Supported */
+ pseudo_bit_t qkv[0x00001]; /* QKey Violation Coutner Supported */
+ pseudo_bit_t vmm[0x00001]; /* Hermon New */
+ pseudo_bit_t reserved27[0x00005];
+ pseudo_bit_t mw[0x00001]; /* Memory windows supported */
+ pseudo_bit_t apm[0x00001]; /* Automatic Path Migration Supported */
+ pseudo_bit_t atm[0x00001]; /* Atomic operations supported (atomicity is guaranteed between QPs on this HCA) */
+ pseudo_bit_t rm[0x00001]; /* Raw Multicast Supported */
+ pseudo_bit_t avp[0x00001]; /* Address Vector Port checking supported */
+ pseudo_bit_t udm[0x00001]; /* UD Multicast Supported */
+ pseudo_bit_t reserved28[0x00002];
+ pseudo_bit_t pg[0x00001]; /* Paging on demand supported */
+ pseudo_bit_t r[0x00001]; /* Router mode supported */
+ pseudo_bit_t reserved29[0x00006];
+/* -------------- */
+ pseudo_bit_t log_pg_sz[0x00008]; /* Minimum system page size supported (log2).
+ For proper operation it must be less than or equal the hosting platform (CPU) minimum page size. */
+ pseudo_bit_t reserved30[0x00008];
+ pseudo_bit_t uar_sz[0x00006]; /* UAR Area Size = 1MB * 2^uar_sz */
+ pseudo_bit_t reserved31[0x00006];
+ pseudo_bit_t num_rsvd_uars[0x00004];/* The number of UARs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_uars-1
+ Note that UAR number num_reserved_uars is always for the kernel. */
+/* -------------- */
+ pseudo_bit_t log_max_bf_pages[0x00006];/* Maximum number of BlueFlame pages is 2^log_max_bf_pages */
+ pseudo_bit_t reserved32[0x00002];
+ pseudo_bit_t log_max_bf_regs_per_page[0x00006];/* Maximum number of BlueFlame registers per page is 2^log_max_bf_regs_per_page. It may be that only the beginning of a page contains BlueFlame registers. */
+ pseudo_bit_t reserved33[0x00002];
+ pseudo_bit_t log_bf_reg_size[0x00005];/* BlueFlame register size in bytes is 2^log_bf_reg_size */
+ pseudo_bit_t reserved34[0x0000a];
+ pseudo_bit_t bf[0x00001]; /* If set to "1" then BlueFlame may be used. */
+/* -------------- */
+ pseudo_bit_t max_desc_sz_sq[0x00010];/* Max descriptor size in bytes for the send queue */
+ pseudo_bit_t max_sg_sq[0x00008]; /* The maximum S/G list elements in a SQ WQE (max_desc_sz/16 - 3) */
+ pseudo_bit_t reserved35[0x00008];
+/* -------------- */
+ pseudo_bit_t max_desc_sz_rq[0x00010];/* Max descriptor size in bytes for the receive queue */
+ pseudo_bit_t max_sg_rq[0x00008]; /* The maximum S/G list elements in a RQ WQE (max_desc_sz/16 - 3) */
+ pseudo_bit_t reserved36[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved37[0x00001];
+ pseudo_bit_t fexch_base_mpt_31_25[0x00007];/* Hermon New. FC mpt base mpt number */
+ pseudo_bit_t fcp_ud_base_23_8[0x00010];/* Hermon New. FC ud QP base QPN */
+ pseudo_bit_t fexch_base_qp_23_16[0x00008];/* Hermon New. FC Exchange QP base QPN */
+/* -------------- */
+ pseudo_bit_t reserved38[0x00020];
+/* -------------- */
+ pseudo_bit_t log_max_mcg[0x00008]; /* Log2 of the maximum number of multicast groups */
+ pseudo_bit_t num_rsvd_mcgs[0x00004];/* The number of MGMs reserved for firmware use in the MGHT.
+ The reserved resources are numbered from 0 to num_reserved_mcgs-1
+ If 0 - no resources are reserved. */
+ pseudo_bit_t reserved39[0x00004];
+ pseudo_bit_t log_max_qp_mcg[0x00008];/* Log2 of the maximum number of QPs per multicast group */
+ pseudo_bit_t reserved40[0x00008];
+/* -------------- */
+ pseudo_bit_t log_max_srcds[0x00004];/* Log2 of the maximum number of SRC Domains */
+ pseudo_bit_t reserved41[0x00008];
+ pseudo_bit_t num_rsvd_scrds[0x00004];/* The number of SRCDs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_rdds-1.
+ If 0 - no resources are reserved. */
+ pseudo_bit_t log_max_pd[0x00005]; /* Log2 of the maximum number of PDs */
+ pseudo_bit_t reserved42[0x00007];
+ pseudo_bit_t num_rsvd_pds[0x00004]; /* The number of PDs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_pds-1
+ If 0 - no resources are reserved. */
+/* -------------- */
+ pseudo_bit_t reserved43[0x000c0];
+/* -------------- */
+ pseudo_bit_t qpc_entry_sz[0x00010]; /* QPC Entry Size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 256 bytes */
+ pseudo_bit_t rdmardc_entry_sz[0x00010];/* RdmaRdC Entry Size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 256 bytes */
+/* -------------- */
+ pseudo_bit_t altc_entry_sz[0x00010];/* Extended QPC entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 32 bytes */
+ pseudo_bit_t aux_entry_sz[0x00010]; /* Auxilary context entry size */
+/* -------------- */
+ pseudo_bit_t cqc_entry_sz[0x00010]; /* CQC entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 64 bytes */
+ pseudo_bit_t eqc_entry_sz[0x00010]; /* EQ context entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 64 bytes */
+/* -------------- */
+ pseudo_bit_t c_mpt_entry_sz[0x00010];/* cMPT entry size in Bytes for the device.
+ For the InfiniHost-III-EX MT25208 entry size is 64 bytes */
+ pseudo_bit_t srq_entry_sz[0x00010]; /* SRQ context entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 32 bytes */
+/* -------------- */
+ pseudo_bit_t d_mpt_entry_sz[0x00010];/* dMPT entry size in Bytes for the device.
+ For the InfiniHost-III-EX MT25208 entry size is 64 bytes */
+ pseudo_bit_t mtt_entry_sz[0x00010]; /* MTT entry size in Bytes for the device.
+ For the InfiniHost-III-EX MT25208 entry size is 8 bytes */
+/* -------------- */
+ pseudo_bit_t bmme[0x00001]; /* Base Memory Management Extension Support */
+ pseudo_bit_t win_type[0x00001]; /* Bound Type 2 Memory Window Association mechanism:
+ 0 - Type 2A - QP Number Association; or
+ 1 - Type 2B - QP Number and PD Association. */
+ pseudo_bit_t mps[0x00001]; /* Ability of this HCA to support multiple page sizes per Memory Region. */
+ pseudo_bit_t bl[0x00001]; /* Ability of this HCA to support Block List Physical Buffer Lists. */
+ pseudo_bit_t zb[0x00001]; /* Zero Based region/windows supported */
+ pseudo_bit_t lif[0x00001]; /* Ability of this HCA to support Local Invalidate Fencing. */
+ pseudo_bit_t reserved44[0x0001a];
+/* -------------- */
+ pseudo_bit_t resd_lkey[0x00020]; /* The value of the reserved Lkey for Base Memory Management Extension */
+/* -------------- */
+ pseudo_bit_t reserved45[0x00020];
+/* -------------- */
+ pseudo_bit_t max_icm_size_h[0x00020];/* Bits [63:32] of maximum ICM size InfiniHost III Ex support in bytes. */
+/* -------------- */
+ pseudo_bit_t max_icm_size_l[0x00020];/* Bits [31:0] of maximum ICM size InfiniHost III Ex support in bytes. */
+/* -------------- */
+ pseudo_bit_t reserved46[0x002c0];
+/* -------------- */
+};
+
+/* QUERY_ADAPTER Parameters Block #### michal - gdror fixed */
+
+struct hermonprm_query_adapter_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00018];
+ pseudo_bit_t intapin[0x00008]; /* Driver should set this field to INTR value in the event queue in order to get Express interrupt messages. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00060];
+/* -------------- */
+ struct hermonprm_vsd_st vsd; /* ###michal- this field was replaced by 2 fields : vsd .1664; vsd(continued/psid .128; */
+/* -------------- */
+};
+
+/* QUERY_FW Parameters Block #### michal - doesn't match PRM */
+
+struct hermonprm_query_fw_st { /* Little Endian */
+ pseudo_bit_t fw_rev_major[0x00010]; /* Firmware Revision - Major */
+ pseudo_bit_t fw_pages[0x00010]; /* Amount of physical memory to be allocated for FW usage is in 4KByte pages. */
+/* -------------- */
+ pseudo_bit_t fw_rev_minor[0x00010]; /* Firmware Revision - Minor */
+ pseudo_bit_t fw_rev_subminor[0x00010];/* Firmware Sub-minor version (Patch level). */
+/* -------------- */
+ pseudo_bit_t cmd_interface_rev[0x00010];/* Command Interface Interpreter Revision ID */
+ pseudo_bit_t reserved0[0x00010];
+/* -------------- */
+ pseudo_bit_t log_max_outstanding_cmd[0x00008];/* Log2 of the maximum number of commands the HCR can support simultaneously */
+ pseudo_bit_t reserved1[0x00017];
+ pseudo_bit_t dt[0x00001]; /* Debug Trace Support
+ 0 - Debug trace is not supported
+ 1 - Debug trace is supported */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t ccq[0x00001]; /* CCQ support */
+ pseudo_bit_t reserved3[0x00006];
+ pseudo_bit_t fw_seconds[0x00008]; /* FW timestamp - seconds. Dispalyed as Hexadecimal number */
+ pseudo_bit_t fw_minutes[0x00008]; /* FW timestamp - minutes. Dispalyed as Hexadecimal number */
+ pseudo_bit_t fw_hour[0x00008]; /* FW timestamp - hour. Dispalyed as Hexadecimal number */
+/* -------------- */
+ pseudo_bit_t fw_day[0x00008]; /* FW timestamp - day. Dispalyed as Hexadecimal number */
+ pseudo_bit_t fw_month[0x00008]; /* FW timestamp - month. Dispalyed as Hexadecimal number */
+ pseudo_bit_t fw_year[0x00010]; /* FW timestamp - year. Dispalyed as Hexadecimal number (e.g. 0x2005) */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00040];
+/* -------------- */
+ pseudo_bit_t clr_int_base_offset_h[0x00020];/* Bits [63:32] of the Clear Interrupt registerÂ’s offset from clr_int_bar register in PCIaddress space. Points to a 64-bit register. */
+/* -------------- */
+ pseudo_bit_t clr_int_base_offset_l[0x00020];/* Bits [31:0] of the Clear Interrupt registerÂ’s offset from clr_int_bar register in PCIaddress space. Points to a 64-bit register. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x0001e];
+ pseudo_bit_t clr_int_bar[0x00002]; /* PCI base address register (BAR) where clr_int register is located.
+ 00 - BAR 0-1
+ 01 - BAR 2-3
+ 10 - BAR 4-5
+ 11 - Reserved
+ The PCI BARs of ConnectX are 64 bit BARs.
+ In ConnectX, clr_int register is located on BAR 0-1. */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00020];
+/* -------------- */
+ pseudo_bit_t error_buf_offset_h[0x00020];/* Read Only buffer for catastrophic error reports (bits [63:32] of offset from error_buf_bar register in PCI address space.) */
+/* -------------- */
+ pseudo_bit_t error_buf_offset_l[0x00020];/* Read Only buffer for catastrophic error reports (bits [31:0] of offset from error_buf_bar register in PCI address space.) */
+/* -------------- */
+ pseudo_bit_t error_buf_size[0x00020];/* Size in words */
+/* -------------- */
+ pseudo_bit_t reserved7[0x0001e];
+ pseudo_bit_t error_buf_bar[0x00002];/* PCI base address register (BAR) where error_buf register is located.
+ 00 - BAR 0-1
+ 01 - BAR 2-3
+ 10 - BAR 4-5
+ 11 - Reserved
+ The PCI BARs of ConnectX are 64 bit BARs.
+ In ConnectX, error_buf register is located on BAR 0-1. */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00600];
+/* -------------- */
+};
+
+/* Memory Access Parameters for UD Address Vector Table */
+
+struct hermonprm_udavtable_memory_parameters_st { /* Little Endian */
+ pseudo_bit_t l_key[0x00020]; /* L_Key used to access TPT */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* PD used by TPT for matching against PD of region entry being accessed. */
+ pseudo_bit_t reserved0[0x00005];
+ pseudo_bit_t xlation_en[0x00001]; /* When cleared, address is physical address and no translation will be done. When set, address is virtual. */
+ pseudo_bit_t reserved1[0x00002];
+/* -------------- */
+};
+
+/* INIT_HCA & QUERY_HCA Parameters Block ####michal-doesn't match PRM (see differs below) new size in bytes:0x300 */
+
+struct hermonprm_init_hca_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00018];
+ pseudo_bit_t version[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00010];
+ pseudo_bit_t hca_core_clock[0x00010];/* Internal Clock freq in MHz */
+/* -------------- */
+ pseudo_bit_t router_qp[0x00018]; /* QP number for router mode (8 LSBits should be 0). Low order 8 bits are taken from the TClass field of the incoming packet.
+ Valid only if RE bit is set */
+ pseudo_bit_t reserved3[0x00005];
+ pseudo_bit_t ipr2[0x00001]; /* Hermon New. IP router on port 2 */
+ pseudo_bit_t ipr1[0x00001]; /* Hermon New. IP router on port 1 */
+ pseudo_bit_t ibr[0x00001]; /* InfiniBand Router Mode */
+/* -------------- */
+ pseudo_bit_t udp[0x00001]; /* UD Port Check Enable
+ 0 - Port field in Address Vector is ignored
+ 1 - HCA will check the port field in AV entry (fetched for UD descriptor) against the Port of the UD QP executing the descriptor. */
+ pseudo_bit_t he[0x00001]; /* Host Endianess - Used for Atomic Operations
+ 0 - Host is Little Endian
+ 1 - Host is Big endian
+ */
+ pseudo_bit_t reserved4[0x00001];
+ pseudo_bit_t ce[0x00001]; /* Checksum Enabled - when Set IPoverIB checksum generation & checking is enabled */
+ pseudo_bit_t reserved5[0x0001c];
+/* -------------- */
+ pseudo_bit_t reserved6[0x00040];
+/* -------------- */
+ struct hermonprm_qpcbaseaddr_st qpc_eec_cqc_eqc_rdb_parameters;/* ## michal - this field has chenged to - "qpc_cqc_eqc_parameters" - gdror, this is ok for now */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00100];
+/* -------------- */
+ struct hermonprm_multicastparam_st multicast_parameters;/* ##michal- this field has chenged to - "IBUD/IPv6_multicast_parameters" - gdror - this is OK for now */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00080];
+/* -------------- */
+ struct hermonprm_tptparams_st tpt_parameters;
+/* -------------- */
+ pseudo_bit_t reserved9[0x00080];
+/* -------------- */
+ struct hermonprm_uar_params_st uar_parameters;/* UAR Parameters */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00600];
+/* -------------- */
+};
+
+/* Event Queue Context Table Entry #### michal - gdror fixed */
+
+struct hermonprm_eqc_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t st[0x00004]; /* Event delivery state machine
+ 0x9 - Armed
+ 0xA - Fired
+ 0xB - Always_Armed (auto-rearm)
+ other - reserved */
+ pseudo_bit_t reserved1[0x00005];
+ pseudo_bit_t oi[0x00001]; /* Oerrun ignore.
+ If set, HW will not check EQ full condition when writing new EQEs. */
+ pseudo_bit_t ec[0x00001]; /* is set, all EQEs are written (coalesced) to first EQ entry */
+ pseudo_bit_t reserved2[0x00009];
+ pseudo_bit_t status[0x00004]; /* EQ status:
+ 0000 - OK
+ 1010 - EQ write failure
+ Valid for the QUERY_EQ and HW2SW_EQ commands only */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00005];
+ pseudo_bit_t page_offset[0x00007]; /* offset bits[11:5] of first EQE in the EQ relative to the first page in memory region mapping this EQ */
+ pseudo_bit_t reserved5[0x00014];
+/* -------------- */
+ pseudo_bit_t reserved6[0x00018];
+ pseudo_bit_t log_eq_size[0x00005]; /* Log (base 2) of the EQ size (in entries). Maximum EQ size is 2^22 EQEs (max log_eq_size is 22) */
+ pseudo_bit_t reserved7[0x00003];
+/* -------------- */
+ pseudo_bit_t eq_max_count[0x00010]; /* Event Generation Moderation counter */
+ pseudo_bit_t eq_period[0x00010]; /* Event Generation moderation timed, microseconds */
+/* -------------- */
+ pseudo_bit_t intr[0x0000a]; /* MSI-X table entry index to be used to signal interrupts on this EQ. Reserved if MSI-X are not enabled in the PCI configuration header. */
+ pseudo_bit_t reserved8[0x00016];
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_h[0x00008];/* MTT Base Address [39:32] relative to INIT_HCA.mtt_base_addr */
+ pseudo_bit_t reserved9[0x00010];
+ pseudo_bit_t log2_page_size[0x00006];/* Log (base 2) of MTT page size in units of 4KByte */
+ pseudo_bit_t reserved10[0x00002];
+/* -------------- */
+ pseudo_bit_t reserved11[0x00003];
+ pseudo_bit_t mtt_base_addr_l[0x0001d];/* MTT Base Address [31:3] relative to INIT_HCA.mtt_base_addr */
+/* -------------- */
+ pseudo_bit_t reserved12[0x00040];
+/* -------------- */
+ pseudo_bit_t consumer_counter[0x00018];/* Consumer counter. The counter is incremented for each EQE polled from the EQ.
+ Must be 0x0 in EQ initialization.
+ Maintained by HW (valid for the QUERY_EQ command only). */
+ pseudo_bit_t reserved13[0x00008];
+/* -------------- */
+ pseudo_bit_t producer_counter[0x00018];/* Producer Coutner. The counter is incremented for each EQE that is written by the HW to the EQ.
+ EQ overrun is reported if Producer_counter + 1 equals to Consumer_counter and a EQE needs to be added.
+ Maintained by HW (valid for the QUERY_EQ command only) */
+ pseudo_bit_t reserved14[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved15[0x00080];
+/* -------------- */
+};
+
+/* Memory Translation Table (MTT) Entry #### michal - match to PRM */
+
+struct hermonprm_mtt_st { /* Little Endian */
+ pseudo_bit_t ptag_h[0x00020]; /* High-order bits of physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */
+/* -------------- */
+ pseudo_bit_t p[0x00001]; /* Present bit. If set, page entry is valid. If cleared, access to this page will generate non-present page access fault. */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t ptag_l[0x0001d]; /* Low-order bits of Physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */
+/* -------------- */
+};
+
+/* Memory Protection Table (MPT) Entry ### doesn't match PRM (new fields were added). new size in bytes : 0x54 */
+
+struct hermonprm_mpt_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t r_w[0x00001]; /* Defines whether this entry is Region (1) or Window (0) */
+ pseudo_bit_t pa[0x00001]; /* Physical address. If set, no virtual-to-physical address translation is performed for this region */
+ pseudo_bit_t lr[0x00001]; /* If set - local read access is enabled. Must be set for all MPT Entries. */
+ pseudo_bit_t lw[0x00001]; /* If set - local write access is enabled */
+ pseudo_bit_t rr[0x00001]; /* If set - remote read access is enabled. */
+ pseudo_bit_t rw[0x00001]; /* If set - remote write access is enabled */
+ pseudo_bit_t atomic[0x00001]; /* If set - remote Atomic access is allowed. */
+ pseudo_bit_t eb[0x00001]; /* If set - bind is enabled. Valid only for regions. */
+ pseudo_bit_t atc_req[0x00001]; /* If set, second hop of address translation (PA to MA) to be performed in the device prior to issuing the uplink request. */
+ pseudo_bit_t atc_xlated[0x00001]; /* If set, uplink cycle to be issues with “ATC_translated” indicator to force bypass of the chipset IOMMU. */
+ pseudo_bit_t reserved1[0x00001];
+ pseudo_bit_t no_snoop[0x00001]; /* If set, issue PCIe cycle with ûno Snoopÿ attribute - cycle not to be snooped in CPU caches */
+ pseudo_bit_t reserved2[0x00008];
+ pseudo_bit_t status[0x00004]; /* 0xF - Not Valid 0x3 - Free. else - HW ownership.Unbound Type1 windows are denoted by reg_wnd_len=0. Unbound Type II windows are denoted by Status = Free. */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00007];
+ pseudo_bit_t bqp[0x00001]; /* 0 - not bound to qp (type 1 window, MR)1 - bound to qp (type 2 window) */
+ pseudo_bit_t qpn[0x00018]; /* QP number this MW is attached to. Valid for type2 memory windows and on QUERY_MPT only */
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* The memory Key. The field holds the mem_key field in the following semantics: {key[7:0],key[31:8]}. */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* Protection Domain. If VMM support is enabled PD[17:23] specify Guest VM Identifier */
+ pseudo_bit_t en_rinv[0x00001]; /* Enable remote invalidation */
+ pseudo_bit_t ei[0x00001]; /* Enable Invalidation - When set, Local/Remote invalidation can be executed on this window/region. Must be set for type2 windows and non-shared physical memory regions. Must be clear for regions that are used to access Work Queues, Completion Queues and Event Queues */
+ pseudo_bit_t nce[0x00001]; /* Data can be cached in Network Cache (see ûNetwork Cacheÿ on page 81) */
+ pseudo_bit_t fre[0x00001]; /* When set, Fast Registration Operations can be executed on this region */
+ pseudo_bit_t rae[0x00001]; /* When set, remote access can be enabled on this region. Used when executing Fast Registration Work Request to validate that remote access rights can be granted to this MPT. If the bit is cleared, Fast Registration Work Request requesting remote access rights will fail */
+ pseudo_bit_t w_dif[0x00001]; /* Wire space contains dif */
+ pseudo_bit_t m_dif[0x00001]; /* Memory space contains dif */
+ pseudo_bit_t reserved4[0x00001];
+/* -------------- */
+ pseudo_bit_t start_addr_h[0x00020]; /* Start Address - Virtual Address where this region/window starts */
+/* -------------- */
+ pseudo_bit_t start_addr_l[0x00020]; /* Start Address - Virtual Address where this region/window starts */
+/* -------------- */
+ pseudo_bit_t len_h[0x00020]; /* Region/Window Length */
+/* -------------- */
+ pseudo_bit_t len_l[0x00020]; /* Region/Window Length */
+/* -------------- */
+ pseudo_bit_t lkey[0x00020]; /* Must be 0 for SW2HW_MPT. On QUERY_MPT and HW2SW_MPT commands for Memory Window it reflects the LKey of the Region that the Window is bound to.The field holds the lkey field in the following semantics: {key[7:0],key[31:8]}. */
+/* -------------- */
+ pseudo_bit_t win_cnt[0x00018]; /* Number of windows bound to this region. Valid for regions only.The field is valid only for the QUERY_MPT and HW2SW_MPT commands. */
+ pseudo_bit_t reserved5[0x00008];
+/* -------------- */
+ pseudo_bit_t mtt_rep[0x00004]; /* Log (base 2) of the number of time an MTT is replicated.E.g. for 64KB virtual blocks from 512B blocks, a replication factor of 2^7 is needed (MTT_REPLICATION_FACTOR=7).Up to 1MB of replicated block works */
+ pseudo_bit_t reserved6[0x00011];
+ pseudo_bit_t block_mode[0x00001]; /* If set, the page size is not power of two, and entity_size is in bytes. */
+ pseudo_bit_t len64[0x00001]; /* Region/Window Length[64]. This bit added to enable registering 2^64 bytes per region */
+ pseudo_bit_t fbo_en[0x00001]; /* If set, mtt_fbo field is valid, otherwise it is calculated from least significant bytes of the address. Must be set when mtt_rep is used or MPT is block-mode region */
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t mtt_adr_h[0x00008]; /* Offset to MTT list for this region. Must be aligned on 8 bytes. */
+ pseudo_bit_t reserved8[0x00018];
+/* -------------- */
+ pseudo_bit_t mtt_adr_l[0x00020]; /* Offset to MTT list for this region. Must be aligned on 8 bytes.###michal-relpaced with: RESERVED .3;mtt_adr_l .29; gdror - this is OK to leave it this way. */
+/* -------------- */
+ pseudo_bit_t mtt_size[0x00020]; /* Number of MTT entries allocated for this MR.When Fast Registration Operations cannot be executed on this region (FRE bit is zero) this field is reserved.When Fast Registration Operation is enabled (FRE bit is set) this field indicates the number of MTTs allocated for this MR. If mtt_sz value cannot be zero. */
+/* -------------- */
+ pseudo_bit_t entity_size[0x00015]; /* Page/block size. If MPT maps pages, the page size is 2entiry_size. If MPT maps blocks, the entity_size field specifies block size in bytes. The minimum amount of memory that can be mapped with single MTT is 512 bytes. */
+ pseudo_bit_t reserved9[0x0000b];
+/* -------------- */
+ pseudo_bit_t mtt_fbo[0x00015]; /* First byte offset in the zero-based region - the first byte within the first block/page start address refers to. When mtt_rep is being used, fbo points within the replicated block (i.e. block-size x 2^mtt_rep) */
+ pseudo_bit_t reserved10[0x0000b];
+/* -------------- */
+};
+
+/* Completion Queue Context Table Entry #### michal - match PRM */
+
+struct hermonprm_completion_queue_context_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t st[0x00004]; /* Event delivery state machine
+ 0x0 - reserved
+ 0x9 - ARMED (Request for Notification)
+ 0x6 - ARMED SOLICITED (Request Solicited Notification)
+ 0xA - FIRED
+ other - reserved
+
+ Must be 0x0 in CQ initialization.
+ Valid for the QUERY_CQ and HW2SW_CQ commands only. */
+ pseudo_bit_t reserved1[0x00005];
+ pseudo_bit_t oi[0x00001]; /* When set, overrun ignore is enabled.
+ When set, Updates of CQ consumer counter (poll for completion) or Request completion notifications (Arm CQ) doorbells should not be rang on that CQ. */
+ pseudo_bit_t cc[0x00001]; /* is set, all CQEs are written (coalesced) to first CQ entry */
+ pseudo_bit_t reserved2[0x00009];
+ pseudo_bit_t status[0x00004]; /* CQ status
+ 0000 - OK
+ 1001 - CQ overflow
+ 1010 - CQ write failure
+ Valid for the QUERY_CQ and HW2SW_CQ commands only */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00005];
+ pseudo_bit_t page_offset[0x00007]; /* offset of first CQE in the CQ relative to the first page in memory region mapping this CQ */
+ pseudo_bit_t reserved5[0x00014];
+/* -------------- */
+ pseudo_bit_t usr_page[0x00018]; /* UAR page this CQ can be accessed through (ringinig CQ doorbells) */
+ pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries).
+ Maximum CQ size is 2^17 CQEs (max log_cq_size is 17) */
+ pseudo_bit_t reserved6[0x00003];
+/* -------------- */
+ pseudo_bit_t cq_max_count[0x00010]; /* Event Generation Moderation counter */
+ pseudo_bit_t cq_period[0x00010]; /* Event Generation moderation timed, microseconds */
+/* -------------- */
+ pseudo_bit_t c_eqn[0x00009]; /* Event Queue this CQ reports completion events to.
+ Valid values are 0 to 63
+ If configured to value other than 0-63, completion events will not be reported on the CQ. */
+ pseudo_bit_t reserved7[0x00017];
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_h[0x00008];/* MTT Base Address [39:32] in ICM relative to INIT_HCA.mtt_base_addr */
+ pseudo_bit_t reserved8[0x00010];
+ pseudo_bit_t log2_page_size[0x00006];
+ pseudo_bit_t reserved9[0x00002];
+/* -------------- */
+ pseudo_bit_t reserved10[0x00003];
+ pseudo_bit_t mtt_base_addr_l[0x0001d];/* MTT Base Address [31:3] in ICM relative to INIT_HCA.mtt_base_addr */
+/* -------------- */
+ pseudo_bit_t last_notified_indx[0x00018];/* Maintained by HW.
+ Valid for QUERY_CQ and HW2SW_CQ commands only. */
+ pseudo_bit_t reserved11[0x00008];
+/* -------------- */
+ pseudo_bit_t solicit_producer_indx[0x00018];/* Maintained by HW.
+ Valid for QUERY_CQ and HW2SW_CQ commands only.
+ */
+ pseudo_bit_t reserved12[0x00008];
+/* -------------- */
+ pseudo_bit_t consumer_counter[0x00018];/* Consumer counter is a 32bits counter that is incremented for each CQE pooled from the CQ.
+ */
+ pseudo_bit_t reserved13[0x00008];
+/* -------------- */
+ pseudo_bit_t producer_counter[0x00018];/* Producer counter is a 32bits counter that is incremented for each CQE that is written by the HW to the CQ.
+ CQ overrun is reported if Producer_counter + 1 equals to Consumer_counter and a CQE needs to be added..
+ Maintained by HW (valid for the QUERY_CQ and HW2SW_CQ commands only) */
+ pseudo_bit_t reserved14[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved15[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved16[0x00020];
+/* -------------- */
+ pseudo_bit_t db_record_addr_h[0x00020];/* CQ DB Record physical address [63:32] */
+/* -------------- */
+ pseudo_bit_t reserved17[0x00003];
+ pseudo_bit_t db_record_addr_l[0x0001d];/* CQ DB Record physical address [31:3] */
+/* -------------- */
+};
+
+/* GPIO_event_data #### michal - gdror fixed */
+
+struct hermonprm_gpio_event_data_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00060];
+/* -------------- */
+ pseudo_bit_t gpio_event_hi[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */
+/* -------------- */
+ pseudo_bit_t gpio_event_lo[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+};
+
+/* Event_data Field - QP/EE Events #### michal - doesn't match PRM */
+
+struct hermonprm_qp_ee_event_st { /* Little Endian */
+ pseudo_bit_t qpn_een[0x00018]; /* QP/EE/SRQ number event is reported for ###michal - field changed to QP number */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved2[0x0001c];
+ pseudo_bit_t e_q[0x00001]; /* If set - EEN if cleared - QP in the QPN/EEN field
+ Not valid on SRQ events ###michal - field replaced with RESERVED */
+ pseudo_bit_t reserved3[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00060];
+/* -------------- */
+};
+
+/* InfiniHost-III-EX Type0 Configuration Header ####michal - doesn't match PRM (new fields added, see below) */
+
+struct hermonprm_mt25208_type0_st { /* Little Endian */
+ pseudo_bit_t vendor_id[0x00010]; /* Hardwired to 0x15B3 */
+ pseudo_bit_t device_id[0x00010]; /* 25208 (decimal) - InfiniHost-III compatible mode
+ 25408 (decimal) - InfiniHost-III EX mode (the mode described in this manual)
+ 25209 (decimal) - Flash burner mode - see Flash burning application note for further details on this mode
+ */
+/* -------------- */
+ pseudo_bit_t command[0x00010]; /* PCI Command Register */
+ pseudo_bit_t status[0x00010]; /* PCI Status Register */
+/* -------------- */
+ pseudo_bit_t revision_id[0x00008];
+ pseudo_bit_t class_code_hca_class_code[0x00018];
+/* -------------- */
+ pseudo_bit_t cache_line_size[0x00008];/* Cache Line Size */
+ pseudo_bit_t latency_timer[0x00008];
+ pseudo_bit_t header_type[0x00008]; /* hardwired to zero */
+ pseudo_bit_t bist[0x00008];
+/* -------------- */
+ pseudo_bit_t bar0_ctrl[0x00004]; /* hard-wired to 0100 */
+ pseudo_bit_t reserved0[0x00010];
+ pseudo_bit_t bar0_l[0x0000c]; /* Lower bits of BAR0 (Device Configuration Space) */
+/* -------------- */
+ pseudo_bit_t bar0_h[0x00020]; /* Upper 32 bits of BAR0 (Device Configuration Space) */
+/* -------------- */
+ pseudo_bit_t bar1_ctrl[0x00004]; /* Hardwired to 1100 */
+ pseudo_bit_t reserved1[0x00010];
+ pseudo_bit_t bar1_l[0x0000c]; /* Lower bits of BAR1 (User Access Region - UAR - space) */
+/* -------------- */
+ pseudo_bit_t bar1_h[0x00020]; /* upper 32 bits of BAR1 (User Access Region - UAR - space) */
+/* -------------- */
+ pseudo_bit_t bar2_ctrl[0x00004]; /* Hardwired to 1100 */
+ pseudo_bit_t reserved2[0x00010];
+ pseudo_bit_t bar2_l[0x0000c]; /* Lower bits of BAR2 - Local Attached Memory if present and enabled. Else zeroed. */
+/* -------------- */
+ pseudo_bit_t bar2_h[0x00020]; /* Upper 32 bits of BAR2 - Local Attached Memory if present and enabled. Else zeroed. */
+/* -------------- */
+ pseudo_bit_t cardbus_cis_pointer[0x00020];
+/* -------------- */
+ pseudo_bit_t subsystem_vendor_id[0x00010];/* Specified by the device NVMEM configuration */
+ pseudo_bit_t subsystem_id[0x00010]; /* Specified by the device NVMEM configuration */
+/* -------------- */
+ pseudo_bit_t expansion_rom_enable[0x00001];/* Expansion ROM Enable. Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */
+ pseudo_bit_t reserved3[0x0000a];
+ pseudo_bit_t expansion_rom_base_address[0x00015];/* Expansion ROM Base Address (upper 21 bit). Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */
+/* -------------- */
+ pseudo_bit_t capabilities_pointer[0x00008];/* Specified by the device NVMEM configuration */
+ pseudo_bit_t reserved4[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t interrupt_line[0x00008];
+ pseudo_bit_t interrupt_pin[0x00008];
+ pseudo_bit_t min_gnt[0x00008];
+ pseudo_bit_t max_latency[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved6[0x00100];
+/* -------------- */
+ pseudo_bit_t msi_cap_id[0x00008];
+ pseudo_bit_t msi_next_cap_ptr[0x00008];
+ pseudo_bit_t msi_en[0x00001];
+ pseudo_bit_t multiple_msg_cap[0x00003];
+ pseudo_bit_t multiple_msg_en[0x00003];
+ pseudo_bit_t cap_64_bit_addr[0x00001];
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t msg_addr_l[0x00020];
+/* -------------- */
+ pseudo_bit_t msg_addr_h[0x00020];
+/* -------------- */
+ pseudo_bit_t msg_data[0x00010];
+ pseudo_bit_t reserved8[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved9[0x00080];
+/* -------------- */
+ pseudo_bit_t pm_cap_id[0x00008]; /* Power management capability ID - 01h */
+ pseudo_bit_t pm_next_cap_ptr[0x00008];
+ pseudo_bit_t pm_cap[0x00010]; /* [2:0] Version - 02h
+ [3] PME clock - 0h
+ [4] RsvP
+ [5] Device specific initialization - 0h
+ [8:6] AUX current - 0h
+ [9] D1 support - 0h
+ [10] D2 support - 0h
+ [15:11] PME support - 0h */
+/* -------------- */
+ pseudo_bit_t pm_status_control[0x00010];/* [14:13] - Data scale - 0h */
+ pseudo_bit_t pm_control_status_brdg_ext[0x00008];
+ pseudo_bit_t data[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved10[0x00040];
+/* -------------- */
+ pseudo_bit_t vpd_cap_id[0x00008]; /* 03h */
+ pseudo_bit_t vpd_next_cap_id[0x00008];
+ pseudo_bit_t vpd_address[0x0000f];
+ pseudo_bit_t f[0x00001];
+/* -------------- */
+ pseudo_bit_t vpd_data[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved11[0x00040];
+/* -------------- */
+ pseudo_bit_t pciex_cap_id[0x00008]; /* PCI-Express capability ID - 10h */
+ pseudo_bit_t pciex_next_cap_ptr[0x00008];
+ pseudo_bit_t pciex_cap[0x00010]; /* [3:0] Capability version - 1h
+ [7:4] Device/Port Type - 0h
+ [8] Slot implemented - 0h
+ [13:9] Interrupt message number
+ */
+/* -------------- */
+ pseudo_bit_t device_cap[0x00020]; /* [2:0] Max_Payload_Size supported - 2h
+ [4:3] Phantom Function supported - 0h
+ [5] Extended Tag Filed supported - 0h
+ [8:6] Endpoint L0s Acceptable Latency - TBD
+ [11:9] Endpoint L1 Acceptable Latency - TBD
+ [12] Attention Button Present - configured through InfiniBurn
+ [13] Attention Indicator Present - configured through InfiniBurn
+ [14] Power Indicator Present - configured through InfiniBurn
+ [25:18] Captured Slot Power Limit Value
+ [27:26] Captured Slot Power Limit Scale */
+/* -------------- */
+ pseudo_bit_t device_control[0x00010];
+ pseudo_bit_t device_status[0x00010];
+/* -------------- */
+ pseudo_bit_t link_cap[0x00020]; /* [3:0] Maximum Link Speed - 1h
+ [9:4] Maximum Link Width - 8h
+ [11:10] Active State Power Management Support - 3h
+ [14:12] L0s Exit Latency - TBD
+ [17:15] L1 Exit Latency - TBD
+ [31:24] Port Number - 0h */
+/* -------------- */
+ pseudo_bit_t link_control[0x00010];
+ pseudo_bit_t link_status[0x00010]; /* [3:0] Link Speed - 1h
+ [9:4] Negotiated Link Width
+ [12] Slot clock configuration - 1h */
+/* -------------- */
+ pseudo_bit_t reserved12[0x00260];
+/* -------------- */
+ pseudo_bit_t advanced_error_reporting_cap_id[0x00010];/* 0001h. */
+ pseudo_bit_t capability_version[0x00004];/* 1h */
+ pseudo_bit_t next_capability_offset[0x0000c];/* 0h */
+/* -------------- */
+ pseudo_bit_t uncorrectable_error_status_register[0x00020];/* 0 Training Error Status
+ 4 Data Link Protocol Error Status
+ 12 Poisoned TLP Status
+ 13 Flow Control Protocol Error Status
+ 14 Completion Timeout Status
+ 15 Completer Abort Status
+ 16 Unexpected Completion Status
+ 17 Receiver Overflow Status
+ 18 Malformed TLP Status
+ 19 ECRC Error Status
+ 20 Unsupported Request Error Status */
+/* -------------- */
+ pseudo_bit_t uncorrectable_error_mask_register[0x00020];/* 0 Training Error Mask
+ 4 Data Link Protocol Error Mask
+ 12 Poisoned TLP Mask
+ 13 Flow Control Protocol Error Mask
+ 14 Completion Timeout Mask
+ 15 Completer Abort Mask
+ 16 Unexpected Completion Mask
+ 17 Receiver Overflow Mask
+ 18 Malformed TLP Mask
+ 19 ECRC Error Mask
+ 20 Unsupported Request Error Mask */
+/* -------------- */
+ pseudo_bit_t uncorrectable_severity_mask_register[0x00020];/* 0 Training Error Severity
+ 4 Data Link Protocol Error Severity
+ 12 Poisoned TLP Severity
+ 13 Flow Control Protocol Error Severity
+ 14 Completion Timeout Severity
+ 15 Completer Abort Severity
+ 16 Unexpected Completion Severity
+ 17 Receiver Overflow Severity
+ 18 Malformed TLP Severity
+ 19 ECRC Error Severity
+ 20 Unsupported Request Error Severity */
+/* -------------- */
+ pseudo_bit_t correctable_error_status_register[0x00020];/* 0 Receiver Error Status
+ 6 Bad TLP Status
+ 7 Bad DLLP Status
+ 8 REPLAY_NUM Rollover Status
+ 12 Replay Timer Timeout Status */
+/* -------------- */
+ pseudo_bit_t correctable_error_mask_register[0x00020];/* 0 Receiver Error Mask
+ 6 Bad TLP Mask
+ 7 Bad DLLP Mask
+ 8 REPLAY_NUM Rollover Mask
+ 12 Replay Timer Timeout Mask */
+/* -------------- */
+ pseudo_bit_t advance_error_capabilities_and_control_register[0x00020];
+/* -------------- */
+ struct hermonprm_header_log_register_st header_log_register;
+/* -------------- */
+ pseudo_bit_t reserved13[0x006a0];
+/* -------------- */
+};
+
+/* Event Data Field - Performance Monitor */
+
+struct hermonprm_performance_monitor_event_st { /* Little Endian */
+ struct hermonprm_performance_monitors_st performance_monitor_snapshot;/* Performance monitor snapshot */
+/* -------------- */
+ pseudo_bit_t monitor_number[0x00008];/* 0x01 - SQPC
+ 0x02 - RQPC
+ 0x03 - CQC
+ 0x04 - Rkey
+ 0x05 - TLB
+ 0x06 - port0
+ 0x07 - port1 */
+ pseudo_bit_t reserved0[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+};
+
+/* Event_data Field - Page Faults */
+
+struct hermonprm_page_fault_event_data_st { /* Little Endian */
+ pseudo_bit_t va_h[0x00020]; /* Virtual Address[63:32] this page fault is reported on */
+/* -------------- */
+ pseudo_bit_t va_l[0x00020]; /* Virtual Address[63:32] this page fault is reported on */
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* Memory Key this page fault is reported on */
+/* -------------- */
+ pseudo_bit_t qp[0x00018]; /* QP this page fault is reported on */
+ pseudo_bit_t reserved0[0x00003];
+ pseudo_bit_t a[0x00001]; /* If set the memory access that caused the page fault was atomic */
+ pseudo_bit_t lw[0x00001]; /* If set the memory access that caused the page fault was local write */
+ pseudo_bit_t lr[0x00001]; /* If set the memory access that caused the page fault was local read */
+ pseudo_bit_t rw[0x00001]; /* If set the memory access that caused the page fault was remote write */
+ pseudo_bit_t rr[0x00001]; /* If set the memory access that caused the page fault was remote read */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* PD this page fault is reported on */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t prefetch_len[0x00020]; /* Indicates how many subsequent pages in the same memory region/window will be accessed by the following transaction after this page fault is resolved. measured in bytes. SW can use this information in order to page-in the subsequent pages if they are not present. */
+/* -------------- */
+};
+
+/* WQE segments format */
+
+struct hermonprm_wqe_segment_st { /* Little Endian */
+ struct hermonprm_send_wqe_segment_st send_wqe_segment;/* Send WQE segment format */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00280];
+/* -------------- */
+ struct hermonprm_wqe_segment_ctrl_mlx_st mlx_wqe_segment_ctrl;/* MLX WQE segment format */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00100];
+/* -------------- */
+ pseudo_bit_t recv_wqe_segment_ctrl[4][0x00020];/* Receive segment format */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00080];
+/* -------------- */
+};
+
+/* Event_data Field - Port State Change #### michal - match PRM */
+
+struct hermonprm_port_state_change_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t reserved1[0x0001c];
+ pseudo_bit_t p[0x00002]; /* Port number (1 or 2) */
+ pseudo_bit_t reserved2[0x00002];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+};
+
+/* Event_data Field - Completion Queue Error #### michal - match PRM */
+
+struct hermonprm_completion_queue_error_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t syndrome[0x00008]; /* Error syndrome
+ 0x01 - CQ overrun
+ 0x02 - CQ access violation error */
+ pseudo_bit_t reserved2[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+};
+
+/* Event_data Field - Completion Event #### michal - match PRM */
+
+struct hermonprm_completion_event_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x000a0];
+/* -------------- */
+};
+
+/* Event Queue Entry #### michal - match to PRM */
+
+struct hermonprm_event_queue_entry_st { /* Little Endian */
+ pseudo_bit_t event_sub_type[0x00008];/* Event Sub Type.
+ Defined for events which have sub types, zero elsewhere. */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t event_type[0x00008]; /* Event Type */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t event_data[6][0x00020];/* Delivers auxilary data to handle event. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00007];
+ pseudo_bit_t owner[0x00001]; /* Owner of the entry
+ 0 SW
+ 1 HW */
+ pseudo_bit_t reserved3[0x00018];
+/* -------------- */
+};
+
+/* QP/EE State Transitions Command Parameters ###michal - doesn't match PRM (field name changed) */
+
+struct hermonprm_qp_ee_state_transitions_st { /* Little Endian */
+ pseudo_bit_t opt_param_mask[0x00020];/* This field defines which optional parameters are passed. Each bit specifies whether optional parameter is passed (set) or not (cleared). The optparammask is defined for each QP/EE command. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ struct hermonprm_queue_pair_ee_context_entry_st qpc_eec_data;/* QPC/EEC data ###michal - field has replaced with "qpc_data" (size .1948) */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00800];
+/* -------------- */
+};
+
+/* Completion Queue Entry Format #### michal - fixed by gdror */
+
+struct hermonprm_completion_queue_entry_st { /* Little Endian */
+ pseudo_bit_t qpn[0x00018]; /* Indicates the QP for which completion is being reported */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t d2s[0x00001]; /* Duplicate to Sniffer. This bit is set if both Send and Receive queues are subject for sniffer queue. The HW delivers
+ packet only to send-associated sniffer receive queue. */
+ pseudo_bit_t fcrc_sd[0x00001]; /* FCRC: If set, FC CRC is correct in FC frame encapsulated in payload. Valid for Raw Frame FC receive queue only.
+ SD: CQ associated with Sniffer receive queue. If set, packets were skipped due to lack of receive buffers on the Sniffer receive queue */
+ pseudo_bit_t fl[0x00001]; /* Force Loopback Valid for responder RawEth and UD only. */
+ pseudo_bit_t vlan[0x00002]; /* Valid for RawEth and UD over Ethernet only. Applicable for RawEth and UD over Ethernet Receive queue
+ 00 - No VLAN header was present in the packet
+ 01 - C-VLAN (802.1q) Header was present in the frame.
+ 10 - S-VLAN (802.1ad) Header was present in the frame. */
+ pseudo_bit_t dife[0x00001]; /* DIF Error */
+/* -------------- */
+ pseudo_bit_t immediate_rssvalue_invalidatekey[0x00020];/* For a responder CQE, if completed WQE Opcode is Send With Immediate or Write With Immediate, this field contains immediate field of the received message.
+ For a responder CQE, if completed WQE Opcode is Send With Invalidate, this field contains the R_key that was invalidated.
+ For a responder CQE of a GSI packet this filed contains the Pkey Index of the packet.
+ For IPoIB (UD) and RawEth CQEs this field contains the RSS hash function value.
+ Otherwise, this field is reserved. */
+/* -------------- */
+ pseudo_bit_t srq_rqpn[0x00018]; /* For Responder UD QPs, Remote (source) QP number.
+ For Responder SRC QPs, SRQ number.
+ Otherwise, this field is reserved. */
+ pseudo_bit_t ml_path_mac_index[0x00007];/* For responder UD over IB CQE: These are the lower LMC bits of the DLID in an incoming UD packet, higher bits of this field, that are not part of the LMC bits are zeroed by HW. Invalid if incoming message DLID is the permissive LID or incoming message is multicast.
+ For responder UD over Ethernet and RawEth CQEs: Index of the MAC Table entry that the packet DMAC was matched against.
+ Otherwise, this field is reserved. */
+ pseudo_bit_t g[0x00001]; /* For responder UD over IB CQE this bit indicates the presence of a GRH
+ For responder UD over Ethernet CQE this bit is set if IPv6 L3 header was present in the packet, this bit is cleared if IPv4 L3 Header was present in the packet.
+ Otherwise, this field is reserved. */
+/* -------------- */
+ pseudo_bit_t slid_smac47_32[0x00010];/* For responder UD over IB CQE it is the source LID of the packet.
+ For responder UD over Ethernet and RawEth CQEs it is the source-MAC[47:32] of the packet.
+ Otherwise, this field is reserved. */
+ pseudo_bit_t vid[0x0000c]; /* Frame VID, valid for Responder Raw Ethernet and UD over Ethernet QP. Otherwise, this field is reserved. */
+ pseudo_bit_t sl[0x00004]; /* For responder UD over IB - the Service Level of the packet.
+ For responder UD over Ethernet and RawEth - it is VLAN-header[15:12]
+ Otherwise, this field is reserved. */
+/* -------------- */
+ pseudo_bit_t smac31_0_rawether_ipoib_status[0x00020];/* For responder UD over Ethernet - source MAC[31:0] of the packet.
+ For responder RawEth and UD over IB - RawEth-IPoIB status {3 reserved, ipok,udp,tcp,ipv4opt,ipv6,ipv4vf,ipv4,rht(6),ipv6extmask(6),reserved(2),l2am,reserved(2),bfcs,reserved(2),enc}
+ Otherwise, this field is reserved. */
+/* -------------- */
+ pseudo_bit_t byte_cnt[0x00020]; /* Byte count of data transferred. Applicable for RDMA-read, Atomic and all receive operations. completions.
+ For Receive Queue that is subject for headers. separation, byte_cnt[31:24] specify number of bytes scattered to the first scatter entry (headers. length). Byte_cnt[23:0] specify total byte count received (including headers). */
+/* -------------- */
+ pseudo_bit_t checksum[0x00010]; /* Valid for RawEth and IPoIB only. */
+ pseudo_bit_t wqe_counter[0x00010];
+/* -------------- */
+ pseudo_bit_t opcode[0x00005]; /* Send completions - same encoding as WQE.
+ Error coding is 0x1F
+ Receive:
+ 0x0 - RDMA-Write with Immediate
+ 0x1 - Send
+ 0x2 - Send with Immediate
+ 0x3 - Send & Invalidate
+ */
+ pseudo_bit_t is[0x00001]; /* inline scatter */
+ pseudo_bit_t s_r[0x00001]; /* send 1 / receive 0 */
+ pseudo_bit_t owner[0x00001]; /* HW Flips this bit for every CQ warp around. Initialized to Zero. */
+ pseudo_bit_t reserved1[0x00010];
+ pseudo_bit_t reserved2[0x00008];
+/* -------------- */
+};
+
+/* */
+
+struct hermonprm_mcg_qps_st { /* Little Endian */
+ struct hermonprm_mcg_qp_dw_st dw[128];
+/* -------------- */
+};
+
+/* */
+
+struct hermonprm_mcg_hdr_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00006];
+ pseudo_bit_t next_mcg[0x0001a];
+/* -------------- */
+ pseudo_bit_t members_count[0x00018];
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t gid3[0x00020];
+/* -------------- */
+ pseudo_bit_t gid2[0x00020];
+/* -------------- */
+ pseudo_bit_t gid1[0x00020];
+/* -------------- */
+ pseudo_bit_t gid0[0x00020];
+/* -------------- */
+};
+
+/* */
+
+struct hermonprm_sched_queue_context_st { /* Little Endian */
+ pseudo_bit_t policy[0x00003]; /* Schedule Queue Policy - 0 - LLSQ, 1 - GBSQ, 2 - BESQ */
+ pseudo_bit_t vl15[0x00001];
+ pseudo_bit_t sl[0x00004]; /* SL this Schedule Queue is associated with (if vl15 bit is 0) */
+ pseudo_bit_t port[0x00002]; /* Port this Schedule Queue is associated with */
+ pseudo_bit_t reserved0[0x00006];
+ pseudo_bit_t weight[0x00010]; /* Weight of this SchQ */
+/* -------------- */
+};
+
+/* */
+
+struct hermonprm_ecc_detect_event_data_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t cause_lsb[0x00001];
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t cause_msb[0x00001];
+ pseudo_bit_t reserved2[0x00002];
+ pseudo_bit_t err_rmw[0x00001];
+ pseudo_bit_t err_src_id[0x00003];
+ pseudo_bit_t err_da[0x00002];
+ pseudo_bit_t err_ba[0x00002];
+ pseudo_bit_t reserved3[0x00011];
+ pseudo_bit_t overflow[0x00001];
+/* -------------- */
+ pseudo_bit_t err_ra[0x00010];
+ pseudo_bit_t err_ca[0x00010];
+/* -------------- */
+};
+
+/* Event_data Field - ECC Detection Event */
+
+struct hermonprm_scrubbing_event_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t cause_lsb[0x00001]; /* data integrity error cause:
+ single ECC error in the 64bit lsb data, on the rise edge of the clock */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t cause_msb[0x00001]; /* data integrity error cause:
+ single ECC error in the 64bit msb data, on the fall edge of the clock */
+ pseudo_bit_t reserved2[0x00002];
+ pseudo_bit_t err_rmw[0x00001]; /* transaction type:
+ 0 - read
+ 1 - read/modify/write */
+ pseudo_bit_t err_src_id[0x00003]; /* source of the transaction: 0x4 - PCI, other - internal or IB */
+ pseudo_bit_t err_da[0x00002]; /* Error DIMM address */
+ pseudo_bit_t err_ba[0x00002]; /* Error bank address */
+ pseudo_bit_t reserved3[0x00011];
+ pseudo_bit_t overflow[0x00001]; /* Fatal: ECC error FIFO overflow - ECC errors were detected, which may or may not have been corrected by InfiniHost-III-EX */
+/* -------------- */
+ pseudo_bit_t err_ra[0x00010]; /* Error row address */
+ pseudo_bit_t err_ca[0x00010]; /* Error column address */
+/* -------------- */
+};
+
+/* */
+
+struct hermonprm_eq_cmd_doorbell_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* 0 */
+
+struct hermonprm_hermon_prm_st { /* Little Endian */
+ struct hermonprm_completion_queue_entry_st completion_queue_entry;/* Completion Queue Entry Format */
+/* -------------- */
+ pseudo_bit_t reserved0[0x7ff00];
+/* -------------- */
+ struct hermonprm_qp_ee_state_transitions_st qp_ee_state_transitions;/* QP/EE State Transitions Command Parameters */
+/* -------------- */
+ pseudo_bit_t reserved1[0x7f000];
+/* -------------- */
+ struct hermonprm_event_queue_entry_st event_queue_entry;/* Event Queue Entry */
+/* -------------- */
+ pseudo_bit_t reserved2[0x7ff00];
+/* -------------- */
+ struct hermonprm_completion_event_st completion_event;/* Event_data Field - Completion Event */
+/* -------------- */
+ pseudo_bit_t reserved3[0x7ff40];
+/* -------------- */
+ struct hermonprm_completion_queue_error_st completion_queue_error;/* Event_data Field - Completion Queue Error */
+/* -------------- */
+ pseudo_bit_t reserved4[0x7ff40];
+/* -------------- */
+ struct hermonprm_port_state_change_st port_state_change;/* Event_data Field - Port State Change */
+/* -------------- */
+ pseudo_bit_t reserved5[0x7ff40];
+/* -------------- */
+ struct hermonprm_wqe_segment_st wqe_segment;/* WQE segments format */
+/* -------------- */
+ pseudo_bit_t reserved6[0x7f000];
+/* -------------- */
+ struct hermonprm_page_fault_event_data_st page_fault_event_data;/* Event_data Field - Page Faults */
+/* -------------- */
+ pseudo_bit_t reserved7[0x7ff40];
+/* -------------- */
+ struct hermonprm_performance_monitor_event_st performance_monitor_event;/* Event Data Field - Performance Monitor */
+/* -------------- */
+ pseudo_bit_t reserved8[0xfff20];
+/* -------------- */
+ struct hermonprm_mt25208_type0_st mt25208_type0;/* InfiniHost-III-EX Type0 Configuration Header */
+/* -------------- */
+ pseudo_bit_t reserved9[0x7f000];
+/* -------------- */
+ struct hermonprm_qp_ee_event_st qp_ee_event;/* Event_data Field - QP/EE Events */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00040];
+/* -------------- */
+ struct hermonprm_gpio_event_data_st gpio_event_data;
+/* -------------- */
+ pseudo_bit_t reserved11[0x7fe40];
+/* -------------- */
+ struct hermonprm_ud_address_vector_st ud_address_vector;/* UD Address Vector */
+/* -------------- */
+ pseudo_bit_t reserved12[0x7ff00];
+/* -------------- */
+ struct hermonprm_queue_pair_ee_context_entry_st queue_pair_ee_context_entry;/* QP and EE Context Entry */
+/* -------------- */
+ pseudo_bit_t reserved13[0x7f840];
+/* -------------- */
+ struct hermonprm_address_path_st address_path;/* Address Path */
+/* -------------- */
+ pseudo_bit_t reserved14[0x7fea0];
+/* -------------- */
+ struct hermonprm_completion_queue_context_st completion_queue_context;/* Completion Queue Context Table Entry */
+/* -------------- */
+ pseudo_bit_t reserved15[0x7fe00];
+/* -------------- */
+ struct hermonprm_mpt_st mpt; /* Memory Protection Table (MPT) Entry */
+/* -------------- */
+ pseudo_bit_t reserved16[0x7fe00];
+/* -------------- */
+ struct hermonprm_mtt_st mtt; /* Memory Translation Table (MTT) Entry */
+/* -------------- */
+ pseudo_bit_t reserved17[0x7ffc0];
+/* -------------- */
+ struct hermonprm_eqc_st eqc; /* Event Queue Context Table Entry */
+/* -------------- */
+ pseudo_bit_t reserved18[0x7fe00];
+/* -------------- */
+ struct hermonprm_performance_monitors_st performance_monitors;/* Performance Monitors */
+/* -------------- */
+ pseudo_bit_t reserved19[0x7ff80];
+/* -------------- */
+ struct hermonprm_hca_command_register_st hca_command_register;/* HCA Command Register (HCR) */
+/* -------------- */
+ pseudo_bit_t reserved20[0xfff20];
+/* -------------- */
+ struct hermonprm_init_hca_st init_hca;/* INIT_HCA & QUERY_HCA Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved21[0x7f000];
+/* -------------- */
+ struct hermonprm_qpcbaseaddr_st qpcbaseaddr;/* QPC/EEC/CQC/EQC/RDB Parameters */
+/* -------------- */
+ pseudo_bit_t reserved22[0x7fc00];
+/* -------------- */
+ struct hermonprm_udavtable_memory_parameters_st udavtable_memory_parameters;/* Memory Access Parameters for UD Address Vector Table */
+/* -------------- */
+ pseudo_bit_t reserved23[0x7ffc0];
+/* -------------- */
+ struct hermonprm_multicastparam_st multicastparam;/* Multicast Support Parameters */
+/* -------------- */
+ pseudo_bit_t reserved24[0x7ff00];
+/* -------------- */
+ struct hermonprm_tptparams_st tptparams;/* Translation and Protection Tables Parameters */
+/* -------------- */
+ pseudo_bit_t reserved25[0x7ff00];
+/* -------------- */
+ pseudo_bit_t reserved26[0x00800];
+/* -------------- */
+ pseudo_bit_t reserved27[0x00100];
+/* -------------- */
+ pseudo_bit_t reserved28[0x7f700];
+/* -------------- */
+ pseudo_bit_t reserved29[0x00100];
+/* -------------- */
+ pseudo_bit_t reserved30[0x7ff00];
+/* -------------- */
+ struct hermonprm_query_fw_st query_fw;/* QUERY_FW Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved31[0x7f800];
+/* -------------- */
+ struct hermonprm_query_adapter_st query_adapter;/* QUERY_ADAPTER Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved32[0x7f800];
+/* -------------- */
+ struct hermonprm_query_dev_cap_st query_dev_cap;/* Query Device Limitations */
+/* -------------- */
+ pseudo_bit_t reserved33[0x7f800];
+/* -------------- */
+ struct hermonprm_uar_params_st uar_params;/* UAR Parameters */
+/* -------------- */
+ pseudo_bit_t reserved34[0x7ff00];
+/* -------------- */
+ struct hermonprm_init_port_st init_port;/* INIT_PORT Parameters */
+/* -------------- */
+ pseudo_bit_t reserved35[0x7f800];
+/* -------------- */
+ struct hermonprm_mgm_entry_st mgm_entry;/* Multicast Group Member */
+/* -------------- */
+ pseudo_bit_t reserved36[0x7fe00];
+/* -------------- */
+ struct hermonprm_set_ib_st set_ib; /* SET_IB Parameters */
+/* -------------- */
+ pseudo_bit_t reserved37[0x7fe00];
+/* -------------- */
+ struct hermonprm_rd_send_doorbell_st rd_send_doorbell;/* RD-send doorbell */
+/* -------------- */
+ pseudo_bit_t reserved38[0x7ff80];
+/* -------------- */
+ struct hermonprm_send_doorbell_st send_doorbell;/* Send doorbell */
+/* -------------- */
+ pseudo_bit_t reserved39[0x7ffc0];
+/* -------------- */
+ struct hermonprm_receive_doorbell_st receive_doorbell;/* Receive doorbell */
+/* -------------- */
+ pseudo_bit_t reserved40[0x7ffc0];
+/* -------------- */
+ struct hermonprm_cq_cmd_doorbell_st cq_cmd_doorbell;/* CQ Doorbell */
+/* -------------- */
+ pseudo_bit_t reserved41[0xfffc0];
+/* -------------- */
+ struct hermonprm_uar_st uar; /* User Access Region */
+/* -------------- */
+ pseudo_bit_t reserved42[0x7c000];
+/* -------------- */
+ struct hermonprm_mgmqp_st mgmqp; /* Multicast Group Member QP */
+/* -------------- */
+ pseudo_bit_t reserved43[0x7ffe0];
+/* -------------- */
+ struct hermonprm_query_debug_msg_st query_debug_msg;/* Query Debug Message */
+/* -------------- */
+ pseudo_bit_t reserved44[0x7f800];
+/* -------------- */
+ struct hermonprm_mad_ifc_st mad_ifc; /* MAD_IFC Input Mailbox */
+/* -------------- */
+ pseudo_bit_t reserved45[0x00900];
+/* -------------- */
+ struct hermonprm_mad_ifc_input_modifier_st mad_ifc_input_modifier;/* MAD_IFC Input Modifier */
+/* -------------- */
+ pseudo_bit_t reserved46[0x7e6e0];
+/* -------------- */
+ struct hermonprm_resize_cq_st resize_cq;/* Resize CQ Input Mailbox */
+/* -------------- */
+ pseudo_bit_t reserved47[0x7fe00];
+/* -------------- */
+ struct hermonprm_completion_with_error_st completion_with_error;/* Completion with Error CQE */
+/* -------------- */
+ pseudo_bit_t reserved48[0x7ff00];
+/* -------------- */
+ struct hermonprm_hcr_completion_event_st hcr_completion_event;/* Event_data Field - HCR Completion Event */
+/* -------------- */
+ pseudo_bit_t reserved49[0x7ff40];
+/* -------------- */
+ struct hermonprm_transport_and_ci_error_counters_st transport_and_ci_error_counters;/* Transport and CI Error Counters */
+/* -------------- */
+ pseudo_bit_t reserved50[0x7f000];
+/* -------------- */
+ struct hermonprm_performance_counters_st performance_counters;/* Performance Counters */
+/* -------------- */
+ pseudo_bit_t reserved51[0x9ff800];
+/* -------------- */
+ struct hermonprm_fast_registration_segment_st fast_registration_segment;/* Fast Registration Segment */
+/* -------------- */
+ pseudo_bit_t reserved52[0x7ff00];
+/* -------------- */
+ struct hermonprm_pbl_st pbl; /* Physical Buffer List */
+/* -------------- */
+ pseudo_bit_t reserved53[0x7ff00];
+/* -------------- */
+ struct hermonprm_srq_context_st srq_context;/* SRQ Context */
+/* -------------- */
+ pseudo_bit_t reserved54[0x7fe80];
+/* -------------- */
+ struct hermonprm_mod_stat_cfg_st mod_stat_cfg;/* MOD_STAT_CFG */
+/* -------------- */
+ pseudo_bit_t reserved55[0x7f800];
+/* -------------- */
+ struct hermonprm_virtual_physical_mapping_st virtual_physical_mapping;/* Virtual and Physical Mapping */
+/* -------------- */
+ pseudo_bit_t reserved56[0x7ff80];
+/* -------------- */
+ struct hermonprm_cq_ci_db_record_st cq_ci_db_record;/* CQ_CI_DB_Record */
+/* -------------- */
+ pseudo_bit_t reserved57[0x7ffc0];
+/* -------------- */
+ struct hermonprm_cq_arm_db_record_st cq_arm_db_record;/* CQ_ARM_DB_Record */
+/* -------------- */
+ pseudo_bit_t reserved58[0x7ffc0];
+/* -------------- */
+ struct hermonprm_qp_db_record_st qp_db_record;/* QP_DB_Record */
+/* -------------- */
+ pseudo_bit_t reserved59[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved60[0x1fffc0];
+/* -------------- */
+ struct hermonprm_configuration_registers_st configuration_registers;/* InfiniHost III EX Configuration Registers */
+/* -------------- */
+ struct hermonprm_eq_set_ci_table_st eq_set_ci_table;/* EQ Set CI DBs Table */
+/* -------------- */
+ pseudo_bit_t reserved61[0x01000];
+/* -------------- */
+ pseudo_bit_t reserved62[0x00040];
+/* -------------- */
+ pseudo_bit_t reserved63[0x00fc0];
+/* -------------- */
+ struct hermonprm_clr_int_st clr_int; /* Clear Interrupt Register */
+/* -------------- */
+ pseudo_bit_t reserved64[0xffcfc0];
+/* -------------- */
+};
+#endif /* H_prefix_hermonprm_bits_fixnames_MT25408_PRM_csp_H */
diff --git a/gpxe/src/drivers/infiniband/arbel.c b/gpxe/src/drivers/infiniband/arbel.c
new file mode 100644
index 00000000..462638ea
--- /dev/null
+++ b/gpxe/src/drivers/infiniband/arbel.c
@@ -0,0 +1,1979 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * Based in part upon the original driver by Mellanox Technologies
+ * Ltd. Portions may be Copyright (c) Mellanox Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/pci.h>
+#include <gpxe/malloc.h>
+#include <gpxe/umalloc.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/infiniband.h>
+#include "arbel.h"
+
+/**
+ * @file
+ *
+ * Mellanox Arbel Infiniband HCA
+ *
+ */
+
+/***************************************************************************
+ *
+ * Queue number allocation
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Allocate queue number
+ *
+ * @v q_inuse Queue usage bitmask
+ * @v max_inuse Maximum number of in-use queues
+ * @ret qn_offset Free queue number offset, or negative error
+ */
+static int arbel_alloc_qn_offset ( arbel_bitmask_t *q_inuse,
+ unsigned int max_inuse ) {
+ unsigned int qn_offset = 0;
+ arbel_bitmask_t mask = 1;
+
+ while ( qn_offset < max_inuse ) {
+ if ( ( mask & *q_inuse ) == 0 ) {
+ *q_inuse |= mask;
+ return qn_offset;
+ }
+ qn_offset++;
+ mask <<= 1;
+ if ( ! mask ) {
+ mask = 1;
+ q_inuse++;
+ }
+ }
+ return -ENFILE;
+}
+
+/**
+ * Free queue number
+ *
+ * @v q_inuse Queue usage bitmask
+ * @v qn_offset Queue number offset
+ */
+static void arbel_free_qn_offset ( arbel_bitmask_t *q_inuse, int qn_offset ) {
+ arbel_bitmask_t mask;
+
+ mask = ( 1 << ( qn_offset % ( 8 * sizeof ( mask ) ) ) );
+ q_inuse += ( qn_offset / ( 8 * sizeof ( mask ) ) );
+ *q_inuse &= ~mask;
+}
+
+/***************************************************************************
+ *
+ * HCA commands
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Wait for Arbel command completion
+ *
+ * @v arbel Arbel device
+ * @ret rc Return status code
+ */
+static int arbel_cmd_wait ( struct arbel *arbel,
+ struct arbelprm_hca_command_register *hcr ) {
+ unsigned int wait;
+
+ for ( wait = ARBEL_HCR_MAX_WAIT_MS ; wait ; wait-- ) {
+ hcr->u.dwords[6] =
+ readl ( arbel->config + ARBEL_HCR_REG ( 6 ) );
+ if ( MLX_GET ( hcr, go ) == 0 )
+ return 0;
+ mdelay ( 1 );
+ }
+ return -EBUSY;
+}
+
+/**
+ * Issue HCA command
+ *
+ * @v arbel Arbel device
+ * @v command Command opcode, flags and input/output lengths
+ * @v op_mod Opcode modifier (0 if no modifier applicable)
+ * @v in Input parameters
+ * @v in_mod Input modifier (0 if no modifier applicable)
+ * @v out Output parameters
+ * @ret rc Return status code
+ */
+static int arbel_cmd ( struct arbel *arbel, unsigned long command,
+ unsigned int op_mod, const void *in,
+ unsigned int in_mod, void *out ) {
+ struct arbelprm_hca_command_register hcr;
+ unsigned int opcode = ARBEL_HCR_OPCODE ( command );
+ size_t in_len = ARBEL_HCR_IN_LEN ( command );
+ size_t out_len = ARBEL_HCR_OUT_LEN ( command );
+ void *in_buffer;
+ void *out_buffer;
+ unsigned int status;
+ unsigned int i;
+ int rc;
+
+ assert ( in_len <= ARBEL_MBOX_SIZE );
+ assert ( out_len <= ARBEL_MBOX_SIZE );
+
+ DBGC2 ( arbel, "Arbel %p command %02x in %zx%s out %zx%s\n",
+ arbel, opcode, in_len,
+ ( ( command & ARBEL_HCR_IN_MBOX ) ? "(mbox)" : "" ), out_len,
+ ( ( command & ARBEL_HCR_OUT_MBOX ) ? "(mbox)" : "" ) );
+
+ /* Check that HCR is free */
+ if ( ( rc = arbel_cmd_wait ( arbel, &hcr ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p command interface locked\n", arbel );
+ return rc;
+ }
+
+ /* Prepare HCR */
+ memset ( &hcr, 0, sizeof ( hcr ) );
+ in_buffer = &hcr.u.dwords[0];
+ if ( in_len && ( command & ARBEL_HCR_IN_MBOX ) ) {
+ in_buffer = arbel->mailbox_in;
+ MLX_FILL_1 ( &hcr, 1, in_param_l, virt_to_bus ( in_buffer ) );
+ }
+ memcpy ( in_buffer, in, in_len );
+ MLX_FILL_1 ( &hcr, 2, input_modifier, in_mod );
+ out_buffer = &hcr.u.dwords[3];
+ if ( out_len && ( command & ARBEL_HCR_OUT_MBOX ) ) {
+ out_buffer = arbel->mailbox_out;
+ MLX_FILL_1 ( &hcr, 4, out_param_l,
+ virt_to_bus ( out_buffer ) );
+ }
+ MLX_FILL_3 ( &hcr, 6,
+ opcode, opcode,
+ opcode_modifier, op_mod,
+ go, 1 );
+ DBGC2_HD ( arbel, &hcr, sizeof ( hcr ) );
+ if ( in_len ) {
+ DBGC2 ( arbel, "Input:\n" );
+ DBGC2_HD ( arbel, in, ( ( in_len < 512 ) ? in_len : 512 ) );
+ }
+
+ /* Issue command */
+ for ( i = 0 ; i < ( sizeof ( hcr ) / sizeof ( hcr.u.dwords[0] ) ) ;
+ i++ ) {
+ writel ( hcr.u.dwords[i],
+ arbel->config + ARBEL_HCR_REG ( i ) );
+ barrier();
+ }
+
+ /* Wait for command completion */
+ if ( ( rc = arbel_cmd_wait ( arbel, &hcr ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p timed out waiting for command:\n",
+ arbel );
+ DBGC_HD ( arbel, &hcr, sizeof ( hcr ) );
+ return rc;
+ }
+
+ /* Check command status */
+ status = MLX_GET ( &hcr, status );
+ if ( status != 0 ) {
+ DBGC ( arbel, "Arbel %p command failed with status %02x:\n",
+ arbel, status );
+ DBGC_HD ( arbel, &hcr, sizeof ( hcr ) );
+ return -EIO;
+ }
+
+ /* Read output parameters, if any */
+ hcr.u.dwords[3] = readl ( arbel->config + ARBEL_HCR_REG ( 3 ) );
+ hcr.u.dwords[4] = readl ( arbel->config + ARBEL_HCR_REG ( 4 ) );
+ memcpy ( out, out_buffer, out_len );
+ if ( out_len ) {
+ DBGC2 ( arbel, "Output:\n" );
+ DBGC2_HD ( arbel, out, ( ( out_len < 512 ) ? out_len : 512 ) );
+ }
+
+ return 0;
+}
+
+static inline int
+arbel_cmd_query_dev_lim ( struct arbel *arbel,
+ struct arbelprm_query_dev_lim *dev_lim ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_OUT_CMD ( ARBEL_HCR_QUERY_DEV_LIM,
+ 1, sizeof ( *dev_lim ) ),
+ 0, NULL, 0, dev_lim );
+}
+
+static inline int
+arbel_cmd_query_fw ( struct arbel *arbel, struct arbelprm_query_fw *fw ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_OUT_CMD ( ARBEL_HCR_QUERY_FW,
+ 1, sizeof ( *fw ) ),
+ 0, NULL, 0, fw );
+}
+
+static inline int
+arbel_cmd_init_hca ( struct arbel *arbel,
+ const struct arbelprm_init_hca *init_hca ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_INIT_HCA,
+ 1, sizeof ( *init_hca ) ),
+ 0, init_hca, 0, NULL );
+}
+
+static inline int
+arbel_cmd_close_hca ( struct arbel *arbel ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_VOID_CMD ( ARBEL_HCR_CLOSE_HCA ),
+ 0, NULL, 0, NULL );
+}
+
+static inline int
+arbel_cmd_init_ib ( struct arbel *arbel, unsigned int port,
+ const struct arbelprm_init_ib *init_ib ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_INIT_IB,
+ 1, sizeof ( *init_ib ) ),
+ 0, init_ib, port, NULL );
+}
+
+static inline int
+arbel_cmd_close_ib ( struct arbel *arbel, unsigned int port ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_VOID_CMD ( ARBEL_HCR_CLOSE_IB ),
+ 0, NULL, port, NULL );
+}
+
+static inline int
+arbel_cmd_sw2hw_mpt ( struct arbel *arbel, unsigned int index,
+ const struct arbelprm_mpt *mpt ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_SW2HW_MPT,
+ 1, sizeof ( *mpt ) ),
+ 0, mpt, index, NULL );
+}
+
+static inline int
+arbel_cmd_sw2hw_eq ( struct arbel *arbel, unsigned int index,
+ const struct arbelprm_eqc *eqc ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_SW2HW_EQ,
+ 1, sizeof ( *eqc ) ),
+ 0, eqc, index, NULL );
+}
+
+static inline int
+arbel_cmd_hw2sw_eq ( struct arbel *arbel, unsigned int index ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_VOID_CMD ( ARBEL_HCR_HW2SW_EQ ),
+ 1, NULL, index, NULL );
+}
+
+static inline int
+arbel_cmd_sw2hw_cq ( struct arbel *arbel, unsigned long cqn,
+ const struct arbelprm_completion_queue_context *cqctx ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_SW2HW_CQ,
+ 1, sizeof ( *cqctx ) ),
+ 0, cqctx, cqn, NULL );
+}
+
+static inline int
+arbel_cmd_hw2sw_cq ( struct arbel *arbel, unsigned long cqn,
+ struct arbelprm_completion_queue_context *cqctx) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_OUT_CMD ( ARBEL_HCR_HW2SW_CQ,
+ 1, sizeof ( *cqctx ) ),
+ 0, NULL, cqn, cqctx );
+}
+
+static inline int
+arbel_cmd_rst2init_qpee ( struct arbel *arbel, unsigned long qpn,
+ const struct arbelprm_qp_ee_state_transitions *ctx ){
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_RST2INIT_QPEE,
+ 1, sizeof ( *ctx ) ),
+ 0, ctx, qpn, NULL );
+}
+
+static inline int
+arbel_cmd_init2rtr_qpee ( struct arbel *arbel, unsigned long qpn,
+ const struct arbelprm_qp_ee_state_transitions *ctx ){
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_INIT2RTR_QPEE,
+ 1, sizeof ( *ctx ) ),
+ 0, ctx, qpn, NULL );
+}
+
+static inline int
+arbel_cmd_rtr2rts_qpee ( struct arbel *arbel, unsigned long qpn,
+ const struct arbelprm_qp_ee_state_transitions *ctx ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_RTR2RTS_QPEE,
+ 1, sizeof ( *ctx ) ),
+ 0, ctx, qpn, NULL );
+}
+
+static inline int
+arbel_cmd_2rst_qpee ( struct arbel *arbel, unsigned long qpn ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_VOID_CMD ( ARBEL_HCR_2RST_QPEE ),
+ 0x03, NULL, qpn, NULL );
+}
+
+static inline int
+arbel_cmd_mad_ifc ( struct arbel *arbel, unsigned int port,
+ union arbelprm_mad *mad ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_INOUT_CMD ( ARBEL_HCR_MAD_IFC,
+ 1, sizeof ( *mad ),
+ 1, sizeof ( *mad ) ),
+ 0x03, mad, port, mad );
+}
+
+static inline int
+arbel_cmd_read_mgm ( struct arbel *arbel, unsigned int index,
+ struct arbelprm_mgm_entry *mgm ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_OUT_CMD ( ARBEL_HCR_READ_MGM,
+ 1, sizeof ( *mgm ) ),
+ 0, NULL, index, mgm );
+}
+
+static inline int
+arbel_cmd_write_mgm ( struct arbel *arbel, unsigned int index,
+ const struct arbelprm_mgm_entry *mgm ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_WRITE_MGM,
+ 1, sizeof ( *mgm ) ),
+ 0, mgm, index, NULL );
+}
+
+static inline int
+arbel_cmd_mgid_hash ( struct arbel *arbel, const struct ib_gid *gid,
+ struct arbelprm_mgm_hash *hash ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_INOUT_CMD ( ARBEL_HCR_MGID_HASH,
+ 1, sizeof ( *gid ),
+ 0, sizeof ( *hash ) ),
+ 0, gid, 0, hash );
+}
+
+static inline int
+arbel_cmd_run_fw ( struct arbel *arbel ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_VOID_CMD ( ARBEL_HCR_RUN_FW ),
+ 0, NULL, 0, NULL );
+}
+
+static inline int
+arbel_cmd_disable_lam ( struct arbel *arbel ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_VOID_CMD ( ARBEL_HCR_DISABLE_LAM ),
+ 0, NULL, 0, NULL );
+}
+
+static inline int
+arbel_cmd_enable_lam ( struct arbel *arbel, struct arbelprm_access_lam *lam ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_OUT_CMD ( ARBEL_HCR_ENABLE_LAM,
+ 1, sizeof ( *lam ) ),
+ 1, NULL, 0, lam );
+}
+
+static inline int
+arbel_cmd_unmap_icm ( struct arbel *arbel, unsigned int page_count ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_VOID_CMD ( ARBEL_HCR_UNMAP_ICM ),
+ 0, NULL, page_count, NULL );
+}
+
+static inline int
+arbel_cmd_map_icm ( struct arbel *arbel,
+ const struct arbelprm_virtual_physical_mapping *map ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_MAP_ICM,
+ 1, sizeof ( *map ) ),
+ 0, map, 1, NULL );
+}
+
+static inline int
+arbel_cmd_unmap_icm_aux ( struct arbel *arbel ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_VOID_CMD ( ARBEL_HCR_UNMAP_ICM_AUX ),
+ 0, NULL, 0, NULL );
+}
+
+static inline int
+arbel_cmd_map_icm_aux ( struct arbel *arbel,
+ const struct arbelprm_virtual_physical_mapping *map ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_MAP_ICM_AUX,
+ 1, sizeof ( *map ) ),
+ 0, map, 1, NULL );
+}
+
+static inline int
+arbel_cmd_set_icm_size ( struct arbel *arbel,
+ const struct arbelprm_scalar_parameter *icm_size,
+ struct arbelprm_scalar_parameter *icm_aux_size ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_INOUT_CMD ( ARBEL_HCR_SET_ICM_SIZE,
+ 0, sizeof ( *icm_size ),
+ 0, sizeof ( *icm_aux_size ) ),
+ 0, icm_size, 0, icm_aux_size );
+}
+
+static inline int
+arbel_cmd_unmap_fa ( struct arbel *arbel ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_VOID_CMD ( ARBEL_HCR_UNMAP_FA ),
+ 0, NULL, 0, NULL );
+}
+
+static inline int
+arbel_cmd_map_fa ( struct arbel *arbel,
+ const struct arbelprm_virtual_physical_mapping *map ) {
+ return arbel_cmd ( arbel,
+ ARBEL_HCR_IN_CMD ( ARBEL_HCR_MAP_FA,
+ 1, sizeof ( *map ) ),
+ 0, map, 1, NULL );
+}
+
+/***************************************************************************
+ *
+ * Completion queue operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Create completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ * @ret rc Return status code
+ */
+static int arbel_create_cq ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ struct arbel_completion_queue *arbel_cq;
+ struct arbelprm_completion_queue_context cqctx;
+ struct arbelprm_cq_ci_db_record *ci_db_rec;
+ struct arbelprm_cq_arm_db_record *arm_db_rec;
+ int cqn_offset;
+ unsigned int i;
+ int rc;
+
+ /* Find a free completion queue number */
+ cqn_offset = arbel_alloc_qn_offset ( arbel->cq_inuse, ARBEL_MAX_CQS );
+ if ( cqn_offset < 0 ) {
+ DBGC ( arbel, "Arbel %p out of completion queues\n", arbel );
+ rc = cqn_offset;
+ goto err_cqn_offset;
+ }
+ cq->cqn = ( arbel->limits.reserved_cqs + cqn_offset );
+
+ /* Allocate control structures */
+ arbel_cq = zalloc ( sizeof ( *arbel_cq ) );
+ if ( ! arbel_cq ) {
+ rc = -ENOMEM;
+ goto err_arbel_cq;
+ }
+ arbel_cq->ci_doorbell_idx = arbel_cq_ci_doorbell_idx ( cqn_offset );
+ arbel_cq->arm_doorbell_idx = arbel_cq_arm_doorbell_idx ( cqn_offset );
+
+ /* Allocate completion queue itself */
+ arbel_cq->cqe_size = ( cq->num_cqes * sizeof ( arbel_cq->cqe[0] ) );
+ arbel_cq->cqe = malloc_dma ( arbel_cq->cqe_size,
+ sizeof ( arbel_cq->cqe[0] ) );
+ if ( ! arbel_cq->cqe ) {
+ rc = -ENOMEM;
+ goto err_cqe;
+ }
+ memset ( arbel_cq->cqe, 0, arbel_cq->cqe_size );
+ for ( i = 0 ; i < cq->num_cqes ; i++ ) {
+ MLX_FILL_1 ( &arbel_cq->cqe[i].normal, 7, owner, 1 );
+ }
+ barrier();
+
+ /* Initialise doorbell records */
+ ci_db_rec = &arbel->db_rec[arbel_cq->ci_doorbell_idx].cq_ci;
+ MLX_FILL_1 ( ci_db_rec, 0, counter, 0 );
+ MLX_FILL_2 ( ci_db_rec, 1,
+ res, ARBEL_UAR_RES_CQ_CI,
+ cq_number, cq->cqn );
+ arm_db_rec = &arbel->db_rec[arbel_cq->arm_doorbell_idx].cq_arm;
+ MLX_FILL_1 ( arm_db_rec, 0, counter, 0 );
+ MLX_FILL_2 ( arm_db_rec, 1,
+ res, ARBEL_UAR_RES_CQ_ARM,
+ cq_number, cq->cqn );
+
+ /* Hand queue over to hardware */
+ memset ( &cqctx, 0, sizeof ( cqctx ) );
+ MLX_FILL_1 ( &cqctx, 0, st, 0xa /* "Event fired" */ );
+ MLX_FILL_1 ( &cqctx, 2, start_address_l,
+ virt_to_bus ( arbel_cq->cqe ) );
+ MLX_FILL_2 ( &cqctx, 3,
+ usr_page, arbel->limits.reserved_uars,
+ log_cq_size, fls ( cq->num_cqes - 1 ) );
+ MLX_FILL_1 ( &cqctx, 5, c_eqn, ARBEL_NO_EQ );
+ MLX_FILL_1 ( &cqctx, 6, pd, ARBEL_GLOBAL_PD );
+ MLX_FILL_1 ( &cqctx, 7, l_key, arbel->reserved_lkey );
+ MLX_FILL_1 ( &cqctx, 12, cqn, cq->cqn );
+ MLX_FILL_1 ( &cqctx, 13,
+ cq_ci_db_record, arbel_cq->ci_doorbell_idx );
+ MLX_FILL_1 ( &cqctx, 14,
+ cq_state_db_record, arbel_cq->arm_doorbell_idx );
+ if ( ( rc = arbel_cmd_sw2hw_cq ( arbel, cq->cqn, &cqctx ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p SW2HW_CQ failed: %s\n",
+ arbel, strerror ( rc ) );
+ goto err_sw2hw_cq;
+ }
+
+ DBGC ( arbel, "Arbel %p CQN %#lx ring at [%p,%p)\n",
+ arbel, cq->cqn, arbel_cq->cqe,
+ ( ( ( void * ) arbel_cq->cqe ) + arbel_cq->cqe_size ) );
+ ib_cq_set_drvdata ( cq, arbel_cq );
+ return 0;
+
+ err_sw2hw_cq:
+ MLX_FILL_1 ( ci_db_rec, 1, res, ARBEL_UAR_RES_NONE );
+ MLX_FILL_1 ( arm_db_rec, 1, res, ARBEL_UAR_RES_NONE );
+ free_dma ( arbel_cq->cqe, arbel_cq->cqe_size );
+ err_cqe:
+ free ( arbel_cq );
+ err_arbel_cq:
+ arbel_free_qn_offset ( arbel->cq_inuse, cqn_offset );
+ err_cqn_offset:
+ return rc;
+}
+
+/**
+ * Destroy completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ */
+static void arbel_destroy_cq ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ struct arbel_completion_queue *arbel_cq = ib_cq_get_drvdata ( cq );
+ struct arbelprm_completion_queue_context cqctx;
+ struct arbelprm_cq_ci_db_record *ci_db_rec;
+ struct arbelprm_cq_arm_db_record *arm_db_rec;
+ int cqn_offset;
+ int rc;
+
+ /* Take ownership back from hardware */
+ if ( ( rc = arbel_cmd_hw2sw_cq ( arbel, cq->cqn, &cqctx ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p FATAL HW2SW_CQ failed on CQN %#lx: "
+ "%s\n", arbel, cq->cqn, strerror ( rc ) );
+ /* Leak memory and return; at least we avoid corruption */
+ return;
+ }
+
+ /* Clear doorbell records */
+ ci_db_rec = &arbel->db_rec[arbel_cq->ci_doorbell_idx].cq_ci;
+ arm_db_rec = &arbel->db_rec[arbel_cq->arm_doorbell_idx].cq_arm;
+ MLX_FILL_1 ( ci_db_rec, 1, res, ARBEL_UAR_RES_NONE );
+ MLX_FILL_1 ( arm_db_rec, 1, res, ARBEL_UAR_RES_NONE );
+
+ /* Free memory */
+ free_dma ( arbel_cq->cqe, arbel_cq->cqe_size );
+ free ( arbel_cq );
+
+ /* Mark queue number as free */
+ cqn_offset = ( cq->cqn - arbel->limits.reserved_cqs );
+ arbel_free_qn_offset ( arbel->cq_inuse, cqn_offset );
+
+ ib_cq_set_drvdata ( cq, NULL );
+}
+
+/***************************************************************************
+ *
+ * Queue pair operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Create send work queue
+ *
+ * @v arbel_send_wq Send work queue
+ * @v num_wqes Number of work queue entries
+ * @ret rc Return status code
+ */
+static int arbel_create_send_wq ( struct arbel_send_work_queue *arbel_send_wq,
+ unsigned int num_wqes ) {
+ struct arbelprm_ud_send_wqe *wqe;
+ struct arbelprm_ud_send_wqe *next_wqe;
+ unsigned int wqe_idx_mask;
+ unsigned int i;
+
+ /* Allocate work queue */
+ arbel_send_wq->wqe_size = ( num_wqes *
+ sizeof ( arbel_send_wq->wqe[0] ) );
+ arbel_send_wq->wqe = malloc_dma ( arbel_send_wq->wqe_size,
+ sizeof ( arbel_send_wq->wqe[0] ) );
+ if ( ! arbel_send_wq->wqe )
+ return -ENOMEM;
+ memset ( arbel_send_wq->wqe, 0, arbel_send_wq->wqe_size );
+
+ /* Link work queue entries */
+ wqe_idx_mask = ( num_wqes - 1 );
+ for ( i = 0 ; i < num_wqes ; i++ ) {
+ wqe = &arbel_send_wq->wqe[i].ud;
+ next_wqe = &arbel_send_wq->wqe[ ( i + 1 ) & wqe_idx_mask ].ud;
+ MLX_FILL_1 ( &wqe->next, 0, nda_31_6,
+ ( virt_to_bus ( next_wqe ) >> 6 ) );
+ }
+
+ return 0;
+}
+
+/**
+ * Create receive work queue
+ *
+ * @v arbel_recv_wq Receive work queue
+ * @v num_wqes Number of work queue entries
+ * @ret rc Return status code
+ */
+static int arbel_create_recv_wq ( struct arbel_recv_work_queue *arbel_recv_wq,
+ unsigned int num_wqes ) {
+ struct arbelprm_recv_wqe *wqe;
+ struct arbelprm_recv_wqe *next_wqe;
+ unsigned int wqe_idx_mask;
+ size_t nds;
+ unsigned int i;
+ unsigned int j;
+
+ /* Allocate work queue */
+ arbel_recv_wq->wqe_size = ( num_wqes *
+ sizeof ( arbel_recv_wq->wqe[0] ) );
+ arbel_recv_wq->wqe = malloc_dma ( arbel_recv_wq->wqe_size,
+ sizeof ( arbel_recv_wq->wqe[0] ) );
+ if ( ! arbel_recv_wq->wqe )
+ return -ENOMEM;
+ memset ( arbel_recv_wq->wqe, 0, arbel_recv_wq->wqe_size );
+
+ /* Link work queue entries */
+ wqe_idx_mask = ( num_wqes - 1 );
+ nds = ( ( offsetof ( typeof ( *wqe ), data ) +
+ sizeof ( wqe->data[0] ) ) >> 4 );
+ for ( i = 0 ; i < num_wqes ; i++ ) {
+ wqe = &arbel_recv_wq->wqe[i].recv;
+ next_wqe = &arbel_recv_wq->wqe[( i + 1 ) & wqe_idx_mask].recv;
+ MLX_FILL_1 ( &wqe->next, 0, nda_31_6,
+ ( virt_to_bus ( next_wqe ) >> 6 ) );
+ MLX_FILL_1 ( &wqe->next, 1, nds, ( sizeof ( *wqe ) / 16 ) );
+ for ( j = 0 ; ( ( ( void * ) &wqe->data[j] ) <
+ ( ( void * ) ( wqe + 1 ) ) ) ; j++ ) {
+ MLX_FILL_1 ( &wqe->data[j], 1,
+ l_key, ARBEL_INVALID_LKEY );
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Create queue pair
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @ret rc Return status code
+ */
+static int arbel_create_qp ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ struct arbel_queue_pair *arbel_qp;
+ struct arbelprm_qp_ee_state_transitions qpctx;
+ struct arbelprm_qp_db_record *send_db_rec;
+ struct arbelprm_qp_db_record *recv_db_rec;
+ int qpn_offset;
+ int rc;
+
+ /* Find a free queue pair number */
+ qpn_offset = arbel_alloc_qn_offset ( arbel->qp_inuse, ARBEL_MAX_QPS );
+ if ( qpn_offset < 0 ) {
+ DBGC ( arbel, "Arbel %p out of queue pairs\n", arbel );
+ rc = qpn_offset;
+ goto err_qpn_offset;
+ }
+ qp->qpn = ( ARBEL_QPN_BASE + arbel->limits.reserved_qps + qpn_offset );
+
+ /* Allocate control structures */
+ arbel_qp = zalloc ( sizeof ( *arbel_qp ) );
+ if ( ! arbel_qp ) {
+ rc = -ENOMEM;
+ goto err_arbel_qp;
+ }
+ arbel_qp->send.doorbell_idx = arbel_send_doorbell_idx ( qpn_offset );
+ arbel_qp->recv.doorbell_idx = arbel_recv_doorbell_idx ( qpn_offset );
+
+ /* Create send and receive work queues */
+ if ( ( rc = arbel_create_send_wq ( &arbel_qp->send,
+ qp->send.num_wqes ) ) != 0 )
+ goto err_create_send_wq;
+ if ( ( rc = arbel_create_recv_wq ( &arbel_qp->recv,
+ qp->recv.num_wqes ) ) != 0 )
+ goto err_create_recv_wq;
+
+ /* Initialise doorbell records */
+ send_db_rec = &arbel->db_rec[arbel_qp->send.doorbell_idx].qp;
+ MLX_FILL_1 ( send_db_rec, 0, counter, 0 );
+ MLX_FILL_2 ( send_db_rec, 1,
+ res, ARBEL_UAR_RES_SQ,
+ qp_number, qp->qpn );
+ recv_db_rec = &arbel->db_rec[arbel_qp->recv.doorbell_idx].qp;
+ MLX_FILL_1 ( recv_db_rec, 0, counter, 0 );
+ MLX_FILL_2 ( recv_db_rec, 1,
+ res, ARBEL_UAR_RES_RQ,
+ qp_number, qp->qpn );
+
+ /* Hand queue over to hardware */
+ memset ( &qpctx, 0, sizeof ( qpctx ) );
+ MLX_FILL_3 ( &qpctx, 2,
+ qpc_eec_data.de, 1,
+ qpc_eec_data.pm_state, 0x03 /* Always 0x03 for UD */,
+ qpc_eec_data.st, ARBEL_ST_UD );
+ MLX_FILL_6 ( &qpctx, 4,
+ qpc_eec_data.mtu, ARBEL_MTU_2048,
+ qpc_eec_data.msg_max, 11 /* 2^11 = 2048 */,
+ qpc_eec_data.log_rq_size, fls ( qp->recv.num_wqes - 1 ),
+ qpc_eec_data.log_rq_stride,
+ ( fls ( sizeof ( arbel_qp->recv.wqe[0] ) - 1 ) - 4 ),
+ qpc_eec_data.log_sq_size, fls ( qp->send.num_wqes - 1 ),
+ qpc_eec_data.log_sq_stride,
+ ( fls ( sizeof ( arbel_qp->send.wqe[0] ) - 1 ) - 4 ) );
+ MLX_FILL_1 ( &qpctx, 5,
+ qpc_eec_data.usr_page, arbel->limits.reserved_uars );
+ MLX_FILL_1 ( &qpctx, 10, qpc_eec_data.primary_address_path.port_number,
+ ibdev->port );
+ MLX_FILL_1 ( &qpctx, 27, qpc_eec_data.pd, ARBEL_GLOBAL_PD );
+ MLX_FILL_1 ( &qpctx, 29, qpc_eec_data.wqe_lkey, arbel->reserved_lkey );
+ MLX_FILL_1 ( &qpctx, 30, qpc_eec_data.ssc, 1 );
+ MLX_FILL_1 ( &qpctx, 33, qpc_eec_data.cqn_snd, qp->send.cq->cqn );
+ MLX_FILL_1 ( &qpctx, 34, qpc_eec_data.snd_wqe_base_adr_l,
+ ( virt_to_bus ( arbel_qp->send.wqe ) >> 6 ) );
+ MLX_FILL_1 ( &qpctx, 35, qpc_eec_data.snd_db_record_index,
+ arbel_qp->send.doorbell_idx );
+ MLX_FILL_1 ( &qpctx, 38, qpc_eec_data.rsc, 1 );
+ MLX_FILL_1 ( &qpctx, 41, qpc_eec_data.cqn_rcv, qp->recv.cq->cqn );
+ MLX_FILL_1 ( &qpctx, 42, qpc_eec_data.rcv_wqe_base_adr_l,
+ ( virt_to_bus ( arbel_qp->recv.wqe ) >> 6 ) );
+ MLX_FILL_1 ( &qpctx, 43, qpc_eec_data.rcv_db_record_index,
+ arbel_qp->recv.doorbell_idx );
+ MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey );
+ if ( ( rc = arbel_cmd_rst2init_qpee ( arbel, qp->qpn, &qpctx )) != 0 ){
+ DBGC ( arbel, "Arbel %p RST2INIT_QPEE failed: %s\n",
+ arbel, strerror ( rc ) );
+ goto err_rst2init_qpee;
+ }
+ memset ( &qpctx, 0, sizeof ( qpctx ) );
+ MLX_FILL_2 ( &qpctx, 4,
+ qpc_eec_data.mtu, ARBEL_MTU_2048,
+ qpc_eec_data.msg_max, 11 /* 2^11 = 2048 */ );
+ if ( ( rc = arbel_cmd_init2rtr_qpee ( arbel, qp->qpn, &qpctx )) != 0 ){
+ DBGC ( arbel, "Arbel %p INIT2RTR_QPEE failed: %s\n",
+ arbel, strerror ( rc ) );
+ goto err_init2rtr_qpee;
+ }
+ memset ( &qpctx, 0, sizeof ( qpctx ) );
+ if ( ( rc = arbel_cmd_rtr2rts_qpee ( arbel, qp->qpn, &qpctx ) ) != 0 ){
+ DBGC ( arbel, "Arbel %p RTR2RTS_QPEE failed: %s\n",
+ arbel, strerror ( rc ) );
+ goto err_rtr2rts_qpee;
+ }
+
+ DBGC ( arbel, "Arbel %p QPN %#lx send ring at [%p,%p)\n",
+ arbel, qp->qpn, arbel_qp->send.wqe,
+ ( ( (void *) arbel_qp->send.wqe ) + arbel_qp->send.wqe_size ) );
+ DBGC ( arbel, "Arbel %p QPN %#lx receive ring at [%p,%p)\n",
+ arbel, qp->qpn, arbel_qp->recv.wqe,
+ ( ( (void *) arbel_qp->recv.wqe ) + arbel_qp->recv.wqe_size ) );
+ ib_qp_set_drvdata ( qp, arbel_qp );
+ return 0;
+
+ err_rtr2rts_qpee:
+ err_init2rtr_qpee:
+ arbel_cmd_2rst_qpee ( arbel, qp->qpn );
+ err_rst2init_qpee:
+ MLX_FILL_1 ( send_db_rec, 1, res, ARBEL_UAR_RES_NONE );
+ MLX_FILL_1 ( recv_db_rec, 1, res, ARBEL_UAR_RES_NONE );
+ free_dma ( arbel_qp->recv.wqe, arbel_qp->recv.wqe_size );
+ err_create_recv_wq:
+ free_dma ( arbel_qp->send.wqe, arbel_qp->send.wqe_size );
+ err_create_send_wq:
+ free ( arbel_qp );
+ err_arbel_qp:
+ arbel_free_qn_offset ( arbel->qp_inuse, qpn_offset );
+ err_qpn_offset:
+ return rc;
+}
+
+/**
+ * Destroy queue pair
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ */
+static void arbel_destroy_qp ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ struct arbel_queue_pair *arbel_qp = ib_qp_get_drvdata ( qp );
+ struct arbelprm_qp_db_record *send_db_rec;
+ struct arbelprm_qp_db_record *recv_db_rec;
+ int qpn_offset;
+ int rc;
+
+ /* Take ownership back from hardware */
+ if ( ( rc = arbel_cmd_2rst_qpee ( arbel, qp->qpn ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p FATAL 2RST_QPEE failed on QPN %#lx: "
+ "%s\n", arbel, qp->qpn, strerror ( rc ) );
+ /* Leak memory and return; at least we avoid corruption */
+ return;
+ }
+
+ /* Clear doorbell records */
+ send_db_rec = &arbel->db_rec[arbel_qp->send.doorbell_idx].qp;
+ recv_db_rec = &arbel->db_rec[arbel_qp->recv.doorbell_idx].qp;
+ MLX_FILL_1 ( send_db_rec, 1, res, ARBEL_UAR_RES_NONE );
+ MLX_FILL_1 ( recv_db_rec, 1, res, ARBEL_UAR_RES_NONE );
+
+ /* Free memory */
+ free_dma ( arbel_qp->send.wqe, arbel_qp->send.wqe_size );
+ free_dma ( arbel_qp->recv.wqe, arbel_qp->recv.wqe_size );
+ free ( arbel_qp );
+
+ /* Mark queue number as free */
+ qpn_offset = ( qp->qpn - ARBEL_QPN_BASE - arbel->limits.reserved_qps );
+ arbel_free_qn_offset ( arbel->qp_inuse, qpn_offset );
+
+ ib_qp_set_drvdata ( qp, NULL );
+}
+
+/***************************************************************************
+ *
+ * Work request operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Ring doorbell register in UAR
+ *
+ * @v arbel Arbel device
+ * @v db_reg Doorbell register structure
+ * @v offset Address of doorbell
+ */
+static void arbel_ring_doorbell ( struct arbel *arbel,
+ union arbelprm_doorbell_register *db_reg,
+ unsigned int offset ) {
+
+ DBGC2 ( arbel, "Arbel %p ringing doorbell %08lx:%08lx at %lx\n",
+ arbel, db_reg->dword[0], db_reg->dword[1],
+ virt_to_phys ( arbel->uar + offset ) );
+
+ barrier();
+ writel ( db_reg->dword[0], ( arbel->uar + offset + 0 ) );
+ barrier();
+ writel ( db_reg->dword[1], ( arbel->uar + offset + 4 ) );
+}
+
+/** GID used for GID-less send work queue entries */
+static const struct ib_gid arbel_no_gid = {
+ { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 } }
+};
+
+/**
+ * Post send work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v av Address vector
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int arbel_post_send ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct ib_address_vector *av,
+ struct io_buffer *iobuf ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ struct arbel_queue_pair *arbel_qp = ib_qp_get_drvdata ( qp );
+ struct ib_work_queue *wq = &qp->send;
+ struct arbel_send_work_queue *arbel_send_wq = &arbel_qp->send;
+ struct arbelprm_ud_send_wqe *prev_wqe;
+ struct arbelprm_ud_send_wqe *wqe;
+ struct arbelprm_qp_db_record *qp_db_rec;
+ union arbelprm_doorbell_register db_reg;
+ const struct ib_gid *gid;
+ unsigned int wqe_idx_mask;
+ size_t nds;
+
+ /* Allocate work queue entry */
+ wqe_idx_mask = ( wq->num_wqes - 1 );
+ if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) {
+ DBGC ( arbel, "Arbel %p send queue full", arbel );
+ return -ENOBUFS;
+ }
+ wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf;
+ prev_wqe = &arbel_send_wq->wqe[(wq->next_idx - 1) & wqe_idx_mask].ud;
+ wqe = &arbel_send_wq->wqe[wq->next_idx & wqe_idx_mask].ud;
+
+ /* Construct work queue entry */
+ MLX_FILL_1 ( &wqe->next, 1, always1, 1 );
+ memset ( &wqe->ctrl, 0, sizeof ( wqe->ctrl ) );
+ MLX_FILL_1 ( &wqe->ctrl, 0, always1, 1 );
+ memset ( &wqe->ud, 0, sizeof ( wqe->ud ) );
+ MLX_FILL_2 ( &wqe->ud, 0,
+ ud_address_vector.pd, ARBEL_GLOBAL_PD,
+ ud_address_vector.port_number, ibdev->port );
+ MLX_FILL_2 ( &wqe->ud, 1,
+ ud_address_vector.rlid, av->dlid,
+ ud_address_vector.g, av->gid_present );
+ MLX_FILL_2 ( &wqe->ud, 2,
+ ud_address_vector.max_stat_rate,
+ ( ( av->rate >= 3 ) ? 0 : 1 ),
+ ud_address_vector.msg, 3 );
+ MLX_FILL_1 ( &wqe->ud, 3, ud_address_vector.sl, av->sl );
+ gid = ( av->gid_present ? &av->gid : &arbel_no_gid );
+ memcpy ( &wqe->ud.u.dwords[4], gid, sizeof ( *gid ) );
+ MLX_FILL_1 ( &wqe->ud, 8, destination_qp, av->dest_qp );
+ MLX_FILL_1 ( &wqe->ud, 9, q_key, av->qkey );
+ MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_len ( iobuf ) );
+ MLX_FILL_1 ( &wqe->data[0], 1, l_key, arbel->reserved_lkey );
+ MLX_FILL_1 ( &wqe->data[0], 3,
+ local_address_l, virt_to_bus ( iobuf->data ) );
+
+ /* Update previous work queue entry's "next" field */
+ nds = ( ( offsetof ( typeof ( *wqe ), data ) +
+ sizeof ( wqe->data[0] ) ) >> 4 );
+ MLX_SET ( &prev_wqe->next, nopcode, ARBEL_OPCODE_SEND );
+ MLX_FILL_3 ( &prev_wqe->next, 1,
+ nds, nds,
+ f, 1,
+ always1, 1 );
+
+ /* Update doorbell record */
+ barrier();
+ qp_db_rec = &arbel->db_rec[arbel_send_wq->doorbell_idx].qp;
+ MLX_FILL_1 ( qp_db_rec, 0,
+ counter, ( ( wq->next_idx + 1 ) & 0xffff ) );
+
+ /* Ring doorbell register */
+ MLX_FILL_4 ( &db_reg.send, 0,
+ nopcode, ARBEL_OPCODE_SEND,
+ f, 1,
+ wqe_counter, ( wq->next_idx & 0xffff ),
+ wqe_cnt, 1 );
+ MLX_FILL_2 ( &db_reg.send, 1,
+ nds, nds,
+ qpn, qp->qpn );
+ arbel_ring_doorbell ( arbel, &db_reg, ARBEL_DB_POST_SND_OFFSET );
+
+ /* Update work queue's index */
+ wq->next_idx++;
+
+ return 0;
+}
+
+/**
+ * Post receive work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int arbel_post_recv ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct io_buffer *iobuf ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ struct arbel_queue_pair *arbel_qp = ib_qp_get_drvdata ( qp );
+ struct ib_work_queue *wq = &qp->recv;
+ struct arbel_recv_work_queue *arbel_recv_wq = &arbel_qp->recv;
+ struct arbelprm_recv_wqe *wqe;
+ union arbelprm_doorbell_record *db_rec;
+ unsigned int wqe_idx_mask;
+
+ /* Allocate work queue entry */
+ wqe_idx_mask = ( wq->num_wqes - 1 );
+ if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) {
+ DBGC ( arbel, "Arbel %p receive queue full", arbel );
+ return -ENOBUFS;
+ }
+ wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf;
+ wqe = &arbel_recv_wq->wqe[wq->next_idx & wqe_idx_mask].recv;
+
+ /* Construct work queue entry */
+ MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) );
+ MLX_FILL_1 ( &wqe->data[0], 1, l_key, arbel->reserved_lkey );
+ MLX_FILL_1 ( &wqe->data[0], 3,
+ local_address_l, virt_to_bus ( iobuf->data ) );
+
+ /* Update doorbell record */
+ barrier();
+ db_rec = &arbel->db_rec[arbel_recv_wq->doorbell_idx];
+ MLX_FILL_1 ( &db_rec->qp, 0,
+ counter, ( ( wq->next_idx + 1 ) & 0xffff ) );
+
+ /* Update work queue's index */
+ wq->next_idx++;
+
+ return 0;
+}
+
+/**
+ * Handle completion
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ * @v cqe Hardware completion queue entry
+ * @v complete_send Send completion handler
+ * @v complete_recv Receive completion handler
+ * @ret rc Return status code
+ */
+static int arbel_complete ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq,
+ union arbelprm_completion_entry *cqe,
+ ib_completer_t complete_send,
+ ib_completer_t complete_recv ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ struct ib_completion completion;
+ struct ib_work_queue *wq;
+ struct ib_queue_pair *qp;
+ struct arbel_queue_pair *arbel_qp;
+ struct arbel_send_work_queue *arbel_send_wq;
+ struct arbel_recv_work_queue *arbel_recv_wq;
+ struct arbelprm_recv_wqe *recv_wqe;
+ struct io_buffer *iobuf;
+ ib_completer_t complete;
+ unsigned int opcode;
+ unsigned long qpn;
+ int is_send;
+ unsigned long wqe_adr;
+ unsigned int wqe_idx;
+ int rc = 0;
+
+ /* Parse completion */
+ memset ( &completion, 0, sizeof ( completion ) );
+ qpn = MLX_GET ( &cqe->normal, my_qpn );
+ is_send = MLX_GET ( &cqe->normal, s );
+ wqe_adr = ( MLX_GET ( &cqe->normal, wqe_adr ) << 6 );
+ opcode = MLX_GET ( &cqe->normal, opcode );
+ if ( opcode >= ARBEL_OPCODE_RECV_ERROR ) {
+ /* "s" field is not valid for error opcodes */
+ is_send = ( opcode == ARBEL_OPCODE_SEND_ERROR );
+ completion.syndrome = MLX_GET ( &cqe->error, syndrome );
+ DBGC ( arbel, "Arbel %p CPN %lx syndrome %x vendor %lx\n",
+ arbel, cq->cqn, completion.syndrome,
+ MLX_GET ( &cqe->error, vendor_code ) );
+ rc = -EIO;
+ /* Don't return immediately; propagate error to completer */
+ }
+
+ /* Identify work queue */
+ wq = ib_find_wq ( cq, qpn, is_send );
+ if ( ! wq ) {
+ DBGC ( arbel, "Arbel %p CQN %lx unknown %s QPN %lx\n",
+ arbel, cq->cqn, ( is_send ? "send" : "recv" ), qpn );
+ return -EIO;
+ }
+ qp = wq->qp;
+ arbel_qp = ib_qp_get_drvdata ( qp );
+ arbel_send_wq = &arbel_qp->send;
+ arbel_recv_wq = &arbel_qp->recv;
+
+ /* Identify work queue entry index */
+ if ( is_send ) {
+ wqe_idx = ( ( wqe_adr - virt_to_bus ( arbel_send_wq->wqe ) ) /
+ sizeof ( arbel_send_wq->wqe[0] ) );
+ assert ( wqe_idx < qp->send.num_wqes );
+ } else {
+ wqe_idx = ( ( wqe_adr - virt_to_bus ( arbel_recv_wq->wqe ) ) /
+ sizeof ( arbel_recv_wq->wqe[0] ) );
+ assert ( wqe_idx < qp->recv.num_wqes );
+ }
+
+ /* Identify I/O buffer */
+ iobuf = wq->iobufs[wqe_idx];
+ if ( ! iobuf ) {
+ DBGC ( arbel, "Arbel %p CQN %lx QPN %lx empty WQE %x\n",
+ arbel, cq->cqn, qpn, wqe_idx );
+ return -EIO;
+ }
+ wq->iobufs[wqe_idx] = NULL;
+
+ /* Fill in length for received packets */
+ if ( ! is_send ) {
+ completion.len = MLX_GET ( &cqe->normal, byte_cnt );
+ recv_wqe = &arbel_recv_wq->wqe[wqe_idx].recv;
+ assert ( MLX_GET ( &recv_wqe->data[0], local_address_l ) ==
+ virt_to_bus ( iobuf->data ) );
+ assert ( MLX_GET ( &recv_wqe->data[0], byte_count ) ==
+ iob_tailroom ( iobuf ) );
+ MLX_FILL_1 ( &recv_wqe->data[0], 0, byte_count, 0 );
+ MLX_FILL_1 ( &recv_wqe->data[0], 1,
+ l_key, ARBEL_INVALID_LKEY );
+ if ( completion.len > iob_tailroom ( iobuf ) ) {
+ DBGC ( arbel, "Arbel %p CQN %lx QPN %lx IDX %x "
+ "overlength received packet length %zd\n",
+ arbel, cq->cqn, qpn, wqe_idx, completion.len );
+ return -EIO;
+ }
+ }
+
+ /* Pass off to caller's completion handler */
+ complete = ( is_send ? complete_send : complete_recv );
+ complete ( ibdev, qp, &completion, iobuf );
+
+ return rc;
+}
+
+/**
+ * Poll completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ * @v complete_send Send completion handler
+ * @v complete_recv Receive completion handler
+ */
+static void arbel_poll_cq ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq,
+ ib_completer_t complete_send,
+ ib_completer_t complete_recv ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ struct arbel_completion_queue *arbel_cq = ib_cq_get_drvdata ( cq );
+ struct arbelprm_cq_ci_db_record *ci_db_rec;
+ union arbelprm_completion_entry *cqe;
+ unsigned int cqe_idx_mask;
+ int rc;
+
+ while ( 1 ) {
+ /* Look for completion entry */
+ cqe_idx_mask = ( cq->num_cqes - 1 );
+ cqe = &arbel_cq->cqe[cq->next_idx & cqe_idx_mask];
+ if ( MLX_GET ( &cqe->normal, owner ) != 0 ) {
+ /* Entry still owned by hardware; end of poll */
+ break;
+ }
+
+ /* Handle completion */
+ if ( ( rc = arbel_complete ( ibdev, cq, cqe, complete_send,
+ complete_recv ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p failed to complete: %s\n",
+ arbel, strerror ( rc ) );
+ DBGC_HD ( arbel, cqe, sizeof ( *cqe ) );
+ }
+
+ /* Return ownership to hardware */
+ MLX_FILL_1 ( &cqe->normal, 7, owner, 1 );
+ barrier();
+ /* Update completion queue's index */
+ cq->next_idx++;
+ /* Update doorbell record */
+ ci_db_rec = &arbel->db_rec[arbel_cq->ci_doorbell_idx].cq_ci;
+ MLX_FILL_1 ( ci_db_rec, 0,
+ counter, ( cq->next_idx & 0xffffffffUL ) );
+ }
+}
+
+/***************************************************************************
+ *
+ * Infiniband link-layer operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Initialise Infiniband link
+ *
+ * @v ibdev Infiniband device
+ * @ret rc Return status code
+ */
+static int arbel_open ( struct ib_device *ibdev ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ struct arbelprm_init_ib init_ib;
+ int rc;
+
+ memset ( &init_ib, 0, sizeof ( init_ib ) );
+ MLX_FILL_3 ( &init_ib, 0,
+ mtu_cap, ARBEL_MTU_2048,
+ port_width_cap, 3,
+ vl_cap, 1 );
+ MLX_FILL_1 ( &init_ib, 1, max_gid, 1 );
+ MLX_FILL_1 ( &init_ib, 2, max_pkey, 64 );
+ if ( ( rc = arbel_cmd_init_ib ( arbel, ibdev->port,
+ &init_ib ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not intialise IB: %s\n",
+ arbel, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Close Infiniband link
+ *
+ * @v ibdev Infiniband device
+ */
+static void arbel_close ( struct ib_device *ibdev ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ int rc;
+
+ if ( ( rc = arbel_cmd_close_ib ( arbel, ibdev->port ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not close IB: %s\n",
+ arbel, strerror ( rc ) );
+ /* Nothing we can do about this */
+ }
+}
+
+/***************************************************************************
+ *
+ * Multicast group operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Attach to multicast group
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v gid Multicast GID
+ * @ret rc Return status code
+ */
+static int arbel_mcast_attach ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct ib_gid *gid ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ struct arbelprm_mgm_hash hash;
+ struct arbelprm_mgm_entry mgm;
+ unsigned int index;
+ int rc;
+
+ /* Generate hash table index */
+ if ( ( rc = arbel_cmd_mgid_hash ( arbel, gid, &hash ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not hash GID: %s\n",
+ arbel, strerror ( rc ) );
+ return rc;
+ }
+ index = MLX_GET ( &hash, hash );
+
+ /* Check for existing hash table entry */
+ if ( ( rc = arbel_cmd_read_mgm ( arbel, index, &mgm ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not read MGM %#x: %s\n",
+ arbel, index, strerror ( rc ) );
+ return rc;
+ }
+ if ( MLX_GET ( &mgm, mgmqp_0.qi ) != 0 ) {
+ /* FIXME: this implementation allows only a single QP
+ * per multicast group, and doesn't handle hash
+ * collisions. Sufficient for IPoIB but may need to
+ * be extended in future.
+ */
+ DBGC ( arbel, "Arbel %p MGID index %#x already in use\n",
+ arbel, index );
+ return -EBUSY;
+ }
+
+ /* Update hash table entry */
+ MLX_FILL_2 ( &mgm, 8,
+ mgmqp_0.qpn_i, qp->qpn,
+ mgmqp_0.qi, 1 );
+ memcpy ( &mgm.u.dwords[4], gid, sizeof ( *gid ) );
+ if ( ( rc = arbel_cmd_write_mgm ( arbel, index, &mgm ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not write MGM %#x: %s\n",
+ arbel, index, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Detach from multicast group
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v gid Multicast GID
+ */
+static void arbel_mcast_detach ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp __unused,
+ struct ib_gid *gid ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ struct arbelprm_mgm_hash hash;
+ struct arbelprm_mgm_entry mgm;
+ unsigned int index;
+ int rc;
+
+ /* Generate hash table index */
+ if ( ( rc = arbel_cmd_mgid_hash ( arbel, gid, &hash ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not hash GID: %s\n",
+ arbel, strerror ( rc ) );
+ return;
+ }
+ index = MLX_GET ( &hash, hash );
+
+ /* Clear hash table entry */
+ memset ( &mgm, 0, sizeof ( mgm ) );
+ if ( ( rc = arbel_cmd_write_mgm ( arbel, index, &mgm ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not write MGM %#x: %s\n",
+ arbel, index, strerror ( rc ) );
+ return;
+ }
+}
+
+/***************************************************************************
+ *
+ * MAD operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Issue management datagram
+ *
+ * @v ibdev Infiniband device
+ * @v mad Management datagram
+ * @v len Length of management datagram
+ * @ret rc Return status code
+ */
+static int arbel_mad ( struct ib_device *ibdev, struct ib_mad_hdr *mad,
+ size_t len ) {
+ struct arbel *arbel = ib_get_drvdata ( ibdev );
+ union arbelprm_mad mad_ifc;
+ int rc;
+
+ /* Copy in request packet */
+ memset ( &mad_ifc, 0, sizeof ( mad_ifc ) );
+ assert ( len <= sizeof ( mad_ifc.mad ) );
+ memcpy ( &mad_ifc.mad, mad, len );
+
+ /* Issue MAD */
+ if ( ( rc = arbel_cmd_mad_ifc ( arbel, ibdev->port,
+ &mad_ifc ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not issue MAD IFC: %s\n",
+ arbel, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy out reply packet */
+ memcpy ( mad, &mad_ifc.mad, len );
+
+ if ( mad->status != 0 ) {
+ DBGC ( arbel, "Arbel %p MAD IFC status %04x\n",
+ arbel, ntohs ( mad->status ) );
+ return -EIO;
+ }
+ return 0;
+}
+
+/** Arbel Infiniband operations */
+static struct ib_device_operations arbel_ib_operations = {
+ .create_cq = arbel_create_cq,
+ .destroy_cq = arbel_destroy_cq,
+ .create_qp = arbel_create_qp,
+ .destroy_qp = arbel_destroy_qp,
+ .post_send = arbel_post_send,
+ .post_recv = arbel_post_recv,
+ .poll_cq = arbel_poll_cq,
+ .open = arbel_open,
+ .close = arbel_close,
+ .mcast_attach = arbel_mcast_attach,
+ .mcast_detach = arbel_mcast_detach,
+ .mad = arbel_mad,
+};
+
+/***************************************************************************
+ *
+ * Firmware control
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Start firmware running
+ *
+ * @v arbel Arbel device
+ * @ret rc Return status code
+ */
+static int arbel_start_firmware ( struct arbel *arbel ) {
+ struct arbelprm_query_fw fw;
+ struct arbelprm_access_lam lam;
+ struct arbelprm_virtual_physical_mapping map_fa;
+ unsigned int fw_pages;
+ unsigned int log2_fw_pages;
+ size_t fw_size;
+ physaddr_t fw_base;
+ int rc;
+
+ /* Get firmware parameters */
+ if ( ( rc = arbel_cmd_query_fw ( arbel, &fw ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not query firmware: %s\n",
+ arbel, strerror ( rc ) );
+ goto err_query_fw;
+ }
+ DBGC ( arbel, "Arbel %p firmware version %ld.%ld.%ld\n", arbel,
+ MLX_GET ( &fw, fw_rev_major ), MLX_GET ( &fw, fw_rev_minor ),
+ MLX_GET ( &fw, fw_rev_subminor ) );
+ fw_pages = MLX_GET ( &fw, fw_pages );
+ log2_fw_pages = fls ( fw_pages - 1 );
+ fw_pages = ( 1 << log2_fw_pages );
+ DBGC ( arbel, "Arbel %p requires %d kB for firmware\n",
+ arbel, ( fw_pages * 4 ) );
+
+ /* Enable locally-attached memory. Ignore failure; there may
+ * be no attached memory.
+ */
+ arbel_cmd_enable_lam ( arbel, &lam );
+
+ /* Allocate firmware pages and map firmware area */
+ fw_size = ( fw_pages * 4096 );
+ arbel->firmware_area = umalloc ( fw_size );
+ if ( ! arbel->firmware_area ) {
+ rc = -ENOMEM;
+ goto err_alloc_fa;
+ }
+ fw_base = ( user_to_phys ( arbel->firmware_area, fw_size ) &
+ ~( fw_size - 1 ) );
+ DBGC ( arbel, "Arbel %p firmware area at physical [%lx,%lx)\n",
+ arbel, fw_base, ( fw_base + fw_size ) );
+ memset ( &map_fa, 0, sizeof ( map_fa ) );
+ MLX_FILL_2 ( &map_fa, 3,
+ log2size, log2_fw_pages,
+ pa_l, ( fw_base >> 12 ) );
+ if ( ( rc = arbel_cmd_map_fa ( arbel, &map_fa ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not map firmware: %s\n",
+ arbel, strerror ( rc ) );
+ goto err_map_fa;
+ }
+
+ /* Start firmware */
+ if ( ( rc = arbel_cmd_run_fw ( arbel ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not run firmware: %s\n",
+ arbel, strerror ( rc ) );
+ goto err_run_fw;
+ }
+
+ DBGC ( arbel, "Arbel %p firmware started\n", arbel );
+ return 0;
+
+ err_run_fw:
+ arbel_cmd_unmap_fa ( arbel );
+ err_map_fa:
+ ufree ( arbel->firmware_area );
+ arbel->firmware_area = UNULL;
+ err_alloc_fa:
+ err_query_fw:
+ return rc;
+}
+
+/**
+ * Stop firmware running
+ *
+ * @v arbel Arbel device
+ */
+static void arbel_stop_firmware ( struct arbel *arbel ) {
+ int rc;
+
+ if ( ( rc = arbel_cmd_unmap_fa ( arbel ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p FATAL could not stop firmware: %s\n",
+ arbel, strerror ( rc ) );
+ /* Leak memory and return; at least we avoid corruption */
+ return;
+ }
+ ufree ( arbel->firmware_area );
+ arbel->firmware_area = UNULL;
+}
+
+/***************************************************************************
+ *
+ * Infinihost Context Memory management
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Get device limits
+ *
+ * @v arbel Arbel device
+ * @ret rc Return status code
+ */
+static int arbel_get_limits ( struct arbel *arbel ) {
+ struct arbelprm_query_dev_lim dev_lim;
+ int rc;
+
+ if ( ( rc = arbel_cmd_query_dev_lim ( arbel, &dev_lim ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not get device limits: %s\n",
+ arbel, strerror ( rc ) );
+ return rc;
+ }
+
+ arbel->limits.reserved_qps =
+ ( 1 << MLX_GET ( &dev_lim, log2_rsvd_qps ) );
+ arbel->limits.qpc_entry_size = MLX_GET ( &dev_lim, qpc_entry_sz );
+ arbel->limits.eqpc_entry_size = MLX_GET ( &dev_lim, eqpc_entry_sz );
+ arbel->limits.reserved_srqs =
+ ( 1 << MLX_GET ( &dev_lim, log2_rsvd_srqs ) );
+ arbel->limits.srqc_entry_size = MLX_GET ( &dev_lim, srq_entry_sz );
+ arbel->limits.reserved_ees =
+ ( 1 << MLX_GET ( &dev_lim, log2_rsvd_ees ) );
+ arbel->limits.eec_entry_size = MLX_GET ( &dev_lim, eec_entry_sz );
+ arbel->limits.eeec_entry_size = MLX_GET ( &dev_lim, eeec_entry_sz );
+ arbel->limits.reserved_cqs =
+ ( 1 << MLX_GET ( &dev_lim, log2_rsvd_cqs ) );
+ arbel->limits.cqc_entry_size = MLX_GET ( &dev_lim, cqc_entry_sz );
+ arbel->limits.reserved_mtts =
+ ( 1 << MLX_GET ( &dev_lim, log2_rsvd_mtts ) );
+ arbel->limits.mtt_entry_size = MLX_GET ( &dev_lim, mtt_entry_sz );
+ arbel->limits.reserved_mrws =
+ ( 1 << MLX_GET ( &dev_lim, log2_rsvd_mrws ) );
+ arbel->limits.mpt_entry_size = MLX_GET ( &dev_lim, mpt_entry_sz );
+ arbel->limits.reserved_rdbs =
+ ( 1 << MLX_GET ( &dev_lim, log2_rsvd_rdbs ) );
+ arbel->limits.eqc_entry_size = MLX_GET ( &dev_lim, eqc_entry_sz );
+ arbel->limits.reserved_uars = MLX_GET ( &dev_lim, num_rsvd_uars );
+
+ return 0;
+}
+
+/**
+ * Get ICM usage
+ *
+ * @v log_num_entries Log2 of the number of entries
+ * @v entry_size Entry size
+ * @ret usage Usage size in ICM
+ */
+static size_t icm_usage ( unsigned int log_num_entries, size_t entry_size ) {
+ size_t usage;
+
+ usage = ( ( 1 << log_num_entries ) * entry_size );
+ usage = ( ( usage + 4095 ) & ~4095 );
+ return usage;
+}
+
+/**
+ * Allocate ICM
+ *
+ * @v arbel Arbel device
+ * @v init_hca INIT_HCA structure to fill in
+ * @ret rc Return status code
+ */
+static int arbel_alloc_icm ( struct arbel *arbel,
+ struct arbelprm_init_hca *init_hca ) {
+ struct arbelprm_scalar_parameter icm_size;
+ struct arbelprm_scalar_parameter icm_aux_size;
+ struct arbelprm_virtual_physical_mapping map_icm_aux;
+ struct arbelprm_virtual_physical_mapping map_icm;
+ union arbelprm_doorbell_record *db_rec;
+ size_t icm_offset = 0;
+ unsigned int log_num_qps, log_num_srqs, log_num_ees, log_num_cqs;
+ unsigned int log_num_mtts, log_num_mpts, log_num_rdbs, log_num_eqs;
+ int rc;
+
+ icm_offset = ( ( arbel->limits.reserved_uars + 1 ) << 12 );
+
+ /* Queue pair contexts */
+ log_num_qps = fls ( arbel->limits.reserved_qps + ARBEL_MAX_QPS - 1 );
+ MLX_FILL_2 ( init_hca, 13,
+ qpc_eec_cqc_eqc_rdb_parameters.qpc_base_addr_l,
+ ( icm_offset >> 7 ),
+ qpc_eec_cqc_eqc_rdb_parameters.log_num_of_qp,
+ log_num_qps );
+ DBGC ( arbel, "Arbel %p ICM QPC base = %zx\n", arbel, icm_offset );
+ icm_offset += icm_usage ( log_num_qps, arbel->limits.qpc_entry_size );
+
+ /* Extended queue pair contexts */
+ MLX_FILL_1 ( init_hca, 25,
+ qpc_eec_cqc_eqc_rdb_parameters.eqpc_base_addr_l,
+ icm_offset );
+ DBGC ( arbel, "Arbel %p ICM EQPC base = %zx\n", arbel, icm_offset );
+ // icm_offset += icm_usage ( log_num_qps, arbel->limits.eqpc_entry_size );
+ icm_offset += icm_usage ( log_num_qps, arbel->limits.qpc_entry_size );
+
+ /* Shared receive queue contexts */
+ log_num_srqs = fls ( arbel->limits.reserved_srqs - 1 );
+ MLX_FILL_2 ( init_hca, 19,
+ qpc_eec_cqc_eqc_rdb_parameters.srqc_base_addr_l,
+ ( icm_offset >> 5 ),
+ qpc_eec_cqc_eqc_rdb_parameters.log_num_of_srq,
+ log_num_srqs );
+ DBGC ( arbel, "Arbel %p ICM SRQC base = %zx\n", arbel, icm_offset );
+ icm_offset += icm_usage ( log_num_srqs, arbel->limits.srqc_entry_size );
+
+ /* End-to-end contexts */
+ log_num_ees = fls ( arbel->limits.reserved_ees - 1 );
+ MLX_FILL_2 ( init_hca, 17,
+ qpc_eec_cqc_eqc_rdb_parameters.eec_base_addr_l,
+ ( icm_offset >> 7 ),
+ qpc_eec_cqc_eqc_rdb_parameters.log_num_of_ee,
+ log_num_ees );
+ DBGC ( arbel, "Arbel %p ICM EEC base = %zx\n", arbel, icm_offset );
+ icm_offset += icm_usage ( log_num_ees, arbel->limits.eec_entry_size );
+
+ /* Extended end-to-end contexts */
+ MLX_FILL_1 ( init_hca, 29,
+ qpc_eec_cqc_eqc_rdb_parameters.eeec_base_addr_l,
+ icm_offset );
+ DBGC ( arbel, "Arbel %p ICM EEEC base = %zx\n", arbel, icm_offset );
+ icm_offset += icm_usage ( log_num_ees, arbel->limits.eeec_entry_size );
+
+ /* Completion queue contexts */
+ log_num_cqs = fls ( arbel->limits.reserved_cqs + ARBEL_MAX_CQS - 1 );
+ MLX_FILL_2 ( init_hca, 21,
+ qpc_eec_cqc_eqc_rdb_parameters.cqc_base_addr_l,
+ ( icm_offset >> 6 ),
+ qpc_eec_cqc_eqc_rdb_parameters.log_num_of_cq,
+ log_num_cqs );
+ DBGC ( arbel, "Arbel %p ICM CQC base = %zx\n", arbel, icm_offset );
+ icm_offset += icm_usage ( log_num_cqs, arbel->limits.cqc_entry_size );
+
+ /* Memory translation table */
+ log_num_mtts = fls ( arbel->limits.reserved_mtts - 1 );
+ MLX_FILL_1 ( init_hca, 65,
+ tpt_parameters.mtt_base_addr_l, icm_offset );
+ DBGC ( arbel, "Arbel %p ICM MTT base = %zx\n", arbel, icm_offset );
+ icm_offset += icm_usage ( log_num_mtts, arbel->limits.mtt_entry_size );
+
+ /* Memory protection table */
+ log_num_mpts = fls ( arbel->limits.reserved_mrws + 1 - 1 );
+ MLX_FILL_1 ( init_hca, 61,
+ tpt_parameters.mpt_base_adr_l, icm_offset );
+ MLX_FILL_1 ( init_hca, 62,
+ tpt_parameters.log_mpt_sz, log_num_mpts );
+ DBGC ( arbel, "Arbel %p ICM MTT base = %zx\n", arbel, icm_offset );
+ icm_offset += icm_usage ( log_num_mpts, arbel->limits.mpt_entry_size );
+
+ /* RDMA something or other */
+ log_num_rdbs = fls ( arbel->limits.reserved_rdbs - 1 );
+ MLX_FILL_1 ( init_hca, 37,
+ qpc_eec_cqc_eqc_rdb_parameters.rdb_base_addr_l,
+ icm_offset );
+ DBGC ( arbel, "Arbel %p ICM RDB base = %zx\n", arbel, icm_offset );
+ icm_offset += icm_usage ( log_num_rdbs, 32 );
+
+ /* Event queue contexts */
+ log_num_eqs = 6;
+ MLX_FILL_2 ( init_hca, 33,
+ qpc_eec_cqc_eqc_rdb_parameters.eqc_base_addr_l,
+ ( icm_offset >> 6 ),
+ qpc_eec_cqc_eqc_rdb_parameters.log_num_eq,
+ log_num_eqs );
+ DBGC ( arbel, "Arbel %p ICM EQ base = %zx\n", arbel, icm_offset );
+ icm_offset += ( ( 1 << log_num_eqs ) * arbel->limits.eqc_entry_size );
+
+ /* Multicast table */
+ MLX_FILL_1 ( init_hca, 49,
+ multicast_parameters.mc_base_addr_l, icm_offset );
+ MLX_FILL_1 ( init_hca, 52,
+ multicast_parameters.log_mc_table_entry_sz,
+ fls ( sizeof ( struct arbelprm_mgm_entry ) - 1 ) );
+ MLX_FILL_1 ( init_hca, 53,
+ multicast_parameters.mc_table_hash_sz, 8 );
+ MLX_FILL_1 ( init_hca, 54,
+ multicast_parameters.log_mc_table_sz, 3 );
+ DBGC ( arbel, "Arbel %p ICM MC base = %zx\n", arbel, icm_offset );
+ icm_offset += ( 8 * sizeof ( struct arbelprm_mgm_entry ) );
+
+ arbel->icm_len = icm_offset;
+ arbel->icm_len = ( ( arbel->icm_len + 4095 ) & ~4095 );
+
+ /* Get ICM auxiliary area size */
+ memset ( &icm_size, 0, sizeof ( icm_size ) );
+ MLX_FILL_1 ( &icm_size, 1, value, arbel->icm_len );
+ if ( ( rc = arbel_cmd_set_icm_size ( arbel, &icm_size,
+ &icm_aux_size ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not set ICM size: %s\n",
+ arbel, strerror ( rc ) );
+ goto err_set_icm_size;
+ }
+ arbel->icm_aux_len = ( MLX_GET ( &icm_aux_size, value ) * 4096 );
+
+ /* Allocate ICM data and auxiliary area */
+ DBGC ( arbel, "Arbel %p requires %zd kB ICM and %zd kB AUX ICM\n",
+ arbel, ( arbel->icm_len / 1024 ),
+ ( arbel->icm_aux_len / 1024 ) );
+ arbel->icm = umalloc ( arbel->icm_len + arbel->icm_aux_len );
+ if ( ! arbel->icm ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ /* Map ICM auxiliary area */
+ memset ( &map_icm_aux, 0, sizeof ( map_icm_aux ) );
+ MLX_FILL_2 ( &map_icm_aux, 3,
+ log2size, fls ( ( arbel->icm_aux_len / 4096 ) - 1 ),
+ pa_l,
+ ( user_to_phys ( arbel->icm, arbel->icm_len ) >> 12 ) );
+ if ( ( rc = arbel_cmd_map_icm_aux ( arbel, &map_icm_aux ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not map AUX ICM: %s\n",
+ arbel, strerror ( rc ) );
+ goto err_map_icm_aux;
+ }
+
+ /* MAP ICM area */
+ memset ( &map_icm, 0, sizeof ( map_icm ) );
+ MLX_FILL_2 ( &map_icm, 3,
+ log2size, fls ( ( arbel->icm_len / 4096 ) - 1 ),
+ pa_l, ( user_to_phys ( arbel->icm, 0 ) >> 12 ) );
+ if ( ( rc = arbel_cmd_map_icm ( arbel, &map_icm ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not map ICM: %s\n",
+ arbel, strerror ( rc ) );
+ goto err_map_icm;
+ }
+
+ /* Initialise UAR context */
+ arbel->db_rec = phys_to_virt ( user_to_phys ( arbel->icm, 0 ) +
+ ( arbel->limits.reserved_uars *
+ ARBEL_PAGE_SIZE ) );
+ memset ( arbel->db_rec, 0, ARBEL_PAGE_SIZE );
+ db_rec = &arbel->db_rec[ARBEL_GROUP_SEPARATOR_DOORBELL];
+ MLX_FILL_1 ( &db_rec->qp, 1, res, ARBEL_UAR_RES_GROUP_SEP );
+
+ return 0;
+
+ arbel_cmd_unmap_icm ( arbel, ( arbel->icm_len / 4096 ) );
+ err_map_icm:
+ arbel_cmd_unmap_icm_aux ( arbel );
+ err_map_icm_aux:
+ ufree ( arbel->icm );
+ arbel->icm = UNULL;
+ err_alloc:
+ err_set_icm_size:
+ return rc;
+}
+
+/**
+ * Free ICM
+ *
+ * @v arbel Arbel device
+ */
+static void arbel_free_icm ( struct arbel *arbel ) {
+ arbel_cmd_unmap_icm ( arbel, ( arbel->icm_len / 4096 ) );
+ arbel_cmd_unmap_icm_aux ( arbel );
+ ufree ( arbel->icm );
+ arbel->icm = UNULL;
+}
+
+/***************************************************************************
+ *
+ * PCI interface
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Set up memory protection table
+ *
+ * @v arbel Arbel device
+ * @ret rc Return status code
+ */
+static int arbel_setup_mpt ( struct arbel *arbel ) {
+ struct arbelprm_mpt mpt;
+ uint32_t key;
+ int rc;
+
+ /* Derive key */
+ key = ( arbel->limits.reserved_mrws | ARBEL_MKEY_PREFIX );
+ arbel->reserved_lkey = ( ( key << 8 ) | ( key >> 24 ) );
+
+ /* Initialise memory protection table */
+ memset ( &mpt, 0, sizeof ( mpt ) );
+ MLX_FILL_4 ( &mpt, 0,
+ r_w, 1,
+ pa, 1,
+ lr, 1,
+ lw, 1 );
+ MLX_FILL_1 ( &mpt, 2, mem_key, key );
+ MLX_FILL_1 ( &mpt, 3, pd, ARBEL_GLOBAL_PD );
+ MLX_FILL_1 ( &mpt, 6, reg_wnd_len_h, 0xffffffffUL );
+ MLX_FILL_1 ( &mpt, 7, reg_wnd_len_l, 0xffffffffUL );
+ if ( ( rc = arbel_cmd_sw2hw_mpt ( arbel, arbel->limits.reserved_mrws,
+ &mpt ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not set up MPT: %s\n",
+ arbel, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @v id PCI ID
+ * @ret rc Return status code
+ */
+static int arbel_probe ( struct pci_device *pci,
+ const struct pci_device_id *id __unused ) {
+ struct arbel *arbel;
+ struct ib_device *ibdev;
+ struct arbelprm_init_hca init_hca;
+ int i;
+ int rc;
+
+ /* Allocate Arbel device */
+ arbel = zalloc ( sizeof ( *arbel ) );
+ if ( ! arbel ) {
+ rc = -ENOMEM;
+ goto err_alloc_arbel;
+ }
+ pci_set_drvdata ( pci, arbel );
+
+ /* Allocate Infiniband devices */
+ for ( i = 0 ; i < ARBEL_NUM_PORTS ; i++ ) {
+ ibdev = alloc_ibdev ( 0 );
+ if ( ! ibdev ) {
+ rc = -ENOMEM;
+ goto err_alloc_ibdev;
+ }
+ arbel->ibdev[i] = ibdev;
+ ibdev->op = &arbel_ib_operations;
+ ibdev->dev = &pci->dev;
+ ibdev->port = ( ARBEL_PORT_BASE + i );
+ ib_set_drvdata ( ibdev, arbel );
+ }
+
+ /* Fix up PCI device */
+ adjust_pci_device ( pci );
+
+ /* Get PCI BARs */
+ arbel->config = ioremap ( pci_bar_start ( pci, ARBEL_PCI_CONFIG_BAR ),
+ ARBEL_PCI_CONFIG_BAR_SIZE );
+ arbel->uar = ioremap ( ( pci_bar_start ( pci, ARBEL_PCI_UAR_BAR ) +
+ ARBEL_PCI_UAR_IDX * ARBEL_PCI_UAR_SIZE ),
+ ARBEL_PCI_UAR_SIZE );
+
+ /* Allocate space for mailboxes */
+ arbel->mailbox_in = malloc_dma ( ARBEL_MBOX_SIZE, ARBEL_MBOX_ALIGN );
+ if ( ! arbel->mailbox_in ) {
+ rc = -ENOMEM;
+ goto err_mailbox_in;
+ }
+ arbel->mailbox_out = malloc_dma ( ARBEL_MBOX_SIZE, ARBEL_MBOX_ALIGN );
+ if ( ! arbel->mailbox_out ) {
+ rc = -ENOMEM;
+ goto err_mailbox_out;
+ }
+
+ /* Start firmware */
+ if ( ( rc = arbel_start_firmware ( arbel ) ) != 0 )
+ goto err_start_firmware;
+
+ /* Get device limits */
+ if ( ( rc = arbel_get_limits ( arbel ) ) != 0 )
+ goto err_get_limits;
+
+ /* Allocate ICM */
+ memset ( &init_hca, 0, sizeof ( init_hca ) );
+ if ( ( rc = arbel_alloc_icm ( arbel, &init_hca ) ) != 0 )
+ goto err_alloc_icm;
+
+ /* Initialise HCA */
+ MLX_FILL_1 ( &init_hca, 74, uar_parameters.log_max_uars, 1 );
+ if ( ( rc = arbel_cmd_init_hca ( arbel, &init_hca ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not initialise HCA: %s\n",
+ arbel, strerror ( rc ) );
+ goto err_init_hca;
+ }
+
+ /* Set up memory protection */
+ if ( ( rc = arbel_setup_mpt ( arbel ) ) != 0 )
+ goto err_setup_mpt;
+
+ /* Register Infiniband devices */
+ for ( i = 0 ; i < ARBEL_NUM_PORTS ; i++ ) {
+ if ( ( rc = register_ibdev ( arbel->ibdev[i] ) ) != 0 ) {
+ DBGC ( arbel, "Arbel %p could not register IB "
+ "device: %s\n", arbel, strerror ( rc ) );
+ goto err_register_ibdev;
+ }
+ }
+
+ return 0;
+
+ i = ( ARBEL_NUM_PORTS - 1 );
+ err_register_ibdev:
+ for ( ; i >= 0 ; i-- )
+ unregister_ibdev ( arbel->ibdev[i] );
+ err_setup_mpt:
+ arbel_cmd_close_hca ( arbel );
+ err_init_hca:
+ arbel_free_icm ( arbel );
+ err_alloc_icm:
+ err_get_limits:
+ arbel_stop_firmware ( arbel );
+ err_start_firmware:
+ free_dma ( arbel->mailbox_out, ARBEL_MBOX_SIZE );
+ err_mailbox_out:
+ free_dma ( arbel->mailbox_in, ARBEL_MBOX_SIZE );
+ err_mailbox_in:
+ i = ( ARBEL_NUM_PORTS - 1 );
+ err_alloc_ibdev:
+ for ( ; i >= 0 ; i-- )
+ free_ibdev ( arbel->ibdev[i] );
+ free ( arbel );
+ err_alloc_arbel:
+ return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void arbel_remove ( struct pci_device *pci ) {
+ struct arbel *arbel = pci_get_drvdata ( pci );
+ int i;
+
+ for ( i = ( ARBEL_NUM_PORTS - 1 ) ; i >= 0 ; i-- )
+ unregister_ibdev ( arbel->ibdev[i] );
+ arbel_cmd_close_hca ( arbel );
+ arbel_free_icm ( arbel );
+ arbel_stop_firmware ( arbel );
+ arbel_stop_firmware ( arbel );
+ free_dma ( arbel->mailbox_out, ARBEL_MBOX_SIZE );
+ free_dma ( arbel->mailbox_in, ARBEL_MBOX_SIZE );
+ for ( i = ( ARBEL_NUM_PORTS - 1 ) ; i >= 0 ; i-- )
+ free_ibdev ( arbel->ibdev[i] );
+ free ( arbel );
+}
+
+static struct pci_device_id arbel_nics[] = {
+ PCI_ROM ( 0x15b3, 0x6282, "mt25218", "MT25218 HCA driver" ),
+ PCI_ROM ( 0x15b3, 0x6274, "mt25204", "MT25204 HCA driver" ),
+};
+
+struct pci_driver arbel_driver __pci_driver = {
+ .ids = arbel_nics,
+ .id_count = ( sizeof ( arbel_nics ) / sizeof ( arbel_nics[0] ) ),
+ .probe = arbel_probe,
+ .remove = arbel_remove,
+};
diff --git a/gpxe/src/drivers/infiniband/arbel.h b/gpxe/src/drivers/infiniband/arbel.h
new file mode 100644
index 00000000..94fa67c1
--- /dev/null
+++ b/gpxe/src/drivers/infiniband/arbel.h
@@ -0,0 +1,468 @@
+#ifndef _ARBEL_H
+#define _ARBEL_H
+
+/** @file
+ *
+ * Mellanox Arbel Infiniband HCA driver
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/uaccess.h>
+#include "mlx_bitops.h"
+#include "MT25218_PRM.h"
+
+/*
+ * Hardware constants
+ *
+ */
+
+/* Ports in existence */
+#define ARBEL_NUM_PORTS 1
+#define ARBEL_PORT_BASE 1
+
+/* PCI BARs */
+#define ARBEL_PCI_CONFIG_BAR PCI_BASE_ADDRESS_0
+#define ARBEL_PCI_CONFIG_BAR_SIZE 0x100000
+#define ARBEL_PCI_UAR_BAR PCI_BASE_ADDRESS_2
+#define ARBEL_PCI_UAR_IDX 1
+#define ARBEL_PCI_UAR_SIZE 0x1000
+
+/* UAR context table (UCE) resource types */
+#define ARBEL_UAR_RES_NONE 0x00
+#define ARBEL_UAR_RES_CQ_CI 0x01
+#define ARBEL_UAR_RES_CQ_ARM 0x02
+#define ARBEL_UAR_RES_SQ 0x03
+#define ARBEL_UAR_RES_RQ 0x04
+#define ARBEL_UAR_RES_GROUP_SEP 0x07
+
+/* Work queue entry and completion queue entry opcodes */
+#define ARBEL_OPCODE_SEND 0x0a
+#define ARBEL_OPCODE_RECV_ERROR 0xfe
+#define ARBEL_OPCODE_SEND_ERROR 0xff
+
+/* HCA command register opcodes */
+#define ARBEL_HCR_QUERY_DEV_LIM 0x0003
+#define ARBEL_HCR_QUERY_FW 0x0004
+#define ARBEL_HCR_INIT_HCA 0x0007
+#define ARBEL_HCR_CLOSE_HCA 0x0008
+#define ARBEL_HCR_INIT_IB 0x0009
+#define ARBEL_HCR_CLOSE_IB 0x000a
+#define ARBEL_HCR_SW2HW_MPT 0x000d
+#define ARBEL_HCR_MAP_EQ 0x0012
+#define ARBEL_HCR_SW2HW_EQ 0x0013
+#define ARBEL_HCR_HW2SW_EQ 0x0014
+#define ARBEL_HCR_SW2HW_CQ 0x0016
+#define ARBEL_HCR_HW2SW_CQ 0x0017
+#define ARBEL_HCR_RST2INIT_QPEE 0x0019
+#define ARBEL_HCR_INIT2RTR_QPEE 0x001a
+#define ARBEL_HCR_RTR2RTS_QPEE 0x001b
+#define ARBEL_HCR_2RST_QPEE 0x0021
+#define ARBEL_HCR_MAD_IFC 0x0024
+#define ARBEL_HCR_READ_MGM 0x0025
+#define ARBEL_HCR_WRITE_MGM 0x0026
+#define ARBEL_HCR_MGID_HASH 0x0027
+#define ARBEL_HCR_RUN_FW 0x0ff6
+#define ARBEL_HCR_DISABLE_LAM 0x0ff7
+#define ARBEL_HCR_ENABLE_LAM 0x0ff8
+#define ARBEL_HCR_UNMAP_ICM 0x0ff9
+#define ARBEL_HCR_MAP_ICM 0x0ffa
+#define ARBEL_HCR_UNMAP_ICM_AUX 0x0ffb
+#define ARBEL_HCR_MAP_ICM_AUX 0x0ffc
+#define ARBEL_HCR_SET_ICM_SIZE 0x0ffd
+#define ARBEL_HCR_UNMAP_FA 0x0ffe
+#define ARBEL_HCR_MAP_FA 0x0fff
+
+/* Service types */
+#define ARBEL_ST_UD 0x03
+
+/* MTUs */
+#define ARBEL_MTU_2048 0x04
+
+#define ARBEL_NO_EQ 64
+
+#define ARBEL_INVALID_LKEY 0x00000100UL
+
+#define ARBEL_PAGE_SIZE 4096
+
+#define ARBEL_DB_POST_SND_OFFSET 0x10
+
+/*
+ * Datatypes that seem to be missing from the autogenerated documentation
+ *
+ */
+struct arbelprm_mgm_hash_st {
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t hash[0x00010];
+ pseudo_bit_t reserved1[0x00010];
+} __attribute__ (( packed ));
+
+struct arbelprm_scalar_parameter_st {
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t value[0x00020];
+} __attribute__ (( packed ));
+
+/*
+ * Wrapper structures for hardware datatypes
+ *
+ */
+
+struct MLX_DECLARE_STRUCT ( arbelprm_access_lam );
+struct MLX_DECLARE_STRUCT ( arbelprm_completion_queue_context );
+struct MLX_DECLARE_STRUCT ( arbelprm_completion_queue_entry );
+struct MLX_DECLARE_STRUCT ( arbelprm_completion_with_error );
+struct MLX_DECLARE_STRUCT ( arbelprm_cq_arm_db_record );
+struct MLX_DECLARE_STRUCT ( arbelprm_cq_ci_db_record );
+struct MLX_DECLARE_STRUCT ( arbelprm_eqc );
+struct MLX_DECLARE_STRUCT ( arbelprm_hca_command_register );
+struct MLX_DECLARE_STRUCT ( arbelprm_init_hca );
+struct MLX_DECLARE_STRUCT ( arbelprm_init_ib );
+struct MLX_DECLARE_STRUCT ( arbelprm_mad_ifc );
+struct MLX_DECLARE_STRUCT ( arbelprm_mgm_entry );
+struct MLX_DECLARE_STRUCT ( arbelprm_mgm_hash );
+struct MLX_DECLARE_STRUCT ( arbelprm_mpt );
+struct MLX_DECLARE_STRUCT ( arbelprm_qp_db_record );
+struct MLX_DECLARE_STRUCT ( arbelprm_qp_ee_state_transitions );
+struct MLX_DECLARE_STRUCT ( arbelprm_query_dev_lim );
+struct MLX_DECLARE_STRUCT ( arbelprm_query_fw );
+struct MLX_DECLARE_STRUCT ( arbelprm_queue_pair_ee_context_entry );
+struct MLX_DECLARE_STRUCT ( arbelprm_recv_wqe_segment_next );
+struct MLX_DECLARE_STRUCT ( arbelprm_scalar_parameter );
+struct MLX_DECLARE_STRUCT ( arbelprm_send_doorbell );
+struct MLX_DECLARE_STRUCT ( arbelprm_ud_address_vector );
+struct MLX_DECLARE_STRUCT ( arbelprm_virtual_physical_mapping );
+struct MLX_DECLARE_STRUCT ( arbelprm_wqe_segment_ctrl_send );
+struct MLX_DECLARE_STRUCT ( arbelprm_wqe_segment_data_ptr );
+struct MLX_DECLARE_STRUCT ( arbelprm_wqe_segment_next );
+struct MLX_DECLARE_STRUCT ( arbelprm_wqe_segment_ud );
+
+/*
+ * Composite hardware datatypes
+ *
+ */
+
+#define ARBEL_MAX_GATHER 1
+
+struct arbelprm_ud_send_wqe {
+ struct arbelprm_wqe_segment_next next;
+ struct arbelprm_wqe_segment_ctrl_send ctrl;
+ struct arbelprm_wqe_segment_ud ud;
+ struct arbelprm_wqe_segment_data_ptr data[ARBEL_MAX_GATHER];
+} __attribute__ (( packed ));
+
+#define ARBEL_MAX_SCATTER 1
+
+struct arbelprm_recv_wqe {
+ /* The autogenerated header is inconsistent between send and
+ * receive WQEs. The "ctrl" structure for receive WQEs is
+ * defined to include the "next" structure. Since the "ctrl"
+ * part of the "ctrl" structure contains only "reserved, must
+ * be zero" bits, we ignore its definition and provide
+ * something more usable.
+ */
+ struct arbelprm_recv_wqe_segment_next next;
+ uint32_t ctrl[2]; /* All "reserved, must be zero" */
+ struct arbelprm_wqe_segment_data_ptr data[ARBEL_MAX_SCATTER];
+} __attribute__ (( packed ));
+
+union arbelprm_completion_entry {
+ struct arbelprm_completion_queue_entry normal;
+ struct arbelprm_completion_with_error error;
+} __attribute__ (( packed ));
+
+union arbelprm_doorbell_record {
+ struct arbelprm_cq_arm_db_record cq_arm;
+ struct arbelprm_cq_ci_db_record cq_ci;
+ struct arbelprm_qp_db_record qp;
+} __attribute__ (( packed ));
+
+union arbelprm_doorbell_register {
+ struct arbelprm_send_doorbell send;
+ uint32_t dword[2];
+} __attribute__ (( packed ));
+
+union arbelprm_mad {
+ struct arbelprm_mad_ifc ifc;
+ union ib_mad mad;
+} __attribute__ (( packed ));
+
+/*
+ * gPXE-specific definitions
+ *
+ */
+
+/** Arbel device limits */
+struct arbel_dev_limits {
+ /** Number of reserved QPs */
+ unsigned int reserved_qps;
+ /** QP context entry size */
+ size_t qpc_entry_size;
+ /** Extended QP context entry size */
+ size_t eqpc_entry_size;
+ /** Number of reserved SRQs */
+ unsigned int reserved_srqs;
+ /** SRQ context entry size */
+ size_t srqc_entry_size;
+ /** Number of reserved EEs */
+ unsigned int reserved_ees;
+ /** EE context entry size */
+ size_t eec_entry_size;
+ /** Extended EE context entry size */
+ size_t eeec_entry_size;
+ /** Number of reserved CQs */
+ unsigned int reserved_cqs;
+ /** CQ context entry size */
+ size_t cqc_entry_size;
+ /** Number of reserved MTTs */
+ unsigned int reserved_mtts;
+ /** MTT entry size */
+ size_t mtt_entry_size;
+ /** Number of reserved MRWs */
+ unsigned int reserved_mrws;
+ /** MPT entry size */
+ size_t mpt_entry_size;
+ /** Number of reserved RDBs */
+ unsigned int reserved_rdbs;
+ /** EQ context entry size */
+ size_t eqc_entry_size;
+ /** Number of reserved UARs */
+ unsigned int reserved_uars;
+};
+
+/** Alignment of Arbel send work queue entries */
+#define ARBEL_SEND_WQE_ALIGN 128
+
+/** An Arbel send work queue entry */
+union arbel_send_wqe {
+ struct arbelprm_ud_send_wqe ud;
+ uint8_t force_align[ARBEL_SEND_WQE_ALIGN];
+} __attribute__ (( packed ));
+
+/** An Arbel send work queue */
+struct arbel_send_work_queue {
+ /** Doorbell record number */
+ unsigned int doorbell_idx;
+ /** Work queue entries */
+ union arbel_send_wqe *wqe;
+ /** Size of work queue */
+ size_t wqe_size;
+};
+
+/** Alignment of Arbel receive work queue entries */
+#define ARBEL_RECV_WQE_ALIGN 64
+
+/** An Arbel receive work queue entry */
+union arbel_recv_wqe {
+ struct arbelprm_recv_wqe recv;
+ uint8_t force_align[ARBEL_RECV_WQE_ALIGN];
+} __attribute__ (( packed ));
+
+/** An Arbel receive work queue */
+struct arbel_recv_work_queue {
+ /** Doorbell record number */
+ unsigned int doorbell_idx;
+ /** Work queue entries */
+ union arbel_recv_wqe *wqe;
+ /** Size of work queue */
+ size_t wqe_size;
+};
+
+/** Maximum number of allocatable queue pairs
+ *
+ * This is a policy decision, not a device limit.
+ */
+#define ARBEL_MAX_QPS 8
+
+/** Base queue pair number */
+#define ARBEL_QPN_BASE 0x550000
+
+/** An Arbel queue pair */
+struct arbel_queue_pair {
+ /** Send work queue */
+ struct arbel_send_work_queue send;
+ /** Receive work queue */
+ struct arbel_recv_work_queue recv;
+};
+
+/** Maximum number of allocatable completion queues
+ *
+ * This is a policy decision, not a device limit.
+ */
+#define ARBEL_MAX_CQS 8
+
+/** An Arbel completion queue */
+struct arbel_completion_queue {
+ /** Consumer counter doorbell record number */
+ unsigned int ci_doorbell_idx;
+ /** Arm queue doorbell record number */
+ unsigned int arm_doorbell_idx;
+ /** Completion queue entries */
+ union arbelprm_completion_entry *cqe;
+ /** Size of completion queue */
+ size_t cqe_size;
+};
+
+/** An Arbel resource bitmask */
+typedef uint32_t arbel_bitmask_t;
+
+/** Size of an Arbel resource bitmask */
+#define ARBEL_BITMASK_SIZE(max_entries) \
+ ( ( (max_entries) + ( 8 * sizeof ( arbel_bitmask_t ) ) - 1 ) / \
+ ( 8 * sizeof ( arbel_bitmask_t ) ) )
+
+/** An Arbel device */
+struct arbel {
+ /** PCI configuration registers */
+ void *config;
+ /** PCI user Access Region */
+ void *uar;
+
+ /** Command input mailbox */
+ void *mailbox_in;
+ /** Command output mailbox */
+ void *mailbox_out;
+
+ /** Firmware area in external memory */
+ userptr_t firmware_area;
+ /** ICM size */
+ size_t icm_len;
+ /** ICM AUX size */
+ size_t icm_aux_len;
+ /** ICM area */
+ userptr_t icm;
+
+ /** Doorbell records */
+ union arbelprm_doorbell_record *db_rec;
+ /** Reserved LKey
+ *
+ * Used to get unrestricted memory access.
+ */
+ unsigned long reserved_lkey;
+
+ /** Completion queue in-use bitmask */
+ arbel_bitmask_t cq_inuse[ ARBEL_BITMASK_SIZE ( ARBEL_MAX_CQS ) ];
+ /** Queue pair in-use bitmask */
+ arbel_bitmask_t qp_inuse[ ARBEL_BITMASK_SIZE ( ARBEL_MAX_QPS ) ];
+
+ /** Device limits */
+ struct arbel_dev_limits limits;
+
+ /** Infiniband devices */
+ struct ib_device *ibdev[ARBEL_NUM_PORTS];
+};
+
+/** Global protection domain */
+#define ARBEL_GLOBAL_PD 0x123456
+
+/** Memory key prefix */
+#define ARBEL_MKEY_PREFIX 0x77000000UL
+
+/*
+ * HCA commands
+ *
+ */
+
+#define ARBEL_HCR_BASE 0x80680
+#define ARBEL_HCR_REG(x) ( ARBEL_HCR_BASE + 4 * (x) )
+#define ARBEL_HCR_MAX_WAIT_MS 2000
+#define ARBEL_MBOX_ALIGN 4096
+#define ARBEL_MBOX_SIZE 512
+
+/* HCA command is split into
+ *
+ * bits 11:0 Opcode
+ * bit 12 Input uses mailbox
+ * bit 13 Output uses mailbox
+ * bits 22:14 Input parameter length (in dwords)
+ * bits 31:23 Output parameter length (in dwords)
+ *
+ * Encoding the information in this way allows us to cut out several
+ * parameters to the arbel_command() call.
+ */
+#define ARBEL_HCR_IN_MBOX 0x00001000UL
+#define ARBEL_HCR_OUT_MBOX 0x00002000UL
+#define ARBEL_HCR_OPCODE( _command ) ( (_command) & 0xfff )
+#define ARBEL_HCR_IN_LEN( _command ) ( ( (_command) >> 12 ) & 0x7fc )
+#define ARBEL_HCR_OUT_LEN( _command ) ( ( (_command) >> 21 ) & 0x7fc )
+
+/** Build HCR command from component parts */
+#define ARBEL_HCR_INOUT_CMD( _opcode, _in_mbox, _in_len, \
+ _out_mbox, _out_len ) \
+ ( (_opcode) | \
+ ( (_in_mbox) ? ARBEL_HCR_IN_MBOX : 0 ) | \
+ ( ( (_in_len) / 4 ) << 14 ) | \
+ ( (_out_mbox) ? ARBEL_HCR_OUT_MBOX : 0 ) | \
+ ( ( (_out_len) / 4 ) << 23 ) )
+
+#define ARBEL_HCR_IN_CMD( _opcode, _in_mbox, _in_len ) \
+ ARBEL_HCR_INOUT_CMD ( _opcode, _in_mbox, _in_len, 0, 0 )
+
+#define ARBEL_HCR_OUT_CMD( _opcode, _out_mbox, _out_len ) \
+ ARBEL_HCR_INOUT_CMD ( _opcode, 0, 0, _out_mbox, _out_len )
+
+#define ARBEL_HCR_VOID_CMD( _opcode ) \
+ ARBEL_HCR_INOUT_CMD ( _opcode, 0, 0, 0, 0 )
+
+/*
+ * Doorbell record allocation
+ *
+ * The doorbell record map looks like:
+ *
+ * ARBEL_MAX_CQS * Arm completion queue doorbell
+ * ARBEL_MAX_QPS * Send work request doorbell
+ * Group separator
+ * ...(empty space)...
+ * ARBEL_MAX_QPS * Receive work request doorbell
+ * ARBEL_MAX_CQS * Completion queue consumer counter update doorbell
+ */
+
+#define ARBEL_MAX_DOORBELL_RECORDS 512
+#define ARBEL_GROUP_SEPARATOR_DOORBELL ( ARBEL_MAX_CQS + ARBEL_MAX_QPS )
+
+/**
+ * Get arm completion queue doorbell index
+ *
+ * @v cqn_offset Completion queue number offset
+ * @ret doorbell_idx Doorbell index
+ */
+static inline unsigned int
+arbel_cq_arm_doorbell_idx ( unsigned int cqn_offset ) {
+ return cqn_offset;
+}
+
+/**
+ * Get send work request doorbell index
+ *
+ * @v qpn_offset Queue pair number offset
+ * @ret doorbell_idx Doorbell index
+ */
+static inline unsigned int
+arbel_send_doorbell_idx ( unsigned int qpn_offset ) {
+ return ( ARBEL_MAX_CQS + qpn_offset );
+}
+
+/**
+ * Get receive work request doorbell index
+ *
+ * @v qpn_offset Queue pair number offset
+ * @ret doorbell_idx Doorbell index
+ */
+static inline unsigned int
+arbel_recv_doorbell_idx ( unsigned int qpn_offset ) {
+ return ( ARBEL_MAX_DOORBELL_RECORDS - ARBEL_MAX_CQS - qpn_offset - 1 );
+}
+
+/**
+ * Get completion queue consumer counter doorbell index
+ *
+ * @v cqn_offset Completion queue number offset
+ * @ret doorbell_idx Doorbell index
+ */
+static inline unsigned int
+arbel_cq_ci_doorbell_idx ( unsigned int cqn_offset ) {
+ return ( ARBEL_MAX_DOORBELL_RECORDS - cqn_offset - 1 );
+}
+
+#endif /* _ARBEL_H */
diff --git a/gpxe/src/drivers/infiniband/hermon.c b/gpxe/src/drivers/infiniband/hermon.c
new file mode 100644
index 00000000..c10559f9
--- /dev/null
+++ b/gpxe/src/drivers/infiniband/hermon.c
@@ -0,0 +1,2016 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ * Copyright (C) 2008 Mellanox Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/pci.h>
+#include <gpxe/malloc.h>
+#include <gpxe/umalloc.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/infiniband.h>
+#include "hermon.h"
+
+/**
+ * @file
+ *
+ * Mellanox Hermon Infiniband HCA
+ *
+ */
+
+/***************************************************************************
+ *
+ * Queue number allocation
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Allocate offsets within usage bitmask
+ *
+ * @v bits Usage bitmask
+ * @v bits_len Length of usage bitmask
+ * @v num_bits Number of contiguous bits to allocate within bitmask
+ * @ret bit First free bit within bitmask, or negative error
+ */
+static int hermon_bitmask_alloc ( hermon_bitmask_t *bits,
+ unsigned int bits_len,
+ unsigned int num_bits ) {
+ unsigned int bit = 0;
+ hermon_bitmask_t mask = 1;
+ unsigned int found = 0;
+
+ /* Search bits for num_bits contiguous free bits */
+ while ( bit < bits_len ) {
+ if ( ( mask & *bits ) == 0 ) {
+ if ( ++found == num_bits )
+ goto found;
+ } else {
+ found = 0;
+ }
+ bit++;
+ mask = ( mask << 1 ) | ( mask >> ( 8 * sizeof ( mask ) - 1 ) );
+ if ( mask == 1 )
+ bits++;
+ }
+ return -ENFILE;
+
+ found:
+ /* Mark bits as in-use */
+ do {
+ *bits |= mask;
+ if ( mask == 1 )
+ bits--;
+ mask = ( mask >> 1 ) | ( mask << ( 8 * sizeof ( mask ) - 1 ) );
+ } while ( --found );
+
+ return ( bit - num_bits + 1 );
+}
+
+/**
+ * Free offsets within usage bitmask
+ *
+ * @v bits Usage bitmask
+ * @v bit Starting bit within bitmask
+ * @v num_bits Number of contiguous bits to free within bitmask
+ */
+static void hermon_bitmask_free ( hermon_bitmask_t *bits,
+ int bit, unsigned int num_bits ) {
+ hermon_bitmask_t mask;
+
+ for ( ; num_bits ; bit++, num_bits-- ) {
+ mask = ( 1 << ( bit % ( 8 * sizeof ( mask ) ) ) );
+ bits[ ( bit / ( 8 * sizeof ( mask ) ) ) ] &= ~mask;
+ }
+}
+
+/***************************************************************************
+ *
+ * HCA commands
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Wait for Hermon command completion
+ *
+ * @v hermon Hermon device
+ * @v hcr HCA command registers
+ * @ret rc Return status code
+ */
+static int hermon_cmd_wait ( struct hermon *hermon,
+ struct hermonprm_hca_command_register *hcr ) {
+ unsigned int wait;
+
+ for ( wait = HERMON_HCR_MAX_WAIT_MS ; wait ; wait-- ) {
+ hcr->u.dwords[6] =
+ readl ( hermon->config + HERMON_HCR_REG ( 6 ) );
+ if ( ( MLX_GET ( hcr, go ) == 0 ) &&
+ ( MLX_GET ( hcr, t ) == hermon->toggle ) )
+ return 0;
+ mdelay ( 1 );
+ }
+ return -EBUSY;
+}
+
+/**
+ * Issue HCA command
+ *
+ * @v hermon Hermon device
+ * @v command Command opcode, flags and input/output lengths
+ * @v op_mod Opcode modifier (0 if no modifier applicable)
+ * @v in Input parameters
+ * @v in_mod Input modifier (0 if no modifier applicable)
+ * @v out Output parameters
+ * @ret rc Return status code
+ */
+static int hermon_cmd ( struct hermon *hermon, unsigned long command,
+ unsigned int op_mod, const void *in,
+ unsigned int in_mod, void *out ) {
+ struct hermonprm_hca_command_register hcr;
+ unsigned int opcode = HERMON_HCR_OPCODE ( command );
+ size_t in_len = HERMON_HCR_IN_LEN ( command );
+ size_t out_len = HERMON_HCR_OUT_LEN ( command );
+ void *in_buffer;
+ void *out_buffer;
+ unsigned int status;
+ unsigned int i;
+ int rc;
+
+ assert ( in_len <= HERMON_MBOX_SIZE );
+ assert ( out_len <= HERMON_MBOX_SIZE );
+
+ DBGC2 ( hermon, "Hermon %p command %02x in %zx%s out %zx%s\n",
+ hermon, opcode, in_len,
+ ( ( command & HERMON_HCR_IN_MBOX ) ? "(mbox)" : "" ), out_len,
+ ( ( command & HERMON_HCR_OUT_MBOX ) ? "(mbox)" : "" ) );
+
+ /* Check that HCR is free */
+ if ( ( rc = hermon_cmd_wait ( hermon, &hcr ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p command interface locked\n",
+ hermon );
+ return rc;
+ }
+
+ /* Flip HCR toggle */
+ hermon->toggle = ( 1 - hermon->toggle );
+
+ /* Prepare HCR */
+ memset ( &hcr, 0, sizeof ( hcr ) );
+ in_buffer = &hcr.u.dwords[0];
+ if ( in_len && ( command & HERMON_HCR_IN_MBOX ) ) {
+ in_buffer = hermon->mailbox_in;
+ MLX_FILL_1 ( &hcr, 1, in_param_l, virt_to_bus ( in_buffer ) );
+ }
+ memcpy ( in_buffer, in, in_len );
+ MLX_FILL_1 ( &hcr, 2, input_modifier, in_mod );
+ out_buffer = &hcr.u.dwords[3];
+ if ( out_len && ( command & HERMON_HCR_OUT_MBOX ) ) {
+ out_buffer = hermon->mailbox_out;
+ MLX_FILL_1 ( &hcr, 4, out_param_l,
+ virt_to_bus ( out_buffer ) );
+ }
+ MLX_FILL_4 ( &hcr, 6,
+ opcode, opcode,
+ opcode_modifier, op_mod,
+ go, 1,
+ t, hermon->toggle );
+ DBGC ( hermon, "Hermon %p issuing command:\n", hermon );
+ DBGC_HDA ( hermon, virt_to_phys ( hermon->config + HERMON_HCR_BASE ),
+ &hcr, sizeof ( hcr ) );
+ if ( in_len && ( command & HERMON_HCR_IN_MBOX ) ) {
+ DBGC2 ( hermon, "Input mailbox:\n" );
+ DBGC2_HDA ( hermon, virt_to_phys ( in_buffer ), in_buffer,
+ ( ( in_len < 512 ) ? in_len : 512 ) );
+ }
+
+ /* Issue command */
+ for ( i = 0 ; i < ( sizeof ( hcr ) / sizeof ( hcr.u.dwords[0] ) ) ;
+ i++ ) {
+ writel ( hcr.u.dwords[i],
+ hermon->config + HERMON_HCR_REG ( i ) );
+ barrier();
+ }
+
+ /* Wait for command completion */
+ if ( ( rc = hermon_cmd_wait ( hermon, &hcr ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p timed out waiting for command:\n",
+ hermon );
+ DBGC_HDA ( hermon,
+ virt_to_phys ( hermon->config + HERMON_HCR_BASE ),
+ &hcr, sizeof ( hcr ) );
+ return rc;
+ }
+
+ /* Check command status */
+ status = MLX_GET ( &hcr, status );
+ if ( status != 0 ) {
+ DBGC ( hermon, "Hermon %p command failed with status %02x:\n",
+ hermon, status );
+ DBGC_HDA ( hermon,
+ virt_to_phys ( hermon->config + HERMON_HCR_BASE ),
+ &hcr, sizeof ( hcr ) );
+ return -EIO;
+ }
+
+ /* Read output parameters, if any */
+ hcr.u.dwords[3] = readl ( hermon->config + HERMON_HCR_REG ( 3 ) );
+ hcr.u.dwords[4] = readl ( hermon->config + HERMON_HCR_REG ( 4 ) );
+ memcpy ( out, out_buffer, out_len );
+ if ( out_len ) {
+ DBGC2 ( hermon, "Output%s:\n",
+ ( command & HERMON_HCR_OUT_MBOX ) ? " mailbox" : "" );
+ DBGC2_HDA ( hermon, virt_to_phys ( out_buffer ), out_buffer,
+ ( ( out_len < 512 ) ? out_len : 512 ) );
+ }
+
+ return 0;
+}
+
+static inline int
+hermon_cmd_query_dev_cap ( struct hermon *hermon,
+ struct hermonprm_query_dev_cap *dev_cap ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_OUT_CMD ( HERMON_HCR_QUERY_DEV_CAP,
+ 1, sizeof ( *dev_cap ) ),
+ 0, NULL, 0, dev_cap );
+}
+
+static inline int
+hermon_cmd_query_fw ( struct hermon *hermon, struct hermonprm_query_fw *fw ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_OUT_CMD ( HERMON_HCR_QUERY_FW,
+ 1, sizeof ( *fw ) ),
+ 0, NULL, 0, fw );
+}
+
+static inline int
+hermon_cmd_init_hca ( struct hermon *hermon,
+ const struct hermonprm_init_hca *init_hca ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_INIT_HCA,
+ 1, sizeof ( *init_hca ) ),
+ 0, init_hca, 0, NULL );
+}
+
+static inline int
+hermon_cmd_close_hca ( struct hermon *hermon ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_VOID_CMD ( HERMON_HCR_CLOSE_HCA ),
+ 0, NULL, 0, NULL );
+}
+
+static inline int
+hermon_cmd_init_port ( struct hermon *hermon, unsigned int port,
+ const struct hermonprm_init_port *init_port ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_INIT_PORT,
+ 1, sizeof ( *init_port ) ),
+ 0, init_port, port, NULL );
+}
+
+static inline int
+hermon_cmd_close_port ( struct hermon *hermon, unsigned int port ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_VOID_CMD ( HERMON_HCR_CLOSE_PORT ),
+ 0, NULL, port, NULL );
+}
+
+static inline int
+hermon_cmd_sw2hw_mpt ( struct hermon *hermon, unsigned int index,
+ const struct hermonprm_mpt *mpt ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_SW2HW_MPT,
+ 1, sizeof ( *mpt ) ),
+ 0, mpt, index, NULL );
+}
+
+static inline int
+hermon_cmd_write_mtt ( struct hermon *hermon,
+ const struct hermonprm_write_mtt *write_mtt ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_WRITE_MTT,
+ 1, sizeof ( *write_mtt ) ),
+ 0, write_mtt, 1, NULL );
+}
+
+static inline int
+hermon_cmd_sw2hw_eq ( struct hermon *hermon, unsigned int index,
+ const struct hermonprm_eqc *eqc ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_SW2HW_EQ,
+ 1, sizeof ( *eqc ) ),
+ 0, eqc, index, NULL );
+}
+
+static inline int
+hermon_cmd_hw2sw_eq ( struct hermon *hermon, unsigned int index ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_VOID_CMD ( HERMON_HCR_HW2SW_EQ ),
+ 1, NULL, index, NULL );
+}
+
+static inline int
+hermon_cmd_sw2hw_cq ( struct hermon *hermon, unsigned long cqn,
+ const struct hermonprm_completion_queue_context *cqctx ){
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_SW2HW_CQ,
+ 1, sizeof ( *cqctx ) ),
+ 0, cqctx, cqn, NULL );
+}
+
+static inline int
+hermon_cmd_hw2sw_cq ( struct hermon *hermon, unsigned long cqn,
+ struct hermonprm_completion_queue_context *cqctx) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_OUT_CMD ( HERMON_HCR_HW2SW_CQ,
+ 1, sizeof ( *cqctx ) ),
+ 0, NULL, cqn, cqctx );
+}
+
+static inline int
+hermon_cmd_rst2init_qp ( struct hermon *hermon, unsigned long qpn,
+ const struct hermonprm_qp_ee_state_transitions *ctx ){
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_RST2INIT_QP,
+ 1, sizeof ( *ctx ) ),
+ 0, ctx, qpn, NULL );
+}
+
+static inline int
+hermon_cmd_init2rtr_qp ( struct hermon *hermon, unsigned long qpn,
+ const struct hermonprm_qp_ee_state_transitions *ctx ){
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_INIT2RTR_QP,
+ 1, sizeof ( *ctx ) ),
+ 0, ctx, qpn, NULL );
+}
+
+static inline int
+hermon_cmd_rtr2rts_qp ( struct hermon *hermon, unsigned long qpn,
+ const struct hermonprm_qp_ee_state_transitions *ctx ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_RTR2RTS_QP,
+ 1, sizeof ( *ctx ) ),
+ 0, ctx, qpn, NULL );
+}
+
+static inline int
+hermon_cmd_2rst_qp ( struct hermon *hermon, unsigned long qpn ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_VOID_CMD ( HERMON_HCR_2RST_QP ),
+ 0x03, NULL, qpn, NULL );
+}
+
+static inline int
+hermon_cmd_mad_ifc ( struct hermon *hermon, unsigned int port,
+ union hermonprm_mad *mad ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_INOUT_CMD ( HERMON_HCR_MAD_IFC,
+ 1, sizeof ( *mad ),
+ 1, sizeof ( *mad ) ),
+ 0x03, mad, port, mad );
+}
+
+static inline int
+hermon_cmd_read_mcg ( struct hermon *hermon, unsigned int index,
+ struct hermonprm_mcg_entry *mcg ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_OUT_CMD ( HERMON_HCR_READ_MCG,
+ 1, sizeof ( *mcg ) ),
+ 0, NULL, index, mcg );
+}
+
+static inline int
+hermon_cmd_write_mcg ( struct hermon *hermon, unsigned int index,
+ const struct hermonprm_mcg_entry *mcg ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_WRITE_MCG,
+ 1, sizeof ( *mcg ) ),
+ 0, mcg, index, NULL );
+}
+
+static inline int
+hermon_cmd_mgid_hash ( struct hermon *hermon, const struct ib_gid *gid,
+ struct hermonprm_mgm_hash *hash ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_INOUT_CMD ( HERMON_HCR_MGID_HASH,
+ 1, sizeof ( *gid ),
+ 0, sizeof ( *hash ) ),
+ 0, gid, 0, hash );
+}
+
+static inline int
+hermon_cmd_run_fw ( struct hermon *hermon ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_VOID_CMD ( HERMON_HCR_RUN_FW ),
+ 0, NULL, 0, NULL );
+}
+
+static inline int
+hermon_cmd_unmap_icm ( struct hermon *hermon, unsigned int page_count,
+ const struct hermonprm_scalar_parameter *offset ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_UNMAP_ICM,
+ 0, sizeof ( *offset ) ),
+ 0, offset, page_count, NULL );
+}
+
+static inline int
+hermon_cmd_map_icm ( struct hermon *hermon,
+ const struct hermonprm_virtual_physical_mapping *map ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_MAP_ICM,
+ 1, sizeof ( *map ) ),
+ 0, map, 1, NULL );
+}
+
+static inline int
+hermon_cmd_unmap_icm_aux ( struct hermon *hermon ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_VOID_CMD ( HERMON_HCR_UNMAP_ICM_AUX ),
+ 0, NULL, 0, NULL );
+}
+
+static inline int
+hermon_cmd_map_icm_aux ( struct hermon *hermon,
+ const struct hermonprm_virtual_physical_mapping *map ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_MAP_ICM_AUX,
+ 1, sizeof ( *map ) ),
+ 0, map, 1, NULL );
+}
+
+static inline int
+hermon_cmd_set_icm_size ( struct hermon *hermon,
+ const struct hermonprm_scalar_parameter *icm_size,
+ struct hermonprm_scalar_parameter *icm_aux_size ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_INOUT_CMD ( HERMON_HCR_SET_ICM_SIZE,
+ 0, sizeof ( *icm_size ),
+ 0, sizeof (*icm_aux_size) ),
+ 0, icm_size, 0, icm_aux_size );
+}
+
+static inline int
+hermon_cmd_unmap_fa ( struct hermon *hermon ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_VOID_CMD ( HERMON_HCR_UNMAP_FA ),
+ 0, NULL, 0, NULL );
+}
+
+static inline int
+hermon_cmd_map_fa ( struct hermon *hermon,
+ const struct hermonprm_virtual_physical_mapping *map ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_IN_CMD ( HERMON_HCR_MAP_FA,
+ 1, sizeof ( *map ) ),
+ 0, map, 1, NULL );
+}
+
+/***************************************************************************
+ *
+ * Memory translation table operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Allocate MTT entries
+ *
+ * @v hermon Hermon device
+ * @v memory Memory to map into MTT
+ * @v len Length of memory to map
+ * @v mtt MTT descriptor to fill in
+ * @ret rc Return status code
+ */
+static int hermon_alloc_mtt ( struct hermon *hermon,
+ const void *memory, size_t len,
+ struct hermon_mtt *mtt ) {
+ struct hermonprm_write_mtt write_mtt;
+ physaddr_t start;
+ unsigned int page_offset;
+ unsigned int num_pages;
+ int mtt_offset;
+ unsigned int mtt_base_addr;
+ unsigned int i;
+ int rc;
+
+ /* Find available MTT entries */
+ start = virt_to_phys ( memory );
+ page_offset = ( start & ( HERMON_PAGE_SIZE - 1 ) );
+ start -= page_offset;
+ len += page_offset;
+ num_pages = ( ( len + HERMON_PAGE_SIZE - 1 ) / HERMON_PAGE_SIZE );
+ mtt_offset = hermon_bitmask_alloc ( hermon->mtt_inuse, HERMON_MAX_MTTS,
+ num_pages );
+ if ( mtt_offset < 0 ) {
+ DBGC ( hermon, "Hermon %p could not allocate %d MTT entries\n",
+ hermon, num_pages );
+ rc = mtt_offset;
+ goto err_mtt_offset;
+ }
+ mtt_base_addr = ( ( hermon->cap.reserved_mtts + mtt_offset ) *
+ hermon->cap.mtt_entry_size );
+
+ /* Fill in MTT structure */
+ mtt->mtt_offset = mtt_offset;
+ mtt->num_pages = num_pages;
+ mtt->mtt_base_addr = mtt_base_addr;
+ mtt->page_offset = page_offset;
+
+ /* Construct and issue WRITE_MTT commands */
+ for ( i = 0 ; i < num_pages ; i++ ) {
+ memset ( &write_mtt, 0, sizeof ( write_mtt ) );
+ MLX_FILL_1 ( &write_mtt.mtt_base_addr, 1,
+ value, mtt_base_addr );
+ MLX_FILL_2 ( &write_mtt.mtt, 1,
+ p, 1,
+ ptag_l, ( start >> 3 ) );
+ if ( ( rc = hermon_cmd_write_mtt ( hermon,
+ &write_mtt ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not write MTT at %x\n",
+ hermon, mtt_base_addr );
+ goto err_write_mtt;
+ }
+ start += HERMON_PAGE_SIZE;
+ mtt_base_addr += hermon->cap.mtt_entry_size;
+ }
+
+ return 0;
+
+ err_write_mtt:
+ hermon_bitmask_free ( hermon->mtt_inuse, mtt_offset, num_pages );
+ err_mtt_offset:
+ return rc;
+}
+
+/**
+ * Free MTT entries
+ *
+ * @v hermon Hermon device
+ * @v mtt MTT descriptor
+ */
+static void hermon_free_mtt ( struct hermon *hermon,
+ struct hermon_mtt *mtt ) {
+ hermon_bitmask_free ( hermon->mtt_inuse, mtt->mtt_offset,
+ mtt->num_pages );
+}
+
+/***************************************************************************
+ *
+ * Completion queue operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Create completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ * @ret rc Return status code
+ */
+static int hermon_create_cq ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermon_completion_queue *hermon_cq;
+ struct hermonprm_completion_queue_context cqctx;
+ int cqn_offset;
+ unsigned int i;
+ int rc;
+
+ /* Find a free completion queue number */
+ cqn_offset = hermon_bitmask_alloc ( hermon->cq_inuse,
+ HERMON_MAX_CQS, 1 );
+ if ( cqn_offset < 0 ) {
+ DBGC ( hermon, "Hermon %p out of completion queues\n",
+ hermon );
+ rc = cqn_offset;
+ goto err_cqn_offset;
+ }
+ cq->cqn = ( hermon->cap.reserved_cqs + cqn_offset );
+
+ /* Allocate control structures */
+ hermon_cq = zalloc ( sizeof ( *hermon_cq ) );
+ if ( ! hermon_cq ) {
+ rc = -ENOMEM;
+ goto err_hermon_cq;
+ }
+
+ /* Allocate completion queue itself */
+ hermon_cq->cqe_size = ( cq->num_cqes * sizeof ( hermon_cq->cqe[0] ) );
+ hermon_cq->cqe = malloc_dma ( hermon_cq->cqe_size,
+ sizeof ( hermon_cq->cqe[0] ) );
+ if ( ! hermon_cq->cqe ) {
+ rc = -ENOMEM;
+ goto err_cqe;
+ }
+ memset ( hermon_cq->cqe, 0, hermon_cq->cqe_size );
+ for ( i = 0 ; i < cq->num_cqes ; i++ ) {
+ MLX_FILL_1 ( &hermon_cq->cqe[i].normal, 7, owner, 1 );
+ }
+ barrier();
+
+ /* Allocate MTT entries */
+ if ( ( rc = hermon_alloc_mtt ( hermon, hermon_cq->cqe,
+ hermon_cq->cqe_size,
+ &hermon_cq->mtt ) ) != 0 )
+ goto err_alloc_mtt;
+
+ /* Hand queue over to hardware */
+ memset ( &cqctx, 0, sizeof ( cqctx ) );
+ MLX_FILL_1 ( &cqctx, 0, st, 0xa /* "Event fired" */ );
+ MLX_FILL_1 ( &cqctx, 2,
+ page_offset, ( hermon_cq->mtt.page_offset >> 5 ) );
+ MLX_FILL_2 ( &cqctx, 3,
+ usr_page, HERMON_UAR_PAGE,
+ log_cq_size, fls ( cq->num_cqes - 1 ) );
+ MLX_FILL_1 ( &cqctx, 7, mtt_base_addr_l,
+ ( hermon_cq->mtt.mtt_base_addr >> 3 ) );
+ MLX_FILL_1 ( &cqctx, 15, db_record_addr_l,
+ ( virt_to_phys ( &hermon_cq->doorbell ) >> 3 ) );
+ if ( ( rc = hermon_cmd_sw2hw_cq ( hermon, cq->cqn, &cqctx ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p SW2HW_CQ failed: %s\n",
+ hermon, strerror ( rc ) );
+ goto err_sw2hw_cq;
+ }
+
+ DBGC ( hermon, "Hermon %p CQN %#lx ring at [%p,%p)\n",
+ hermon, cq->cqn, hermon_cq->cqe,
+ ( ( ( void * ) hermon_cq->cqe ) + hermon_cq->cqe_size ) );
+ ib_cq_set_drvdata ( cq, hermon_cq );
+ return 0;
+
+ err_sw2hw_cq:
+ hermon_free_mtt ( hermon, &hermon_cq->mtt );
+ err_alloc_mtt:
+ free_dma ( hermon_cq->cqe, hermon_cq->cqe_size );
+ err_cqe:
+ free ( hermon_cq );
+ err_hermon_cq:
+ hermon_bitmask_free ( hermon->cq_inuse, cqn_offset, 1 );
+ err_cqn_offset:
+ return rc;
+}
+
+/**
+ * Destroy completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ */
+static void hermon_destroy_cq ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermon_completion_queue *hermon_cq = ib_cq_get_drvdata ( cq );
+ struct hermonprm_completion_queue_context cqctx;
+ int cqn_offset;
+ int rc;
+
+ /* Take ownership back from hardware */
+ if ( ( rc = hermon_cmd_hw2sw_cq ( hermon, cq->cqn, &cqctx ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p FATAL HW2SW_CQ failed on CQN %#lx: "
+ "%s\n", hermon, cq->cqn, strerror ( rc ) );
+ /* Leak memory and return; at least we avoid corruption */
+ return;
+ }
+
+ /* Free MTT entries */
+ hermon_free_mtt ( hermon, &hermon_cq->mtt );
+
+ /* Free memory */
+ free_dma ( hermon_cq->cqe, hermon_cq->cqe_size );
+ free ( hermon_cq );
+
+ /* Mark queue number as free */
+ cqn_offset = ( cq->cqn - hermon->cap.reserved_cqs );
+ hermon_bitmask_free ( hermon->cq_inuse, cqn_offset, 1 );
+
+ ib_cq_set_drvdata ( cq, NULL );
+}
+
+/***************************************************************************
+ *
+ * Queue pair operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Create queue pair
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @ret rc Return status code
+ */
+static int hermon_create_qp ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermon_queue_pair *hermon_qp;
+ struct hermonprm_qp_ee_state_transitions qpctx;
+ int qpn_offset;
+ int rc;
+
+ /* Find a free queue pair number */
+ qpn_offset = hermon_bitmask_alloc ( hermon->qp_inuse,
+ HERMON_MAX_QPS, 1 );
+ if ( qpn_offset < 0 ) {
+ DBGC ( hermon, "Hermon %p out of queue pairs\n", hermon );
+ rc = qpn_offset;
+ goto err_qpn_offset;
+ }
+ qp->qpn = ( HERMON_QPN_BASE + hermon->cap.reserved_qps +
+ qpn_offset );
+
+ /* Allocate control structures */
+ hermon_qp = zalloc ( sizeof ( *hermon_qp ) );
+ if ( ! hermon_qp ) {
+ rc = -ENOMEM;
+ goto err_hermon_qp;
+ }
+
+ /* Allocate work queue buffer */
+ hermon_qp->send.num_wqes = ( qp->send.num_wqes /* headroom */ + 1 +
+ ( 2048 / sizeof ( hermon_qp->send.wqe[0] ) ) );
+ hermon_qp->send.num_wqes =
+ ( 1 << fls ( hermon_qp->send.num_wqes - 1 ) ); /* round up */
+ hermon_qp->send.wqe_size = ( hermon_qp->send.num_wqes *
+ sizeof ( hermon_qp->send.wqe[0] ) );
+ hermon_qp->recv.wqe_size = ( qp->recv.num_wqes *
+ sizeof ( hermon_qp->recv.wqe[0] ) );
+ hermon_qp->wqe_size = ( hermon_qp->send.wqe_size +
+ hermon_qp->recv.wqe_size );
+ hermon_qp->wqe = malloc_dma ( hermon_qp->wqe_size,
+ sizeof ( hermon_qp->send.wqe[0] ) );
+ if ( ! hermon_qp->wqe ) {
+ rc = -ENOMEM;
+ goto err_alloc_wqe;
+ }
+ hermon_qp->send.wqe = hermon_qp->wqe;
+ memset ( hermon_qp->send.wqe, 0xff, hermon_qp->send.wqe_size );
+ hermon_qp->recv.wqe = ( hermon_qp->wqe + hermon_qp->send.wqe_size );
+ memset ( hermon_qp->recv.wqe, 0, hermon_qp->recv.wqe_size );
+
+ /* Allocate MTT entries */
+ if ( ( rc = hermon_alloc_mtt ( hermon, hermon_qp->wqe,
+ hermon_qp->wqe_size,
+ &hermon_qp->mtt ) ) != 0 ) {
+ goto err_alloc_mtt;
+ }
+
+ /* Transition queue to INIT state */
+ memset ( &qpctx, 0, sizeof ( qpctx ) );
+ MLX_FILL_2 ( &qpctx, 2,
+ qpc_eec_data.pm_state, 0x03 /* Always 0x03 for UD */,
+ qpc_eec_data.st, HERMON_ST_UD );
+ MLX_FILL_1 ( &qpctx, 3, qpc_eec_data.pd, HERMON_GLOBAL_PD );
+ MLX_FILL_4 ( &qpctx, 4,
+ qpc_eec_data.log_rq_size, fls ( qp->recv.num_wqes - 1 ),
+ qpc_eec_data.log_rq_stride,
+ ( fls ( sizeof ( hermon_qp->recv.wqe[0] ) - 1 ) - 4 ),
+ qpc_eec_data.log_sq_size,
+ fls ( hermon_qp->send.num_wqes - 1 ),
+ qpc_eec_data.log_sq_stride,
+ ( fls ( sizeof ( hermon_qp->send.wqe[0] ) - 1 ) - 4 ) );
+ MLX_FILL_1 ( &qpctx, 5,
+ qpc_eec_data.usr_page, HERMON_UAR_PAGE );
+ MLX_FILL_1 ( &qpctx, 33, qpc_eec_data.cqn_snd, qp->send.cq->cqn );
+ MLX_FILL_1 ( &qpctx, 38, qpc_eec_data.page_offset,
+ ( hermon_qp->mtt.page_offset >> 6 ) );
+ MLX_FILL_1 ( &qpctx, 41, qpc_eec_data.cqn_rcv, qp->recv.cq->cqn );
+ MLX_FILL_1 ( &qpctx, 43, qpc_eec_data.db_record_addr_l,
+ ( virt_to_phys ( &hermon_qp->recv.doorbell ) >> 2 ) );
+ MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey );
+ MLX_FILL_1 ( &qpctx, 53, qpc_eec_data.mtt_base_addr_l,
+ ( hermon_qp->mtt.mtt_base_addr >> 3 ) );
+ if ( ( rc = hermon_cmd_rst2init_qp ( hermon, qp->qpn,
+ &qpctx ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p RST2INIT_QP failed: %s\n",
+ hermon, strerror ( rc ) );
+ goto err_rst2init_qp;
+ }
+
+ /* Transition queue to RTR state */
+ memset ( &qpctx, 0, sizeof ( qpctx ) );
+ MLX_FILL_2 ( &qpctx, 4,
+ qpc_eec_data.mtu, HERMON_MTU_2048,
+ qpc_eec_data.msg_max, 11 /* 2^11 = 2048 */ );
+ MLX_FILL_1 ( &qpctx, 16,
+ qpc_eec_data.primary_address_path.sched_queue,
+ ( 0x83 /* default policy */ |
+ ( ( ibdev->port - 1 ) << 6 ) ) );
+ if ( ( rc = hermon_cmd_init2rtr_qp ( hermon, qp->qpn,
+ &qpctx ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p INIT2RTR_QP failed: %s\n",
+ hermon, strerror ( rc ) );
+ goto err_init2rtr_qp;
+ }
+ memset ( &qpctx, 0, sizeof ( qpctx ) );
+ if ( ( rc = hermon_cmd_rtr2rts_qp ( hermon, qp->qpn, &qpctx ) ) != 0 ){
+ DBGC ( hermon, "Hermon %p RTR2RTS_QP failed: %s\n",
+ hermon, strerror ( rc ) );
+ goto err_rtr2rts_qp;
+ }
+
+ DBGC ( hermon, "Hermon %p QPN %#lx send ring at [%p,%p)\n",
+ hermon, qp->qpn, hermon_qp->send.wqe,
+ ( ((void *)hermon_qp->send.wqe ) + hermon_qp->send.wqe_size ) );
+ DBGC ( hermon, "Hermon %p QPN %#lx receive ring at [%p,%p)\n",
+ hermon, qp->qpn, hermon_qp->recv.wqe,
+ ( ((void *)hermon_qp->recv.wqe ) + hermon_qp->recv.wqe_size ) );
+ ib_qp_set_drvdata ( qp, hermon_qp );
+ return 0;
+
+ err_rtr2rts_qp:
+ err_init2rtr_qp:
+ hermon_cmd_2rst_qp ( hermon, qp->qpn );
+ err_rst2init_qp:
+ hermon_free_mtt ( hermon, &hermon_qp->mtt );
+ err_alloc_mtt:
+ free_dma ( hermon_qp->wqe, hermon_qp->wqe_size );
+ err_alloc_wqe:
+ free ( hermon_qp );
+ err_hermon_qp:
+ hermon_bitmask_free ( hermon->qp_inuse, qpn_offset, 1 );
+ err_qpn_offset:
+ return rc;
+}
+
+/**
+ * Destroy queue pair
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ */
+static void hermon_destroy_qp ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp );
+ int qpn_offset;
+ int rc;
+
+ /* Take ownership back from hardware */
+ if ( ( rc = hermon_cmd_2rst_qp ( hermon, qp->qpn ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p FATAL 2RST_QP failed on QPN %#lx: "
+ "%s\n", hermon, qp->qpn, strerror ( rc ) );
+ /* Leak memory and return; at least we avoid corruption */
+ return;
+ }
+
+ /* Free MTT entries */
+ hermon_free_mtt ( hermon, &hermon_qp->mtt );
+
+ /* Free memory */
+ free_dma ( hermon_qp->wqe, hermon_qp->wqe_size );
+ free ( hermon_qp );
+
+ /* Mark queue number as free */
+ qpn_offset = ( qp->qpn - HERMON_QPN_BASE -
+ hermon->cap.reserved_qps );
+ hermon_bitmask_free ( hermon->qp_inuse, qpn_offset, 1 );
+
+ ib_qp_set_drvdata ( qp, NULL );
+}
+
+/***************************************************************************
+ *
+ * Work request operations
+ *
+ ***************************************************************************
+ */
+
+/** GID used for GID-less send work queue entries */
+static const struct ib_gid hermon_no_gid = {
+ { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
+};
+
+/**
+ * Post send work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v av Address vector
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int hermon_post_send ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct ib_address_vector *av,
+ struct io_buffer *iobuf ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp );
+ struct ib_work_queue *wq = &qp->send;
+ struct hermon_send_work_queue *hermon_send_wq = &hermon_qp->send;
+ struct hermonprm_ud_send_wqe *wqe;
+ const struct ib_gid *gid;
+ union hermonprm_doorbell_register db_reg;
+ unsigned int wqe_idx_mask;
+
+ /* Allocate work queue entry */
+ wqe_idx_mask = ( wq->num_wqes - 1 );
+ if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) {
+ DBGC ( hermon, "Hermon %p send queue full", hermon );
+ return -ENOBUFS;
+ }
+ wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf;
+ wqe = &hermon_send_wq->wqe[ wq->next_idx &
+ ( hermon_send_wq->num_wqes - 1 ) ].ud;
+
+ /* Construct work queue entry */
+ memset ( ( ( ( void * ) wqe ) + 4 /* avoid ctrl.owner */ ), 0,
+ ( sizeof ( *wqe ) - 4 ) );
+ MLX_FILL_1 ( &wqe->ctrl, 1, ds, ( sizeof ( *wqe ) / 16 ) );
+ MLX_FILL_1 ( &wqe->ctrl, 2, c, 0x03 /* generate completion */ );
+ MLX_FILL_2 ( &wqe->ud, 0,
+ ud_address_vector.pd, HERMON_GLOBAL_PD,
+ ud_address_vector.port_number, ibdev->port );
+ MLX_FILL_2 ( &wqe->ud, 1,
+ ud_address_vector.rlid, av->dlid,
+ ud_address_vector.g, av->gid_present );
+ MLX_FILL_1 ( &wqe->ud, 2,
+ ud_address_vector.max_stat_rate,
+ ( ( ( av->rate < 2 ) || ( av->rate > 10 ) ) ?
+ 8 : ( av->rate + 5 ) ) );
+ MLX_FILL_1 ( &wqe->ud, 3, ud_address_vector.sl, av->sl );
+ gid = ( av->gid_present ? &av->gid : &hermon_no_gid );
+ memcpy ( &wqe->ud.u.dwords[4], gid, sizeof ( *gid ) );
+ MLX_FILL_1 ( &wqe->ud, 8, destination_qp, av->dest_qp );
+ MLX_FILL_1 ( &wqe->ud, 9, q_key, av->qkey );
+ MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_len ( iobuf ) );
+ MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->reserved_lkey );
+ MLX_FILL_1 ( &wqe->data[0], 3,
+ local_address_l, virt_to_bus ( iobuf->data ) );
+ barrier();
+ MLX_FILL_2 ( &wqe->ctrl, 0,
+ opcode, HERMON_OPCODE_SEND,
+ owner,
+ ( ( wq->next_idx & hermon_send_wq->num_wqes ) ? 1 : 0 ) );
+ DBGCP ( hermon, "Hermon %p posting send WQE:\n", hermon );
+ DBGCP_HD ( hermon, wqe, sizeof ( *wqe ) );
+ barrier();
+
+ /* Ring doorbell register */
+ MLX_FILL_1 ( &db_reg.send, 0, qn, qp->qpn );
+ DBGCP ( hermon, "Ringing doorbell %08lx with %08lx\n",
+ virt_to_phys ( hermon->uar + HERMON_DB_POST_SND_OFFSET ),
+ db_reg.dword[0] );
+ writel ( db_reg.dword[0], ( hermon->uar + HERMON_DB_POST_SND_OFFSET ));
+
+ /* Update work queue's index */
+ wq->next_idx++;
+
+ return 0;
+}
+
+/**
+ * Post receive work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int hermon_post_recv ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct io_buffer *iobuf ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp );
+ struct ib_work_queue *wq = &qp->recv;
+ struct hermon_recv_work_queue *hermon_recv_wq = &hermon_qp->recv;
+ struct hermonprm_recv_wqe *wqe;
+ unsigned int wqe_idx_mask;
+
+ /* Allocate work queue entry */
+ wqe_idx_mask = ( wq->num_wqes - 1 );
+ if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) {
+ DBGC ( hermon, "Hermon %p receive queue full", hermon );
+ return -ENOBUFS;
+ }
+ wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf;
+ wqe = &hermon_recv_wq->wqe[wq->next_idx & wqe_idx_mask].recv;
+
+ /* Construct work queue entry */
+ MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) );
+ MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->reserved_lkey );
+ MLX_FILL_1 ( &wqe->data[0], 3,
+ local_address_l, virt_to_bus ( iobuf->data ) );
+
+ /* Update work queue's index */
+ wq->next_idx++;
+
+ /* Update doorbell record */
+ barrier();
+ MLX_FILL_1 ( &hermon_recv_wq->doorbell, 0, receive_wqe_counter,
+ ( wq->next_idx & 0xffff ) );
+
+ return 0;
+}
+
+/**
+ * Handle completion
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ * @v cqe Hardware completion queue entry
+ * @v complete_send Send completion handler
+ * @v complete_recv Receive completion handler
+ * @ret rc Return status code
+ */
+static int hermon_complete ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq,
+ union hermonprm_completion_entry *cqe,
+ ib_completer_t complete_send,
+ ib_completer_t complete_recv ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct ib_completion completion;
+ struct ib_work_queue *wq;
+ struct ib_queue_pair *qp;
+ struct hermon_queue_pair *hermon_qp;
+ struct io_buffer *iobuf;
+ ib_completer_t complete;
+ unsigned int opcode;
+ unsigned long qpn;
+ int is_send;
+ unsigned int wqe_idx;
+ int rc = 0;
+
+ /* Parse completion */
+ memset ( &completion, 0, sizeof ( completion ) );
+ qpn = MLX_GET ( &cqe->normal, qpn );
+ is_send = MLX_GET ( &cqe->normal, s_r );
+ opcode = MLX_GET ( &cqe->normal, opcode );
+ if ( opcode >= HERMON_OPCODE_RECV_ERROR ) {
+ /* "s" field is not valid for error opcodes */
+ is_send = ( opcode == HERMON_OPCODE_SEND_ERROR );
+ completion.syndrome = MLX_GET ( &cqe->error, syndrome );
+ DBGC ( hermon, "Hermon %p CQN %lx syndrome %x vendor %lx\n",
+ hermon, cq->cqn, completion.syndrome,
+ MLX_GET ( &cqe->error, vendor_error_syndrome ) );
+ rc = -EIO;
+ /* Don't return immediately; propagate error to completer */
+ }
+
+ /* Identify work queue */
+ wq = ib_find_wq ( cq, qpn, is_send );
+ if ( ! wq ) {
+ DBGC ( hermon, "Hermon %p CQN %lx unknown %s QPN %lx\n",
+ hermon, cq->cqn, ( is_send ? "send" : "recv" ), qpn );
+ return -EIO;
+ }
+ qp = wq->qp;
+ hermon_qp = ib_qp_get_drvdata ( qp );
+
+ /* Identify I/O buffer */
+ wqe_idx = ( MLX_GET ( &cqe->normal, wqe_counter ) &
+ ( wq->num_wqes - 1 ) );
+ iobuf = wq->iobufs[wqe_idx];
+ if ( ! iobuf ) {
+ DBGC ( hermon, "Hermon %p CQN %lx QPN %lx empty WQE %x\n",
+ hermon, cq->cqn, qpn, wqe_idx );
+ return -EIO;
+ }
+ wq->iobufs[wqe_idx] = NULL;
+
+ /* Fill in length for received packets */
+ if ( ! is_send ) {
+ completion.len = MLX_GET ( &cqe->normal, byte_cnt );
+ if ( completion.len > iob_tailroom ( iobuf ) ) {
+ DBGC ( hermon, "Hermon %p CQN %lx QPN %lx IDX %x "
+ "overlength received packet length %zd\n",
+ hermon, cq->cqn, qpn, wqe_idx, completion.len );
+ return -EIO;
+ }
+ }
+
+ /* Pass off to caller's completion handler */
+ complete = ( is_send ? complete_send : complete_recv );
+ complete ( ibdev, qp, &completion, iobuf );
+
+ return rc;
+}
+
+/**
+ * Poll completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ * @v complete_send Send completion handler
+ * @v complete_recv Receive completion handler
+ */
+static void hermon_poll_cq ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq,
+ ib_completer_t complete_send,
+ ib_completer_t complete_recv ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermon_completion_queue *hermon_cq = ib_cq_get_drvdata ( cq );
+ union hermonprm_completion_entry *cqe;
+ unsigned int cqe_idx_mask;
+ int rc;
+
+ while ( 1 ) {
+ /* Look for completion entry */
+ cqe_idx_mask = ( cq->num_cqes - 1 );
+ cqe = &hermon_cq->cqe[cq->next_idx & cqe_idx_mask];
+ if ( MLX_GET ( &cqe->normal, owner ) ^
+ ( ( cq->next_idx & cq->num_cqes ) ? 1 : 0 ) ) {
+ /* Entry still owned by hardware; end of poll */
+ break;
+ }
+ DBGCP ( hermon, "Hermon %p completion:\n", hermon );
+ DBGCP_HD ( hermon, cqe, sizeof ( *cqe ) );
+
+ /* Handle completion */
+ if ( ( rc = hermon_complete ( ibdev, cq, cqe, complete_send,
+ complete_recv ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p failed to complete: %s\n",
+ hermon, strerror ( rc ) );
+ DBGC_HD ( hermon, cqe, sizeof ( *cqe ) );
+ }
+
+ /* Update completion queue's index */
+ cq->next_idx++;
+
+ /* Update doorbell record */
+ MLX_FILL_1 ( &hermon_cq->doorbell, 0, update_ci,
+ ( cq->next_idx & 0xffffffUL ) );
+ }
+}
+
+/***************************************************************************
+ *
+ * Infiniband link-layer operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Initialise Infiniband link
+ *
+ * @v ibdev Infiniband device
+ * @ret rc Return status code
+ */
+static int hermon_open ( struct ib_device *ibdev ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermonprm_init_port init_port;
+ int rc;
+
+ memset ( &init_port, 0, sizeof ( init_port ) );
+ MLX_FILL_2 ( &init_port, 0,
+ port_width_cap, 3,
+ vl_cap, 1 );
+ MLX_FILL_2 ( &init_port, 1,
+ mtu, HERMON_MTU_2048,
+ max_gid, 1 );
+ MLX_FILL_1 ( &init_port, 2, max_pkey, 64 );
+ if ( ( rc = hermon_cmd_init_port ( hermon, ibdev->port,
+ &init_port ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not intialise port: %s\n",
+ hermon, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Close Infiniband link
+ *
+ * @v ibdev Infiniband device
+ */
+static void hermon_close ( struct ib_device *ibdev ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ int rc;
+
+ if ( ( rc = hermon_cmd_close_port ( hermon, ibdev->port ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not close port: %s\n",
+ hermon, strerror ( rc ) );
+ /* Nothing we can do about this */
+ }
+}
+
+/***************************************************************************
+ *
+ * Multicast group operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Attach to multicast group
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v gid Multicast GID
+ * @ret rc Return status code
+ */
+static int hermon_mcast_attach ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct ib_gid *gid ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermonprm_mgm_hash hash;
+ struct hermonprm_mcg_entry mcg;
+ unsigned int index;
+ int rc;
+
+ /* Generate hash table index */
+ if ( ( rc = hermon_cmd_mgid_hash ( hermon, gid, &hash ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not hash GID: %s\n",
+ hermon, strerror ( rc ) );
+ return rc;
+ }
+ index = MLX_GET ( &hash, hash );
+
+ /* Check for existing hash table entry */
+ if ( ( rc = hermon_cmd_read_mcg ( hermon, index, &mcg ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not read MCG %#x: %s\n",
+ hermon, index, strerror ( rc ) );
+ return rc;
+ }
+ if ( MLX_GET ( &mcg, hdr.members_count ) != 0 ) {
+ /* FIXME: this implementation allows only a single QP
+ * per multicast group, and doesn't handle hash
+ * collisions. Sufficient for IPoIB but may need to
+ * be extended in future.
+ */
+ DBGC ( hermon, "Hermon %p MGID index %#x already in use\n",
+ hermon, index );
+ return -EBUSY;
+ }
+
+ /* Update hash table entry */
+ MLX_FILL_1 ( &mcg, 1, hdr.members_count, 1 );
+ MLX_FILL_1 ( &mcg, 8, qp[0].qpn, qp->qpn );
+ memcpy ( &mcg.u.dwords[4], gid, sizeof ( *gid ) );
+ if ( ( rc = hermon_cmd_write_mcg ( hermon, index, &mcg ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not write MCG %#x: %s\n",
+ hermon, index, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Detach from multicast group
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v gid Multicast GID
+ */
+static void hermon_mcast_detach ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp __unused,
+ struct ib_gid *gid ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermonprm_mgm_hash hash;
+ struct hermonprm_mcg_entry mcg;
+ unsigned int index;
+ int rc;
+
+ /* Generate hash table index */
+ if ( ( rc = hermon_cmd_mgid_hash ( hermon, gid, &hash ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not hash GID: %s\n",
+ hermon, strerror ( rc ) );
+ return;
+ }
+ index = MLX_GET ( &hash, hash );
+
+ /* Clear hash table entry */
+ memset ( &mcg, 0, sizeof ( mcg ) );
+ if ( ( rc = hermon_cmd_write_mcg ( hermon, index, &mcg ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not write MCG %#x: %s\n",
+ hermon, index, strerror ( rc ) );
+ return;
+ }
+}
+
+/***************************************************************************
+ *
+ * MAD operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Issue management datagram
+ *
+ * @v ibdev Infiniband device
+ * @v mad Management datagram
+ * @v len Length of management datagram
+ * @ret rc Return status code
+ */
+static int hermon_mad ( struct ib_device *ibdev, struct ib_mad_hdr *mad,
+ size_t len ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ union hermonprm_mad mad_ifc;
+ int rc;
+
+ /* Copy in request packet */
+ memset ( &mad_ifc, 0, sizeof ( mad_ifc ) );
+ assert ( len <= sizeof ( mad_ifc.mad ) );
+ memcpy ( &mad_ifc.mad, mad, len );
+
+ /* Issue MAD */
+ if ( ( rc = hermon_cmd_mad_ifc ( hermon, ibdev->port,
+ &mad_ifc ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not issue MAD IFC: %s\n",
+ hermon, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy out reply packet */
+ memcpy ( mad, &mad_ifc.mad, len );
+
+ if ( mad->status != 0 ) {
+ DBGC ( hermon, "Hermon %p MAD IFC status %04x\n",
+ hermon, ntohs ( mad->status ) );
+ return -EIO;
+ }
+ return 0;
+}
+
+/** Hermon Infiniband operations */
+static struct ib_device_operations hermon_ib_operations = {
+ .create_cq = hermon_create_cq,
+ .destroy_cq = hermon_destroy_cq,
+ .create_qp = hermon_create_qp,
+ .destroy_qp = hermon_destroy_qp,
+ .post_send = hermon_post_send,
+ .post_recv = hermon_post_recv,
+ .poll_cq = hermon_poll_cq,
+ .open = hermon_open,
+ .close = hermon_close,
+ .mcast_attach = hermon_mcast_attach,
+ .mcast_detach = hermon_mcast_detach,
+ .mad = hermon_mad,
+};
+
+/***************************************************************************
+ *
+ * Firmware control
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Start firmware running
+ *
+ * @v hermon Hermon device
+ * @ret rc Return status code
+ */
+static int hermon_start_firmware ( struct hermon *hermon ) {
+ struct hermonprm_query_fw fw;
+ struct hermonprm_virtual_physical_mapping map_fa;
+ unsigned int fw_pages;
+ unsigned int log2_fw_pages;
+ size_t fw_size;
+ physaddr_t fw_base;
+ int rc;
+
+ /* Get firmware parameters */
+ if ( ( rc = hermon_cmd_query_fw ( hermon, &fw ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not query firmware: %s\n",
+ hermon, strerror ( rc ) );
+ goto err_query_fw;
+ }
+ DBGC ( hermon, "Hermon %p firmware version %ld.%ld.%ld\n", hermon,
+ MLX_GET ( &fw, fw_rev_major ), MLX_GET ( &fw, fw_rev_minor ),
+ MLX_GET ( &fw, fw_rev_subminor ) );
+ fw_pages = MLX_GET ( &fw, fw_pages );
+ log2_fw_pages = fls ( fw_pages - 1 );
+ fw_pages = ( 1 << log2_fw_pages );
+ DBGC ( hermon, "Hermon %p requires %d kB for firmware\n",
+ hermon, ( fw_pages * 4 ) );
+
+ /* Allocate firmware pages and map firmware area */
+ fw_size = ( fw_pages * HERMON_PAGE_SIZE );
+ hermon->firmware_area = umalloc ( fw_size );
+ if ( ! hermon->firmware_area ) {
+ rc = -ENOMEM;
+ goto err_alloc_fa;
+ }
+ fw_base = ( user_to_phys ( hermon->firmware_area, fw_size ) &
+ ~( fw_size - 1 ) );
+ DBGC ( hermon, "Hermon %p firmware area at physical [%lx,%lx)\n",
+ hermon, fw_base, ( fw_base + fw_size ) );
+ memset ( &map_fa, 0, sizeof ( map_fa ) );
+ MLX_FILL_2 ( &map_fa, 3,
+ log2size, log2_fw_pages,
+ pa_l, ( fw_base >> 12 ) );
+ if ( ( rc = hermon_cmd_map_fa ( hermon, &map_fa ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not map firmware: %s\n",
+ hermon, strerror ( rc ) );
+ goto err_map_fa;
+ }
+
+ /* Start firmware */
+ if ( ( rc = hermon_cmd_run_fw ( hermon ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not run firmware: %s\n",
+ hermon, strerror ( rc ) );
+ goto err_run_fw;
+ }
+
+ DBGC ( hermon, "Hermon %p firmware started\n", hermon );
+ return 0;
+
+ err_run_fw:
+ hermon_cmd_unmap_fa ( hermon );
+ err_map_fa:
+ ufree ( hermon->firmware_area );
+ hermon->firmware_area = UNULL;
+ err_alloc_fa:
+ err_query_fw:
+ return rc;
+}
+
+/**
+ * Stop firmware running
+ *
+ * @v hermon Hermon device
+ */
+static void hermon_stop_firmware ( struct hermon *hermon ) {
+ int rc;
+
+ if ( ( rc = hermon_cmd_unmap_fa ( hermon ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p FATAL could not stop firmware: %s\n",
+ hermon, strerror ( rc ) );
+ /* Leak memory and return; at least we avoid corruption */
+ return;
+ }
+ ufree ( hermon->firmware_area );
+ hermon->firmware_area = UNULL;
+}
+
+/***************************************************************************
+ *
+ * Infinihost Context Memory management
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Get device limits
+ *
+ * @v hermon Hermon device
+ * @ret rc Return status code
+ */
+static int hermon_get_cap ( struct hermon *hermon ) {
+ struct hermonprm_query_dev_cap dev_cap;
+ int rc;
+
+ if ( ( rc = hermon_cmd_query_dev_cap ( hermon, &dev_cap ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not get device limits: %s\n",
+ hermon, strerror ( rc ) );
+ return rc;
+ }
+
+ hermon->cap.cmpt_entry_size = MLX_GET ( &dev_cap, c_mpt_entry_sz );
+ hermon->cap.reserved_qps =
+ ( 1 << MLX_GET ( &dev_cap, log2_rsvd_qps ) );
+ hermon->cap.qpc_entry_size = MLX_GET ( &dev_cap, qpc_entry_sz );
+ hermon->cap.altc_entry_size = MLX_GET ( &dev_cap, altc_entry_sz );
+ hermon->cap.auxc_entry_size = MLX_GET ( &dev_cap, aux_entry_sz );
+ hermon->cap.reserved_srqs =
+ ( 1 << MLX_GET ( &dev_cap, log2_rsvd_srqs ) );
+ hermon->cap.srqc_entry_size = MLX_GET ( &dev_cap, srq_entry_sz );
+ hermon->cap.reserved_cqs =
+ ( 1 << MLX_GET ( &dev_cap, log2_rsvd_cqs ) );
+ hermon->cap.cqc_entry_size = MLX_GET ( &dev_cap, cqc_entry_sz );
+ hermon->cap.reserved_eqs = MLX_GET ( &dev_cap, num_rsvd_eqs );
+ hermon->cap.eqc_entry_size = MLX_GET ( &dev_cap, eqc_entry_sz );
+ hermon->cap.reserved_mtts =
+ ( 1 << MLX_GET ( &dev_cap, log2_rsvd_mtts ) );
+ hermon->cap.mtt_entry_size = MLX_GET ( &dev_cap, mtt_entry_sz );
+ hermon->cap.reserved_mrws =
+ ( 1 << MLX_GET ( &dev_cap, log2_rsvd_mrws ) );
+ hermon->cap.dmpt_entry_size = MLX_GET ( &dev_cap, d_mpt_entry_sz );
+ hermon->cap.reserved_uars = MLX_GET ( &dev_cap, num_rsvd_uars );
+
+ return 0;
+}
+
+/**
+ * Get ICM usage
+ *
+ * @v log_num_entries Log2 of the number of entries
+ * @v entry_size Entry size
+ * @ret usage Usage size in ICM
+ */
+static size_t icm_usage ( unsigned int log_num_entries, size_t entry_size ) {
+ size_t usage;
+
+ usage = ( ( 1 << log_num_entries ) * entry_size );
+ usage = ( ( usage + HERMON_PAGE_SIZE - 1 ) &
+ ~( HERMON_PAGE_SIZE - 1 ) );
+ return usage;
+}
+
+/**
+ * Allocate ICM
+ *
+ * @v hermon Hermon device
+ * @v init_hca INIT_HCA structure to fill in
+ * @ret rc Return status code
+ */
+static int hermon_alloc_icm ( struct hermon *hermon,
+ struct hermonprm_init_hca *init_hca ) {
+ struct hermonprm_scalar_parameter icm_size;
+ struct hermonprm_scalar_parameter icm_aux_size;
+ struct hermonprm_virtual_physical_mapping map_icm_aux;
+ struct hermonprm_virtual_physical_mapping map_icm;
+ uint64_t icm_offset = 0;
+ unsigned int log_num_qps, log_num_srqs, log_num_cqs, log_num_eqs;
+ unsigned int log_num_mtts, log_num_mpts;
+ size_t cmpt_max_len;
+ size_t qp_cmpt_len, srq_cmpt_len, cq_cmpt_len, eq_cmpt_len;
+ size_t icm_len, icm_aux_len;
+ physaddr_t icm_phys;
+ int i;
+ int rc;
+
+ /*
+ * Start by carving up the ICM virtual address space
+ *
+ */
+
+ /* Calculate number of each object type within ICM */
+ log_num_qps = fls ( hermon->cap.reserved_qps + HERMON_MAX_QPS - 1 );
+ log_num_srqs = fls ( hermon->cap.reserved_srqs - 1 );
+ log_num_cqs = fls ( hermon->cap.reserved_cqs + HERMON_MAX_CQS - 1 );
+ log_num_eqs = fls ( hermon->cap.reserved_eqs + HERMON_MAX_EQS - 1 );
+ log_num_mtts = fls ( hermon->cap.reserved_mtts + HERMON_MAX_MTTS - 1 );
+
+ /* ICM starts with the cMPT tables, which are sparse */
+ cmpt_max_len = ( HERMON_CMPT_MAX_ENTRIES *
+ ( ( uint64_t ) hermon->cap.cmpt_entry_size ) );
+ qp_cmpt_len = icm_usage ( log_num_qps, hermon->cap.cmpt_entry_size );
+ hermon->icm_map[HERMON_ICM_QP_CMPT].offset = icm_offset;
+ hermon->icm_map[HERMON_ICM_QP_CMPT].len = qp_cmpt_len;
+ icm_offset += cmpt_max_len;
+ srq_cmpt_len = icm_usage ( log_num_srqs, hermon->cap.cmpt_entry_size );
+ hermon->icm_map[HERMON_ICM_SRQ_CMPT].offset = icm_offset;
+ hermon->icm_map[HERMON_ICM_SRQ_CMPT].len = srq_cmpt_len;
+ icm_offset += cmpt_max_len;
+ cq_cmpt_len = icm_usage ( log_num_cqs, hermon->cap.cmpt_entry_size );
+ hermon->icm_map[HERMON_ICM_CQ_CMPT].offset = icm_offset;
+ hermon->icm_map[HERMON_ICM_CQ_CMPT].len = cq_cmpt_len;
+ icm_offset += cmpt_max_len;
+ eq_cmpt_len = icm_usage ( log_num_eqs, hermon->cap.cmpt_entry_size );
+ hermon->icm_map[HERMON_ICM_EQ_CMPT].offset = icm_offset;
+ hermon->icm_map[HERMON_ICM_EQ_CMPT].len = eq_cmpt_len;
+ icm_offset += cmpt_max_len;
+
+ hermon->icm_map[HERMON_ICM_OTHER].offset = icm_offset;
+
+ /* Queue pair contexts */
+ MLX_FILL_1 ( init_hca, 12,
+ qpc_eec_cqc_eqc_rdb_parameters.qpc_base_addr_h,
+ ( icm_offset >> 32 ) );
+ MLX_FILL_2 ( init_hca, 13,
+ qpc_eec_cqc_eqc_rdb_parameters.qpc_base_addr_l,
+ ( icm_offset >> 5 ),
+ qpc_eec_cqc_eqc_rdb_parameters.log_num_of_qp,
+ log_num_qps );
+ DBGC ( hermon, "Hermon %p ICM QPC base = %llx\n", hermon, icm_offset );
+ icm_offset += icm_usage ( log_num_qps, hermon->cap.qpc_entry_size );
+
+ /* Extended alternate path contexts */
+ MLX_FILL_1 ( init_hca, 24,
+ qpc_eec_cqc_eqc_rdb_parameters.altc_base_addr_h,
+ ( icm_offset >> 32 ) );
+ MLX_FILL_1 ( init_hca, 25,
+ qpc_eec_cqc_eqc_rdb_parameters.altc_base_addr_l,
+ icm_offset );
+ DBGC ( hermon, "Hermon %p ICM ALTC base = %llx\n", hermon, icm_offset);
+ icm_offset += icm_usage ( log_num_qps,
+ hermon->cap.altc_entry_size );
+
+ /* Extended auxiliary contexts */
+ MLX_FILL_1 ( init_hca, 28,
+ qpc_eec_cqc_eqc_rdb_parameters.auxc_base_addr_h,
+ ( icm_offset >> 32 ) );
+ MLX_FILL_1 ( init_hca, 29,
+ qpc_eec_cqc_eqc_rdb_parameters.auxc_base_addr_l,
+ icm_offset );
+ DBGC ( hermon, "Hermon %p ICM AUXC base = %llx\n", hermon, icm_offset);
+ icm_offset += icm_usage ( log_num_qps,
+ hermon->cap.auxc_entry_size );
+
+ /* Shared receive queue contexts */
+ MLX_FILL_1 ( init_hca, 18,
+ qpc_eec_cqc_eqc_rdb_parameters.srqc_base_addr_h,
+ ( icm_offset >> 32 ) );
+ MLX_FILL_2 ( init_hca, 19,
+ qpc_eec_cqc_eqc_rdb_parameters.srqc_base_addr_l,
+ ( icm_offset >> 5 ),
+ qpc_eec_cqc_eqc_rdb_parameters.log_num_of_srq,
+ log_num_srqs );
+ DBGC ( hermon, "Hermon %p ICM SRQC base = %llx\n", hermon, icm_offset);
+ icm_offset += icm_usage ( log_num_srqs,
+ hermon->cap.srqc_entry_size );
+
+ /* Completion queue contexts */
+ MLX_FILL_1 ( init_hca, 20,
+ qpc_eec_cqc_eqc_rdb_parameters.cqc_base_addr_h,
+ ( icm_offset >> 32 ) );
+ MLX_FILL_2 ( init_hca, 21,
+ qpc_eec_cqc_eqc_rdb_parameters.cqc_base_addr_l,
+ ( icm_offset >> 5 ),
+ qpc_eec_cqc_eqc_rdb_parameters.log_num_of_cq,
+ log_num_cqs );
+ DBGC ( hermon, "Hermon %p ICM CQC base = %llx\n", hermon, icm_offset );
+ icm_offset += icm_usage ( log_num_cqs, hermon->cap.cqc_entry_size );
+
+ /* Event queue contexts */
+ MLX_FILL_1 ( init_hca, 32,
+ qpc_eec_cqc_eqc_rdb_parameters.eqc_base_addr_h,
+ ( icm_offset >> 32 ) );
+ MLX_FILL_2 ( init_hca, 33,
+ qpc_eec_cqc_eqc_rdb_parameters.eqc_base_addr_l,
+ ( icm_offset >> 5 ),
+ qpc_eec_cqc_eqc_rdb_parameters.log_num_of_eq,
+ log_num_eqs );
+ DBGC ( hermon, "Hermon %p ICM EQC base = %llx\n", hermon, icm_offset );
+ icm_offset += icm_usage ( log_num_eqs, hermon->cap.eqc_entry_size );
+
+ /* Memory translation table */
+ MLX_FILL_1 ( init_hca, 64,
+ tpt_parameters.mtt_base_addr_h, ( icm_offset >> 32 ) );
+ MLX_FILL_1 ( init_hca, 65,
+ tpt_parameters.mtt_base_addr_l, icm_offset );
+ DBGC ( hermon, "Hermon %p ICM MTT base = %llx\n", hermon, icm_offset );
+ icm_offset += icm_usage ( log_num_mtts,
+ hermon->cap.mtt_entry_size );
+
+ /* Memory protection table */
+ log_num_mpts = fls ( hermon->cap.reserved_mrws + 1 - 1 );
+ MLX_FILL_1 ( init_hca, 60,
+ tpt_parameters.dmpt_base_adr_h, ( icm_offset >> 32 ) );
+ MLX_FILL_1 ( init_hca, 61,
+ tpt_parameters.dmpt_base_adr_l, icm_offset );
+ MLX_FILL_1 ( init_hca, 62,
+ tpt_parameters.log_dmpt_sz, log_num_mpts );
+ DBGC ( hermon, "Hermon %p ICM DMPT base = %llx\n", hermon, icm_offset);
+ icm_offset += icm_usage ( log_num_mpts,
+ hermon->cap.dmpt_entry_size );
+
+ /* Multicast table */
+ MLX_FILL_1 ( init_hca, 48,
+ multicast_parameters.mc_base_addr_h,
+ ( icm_offset >> 32 ) );
+ MLX_FILL_1 ( init_hca, 49,
+ multicast_parameters.mc_base_addr_l, icm_offset );
+ MLX_FILL_1 ( init_hca, 52,
+ multicast_parameters.log_mc_table_entry_sz,
+ fls ( sizeof ( struct hermonprm_mcg_entry ) - 1 ) );
+ MLX_FILL_1 ( init_hca, 53,
+ multicast_parameters.log_mc_table_hash_sz, 3 );
+ MLX_FILL_1 ( init_hca, 54,
+ multicast_parameters.log_mc_table_sz, 3 );
+ DBGC ( hermon, "Hermon %p ICM MC base = %llx\n", hermon, icm_offset );
+ icm_offset += ( ( 8 * sizeof ( struct hermonprm_mcg_entry ) +
+ HERMON_PAGE_SIZE - 1 ) & ~( HERMON_PAGE_SIZE - 1 ) );
+
+ hermon->icm_map[HERMON_ICM_OTHER].len =
+ ( icm_offset - hermon->icm_map[HERMON_ICM_OTHER].offset );
+
+ /*
+ * Allocate and map physical memory for (portions of) ICM
+ *
+ * Map is:
+ * ICM AUX area (aligned to its own size)
+ * cMPT areas
+ * Other areas
+ */
+
+ /* Calculate physical memory required for ICM */
+ icm_len = 0;
+ for ( i = 0 ; i < HERMON_ICM_NUM_REGIONS ; i++ ) {
+ icm_len += hermon->icm_map[i].len;
+ }
+
+ /* Get ICM auxiliary area size */
+ memset ( &icm_size, 0, sizeof ( icm_size ) );
+ MLX_FILL_1 ( &icm_size, 0, value_hi, ( icm_offset >> 32 ) );
+ MLX_FILL_1 ( &icm_size, 1, value, icm_offset );
+ if ( ( rc = hermon_cmd_set_icm_size ( hermon, &icm_size,
+ &icm_aux_size ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not set ICM size: %s\n",
+ hermon, strerror ( rc ) );
+ goto err_set_icm_size;
+ }
+ icm_aux_len = ( MLX_GET ( &icm_aux_size, value ) * HERMON_PAGE_SIZE );
+ /* Must round up to nearest power of two :( */
+ icm_aux_len = ( 1 << fls ( icm_aux_len - 1 ) );
+
+ /* Allocate ICM data and auxiliary area */
+ DBGC ( hermon, "Hermon %p requires %zd kB ICM and %zd kB AUX ICM\n",
+ hermon, ( icm_len / 1024 ), ( icm_aux_len / 1024 ) );
+ hermon->icm = umalloc ( 2 * icm_aux_len + icm_len );
+ if ( ! hermon->icm ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ icm_phys = user_to_phys ( hermon->icm, 0 );
+
+ /* Map ICM auxiliary area */
+ icm_phys = ( ( icm_phys + icm_aux_len - 1 ) & ~( icm_aux_len - 1 ) );
+ memset ( &map_icm_aux, 0, sizeof ( map_icm_aux ) );
+ MLX_FILL_2 ( &map_icm_aux, 3,
+ log2size, fls ( ( icm_aux_len / HERMON_PAGE_SIZE ) - 1 ),
+ pa_l, ( icm_phys >> 12 ) );
+ DBGC ( hermon, "Hermon %p mapping ICM AUX (2^%d pages) => %08lx\n",
+ hermon, fls ( ( icm_aux_len / HERMON_PAGE_SIZE ) - 1 ),
+ icm_phys );
+ if ( ( rc = hermon_cmd_map_icm_aux ( hermon, &map_icm_aux ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not map AUX ICM: %s\n",
+ hermon, strerror ( rc ) );
+ goto err_map_icm_aux;
+ }
+ icm_phys += icm_aux_len;
+
+ /* MAP ICM area */
+ for ( i = 0 ; i < HERMON_ICM_NUM_REGIONS ; i++ ) {
+ memset ( &map_icm, 0, sizeof ( map_icm ) );
+ MLX_FILL_1 ( &map_icm, 0,
+ va_h, ( hermon->icm_map[i].offset >> 32 ) );
+ MLX_FILL_1 ( &map_icm, 1,
+ va_l, ( hermon->icm_map[i].offset >> 12 ) );
+ MLX_FILL_2 ( &map_icm, 3,
+ log2size,
+ fls ( ( hermon->icm_map[i].len /
+ HERMON_PAGE_SIZE ) - 1 ),
+ pa_l, ( icm_phys >> 12 ) );
+ DBGC ( hermon, "Hermon %p mapping ICM %llx+%zx (2^%d pages) "
+ "=> %08lx\n", hermon, hermon->icm_map[i].offset,
+ hermon->icm_map[i].len,
+ fls ( ( hermon->icm_map[i].len /
+ HERMON_PAGE_SIZE ) - 1 ), icm_phys );
+ if ( ( rc = hermon_cmd_map_icm ( hermon, &map_icm ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not map ICM: %s\n",
+ hermon, strerror ( rc ) );
+ goto err_map_icm;
+ }
+ icm_phys += hermon->icm_map[i].len;
+ }
+
+ return 0;
+
+ err_map_icm:
+ assert ( i == 0 ); /* We don't handle partial failure at present */
+ hermon_cmd_unmap_icm_aux ( hermon );
+ err_map_icm_aux:
+ ufree ( hermon->icm );
+ hermon->icm = UNULL;
+ err_alloc:
+ err_set_icm_size:
+ return rc;
+}
+
+/**
+ * Free ICM
+ *
+ * @v hermon Hermon device
+ */
+static void hermon_free_icm ( struct hermon *hermon ) {
+ struct hermonprm_scalar_parameter unmap_icm;
+ int i;
+
+ for ( i = ( HERMON_ICM_NUM_REGIONS - 1 ) ; i >= 0 ; i-- ) {
+ memset ( &unmap_icm, 0, sizeof ( unmap_icm ) );
+ MLX_FILL_1 ( &unmap_icm, 0, value_hi,
+ ( hermon->icm_map[i].offset >> 32 ) );
+ MLX_FILL_1 ( &unmap_icm, 1, value,
+ hermon->icm_map[i].offset );
+ hermon_cmd_unmap_icm ( hermon,
+ ( 1 << fls ( ( hermon->icm_map[i].len /
+ HERMON_PAGE_SIZE ) - 1)),
+ &unmap_icm );
+ }
+ hermon_cmd_unmap_icm_aux ( hermon );
+ ufree ( hermon->icm );
+ hermon->icm = UNULL;
+}
+
+/***************************************************************************
+ *
+ * PCI interface
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Set up memory protection table
+ *
+ * @v hermon Hermon device
+ * @ret rc Return status code
+ */
+static int hermon_setup_mpt ( struct hermon *hermon ) {
+ struct hermonprm_mpt mpt;
+ uint32_t key;
+ int rc;
+
+ /* Derive key */
+ key = ( hermon->cap.reserved_mrws | HERMON_MKEY_PREFIX );
+ hermon->reserved_lkey = ( ( key << 8 ) | ( key >> 24 ) );
+
+ /* Initialise memory protection table */
+ memset ( &mpt, 0, sizeof ( mpt ) );
+ MLX_FILL_4 ( &mpt, 0,
+ r_w, 1,
+ pa, 1,
+ lr, 1,
+ lw, 1 );
+ MLX_FILL_1 ( &mpt, 2, mem_key, key );
+ MLX_FILL_1 ( &mpt, 3, pd, HERMON_GLOBAL_PD );
+ MLX_FILL_1 ( &mpt, 10, len64, 1 );
+ if ( ( rc = hermon_cmd_sw2hw_mpt ( hermon,
+ hermon->cap.reserved_mrws,
+ &mpt ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not set up MPT: %s\n",
+ hermon, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @v id PCI ID
+ * @ret rc Return status code
+ */
+static int hermon_probe ( struct pci_device *pci,
+ const struct pci_device_id *id __unused ) {
+ struct hermon *hermon;
+ struct ib_device *ibdev;
+ struct hermonprm_init_hca init_hca;
+ int i;
+ int rc;
+
+ /* Allocate Hermon device */
+ hermon = zalloc ( sizeof ( *hermon ) );
+ if ( ! hermon ) {
+ rc = -ENOMEM;
+ goto err_alloc_hermon;
+ }
+ pci_set_drvdata ( pci, hermon );
+
+ /* Allocate Infiniband devices */
+ for ( i = 0 ; i < HERMON_NUM_PORTS ; i++ ) {
+ ibdev = alloc_ibdev ( 0 );
+ if ( ! ibdev ) {
+ rc = -ENOMEM;
+ goto err_alloc_ibdev;
+ }
+ hermon->ibdev[i] = ibdev;
+ ibdev->op = &hermon_ib_operations;
+ ibdev->dev = &pci->dev;
+ ibdev->port = ( HERMON_PORT_BASE + i );
+ ib_set_drvdata ( ibdev, hermon );
+ }
+
+ /* Fix up PCI device */
+ adjust_pci_device ( pci );
+
+ /* Get PCI BARs */
+ hermon->config = ioremap ( pci_bar_start ( pci, HERMON_PCI_CONFIG_BAR),
+ HERMON_PCI_CONFIG_BAR_SIZE );
+ hermon->uar = ioremap ( ( pci_bar_start ( pci, HERMON_PCI_UAR_BAR ) +
+ HERMON_UAR_PAGE * HERMON_PAGE_SIZE ),
+ HERMON_PAGE_SIZE );
+
+ /* Allocate space for mailboxes */
+ hermon->mailbox_in = malloc_dma ( HERMON_MBOX_SIZE,
+ HERMON_MBOX_ALIGN );
+ if ( ! hermon->mailbox_in ) {
+ rc = -ENOMEM;
+ goto err_mailbox_in;
+ }
+ hermon->mailbox_out = malloc_dma ( HERMON_MBOX_SIZE,
+ HERMON_MBOX_ALIGN );
+ if ( ! hermon->mailbox_out ) {
+ rc = -ENOMEM;
+ goto err_mailbox_out;
+ }
+
+ /* Start firmware */
+ if ( ( rc = hermon_start_firmware ( hermon ) ) != 0 )
+ goto err_start_firmware;
+
+ /* Get device limits */
+ if ( ( rc = hermon_get_cap ( hermon ) ) != 0 )
+ goto err_get_cap;
+
+ /* Allocate ICM */
+ memset ( &init_hca, 0, sizeof ( init_hca ) );
+ if ( ( rc = hermon_alloc_icm ( hermon, &init_hca ) ) != 0 )
+ goto err_alloc_icm;
+
+ /* Initialise HCA */
+ MLX_FILL_1 ( &init_hca, 0, version, 0x02 /* "Must be 0x02" */ );
+ MLX_FILL_1 ( &init_hca, 5, udp, 1 );
+ MLX_FILL_1 ( &init_hca, 74, uar_parameters.log_max_uars, 8 );
+ if ( ( rc = hermon_cmd_init_hca ( hermon, &init_hca ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not initialise HCA: %s\n",
+ hermon, strerror ( rc ) );
+ goto err_init_hca;
+ }
+
+ /* Set up memory protection */
+ if ( ( rc = hermon_setup_mpt ( hermon ) ) != 0 )
+ goto err_setup_mpt;
+
+ /* Register Infiniband devices */
+ for ( i = 0 ; i < HERMON_NUM_PORTS ; i++ ) {
+ if ( ( rc = register_ibdev ( hermon->ibdev[i] ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not register IB "
+ "device: %s\n", hermon, strerror ( rc ) );
+ goto err_register_ibdev;
+ }
+ }
+
+ return 0;
+
+ i = ( HERMON_NUM_PORTS - 1 );
+ err_register_ibdev:
+ for ( ; i >= 0 ; i-- )
+ unregister_ibdev ( hermon->ibdev[i] );
+ err_setup_mpt:
+ hermon_cmd_close_hca ( hermon );
+ err_init_hca:
+ hermon_free_icm ( hermon );
+ err_alloc_icm:
+ err_get_cap:
+ hermon_stop_firmware ( hermon );
+ err_start_firmware:
+ free_dma ( hermon->mailbox_out, HERMON_MBOX_SIZE );
+ err_mailbox_out:
+ free_dma ( hermon->mailbox_in, HERMON_MBOX_SIZE );
+ err_mailbox_in:
+ i = ( HERMON_NUM_PORTS - 1 );
+ err_alloc_ibdev:
+ for ( ; i >= 0 ; i-- )
+ free_ibdev ( hermon->ibdev[i] );
+ free ( hermon );
+ err_alloc_hermon:
+ return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void hermon_remove ( struct pci_device *pci ) {
+ struct hermon *hermon = pci_get_drvdata ( pci );
+ int i;
+
+ for ( i = ( HERMON_NUM_PORTS - 1 ) ; i >= 0 ; i-- )
+ unregister_ibdev ( hermon->ibdev[i] );
+ hermon_cmd_close_hca ( hermon );
+ hermon_free_icm ( hermon );
+ hermon_stop_firmware ( hermon );
+ hermon_stop_firmware ( hermon );
+ free_dma ( hermon->mailbox_out, HERMON_MBOX_SIZE );
+ free_dma ( hermon->mailbox_in, HERMON_MBOX_SIZE );
+ for ( i = ( HERMON_NUM_PORTS - 1 ) ; i >= 0 ; i-- )
+ free_ibdev ( hermon->ibdev[i] );
+ free ( hermon );
+}
+
+static struct pci_device_id hermon_nics[] = {
+ PCI_ROM ( 0x15b3, 0x6340, "mt25408", "MT25408 HCA driver" ),
+ PCI_ROM ( 0x15b3, 0x634a, "mt25418", "MT25418 HCA driver" ),
+};
+
+struct pci_driver hermon_driver __pci_driver = {
+ .ids = hermon_nics,
+ .id_count = ( sizeof ( hermon_nics ) / sizeof ( hermon_nics[0] ) ),
+ .probe = hermon_probe,
+ .remove = hermon_remove,
+};
diff --git a/gpxe/src/drivers/infiniband/hermon.h b/gpxe/src/drivers/infiniband/hermon.h
new file mode 100644
index 00000000..959e6a9d
--- /dev/null
+++ b/gpxe/src/drivers/infiniband/hermon.h
@@ -0,0 +1,466 @@
+#ifndef _HERMON_H
+#define _HERMON_H
+
+/** @file
+ *
+ * Mellanox Hermon Infiniband HCA driver
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/uaccess.h>
+#include "mlx_bitops.h"
+#include "MT25408_PRM.h"
+
+/*
+ * Hardware constants
+ *
+ */
+
+/* Ports in existence */
+#define HERMON_NUM_PORTS 1
+#define HERMON_PORT_BASE 1
+
+/* PCI BARs */
+#define HERMON_PCI_CONFIG_BAR PCI_BASE_ADDRESS_0
+#define HERMON_PCI_CONFIG_BAR_SIZE 0x100000
+#define HERMON_PCI_UAR_BAR PCI_BASE_ADDRESS_2
+
+/* Work queue entry and completion queue entry opcodes */
+#define HERMON_OPCODE_SEND 0x0a
+#define HERMON_OPCODE_RECV_ERROR 0xfe
+#define HERMON_OPCODE_SEND_ERROR 0xff
+
+/* HCA command register opcodes */
+#define HERMON_HCR_QUERY_DEV_CAP 0x0003
+#define HERMON_HCR_QUERY_FW 0x0004
+#define HERMON_HCR_INIT_HCA 0x0007
+#define HERMON_HCR_CLOSE_HCA 0x0008
+#define HERMON_HCR_INIT_PORT 0x0009
+#define HERMON_HCR_CLOSE_PORT 0x000a
+#define HERMON_HCR_SW2HW_MPT 0x000d
+#define HERMON_HCR_WRITE_MTT 0x0011
+#define HERMON_HCR_MAP_EQ 0x0012
+#define HERMON_HCR_SW2HW_EQ 0x0013
+#define HERMON_HCR_HW2SW_EQ 0x0014
+#define HERMON_HCR_SW2HW_CQ 0x0016
+#define HERMON_HCR_HW2SW_CQ 0x0017
+#define HERMON_HCR_RST2INIT_QP 0x0019
+#define HERMON_HCR_INIT2RTR_QP 0x001a
+#define HERMON_HCR_RTR2RTS_QP 0x001b
+#define HERMON_HCR_2RST_QP 0x0021
+#define HERMON_HCR_MAD_IFC 0x0024
+#define HERMON_HCR_READ_MCG 0x0025
+#define HERMON_HCR_WRITE_MCG 0x0026
+#define HERMON_HCR_MGID_HASH 0x0027
+#define HERMON_HCR_RUN_FW 0x0ff6
+#define HERMON_HCR_DISABLE_LAM 0x0ff7
+#define HERMON_HCR_ENABLE_LAM 0x0ff8
+#define HERMON_HCR_UNMAP_ICM 0x0ff9
+#define HERMON_HCR_MAP_ICM 0x0ffa
+#define HERMON_HCR_UNMAP_ICM_AUX 0x0ffb
+#define HERMON_HCR_MAP_ICM_AUX 0x0ffc
+#define HERMON_HCR_SET_ICM_SIZE 0x0ffd
+#define HERMON_HCR_UNMAP_FA 0x0ffe
+#define HERMON_HCR_MAP_FA 0x0fff
+
+/* Service types */
+#define HERMON_ST_UD 0x03
+
+/* MTUs */
+#define HERMON_MTU_2048 0x04
+
+#define HERMON_INVALID_LKEY 0x00000100UL
+
+#define HERMON_PAGE_SIZE 4096
+
+#define HERMON_DB_POST_SND_OFFSET 0x14
+
+/*
+ * Datatypes that seem to be missing from the autogenerated documentation
+ *
+ */
+struct hermonprm_mgm_hash_st {
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t hash[0x00010];
+ pseudo_bit_t reserved1[0x00010];
+} __attribute__ (( packed ));
+
+struct hermonprm_mcg_entry_st {
+ struct hermonprm_mcg_hdr_st hdr;
+ struct hermonprm_mcg_qp_dw_st qp[8];
+} __attribute__ (( packed ));
+
+struct hermonprm_cq_db_record_st {
+ pseudo_bit_t update_ci[0x00018];
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t arm_ci[0x00018];
+ pseudo_bit_t cmd[0x00003];
+ pseudo_bit_t reserved1[0x00001];
+ pseudo_bit_t cmd_sn[0x00002];
+ pseudo_bit_t reserved2[0x00002];
+} __attribute__ (( packed ));
+
+struct hermonprm_send_db_register_st {
+ pseudo_bit_t reserved[0x00008];
+ pseudo_bit_t qn[0x00018];
+} __attribute__ (( packed ));
+
+struct hermonprm_scalar_parameter_st {
+ pseudo_bit_t value_hi[0x00020];
+/* -------------- */
+ pseudo_bit_t value[0x00020];
+} __attribute__ (( packed ));
+
+/*
+ * Wrapper structures for hardware datatypes
+ *
+ */
+
+struct MLX_DECLARE_STRUCT ( hermonprm_completion_queue_context );
+struct MLX_DECLARE_STRUCT ( hermonprm_completion_queue_entry );
+struct MLX_DECLARE_STRUCT ( hermonprm_completion_with_error );
+struct MLX_DECLARE_STRUCT ( hermonprm_cq_db_record );
+struct MLX_DECLARE_STRUCT ( hermonprm_eqc );
+struct MLX_DECLARE_STRUCT ( hermonprm_hca_command_register );
+struct MLX_DECLARE_STRUCT ( hermonprm_init_hca );
+struct MLX_DECLARE_STRUCT ( hermonprm_init_port );
+struct MLX_DECLARE_STRUCT ( hermonprm_mad_ifc );
+struct MLX_DECLARE_STRUCT ( hermonprm_mcg_entry );
+struct MLX_DECLARE_STRUCT ( hermonprm_mgm_hash );
+struct MLX_DECLARE_STRUCT ( hermonprm_mpt );
+struct MLX_DECLARE_STRUCT ( hermonprm_mtt );
+struct MLX_DECLARE_STRUCT ( hermonprm_qp_db_record );
+struct MLX_DECLARE_STRUCT ( hermonprm_qp_ee_state_transitions );
+struct MLX_DECLARE_STRUCT ( hermonprm_query_dev_cap );
+struct MLX_DECLARE_STRUCT ( hermonprm_query_fw );
+struct MLX_DECLARE_STRUCT ( hermonprm_queue_pair_ee_context_entry );
+struct MLX_DECLARE_STRUCT ( hermonprm_scalar_parameter );
+struct MLX_DECLARE_STRUCT ( hermonprm_send_db_register );
+struct MLX_DECLARE_STRUCT ( hermonprm_ud_address_vector );
+struct MLX_DECLARE_STRUCT ( hermonprm_virtual_physical_mapping );
+struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_ctrl_send );
+struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_data_ptr );
+struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_ud );
+
+/*
+ * Composite hardware datatypes
+ *
+ */
+
+struct hermonprm_write_mtt {
+ struct hermonprm_scalar_parameter mtt_base_addr;
+ struct hermonprm_scalar_parameter reserved;
+ struct hermonprm_mtt mtt;
+} __attribute__ (( packed ));
+
+#define HERMON_MAX_GATHER 1
+
+struct hermonprm_ud_send_wqe {
+ struct hermonprm_wqe_segment_ctrl_send ctrl;
+ struct hermonprm_wqe_segment_ud ud;
+ struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_GATHER];
+} __attribute__ (( packed ));
+
+#define HERMON_MAX_SCATTER 1
+
+struct hermonprm_recv_wqe {
+ struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_SCATTER];
+} __attribute__ (( packed ));
+
+union hermonprm_completion_entry {
+ struct hermonprm_completion_queue_entry normal;
+ struct hermonprm_completion_with_error error;
+} __attribute__ (( packed ));
+
+union hermonprm_doorbell_register {
+ struct hermonprm_send_db_register send;
+ uint32_t dword[1];
+} __attribute__ (( packed ));
+
+union hermonprm_mad {
+ struct hermonprm_mad_ifc ifc;
+ union ib_mad mad;
+} __attribute__ (( packed ));
+
+/*
+ * gPXE-specific definitions
+ *
+ */
+
+/** Hermon device capabilitiess */
+struct hermon_dev_cap {
+ /** CMPT entry size */
+ size_t cmpt_entry_size;
+ /** Number of reserved QPs */
+ unsigned int reserved_qps;
+ /** QP context entry size */
+ size_t qpc_entry_size;
+ /** Alternate path context entry size */
+ size_t altc_entry_size;
+ /** Auxiliary context entry size */
+ size_t auxc_entry_size;
+ /** Number of reserved SRQs */
+ unsigned int reserved_srqs;
+ /** SRQ context entry size */
+ size_t srqc_entry_size;
+ /** Number of reserved CQs */
+ unsigned int reserved_cqs;
+ /** CQ context entry size */
+ size_t cqc_entry_size;
+ /** Number of reserved EQs */
+ unsigned int reserved_eqs;
+ /** EQ context entry size */
+ size_t eqc_entry_size;
+ /** Number of reserved MTTs */
+ unsigned int reserved_mtts;
+ /** MTT entry size */
+ size_t mtt_entry_size;
+ /** Number of reserved MRWs */
+ unsigned int reserved_mrws;
+ /** DMPT entry size */
+ size_t dmpt_entry_size;
+ /** Number of reserved UARs */
+ unsigned int reserved_uars;
+};
+
+/** Number of cMPT entries of each type */
+#define HERMON_CMPT_MAX_ENTRIES ( 1 << 24 )
+
+/** Hermon ICM memory map entry */
+struct hermon_icm_map {
+ /** Offset (virtual address within ICM) */
+ uint64_t offset;
+ /** Length */
+ size_t len;
+};
+
+/** Discontiguous regions within Hermon ICM */
+enum hermon_icm_map_regions {
+ HERMON_ICM_QP_CMPT = 0,
+ HERMON_ICM_SRQ_CMPT,
+ HERMON_ICM_CQ_CMPT,
+ HERMON_ICM_EQ_CMPT,
+ HERMON_ICM_OTHER,
+ HERMON_ICM_NUM_REGIONS
+};
+
+/** UAR page for doorbell accesses
+ *
+ * Pages 0-127 are reserved for event queue doorbells only, so we use
+ * page 128.
+ */
+#define HERMON_UAR_PAGE 128
+
+/** Maximum number of allocatable MTT entries
+ *
+ * This is a policy decision, not a device limit.
+ */
+#define HERMON_MAX_MTTS 64
+
+/** A Hermon MTT descriptor */
+struct hermon_mtt {
+ /** MTT offset */
+ unsigned int mtt_offset;
+ /** Number of pages */
+ unsigned int num_pages;
+ /** MTT base address */
+ unsigned int mtt_base_addr;
+ /** Offset within page */
+ unsigned int page_offset;
+};
+
+/** Alignment of Hermon send work queue entries */
+#define HERMON_SEND_WQE_ALIGN 128
+
+/** A Hermon send work queue entry */
+union hermon_send_wqe {
+ struct hermonprm_ud_send_wqe ud;
+ uint8_t force_align[HERMON_SEND_WQE_ALIGN];
+} __attribute__ (( packed ));
+
+/** A Hermon send work queue */
+struct hermon_send_work_queue {
+ /** Number of work queue entries, including headroom
+ *
+ * Hermon requires us to leave unused space within the send
+ * WQ, so we create a send WQ with more entries than are
+ * requested in the create_qp() call.
+ */
+ unsigned int num_wqes;
+ /** Work queue entries */
+ union hermon_send_wqe *wqe;
+ /** Size of work queue */
+ size_t wqe_size;
+};
+
+/** Alignment of Hermon receive work queue entries */
+#define HERMON_RECV_WQE_ALIGN 16
+
+/** A Hermon receive work queue entry */
+union hermon_recv_wqe {
+ struct hermonprm_recv_wqe recv;
+ uint8_t force_align[HERMON_RECV_WQE_ALIGN];
+} __attribute__ (( packed ));
+
+/** A Hermon receive work queue */
+struct hermon_recv_work_queue {
+ /** Work queue entries */
+ union hermon_recv_wqe *wqe;
+ /** Size of work queue */
+ size_t wqe_size;
+ /** Doorbell */
+ struct hermonprm_qp_db_record doorbell __attribute__ (( aligned (4) ));
+};
+
+/** Maximum number of allocatable queue pairs
+ *
+ * This is a policy decision, not a device limit.
+ */
+#define HERMON_MAX_QPS 8
+
+/** Base queue pair number */
+#define HERMON_QPN_BASE 0x550000
+
+/** A Hermon queue pair */
+struct hermon_queue_pair {
+ /** Work queue buffer */
+ void *wqe;
+ /** Size of work queue buffer */
+ size_t wqe_size;
+ /** MTT descriptor */
+ struct hermon_mtt mtt;
+ /** Send work queue */
+ struct hermon_send_work_queue send;
+ /** Receive work queue */
+ struct hermon_recv_work_queue recv;
+};
+
+/** Maximum number of allocatable completion queues
+ *
+ * This is a policy decision, not a device limit.
+ */
+#define HERMON_MAX_CQS 8
+
+/** A Hermon completion queue */
+struct hermon_completion_queue {
+ /** Completion queue entries */
+ union hermonprm_completion_entry *cqe;
+ /** Size of completion queue */
+ size_t cqe_size;
+ /** MTT descriptor */
+ struct hermon_mtt mtt;
+ /** Doorbell */
+ struct hermonprm_cq_db_record doorbell __attribute__ (( aligned (8) ));
+};
+
+/** Maximum number of allocatable event queues
+ *
+ * This is a policy decision, not a device limit.
+ */
+#define HERMON_MAX_EQS 4
+
+/** A Hermon resource bitmask */
+typedef uint32_t hermon_bitmask_t;
+
+/** Size of a hermon resource bitmask */
+#define HERMON_BITMASK_SIZE(max_entries) \
+ ( ( (max_entries) + ( 8 * sizeof ( hermon_bitmask_t ) ) - 1 ) / \
+ ( 8 * sizeof ( hermon_bitmask_t ) ) )
+
+/** A Hermon device */
+struct hermon {
+ /** PCI configuration registers */
+ void *config;
+ /** PCI user Access Region */
+ void *uar;
+
+ /** Command toggle */
+ unsigned int toggle;
+ /** Command input mailbox */
+ void *mailbox_in;
+ /** Command output mailbox */
+ void *mailbox_out;
+
+ /** Firmware area in external memory */
+ userptr_t firmware_area;
+ /** ICM map */
+ struct hermon_icm_map icm_map[HERMON_ICM_NUM_REGIONS];
+ /** ICM area */
+ userptr_t icm;
+
+ /** Reserved LKey
+ *
+ * Used to get unrestricted memory access.
+ */
+ unsigned long reserved_lkey;
+
+ /** Completion queue in-use bitmask */
+ hermon_bitmask_t cq_inuse[ HERMON_BITMASK_SIZE ( HERMON_MAX_CQS ) ];
+ /** Queue pair in-use bitmask */
+ hermon_bitmask_t qp_inuse[ HERMON_BITMASK_SIZE ( HERMON_MAX_QPS ) ];
+ /** MTT entry in-use bitmask */
+ hermon_bitmask_t mtt_inuse[ HERMON_BITMASK_SIZE ( HERMON_MAX_MTTS ) ];
+
+ /** Device capabilities */
+ struct hermon_dev_cap cap;
+
+ /** Infiniband devices */
+ struct ib_device *ibdev[HERMON_NUM_PORTS];
+};
+
+/** Global protection domain */
+#define HERMON_GLOBAL_PD 0x123456
+
+/** Memory key prefix */
+#define HERMON_MKEY_PREFIX 0x77000000UL
+
+/*
+ * HCA commands
+ *
+ */
+
+#define HERMON_HCR_BASE 0x80680
+#define HERMON_HCR_REG(x) ( HERMON_HCR_BASE + 4 * (x) )
+#define HERMON_HCR_MAX_WAIT_MS 2000
+#define HERMON_MBOX_ALIGN 4096
+#define HERMON_MBOX_SIZE 512
+
+/* HCA command is split into
+ *
+ * bits 11:0 Opcode
+ * bit 12 Input uses mailbox
+ * bit 13 Output uses mailbox
+ * bits 22:14 Input parameter length (in dwords)
+ * bits 31:23 Output parameter length (in dwords)
+ *
+ * Encoding the information in this way allows us to cut out several
+ * parameters to the hermon_command() call.
+ */
+#define HERMON_HCR_IN_MBOX 0x00001000UL
+#define HERMON_HCR_OUT_MBOX 0x00002000UL
+#define HERMON_HCR_OPCODE( _command ) ( (_command) & 0xfff )
+#define HERMON_HCR_IN_LEN( _command ) ( ( (_command) >> 12 ) & 0x7fc )
+#define HERMON_HCR_OUT_LEN( _command ) ( ( (_command) >> 21 ) & 0x7fc )
+
+/** Build HCR command from component parts */
+#define HERMON_HCR_INOUT_CMD( _opcode, _in_mbox, _in_len, \
+ _out_mbox, _out_len ) \
+ ( (_opcode) | \
+ ( (_in_mbox) ? HERMON_HCR_IN_MBOX : 0 ) | \
+ ( ( (_in_len) / 4 ) << 14 ) | \
+ ( (_out_mbox) ? HERMON_HCR_OUT_MBOX : 0 ) | \
+ ( ( (_out_len) / 4 ) << 23 ) )
+
+#define HERMON_HCR_IN_CMD( _opcode, _in_mbox, _in_len ) \
+ HERMON_HCR_INOUT_CMD ( _opcode, _in_mbox, _in_len, 0, 0 )
+
+#define HERMON_HCR_OUT_CMD( _opcode, _out_mbox, _out_len ) \
+ HERMON_HCR_INOUT_CMD ( _opcode, 0, 0, _out_mbox, _out_len )
+
+#define HERMON_HCR_VOID_CMD( _opcode ) \
+ HERMON_HCR_INOUT_CMD ( _opcode, 0, 0, 0, 0 )
+
+#endif /* _HERMON_H */
diff --git a/gpxe/src/drivers/infiniband/mlx_bitops.h b/gpxe/src/drivers/infiniband/mlx_bitops.h
new file mode 100644
index 00000000..ec57d7b0
--- /dev/null
+++ b/gpxe/src/drivers/infiniband/mlx_bitops.h
@@ -0,0 +1,209 @@
+#ifndef _MLX_BITOPS_H
+#define _MLX_BITOPS_H
+
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Mellanox bit operations
+ *
+ */
+
+/* Datatype used to represent a bit in the Mellanox autogenerated headers */
+typedef unsigned char pseudo_bit_t;
+
+/**
+ * Wrapper structure for pseudo_bit_t structures
+ *
+ * This structure provides a wrapper around the autogenerated
+ * pseudo_bit_t structures. It has the correct size, and also
+ * encapsulates type information about the underlying pseudo_bit_t
+ * structure, which allows the MLX_FILL etc. macros to work without
+ * requiring explicit type information.
+ */
+#define MLX_DECLARE_STRUCT( _structure ) \
+ _structure { \
+ union { \
+ uint8_t bytes[ sizeof ( struct _structure ## _st ) / 8 ]; \
+ uint32_t dwords[ sizeof ( struct _structure ## _st ) / 32 ]; \
+ struct _structure ## _st *dummy[0]; \
+ } u; \
+ }
+
+/** Get pseudo_bit_t structure type from wrapper structure pointer */
+#define MLX_PSEUDO_STRUCT( _ptr ) \
+ typeof ( *((_ptr)->u.dummy[0]) )
+
+/** Bit offset of a field within a pseudo_bit_t structure */
+#define MLX_BIT_OFFSET( _structure_st, _field ) \
+ offsetof ( _structure_st, _field )
+
+/** Dword offset of a field within a pseudo_bit_t structure */
+#define MLX_DWORD_OFFSET( _structure_st, _field ) \
+ ( MLX_BIT_OFFSET ( _structure_st, _field ) / 32 )
+
+/** Dword bit offset of a field within a pseudo_bit_t structure
+ *
+ * Yes, using mod-32 would work, but would lose the check for the
+ * error of specifying a mismatched field name and dword index.
+ */
+#define MLX_DWORD_BIT_OFFSET( _structure_st, _index, _field ) \
+ ( MLX_BIT_OFFSET ( _structure_st, _field ) - ( 32 * (_index) ) )
+
+/** Bit width of a field within a pseudo_bit_t structure */
+#define MLX_BIT_WIDTH( _structure_st, _field ) \
+ sizeof ( ( ( _structure_st * ) NULL )->_field )
+
+/** Bit mask for a field within a pseudo_bit_t structure */
+#define MLX_BIT_MASK( _structure_st, _field ) \
+ ( ( ~( ( uint32_t ) 0 ) ) >> \
+ ( 32 - MLX_BIT_WIDTH ( _structure_st, _field ) ) )
+
+/*
+ * Assemble native-endian dword from named fields and values
+ *
+ */
+
+#define MLX_ASSEMBLE_1( _structure_st, _index, _field, _value ) \
+ ( (_value) << MLX_DWORD_BIT_OFFSET ( _structure_st, _index, _field ) )
+
+#define MLX_ASSEMBLE_2( _structure_st, _index, _field, _value, ... ) \
+ ( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \
+ MLX_ASSEMBLE_1 ( _structure_st, _index, __VA_ARGS__ ) )
+
+#define MLX_ASSEMBLE_3( _structure_st, _index, _field, _value, ... ) \
+ ( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \
+ MLX_ASSEMBLE_2 ( _structure_st, _index, __VA_ARGS__ ) )
+
+#define MLX_ASSEMBLE_4( _structure_st, _index, _field, _value, ... ) \
+ ( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \
+ MLX_ASSEMBLE_3 ( _structure_st, _index, __VA_ARGS__ ) )
+
+#define MLX_ASSEMBLE_5( _structure_st, _index, _field, _value, ... ) \
+ ( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \
+ MLX_ASSEMBLE_4 ( _structure_st, _index, __VA_ARGS__ ) )
+
+#define MLX_ASSEMBLE_6( _structure_st, _index, _field, _value, ... ) \
+ ( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \
+ MLX_ASSEMBLE_5 ( _structure_st, _index, __VA_ARGS__ ) )
+
+/*
+ * Build native-endian (positive) dword bitmasks from named fields
+ *
+ */
+
+#define MLX_MASK_1( _structure_st, _index, _field ) \
+ ( MLX_BIT_MASK ( _structure_st, _field ) << \
+ MLX_DWORD_BIT_OFFSET ( _structure_st, _index, _field ) )
+
+#define MLX_MASK_2( _structure_st, _index, _field, ... ) \
+ ( MLX_MASK_1 ( _structure_st, _index, _field ) | \
+ MLX_MASK_1 ( _structure_st, _index, __VA_ARGS__ ) )
+
+#define MLX_MASK_3( _structure_st, _index, _field, ... ) \
+ ( MLX_MASK_1 ( _structure_st, _index, _field ) | \
+ MLX_MASK_2 ( _structure_st, _index, __VA_ARGS__ ) )
+
+#define MLX_MASK_4( _structure_st, _index, _field, ... ) \
+ ( MLX_MASK_1 ( _structure_st, _index, _field ) | \
+ MLX_MASK_3 ( _structure_st, _index, __VA_ARGS__ ) )
+
+#define MLX_MASK_5( _structure_st, _index, _field, ... ) \
+ ( MLX_MASK_1 ( _structure_st, _index, _field ) | \
+ MLX_MASK_4 ( _structure_st, _index, __VA_ARGS__ ) )
+
+#define MLX_MASK_6( _structure_st, _index, _field, ... ) \
+ ( MLX_MASK_1 ( _structure_st, _index, _field ) | \
+ MLX_MASK_5 ( _structure_st, _index, __VA_ARGS__ ) )
+
+/*
+ * Populate big-endian dwords from named fields and values
+ *
+ */
+
+#define MLX_FILL( _ptr, _index, _assembled ) \
+ do { \
+ uint32_t *__ptr = &(_ptr)->u.dwords[(_index)]; \
+ uint32_t __assembled = (_assembled); \
+ *__ptr = cpu_to_be32 ( __assembled ); \
+ } while ( 0 )
+
+#define MLX_FILL_1( _ptr, _index, ... ) \
+ MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_1 ( MLX_PSEUDO_STRUCT ( _ptr ),\
+ _index, __VA_ARGS__ ) )
+
+#define MLX_FILL_2( _ptr, _index, ... ) \
+ MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_2 ( MLX_PSEUDO_STRUCT ( _ptr ),\
+ _index, __VA_ARGS__ ) )
+
+#define MLX_FILL_3( _ptr, _index, ... ) \
+ MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_3 ( MLX_PSEUDO_STRUCT ( _ptr ),\
+ _index, __VA_ARGS__ ) )
+
+#define MLX_FILL_4( _ptr, _index, ... ) \
+ MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_4 ( MLX_PSEUDO_STRUCT ( _ptr ),\
+ _index, __VA_ARGS__ ) )
+
+#define MLX_FILL_5( _ptr, _index, ... ) \
+ MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_5 ( MLX_PSEUDO_STRUCT ( _ptr ),\
+ _index, __VA_ARGS__ ) )
+
+#define MLX_FILL_6( _ptr, _index, ... ) \
+ MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_6 ( MLX_PSEUDO_STRUCT ( _ptr ),\
+ _index, __VA_ARGS__ ) )
+
+/*
+ * Modify big-endian dword using named field and value
+ *
+ */
+
+#define MLX_SET( _ptr, _field, _value ) \
+ do { \
+ unsigned int __index = \
+ MLX_DWORD_OFFSET ( MLX_PSEUDO_STRUCT ( _ptr ), _field ); \
+ uint32_t *__ptr = &(_ptr)->u.dwords[__index]; \
+ uint32_t __value = be32_to_cpu ( *__ptr ); \
+ __value &= ~( MLX_MASK_1 ( MLX_PSEUDO_STRUCT ( _ptr ), \
+ __index, _field ) ); \
+ __value |= MLX_ASSEMBLE_1 ( MLX_PSEUDO_STRUCT ( _ptr ), \
+ __index, _field, _value ); \
+ *__ptr = cpu_to_be32 ( __value ); \
+ } while ( 0 )
+
+/*
+ * Extract value of named field
+ *
+ */
+
+#define MLX_GET( _ptr, _field ) \
+ ( { \
+ unsigned int __index = \
+ MLX_DWORD_OFFSET ( MLX_PSEUDO_STRUCT ( _ptr ), _field ); \
+ uint32_t *__ptr = &(_ptr)->u.dwords[__index]; \
+ uint32_t __value = be32_to_cpu ( *__ptr ); \
+ __value >>= \
+ MLX_DWORD_BIT_OFFSET ( MLX_PSEUDO_STRUCT ( _ptr ), \
+ __index, _field ); \
+ __value &= \
+ MLX_BIT_MASK ( MLX_PSEUDO_STRUCT ( _ptr ), _field ); \
+ __value; \
+ } )
+
+#endif /* _MLX_BITOPS_H */
diff --git a/gpxe/src/drivers/net/3c509-eisa.c b/gpxe/src/drivers/net/3c509-eisa.c
new file mode 100644
index 00000000..d57c05b4
--- /dev/null
+++ b/gpxe/src/drivers/net/3c509-eisa.c
@@ -0,0 +1,49 @@
+/*
+ * Split out from 3c509.c, since EISA cards are relatively rare, and
+ * ROM space in 3c509s is very limited.
+ *
+ */
+
+#include <gpxe/eisa.h>
+#include <gpxe/isa.h>
+#include "console.h"
+#include "3c509.h"
+
+/*
+ * The EISA probe function
+ *
+ */
+static int el3_eisa_probe ( struct nic *nic, struct eisa_device *eisa ) {
+
+
+ nic->ioaddr = eisa->ioaddr;
+ nic->irqno = 0;
+ enable_eisa_device ( eisa );
+
+ /* Hand off to generic t5x9 probe routine */
+ return t5x9_probe ( nic, ISA_PROD_ID ( PROD_ID ), ISA_PROD_ID_MASK );
+}
+
+static void el3_eisa_disable ( struct nic *nic, struct eisa_device *eisa ) {
+ t5x9_disable ( nic );
+ disable_eisa_device ( eisa );
+}
+
+static struct eisa_device_id el3_eisa_adapters[] = {
+ { "3Com 3c509 EtherLink III (EISA)", MFG_ID, PROD_ID },
+};
+
+EISA_DRIVER ( el3_eisa_driver, el3_eisa_adapters );
+
+DRIVER ( "3c509 (EISA)", nic_driver, eisa_driver, el3_eisa_driver,
+ el3_eisa_probe, el3_eisa_disable );
+
+ISA_ROM ( "3c509-eisa","3c509 (EISA)" );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/3c509.c b/gpxe/src/drivers/net/3c509.c
new file mode 100644
index 00000000..8a15aff2
--- /dev/null
+++ b/gpxe/src/drivers/net/3c509.c
@@ -0,0 +1,430 @@
+/*
+ * Split out into 3c509.c and 3c5x9.c, to make it possible to build a
+ * 3c529 module without including ISA, ISAPnP and EISA code.
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <io.h>
+#include <unistd.h>
+#include <gpxe/device.h>
+#include <gpxe/isa.h>
+#include "3c509.h"
+
+/*
+ * 3c509 cards have their own method of contention resolution; this
+ * effectively defines another bus type similar to ISAPnP. Even the
+ * original ISA cards can be programatically mapped to any I/O address
+ * in the range 0x200-0x3e0.
+ *
+ * However, there is a small problem: once you've activated a card,
+ * the only ways to deactivate it will also wipe its tag, meaning that
+ * you won't be able to subsequently reactivate it without going
+ * through the whole ID sequence again. The solution we adopt is to
+ * isolate and tag all cards at the start, and to immediately
+ * re-isolate and re-tag a card after disabling it.
+ *
+ */
+
+static void t509bus_remove ( struct root_device *rootdev );
+
+static unsigned int t509_id_port = 0;
+static unsigned int t509_max_tag = 0;
+
+/** A 3c509 device */
+struct t509_device {
+ /** Generic device */
+ struct device dev;
+ /** Tag */
+ unsigned int tag;
+ /** I/O address */
+ uint16_t ioaddr;
+ /** Driver-private data
+ *
+ * Use t509_set_drvdata() and t509_get_drvdata() to access
+ * this field.
+ */
+ void *priv;
+};
+
+/**
+ * Set 3c509 driver-private data
+ *
+ * @v t509 3c509 device
+ * @v priv Private data
+ */
+static inline void t509_set_drvdata ( struct t509_device *t509, void *priv ) {
+ t509->priv = priv;
+}
+
+/**
+ * Get 3c509 driver-private data
+ *
+ * @v t509 3c509 device
+ * @ret priv Private data
+ */
+static inline void * t509_get_drvdata ( struct t509_device *t509 ) {
+ return t509->priv;
+}
+
+/*
+ * t509 utility functions
+ *
+ */
+
+static inline void t509_set_id_port ( void ) {
+ outb ( 0x00, t509_id_port );
+}
+
+static inline void t509_wait_for_id_sequence ( void ) {
+ outb ( 0x00, t509_id_port );
+}
+
+static inline void t509_global_reset ( void ) {
+ outb ( 0xc0, t509_id_port );
+}
+
+static inline void t509_reset_tag ( void ) {
+ outb ( 0xd0, t509_id_port );
+}
+
+static inline void t509_set_tag ( uint8_t tag ) {
+ outb ( 0xd0 | tag, t509_id_port );
+}
+
+static inline void t509_select_tag ( uint8_t tag ) {
+ outb ( 0xd8 | tag, t509_id_port );
+}
+
+static inline void t509_activate ( uint16_t ioaddr ) {
+ outb ( 0xe0 | ( ioaddr >> 4 ), t509_id_port );
+}
+
+static inline void t509_deactivate_and_reset_tag ( uint16_t ioaddr ) {
+ outb ( GLOBAL_RESET, ioaddr + EP_COMMAND );
+}
+
+static inline void t509_load_eeprom_word ( uint8_t offset ) {
+ outb ( 0x80 | offset, t509_id_port );
+}
+
+/*
+ * Find a suitable ID port
+ *
+ */
+static inline int t509_find_id_port ( void ) {
+
+ for ( t509_id_port = EP_ID_PORT_START ;
+ t509_id_port < EP_ID_PORT_END ;
+ t509_id_port += EP_ID_PORT_INC ) {
+ t509_set_id_port ();
+ /* See if anything's listening */
+ outb ( 0xff, t509_id_port );
+ if ( inb ( t509_id_port ) & 0x01 ) {
+ /* Found a suitable port */
+ DBG ( "T509 using ID port at %04x\n", t509_id_port );
+ return 0;
+ }
+ }
+ /* No id port available */
+ DBG ( "T509 found no available ID port\n" );
+ return -ENOENT;
+}
+
+/*
+ * Send ID sequence to the ID port
+ *
+ */
+static void t509_send_id_sequence ( void ) {
+ unsigned short lrs_state, i;
+
+ t509_set_id_port ();
+ /* Reset IDS on cards */
+ t509_wait_for_id_sequence ();
+ lrs_state = 0xff;
+ for ( i = 0; i < 255; i++ ) {
+ outb ( lrs_state, t509_id_port );
+ lrs_state <<= 1;
+ lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
+ }
+}
+
+/*
+ * We get eeprom data from the id_port given an offset into the eeprom.
+ * Basically; after the ID_sequence is sent to all of the cards; they enter
+ * the ID_CMD state where they will accept command requests. 0x80-0xbf loads
+ * the eeprom data. We then read the port 16 times and with every read; the
+ * cards check for contention (ie: if one card writes a 0 bit and another
+ * writes a 1 bit then the host sees a 0. At the end of the cycle; each card
+ * compares the data on the bus; if there is a difference then that card goes
+ * into ID_WAIT state again). In the meantime; one bit of data is returned in
+ * the AX register which is conveniently returned to us by inb(). Hence; we
+ * read 16 times getting one bit of data with each read.
+ */
+static uint16_t t509_id_read_eeprom ( int offset ) {
+ int i, data = 0;
+
+ t509_load_eeprom_word ( offset );
+ /* Do we really need this wait? Won't be noticeable anyway */
+ udelay(10000);
+
+ for ( i = 0; i < 16; i++ ) {
+ data = ( data << 1 ) | ( inw ( t509_id_port ) & 1 );
+ }
+ return data;
+}
+
+/*
+ * Isolate and tag all t509 cards
+ *
+ */
+static int t509_isolate ( void ) {
+ unsigned int i;
+ uint16_t contend[3];
+ int rc;
+
+ /* Find a suitable ID port */
+ if ( ( rc = t509_find_id_port() ) != 0 )
+ return rc;
+
+ while ( 1 ) {
+
+ /* All cards are in ID_WAIT state each time we go
+ * through this loop.
+ */
+
+ /* Send the ID sequence */
+ t509_send_id_sequence();
+
+ /* First time through, reset all tags. On subsequent
+ * iterations, kill off any already-tagged cards
+ */
+ if ( t509_max_tag == 0 ) {
+ t509_reset_tag();
+ } else {
+ t509_select_tag ( 0 );
+ }
+
+ /* Read the manufacturer ID, to see if there are any
+ * more cards
+ */
+ if ( t509_id_read_eeprom ( EEPROM_MFG_ID ) != MFG_ID ) {
+ DBG ( "T509 saw %s signs of life\n",
+ t509_max_tag ? "no further" : "no" );
+ break;
+ }
+
+ /* Perform contention selection on the MAC address */
+ for ( i = 0 ; i < 3 ; i++ ) {
+ contend[i] = t509_id_read_eeprom ( i );
+ }
+
+ /* Only one device will still be left alive. Tag it. */
+ ++t509_max_tag;
+ DBG ( "T509 found card %04x%04x%04x, assigning tag %02x\n",
+ contend[0], contend[1], contend[2], t509_max_tag );
+ t509_set_tag ( t509_max_tag );
+
+ /* Return all cards back to ID_WAIT state */
+ t509_wait_for_id_sequence();
+ }
+
+ DBG ( "T509 found %d cards using ID port %04x\n",
+ t509_max_tag, t509_id_port );
+ return 0;
+}
+
+/*
+ * Activate a T509 device
+ *
+ * The device will be enabled at whatever ioaddr is specified in the
+ * struct t509_device; there is no need to stick with the default
+ * ioaddr read from the EEPROM.
+ *
+ */
+static inline void activate_t509_device ( struct t509_device *t509 ) {
+ t509_send_id_sequence ();
+ t509_select_tag ( t509->tag );
+ t509_activate ( t509->ioaddr );
+ DBG ( "T509 activated device %02x at ioaddr %04x\n",
+ t509->tag, t509->ioaddr );
+}
+
+/*
+ * Deactivate a T509 device
+ *
+ * Disabling also clears the tag, so we immediately isolate and re-tag
+ * this card.
+ *
+ */
+static inline void deactivate_t509_device ( struct t509_device *t509 ) {
+ t509_deactivate_and_reset_tag ( t509->ioaddr );
+ udelay ( 1000 );
+ t509_send_id_sequence ();
+ t509_select_tag ( 0 );
+ t509_set_tag ( t509->tag );
+ t509_wait_for_id_sequence ();
+ DBG ( "T509 deactivated device at %04x and re-tagged as %02x\n",
+ t509->ioaddr, t509->tag );
+}
+
+/*
+ * The ISA probe function
+ *
+ */
+static int legacy_t509_probe ( struct nic *nic, void *hwdev ) {
+ struct t509_device *t509 = hwdev;
+
+ /* We could change t509->ioaddr if we wanted to */
+ activate_t509_device ( t509 );
+ nic->ioaddr = t509->ioaddr;
+
+ /* Hand off to generic t5x9 probe routine */
+ return t5x9_probe ( nic, ISA_PROD_ID ( PROD_ID ), ISA_PROD_ID_MASK );
+}
+
+static void legacy_t509_disable ( struct nic *nic, void *hwdev ) {
+ struct t509_device *t509 = hwdev;
+
+ t5x9_disable ( nic );
+ deactivate_t509_device ( t509 );
+}
+
+static inline void legacy_t509_set_drvdata ( void *hwdev, void *priv ) {
+ t509_set_drvdata ( hwdev, priv );
+}
+
+static inline void * legacy_t509_get_drvdata ( void *hwdev ) {
+ return t509_get_drvdata ( hwdev );
+}
+
+/**
+ * Probe a 3c509 device
+ *
+ * @v t509 3c509 device
+ * @ret rc Return status code
+ *
+ * Searches for a driver for the 3c509 device. If a driver is found,
+ * its probe() routine is called.
+ */
+static int t509_probe ( struct t509_device *t509 ) {
+ DBG ( "Adding 3c509 device %02x (I/O %04x)\n",
+ t509->tag, t509->ioaddr );
+ return legacy_probe ( t509, legacy_t509_set_drvdata, &t509->dev,
+ legacy_t509_probe, legacy_t509_disable );
+}
+
+/**
+ * Remove a 3c509 device
+ *
+ * @v t509 3c509 device
+ */
+static void t509_remove ( struct t509_device *t509 ) {
+ legacy_remove ( t509, legacy_t509_get_drvdata, legacy_t509_disable );
+ DBG ( "Removed 3c509 device %02x\n", t509->tag );
+}
+
+/**
+ * Probe 3c509 root bus
+ *
+ * @v rootdev 3c509 bus root device
+ *
+ * Scans the 3c509 bus for devices and registers all devices it can
+ * find.
+ */
+static int t509bus_probe ( struct root_device *rootdev ) {
+ struct t509_device *t509 = NULL;
+ unsigned int tag;
+ unsigned int iobase;
+ int rc;
+
+ /* Perform isolation and tagging */
+ if ( ( rc = t509_isolate() ) != 0 )
+ return rc;
+
+ for ( tag = 1 ; tag <= t509_max_tag ; tag++ ) {
+ /* Allocate struct t509_device */
+ if ( ! t509 )
+ t509 = malloc ( sizeof ( *t509 ) );
+ if ( ! t509 ) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset ( t509, 0, sizeof ( *t509 ) );
+ t509->tag = tag;
+
+ /* Send the ID sequence */
+ t509_send_id_sequence ();
+
+ /* Select the specified tag */
+ t509_select_tag ( t509->tag );
+
+ /* Read the default I/O address */
+ iobase = t509_id_read_eeprom ( EEPROM_ADDR_CFG );
+ t509->ioaddr = 0x200 + ( ( iobase & 0x1f ) << 4 );
+
+ /* Send card back to ID_WAIT */
+ t509_wait_for_id_sequence();
+
+ /* Add to device hierarchy */
+ snprintf ( t509->dev.name, sizeof ( t509->dev.name ),
+ "t509%02x", tag );
+ t509->dev.desc.bus_type = BUS_TYPE_ISA;
+ t509->dev.desc.vendor = MFG_ID;
+ t509->dev.desc.device = PROD_ID;
+ t509->dev.parent = &rootdev->dev;
+ list_add ( &t509->dev.siblings, &rootdev->dev.children );
+ INIT_LIST_HEAD ( &t509->dev.children );
+
+ /* Look for a driver */
+ if ( t509_probe ( t509 ) == 0 ) {
+ /* t509dev registered, we can drop our ref */
+ t509 = NULL;
+ } else {
+ /* Not registered; re-use struct */
+ list_del ( &t509->dev.siblings );
+ }
+ }
+
+ free ( t509 );
+ return 0;
+
+ err:
+ free ( t509 );
+ t509bus_remove ( rootdev );
+ return rc;
+}
+
+/**
+ * Remove 3c509 root bus
+ *
+ * @v rootdev 3c509 bus root device
+ */
+static void t509bus_remove ( struct root_device *rootdev ) {
+ struct t509_device *t509;
+ struct t509_device *tmp;
+
+ list_for_each_entry_safe ( t509, tmp, &rootdev->dev.children,
+ dev.siblings ) {
+ t509_remove ( t509 );
+ list_del ( &t509->dev.siblings );
+ free ( t509 );
+ }
+}
+
+/** 3c509 bus root device driver */
+static struct root_driver t509_root_driver = {
+ .probe = t509bus_probe,
+ .remove = t509bus_remove,
+};
+
+/** 3c509 bus root device */
+struct root_device t509_root_device __root_device = {
+ .dev = { .name = "3c509" },
+ .driver = &t509_root_driver,
+};
+
+ISA_ROM ( "3c509", "3c509" );
diff --git a/gpxe/src/drivers/net/3c509.h b/gpxe/src/drivers/net/3c509.h
new file mode 100644
index 00000000..a06d91ea
--- /dev/null
+++ b/gpxe/src/drivers/net/3c509.h
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2. The name
+ * of the author may not be used to endorse or promote products derived from
+ * this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * if_epreg.h,v 1.4 1994/11/13 10:12:37 gibbs Exp Modified by:
+ *
+ October 2, 1994
+
+ Modified by: Andres Vega Garcia
+
+ INRIA - Sophia Antipolis, France
+ e-mail: avega@sophia.inria.fr
+ finger: avega@pax.inria.fr
+
+ */
+
+#include "nic.h"
+
+/*
+ * Ethernet software status per interface.
+ */
+/*
+ * Some global constants
+ */
+
+#define TX_INIT_RATE 16
+#define TX_INIT_MAX_RATE 64
+#define RX_INIT_LATENCY 64
+#define RX_INIT_EARLY_THRESH 64
+#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */
+#define MIN_RX_EARLY_THRESHL 4
+
+#define EEPROMSIZE 0x40
+#define MAX_EEPROMBUSY 1000
+#define EP_ID_PORT_START 0x110 /* avoid 0x100 to avoid conflict with SB16 */
+#define EP_ID_PORT_INC 0x10
+#define EP_ID_PORT_END 0x200
+#define EP_TAG_MAX 0x7 /* must be 2^n - 1 */
+
+/*
+ * Commands to read/write EEPROM trough EEPROM command register (Window 0,
+ * Offset 0xa)
+ */
+#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */
+#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */
+#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */
+#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */
+
+#define EEPROM_BUSY (1<<15)
+#define EEPROM_TST_MODE (1<<14)
+
+/*
+ * Some short functions, worth to let them be a macro
+ */
+#define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY)
+#define GO_WINDOW(b,x) outw(WINDOW_SELECT|(x), (b)+EP_COMMAND)
+
+/**************************************************************************
+ *
+ * These define the EEPROM data structure. They are used in the probe
+ * function to verify the existance of the adapter after having sent
+ * the ID_Sequence.
+ *
+ * There are others but only the ones we use are defined here.
+ *
+ **************************************************************************/
+
+#define EEPROM_NODE_ADDR_0 0x0 /* Word */
+#define EEPROM_NODE_ADDR_1 0x1 /* Word */
+#define EEPROM_NODE_ADDR_2 0x2 /* Word */
+#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
+#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
+#define EEPROM_ADDR_CFG 0x8 /* Base addr */
+#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
+
+/**************************************************************************
+ *
+ * These are the registers for the 3Com 3c509 and their bit patterns when
+ * applicable. They have been taken out the the "EtherLink III Parallel
+ * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual
+ * from 3com.
+ *
+ * Getting this document out of 3Com is almost impossible. However,
+ * archived copies are available at
+ * http://www.osdever.net/cottontail/downloads/docs/3c5x9b.zip and
+ * several other places on the web (search for 3c5x9b.pdf).
+ *
+ **************************************************************************/
+
+#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a
+ * command reg. */
+#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status
+ * reg. */
+#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window
+ * reg. */
+/*
+ * Window 0 registers. Setup.
+ */
+/* Write */
+#define EP_W0_EEPROM_DATA 0x0c
+#define EP_W0_EEPROM_COMMAND 0x0a
+#define EP_W0_RESOURCE_CFG 0x08
+#define EP_W0_ADDRESS_CFG 0x06
+#define EP_W0_CONFIG_CTRL 0x04
+/* Read */
+#define EP_W0_PRODUCT_ID 0x02
+#define EP_W0_MFG_ID 0x00
+
+/*
+ * Window 1 registers. Operating Set.
+ */
+/* Write */
+#define EP_W1_TX_PIO_WR_2 0x02
+#define EP_W1_TX_PIO_WR_1 0x00
+/* Read */
+#define EP_W1_FREE_TX 0x0c
+#define EP_W1_TX_STATUS 0x0b /* byte */
+#define EP_W1_TIMER 0x0a /* byte */
+#define EP_W1_RX_STATUS 0x08
+#define EP_W1_RX_PIO_RD_2 0x02
+#define EP_W1_RX_PIO_RD_1 0x00
+
+/*
+ * Window 2 registers. Station Address Setup/Read
+ */
+/* Read/Write */
+#define EP_W2_ADDR_5 0x05
+#define EP_W2_ADDR_4 0x04
+#define EP_W2_ADDR_3 0x03
+#define EP_W2_ADDR_2 0x02
+#define EP_W2_ADDR_1 0x01
+#define EP_W2_ADDR_0 0x00
+
+/*
+ * Window 3 registers. FIFO Management.
+ */
+/* Read */
+#define EP_W3_FREE_TX 0x0c
+#define EP_W3_FREE_RX 0x0a
+
+/*
+ * Window 4 registers. Diagnostics.
+ */
+/* Read/Write */
+#define EP_W4_MEDIA_TYPE 0x0a
+#define EP_W4_CTRLR_STATUS 0x08
+#define EP_W4_NET_DIAG 0x06
+#define EP_W4_FIFO_DIAG 0x04
+#define EP_W4_HOST_DIAG 0x02
+#define EP_W4_TX_DIAG 0x00
+
+/*
+ * Window 5 Registers. Results and Internal status.
+ */
+/* Read */
+#define EP_W5_READ_0_MASK 0x0c
+#define EP_W5_INTR_MASK 0x0a
+#define EP_W5_RX_FILTER 0x08
+#define EP_W5_RX_EARLY_THRESH 0x06
+#define EP_W5_TX_AVAIL_THRESH 0x02
+#define EP_W5_TX_START_THRESH 0x00
+
+/*
+ * Window 6 registers. Statistics.
+ */
+/* Read/Write */
+#define TX_TOTAL_OK 0x0c
+#define RX_TOTAL_OK 0x0a
+#define TX_DEFERRALS 0x08
+#define RX_FRAMES_OK 0x07
+#define TX_FRAMES_OK 0x06
+#define RX_OVERRUNS 0x05
+#define TX_COLLISIONS 0x04
+#define TX_AFTER_1_COLLISION 0x03
+#define TX_AFTER_X_COLLISIONS 0x02
+#define TX_NO_SQE 0x01
+#define TX_CD_LOST 0x00
+
+/****************************************
+ *
+ * Register definitions.
+ *
+ ****************************************/
+
+/*
+ * Command register. All windows.
+ *
+ * 16 bit register.
+ * 15-11: 5-bit code for command to be executed.
+ * 10-0: 11-bit arg if any. For commands with no args;
+ * this can be set to anything.
+ */
+#define GLOBAL_RESET (unsigned short) 0x0000 /* Wait at least 1ms
+ * after issuing */
+#define WINDOW_SELECT (unsigned short) (0x1<<11)
+#define START_TRANSCEIVER (unsigned short) (0x2<<11) /* Read ADDR_CFG reg to
+ * determine whether
+ * this is needed. If
+ * so; wait 800 uSec
+ * before using trans-
+ * ceiver. */
+#define RX_DISABLE (unsigned short) (0x3<<11) /* state disabled on
+ * power-up */
+#define RX_ENABLE (unsigned short) (0x4<<11)
+#define RX_RESET (unsigned short) (0x5<<11)
+#define RX_DISCARD_TOP_PACK (unsigned short) (0x8<<11)
+#define TX_ENABLE (unsigned short) (0x9<<11)
+#define TX_DISABLE (unsigned short) (0xa<<11)
+#define TX_RESET (unsigned short) (0xb<<11)
+#define REQ_INTR (unsigned short) (0xc<<11)
+#define SET_INTR_MASK (unsigned short) (0xe<<11)
+#define SET_RD_0_MASK (unsigned short) (0xf<<11)
+#define SET_RX_FILTER (unsigned short) (0x10<<11)
+#define FIL_INDIVIDUAL (unsigned short) (0x1)
+#define FIL_GROUP (unsigned short) (0x2)
+#define FIL_BRDCST (unsigned short) (0x4)
+#define FIL_ALL (unsigned short) (0x8)
+#define SET_RX_EARLY_THRESH (unsigned short) (0x11<<11)
+#define SET_TX_AVAIL_THRESH (unsigned short) (0x12<<11)
+#define SET_TX_START_THRESH (unsigned short) (0x13<<11)
+#define STATS_ENABLE (unsigned short) (0x15<<11)
+#define STATS_DISABLE (unsigned short) (0x16<<11)
+#define STOP_TRANSCEIVER (unsigned short) (0x17<<11)
+/*
+ * The following C_* acknowledge the various interrupts. Some of them don't
+ * do anything. See the manual.
+ */
+#define ACK_INTR (unsigned short) (0x6800)
+#define C_INTR_LATCH (unsigned short) (ACK_INTR|0x1)
+#define C_CARD_FAILURE (unsigned short) (ACK_INTR|0x2)
+#define C_TX_COMPLETE (unsigned short) (ACK_INTR|0x4)
+#define C_TX_AVAIL (unsigned short) (ACK_INTR|0x8)
+#define C_RX_COMPLETE (unsigned short) (ACK_INTR|0x10)
+#define C_RX_EARLY (unsigned short) (ACK_INTR|0x20)
+#define C_INT_RQD (unsigned short) (ACK_INTR|0x40)
+#define C_UPD_STATS (unsigned short) (ACK_INTR|0x80)
+
+/*
+ * Status register. All windows.
+ *
+ * 15-13: Window number(0-7).
+ * 12: Command_in_progress.
+ * 11: reserved.
+ * 10: reserved.
+ * 9: reserved.
+ * 8: reserved.
+ * 7: Update Statistics.
+ * 6: Interrupt Requested.
+ * 5: RX Early.
+ * 4: RX Complete.
+ * 3: TX Available.
+ * 2: TX Complete.
+ * 1: Adapter Failure.
+ * 0: Interrupt Latch.
+ */
+#define S_INTR_LATCH (unsigned short) (0x1)
+#define S_CARD_FAILURE (unsigned short) (0x2)
+#define S_TX_COMPLETE (unsigned short) (0x4)
+#define S_TX_AVAIL (unsigned short) (0x8)
+#define S_RX_COMPLETE (unsigned short) (0x10)
+#define S_RX_EARLY (unsigned short) (0x20)
+#define S_INT_RQD (unsigned short) (0x40)
+#define S_UPD_STATS (unsigned short) (0x80)
+#define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\
+ S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY)
+#define S_COMMAND_IN_PROGRESS (unsigned short) (0x1000)
+
+/*
+ * FIFO Registers.
+ * RX Status. Window 1/Port 08
+ *
+ * 15: Incomplete or FIFO empty.
+ * 14: 1: Error in RX Packet 0: Incomplete or no error.
+ * 13-11: Type of error.
+ * 1000 = Overrun.
+ * 1011 = Run Packet Error.
+ * 1100 = Alignment Error.
+ * 1101 = CRC Error.
+ * 1001 = Oversize Packet Error (>1514 bytes)
+ * 0010 = Dribble Bits.
+ * (all other error codes, no errors.)
+ *
+ * 10-0: RX Bytes (0-1514)
+ */
+#define ERR_RX_INCOMPLETE (unsigned short) (0x1<<15)
+#define ERR_RX (unsigned short) (0x1<<14)
+#define ERR_RX_OVERRUN (unsigned short) (0x8<<11)
+#define ERR_RX_RUN_PKT (unsigned short) (0xb<<11)
+#define ERR_RX_ALIGN (unsigned short) (0xc<<11)
+#define ERR_RX_CRC (unsigned short) (0xd<<11)
+#define ERR_RX_OVERSIZE (unsigned short) (0x9<<11)
+#define ERR_RX_DRIBBLE (unsigned short) (0x2<<11)
+
+/*
+ * FIFO Registers.
+ * TX Status. Window 1/Port 0B
+ *
+ * Reports the transmit status of a completed transmission. Writing this
+ * register pops the transmit completion stack.
+ *
+ * Window 1/Port 0x0b.
+ *
+ * 7: Complete
+ * 6: Interrupt on successful transmission requested.
+ * 5: Jabber Error (TP Only, TX Reset required. )
+ * 4: Underrun (TX Reset required. )
+ * 3: Maximum Collisions.
+ * 2: TX Status Overflow.
+ * 1-0: Undefined.
+ *
+ */
+#define TXS_COMPLETE 0x80
+#define TXS_SUCCES_INTR_REQ 0x40
+#define TXS_JABBER 0x20
+#define TXS_UNDERRUN 0x10
+#define TXS_MAX_COLLISION 0x8
+#define TXS_STATUS_OVERFLOW 0x4
+
+/*
+ * Configuration control register.
+ * Window 0/Port 04
+ */
+/* Read */
+#define IS_AUI (1<<13)
+#define IS_BNC (1<<12)
+#define IS_UTP (1<<9)
+/* Write */
+#define ENABLE_DRQ_IRQ 0x0001
+#define W0_P4_CMD_RESET_ADAPTER 0x4
+#define W0_P4_CMD_ENABLE_ADAPTER 0x1
+/*
+ * Media type and status.
+ * Window 4/Port 0A
+ */
+#define ENABLE_UTP 0xc0
+#define DISABLE_UTP 0x0
+
+/*
+ * Resource control register
+ */
+
+#define SET_IRQ(i) ( ((i)<<12) | 0xF00) /* set IRQ i */
+
+/*
+ * Receive status register
+ */
+
+#define RX_BYTES_MASK (unsigned short) (0x07ff)
+#define RX_ERROR 0x4000
+#define RX_INCOMPLETE 0x8000
+
+/*
+ * Misc defines for various things.
+ */
+#define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */
+#define PROD_ID 0x9150
+
+#define AUI 0x1
+#define BNC 0x2
+#define UTP 0x4
+
+#define RX_BYTES_MASK (unsigned short) (0x07ff)
+
+/*
+ * Function shared between 3c509.c and 3c529.c
+ */
+extern int t5x9_probe ( struct nic *nic,
+ uint16_t prod_id_check, uint16_t prod_id_mask );
+extern void t5x9_disable ( struct nic *nic );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/3c515.c b/gpxe/src/drivers/net/3c515.c
new file mode 100644
index 00000000..02f03dc7
--- /dev/null
+++ b/gpxe/src/drivers/net/3c515.c
@@ -0,0 +1,762 @@
+/*
+* 3c515.c -- 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX driver for etherboot
+* Copyright (C) 2002 Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code:
+* Copyright (C) 1997-2002 Donald Becker 3c515.c: A 3Com ISA EtherLink XL "Corkscrew" ethernet driver for linux.
+* Copyright (C) 2001 P.J.H.Fox (fox@roestock.demon.co.uk) ISAPNP Tools
+* Copyright (c) 2002 Jaroslav Kysela <perex@suse.cz> ISA Plug & Play support Linux Kernel
+* Copyright (C) 2000 Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp> etherboot-5.0.5 3c595.c
+* Coptright (C) 1995 Martin Renters etherboot-5.0.5 3c509.c
+* Copyright (C) 1999 LightSys Technology Services, Inc. etherboot-5.0.5 3c90x.c
+* Portions Copyright (C) 1999 Steve Smith etherboot-5.0.5 3c90x.c
+*
+* The probe and reset functions and defines are direct copies from the
+* Becker code modified where necessary to make it work for etherboot
+*
+* The poll and transmit functions either contain code from or were written by referencing
+* the above referenced etherboot drivers. This driver would not have been
+* possible without this prior work
+*
+* REVISION HISTORY:
+* ================
+* v0.10 4-17-2002 TJL Initial implementation.
+* v0.11 4-17-2002 TJL Cleanup of the code
+* v0.12 4-26-2002 TJL Added ISA Plug and Play for Non-PNP Bioses
+* v0.13 6-10-2002 TJL Fixed ISA_PNP MAC Address problem
+* v0.14 9-23-2003 TJL Replaced delay with currticks
+*
+* Indent Options: indent -kr -i8
+* *********************************************************/
+
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+#include <gpxe/isapnp.h>
+#include <gpxe/isa.h> /* for ISA_ROM */
+#include <gpxe/ethernet.h>
+
+static void t3c515_wait(unsigned int nticks)
+{
+ unsigned int to = currticks() + nticks;
+ while (currticks() < to)
+ /* wait */ ;
+}
+
+/* TJL definations */
+#define HZ 100
+static int if_port;
+static struct corkscrew_private *vp;
+/* Brought directly from 3c515.c by Becker */
+#define CORKSCREW 1
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt.
+static int max_interrupt_work = 20;
+*/
+
+/* Enable the automatic media selection code -- usually set. */
+#define AUTOMEDIA 1
+
+/* Allow the use of fragment bus master transfers instead of only
+ programmed-I/O for Vortex cards. Full-bus-master transfers are always
+ enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined,
+ the feature may be turned on using 'options'. */
+#define VORTEX_BUS_MASTER
+
+/* A few values that may be tweaked. */
+/* Keep the ring sizes a power of two for efficiency. */
+#define TX_RING_SIZE 16
+#define RX_RING_SIZE 16
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
+
+/* "Knobs" for adjusting internal parameters. */
+/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
+#define DRIVER_DEBUG 1
+/* Some values here only for performance evaluation and path-coverage
+ debugging.
+static int rx_nocopy, rx_copy, queued_packet;
+*/
+
+#define CORKSCREW_ID 10
+
+#define EL3WINDOW(win_num) \
+ outw(SelectWindow + (win_num), nic->ioaddr + EL3_CMD)
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+#define RX_BYTES_MASK (unsigned short) (0x07ff)
+
+enum corkscrew_cmd {
+ TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
+ RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
+ UpStall = 6 << 11, UpUnstall = (6 << 11) + 1,
+ DownStall = (6 << 11) + 2, DownUnstall = (6 << 11) + 3,
+ RxDiscard = 8 << 11, TxEnable = 9 << 11, TxDisable =
+ 10 << 11, TxReset = 11 << 11,
+ FakeIntr = 12 << 11, AckIntr = 13 << 11, SetIntrEnb = 14 << 11,
+ SetStatusEnb = 15 << 11, SetRxFilter = 16 << 11, SetRxThreshold =
+ 17 << 11,
+ SetTxThreshold = 18 << 11, SetTxStart = 19 << 11,
+ StartDMAUp = 20 << 11, StartDMADown = (20 << 11) + 1, StatsEnable =
+ 21 << 11,
+ StatsDisable = 22 << 11, StopCoax = 23 << 11,
+};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
+};
+
+/* Bits in the general status register. */
+enum corkscrew_status {
+ IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+ TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+ IntReq = 0x0040, StatsFull = 0x0080,
+ DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10,
+ DMAInProgress = 1 << 11, /* DMA controller is still busy. */
+ CmdInProgress = 1 << 12, /* EL3_CMD is still busy. */
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+ On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */
+enum Window1 {
+ TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
+ RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B,
+ TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
+};
+enum Window0 {
+ Wn0IRQ = 0x08,
+#if defined(CORKSCREW)
+ Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */
+ Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */
+#else
+ Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
+ Wn0EepromData = 12, /* Window 0: EEPROM results register. */
+#endif
+};
+enum Win0_EEPROM_bits {
+ EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
+ EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
+ EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
+};
+
+enum Window3 { /* Window 3: MAC/config bits. */
+ Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8,
+};
+union wn3_config {
+ int i;
+ struct w3_config_fields {
+ unsigned int ram_size:3, ram_width:1, ram_speed:2,
+ rom_size:2;
+ int pad8:8;
+ unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1,
+ autoselect:1;
+ int pad24:7;
+ } u;
+};
+
+enum Window4 {
+ Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */
+};
+enum Win4_Media_bits {
+ Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */
+ Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
+ Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */
+ Media_LnkBeat = 0x0800,
+};
+enum Window7 { /* Window 7: Bus Master control. */
+ Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
+};
+
+/* Boomerang-style bus master control registers. Note ISA aliases! */
+enum MasterCtrl {
+ PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen =
+ 0x40c,
+ TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418,
+};
+
+/* The Rx and Tx descriptor lists.
+ Caution Alpha hackers: these types are 32 bits! Note also the 8 byte
+ alignment contraint on tx_ring[] and rx_ring[]. */
+struct boom_rx_desc {
+ u32 next;
+ s32 status;
+ u32 addr;
+ s32 length;
+};
+
+/* Values for the Rx status entry. */
+enum rx_desc_status {
+ RxDComplete = 0x00008000, RxDError = 0x4000,
+ /* See boomerang_rx() for actual error bits */
+};
+
+struct boom_tx_desc {
+ u32 next;
+ s32 status;
+ u32 addr;
+ s32 length;
+};
+
+struct corkscrew_private {
+ const char *product_name;
+ struct net_device *next_module;
+ /* The Rx and Tx rings are here to keep them quad-word-aligned. */
+ struct boom_rx_desc rx_ring[RX_RING_SIZE];
+ struct boom_tx_desc tx_ring[TX_RING_SIZE];
+ /* The addresses of transmit- and receive-in-place skbuffs. */
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
+ int capabilities; /* Adapter capabilities word. */
+ int options; /* User-settable misc. driver options. */
+ int last_rx_packets; /* For media autoselection. */
+ unsigned int available_media:8, /* From Wn3_Options */
+ media_override:3, /* Passed-in media type. */
+ default_media:3, /* Read from the EEPROM. */
+ full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */
+ full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */
+ tx_full:1;
+};
+
+/* The action to take with a media selection timer tick.
+ Note that we deviate from the 3Com order by checking 10base2 before AUI.
+ */
+enum xcvr_types {
+ XCVR_10baseT =
+ 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
+ XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
+};
+
+static struct media_table {
+ char *name;
+ unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
+ mask:8, /* The transceiver-present bit in Wn3_Config. */
+ next:8; /* The media type to try next. */
+ short wait; /* Time before we check media status. */
+} media_tbl[] = {
+ {
+ "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10}
+ , {
+ "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10}
+ , {
+ "undefined", 0, 0x80, XCVR_10baseT, 10000}
+ , {
+ "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10}
+ , {
+ "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx,
+ (14 * HZ) / 10}
+ , {
+ "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10}
+ , {
+ "MII", 0, 0x40, XCVR_10baseT, 3 * HZ}
+ , {
+ "undefined", 0, 0x01, XCVR_10baseT, 10000}
+ , {
+ "Default", 0, 0xFF, XCVR_10baseT, 10000}
+,};
+
+/* TILEG Modified to remove reference to dev */
+static int corkscrew_found_device(int ioaddr, int irq, int product_index,
+ int options, struct nic *nic);
+static int corkscrew_probe1(int ioaddr, int irq, int product_index,
+ struct nic *nic);
+
+/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
+/* Note: this is the only limit on the number of cards supported!! */
+static int options = -1;
+
+/* End Brought directly from 3c515.c by Becker */
+
+/**************************************************************************
+RESET - Reset adapter
+***************************************************************************/
+static void t515_reset(struct nic *nic)
+{
+ union wn3_config config;
+ int i;
+
+ /* Before initializing select the active media port. */
+ EL3WINDOW(3);
+ if (vp->full_duplex)
+ outb(0x20, nic->ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
+ config.i = inl(nic->ioaddr + Wn3_Config);
+
+ if (vp->media_override != 7) {
+ DBG ( "Media override to transceiver %d (%s).\n",
+ vp->media_override,
+ media_tbl[vp->media_override].name);
+ if_port = vp->media_override;
+ } else if (vp->autoselect) {
+ /* Find first available media type, starting with 100baseTx. */
+ if_port = 4;
+ while (!(vp->available_media & media_tbl[if_port].mask))
+ if_port = media_tbl[if_port].next;
+
+ DBG ( "Initial media type %s.\n",
+ media_tbl[if_port].name);
+ } else
+ if_port = vp->default_media;
+
+ config.u.xcvr = if_port;
+ outl(config.i, nic->ioaddr + Wn3_Config);
+
+ DBG ( "corkscrew_open() InternalConfig 0x%hX.\n",
+ config.i);
+
+ outw(TxReset, nic->ioaddr + EL3_CMD);
+ for (i = 20; i >= 0; i--)
+ if (!(inw(nic->ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+
+ outw(RxReset, nic->ioaddr + EL3_CMD);
+ /* Wait a few ticks for the RxReset command to complete. */
+ for (i = 20; i >= 0; i--)
+ if (!(inw(nic->ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+
+ outw(SetStatusEnb | 0x00, nic->ioaddr + EL3_CMD);
+
+#ifdef debug_3c515
+ EL3WINDOW(4);
+ DBG ( "FIXME: fix print for irq, not 9" );
+ DBG ( "corkscrew_open() irq %d media status 0x%hX.\n",
+ 9, inw(nic->ioaddr + Wn4_Media) );
+#endif
+
+ /* Set the station address and mask in window 2 each time opened. */
+ EL3WINDOW(2);
+ for (i = 0; i < 6; i++)
+ outb(nic->node_addr[i], nic->ioaddr + i);
+ for (; i < 12; i += 2)
+ outw(0, nic->ioaddr + i);
+
+ if (if_port == 3)
+ /* Start the thinnet transceiver. We should really wait 50ms... */
+ outw(StartCoax, nic->ioaddr + EL3_CMD);
+ EL3WINDOW(4);
+ outw((inw(nic->ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) |
+ media_tbl[if_port].media_bits, nic->ioaddr + Wn4_Media);
+
+ /* Switch to the stats window, and clear all stats by reading. */
+/* outw(StatsDisable, nic->ioaddr + EL3_CMD);*/
+ EL3WINDOW(6);
+ for (i = 0; i < 10; i++)
+ inb(nic->ioaddr + i);
+ inw(nic->ioaddr + 10);
+ inw(nic->ioaddr + 12);
+ /* New: On the Vortex we must also clear the BadSSD counter. */
+ EL3WINDOW(4);
+ inb(nic->ioaddr + 12);
+ /* ..and on the Boomerang we enable the extra statistics bits. */
+ outw(0x0040, nic->ioaddr + Wn4_NetDiag);
+
+ /* Switch to register set 7 for normal use. */
+ EL3WINDOW(7);
+
+ /* Temporarily left in place. If these FIXMEs are printed
+ it meand that special logic for that card may need to be added
+ see Becker's 3c515.c driver */
+ if (vp->full_bus_master_rx) { /* Boomerang bus master. */
+ printf("FIXME: Is this if necessary");
+ vp->cur_rx = vp->dirty_rx = 0;
+ DBG ( " Filling in the Rx ring.\n" );
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ printf("FIXME: Is this if necessary");
+ }
+ }
+ if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
+ vp->cur_tx = vp->dirty_tx = 0;
+ outb(PKT_BUF_SZ >> 8, nic->ioaddr + TxFreeThreshold); /* Room for a packet. */
+ /* Clear the Tx ring. */
+ for (i = 0; i < TX_RING_SIZE; i++)
+ vp->tx_skbuff[i] = 0;
+ outl(0, nic->ioaddr + DownListPtr);
+ }
+ /* Set receiver mode: presumably accept b-case and phys addr only. */
+ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+ nic->ioaddr + EL3_CMD);
+
+ outw(RxEnable, nic->ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, nic->ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull |
+ (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
+ (vp->full_bus_master_rx ? UpComplete : RxComplete) |
+ (vp->bus_master ? DMADone : 0), nic->ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ nic->ioaddr + EL3_CMD);
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+ | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
+ nic->ioaddr + EL3_CMD);
+
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int t515_poll(struct nic *nic, int retrieve)
+{
+ short status, cst;
+ register short rx_fifo;
+
+ cst = inw(nic->ioaddr + EL3_STATUS);
+
+ if ((cst & RxComplete) == 0) {
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ nic->ioaddr + EL3_CMD);
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete |
+ StatsFull | (vp->
+ bus_master ? DMADone : 0) | UpComplete |
+ DownComplete, nic->ioaddr + EL3_CMD);
+ return 0;
+ }
+ status = inw(nic->ioaddr + RxStatus);
+
+ if (status & RxDError) {
+ printf("RxDError\n");
+ outw(RxDiscard, nic->ioaddr + EL3_CMD);
+ return 0;
+ }
+
+ rx_fifo = status & RX_BYTES_MASK;
+ if (rx_fifo == 0)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ DBG ( "[l=%d", rx_fifo );
+ insw(nic->ioaddr + RX_FIFO, nic->packet, rx_fifo / 2);
+ if (rx_fifo & 1)
+ nic->packet[rx_fifo - 1] = inb(nic->ioaddr + RX_FIFO);
+ nic->packetlen = rx_fifo;
+
+ while (1) {
+ status = inw(nic->ioaddr + RxStatus);
+ DBG ( "0x%hX*", status );
+ rx_fifo = status & RX_BYTES_MASK;
+
+ if (rx_fifo > 0) {
+ insw(nic->ioaddr + RX_FIFO, nic->packet + nic->packetlen,
+ rx_fifo / 2);
+ if (rx_fifo & 1)
+ nic->packet[nic->packetlen + rx_fifo - 1] =
+ inb(nic->ioaddr + RX_FIFO);
+ nic->packetlen += rx_fifo;
+ DBG ( "+%d", rx_fifo );
+ }
+ if ((status & RxComplete) == 0) {
+ DBG ( "=%d", nic->packetlen );
+ break;
+ }
+ udelay(1000);
+ }
+
+ /* acknowledge reception of packet */
+ outw(RxDiscard, nic->ioaddr + EL3_CMD);
+ while (inw(nic->ioaddr + EL3_STATUS) & CmdInProgress);
+#ifdef debug_3c515
+ {
+ unsigned short type = 0;
+ type = (nic->packet[12] << 8) | nic->packet[13];
+ if (nic->packet[0] + nic->packet[1] + nic->packet[2] +
+ nic->packet[3] + nic->packet[4] + nic->packet[5] ==
+ 0xFF * ETH_ALEN)
+ DBG ( ",t=0x%hX,b]", type );
+ else
+ DBG ( ",t=0x%hX]", type );
+ }
+#endif
+
+ return 1;
+}
+
+/*************************************************************************
+ 3Com 515 - specific routines
+**************************************************************************/
+static char padmap[] = {
+ 0, 3, 2, 1
+};
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void t515_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ register int len;
+ int pad;
+ int status;
+
+ DBG ( "{l=%d,t=0x%hX}", s + ETH_HLEN, t );
+
+ /* swap bytes of type */
+ t = htons(t);
+
+ len = s + ETH_HLEN; /* actual length of packet */
+ pad = padmap[len & 3];
+
+ /*
+ * The 3c515 automatically pads short packets to minimum ethernet length,
+ * but we drop packets that are too large. Perhaps we should truncate
+ * them instead?
+ Copied from 3c595. Is this true for the 3c515?
+ */
+ if (len + pad > ETH_FRAME_LEN) {
+ return;
+ }
+ /* drop acknowledgements */
+ while ((status = inb(nic->ioaddr + TxStatus)) & TxComplete) {
+ /*if(status & (TXS_UNDERRUN|0x88|TXS_STATUS_OVERFLOW)) { */
+ outw(TxReset, nic->ioaddr + EL3_CMD);
+ outw(TxEnable, nic->ioaddr + EL3_CMD);
+/* } */
+
+ outb(0x0, nic->ioaddr + TxStatus);
+ }
+
+ while (inw(nic->ioaddr + TxFree) < len + pad + 4) {
+ /* no room in FIFO */
+ }
+
+ outw(len, nic->ioaddr + TX_FIFO);
+ outw(0x0, nic->ioaddr + TX_FIFO); /* Second dword meaningless */
+
+ /* write packet */
+ outsw(nic->ioaddr + TX_FIFO, d, ETH_ALEN / 2);
+ outsw(nic->ioaddr + TX_FIFO, nic->node_addr, ETH_ALEN / 2);
+ outw(t, nic->ioaddr + TX_FIFO);
+ outsw(nic->ioaddr + TX_FIFO, p, s / 2);
+
+ if (s & 1)
+ outb(*(p + s - 1), nic->ioaddr + TX_FIFO);
+
+ while (pad--)
+ outb(0, nic->ioaddr + TX_FIFO); /* Padding */
+
+ /* wait for Tx complete */
+ while ((inw(nic->ioaddr + EL3_STATUS) & CmdInProgress) != 0);
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void t515_disable ( struct nic *nic,
+ struct isapnp_device *isapnp ) {
+
+ t515_reset(nic);
+
+ /* This is a hack. Since ltsp worked on my
+ system without any disable functionality I
+ have no way to determine if this works */
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, nic->ioaddr + EL3_CMD);
+ outw(TxDisable, nic->ioaddr + EL3_CMD);
+
+ if (if_port == XCVR_10base2)
+ /* Turn off thinnet power. Green! */
+ outw(StopCoax, nic->ioaddr + EL3_CMD);
+
+
+ outw(SetIntrEnb | 0x0000, nic->ioaddr + EL3_CMD);
+
+ deactivate_isapnp_device ( isapnp );
+ return;
+}
+
+static void t515_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct nic_operations t515_operations = {
+ .connect = dummy_connect,
+ .poll = t515_poll,
+ .transmit = t515_transmit,
+ .irq = t515_irq,
+
+};
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+You should omit the last argument struct pci_device * for a non-PCI NIC
+***************************************************************************/
+static int t515_probe ( struct nic *nic, struct isapnp_device *isapnp ) {
+
+ /* Direct copy from Beckers 3c515.c removing any ISAPNP sections */
+
+ nic->ioaddr = isapnp->ioaddr;
+ nic->irqno = isapnp->irqno;
+ activate_isapnp_device ( isapnp );
+
+ /* Check the resource configuration for a matching ioaddr. */
+ if ((unsigned)(inw(nic->ioaddr + 0x2002) & 0x1f0)
+ != (nic->ioaddr & 0x1f0)) {
+ DBG ( "3c515 ioaddr mismatch\n" );
+ return 0;
+ }
+
+ /* Verify by reading the device ID from the EEPROM. */
+ {
+ int timer;
+ outw(EEPROM_Read + 7, nic->ioaddr + Wn0EepromCmd);
+ /* Pause for at least 162 us. for the read to take place. */
+ for (timer = 4; timer >= 0; timer--) {
+ t3c515_wait(1);
+ if ((inw(nic->ioaddr + Wn0EepromCmd) & 0x0200) == 0)
+ break;
+ }
+ if (inw(nic->ioaddr + Wn0EepromData) != 0x6d50) {
+ DBG ( "3c515 read incorrect vendor ID from EEPROM" );
+ return 0;
+ }
+
+ }
+ DBG ( "3c515 Resource configuration register 0x%lX, DCR 0x%hX.\n",
+ inl(nic->ioaddr + 0x2002), inw(nic->ioaddr + 0x2000) );
+ corkscrew_found_device(nic->ioaddr, nic->irqno, CORKSCREW_ID,
+ options, nic);
+
+ t515_reset(nic);
+ nic->nic_op = &t515_operations;
+ return 1;
+}
+
+static int
+corkscrew_found_device(int ioaddr, int irq,
+ int product_index, int options, struct nic *nic)
+{
+ /* Direct copy from Becker 3c515.c with unecessary parts removed */
+ vp->product_name = "3c515";
+ vp->options = options;
+ if (options >= 0) {
+ vp->media_override =
+ ((options & 7) == 2) ? 0 : options & 7;
+ vp->full_duplex = (options & 8) ? 1 : 0;
+ vp->bus_master = (options & 16) ? 1 : 0;
+ } else {
+ vp->media_override = 7;
+ vp->full_duplex = 0;
+ vp->bus_master = 0;
+ }
+
+ corkscrew_probe1(ioaddr, irq, product_index, nic);
+ return 0;
+}
+
+static int
+corkscrew_probe1(int ioaddr, int irq, int product_index __unused,
+ struct nic *nic)
+{
+ unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
+ int i;
+
+ printf("3Com %s at 0x%hX, ", vp->product_name, ioaddr);
+
+ /* Read the station address from the EEPROM. */
+ EL3WINDOW(0);
+ for (i = 0; i < 0x18; i++) {
+ short *phys_addr = (short *) nic->node_addr;
+ int timer;
+ outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
+ /* Pause for at least 162 us. for the read to take place. */
+ for (timer = 4; timer >= 0; timer--) {
+ t3c515_wait(1);
+ if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
+ break;
+ }
+ eeprom[i] = inw(ioaddr + Wn0EepromData);
+ DBG ( "Value %d: %hX ", i, eeprom[i] );
+ checksum ^= eeprom[i];
+ if (i < 3)
+ phys_addr[i] = htons(eeprom[i]);
+ }
+ checksum = (checksum ^ (checksum >> 8)) & 0xff;
+ if (checksum != 0x00)
+ printf(" ***INVALID CHECKSUM 0x%hX*** ", checksum);
+
+ DBG ( "%s", eth_ntoa ( nic->node_addr ) );
+
+ if (eeprom[16] == 0x11c7) { /* Corkscrew */
+
+ }
+ printf(", IRQ %d\n", irq);
+ /* Tell them about an invalid IRQ. */
+ if ( (irq <= 0 || irq > 15) ) {
+ DBG (" *** Warning: this IRQ is unlikely to work! ***\n" );
+ }
+
+ {
+ char *ram_split[] = { "5:3", "3:1", "1:1", "3:5" };
+ union wn3_config config;
+ EL3WINDOW(3);
+ vp->available_media = inw(ioaddr + Wn3_Options);
+ config.i = inl(ioaddr + Wn3_Config);
+ DBG ( " Internal config register is %4.4x, "
+ "transceivers 0x%hX.\n",
+ config.i, inw(ioaddr + Wn3_Options) );
+ printf
+ (" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
+ 8 << config.u.ram_size,
+ config.u.ram_width ? "word" : "byte",
+ ram_split[config.u.ram_split],
+ config.u.autoselect ? "autoselect/" : "",
+ media_tbl[config.u.xcvr].name);
+ if_port = config.u.xcvr;
+ vp->default_media = config.u.xcvr;
+ vp->autoselect = config.u.autoselect;
+ }
+ if (vp->media_override != 7) {
+ printf(" Media override to transceiver type %d (%s).\n",
+ vp->media_override,
+ media_tbl[vp->media_override].name);
+ if_port = vp->media_override;
+ }
+
+ vp->capabilities = eeprom[16];
+ vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0;
+ /* Rx is broken at 10mbps, so we always disable it. */
+ /* vp->full_bus_master_rx = 0; */
+ vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0;
+
+ return 0;
+}
+
+static struct isapnp_device_id t515_adapters[] = {
+ { "3c515 (ISAPnP)", ISAPNP_VENDOR('T','C','M'), 0x5051 },
+};
+
+ISAPNP_DRIVER ( t515_driver, t515_adapters );
+
+DRIVER ( "3c515", nic_driver, isapnp_driver, t515_driver,
+ t515_probe, t515_disable );
+
+ISA_ROM ( "3c515", "3c515 Fast EtherLink ISAPnP" );
diff --git a/gpxe/src/drivers/net/3c515.txt b/gpxe/src/drivers/net/3c515.txt
new file mode 100644
index 00000000..8f7b3a72
--- /dev/null
+++ b/gpxe/src/drivers/net/3c515.txt
@@ -0,0 +1,31 @@
+3c515.c -- 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX driver for etherboot
+Copyright (C) 2002 Timothy Legge <tlegge@rogers.com>
+
+This driver is for the 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX
+
+REVISION HISTORY:
+================
+v0.10 4-17-2002 TJL Initial implementation.
+v0.11 4-17-2002 TJL Cleanup of the code
+v0.12 4-26-2002 TJL Added ISA Plug and Play for Non-PNP Bioses
+v0.13 3-31-2003 TJL Fixed issue 1 and 2 below
+
+The driver is heavily based on the work of others are referenced in the 3c515.c file.
+
+ISA Plug and Play (ISAPNP) support has been added for Non-PNP Bioses. The ISAPNP code requires the defination of ISA_PNP as:
+
+#define ISA_PNP
+
+Issues:
+=======
+1) RESOLVED - When ISAPNP is defined, the etherboot probe is unable to find the card during the first probe. This is true even though the ISA PNP code actually found and activated the driver.
+
+2) RESOLVED - When ISA_PNP is defined, the etherboot probe finds the incorrect MAC address for the card. However, when the linux kernel boots and loads the linux 3c515 driver the correct MAC address is found. This means that with ISA_PNP defined, you require both MAC addresses defined in the /etc/dhcpd.conf file. The first MAC address allows the driver to load the LTSP Linux kernel. The second allows the Linux dhclient to resolve its IP address.
+
+3) Although the ISA PNP docs specify that the IRQ, DMA and IO Address needs to be assigned to the card before it is activated, Etherboot does not seem to care. Therefore the code does not assign the card with these values.
+
+If you can help address any of thse issues, please feel free.
+
+Timothy Legge
+timlegge@users.sourceforge.net
+April 9, 2003
diff --git a/gpxe/src/drivers/net/3c529.c b/gpxe/src/drivers/net/3c529.c
new file mode 100644
index 00000000..31931048
--- /dev/null
+++ b/gpxe/src/drivers/net/3c529.c
@@ -0,0 +1,60 @@
+/*
+ * Split out from 3c509.c to make build process more sane
+ *
+ */
+
+#include "etherboot.h"
+#include <gpxe/mca.h>
+#include <gpxe/isa.h> /* for ISA_ROM */
+#include "nic.h"
+#include "3c509.h"
+
+/*
+ * Several other pieces of the MCA support code were shamelessly
+ * borrowed from the Linux kernel source.
+ *
+ * MCA support added by Adam Fritzler (mid@auk.cx)
+ *
+ * Generalised out of the 3c529 driver and into a bus type by Michael
+ * Brown <mbrown@fensystems.co.uk>
+ *
+ */
+
+static int t529_probe ( struct nic *nic, struct mca_device *mca ) {
+
+ /* Retrieve NIC parameters from MCA device parameters */
+ nic->ioaddr = ( ( mca->pos[4] & 0xfc ) | 0x02 ) << 8;
+ nic->irqno = mca->pos[5] & 0x0f;
+ printf ( "3c529 board found on MCA at %#hx IRQ %d -",
+ nic->ioaddr, nic->irqno );
+
+ /* Hand off to generic t5x9 probe routine */
+ return t5x9_probe ( nic, MCA_ID ( mca ), 0xffff );
+}
+
+static void t529_disable ( struct nic *nic, struct mca_device *mca __unused ) {
+ t5x9_disable ( nic );
+}
+
+static struct mca_device_id el3_mca_adapters[] = {
+ { "3Com 3c529 EtherLink III (10base2)", 0x627c },
+ { "3Com 3c529 EtherLink III (10baseT)", 0x627d },
+ { "3Com 3c529 EtherLink III (test mode)", 0x62db },
+ { "3Com 3c529 EtherLink III (TP or coax)", 0x62f6 },
+ { "3Com 3c529 EtherLink III (TP)", 0x62f7 },
+};
+
+MCA_DRIVER ( t529_driver, el3_mca_adapters );
+
+DRIVER ( "3c529", nic_driver, mca_driver, t529_driver,
+ t529_probe, t529_disable );
+
+ISA_ROM( "3c529", "3c529 == MCA 3c509" );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/3c595.c b/gpxe/src/drivers/net/3c595.c
new file mode 100644
index 00000000..7138f936
--- /dev/null
+++ b/gpxe/src/drivers/net/3c595.c
@@ -0,0 +1,551 @@
+/*
+* 3c595.c -- 3COM 3C595 Fast Etherlink III PCI driver for etherboot
+*
+* Copyright (C) 2000 Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp>
+* All rights reserved.
+* Mar. 14, 2000
+*
+* This software may be used, modified, copied, distributed, and sold, in
+* both source and binary form provided that the above copyright and these
+* terms are retained. Under no circumstances are the authors responsible for
+* the proper functioning of this software, nor do the authors assume any
+* responsibility for damages incurred with its use.
+*
+* This code is based on Martin Renters' etherboot-4.4.3 3c509.c and
+* Herb Peyerl's FreeBSD 3.4-RELEASE if_vx.c driver.
+*
+* Copyright (C) 1993-1994, David Greenman, Martin Renters.
+* Copyright (C) 1993-1995, Andres Vega Garcia.
+* Copyright (C) 1995, Serge Babkin.
+*
+* Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
+*
+* timlegge 08-24-2003 Add Multicast Support
+*/
+
+/* #define EDEBUG */
+
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+#include "3c595.h"
+
+static struct nic_operations t595_operations;
+
+static unsigned short eth_nic_base;
+static unsigned short vx_connector, vx_connectors;
+
+static struct connector_entry {
+ int bit;
+ char *name;
+} conn_tab[VX_CONNECTORS] = {
+#define CONNECTOR_UTP 0
+ { 0x08, "utp"},
+#define CONNECTOR_AUI 1
+ { 0x20, "aui"},
+/* dummy */
+ { 0, "???"},
+#define CONNECTOR_BNC 3
+ { 0x10, "bnc"},
+#define CONNECTOR_TX 4
+ { 0x02, "tx"},
+#define CONNECTOR_FX 5
+ { 0x04, "fx"},
+#define CONNECTOR_MII 6
+ { 0x40, "mii"},
+ { 0, "???"}
+};
+
+static void vxgetlink(void);
+static void vxsetlink(void);
+
+/**************************************************************************
+ETH_RESET - Reset adapter
+***************************************************************************/
+static void t595_reset(struct nic *nic)
+{
+ int i;
+
+ /***********************************************************
+ Reset 3Com 595 card
+ *************************************************************/
+
+ /* stop card */
+ outw(RX_DISABLE, BASE + VX_COMMAND);
+ outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+ outw(TX_DISABLE, BASE + VX_COMMAND);
+ outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
+ udelay(8000);
+ outw(RX_RESET, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+ outw(TX_RESET, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+ outw(C_INTR_LATCH, BASE + VX_COMMAND);
+ outw(SET_RD_0_MASK, BASE + VX_COMMAND);
+ outw(SET_INTR_MASK, BASE + VX_COMMAND);
+ outw(SET_RX_FILTER, BASE + VX_COMMAND);
+
+ /*
+ * initialize card
+ */
+ VX_BUSY_WAIT;
+
+ GO_WINDOW(0);
+
+ /* Disable the card */
+/* outw(0, BASE + VX_W0_CONFIG_CTRL); */
+
+ /* Configure IRQ to none */
+/* outw(SET_IRQ(0), BASE + VX_W0_RESOURCE_CFG); */
+
+ /* Enable the card */
+/* outw(ENABLE_DRQ_IRQ, BASE + VX_W0_CONFIG_CTRL); */
+
+ GO_WINDOW(2);
+
+ /* Reload the ether_addr. */
+ for (i = 0; i < ETH_ALEN; i++)
+ outb(nic->node_addr[i], BASE + VX_W2_ADDR_0 + i);
+
+ outw(RX_RESET, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+ outw(TX_RESET, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+
+ /* Window 1 is operating window */
+ GO_WINDOW(1);
+ for (i = 0; i < 31; i++)
+ inb(BASE + VX_W1_TX_STATUS);
+
+ outw(SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
+ S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
+ outw(SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
+ S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
+
+/*
+ * Attempt to get rid of any stray interrupts that occured during
+ * configuration. On the i386 this isn't possible because one may
+ * already be queued. However, a single stray interrupt is
+ * unimportant.
+ */
+
+ outw(ACK_INTR | 0xff, BASE + VX_COMMAND);
+
+ outw(SET_RX_FILTER | FIL_INDIVIDUAL |
+ FIL_BRDCST|FIL_MULTICAST, BASE + VX_COMMAND);
+
+ vxsetlink();
+/*{
+ int i,j;
+ i = CONNECTOR_TX;
+ GO_WINDOW(3);
+ j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
+ outl(BASE + VX_W3_INTERNAL_CFG, j | (i <<INTERNAL_CONNECTOR_BITS));
+ GO_WINDOW(4);
+ outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
+ GO_WINDOW(1);
+}*/
+
+ /* start tranciever and receiver */
+ outw(RX_ENABLE, BASE + VX_COMMAND);
+ outw(TX_ENABLE, BASE + VX_COMMAND);
+
+}
+
+/**************************************************************************
+ETH_TRANSMIT - Transmit a frame
+***************************************************************************/
+static char padmap[] = {
+ 0, 3, 2, 1};
+
+static void t595_transmit(
+struct nic *nic,
+const char *d, /* Destination */
+unsigned int t, /* Type */
+unsigned int s, /* size */
+const char *p) /* Packet */
+{
+ register int len;
+ int pad;
+ int status;
+
+#ifdef EDEBUG
+ printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
+#endif
+
+ /* swap bytes of type */
+ t= htons(t);
+
+ len=s+ETH_HLEN; /* actual length of packet */
+ pad = padmap[len & 3];
+
+ /*
+ * The 3c595 automatically pads short packets to minimum ethernet length,
+ * but we drop packets that are too large. Perhaps we should truncate
+ * them instead?
+ */
+ if (len + pad > ETH_FRAME_LEN) {
+ return;
+ }
+
+ /* drop acknowledgements */
+ while(( status=inb(BASE + VX_W1_TX_STATUS) )& TXS_COMPLETE ) {
+ if(status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
+ outw(TX_RESET, BASE + VX_COMMAND);
+ outw(TX_ENABLE, BASE + VX_COMMAND);
+ }
+
+ outb(0x0, BASE + VX_W1_TX_STATUS);
+ }
+
+ while (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) {
+ /* no room in FIFO */
+ }
+
+ outw(len, BASE + VX_W1_TX_PIO_WR_1);
+ outw(0x0, BASE + VX_W1_TX_PIO_WR_1); /* Second dword meaningless */
+
+ /* write packet */
+ outsw(BASE + VX_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
+ outsw(BASE + VX_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
+ outw(t, BASE + VX_W1_TX_PIO_WR_1);
+ outsw(BASE + VX_W1_TX_PIO_WR_1, p, s / 2);
+ if (s & 1)
+ outb(*(p+s - 1), BASE + VX_W1_TX_PIO_WR_1);
+
+ while (pad--)
+ outb(0, BASE + VX_W1_TX_PIO_WR_1); /* Padding */
+
+ /* wait for Tx complete */
+ while((inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
+ ;
+}
+
+/**************************************************************************
+ETH_POLL - Wait for a frame
+***************************************************************************/
+static int t595_poll(struct nic *nic, int retrieve)
+{
+ /* common variables */
+ /* variables for 3C595 */
+ short status, cst;
+ register short rx_fifo;
+
+ cst=inw(BASE + VX_STATUS);
+
+#ifdef EDEBUG
+ if(cst & 0x1FFF)
+ printf("-%hX-",cst);
+#endif
+
+ if( (cst & S_RX_COMPLETE)==0 ) {
+ /* acknowledge everything */
+ outw(ACK_INTR | cst, BASE + VX_COMMAND);
+ outw(C_INTR_LATCH, BASE + VX_COMMAND);
+
+ return 0;
+ }
+
+ status = inw(BASE + VX_W1_RX_STATUS);
+#ifdef EDEBUG
+ printf("*%hX*",status);
+#endif
+
+ if (status & ERR_RX) {
+ outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
+ return 0;
+ }
+
+ rx_fifo = status & RX_BYTES_MASK;
+ if (rx_fifo==0)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ /* read packet */
+#ifdef EDEBUG
+ printf("[l=%d",rx_fifo);
+#endif
+ insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
+ if(rx_fifo & 1)
+ nic->packet[rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
+ nic->packetlen=rx_fifo;
+
+ while(1) {
+ status = inw(BASE + VX_W1_RX_STATUS);
+#ifdef EDEBUG
+ printf("*%hX*",status);
+#endif
+ rx_fifo = status & RX_BYTES_MASK;
+
+ if(rx_fifo>0) {
+ insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
+ if(rx_fifo & 1)
+ nic->packet[nic->packetlen+rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
+ nic->packetlen+=rx_fifo;
+#ifdef EDEBUG
+ printf("+%d",rx_fifo);
+#endif
+ }
+ if(( status & RX_INCOMPLETE )==0) {
+#ifdef EDEBUG
+ printf("=%d",nic->packetlen);
+#endif
+ break;
+ }
+ udelay(1000);
+ }
+
+ /* acknowledge reception of packet */
+ outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
+ while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS);
+#ifdef EDEBUG
+{
+ unsigned short type = 0; /* used by EDEBUG */
+ type = (nic->packet[12]<<8) | nic->packet[13];
+ if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
+ nic->packet[5] == 0xFF*ETH_ALEN)
+ printf(",t=%hX,b]",type);
+ else
+ printf(",t=%hX]",type);
+}
+#endif
+ return 1;
+}
+
+
+/*************************************************************************
+ 3Com 595 - specific routines
+**************************************************************************/
+
+static int
+eeprom_rdy()
+{
+ int i;
+
+ for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++)
+ udelay(1000);
+ if (i >= MAX_EEPROMBUSY) {
+ /* printf("3c595: eeprom failed to come ready.\n"); */
+ printf("3c595: eeprom is busy.\n"); /* memory in EPROM is tight */
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * get_e: gets a 16 bits word from the EEPROM. we must have set the window
+ * before
+ */
+static int
+get_e(offset)
+int offset;
+{
+ if (!eeprom_rdy())
+ return (0xffff);
+ outw(EEPROM_CMD_RD | offset, BASE + VX_W0_EEPROM_COMMAND);
+ if (!eeprom_rdy())
+ return (0xffff);
+ return (inw(BASE + VX_W0_EEPROM_DATA));
+}
+
+static void
+vxgetlink(void)
+{
+ int n, k;
+
+ GO_WINDOW(3);
+ vx_connectors = inw(BASE + VX_W3_RESET_OPT) & 0x7f;
+ for (n = 0, k = 0; k < VX_CONNECTORS; k++) {
+ if (vx_connectors & conn_tab[k].bit) {
+ if (n > 0) {
+ printf("/");
+ }
+ printf(conn_tab[k].name);
+ n++;
+ }
+ }
+ if (vx_connectors == 0) {
+ printf("no connectors!");
+ return;
+ }
+ GO_WINDOW(3);
+ vx_connector = (inl(BASE + VX_W3_INTERNAL_CFG)
+ & INTERNAL_CONNECTOR_MASK)
+ >> INTERNAL_CONNECTOR_BITS;
+ if (vx_connector & 0x10) {
+ vx_connector &= 0x0f;
+ printf("[*%s*]", conn_tab[vx_connector].name);
+ printf(": disable 'auto select' with DOS util!");
+ } else {
+ printf("[*%s*]", conn_tab[vx_connector].name);
+ }
+}
+
+static void
+vxsetlink(void)
+{
+ int i, j;
+ char *reason, *warning;
+ static char prev_conn = -1;
+
+ if (prev_conn == -1) {
+ prev_conn = vx_connector;
+ }
+
+ i = vx_connector; /* default in EEPROM */
+ reason = "default";
+ warning = 0;
+
+ if ((vx_connectors & conn_tab[vx_connector].bit) == 0) {
+ warning = "strange connector type in EEPROM.";
+ reason = "forced";
+ i = CONNECTOR_UTP;
+ }
+
+ if (warning != 0) {
+ printf("warning: %s\n", warning);
+ }
+ printf("selected %s. (%s)\n", conn_tab[i].name, reason);
+
+ /* Set the selected connector. */
+ GO_WINDOW(3);
+ j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
+ outl(j | (i <<INTERNAL_CONNECTOR_BITS), BASE + VX_W3_INTERNAL_CFG);
+
+ /* First, disable all. */
+ outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
+ udelay(8000);
+ GO_WINDOW(4);
+ outw(0, BASE + VX_W4_MEDIA_TYPE);
+
+ /* Second, enable the selected one. */
+ switch(i) {
+ case CONNECTOR_UTP:
+ GO_WINDOW(4);
+ outw(ENABLE_UTP, BASE + VX_W4_MEDIA_TYPE);
+ break;
+ case CONNECTOR_BNC:
+ outw(START_TRANSCEIVER,BASE + VX_COMMAND);
+ udelay(8000);
+ break;
+ case CONNECTOR_TX:
+ case CONNECTOR_FX:
+ GO_WINDOW(4);
+ outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
+ break;
+ default: /* AUI and MII fall here */
+ break;
+ }
+ GO_WINDOW(1);
+}
+
+static void t595_disable ( struct nic *nic ) {
+
+ t595_reset(nic);
+
+ outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
+ udelay(8000);
+ GO_WINDOW(4);
+ outw(0, BASE + VX_W4_MEDIA_TYPE);
+ GO_WINDOW(1);
+}
+
+static void t595_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+***************************************************************************/
+static int t595_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ int i;
+ unsigned short *p;
+
+ if (pci->ioaddr == 0)
+ return 0;
+ eth_nic_base = pci->ioaddr;
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr;
+
+ GO_WINDOW(0);
+ outw(GLOBAL_RESET, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+
+ vxgetlink();
+
+/*
+ printf("\nEEPROM:");
+ for (i = 0; i < (EEPROMSIZE/2); i++) {
+ printf("%hX:", get_e(i));
+ }
+ printf("\n");
+*/
+ /*
+ * Read the station address from the eeprom
+ */
+ p = (unsigned short *) nic->node_addr;
+ for (i = 0; i < 3; i++) {
+ GO_WINDOW(0);
+ p[i] = htons(get_e(EEPROM_OEM_ADDR_0 + i));
+ GO_WINDOW(2);
+ outw(ntohs(p[i]), BASE + VX_W2_ADDR_0 + (i * 2));
+ }
+
+ DBG ( "Ethernet address: %s\n", eth_ntoa (nic->node_addr) );
+
+ t595_reset(nic);
+ nic->nic_op = &t595_operations;
+ return 1;
+
+}
+
+static struct nic_operations t595_operations = {
+ .connect = dummy_connect,
+ .poll = t595_poll,
+ .transmit = t595_transmit,
+ .irq = t595_irq,
+
+};
+
+static struct pci_device_id t595_nics[] = {
+PCI_ROM(0x10b7, 0x5900, "3c590", "3Com590"), /* Vortex 10Mbps */
+PCI_ROM(0x10b7, 0x5950, "3c595", "3Com595"), /* Vortex 100baseTx */
+PCI_ROM(0x10b7, 0x5951, "3c595-1", "3Com595"), /* Vortex 100baseT4 */
+PCI_ROM(0x10b7, 0x5952, "3c595-2", "3Com595"), /* Vortex 100base-MII */
+PCI_ROM(0x10b7, 0x9000, "3c900-tpo", "3Com900-TPO"), /* 10 Base TPO */
+PCI_ROM(0x10b7, 0x9001, "3c900-t4", "3Com900-Combo"), /* 10/100 T4 */
+PCI_ROM(0x10b7, 0x9004, "3c900b-tpo", "3Com900B-TPO"), /* 10 Base TPO */
+PCI_ROM(0x10b7, 0x9005, "3c900b-combo", "3Com900B-Combo"), /* 10 Base Combo */
+PCI_ROM(0x10b7, 0x9006, "3c900b-tpb2", "3Com900B-2/T"), /* 10 Base TP and Base2 */
+PCI_ROM(0x10b7, 0x900a, "3c900b-fl", "3Com900B-FL"), /* 10 Base F */
+PCI_ROM(0x10b7, 0x9800, "3c980-cyclone-1", "3Com980-Cyclone"), /* Cyclone */
+PCI_ROM(0x10b7, 0x9805, "3c9805-1", "3Com9805"), /* Dual Port Server Cyclone */
+PCI_ROM(0x10b7, 0x7646, "3csoho100-tx-1", "3CSOHO100-TX"), /* Hurricane */
+PCI_ROM(0x10b7, 0x4500, "3c450-1", "3Com450 HomePNA Tornado"),
+};
+
+PCI_DRIVER ( t595_driver, t595_nics, PCI_NO_CLASS );
+
+DRIVER ( "3C595", nic_driver, pci_driver, t595_driver,
+ t595_probe, t595_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/3c595.h b/gpxe/src/drivers/net/3c595.h
new file mode 100644
index 00000000..49d8d9b0
--- /dev/null
+++ b/gpxe/src/drivers/net/3c595.h
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2. The name
+ * of the author may not be used to endorse or promote products derived from
+ * this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ October 2, 1994
+
+ Modified by: Andres Vega Garcia
+
+ INRIA - Sophia Antipolis, France
+ e-mail: avega@sophia.inria.fr
+ finger: avega@pax.inria.fr
+
+ */
+
+/*
+ * Created from if_epreg.h by Fred Gray (fgray@rice.edu) to support the
+ * 3c590 family.
+ */
+
+/*
+ * Modified by Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp>
+ * for etherboot
+ * Mar. 14, 2000
+*/
+
+/*
+ * Ethernet software status per interface.
+ */
+
+/*
+ * Some global constants
+ */
+
+#define TX_INIT_RATE 16
+#define TX_INIT_MAX_RATE 64
+#define RX_INIT_LATENCY 64
+#define RX_INIT_EARLY_THRESH 64
+#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */
+#define MIN_RX_EARLY_THRESHL 4
+
+#define EEPROMSIZE 0x40
+#define MAX_EEPROMBUSY 1000
+#define VX_LAST_TAG 0xd7
+#define VX_MAX_BOARDS 16
+#define VX_ID_PORT 0x100
+
+/*
+ * some macros to acces long named fields
+ */
+#define BASE (eth_nic_base)
+
+/*
+ * Commands to read/write EEPROM trough EEPROM command register (Window 0,
+ * Offset 0xa)
+ */
+#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */
+#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */
+#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */
+#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */
+
+#define EEPROM_BUSY (1<<15)
+
+/*
+ * Some short functions, worth to let them be a macro
+ */
+
+/**************************************************************************
+ * *
+ * These define the EEPROM data structure. They are used in the probe
+ * function to verify the existence of the adapter after having sent
+ * the ID_Sequence.
+ *
+ * There are others but only the ones we use are defined here.
+ *
+ **************************************************************************/
+
+#define EEPROM_NODE_ADDR_0 0x0 /* Word */
+#define EEPROM_NODE_ADDR_1 0x1 /* Word */
+#define EEPROM_NODE_ADDR_2 0x2 /* Word */
+#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
+#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
+#define EEPROM_ADDR_CFG 0x8 /* Base addr */
+#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
+#define EEPROM_OEM_ADDR_0 0xa /* Word */
+#define EEPROM_OEM_ADDR_1 0xb /* Word */
+#define EEPROM_OEM_ADDR_2 0xc /* Word */
+#define EEPROM_SOFT_INFO_2 0xf /* Software information 2 */
+
+#define NO_RX_OVN_ANOMALY (1<<5)
+
+/**************************************************************************
+ * *
+ * These are the registers for the 3Com 3c509 and their bit patterns when *
+ * applicable. They have been taken out the the "EtherLink III Parallel *
+ * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual *
+ * from 3com. *
+ * *
+ **************************************************************************/
+
+#define VX_COMMAND 0x0e /* Write. BASE+0x0e is always a
+ * command reg. */
+#define VX_STATUS 0x0e /* Read. BASE+0x0e is always status
+ * reg. */
+#define VX_WINDOW 0x0f /* Read. BASE+0x0f is always window
+ * reg. */
+/*
+ * Window 0 registers. Setup.
+ */
+/* Write */
+#define VX_W0_EEPROM_DATA 0x0c
+#define VX_W0_EEPROM_COMMAND 0x0a
+#define VX_W0_RESOURCE_CFG 0x08
+#define VX_W0_ADDRESS_CFG 0x06
+#define VX_W0_CONFIG_CTRL 0x04
+ /* Read */
+#define VX_W0_PRODUCT_ID 0x02
+#define VX_W0_MFG_ID 0x00
+
+
+/*
+ * Window 1 registers. Operating Set.
+ */
+/* Write */
+#define VX_W1_TX_PIO_WR_2 0x02
+#define VX_W1_TX_PIO_WR_1 0x00
+/* Read */
+#define VX_W1_FREE_TX 0x0c
+#define VX_W1_TX_STATUS 0x0b /* byte */
+#define VX_W1_TIMER 0x0a /* byte */
+#define VX_W1_RX_STATUS 0x08
+#define VX_W1_RX_PIO_RD_2 0x02
+#define VX_W1_RX_PIO_RD_1 0x00
+
+/*
+ * Window 2 registers. Station Address Setup/Read
+ */
+/* Read/Write */
+#define VX_W2_ADDR_5 0x05
+#define VX_W2_ADDR_4 0x04
+#define VX_W2_ADDR_3 0x03
+#define VX_W2_ADDR_2 0x02
+#define VX_W2_ADDR_1 0x01
+#define VX_W2_ADDR_0 0x00
+
+/*
+ * Window 3 registers. FIFO Management.
+ */
+/* Read */
+#define VX_W3_INTERNAL_CFG 0x00
+#define VX_W3_RESET_OPT 0x08
+#define VX_W3_FREE_TX 0x0c
+#define VX_W3_FREE_RX 0x0a
+
+/*
+ * Window 4 registers. Diagnostics.
+ */
+/* Read/Write */
+#define VX_W4_MEDIA_TYPE 0x0a
+#define VX_W4_CTRLR_STATUS 0x08
+#define VX_W4_NET_DIAG 0x06
+#define VX_W4_FIFO_DIAG 0x04
+#define VX_W4_HOST_DIAG 0x02
+#define VX_W4_TX_DIAG 0x00
+
+/*
+ * Window 5 Registers. Results and Internal status.
+ */
+/* Read */
+#define VX_W5_READ_0_MASK 0x0c
+#define VX_W5_INTR_MASK 0x0a
+#define VX_W5_RX_FILTER 0x08
+#define VX_W5_RX_EARLY_THRESH 0x06
+#define VX_W5_TX_AVAIL_THRESH 0x02
+#define VX_W5_TX_START_THRESH 0x00
+
+/*
+ * Window 6 registers. Statistics.
+ */
+/* Read/Write */
+#define TX_TOTAL_OK 0x0c
+#define RX_TOTAL_OK 0x0a
+#define TX_DEFERRALS 0x08
+#define RX_FRAMES_OK 0x07
+#define TX_FRAMES_OK 0x06
+#define RX_OVERRUNS 0x05
+#define TX_COLLISIONS 0x04
+#define TX_AFTER_1_COLLISION 0x03
+#define TX_AFTER_X_COLLISIONS 0x02
+#define TX_NO_SQE 0x01
+#define TX_CD_LOST 0x00
+
+/****************************************
+ *
+ * Register definitions.
+ *
+ ****************************************/
+
+/*
+ * Command register. All windows.
+ *
+ * 16 bit register.
+ * 15-11: 5-bit code for command to be executed.
+ * 10-0: 11-bit arg if any. For commands with no args;
+ * this can be set to anything.
+ */
+#define GLOBAL_RESET (unsigned short) 0x0000 /* Wait at least 1ms
+ * after issuing */
+#define WINDOW_SELECT (unsigned short) (0x1<<11)
+#define START_TRANSCEIVER (unsigned short) (0x2<<11) /* Read ADDR_CFG reg to
+ * determine whether
+ * this is needed. If
+ * so; wait 800 uSec
+ * before using trans-
+ * ceiver. */
+#define RX_DISABLE (unsigned short) (0x3<<11) /* state disabled on
+ * power-up */
+#define RX_ENABLE (unsigned short) (0x4<<11)
+#define RX_RESET (unsigned short) (0x5<<11)
+#define RX_DISCARD_TOP_PACK (unsigned short) (0x8<<11)
+#define TX_ENABLE (unsigned short) (0x9<<11)
+#define TX_DISABLE (unsigned short) (0xa<<11)
+#define TX_RESET (unsigned short) (0xb<<11)
+#define REQ_INTR (unsigned short) (0xc<<11)
+/*
+ * The following C_* acknowledge the various interrupts. Some of them don't
+ * do anything. See the manual.
+ */
+#define ACK_INTR (unsigned short) (0x6800)
+# define C_INTR_LATCH (unsigned short) (ACK_INTR|0x1)
+# define C_CARD_FAILURE (unsigned short) (ACK_INTR|0x2)
+# define C_TX_COMPLETE (unsigned short) (ACK_INTR|0x4)
+# define C_TX_AVAIL (unsigned short) (ACK_INTR|0x8)
+# define C_RX_COMPLETE (unsigned short) (ACK_INTR|0x10)
+# define C_RX_EARLY (unsigned short) (ACK_INTR|0x20)
+# define C_INT_RQD (unsigned short) (ACK_INTR|0x40)
+# define C_UPD_STATS (unsigned short) (ACK_INTR|0x80)
+#define SET_INTR_MASK (unsigned short) (0xe<<11)
+#define SET_RD_0_MASK (unsigned short) (0xf<<11)
+#define SET_RX_FILTER (unsigned short) (0x10<<11)
+# define FIL_INDIVIDUAL (unsigned short) (0x1)
+# define FIL_MULTICAST (unsigned short) (0x02)
+# define FIL_BRDCST (unsigned short) (0x04)
+# define FIL_PROMISC (unsigned short) (0x08)
+#define SET_RX_EARLY_THRESH (unsigned short) (0x11<<11)
+#define SET_TX_AVAIL_THRESH (unsigned short) (0x12<<11)
+#define SET_TX_START_THRESH (unsigned short) (0x13<<11)
+#define STATS_ENABLE (unsigned short) (0x15<<11)
+#define STATS_DISABLE (unsigned short) (0x16<<11)
+#define STOP_TRANSCEIVER (unsigned short) (0x17<<11)
+
+/*
+ * Status register. All windows.
+ *
+ * 15-13: Window number(0-7).
+ * 12: Command_in_progress.
+ * 11: reserved.
+ * 10: reserved.
+ * 9: reserved.
+ * 8: reserved.
+ * 7: Update Statistics.
+ * 6: Interrupt Requested.
+ * 5: RX Early.
+ * 4: RX Complete.
+ * 3: TX Available.
+ * 2: TX Complete.
+ * 1: Adapter Failure.
+ * 0: Interrupt Latch.
+ */
+#define S_INTR_LATCH (unsigned short) (0x1)
+#define S_CARD_FAILURE (unsigned short) (0x2)
+#define S_TX_COMPLETE (unsigned short) (0x4)
+#define S_TX_AVAIL (unsigned short) (0x8)
+#define S_RX_COMPLETE (unsigned short) (0x10)
+#define S_RX_EARLY (unsigned short) (0x20)
+#define S_INT_RQD (unsigned short) (0x40)
+#define S_UPD_STATS (unsigned short) (0x80)
+#define S_COMMAND_IN_PROGRESS (unsigned short) (0x1000)
+
+#define VX_BUSY_WAIT while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS)
+
+/* Address Config. Register.
+ * Window 0/Port 06
+ */
+
+#define ACF_CONNECTOR_BITS 14
+#define ACF_CONNECTOR_UTP 0
+#define ACF_CONNECTOR_AUI 1
+#define ACF_CONNECTOR_BNC 3
+
+#define INTERNAL_CONNECTOR_BITS 20
+#define INTERNAL_CONNECTOR_MASK 0x01700000
+
+/*
+ * FIFO Registers. RX Status.
+ *
+ * 15: Incomplete or FIFO empty.
+ * 14: 1: Error in RX Packet 0: Incomplete or no error.
+ * 13-11: Type of error.
+ * 1000 = Overrun.
+ * 1011 = Run Packet Error.
+ * 1100 = Alignment Error.
+ * 1101 = CRC Error.
+ * 1001 = Oversize Packet Error (>1514 bytes)
+ * 0010 = Dribble Bits.
+ * (all other error codes, no errors.)
+ *
+ * 10-0: RX Bytes (0-1514)
+ */
+#define ERR_INCOMPLETE (unsigned short) (0x8000)
+#define ERR_RX (unsigned short) (0x4000)
+#define ERR_MASK (unsigned short) (0x7800)
+#define ERR_OVERRUN (unsigned short) (0x4000)
+#define ERR_RUNT (unsigned short) (0x5800)
+#define ERR_ALIGNMENT (unsigned short) (0x6000)
+#define ERR_CRC (unsigned short) (0x6800)
+#define ERR_OVERSIZE (unsigned short) (0x4800)
+#define ERR_DRIBBLE (unsigned short) (0x1000)
+
+/*
+ * TX Status.
+ *
+ * Reports the transmit status of a completed transmission. Writing this
+ * register pops the transmit completion stack.
+ *
+ * Window 1/Port 0x0b.
+ *
+ * 7: Complete
+ * 6: Interrupt on successful transmission requested.
+ * 5: Jabber Error (TP Only, TX Reset required. )
+ * 4: Underrun (TX Reset required. )
+ * 3: Maximum Collisions.
+ * 2: TX Status Overflow.
+ * 1-0: Undefined.
+ *
+ */
+#define TXS_COMPLETE 0x80
+#define TXS_INTR_REQ 0x40
+#define TXS_JABBER 0x20
+#define TXS_UNDERRUN 0x10
+#define TXS_MAX_COLLISION 0x8
+#define TXS_STATUS_OVERFLOW 0x4
+
+#define RS_AUI (1<<5)
+#define RS_BNC (1<<4)
+#define RS_UTP (1<<3)
+#define RS_T4 (1<<0)
+#define RS_TX (1<<1)
+#define RS_FX (1<<2)
+#define RS_MII (1<<6)
+
+
+/*
+ * FIFO Status (Window 4)
+ *
+ * Supports FIFO diagnostics
+ *
+ * Window 4/Port 0x04.1
+ *
+ * 15: 1=RX receiving (RO). Set when a packet is being received
+ * into the RX FIFO.
+ * 14: Reserved
+ * 13: 1=RX underrun (RO). Generates Adapter Failure interrupt.
+ * Requires RX Reset or Global Reset command to recover.
+ * It is generated when you read past the end of a packet -
+ * reading past what has been received so far will give bad
+ * data.
+ * 12: 1=RX status overrun (RO). Set when there are already 8
+ * packets in the RX FIFO. While this bit is set, no additional
+ * packets are received. Requires no action on the part of
+ * the host. The condition is cleared once a packet has been
+ * read out of the RX FIFO.
+ * 11: 1=RX overrun (RO). Set when the RX FIFO is full (there
+ * may not be an overrun packet yet). While this bit is set,
+ * no additional packets will be received (some additional
+ * bytes can still be pending between the wire and the RX
+ * FIFO). Requires no action on the part of the host. The
+ * condition is cleared once a few bytes have been read out
+ * from the RX FIFO.
+ * 10: 1=TX overrun (RO). Generates adapter failure interrupt.
+ * Requires TX Reset or Global Reset command to recover.
+ * Disables Transmitter.
+ * 9-8: Unassigned.
+ * 7-0: Built in self test bits for the RX and TX FIFO's.
+ */
+#define FIFOS_RX_RECEIVING (unsigned short) 0x8000
+#define FIFOS_RX_UNDERRUN (unsigned short) 0x2000
+#define FIFOS_RX_STATUS_OVERRUN (unsigned short) 0x1000
+#define FIFOS_RX_OVERRUN (unsigned short) 0x0800
+#define FIFOS_TX_OVERRUN (unsigned short) 0x0400
+
+/*
+ * Misc defines for various things.
+ */
+#define TAG_ADAPTER 0xd0
+#define ACTIVATE_ADAPTER_TO_CONFIG 0xff
+#define ENABLE_DRQ_IRQ 0x0001
+#define MFG_ID 0x506d /* `TCM' */
+#define PROD_ID 0x5090
+#define GO_WINDOW(x) outw(WINDOW_SELECT|(x),BASE+VX_COMMAND)
+#define JABBER_GUARD_ENABLE 0x40
+#define LINKBEAT_ENABLE 0x80
+#define ENABLE_UTP (JABBER_GUARD_ENABLE | LINKBEAT_ENABLE)
+#define DISABLE_UTP 0x0
+#define RX_BYTES_MASK (unsigned short) (0x07ff)
+#define RX_ERROR 0x4000
+#define RX_INCOMPLETE 0x8000
+#define TX_INDICATE 1<<15
+#define is_eeprom_busy(b) (inw((b)+VX_W0_EEPROM_COMMAND)&EEPROM_BUSY)
+
+#define VX_IOSIZE 0x20
+
+#define VX_CONNECTORS 8
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/3c5x9.c b/gpxe/src/drivers/net/3c5x9.c
new file mode 100644
index 00000000..565044a1
--- /dev/null
+++ b/gpxe/src/drivers/net/3c5x9.c
@@ -0,0 +1,414 @@
+/**************************************************************************
+ETHERBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters.
+ Date: Mar 22 1995
+
+ This code is based heavily on David Greenman's if_ed.c driver and
+ Andres Vega Garcia's if_ep.c driver.
+
+ Copyright (C) 1993-1994, David Greenman, Martin Renters.
+ Copyright (C) 1993-1995, Andres Vega Garcia.
+ Copyright (C) 1995, Serge Babkin.
+ This software may be used, modified, copied, distributed, and sold, in
+ both source and binary form provided that the above copyright and these
+ terms are retained. Under no circumstances are the authors responsible for
+ the proper functioning of this software, nor do the authors assume any
+ responsibility for damages incurred with its use.
+
+3c509 support added by Serge Babkin (babkin@hq.icb.chel.su)
+
+$Id$
+
+***************************************************************************/
+
+/* #define EDEBUG */
+
+#include <gpxe/ethernet.h>
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/isa.h>
+#include "3c509.h"
+
+static enum { none, bnc, utp } connector = none; /* for 3C509 */
+
+/**************************************************************************
+ETH_RESET - Reset adapter
+***************************************************************************/
+void t5x9_disable ( struct nic *nic ) {
+ /* stop card */
+ outw(RX_DISABLE, nic->ioaddr + EP_COMMAND);
+ outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
+ while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+ outw(TX_DISABLE, nic->ioaddr + EP_COMMAND);
+ outw(STOP_TRANSCEIVER, nic->ioaddr + EP_COMMAND);
+ udelay(1000);
+ outw(RX_RESET, nic->ioaddr + EP_COMMAND);
+ outw(TX_RESET, nic->ioaddr + EP_COMMAND);
+ outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND);
+ outw(SET_RD_0_MASK, nic->ioaddr + EP_COMMAND);
+ outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND);
+ outw(SET_RX_FILTER, nic->ioaddr + EP_COMMAND);
+
+ /*
+ * wait for reset to complete
+ */
+ while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+
+ GO_WINDOW(nic->ioaddr,0);
+
+ /* Disable the card */
+ outw(0, nic->ioaddr + EP_W0_CONFIG_CTRL);
+
+ /* Configure IRQ to none */
+ outw(SET_IRQ(0), nic->ioaddr + EP_W0_RESOURCE_CFG);
+}
+
+static void t509_enable ( struct nic *nic ) {
+ int i;
+
+ /* Enable the card */
+ GO_WINDOW(nic->ioaddr,0);
+ outw(ENABLE_DRQ_IRQ, nic->ioaddr + EP_W0_CONFIG_CTRL);
+
+ GO_WINDOW(nic->ioaddr,2);
+
+ /* Reload the ether_addr. */
+ for (i = 0; i < ETH_ALEN; i++)
+ outb(nic->node_addr[i], nic->ioaddr + EP_W2_ADDR_0 + i);
+
+ outw(RX_RESET, nic->ioaddr + EP_COMMAND);
+ outw(TX_RESET, nic->ioaddr + EP_COMMAND);
+
+ /* Window 1 is operating window */
+ GO_WINDOW(nic->ioaddr,1);
+ for (i = 0; i < 31; i++)
+ inb(nic->ioaddr + EP_W1_TX_STATUS);
+
+ /* get rid of stray intr's */
+ outw(ACK_INTR | 0xff, nic->ioaddr + EP_COMMAND);
+
+ outw(SET_RD_0_MASK | S_5_INTS, nic->ioaddr + EP_COMMAND);
+
+ outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND);
+
+ outw(SET_RX_FILTER | FIL_GROUP | FIL_INDIVIDUAL | FIL_BRDCST,
+ nic->ioaddr + EP_COMMAND);
+
+ /* configure BNC */
+ if (connector == bnc) {
+ outw(START_TRANSCEIVER, nic->ioaddr + EP_COMMAND);
+ udelay(1000);
+ }
+ /* configure UTP */
+ else if (connector == utp) {
+ GO_WINDOW(nic->ioaddr,4);
+ outw(ENABLE_UTP, nic->ioaddr + EP_W4_MEDIA_TYPE);
+ sleep(2); /* Give time for media to negotiate */
+ GO_WINDOW(nic->ioaddr,1);
+ }
+
+ /* start transceiver and receiver */
+ outw(RX_ENABLE, nic->ioaddr + EP_COMMAND);
+ outw(TX_ENABLE, nic->ioaddr + EP_COMMAND);
+
+ /* set early threshold for minimal packet length */
+ outw(SET_RX_EARLY_THRESH | ETH_ZLEN, nic->ioaddr + EP_COMMAND);
+ outw(SET_TX_START_THRESH | 16, nic->ioaddr + EP_COMMAND);
+}
+
+static void t509_reset ( struct nic *nic ) {
+ t5x9_disable ( nic );
+ t509_enable ( nic );
+}
+
+/**************************************************************************
+ETH_TRANSMIT - Transmit a frame
+***************************************************************************/
+static char padmap[] = {
+ 0, 3, 2, 1};
+
+static void t509_transmit(
+struct nic *nic,
+const char *d, /* Destination */
+unsigned int t, /* Type */
+unsigned int s, /* size */
+const char *p) /* Packet */
+{
+ register unsigned int len;
+ int pad;
+ int status;
+
+#ifdef EDEBUG
+ printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
+#endif
+
+ /* swap bytes of type */
+ t= htons(t);
+
+ len=s+ETH_HLEN; /* actual length of packet */
+ pad = padmap[len & 3];
+
+ /*
+ * The 3c509 automatically pads short packets to minimum ethernet length,
+ * but we drop packets that are too large. Perhaps we should truncate
+ * them instead?
+ */
+ if (len + pad > ETH_FRAME_LEN) {
+ return;
+ }
+
+ /* drop acknowledgements */
+ while ((status=inb(nic->ioaddr + EP_W1_TX_STATUS)) & TXS_COMPLETE ) {
+ if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
+ outw(TX_RESET, nic->ioaddr + EP_COMMAND);
+ outw(TX_ENABLE, nic->ioaddr + EP_COMMAND);
+ }
+ outb(0x0, nic->ioaddr + EP_W1_TX_STATUS);
+ }
+
+ while (inw(nic->ioaddr + EP_W1_FREE_TX) < (unsigned short)len + pad + 4)
+ ; /* no room in FIFO */
+
+ outw(len, nic->ioaddr + EP_W1_TX_PIO_WR_1);
+ outw(0x0, nic->ioaddr + EP_W1_TX_PIO_WR_1); /* Second dword meaningless */
+
+ /* write packet */
+ outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
+ outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
+ outw(t, nic->ioaddr + EP_W1_TX_PIO_WR_1);
+ outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, p, s / 2);
+ if (s & 1)
+ outb(*(p+s - 1), nic->ioaddr + EP_W1_TX_PIO_WR_1);
+
+ while (pad--)
+ outb(0, nic->ioaddr + EP_W1_TX_PIO_WR_1); /* Padding */
+
+ /* wait for Tx complete */
+ while((inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
+ ;
+}
+
+/**************************************************************************
+ETH_POLL - Wait for a frame
+***************************************************************************/
+static int t509_poll(struct nic *nic, int retrieve)
+{
+ /* common variables */
+ /* variables for 3C509 */
+ short status, cst;
+ register short rx_fifo;
+
+ cst=inw(nic->ioaddr + EP_STATUS);
+
+#ifdef EDEBUG
+ if(cst & 0x1FFF)
+ printf("-%hX-",cst);
+#endif
+
+ if( (cst & S_RX_COMPLETE)==0 ) {
+ /* acknowledge everything */
+ outw(ACK_INTR| (cst & S_5_INTS), nic->ioaddr + EP_COMMAND);
+ outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND);
+
+ return 0;
+ }
+
+ status = inw(nic->ioaddr + EP_W1_RX_STATUS);
+#ifdef EDEBUG
+ printf("*%hX*",status);
+#endif
+
+ if (status & ERR_RX) {
+ outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
+ return 0;
+ }
+
+ rx_fifo = status & RX_BYTES_MASK;
+ if (rx_fifo==0)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ /* read packet */
+#ifdef EDEBUG
+ printf("[l=%d",rx_fifo);
+#endif
+ insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
+ if(rx_fifo & 1)
+ nic->packet[rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1);
+ nic->packetlen=rx_fifo;
+
+ while(1) {
+ status = inw(nic->ioaddr + EP_W1_RX_STATUS);
+#ifdef EDEBUG
+ printf("*%hX*",status);
+#endif
+ rx_fifo = status & RX_BYTES_MASK;
+ if(rx_fifo>0) {
+ insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
+ if(rx_fifo & 1)
+ nic->packet[nic->packetlen+rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1);
+ nic->packetlen+=rx_fifo;
+#ifdef EDEBUG
+ printf("+%d",rx_fifo);
+#endif
+ }
+ if(( status & RX_INCOMPLETE )==0) {
+#ifdef EDEBUG
+ printf("=%d",nic->packetlen);
+#endif
+ break;
+ }
+ udelay(1000); /* if incomplete wait 1 ms */
+ }
+ /* acknowledge reception of packet */
+ outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
+ while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+#ifdef EDEBUG
+{
+ unsigned short type = 0; /* used by EDEBUG */
+ type = (nic->packet[12]<<8) | nic->packet[13];
+ if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
+ nic->packet[5] == 0xFF*ETH_ALEN)
+ printf(",t=%hX,b]",type);
+ else
+ printf(",t=%hX]",type);
+}
+#endif
+ return (1);
+}
+
+/**************************************************************************
+ETH_IRQ - interrupt handling
+***************************************************************************/
+static void t509_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/*************************************************************************
+ 3Com 509 - specific routines
+**************************************************************************/
+
+static int eeprom_rdy ( uint16_t ioaddr ) {
+ int i;
+
+ for (i = 0; is_eeprom_busy(ioaddr) && i < MAX_EEPROMBUSY; i++);
+ if (i >= MAX_EEPROMBUSY) {
+ /* printf("3c509: eeprom failed to come ready.\n"); */
+ /* memory in EPROM is tight */
+ /* printf("3c509: eeprom busy.\n"); */
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * get_e: gets a 16 bits word from the EEPROM.
+ */
+static int get_e ( uint16_t ioaddr, int offset ) {
+ GO_WINDOW(ioaddr,0);
+ if (!eeprom_rdy(ioaddr))
+ return (0xffff);
+ outw(EEPROM_CMD_RD | offset, ioaddr + EP_W0_EEPROM_COMMAND);
+ if (!eeprom_rdy(ioaddr))
+ return (0xffff);
+ return (inw(ioaddr + EP_W0_EEPROM_DATA));
+}
+
+static struct nic_operations t509_operations = {
+ .connect = dummy_connect,
+ .poll = t509_poll,
+ .transmit = t509_transmit,
+ .irq = t509_irq,
+};
+
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+***************************************************************************/
+int t5x9_probe ( struct nic *nic,
+ uint16_t prod_id_check, uint16_t prod_id_mask ) {
+ uint16_t prod_id;
+ int i,j;
+ unsigned short *p;
+
+ /* Check product ID */
+ prod_id = get_e ( nic->ioaddr, EEPROM_PROD_ID );
+ if ( ( prod_id & prod_id_mask ) != prod_id_check ) {
+ printf ( "EEPROM Product ID is incorrect (%hx & %hx != %hx)\n",
+ prod_id, prod_id_mask, prod_id_check );
+ return 0;
+ }
+
+ /* test for presence of connectors */
+ GO_WINDOW(nic->ioaddr,0);
+ i = inw(nic->ioaddr + EP_W0_CONFIG_CTRL);
+ j = (inw(nic->ioaddr + EP_W0_ADDRESS_CFG) >> 14) & 0x3;
+
+ switch(j) {
+ case 0:
+ if (i & IS_UTP) {
+ printf("10baseT\n");
+ connector = utp;
+ } else {
+ printf("10baseT not present\n");
+ return 0;
+ }
+ break;
+ case 1:
+ if (i & IS_AUI) {
+ printf("10base5\n");
+ } else {
+ printf("10base5 not present\n");
+ return 0;
+ }
+ break;
+ case 3:
+ if (i & IS_BNC) {
+ printf("10base2\n");
+ connector = bnc;
+ } else {
+ printf("10base2 not present\n");
+ return 0;
+ }
+ break;
+ default:
+ printf("unknown connector\n");
+ return 0;
+ }
+
+ /*
+ * Read the station address from the eeprom
+ */
+ p = (unsigned short *) nic->node_addr;
+ for (i = 0; i < ETH_ALEN / 2; i++) {
+ p[i] = htons(get_e(nic->ioaddr,i));
+ GO_WINDOW(nic->ioaddr,2);
+ outw(ntohs(p[i]), nic->ioaddr + EP_W2_ADDR_0 + (i * 2));
+ }
+
+ DBG ( "Ethernet Address: %s\n", eth_ntoa ( nic->node_addr ) );
+
+ t509_reset(nic);
+
+ nic->nic_op = &t509_operations;
+ return 1;
+
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/3c90x.c b/gpxe/src/drivers/net/3c90x.c
new file mode 100644
index 00000000..8158239d
--- /dev/null
+++ b/gpxe/src/drivers/net/3c90x.c
@@ -0,0 +1,1027 @@
+/*
+ * 3c90x.c -- This file implements the 3c90x driver for etherboot. Written
+ * by Greg Beeley, Greg.Beeley@LightSys.org. Modified by Steve Smith,
+ * Steve.Smith@Juno.Com. Alignment bug fix Neil Newell (nn@icenoir.net).
+ *
+ * This program Copyright (C) 1999 LightSys Technology Services, Inc.
+ * Portions Copyright (C) 1999 Steve Smith
+ *
+ * This program may be re-distributed in source or binary form, modified,
+ * sold, or copied for any purpose, provided that the above copyright message
+ * and this text are included with all source copies or derivative works, and
+ * provided that the above copyright message and this text are included in the
+ * documentation of any binary-only distributions. This program is distributed
+ * WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR
+ * PURPOSE or MERCHANTABILITY. Please read the associated documentation
+ * "3c90x.txt" before compiling and using this driver.
+ *
+ * --------
+ *
+ * Program written with the assistance of the 3com documentation for
+ * the 3c905B-TX card, as well as with some assistance from the 3c59x
+ * driver Donald Becker wrote for the Linux kernel, and with some assistance
+ * from the remainder of the Etherboot distribution.
+ *
+ * REVISION HISTORY:
+ *
+ * v0.10 1-26-1998 GRB Initial implementation.
+ * v0.90 1-27-1998 GRB System works.
+ * v1.00pre1 2-11-1998 GRB Got prom boot issue fixed.
+ * v2.0 9-24-1999 SCS Modified for 3c905 (from 3c905b code)
+ * Re-wrote poll and transmit for
+ * better error recovery and heavy
+ * network traffic operation
+ * v2.01 5-26-2003 NN Fixed driver alignment issue which
+ * caused system lockups if driver structures
+ * not 8-byte aligned.
+ * v2.02 11-28-2007 GSt Got polling working again by replacing
+ * "for(i=0;i<40000;i++);" with "mdelay(1);"
+ *
+ */
+
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+
+static struct nic_operations a3c90x_operations;
+
+#define XCVR_MAGIC (0x5A00)
+/** any single transmission fails after 16 collisions or other errors
+ ** this is the number of times to retry the transmission -- this should
+ ** be plenty
+ **/
+#define XMIT_RETRIES 250
+
+/*** Register definitions for the 3c905 ***/
+enum Registers
+ {
+ regPowerMgmtCtrl_w = 0x7c, /** 905B Revision Only **/
+ regUpMaxBurst_w = 0x7a, /** 905B Revision Only **/
+ regDnMaxBurst_w = 0x78, /** 905B Revision Only **/
+ regDebugControl_w = 0x74, /** 905B Revision Only **/
+ regDebugData_l = 0x70, /** 905B Revision Only **/
+ regRealTimeCnt_l = 0x40, /** Universal **/
+ regUpBurstThresh_b = 0x3e, /** 905B Revision Only **/
+ regUpPoll_b = 0x3d, /** 905B Revision Only **/
+ regUpPriorityThresh_b = 0x3c, /** 905B Revision Only **/
+ regUpListPtr_l = 0x38, /** Universal **/
+ regCountdown_w = 0x36, /** Universal **/
+ regFreeTimer_w = 0x34, /** Universal **/
+ regUpPktStatus_l = 0x30, /** Universal with Exception, pg 130 **/
+ regTxFreeThresh_b = 0x2f, /** 90X Revision Only **/
+ regDnPoll_b = 0x2d, /** 905B Revision Only **/
+ regDnPriorityThresh_b = 0x2c, /** 905B Revision Only **/
+ regDnBurstThresh_b = 0x2a, /** 905B Revision Only **/
+ regDnListPtr_l = 0x24, /** Universal with Exception, pg 107 **/
+ regDmaCtrl_l = 0x20, /** Universal with Exception, pg 106 **/
+ /** **/
+ regIntStatusAuto_w = 0x1e, /** 905B Revision Only **/
+ regTxStatus_b = 0x1b, /** Universal with Exception, pg 113 **/
+ regTimer_b = 0x1a, /** Universal **/
+ regTxPktId_b = 0x18, /** 905B Revision Only **/
+ regCommandIntStatus_w = 0x0e, /** Universal (Command Variations) **/
+ };
+
+/** following are windowed registers **/
+enum Registers7
+ {
+ regPowerMgmtEvent_7_w = 0x0c, /** 905B Revision Only **/
+ regVlanEtherType_7_w = 0x04, /** 905B Revision Only **/
+ regVlanMask_7_w = 0x00, /** 905B Revision Only **/
+ };
+
+enum Registers6
+ {
+ regBytesXmittedOk_6_w = 0x0c, /** Universal **/
+ regBytesRcvdOk_6_w = 0x0a, /** Universal **/
+ regUpperFramesOk_6_b = 0x09, /** Universal **/
+ regFramesDeferred_6_b = 0x08, /** Universal **/
+ regFramesRecdOk_6_b = 0x07, /** Universal with Exceptions, pg 142 **/
+ regFramesXmittedOk_6_b = 0x06, /** Universal **/
+ regRxOverruns_6_b = 0x05, /** Universal **/
+ regLateCollisions_6_b = 0x04, /** Universal **/
+ regSingleCollisions_6_b = 0x03, /** Universal **/
+ regMultipleCollisions_6_b = 0x02, /** Universal **/
+ regSqeErrors_6_b = 0x01, /** Universal **/
+ regCarrierLost_6_b = 0x00, /** Universal **/
+ };
+
+enum Registers5
+ {
+ regIndicationEnable_5_w = 0x0c, /** Universal **/
+ regInterruptEnable_5_w = 0x0a, /** Universal **/
+ regTxReclaimThresh_5_b = 0x09, /** 905B Revision Only **/
+ regRxFilter_5_b = 0x08, /** Universal **/
+ regRxEarlyThresh_5_w = 0x06, /** Universal **/
+ regTxStartThresh_5_w = 0x00, /** Universal **/
+ };
+
+enum Registers4
+ {
+ regUpperBytesOk_4_b = 0x0d, /** Universal **/
+ regBadSSD_4_b = 0x0c, /** Universal **/
+ regMediaStatus_4_w = 0x0a, /** Universal with Exceptions, pg 201 **/
+ regPhysicalMgmt_4_w = 0x08, /** Universal **/
+ regNetworkDiagnostic_4_w = 0x06, /** Universal with Exceptions, pg 203 **/
+ regFifoDiagnostic_4_w = 0x04, /** Universal with Exceptions, pg 196 **/
+ regVcoDiagnostic_4_w = 0x02, /** Undocumented? **/
+ };
+
+enum Registers3
+ {
+ regTxFree_3_w = 0x0c, /** Universal **/
+ regRxFree_3_w = 0x0a, /** Universal with Exceptions, pg 125 **/
+ regResetMediaOptions_3_w = 0x08, /** Media Options on B Revision, **/
+ /** Reset Options on Non-B Revision **/
+ regMacControl_3_w = 0x06, /** Universal with Exceptions, pg 199 **/
+ regMaxPktSize_3_w = 0x04, /** 905B Revision Only **/
+ regInternalConfig_3_l = 0x00, /** Universal, different bit **/
+ /** definitions, pg 59 **/
+ };
+
+enum Registers2
+ {
+ regResetOptions_2_w = 0x0c, /** 905B Revision Only **/
+ regStationMask_2_3w = 0x06, /** Universal with Exceptions, pg 127 **/
+ regStationAddress_2_3w = 0x00, /** Universal with Exceptions, pg 127 **/
+ };
+
+enum Registers1
+ {
+ regRxStatus_1_w = 0x0a, /** 90X Revision Only, Pg 126 **/
+ };
+
+enum Registers0
+ {
+ regEepromData_0_w = 0x0c, /** Universal **/
+ regEepromCommand_0_w = 0x0a, /** Universal **/
+ regBiosRomData_0_b = 0x08, /** 905B Revision Only **/
+ regBiosRomAddr_0_l = 0x04, /** 905B Revision Only **/
+ };
+
+
+/*** The names for the eight register windows ***/
+enum Windows
+ {
+ winPowerVlan7 = 0x07,
+ winStatistics6 = 0x06,
+ winTxRxControl5 = 0x05,
+ winDiagnostics4 = 0x04,
+ winTxRxOptions3 = 0x03,
+ winAddressing2 = 0x02,
+ winUnused1 = 0x01,
+ winEepromBios0 = 0x00,
+ };
+
+
+/*** Command definitions for the 3c90X ***/
+enum Commands
+ {
+ cmdGlobalReset = 0x00, /** Universal with Exceptions, pg 151 **/
+ cmdSelectRegisterWindow = 0x01, /** Universal **/
+ cmdEnableDcConverter = 0x02, /** **/
+ cmdRxDisable = 0x03, /** **/
+ cmdRxEnable = 0x04, /** Universal **/
+ cmdRxReset = 0x05, /** Universal **/
+ cmdStallCtl = 0x06, /** Universal **/
+ cmdTxEnable = 0x09, /** Universal **/
+ cmdTxDisable = 0x0A, /** **/
+ cmdTxReset = 0x0B, /** Universal **/
+ cmdRequestInterrupt = 0x0C, /** **/
+ cmdAcknowledgeInterrupt = 0x0D, /** Universal **/
+ cmdSetInterruptEnable = 0x0E, /** Universal **/
+ cmdSetIndicationEnable = 0x0F, /** Universal **/
+ cmdSetRxFilter = 0x10, /** Universal **/
+ cmdSetRxEarlyThresh = 0x11, /** **/
+ cmdSetTxStartThresh = 0x13, /** **/
+ cmdStatisticsEnable = 0x15, /** **/
+ cmdStatisticsDisable = 0x16, /** **/
+ cmdDisableDcConverter = 0x17, /** **/
+ cmdSetTxReclaimThresh = 0x18, /** **/
+ cmdSetHashFilterBit = 0x19, /** **/
+ };
+
+
+/*** Values for int status register bitmask **/
+#define INT_INTERRUPTLATCH (1<<0)
+#define INT_HOSTERROR (1<<1)
+#define INT_TXCOMPLETE (1<<2)
+#define INT_RXCOMPLETE (1<<4)
+#define INT_RXEARLY (1<<5)
+#define INT_INTREQUESTED (1<<6)
+#define INT_UPDATESTATS (1<<7)
+#define INT_LINKEVENT (1<<8)
+#define INT_DNCOMPLETE (1<<9)
+#define INT_UPCOMPLETE (1<<10)
+#define INT_CMDINPROGRESS (1<<12)
+#define INT_WINDOWNUMBER (7<<13)
+
+
+/*** TX descriptor ***/
+typedef struct
+ {
+ unsigned int DnNextPtr;
+ unsigned int FrameStartHeader;
+ unsigned int HdrAddr;
+ unsigned int HdrLength;
+ unsigned int DataAddr;
+ unsigned int DataLength;
+ }
+ TXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */
+
+/*** RX descriptor ***/
+typedef struct
+ {
+ unsigned int UpNextPtr;
+ unsigned int UpPktStatus;
+ unsigned int DataAddr;
+ unsigned int DataLength;
+ }
+ RXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */
+
+/*** Global variables ***/
+static struct
+ {
+ unsigned int is3c556;
+ unsigned char isBrev;
+ unsigned char CurrentWindow;
+ unsigned int IOAddr;
+ unsigned char HWAddr[ETH_ALEN];
+ TXD TransmitDPD;
+ RXD ReceiveUPD;
+ }
+ INF_3C90X;
+
+
+/*** a3c90x_internal_IssueCommand: sends a command to the 3c90x card
+ ***/
+static int
+a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param)
+ {
+ unsigned int val;
+
+ /** Build the cmd. **/
+ val = cmd;
+ val <<= 11;
+ val |= param;
+
+ /** Send the cmd to the cmd register **/
+ outw(val, ioaddr + regCommandIntStatus_w);
+
+ /** Wait for the cmd to complete, if necessary **/
+ while (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
+
+ return 0;
+ }
+
+
+/*** a3c90x_internal_SetWindow: selects a register window set.
+ ***/
+static int
+a3c90x_internal_SetWindow(int ioaddr, int window)
+ {
+
+ /** Window already as set? **/
+ if (INF_3C90X.CurrentWindow == window) return 0;
+
+ /** Issue the window command. **/
+ a3c90x_internal_IssueCommand(ioaddr, cmdSelectRegisterWindow, window);
+ INF_3C90X.CurrentWindow = window;
+
+ return 0;
+ }
+
+
+/*** a3c90x_internal_ReadEeprom - read data from the serial eeprom.
+ ***/
+static unsigned short
+a3c90x_internal_ReadEeprom(int ioaddr, int address)
+ {
+ unsigned short val;
+
+ /** Select correct window **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winEepromBios0);
+
+ /** Make sure the eeprom isn't busy **/
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ /** Read the value. **/
+ if (INF_3C90X.is3c556)
+ {
+ outw(address + (0x230), ioaddr + regEepromCommand_0_w);
+ }
+ else
+ {
+ outw(address + ((0x02)<<6), ioaddr + regEepromCommand_0_w);
+ }
+
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+ val = inw(ioaddr + regEepromData_0_w);
+
+ return val;
+ }
+
+
+#if 0
+/*** a3c90x_internal_WriteEepromWord - write a physical word of
+ *** data to the onboard serial eeprom (not the BIOS prom, but the
+ *** nvram in the card that stores, among other things, the MAC
+ *** address).
+ ***/
+static int
+a3c90x_internal_WriteEepromWord(int ioaddr, int address, unsigned short value)
+ {
+ /** Select register window **/
+ a3c90x_internal_SetWindow(ioaddr, winEepromBios0);
+
+ /** Verify Eeprom not busy **/
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ /** Issue WriteEnable, and wait for completion. **/
+ outw(0x30, ioaddr + regEepromCommand_0_w);
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ /** Issue EraseRegister, and wait for completion. **/
+ outw(address + ((0x03)<<6), ioaddr + regEepromCommand_0_w);
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ /** Send the new data to the eeprom, and wait for completion. **/
+ outw(value, ioaddr + regEepromData_0_w);
+ outw(0x30, ioaddr + regEepromCommand_0_w);
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ /** Burn the new data into the eeprom, and wait for completion. **/
+ outw(address + ((0x01)<<6), ioaddr + regEepromCommand_0_w);
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ return 0;
+ }
+#endif
+
+#if 0
+/*** a3c90x_internal_WriteEeprom - write data to the serial eeprom,
+ *** and re-compute the eeprom checksum.
+ ***/
+static int
+a3c90x_internal_WriteEeprom(int ioaddr, int address, unsigned short value)
+ {
+ int cksum = 0,v;
+ int i;
+ int maxAddress, cksumAddress;
+
+ if (INF_3C90X.isBrev)
+ {
+ maxAddress=0x1f;
+ cksumAddress=0x20;
+ }
+ else
+ {
+ maxAddress=0x16;
+ cksumAddress=0x17;
+ }
+
+ /** Write the value. **/
+ if (a3c90x_internal_WriteEepromWord(ioaddr, address, value) == -1)
+ return -1;
+
+ /** Recompute the checksum. **/
+ for(i=0;i<=maxAddress;i++)
+ {
+ v = a3c90x_internal_ReadEeprom(ioaddr, i);
+ cksum ^= (v & 0xFF);
+ cksum ^= ((v>>8) & 0xFF);
+ }
+ /** Write the checksum to the location in the eeprom **/
+ if (a3c90x_internal_WriteEepromWord(ioaddr, cksumAddress, cksum) == -1)
+ return -1;
+
+ return 0;
+ }
+#endif
+
+/*** a3c90x_reset: exported function that resets the card to its default
+ *** state. This is so the Linux driver can re-set the card up the way
+ *** it wants to. If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will
+ *** not alter the selected transceiver that we used to download the boot
+ *** image.
+ ***/
+static void a3c90x_reset(void)
+ {
+#ifdef CFG_3C90X_PRESERVE_XCVR
+ int cfg;
+ /** Read the current InternalConfig value. **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
+ cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
+#endif
+
+ /** Send the reset command to the card **/
+ printf("Issuing RESET:\n");
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdGlobalReset, 0);
+
+ /** wait for reset command to complete **/
+ while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
+
+ /** global reset command resets station mask, non-B revision cards
+ ** require explicit reset of values
+ **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
+
+#ifdef CFG_3C90X_PRESERVE_XCVR
+ /** Re-set the original InternalConfig value from before reset **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
+ outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
+
+ /** enable DC converter for 10-Base-T **/
+ if ((cfg&0x0300) == 0x0300)
+ {
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
+ }
+#endif
+
+ /** Issue transmit reset, wait for command completion **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0);
+ while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
+ ;
+ if (! INF_3C90X.isBrev)
+ outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
+
+ /**
+ ** reset of the receiver on B-revision cards re-negotiates the link
+ ** takes several seconds (a computer eternity)
+ **/
+ if (INF_3C90X.isBrev)
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
+ else
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
+ while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
+ ;
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
+
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
+ cmdSetInterruptEnable, 0);
+ /** enable rxComplete and txComplete **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
+ cmdSetIndicationEnable, 0x0014);
+ /** acknowledge any pending status flags **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
+ cmdAcknowledgeInterrupt, 0x661);
+
+ return;
+ }
+
+
+
+/*** a3c90x_transmit: exported function that transmits a packet. Does not
+ *** return any particular status. Parameters are:
+ *** d[6] - destination address, ethernet;
+ *** t - protocol type (ARP, IP, etc);
+ *** s - size of the non-header part of the packet that needs transmitted;
+ *** p - the pointer to the packet data itself.
+ ***/
+static void
+a3c90x_transmit(struct nic *nic __unused, const char *d, unsigned int t,
+ unsigned int s, const char *p)
+ {
+
+ struct eth_hdr
+ {
+ unsigned char dst_addr[ETH_ALEN];
+ unsigned char src_addr[ETH_ALEN];
+ unsigned short type;
+ } hdr;
+
+ unsigned char status;
+ unsigned i, retries;
+ tick_t ct;
+
+ for (retries=0; retries < XMIT_RETRIES ; retries++)
+ {
+ /** Stall the download engine **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 2);
+
+ /** Make sure the card is not waiting on us **/
+ inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
+ inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
+
+ while (inw(INF_3C90X.IOAddr+regCommandIntStatus_w) &
+ INT_CMDINPROGRESS)
+ ;
+
+ /** Set the ethernet packet type **/
+ hdr.type = htons(t);
+
+ /** Copy the destination address **/
+ memcpy(hdr.dst_addr, d, ETH_ALEN);
+
+ /** Copy our MAC address **/
+ memcpy(hdr.src_addr, INF_3C90X.HWAddr, ETH_ALEN);
+
+ /** Setup the DPD (download descriptor) **/
+ INF_3C90X.TransmitDPD.DnNextPtr = 0;
+ /** set notification for transmission completion (bit 15) **/
+ INF_3C90X.TransmitDPD.FrameStartHeader = (s + sizeof(hdr)) | 0x8000;
+ INF_3C90X.TransmitDPD.HdrAddr = virt_to_bus(&hdr);
+ INF_3C90X.TransmitDPD.HdrLength = sizeof(hdr);
+ INF_3C90X.TransmitDPD.DataAddr = virt_to_bus(p);
+ INF_3C90X.TransmitDPD.DataLength = s + (1<<31);
+
+ /** Send the packet **/
+ outl(virt_to_bus(&(INF_3C90X.TransmitDPD)),
+ INF_3C90X.IOAddr + regDnListPtr_l);
+
+ /** End Stall and Wait for upload to complete. **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 3);
+ while(inl(INF_3C90X.IOAddr + regDnListPtr_l) != 0)
+ ;
+
+ /** Wait for NIC Transmit to Complete **/
+ ct = currticks();
+
+ while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) &&
+ ct + 10*USECS_IN_MSEC < currticks());
+ ;
+
+ if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004))
+ {
+ printf("3C90X: Tx Timeout\n");
+ continue;
+ }
+
+ status = inb(INF_3C90X.IOAddr + regTxStatus_b);
+
+ /** acknowledge transmit interrupt by writing status **/
+ outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
+
+ /** successful completion (sans "interrupt Requested" bit) **/
+ if ((status & 0xbf) == 0x80)
+ return;
+
+ printf("3C90X: Status (%hhX)\n", status);
+ /** check error codes **/
+ if (status & 0x02)
+ {
+ printf("3C90X: Tx Reclaim Error (%hhX)\n", status);
+ a3c90x_reset();
+ }
+ else if (status & 0x04)
+ {
+ printf("3C90X: Tx Status Overflow (%hhX)\n", status);
+ for (i=0; i<32; i++)
+ outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
+ /** must re-enable after max collisions before re-issuing tx **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
+ }
+ else if (status & 0x08)
+ {
+ printf("3C90X: Tx Max Collisions (%hhX)\n", status);
+ /** must re-enable after max collisions before re-issuing tx **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
+ }
+ else if (status & 0x10)
+ {
+ printf("3C90X: Tx Underrun (%hhX)\n", status);
+ a3c90x_reset();
+ }
+ else if (status & 0x20)
+ {
+ printf("3C90X: Tx Jabber (%hhX)\n", status);
+ a3c90x_reset();
+ }
+ else if ((status & 0x80) != 0x80)
+ {
+ printf("3C90X: Internal Error - Incomplete Transmission (%hhX)\n",
+ status);
+ a3c90x_reset();
+ }
+ }
+
+ /** failed after RETRY attempts **/
+ printf("Failed to send after %d retries\n", retries);
+ return;
+
+ }
+
+
+
+/*** a3c90x_poll: exported routine that waits for a certain length of time
+ *** for a packet, and if it sees none, returns 0. This routine should
+ *** copy the packet to nic->packet if it gets a packet and set the size
+ *** in nic->packetlen. Return 1 if a packet was found.
+ ***/
+static int
+a3c90x_poll(struct nic *nic, int retrieve)
+ {
+ int errcode;
+
+ if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0010))
+ {
+ return 0;
+ }
+
+ if ( ! retrieve ) return 1;
+
+ /** we don't need to acknowledge rxComplete -- the upload engine
+ ** does it for us.
+ **/
+
+ /** Build the up-load descriptor **/
+ INF_3C90X.ReceiveUPD.UpNextPtr = 0;
+ INF_3C90X.ReceiveUPD.UpPktStatus = 0;
+ INF_3C90X.ReceiveUPD.DataAddr = virt_to_bus(nic->packet);
+ INF_3C90X.ReceiveUPD.DataLength = 1536 + (1<<31);
+
+ /** Submit the upload descriptor to the NIC **/
+ outl(virt_to_bus(&(INF_3C90X.ReceiveUPD)),
+ INF_3C90X.IOAddr + regUpListPtr_l);
+
+ /** Wait for upload completion (upComplete(15) or upError (14)) **/
+ mdelay(1);
+ while((INF_3C90X.ReceiveUPD.UpPktStatus & ((1<<14) | (1<<15))) == 0)
+ mdelay(1);
+
+ /** Check for Error (else we have good packet) **/
+ if (INF_3C90X.ReceiveUPD.UpPktStatus & (1<<14))
+ {
+ errcode = INF_3C90X.ReceiveUPD.UpPktStatus;
+ if (errcode & (1<<16))
+ printf("3C90X: Rx Overrun (%hX)\n",errcode>>16);
+ else if (errcode & (1<<17))
+ printf("3C90X: Runt Frame (%hX)\n",errcode>>16);
+ else if (errcode & (1<<18))
+ printf("3C90X: Alignment Error (%hX)\n",errcode>>16);
+ else if (errcode & (1<<19))
+ printf("3C90X: CRC Error (%hX)\n",errcode>>16);
+ else if (errcode & (1<<20))
+ printf("3C90X: Oversized Frame (%hX)\n",errcode>>16);
+ else
+ printf("3C90X: Packet error (%hX)\n",errcode>>16);
+ return 0;
+ }
+
+ /** Ok, got packet. Set length in nic->packetlen. **/
+ nic->packetlen = (INF_3C90X.ReceiveUPD.UpPktStatus & 0x1FFF);
+
+ return 1;
+ }
+
+
+
+/*** a3c90x_disable: exported routine to disable the card. What's this for?
+ *** the eepro100.c driver didn't have one, so I just left this one empty too.
+ *** Ideas anyone?
+ *** Must turn off receiver at least so stray packets will not corrupt memory
+ *** [Ken]
+ ***/
+static void
+a3c90x_disable ( struct nic *nic __unused ) {
+ a3c90x_reset();
+ /* Disable the receiver and transmitter. */
+ outw(cmdRxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
+ outw(cmdTxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
+}
+
+static void a3c90x_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/*** a3c90x_probe: exported routine to probe for the 3c905 card and perform
+ *** initialization. If this routine is called, the pci functions did find the
+ *** card. We just have to init it here.
+ ***/
+static int a3c90x_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ int i, c;
+ unsigned short eeprom[0x21];
+ unsigned int cfg;
+ unsigned int mopt;
+ unsigned int mstat;
+ unsigned short linktype;
+#define HWADDR_OFFSET 10
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ adjust_pci_device(pci);
+
+ nic->ioaddr = pci->ioaddr;
+ nic->irqno = 0;
+
+ INF_3C90X.is3c556 = (pci->device == 0x6055);
+ INF_3C90X.IOAddr = pci->ioaddr & ~3;
+ INF_3C90X.CurrentWindow = 255;
+ switch (a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, 0x03))
+ {
+ case 0x9000: /** 10 Base TPO **/
+ case 0x9001: /** 10/100 T4 **/
+ case 0x9050: /** 10/100 TPO **/
+ case 0x9051: /** 10 Base Combo **/
+ INF_3C90X.isBrev = 0;
+ break;
+
+ case 0x9004: /** 10 Base TPO **/
+ case 0x9005: /** 10 Base Combo **/
+ case 0x9006: /** 10 Base TPO and Base2 **/
+ case 0x900A: /** 10 Base FL **/
+ case 0x9055: /** 10/100 TPO **/
+ case 0x9056: /** 10/100 T4 **/
+ case 0x905A: /** 10 Base FX **/
+ default:
+ INF_3C90X.isBrev = 1;
+ break;
+ }
+
+ /** Load the EEPROM contents **/
+ if (INF_3C90X.isBrev)
+ {
+ for(i=0;i<=0x20;i++)
+ {
+ eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
+ }
+
+#ifdef CFG_3C90X_BOOTROM_FIX
+ /** Set xcvrSelect in InternalConfig in eeprom. **/
+ /* only necessary for 3c905b revision cards with boot PROM bug!!! */
+ a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x13, 0x0160);
+#endif
+
+#ifdef CFG_3C90X_XCVR
+ if (CFG_3C90X_XCVR == 255)
+ {
+ /** Clear the LanWorks register **/
+ a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, 0);
+ }
+ else
+ {
+ /** Set the selected permanent-xcvrSelect in the
+ ** LanWorks register
+ **/
+ a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16,
+ XCVR_MAGIC + ((CFG_3C90X_XCVR) & 0x000F));
+ }
+#endif
+ }
+ else
+ {
+ for(i=0;i<=0x17;i++)
+ {
+ eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
+ }
+ }
+
+ /** Print identification message **/
+ printf("\n\n3C90X Driver 2.02 "
+ "Copyright 1999 LightSys Technology Services, Inc.\n"
+ "Portions Copyright 1999 Steve Smith\n");
+ printf("Provided with ABSOLUTELY NO WARRANTY.\n");
+#ifdef CFG_3C90X_BOOTROM_FIX
+ if (INF_3C90X.isBrev)
+ {
+ printf("NOTE: 3c905b bootrom fix enabled; has side "
+ "effects. See 3c90x.txt for info.\n");
+ }
+#endif
+ printf("-------------------------------------------------------"
+ "------------------------\n");
+
+ /** Retrieve the Hardware address and print it on the screen. **/
+ INF_3C90X.HWAddr[0] = eeprom[HWADDR_OFFSET + 0]>>8;
+ INF_3C90X.HWAddr[1] = eeprom[HWADDR_OFFSET + 0]&0xFF;
+ INF_3C90X.HWAddr[2] = eeprom[HWADDR_OFFSET + 1]>>8;
+ INF_3C90X.HWAddr[3] = eeprom[HWADDR_OFFSET + 1]&0xFF;
+ INF_3C90X.HWAddr[4] = eeprom[HWADDR_OFFSET + 2]>>8;
+ INF_3C90X.HWAddr[5] = eeprom[HWADDR_OFFSET + 2]&0xFF;
+
+ DBG ( "MAC Address = %s\n", eth_ntoa ( INF_3C90X.HWAddr ) );
+
+ /** 3C556: Invert MII power **/
+ if (INF_3C90X.is3c556) {
+ unsigned int tmp;
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
+ tmp = inw(INF_3C90X.IOAddr + regResetOptions_2_w);
+ tmp |= 0x4000;
+ outw(tmp, INF_3C90X.IOAddr + regResetOptions_2_w);
+ }
+
+ /* Test if the link is good, if not continue */
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winDiagnostics4);
+ mstat = inw(INF_3C90X.IOAddr + regMediaStatus_4_w);
+ if((mstat & (1<<11)) == 0) {
+ printf("Valid link not established\n");
+ return 0;
+ }
+
+ /** Program the MAC address into the station address registers **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
+ outw(htons(eeprom[HWADDR_OFFSET + 0]), INF_3C90X.IOAddr + regStationAddress_2_3w);
+ outw(htons(eeprom[HWADDR_OFFSET + 1]), INF_3C90X.IOAddr + regStationAddress_2_3w+2);
+ outw(htons(eeprom[HWADDR_OFFSET + 2]), INF_3C90X.IOAddr + regStationAddress_2_3w+4);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
+
+ /** Fill in our entry in the etherboot arp table **/
+ for(i=0;i<ETH_ALEN;i++)
+ nic->node_addr[i] = (eeprom[HWADDR_OFFSET + i/2] >> (8*((i&1)^1))) & 0xff;
+
+ /** Read the media options register, print a message and set default
+ ** xcvr.
+ **
+ ** Uses Media Option command on B revision, Reset Option on non-B
+ ** revision cards -- same register address
+ **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
+ mopt = inw(INF_3C90X.IOAddr + regResetMediaOptions_3_w);
+
+ /** mask out VCO bit that is defined as 10baseFL bit on B-rev cards **/
+ if (! INF_3C90X.isBrev)
+ {
+ mopt &= 0x7F;
+ }
+
+ printf("Connectors present: ");
+ c = 0;
+ linktype = 0x0008;
+ if (mopt & 0x01)
+ {
+ printf("%s100Base-T4",(c++)?", ":"");
+ linktype = 0x0006;
+ }
+ if (mopt & 0x04)
+ {
+ printf("%s100Base-FX",(c++)?", ":"");
+ linktype = 0x0005;
+ }
+ if (mopt & 0x10)
+ {
+ printf("%s10Base-2",(c++)?", ":"");
+ linktype = 0x0003;
+ }
+ if (mopt & 0x20)
+ {
+ printf("%sAUI",(c++)?", ":"");
+ linktype = 0x0001;
+ }
+ if (mopt & 0x40)
+ {
+ printf("%sMII",(c++)?", ":"");
+ linktype = 0x0006;
+ }
+ if ((mopt & 0xA) == 0xA)
+ {
+ printf("%s10Base-T / 100Base-TX",(c++)?", ":"");
+ linktype = 0x0008;
+ }
+ else if ((mopt & 0xA) == 0x2)
+ {
+ printf("%s100Base-TX",(c++)?", ":"");
+ linktype = 0x0008;
+ }
+ else if ((mopt & 0xA) == 0x8)
+ {
+ printf("%s10Base-T",(c++)?", ":"");
+ linktype = 0x0008;
+ }
+ printf(".\n");
+
+ /** Determine transceiver type to use, depending on value stored in
+ ** eeprom 0x16
+ **/
+ if (INF_3C90X.isBrev)
+ {
+ if ((eeprom[0x16] & 0xFF00) == XCVR_MAGIC)
+ {
+ /** User-defined **/
+ linktype = eeprom[0x16] & 0x000F;
+ }
+ }
+ else
+ {
+#ifdef CFG_3C90X_XCVR
+ if (CFG_3C90X_XCVR != 255)
+ linktype = CFG_3C90X_XCVR;
+#endif /* CFG_3C90X_XCVR */
+
+ /** I don't know what MII MAC only mode is!!! **/
+ if (linktype == 0x0009)
+ {
+ if (INF_3C90X.isBrev)
+ printf("WARNING: MII External MAC Mode only supported on B-revision "
+ "cards!!!!\nFalling Back to MII Mode\n");
+ linktype = 0x0006;
+ }
+ }
+
+ /** enable DC converter for 10-Base-T **/
+ if (linktype == 0x0003)
+ {
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
+ }
+
+ /** Set the link to the type we just determined. **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
+ cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
+ cfg &= ~(0xF<<20);
+ cfg |= (linktype<<20);
+ outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
+
+ /** Now that we set the xcvr type, reset the Tx and Rx, re-enable. **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0x00);
+ while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
+ ;
+
+ if (!INF_3C90X.isBrev)
+ outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
+
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
+
+ /**
+ ** reset of the receiver on B-revision cards re-negotiates the link
+ ** takes several seconds (a computer eternity)
+ **/
+ if (INF_3C90X.isBrev)
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
+ else
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
+ while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
+ ;
+
+ /** Set the RX filter = receive only individual pkts & multicast & bcast. **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetRxFilter, 0x01 + 0x02 + 0x04);
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
+
+
+ /**
+ ** set Indication and Interrupt flags , acknowledge any IRQ's
+ **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetInterruptEnable, 0);
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
+ cmdSetIndicationEnable, 0x0014);
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
+ cmdAcknowledgeInterrupt, 0x661);
+
+ /** Set our exported functions **/
+ nic->nic_op = &a3c90x_operations;
+ return 1;
+}
+
+static struct nic_operations a3c90x_operations = {
+ .connect = dummy_connect,
+ .poll = a3c90x_poll,
+ .transmit = a3c90x_transmit,
+ .irq = a3c90x_irq,
+
+};
+
+static struct pci_device_id a3c90x_nics[] = {
+/* Original 90x revisions: */
+PCI_ROM(0x10b7, 0x6055, "3c556", "3C556"), /* Huricane */
+PCI_ROM(0x10b7, 0x9000, "3c905-tpo", "3Com900-TPO"), /* 10 Base TPO */
+PCI_ROM(0x10b7, 0x9001, "3c905-t4", "3Com900-Combo"), /* 10/100 T4 */
+PCI_ROM(0x10b7, 0x9050, "3c905-tpo100", "3Com905-TX"), /* 100 Base TX / 10/100 TPO */
+PCI_ROM(0x10b7, 0x9051, "3c905-combo", "3Com905-T4"), /* 100 Base T4 / 10 Base Combo */
+/* Newer 90xB revisions: */
+PCI_ROM(0x10b7, 0x9004, "3c905b-tpo", "3Com900B-TPO"), /* 10 Base TPO */
+PCI_ROM(0x10b7, 0x9005, "3c905b-combo", "3Com900B-Combo"), /* 10 Base Combo */
+PCI_ROM(0x10b7, 0x9006, "3c905b-tpb2", "3Com900B-2/T"), /* 10 Base TP and Base2 */
+PCI_ROM(0x10b7, 0x900a, "3c905b-fl", "3Com900B-FL"), /* 10 Base FL */
+PCI_ROM(0x10b7, 0x9055, "3c905b-tpo100", "3Com905B-TX"), /* 10/100 TPO */
+PCI_ROM(0x10b7, 0x9056, "3c905b-t4", "3Com905B-T4"), /* 10/100 T4 */
+PCI_ROM(0x10b7, 0x9058, "3c905b-9058", "3Com905B-9058"), /* Cyclone 10/100/BNC */
+PCI_ROM(0x10b7, 0x905a, "3c905b-fx", "3Com905B-FL"), /* 100 Base FX / 10 Base FX */
+/* Newer 90xC revision: */
+PCI_ROM(0x10b7, 0x9200, "3c905c-tpo", "3Com905C-TXM"), /* 10/100 TPO (3C905C-TXM) */
+PCI_ROM(0x10b7, 0x9202, "3c920b-emb-ati", "3c920B-EMB-WNM (ATI Radeon 9100 IGP)"), /* 3c920B-EMB-WNM (ATI Radeon 9100 IGP) */
+PCI_ROM(0x10b7, 0x9210, "3c920b-emb-wnm","3Com20B-EMB WNM"),
+PCI_ROM(0x10b7, 0x9800, "3c980", "3Com980-Cyclone"), /* Cyclone */
+PCI_ROM(0x10b7, 0x9805, "3c9805", "3Com9805"), /* Dual Port Server Cyclone */
+PCI_ROM(0x10b7, 0x7646, "3csoho100-tx", "3CSOHO100-TX"), /* Hurricane */
+PCI_ROM(0x10b7, 0x4500, "3c450", "3Com450 HomePNA Tornado"),
+PCI_ROM(0x10b7, 0x1201, "3c982a", "3Com982A"),
+PCI_ROM(0x10b7, 0x1202, "3c982b", "3Com982B"),
+};
+
+PCI_DRIVER ( a3c90x_driver, a3c90x_nics, PCI_NO_CLASS );
+
+DRIVER ( "3C90X", nic_driver, pci_driver, a3c90x_driver,
+ a3c90x_probe, a3c90x_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/3c90x.txt b/gpxe/src/drivers/net/3c90x.txt
new file mode 100644
index 00000000..3d6746c5
--- /dev/null
+++ b/gpxe/src/drivers/net/3c90x.txt
@@ -0,0 +1,307 @@
+
+ Instructions for use of the 3C90X driver for EtherBoot
+
+ Original 3C905B support by:
+ Greg Beeley (Greg.Beeley@LightSys.org),
+ LightSys Technology Services, Inc.
+ February 11, 1999
+
+ Updates for 3C90X family by:
+ Steve Smith (steve.smith@juno.com)
+ October 1, 1999
+
+ Minor documentation updates by
+ Greg Beeley (Greg.Beeley@LightSys.org)
+ March 29, 2000
+
+-------------------------------------------------------------------------------
+
+I OVERVIEW
+
+ The 3c90X series ethernet cards are a group of high-performance busmaster
+ DMA cards from 3Com. This particular driver supports both the 3c90x and
+ the 3c90xB revision cards. 3C90xC family support has been tested to some
+ degree but not extensively.
+
+ Here's the licensing information:
+
+ This program Copyright (C) 1999 LightSys Technology Services, Inc.
+ Portions Copyright (C) 1999 Steve Smith.
+
+ This program may be re-distributed in source or binary form, modified,
+ sold, or copied for any purpose, provided that the above copyright message
+ and this text are included with all source copies or derivative works, and
+ provided that the above copyright message and this text are included in the
+ documentation of any binary-only distributions. This program is
+ distributed WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR
+ A PARTICULAR PURPOSE or MERCHANTABILITY. Please read the associated
+ documentation "3c90x.txt" before compiling and using this driver.
+
+
+II FLASH PROMS
+
+ The 3c90xB cards, according to the 3Com documentation, only accept the
+ following flash memory chips:
+
+ Atmel AT29C512 (64 kilobyte)
+ Atmel AT29C010 (128 kilobyte)
+
+ The 3c90x cards, according to the 3Com documentation, accept the
+ following flash memory chips capacities:
+
+ 64 kb (8 kB)
+ 128 kb (16 kB)
+ 256 kb (32 kB) and
+ 512 kb (64 kB)
+
+ Atmel AT29C512 (64 kilobyte) chips are specifically listed for both
+ adapters, but flashing on the 3c905b cards would only be supported
+ through the Atmel parts. Any device, of the supported size, should
+ be supported when programmed by a dedicated PROM programmer (e.g.
+ not the card).
+
+ To use this driver in such a PROM, visit Atmel's web site and download
+ their .PDF file containing a list of their distributors. Contact the
+ distributors for pricing information. The prices are quite reasonable
+ (about $3 US each for the 64 kB part), and are comparable to what one would
+ expect for similarly sized standard EPROMs. And, the flash chips are much
+ easier to work with, as they don't need to be UV-erased to be reprogrammed.
+ The 3C905B card actually provides a method to program the flash memory
+ while it is resident on board the card itself; if someone would like to
+ write a small DOS program to do the programming, I can provide the
+ information about the registers and so forth.
+
+ A utility program, 3c90xutil, is provided with Etherboot in the 'contrib'
+ directory that allows for the on-board flashing of the ROM while Linux
+ is running. The program has been successfully used under Linux, but I
+ have heard problem reports of its use under FreeBSD. Anyone willing to
+ make it work under FreeBSD is more than welcome to do so!
+
+ You also have the option of using EPROM chips - the 3C905B-TX-NM has been
+ successfully tested with 27C256 (32kB) and 27C512 (64kB) chips with a
+ specified access time of 100ns and faster.
+
+
+III GENERAL USE
+
+ Normally, the basic procedure for using this driver is as follows:
+
+ 1. Run the 3c90xcfg program on the driver diskette to enable the
+ boot PROM and set it to 64k or 128k, as appropriate.
+ 2. Build the appropriate 3c90x.fd0 or 3c90x.fd0 floppy image with
+ possibly the value CFG_3C90X_XCVR defined to the transceiver type that
+ you want to use (i.e., 10/100 rj45, AUI, coax, MII).
+ 3. Run the floppy image on the PC to be network booted, to get
+ it configured, and to verify that it will boot properly.
+ 4. Build the 3c90x.rom or 3c90x.lzrom PROM image and program
+ it into the flash or EPROM memory chip.
+ 5. Put the PROM in the ethernet card, boot and enable 'boot from
+ network first' in the system BIOS, save and reboot.
+
+ Here are some issues to be aware of:
+
+ 1. If you experience crashes or different behaviour when using the
+ boot PROM, add the setting CFG_3C90X_BOOTROM_FIX and go through the
+ steps 2-5 above. This works around a bug in some 3c905B cards (see
+ below), but has some side-effects which may not be desirable.
+ Please note that you have to boot off a floppy (not PROM!) once for
+ this fix to take effect.
+ 2. The possible need to manually set the CFG_3C90X_XCVR value to
+ configure the transceiver type. Values are listed below.
+ 3. The possible need to define CFG_3C90X_PRESERVE_XCVR for use in
+ operating systems that don't intelligently determine the
+ transceiver type.
+
+ Some things that are on the 'To-Do' list, perhaps for me, but perhaps
+ for any other volunteers out there:
+
+ 1. Extend the driver to fully implement the auto-select
+ algorithm if the card has multiple media ports.
+ 2. Fix any bugs in the code <grin>....
+ 3. Extend the driver to support the 3c905c revision cards
+ "officially". Right now, the support has been primarily empirical
+ and not based on 3c905C documentation.
+
+ Now for the details....
+
+ This driver has been tested on roughly 300 systems. The main two
+ configuration issues to contend with are:
+
+ 1. Ensure that PCI Busmastering is enabled for the adapter (configured
+ in the CMOS setup)
+ 2. Some systems don't work properly with the adapter when plug and
+ play OS is enabled; I always set it to "No" or "Disabled" -- this makes
+ it easier and really doesn't adversely affect anything.
+
+ Roughly 95% of the systems worked when configured properly. A few
+ have issues with booting locally once the boot PROM has been installed
+ (this number has been less than 2%). Other configuration issues that
+ to check:
+
+ 1. Newer BIOS's actually work correctly with the network boot order.
+ Set the network adapter first. Most older BIOS's automatically go to
+ the network boot PROM first.
+ 2. For systems where the adapter was already installed and is just
+ having the PROM installed, try setting the "reset configuration data"
+ to yes in the CMOS setup if the BIOS isn't seen at first. If your BIOS
+ doesn't have this option, remove the card, start the system, shut down,
+ install the card and restart (or switch to a different PCI slot).
+ 3. Make sure the CMOS security settings aren't preventing a boot.
+
+ The 3c905B cards have a significant 'bug' that relates to the flash prom:
+ unless the card is set internally to the MII transceiver, it will only
+ read the first 8k of the PROM image. Don't ask why -- it seems really
+ obscure, but it has to do with the way they mux'd the address lines
+ from the PCI bus to the ROM. Unfortunately, most of us are not using
+ MII transceivers, and even the .lzrom image ends up being just a little
+ bit larger than 8k. Note that the workaround for this is disabled by
+ default, because the Windows NT 4.0 driver does not like it (no packets
+ are transmitted).
+
+ So, the solution that I've used is to internally set the card's nvram
+ configuration to use MII when it boots. The 3c905b driver does this
+ automatically. This way, the 16k prom image can be loaded into memory,
+ and then the 3c905b driver can set the temporary configuration of the
+ card to an appropriate value, either configurable by the user or chosen
+ by the driver.
+
+ To enable the 3c905B bugfix, which is necessary for these cards when
+ booting from the Flash ROM, define -DCFG_3C90X_BOOTROM_FIX when building,
+ create a floppy image and boot it once.
+ Thereafter, the card should accept the larger prom image.
+
+ The driver should choose an appropriate transceiver on the card. However,
+ if it doesn't on your card or if you need to, for instance, set your
+ card to 10mbps when connected to an unmanaged 10/100 hub, you can specify
+ which transceiver you want to use. To do this, build the 3c905b.fd0
+ image with -DCFG_3C90X_XCVR=x, where 'x' is one of the following
+ values:
+
+ 0 10Base-T
+ 1 10mbps AUI
+ 3 10Base-2 (thinnet/coax)
+ 4 100Base-TX
+ 5 100Base-FX
+ 6 MII
+ 8 Auto-negotiation 10Base-T / 100Base-TX (usually the default)
+ 9 MII External MAC Mode
+ 255 Allow driver to choose an 'appropriate' media port.
+
+ Then proceed from step 2 in the above 'general use' instructions. The
+ .rom image can be built with CFG_3C90X_XCVR set to a value, but you
+ normally don't want to do this, since it is easier to change the
+ transceiver type by rebuilding a new floppy, changing the BIOS to floppy
+ boot, booting, and then changing the BIOS back to network boot. If
+ CFG_3C90X_XCVR is not set in a particular build, it just uses the
+ current configuration (either its 'best guess' or whatever the stored
+ CFG_3C90X_XCVR value was from the last time it was set).
+
+ [[ Note for the more technically inclined: The CFG_3C90X_XCVR value is
+ programmed into a register in the card's NVRAM that was reserved for
+ LanWorks PROM images to use. When the driver boots, the card comes
+ up in MII mode, and the driver checks the LanWorks register to find
+ out if the user specified a transceiver type. If it finds that
+ information, it uses that, otherwise it picks a transceiver that the
+ card has based on the 3c905b's MediaOptions register. This driver isn't
+ quite smart enough to always determine which media port is actually
+ _connected_; maybe someone else would like to take on that task (it
+ actually involves sending a self-directed packet and seeing if it
+ comes back. IF it does, that port is connected). ]]
+
+ Another issue to keep in mind is that it is possible that some OS'es
+ might not be happy with the way I've handled the PROM-image hack with
+ setting MII mode on bootup. Linux 2.0.35 does not have this problem.
+ Behavior of other systems may vary. The 3com documentation specifically
+ says that, at least with the card that I have, the device driver in the
+ OS should auto-select the media port, so other drivers should work fine
+ with this 'hack'. However, if yours doesn't seem to, you can try defining
+ CFG_3C90X_PRESERVE_XCVR when building to cause Etherboot to keep the
+ working setting (that allowed the bootp/tftp process) across the eth_reset
+ operation.
+
+
+IV FOR DEVELOPERS....
+
+ If you would like to fix/extend/etc. this driver, feel free to do so; just
+ be sure you can test the modified version on the 3c905B-TX cards that the
+ driver was originally designed for. This section of this document gives
+ some information that might be relevant to a programmer.
+
+ A. Main Entry Point
+
+ a3c90x_probe is the main entry point for this driver. It is referred
+ to in an array in 'config.c'.
+
+ B. Other Important Functions
+
+ The functions a3c90x_transmit, a3c90x_poll, a3c90x_reset, and
+ a3c90x_disable are static functions that EtherBoot finds out about
+ as a result of a3c90x_probe setting entries in the nic structure
+ for them. The EtherBoot framework does not use interrupts. It is
+ polled. All transmit and receive operations are initiated by the
+ etherboot framework, not by an interrupt or by the driver.
+
+ C. Internal Functions
+
+ The following functions are internal to the driver:
+
+ a3c90x_internal_IssueCommand - sends a command to the 3c905b card.
+ a3c90x_internal_SetWindow - shifts between one of eight register
+ windows onboard the 3c90x. The bottom 16 bytes of the card's
+ I/O space are multiplexed among 128 bytes, only 16 of which are
+ visible at any one time. This SetWindow function selects one of
+ the eight sets.
+ a3c90x_internal_ReadEeprom - reads a word (16 bits) from the
+ card's onboard nvram. This is NOT the BIOS boot rom. This is
+ where the card stores such things as its hardware address.
+ a3c90x_internal_WriteEeprom - writes a word (16 bits) to the
+ card's nvram, and recomputes the eeprom checksum.
+ a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the
+ card's nvram. Used by the above routine.
+ a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the
+ card's nvram. Used by the above routine.
+
+ D. Globals
+
+ All global variables are inside a global structure named INF_3C90X.
+ So, wherever you see that structure referenced, you know the variable
+ is a global. Just keeps things a little neater.
+
+ E. Enumerations
+
+ There are quite a few enumerated type definitions for registers and
+ so forth, many for registers that I didn't even touch in the driver.
+ Register types start with 'reg', window numbers (for SetWindow)
+ start with 'win', and commands (for IssueCommand) start with 'cmd'.
+ Register offsets also include an indication in the name as to the
+ size of the register (_b = byte, _w = word, _l = long), and which
+ window the register is in, if it is windowed (0-7).
+
+ F. Why the 'a3c90x' name?
+
+ I had to come up with a letter at the beginning of all of the
+ identifiers, since 3com so conveniently had their name start with a
+ number. Another driver used 't' (for 'three'?); I chose 'a' for
+ no reason at all.
+
+Addendum by Jorge L. deLyra <delyra@latt.if.usp.br>, 22Nov2000 re
+working around the 3C905 hardware bug mentioned above:
+
+Use this floppy to fix any 3COM model 3C905B PCI 10/100 Ethernet cards
+that fail to load and run the boot program the first time around. If
+they have a "Lucent" rather than a "Broadcom" chipset these cards have
+a configuration bug that causes a hang when trying to load the boot
+program from the PROM, if you try to use them right out of the box.
+
+The boot program in this floppy is the file named 3c905b-tpo100.rom
+from Etherboot version 4.6.10, compiled with the bugfix parameter
+
+ CFG_3C90X_BOOTROM_FIX
+
+You have to take the chip off the card and boot the system once using
+this floppy. Once loaded from the floppy, the boot program will access
+the card and change some setting in it, correcting the problem. After
+that you may use either this boot program or the normal one, compiled
+without this bugfix parameter, to boot the machine from the PROM chip.
+
+[Any recent Etherboot version should do, not just 4.6.10 - Ed.]
diff --git a/gpxe/src/drivers/net/amd8111e.c b/gpxe/src/drivers/net/amd8111e.c
new file mode 100644
index 00000000..1c41add1
--- /dev/null
+++ b/gpxe/src/drivers/net/amd8111e.c
@@ -0,0 +1,691 @@
+/* Advanced Micro Devices Inc. AMD8111E Linux Network Driver
+ * Copyright (C) 2004 Advanced Micro Devices
+ * Copyright (C) 2005 Liu Tao <liutao1980@gmail.com> [etherboot port]
+ *
+ * Copyright 2001,2002 Jeff Garzik <jgarzik@mandrakesoft.com> [ 8139cp.c,tg3.c ]
+ * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)[ tg3.c]
+ * Copyright 1996-1999 Thomas Bogendoerfer [ pcnet32.c ]
+ * Derived from the lance driver written 1993,1994,1995 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.[ pcnet32.c ]
+ * Carsten Langgaard, carstenl@mips.com [ pcnet32.c ]
+ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "mii.h"
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+#include "string.h"
+#include "stdint.h"
+#include "amd8111e.h"
+
+
+/* driver definitions */
+#define NUM_TX_SLOTS 2
+#define NUM_RX_SLOTS 4
+#define TX_SLOTS_MASK 1
+#define RX_SLOTS_MASK 3
+
+#define TX_BUF_LEN 1536
+#define RX_BUF_LEN 1536
+
+#define TX_PKT_LEN_MAX (ETH_FRAME_LEN - ETH_HLEN)
+#define RX_PKT_LEN_MIN 60
+#define RX_PKT_LEN_MAX ETH_FRAME_LEN
+
+#define TX_TIMEOUT 3000
+#define TX_PROCESS_TIME 10
+#define TX_RETRY (TX_TIMEOUT / TX_PROCESS_TIME)
+
+#define PHY_RW_RETRY 10
+
+
+struct amd8111e_tx_desc {
+ u16 buf_len;
+ u16 tx_flags;
+ u16 tag_ctrl_info;
+ u16 tag_ctrl_cmd;
+ u32 buf_phy_addr;
+ u32 reserved;
+};
+
+struct amd8111e_rx_desc {
+ u32 reserved;
+ u16 msg_len;
+ u16 tag_ctrl_info;
+ u16 buf_len;
+ u16 rx_flags;
+ u32 buf_phy_addr;
+};
+
+struct eth_frame {
+ u8 dst_addr[ETH_ALEN];
+ u8 src_addr[ETH_ALEN];
+ u16 type;
+ u8 data[ETH_FRAME_LEN - ETH_HLEN];
+} __attribute__((packed));
+
+struct amd8111e_priv {
+ struct amd8111e_tx_desc tx_ring[NUM_TX_SLOTS];
+ struct amd8111e_rx_desc rx_ring[NUM_RX_SLOTS];
+ unsigned char tx_buf[NUM_TX_SLOTS][TX_BUF_LEN];
+ unsigned char rx_buf[NUM_RX_SLOTS][RX_BUF_LEN];
+ unsigned long tx_idx, rx_idx;
+ int tx_consistent;
+
+ char opened;
+ char link;
+ char speed;
+ char duplex;
+ int ext_phy_addr;
+ u32 ext_phy_id;
+
+ struct pci_device *pdev;
+ struct nic *nic;
+ void *mmio;
+};
+
+static struct amd8111e_priv amd8111e;
+
+
+/********************************************************
+ * locale functions *
+ ********************************************************/
+static void amd8111e_init_hw_default(struct amd8111e_priv *lp);
+static int amd8111e_start(struct amd8111e_priv *lp);
+static int amd8111e_read_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 *val);
+#if 0
+static int amd8111e_write_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 val);
+#endif
+static void amd8111e_probe_ext_phy(struct amd8111e_priv *lp);
+static void amd8111e_disable_interrupt(struct amd8111e_priv *lp);
+static void amd8111e_enable_interrupt(struct amd8111e_priv *lp);
+static void amd8111e_force_interrupt(struct amd8111e_priv *lp);
+static int amd8111e_get_mac_address(struct amd8111e_priv *lp);
+static int amd8111e_init_rx_ring(struct amd8111e_priv *lp);
+static int amd8111e_init_tx_ring(struct amd8111e_priv *lp);
+static int amd8111e_wait_tx_ring(struct amd8111e_priv *lp, unsigned int index);
+static void amd8111e_wait_link(struct amd8111e_priv *lp);
+static void amd8111e_poll_link(struct amd8111e_priv *lp);
+static void amd8111e_restart(struct amd8111e_priv *lp);
+
+
+/*
+ * This function clears necessary the device registers.
+ */
+static void amd8111e_init_hw_default(struct amd8111e_priv *lp)
+{
+ unsigned int reg_val;
+ void *mmio = lp->mmio;
+
+ /* stop the chip */
+ writel(RUN, mmio + CMD0);
+
+ /* Clear RCV_RING_BASE_ADDR */
+ writel(0, mmio + RCV_RING_BASE_ADDR0);
+
+ /* Clear XMT_RING_BASE_ADDR */
+ writel(0, mmio + XMT_RING_BASE_ADDR0);
+ writel(0, mmio + XMT_RING_BASE_ADDR1);
+ writel(0, mmio + XMT_RING_BASE_ADDR2);
+ writel(0, mmio + XMT_RING_BASE_ADDR3);
+
+ /* Clear CMD0 */
+ writel(CMD0_CLEAR, mmio + CMD0);
+
+ /* Clear CMD2 */
+ writel(CMD2_CLEAR, mmio + CMD2);
+
+ /* Clear CMD7 */
+ writel(CMD7_CLEAR, mmio + CMD7);
+
+ /* Clear DLY_INT_A and DLY_INT_B */
+ writel(0x0, mmio + DLY_INT_A);
+ writel(0x0, mmio + DLY_INT_B);
+
+ /* Clear FLOW_CONTROL */
+ writel(0x0, mmio + FLOW_CONTROL);
+
+ /* Clear INT0 write 1 to clear register */
+ reg_val = readl(mmio + INT0);
+ writel(reg_val, mmio + INT0);
+
+ /* Clear STVAL */
+ writel(0x0, mmio + STVAL);
+
+ /* Clear INTEN0 */
+ writel(INTEN0_CLEAR, mmio + INTEN0);
+
+ /* Clear LADRF */
+ writel(0x0, mmio + LADRF);
+
+ /* Set SRAM_SIZE & SRAM_BOUNDARY registers */
+ writel(0x80010, mmio + SRAM_SIZE);
+
+ /* Clear RCV_RING0_LEN */
+ writel(0x0, mmio + RCV_RING_LEN0);
+
+ /* Clear XMT_RING0/1/2/3_LEN */
+ writel(0x0, mmio + XMT_RING_LEN0);
+ writel(0x0, mmio + XMT_RING_LEN1);
+ writel(0x0, mmio + XMT_RING_LEN2);
+ writel(0x0, mmio + XMT_RING_LEN3);
+
+ /* Clear XMT_RING_LIMIT */
+ writel(0x0, mmio + XMT_RING_LIMIT);
+
+ /* Clear MIB */
+ writew(MIB_CLEAR, mmio + MIB_ADDR);
+
+ /* Clear LARF */
+ writel( 0, mmio + LADRF);
+ writel( 0, mmio + LADRF + 4);
+
+ /* SRAM_SIZE register */
+ reg_val = readl(mmio + SRAM_SIZE);
+
+ /* Set default value to CTRL1 Register */
+ writel(CTRL1_DEFAULT, mmio + CTRL1);
+
+ /* To avoid PCI posting bug */
+ readl(mmio + CMD2);
+}
+
+/*
+ * This function initializes the device registers and starts the device.
+ */
+static int amd8111e_start(struct amd8111e_priv *lp)
+{
+ struct nic *nic = lp->nic;
+ void *mmio = lp->mmio;
+ int i, reg_val;
+
+ /* stop the chip */
+ writel(RUN, mmio + CMD0);
+
+ /* AUTOPOLL0 Register *//*TBD default value is 8100 in FPS */
+ writew(0x8100 | lp->ext_phy_addr, mmio + AUTOPOLL0);
+
+ /* enable the port manager and set auto negotiation always */
+ writel(VAL1 | EN_PMGR, mmio + CMD3 );
+ writel(XPHYANE | XPHYRST, mmio + CTRL2);
+
+ /* set control registers */
+ reg_val = readl(mmio + CTRL1);
+ reg_val &= ~XMTSP_MASK;
+ writel(reg_val | XMTSP_128 | CACHE_ALIGN, mmio + CTRL1);
+
+ /* initialize tx and rx ring base addresses */
+ amd8111e_init_tx_ring(lp);
+ amd8111e_init_rx_ring(lp);
+ writel(virt_to_bus(lp->tx_ring), mmio + XMT_RING_BASE_ADDR0);
+ writel(virt_to_bus(lp->rx_ring), mmio + RCV_RING_BASE_ADDR0);
+ writew(NUM_TX_SLOTS, mmio + XMT_RING_LEN0);
+ writew(NUM_RX_SLOTS, mmio + RCV_RING_LEN0);
+
+ /* set default IPG to 96 */
+ writew(DEFAULT_IPG, mmio + IPG);
+ writew(DEFAULT_IPG - IFS1_DELTA, mmio + IFS1);
+
+ /* AutoPAD transmit, Retransmit on Underflow */
+ writel(VAL0 | APAD_XMT | REX_RTRY | REX_UFLO, mmio + CMD2);
+
+ /* JUMBO disabled */
+ writel(JUMBO, mmio + CMD3);
+
+ /* Setting the MAC address to the device */
+ for(i = 0; i < ETH_ALEN; i++)
+ writeb(nic->node_addr[i], mmio + PADR + i);
+
+ /* set RUN bit to start the chip, interrupt not enabled */
+ writel(VAL2 | RDMD0 | VAL0 | RUN, mmio + CMD0);
+
+ /* To avoid PCI posting bug */
+ readl(mmio + CMD0);
+ return 0;
+}
+
+/*
+This function will read the PHY registers.
+*/
+static int amd8111e_read_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 *val)
+{
+ void *mmio = lp->mmio;
+ unsigned int reg_val;
+ unsigned int retry = PHY_RW_RETRY;
+
+ reg_val = readl(mmio + PHY_ACCESS);
+ while (reg_val & PHY_CMD_ACTIVE)
+ reg_val = readl(mmio + PHY_ACCESS);
+
+ writel(PHY_RD_CMD | ((phy_addr & 0x1f) << 21) | ((reg & 0x1f) << 16),
+ mmio + PHY_ACCESS);
+ do {
+ reg_val = readl(mmio + PHY_ACCESS);
+ udelay(30); /* It takes 30 us to read/write data */
+ } while (--retry && (reg_val & PHY_CMD_ACTIVE));
+
+ if (reg_val & PHY_RD_ERR) {
+ *val = 0;
+ return -1;
+ }
+
+ *val = reg_val & 0xffff;
+ return 0;
+}
+
+/*
+This function will write into PHY registers.
+*/
+#if 0
+static int amd8111e_write_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 val)
+{
+ void *mmio = lp->mmio;
+ unsigned int reg_val;
+ unsigned int retry = PHY_RW_RETRY;
+
+ reg_val = readl(mmio + PHY_ACCESS);
+ while (reg_val & PHY_CMD_ACTIVE)
+ reg_val = readl(mmio + PHY_ACCESS);
+
+ writel(PHY_WR_CMD | ((phy_addr & 0x1f) << 21) | ((reg & 0x1f) << 16) | val,
+ mmio + PHY_ACCESS);
+ do {
+ reg_val = readl(mmio + PHY_ACCESS);
+ udelay(30); /* It takes 30 us to read/write the data */
+ } while (--retry && (reg_val & PHY_CMD_ACTIVE));
+
+ if(reg_val & PHY_RD_ERR)
+ return -1;
+
+ return 0;
+}
+#endif
+
+static void amd8111e_probe_ext_phy(struct amd8111e_priv *lp)
+{
+ int i;
+
+ lp->ext_phy_id = 0;
+ lp->ext_phy_addr = 1;
+
+ for (i = 0x1e; i >= 0; i--) {
+ u32 id1, id2;
+
+ if (amd8111e_read_phy(lp, i, MII_PHYSID1, &id1))
+ continue;
+ if (amd8111e_read_phy(lp, i, MII_PHYSID2, &id2))
+ continue;
+ lp->ext_phy_id = (id1 << 16) | id2;
+ lp->ext_phy_addr = i;
+ break;
+ }
+
+ if (lp->ext_phy_id)
+ printf("Found MII PHY ID 0x%08x at address 0x%02x\n",
+ (unsigned int) lp->ext_phy_id, lp->ext_phy_addr);
+ else
+ printf("Couldn't detect MII PHY, assuming address 0x01\n");
+}
+
+static void amd8111e_disable_interrupt(struct amd8111e_priv *lp)
+{
+ void *mmio = lp->mmio;
+ unsigned int int0;
+
+ writel(INTREN, mmio + CMD0);
+ writel(INTEN0_CLEAR, mmio + INTEN0);
+ int0 = readl(mmio + INT0);
+ writel(int0, mmio + INT0);
+ readl(mmio + INT0);
+}
+
+static void amd8111e_enable_interrupt(struct amd8111e_priv *lp)
+{
+ void *mmio = lp->mmio;
+
+ writel(VAL3 | LCINTEN | VAL1 | TINTEN0 | VAL0 | RINTEN0, mmio + INTEN0);
+ writel(VAL0 | INTREN, mmio + CMD0);
+ readl(mmio + CMD0);
+}
+
+static void amd8111e_force_interrupt(struct amd8111e_priv *lp)
+{
+ void *mmio = lp->mmio;
+
+ writel(VAL0 | UINTCMD, mmio + CMD0);
+ readl(mmio + CMD0);
+}
+
+static int amd8111e_get_mac_address(struct amd8111e_priv *lp)
+{
+ struct nic *nic = lp->nic;
+ void *mmio = lp->mmio;
+ int i;
+
+ /* BIOS should have set mac address to PADR register,
+ * so we read PADR to get it.
+ */
+ for (i = 0; i < ETH_ALEN; i++)
+ nic->node_addr[i] = readb(mmio + PADR + i);
+
+ DBG ( "Ethernet addr: %s\n", eth_ntoa ( nic->node_addr ) );
+
+ return 0;
+}
+
+static int amd8111e_init_rx_ring(struct amd8111e_priv *lp)
+{
+ int i;
+
+ lp->rx_idx = 0;
+
+ /* Initilaizing receive descriptors */
+ for (i = 0; i < NUM_RX_SLOTS; i++) {
+ lp->rx_ring[i].buf_phy_addr = cpu_to_le32(virt_to_bus(lp->rx_buf[i]));
+ lp->rx_ring[i].buf_len = cpu_to_le16(RX_BUF_LEN);
+ wmb();
+ lp->rx_ring[i].rx_flags = cpu_to_le16(OWN_BIT);
+ }
+
+ return 0;
+}
+
+static int amd8111e_init_tx_ring(struct amd8111e_priv *lp)
+{
+ int i;
+
+ lp->tx_idx = 0;
+ lp->tx_consistent = 1;
+
+ /* Initializing transmit descriptors */
+ for (i = 0; i < NUM_TX_SLOTS; i++) {
+ lp->tx_ring[i].tx_flags = 0;
+ lp->tx_ring[i].buf_phy_addr = 0;
+ lp->tx_ring[i].buf_len = 0;
+ }
+
+ return 0;
+}
+
+static int amd8111e_wait_tx_ring(struct amd8111e_priv *lp, unsigned int index)
+{
+ volatile u16 status;
+ int retry = TX_RETRY;
+
+ status = le16_to_cpu(lp->tx_ring[index].tx_flags);
+ while (--retry && (status & OWN_BIT)) {
+ mdelay(TX_PROCESS_TIME);
+ status = le16_to_cpu(lp->tx_ring[index].tx_flags);
+ }
+ if (status & OWN_BIT) {
+ printf("Error: tx slot %d timeout, stat = 0x%x\n", index, status);
+ amd8111e_restart(lp);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void amd8111e_wait_link(struct amd8111e_priv *lp)
+{
+ unsigned int status;
+ u32 reg_val;
+
+ do {
+ /* read phy to update STAT0 register */
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMCR, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMSR, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_ADVERTISE, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_LPA, &reg_val);
+ status = readl(lp->mmio + STAT0);
+ } while (!(status & AUTONEG_COMPLETE) || !(status & LINK_STATS));
+}
+
+static void amd8111e_poll_link(struct amd8111e_priv *lp)
+{
+ unsigned int status, speed;
+ u32 reg_val;
+
+ if (!lp->link) {
+ /* read phy to update STAT0 register */
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMCR, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMSR, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_ADVERTISE, &reg_val);
+ amd8111e_read_phy(lp, lp->ext_phy_addr, MII_LPA, &reg_val);
+ status = readl(lp->mmio + STAT0);
+
+ if (status & LINK_STATS) {
+ lp->link = 1;
+ speed = (status & SPEED_MASK) >> 7;
+ if (speed == PHY_SPEED_100)
+ lp->speed = 1;
+ else
+ lp->speed = 0;
+ if (status & FULL_DPLX)
+ lp->duplex = 1;
+ else
+ lp->duplex = 0;
+
+ printf("Link is up: %s Mbps %s duplex\n",
+ lp->speed ? "100" : "10", lp->duplex ? "full" : "half");
+ }
+ } else {
+ status = readl(lp->mmio + STAT0);
+ if (!(status & LINK_STATS)) {
+ lp->link = 0;
+ printf("Link is down\n");
+ }
+ }
+}
+
+static void amd8111e_restart(struct amd8111e_priv *lp)
+{
+ printf("\nStarting nic...\n");
+ amd8111e_disable_interrupt(lp);
+ amd8111e_init_hw_default(lp);
+ amd8111e_probe_ext_phy(lp);
+ amd8111e_get_mac_address(lp);
+ amd8111e_start(lp);
+
+ printf("Waiting link up...\n");
+ lp->link = 0;
+ amd8111e_wait_link(lp);
+ amd8111e_poll_link(lp);
+}
+
+
+/********************************************************
+ * Interface Functions *
+ ********************************************************/
+
+static void amd8111e_transmit(struct nic *nic, const char *dst_addr,
+ unsigned int type, unsigned int size, const char *packet)
+{
+ struct amd8111e_priv *lp = nic->priv_data;
+ struct eth_frame *frame;
+ unsigned int index;
+
+ /* check packet size */
+ if (size > TX_PKT_LEN_MAX) {
+ printf("amd8111e_transmit(): too large packet, drop\n");
+ return;
+ }
+
+ /* get tx slot */
+ index = lp->tx_idx;
+ if (amd8111e_wait_tx_ring(lp, index))
+ return;
+
+ /* fill frame */
+ frame = (struct eth_frame *)lp->tx_buf[index];
+ memset(frame->data, 0, TX_PKT_LEN_MAX);
+ memcpy(frame->dst_addr, dst_addr, ETH_ALEN);
+ memcpy(frame->src_addr, nic->node_addr, ETH_ALEN);
+ frame->type = htons(type);
+ memcpy(frame->data, packet, size);
+
+ /* start xmit */
+ lp->tx_ring[index].buf_len = cpu_to_le16(ETH_HLEN + size);
+ lp->tx_ring[index].buf_phy_addr = cpu_to_le32(virt_to_bus(frame));
+ wmb();
+ lp->tx_ring[index].tx_flags =
+ cpu_to_le16(OWN_BIT | STP_BIT | ENP_BIT | ADD_FCS_BIT | LTINT_BIT);
+ writel(VAL1 | TDMD0, lp->mmio + CMD0);
+ readl(lp->mmio + CMD0);
+
+ /* update slot pointer */
+ lp->tx_idx = (lp->tx_idx + 1) & TX_SLOTS_MASK;
+}
+
+static int amd8111e_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+
+ struct amd8111e_priv *lp = nic->priv_data;
+ u16 status, pkt_len;
+ unsigned int index, pkt_ok;
+
+ amd8111e_poll_link(lp);
+
+ index = lp->rx_idx;
+ status = le16_to_cpu(lp->rx_ring[index].rx_flags);
+ pkt_len = le16_to_cpu(lp->rx_ring[index].msg_len) - 4; /* remove 4bytes FCS */
+
+ if (status & OWN_BIT)
+ return 0;
+
+ if (status & ERR_BIT)
+ pkt_ok = 0;
+ else if (!(status & STP_BIT))
+ pkt_ok = 0;
+ else if (!(status & ENP_BIT))
+ pkt_ok = 0;
+ else if (pkt_len < RX_PKT_LEN_MIN)
+ pkt_ok = 0;
+ else if (pkt_len > RX_PKT_LEN_MAX)
+ pkt_ok = 0;
+ else
+ pkt_ok = 1;
+
+ if (pkt_ok) {
+ if (!retrieve)
+ return 1;
+ nic->packetlen = pkt_len;
+ memcpy(nic->packet, lp->rx_buf[index], nic->packetlen);
+ }
+
+ lp->rx_ring[index].buf_phy_addr = cpu_to_le32(virt_to_bus(lp->rx_buf[index]));
+ lp->rx_ring[index].buf_len = cpu_to_le16(RX_BUF_LEN);
+ wmb();
+ lp->rx_ring[index].rx_flags = cpu_to_le16(OWN_BIT);
+ writel(VAL2 | RDMD0, lp->mmio + CMD0);
+ readl(lp->mmio + CMD0);
+
+ lp->rx_idx = (lp->rx_idx + 1) & RX_SLOTS_MASK;
+ return pkt_ok;
+}
+
+static void amd8111e_disable(struct nic *nic)
+{
+ struct amd8111e_priv *lp = nic->priv_data;
+
+ /* disable interrupt */
+ amd8111e_disable_interrupt(lp);
+
+ /* stop chip */
+ amd8111e_init_hw_default(lp);
+
+ /* unmap mmio */
+ iounmap(lp->mmio);
+
+ /* update status */
+ lp->opened = 0;
+}
+
+static void amd8111e_irq(struct nic *nic, irq_action_t action)
+{
+ struct amd8111e_priv *lp = nic->priv_data;
+
+ switch (action) {
+ case DISABLE:
+ amd8111e_disable_interrupt(lp);
+ break;
+ case ENABLE:
+ amd8111e_enable_interrupt(lp);
+ break;
+ case FORCE:
+ amd8111e_force_interrupt(lp);
+ break;
+ }
+}
+
+static struct nic_operations amd8111e_operations = {
+ .connect = dummy_connect,
+ .poll = amd8111e_poll,
+ .transmit = amd8111e_transmit,
+ .irq = amd8111e_irq,
+};
+
+static int amd8111e_probe(struct nic *nic, struct pci_device *pdev)
+{
+ struct amd8111e_priv *lp = &amd8111e;
+ unsigned long mmio_start, mmio_len;
+
+ nic->ioaddr = pdev->ioaddr;
+ nic->irqno = pdev->irq;
+
+ mmio_start = pci_bar_start(pdev, PCI_BASE_ADDRESS_0);
+ mmio_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_0);
+
+ memset(lp, 0, sizeof(*lp));
+ lp->pdev = pdev;
+ lp->nic = nic;
+ lp->mmio = ioremap(mmio_start, mmio_len);
+ lp->opened = 1;
+ adjust_pci_device(pdev);
+
+ nic->priv_data = lp;
+
+ amd8111e_restart(lp);
+
+ nic->nic_op = &amd8111e_operations;
+ return 1;
+}
+
+static struct pci_device_id amd8111e_nics[] = {
+ PCI_ROM(0x1022, 0x7462, "amd8111e", "AMD8111E"),
+};
+
+PCI_DRIVER ( amd8111e_driver, amd8111e_nics, PCI_NO_CLASS );
+
+DRIVER ( "AMD8111E", nic_driver, pci_driver, amd8111e_driver,
+ amd8111e_probe, amd8111e_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/amd8111e.h b/gpxe/src/drivers/net/amd8111e.h
new file mode 100644
index 00000000..82b8f7a3
--- /dev/null
+++ b/gpxe/src/drivers/net/amd8111e.h
@@ -0,0 +1,629 @@
+/*
+ * Advanced Micro Devices Inc. AMD8111E Linux Network Driver
+ * Copyright (C) 2003 Advanced Micro Devices
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+
+Module Name:
+
+ amd8111e.h
+
+Abstract:
+
+ AMD8111 based 10/100 Ethernet Controller driver definitions.
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+ 3.0.0
+ Initial Revision.
+ 3.0.1
+*/
+
+#ifndef _AMD811E_H
+#define _AMD811E_H
+
+/* Command style register access
+
+Registers CMD0, CMD2, CMD3,CMD7 and INTEN0 uses a write access technique called command style access. It allows the write to selected bits of this register without altering the bits that are not selected. Command style registers are divided into 4 bytes that can be written independently. Higher order bit of each byte is the value bit that specifies the value that will be written into the selected bits of register.
+
+eg., if the value 10011010b is written into the least significant byte of a command style register, bits 1,3 and 4 of the register will be set to 1, and the other bits will not be altered. If the value 00011010b is written into the same byte, bits 1,3 and 4 will be cleared to 0 and the other bits will not be altered.
+
+*/
+
+/* Offset for Memory Mapped Registers. */
+/* 32 bit registers */
+
+#define ASF_STAT 0x00 /* ASF status register */
+#define CHIPID 0x04 /* Chip ID regsiter */
+#define MIB_DATA 0x10 /* MIB data register */
+#define MIB_ADDR 0x14 /* MIB address register */
+#define STAT0 0x30 /* Status0 register */
+#define INT0 0x38 /* Interrupt0 register */
+#define INTEN0 0x40 /* Interrupt0 enable register*/
+#define CMD0 0x48 /* Command0 register */
+#define CMD2 0x50 /* Command2 register */
+#define CMD3 0x54 /* Command3 resiter */
+#define CMD7 0x64 /* Command7 register */
+
+#define CTRL1 0x6C /* Control1 register */
+#define CTRL2 0x70 /* Control2 register */
+
+#define XMT_RING_LIMIT 0x7C /* Transmit ring limit register */
+
+#define AUTOPOLL0 0x88 /* Auto-poll0 register */
+#define AUTOPOLL1 0x8A /* Auto-poll1 register */
+#define AUTOPOLL2 0x8C /* Auto-poll2 register */
+#define AUTOPOLL3 0x8E /* Auto-poll3 register */
+#define AUTOPOLL4 0x90 /* Auto-poll4 register */
+#define AUTOPOLL5 0x92 /* Auto-poll5 register */
+
+#define AP_VALUE 0x98 /* Auto-poll value register */
+#define DLY_INT_A 0xA8 /* Group A delayed interrupt register */
+#define DLY_INT_B 0xAC /* Group B delayed interrupt register */
+
+#define FLOW_CONTROL 0xC8 /* Flow control register */
+#define PHY_ACCESS 0xD0 /* PHY access register */
+
+#define STVAL 0xD8 /* Software timer value register */
+
+#define XMT_RING_BASE_ADDR0 0x100 /* Transmit ring0 base addr register */
+#define XMT_RING_BASE_ADDR1 0x108 /* Transmit ring1 base addr register */
+#define XMT_RING_BASE_ADDR2 0x110 /* Transmit ring2 base addr register */
+#define XMT_RING_BASE_ADDR3 0x118 /* Transmit ring2 base addr register */
+
+#define RCV_RING_BASE_ADDR0 0x120 /* Transmit ring0 base addr register */
+
+#define PMAT0 0x190 /* OnNow pattern register0 */
+#define PMAT1 0x194 /* OnNow pattern register1 */
+
+/* 16bit registers */
+
+#define XMT_RING_LEN0 0x140 /* Transmit Ring0 length register */
+#define XMT_RING_LEN1 0x144 /* Transmit Ring1 length register */
+#define XMT_RING_LEN2 0x148 /* Transmit Ring2 length register */
+#define XMT_RING_LEN3 0x14C /* Transmit Ring3 length register */
+
+#define RCV_RING_LEN0 0x150 /* Receive Ring0 length register */
+
+#define SRAM_SIZE 0x178 /* SRAM size register */
+#define SRAM_BOUNDARY 0x17A /* SRAM boundary register */
+
+/* 48bit register */
+
+#define PADR 0x160 /* Physical address register */
+
+#define IFS1 0x18C /* Inter-frame spacing Part1 register */
+#define IFS 0x18D /* Inter-frame spacing register */
+#define IPG 0x18E /* Inter-frame gap register */
+/* 64bit register */
+
+#define LADRF 0x168 /* Logical address filter register */
+
+
+/* Register Bit Definitions */
+typedef enum {
+
+ ASF_INIT_DONE = (1 << 1),
+ ASF_INIT_PRESENT = (1 << 0),
+
+}STAT_ASF_BITS;
+
+typedef enum {
+
+ MIB_CMD_ACTIVE = (1 << 15 ),
+ MIB_RD_CMD = (1 << 13 ),
+ MIB_CLEAR = (1 << 12 ),
+ MIB_ADDRESS = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3)|
+ (1 << 4) | (1 << 5),
+}MIB_ADDR_BITS;
+
+
+typedef enum {
+
+ PMAT_DET = (1 << 12),
+ MP_DET = (1 << 11),
+ LC_DET = (1 << 10),
+ SPEED_MASK = (1 << 9)|(1 << 8)|(1 << 7),
+ FULL_DPLX = (1 << 6),
+ LINK_STATS = (1 << 5),
+ AUTONEG_COMPLETE = (1 << 4),
+ MIIPD = (1 << 3),
+ RX_SUSPENDED = (1 << 2),
+ TX_SUSPENDED = (1 << 1),
+ RUNNING = (1 << 0),
+
+}STAT0_BITS;
+
+#define PHY_SPEED_10 0x2
+#define PHY_SPEED_100 0x3
+
+/* INT0 0x38, 32bit register */
+typedef enum {
+
+ INTR = (1 << 31),
+ PCSINT = (1 << 28),
+ LCINT = (1 << 27),
+ APINT5 = (1 << 26),
+ APINT4 = (1 << 25),
+ APINT3 = (1 << 24),
+ TINT_SUM = (1 << 23),
+ APINT2 = (1 << 22),
+ APINT1 = (1 << 21),
+ APINT0 = (1 << 20),
+ MIIPDTINT = (1 << 19),
+ MCCINT = (1 << 17),
+ MREINT = (1 << 16),
+ RINT_SUM = (1 << 15),
+ SPNDINT = (1 << 14),
+ MPINT = (1 << 13),
+ SINT = (1 << 12),
+ TINT3 = (1 << 11),
+ TINT2 = (1 << 10),
+ TINT1 = (1 << 9),
+ TINT0 = (1 << 8),
+ UINT = (1 << 7),
+ STINT = (1 << 4),
+ RINT0 = (1 << 0),
+
+}INT0_BITS;
+
+typedef enum {
+
+ VAL3 = (1 << 31), /* VAL bit for byte 3 */
+ VAL2 = (1 << 23), /* VAL bit for byte 2 */
+ VAL1 = (1 << 15), /* VAL bit for byte 1 */
+ VAL0 = (1 << 7), /* VAL bit for byte 0 */
+
+}VAL_BITS;
+
+typedef enum {
+
+ /* VAL3 */
+ LCINTEN = (1 << 27),
+ APINT5EN = (1 << 26),
+ APINT4EN = (1 << 25),
+ APINT3EN = (1 << 24),
+ /* VAL2 */
+ APINT2EN = (1 << 22),
+ APINT1EN = (1 << 21),
+ APINT0EN = (1 << 20),
+ MIIPDTINTEN = (1 << 19),
+ MCCIINTEN = (1 << 18),
+ MCCINTEN = (1 << 17),
+ MREINTEN = (1 << 16),
+ /* VAL1 */
+ SPNDINTEN = (1 << 14),
+ MPINTEN = (1 << 13),
+ TINTEN3 = (1 << 11),
+ SINTEN = (1 << 12),
+ TINTEN2 = (1 << 10),
+ TINTEN1 = (1 << 9),
+ TINTEN0 = (1 << 8),
+ /* VAL0 */
+ STINTEN = (1 << 4),
+ RINTEN0 = (1 << 0),
+
+ INTEN0_CLEAR = 0x1F7F7F1F, /* Command style register */
+
+}INTEN0_BITS;
+
+typedef enum {
+ /* VAL2 */
+ RDMD0 = (1 << 16),
+ /* VAL1 */
+ TDMD3 = (1 << 11),
+ TDMD2 = (1 << 10),
+ TDMD1 = (1 << 9),
+ TDMD0 = (1 << 8),
+ /* VAL0 */
+ UINTCMD = (1 << 6),
+ RX_FAST_SPND = (1 << 5),
+ TX_FAST_SPND = (1 << 4),
+ RX_SPND = (1 << 3),
+ TX_SPND = (1 << 2),
+ INTREN = (1 << 1),
+ RUN = (1 << 0),
+
+ CMD0_CLEAR = 0x000F0F7F, /* Command style register */
+
+}CMD0_BITS;
+
+typedef enum {
+
+ /* VAL3 */
+ CONDUIT_MODE = (1 << 29),
+ /* VAL2 */
+ RPA = (1 << 19),
+ DRCVPA = (1 << 18),
+ DRCVBC = (1 << 17),
+ PROM = (1 << 16),
+ /* VAL1 */
+ ASTRP_RCV = (1 << 13),
+ RCV_DROP0 = (1 << 12),
+ EMBA = (1 << 11),
+ DXMT2PD = (1 << 10),
+ LTINTEN = (1 << 9),
+ DXMTFCS = (1 << 8),
+ /* VAL0 */
+ APAD_XMT = (1 << 6),
+ DRTY = (1 << 5),
+ INLOOP = (1 << 4),
+ EXLOOP = (1 << 3),
+ REX_RTRY = (1 << 2),
+ REX_UFLO = (1 << 1),
+ REX_LCOL = (1 << 0),
+
+ CMD2_CLEAR = 0x3F7F3F7F, /* Command style register */
+
+}CMD2_BITS;
+
+typedef enum {
+
+ /* VAL3 */
+ ASF_INIT_DONE_ALIAS = (1 << 29),
+ /* VAL2 */
+ JUMBO = (1 << 21),
+ VSIZE = (1 << 20),
+ VLONLY = (1 << 19),
+ VL_TAG_DEL = (1 << 18),
+ /* VAL1 */
+ EN_PMGR = (1 << 14),
+ INTLEVEL = (1 << 13),
+ FORCE_FULL_DUPLEX = (1 << 12),
+ FORCE_LINK_STATUS = (1 << 11),
+ APEP = (1 << 10),
+ MPPLBA = (1 << 9),
+ /* VAL0 */
+ RESET_PHY_PULSE = (1 << 2),
+ RESET_PHY = (1 << 1),
+ PHY_RST_POL = (1 << 0),
+
+}CMD3_BITS;
+
+
+typedef enum {
+
+ /* VAL0 */
+ PMAT_SAVE_MATCH = (1 << 4),
+ PMAT_MODE = (1 << 3),
+ MPEN_SW = (1 << 1),
+ LCMODE_SW = (1 << 0),
+
+ CMD7_CLEAR = 0x0000001B /* Command style register */
+
+}CMD7_BITS;
+
+
+typedef enum {
+
+ RESET_PHY_WIDTH = (0xF << 16) | (0xF<< 20), /* 0x00FF0000 */
+ XMTSP_MASK = (1 << 9) | (1 << 8), /* 9:8 */
+ XMTSP_128 = (1 << 9), /* 9 */
+ XMTSP_64 = (1 << 8),
+ CACHE_ALIGN = (1 << 4),
+ BURST_LIMIT_MASK = (0xF << 0 ),
+ CTRL1_DEFAULT = 0x00010111,
+
+}CTRL1_BITS;
+
+typedef enum {
+
+ FMDC_MASK = (1 << 9)|(1 << 8), /* 9:8 */
+ XPHYRST = (1 << 7),
+ XPHYANE = (1 << 6),
+ XPHYFD = (1 << 5),
+ XPHYSP = (1 << 4) | (1 << 3), /* 4:3 */
+ APDW_MASK = (1 << 2) | (1 << 1) | (1 << 0), /* 2:0 */
+
+}CTRL2_BITS;
+
+/* XMT_RING_LIMIT 0x7C, 32bit register */
+typedef enum {
+
+ XMT_RING2_LIMIT = (0xFF << 16), /* 23:16 */
+ XMT_RING1_LIMIT = (0xFF << 8), /* 15:8 */
+ XMT_RING0_LIMIT = (0xFF << 0), /* 7:0 */
+
+}XMT_RING_LIMIT_BITS;
+
+typedef enum {
+
+ AP_REG0_EN = (1 << 15),
+ AP_REG0_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PHY0_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL0_BITS;
+
+/* AUTOPOLL1 0x8A, 16bit register */
+typedef enum {
+
+ AP_REG1_EN = (1 << 15),
+ AP_REG1_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP1 = (1 << 6),
+ AP_PHY1_DFLT = (1 << 5),
+ AP_PHY1_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL1_BITS;
+
+
+typedef enum {
+
+ AP_REG2_EN = (1 << 15),
+ AP_REG2_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP2 = (1 << 6),
+ AP_PHY2_DFLT = (1 << 5),
+ AP_PHY2_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL2_BITS;
+
+typedef enum {
+
+ AP_REG3_EN = (1 << 15),
+ AP_REG3_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP3 = (1 << 6),
+ AP_PHY3_DFLT = (1 << 5),
+ AP_PHY3_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL3_BITS;
+
+
+typedef enum {
+
+ AP_REG4_EN = (1 << 15),
+ AP_REG4_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP4 = (1 << 6),
+ AP_PHY4_DFLT = (1 << 5),
+ AP_PHY4_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL4_BITS;
+
+
+typedef enum {
+
+ AP_REG5_EN = (1 << 15),
+ AP_REG5_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
+ AP_PRE_SUP5 = (1 << 6),
+ AP_PHY5_DFLT = (1 << 5),
+ AP_PHY5_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
+
+}AUTOPOLL5_BITS;
+
+
+
+
+/* AP_VALUE 0x98, 32bit ragister */
+typedef enum {
+
+ AP_VAL_ACTIVE = (1 << 31),
+ AP_VAL_RD_CMD = ( 1 << 29),
+ AP_ADDR = (1 << 18)|(1 << 17)|(1 << 16), /* 18:16 */
+ AP_VAL = (0xF << 0) | (0xF << 4) |( 0xF << 8) |
+ (0xF << 12), /* 15:0 */
+
+}AP_VALUE_BITS;
+
+typedef enum {
+
+ DLY_INT_A_R3 = (1 << 31),
+ DLY_INT_A_R2 = (1 << 30),
+ DLY_INT_A_R1 = (1 << 29),
+ DLY_INT_A_R0 = (1 << 28),
+ DLY_INT_A_T3 = (1 << 27),
+ DLY_INT_A_T2 = (1 << 26),
+ DLY_INT_A_T1 = (1 << 25),
+ DLY_INT_A_T0 = ( 1 << 24),
+ EVENT_COUNT_A = (0xF << 16) | (0x1 << 20),/* 20:16 */
+ MAX_DELAY_TIME_A = (0xF << 0) | (0xF << 4) | (1 << 8)|
+ (1 << 9) | (1 << 10), /* 10:0 */
+
+}DLY_INT_A_BITS;
+
+typedef enum {
+
+ DLY_INT_B_R3 = (1 << 31),
+ DLY_INT_B_R2 = (1 << 30),
+ DLY_INT_B_R1 = (1 << 29),
+ DLY_INT_B_R0 = (1 << 28),
+ DLY_INT_B_T3 = (1 << 27),
+ DLY_INT_B_T2 = (1 << 26),
+ DLY_INT_B_T1 = (1 << 25),
+ DLY_INT_B_T0 = ( 1 << 24),
+ EVENT_COUNT_B = (0xF << 16) | (0x1 << 20),/* 20:16 */
+ MAX_DELAY_TIME_B = (0xF << 0) | (0xF << 4) | (1 << 8)|
+ (1 << 9) | (1 << 10), /* 10:0 */
+}DLY_INT_B_BITS;
+
+
+/* FLOW_CONTROL 0xC8, 32bit register */
+typedef enum {
+
+ PAUSE_LEN_CHG = (1 << 30),
+ FTPE = (1 << 22),
+ FRPE = (1 << 21),
+ NAPA = (1 << 20),
+ NPA = (1 << 19),
+ FIXP = ( 1 << 18),
+ FCCMD = ( 1 << 16),
+ PAUSE_LEN = (0xF << 0) | (0xF << 4) |( 0xF << 8) | (0xF << 12), /* 15:0 */
+
+}FLOW_CONTROL_BITS;
+
+/* PHY_ ACCESS 0xD0, 32bit register */
+typedef enum {
+
+ PHY_CMD_ACTIVE = (1 << 31),
+ PHY_WR_CMD = (1 << 30),
+ PHY_RD_CMD = (1 << 29),
+ PHY_RD_ERR = (1 << 28),
+ PHY_PRE_SUP = (1 << 27),
+ PHY_ADDR = (1 << 21) | (1 << 22) | (1 << 23)|
+ (1 << 24) |(1 << 25),/* 25:21 */
+ PHY_REG_ADDR = (1 << 16) | (1 << 17) | (1 << 18)| (1 << 19) | (1 << 20),/* 20:16 */
+ PHY_DATA = (0xF << 0)|(0xF << 4) |(0xF << 8)|
+ (0xF << 12),/* 15:0 */
+
+}PHY_ACCESS_BITS;
+
+
+/* PMAT0 0x190, 32bit register */
+typedef enum {
+ PMR_ACTIVE = (1 << 31),
+ PMR_WR_CMD = (1 << 30),
+ PMR_RD_CMD = (1 << 29),
+ PMR_BANK = (1 <<28),
+ PMR_ADDR = (0xF << 16)|(1 << 20)|(1 << 21)|
+ (1 << 22),/* 22:16 */
+ PMR_B4 = (0xF << 0) | (0xF << 4),/* 15:0 */
+}PMAT0_BITS;
+
+
+/* PMAT1 0x194, 32bit register */
+typedef enum {
+ PMR_B3 = (0xF << 24) | (0xF <<28),/* 31:24 */
+ PMR_B2 = (0xF << 16) |(0xF << 20),/* 23:16 */
+ PMR_B1 = (0xF << 8) | (0xF <<12), /* 15:8 */
+ PMR_B0 = (0xF << 0)|(0xF << 4),/* 7:0 */
+}PMAT1_BITS;
+
+/************************************************************************/
+/* */
+/* MIB counter definitions */
+/* */
+/************************************************************************/
+
+#define rcv_miss_pkts 0x00
+#define rcv_octets 0x01
+#define rcv_broadcast_pkts 0x02
+#define rcv_multicast_pkts 0x03
+#define rcv_undersize_pkts 0x04
+#define rcv_oversize_pkts 0x05
+#define rcv_fragments 0x06
+#define rcv_jabbers 0x07
+#define rcv_unicast_pkts 0x08
+#define rcv_alignment_errors 0x09
+#define rcv_fcs_errors 0x0A
+#define rcv_good_octets 0x0B
+#define rcv_mac_ctrl 0x0C
+#define rcv_flow_ctrl 0x0D
+#define rcv_pkts_64_octets 0x0E
+#define rcv_pkts_65to127_octets 0x0F
+#define rcv_pkts_128to255_octets 0x10
+#define rcv_pkts_256to511_octets 0x11
+#define rcv_pkts_512to1023_octets 0x12
+#define rcv_pkts_1024to1518_octets 0x13
+#define rcv_unsupported_opcode 0x14
+#define rcv_symbol_errors 0x15
+#define rcv_drop_pkts_ring1 0x16
+#define rcv_drop_pkts_ring2 0x17
+#define rcv_drop_pkts_ring3 0x18
+#define rcv_drop_pkts_ring4 0x19
+#define rcv_jumbo_pkts 0x1A
+
+#define xmt_underrun_pkts 0x20
+#define xmt_octets 0x21
+#define xmt_packets 0x22
+#define xmt_broadcast_pkts 0x23
+#define xmt_multicast_pkts 0x24
+#define xmt_collisions 0x25
+#define xmt_unicast_pkts 0x26
+#define xmt_one_collision 0x27
+#define xmt_multiple_collision 0x28
+#define xmt_deferred_transmit 0x29
+#define xmt_late_collision 0x2A
+#define xmt_excessive_defer 0x2B
+#define xmt_loss_carrier 0x2C
+#define xmt_excessive_collision 0x2D
+#define xmt_back_pressure 0x2E
+#define xmt_flow_ctrl 0x2F
+#define xmt_pkts_64_octets 0x30
+#define xmt_pkts_65to127_octets 0x31
+#define xmt_pkts_128to255_octets 0x32
+#define xmt_pkts_256to511_octets 0x33
+#define xmt_pkts_512to1023_octets 0x34
+#define xmt_pkts_1024to1518_octet 0x35
+#define xmt_oversize_pkts 0x36
+#define xmt_jumbo_pkts 0x37
+
+/* ipg parameters */
+#define DEFAULT_IPG 0x60
+#define IFS1_DELTA 36
+#define IPG_CONVERGE_JIFFIES (HZ/2)
+#define IPG_STABLE_TIME 5
+#define MIN_IPG 96
+#define MAX_IPG 255
+#define IPG_STEP 16
+#define CSTATE 1
+#define SSTATE 2
+
+/* amd8111e decriptor flag definitions */
+typedef enum {
+
+ OWN_BIT = (1 << 15),
+ ADD_FCS_BIT = (1 << 13),
+ LTINT_BIT = (1 << 12),
+ STP_BIT = (1 << 9),
+ ENP_BIT = (1 << 8),
+ KILL_BIT = (1 << 6),
+ TCC_VLAN_INSERT = (1 << 1),
+ TCC_VLAN_REPLACE = (1 << 1) |( 1<< 0),
+
+}TX_FLAG_BITS;
+
+typedef enum {
+ ERR_BIT = (1 << 14),
+ FRAM_BIT = (1 << 13),
+ OFLO_BIT = (1 << 12),
+ CRC_BIT = (1 << 11),
+ PAM_BIT = (1 << 6),
+ LAFM_BIT = (1 << 5),
+ BAM_BIT = (1 << 4),
+ TT_VLAN_TAGGED = (1 << 3) |(1 << 2),/* 0x000 */
+ TT_PRTY_TAGGED = (1 << 3),/* 0x0008 */
+
+}RX_FLAG_BITS;
+
+#define RESET_RX_FLAGS 0x0000
+#define TT_MASK 0x000c
+#define TCC_MASK 0x0003
+
+/* driver ioctl parameters */
+#define AMD8111E_REG_DUMP_LEN 13*sizeof(u32)
+
+/* crc generator constants */
+#define CRC32 0xedb88320
+#define INITCRC 0xFFFFFFFF
+
+/* kernel provided writeq does not write 64 bits into the amd8111e device register instead writes only higher 32bits data into lower 32bits of the register.
+BUG? */
+#define amd8111e_writeq(_UlData,_memMap) \
+ writel(*(u32*)(&_UlData), _memMap); \
+ writel(*(u32*)((u8*)(&_UlData)+4), _memMap+4)
+
+/* maps the external speed options to internal value */
+typedef enum {
+ SPEED_AUTONEG,
+ SPEED10_HALF,
+ SPEED10_FULL,
+ SPEED100_HALF,
+ SPEED100_FULL,
+}EXT_PHY_OPTION;
+
+
+#endif /* _AMD8111E_H */
+
diff --git a/gpxe/src/drivers/net/bnx2.c b/gpxe/src/drivers/net/bnx2.c
new file mode 100644
index 00000000..9c989c4c
--- /dev/null
+++ b/gpxe/src/drivers/net/bnx2.c
@@ -0,0 +1,2695 @@
+/* bnx2.c: Broadcom NX2 network driver.
+ *
+ * Copyright (c) 2004, 2005, 2006 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Michael Chan (mchan@broadcom.com)
+ *
+ * Etherboot port by Ryan Jackson (rjackson@lnxi.com), based on driver
+ * version 1.4.40 from linux 2.6.17
+ */
+
+
+#include "etherboot.h"
+#include "nic.h"
+#include <errno.h>
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+#include "string.h"
+#include "bnx2.h"
+#include "bnx2_fw.h"
+
+#if 0
+/* Dummy defines for error handling */
+#define EBUSY 1
+#define ENODEV 2
+#define EINVAL 3
+#define ENOMEM 4
+#define EIO 5
+#endif
+
+/* The bnx2 seems to be picky about the alignment of the receive buffers
+ * and possibly the status block.
+ */
+static struct bss {
+ struct tx_bd tx_desc_ring[TX_DESC_CNT];
+ struct rx_bd rx_desc_ring[RX_DESC_CNT];
+ unsigned char rx_buf[RX_BUF_CNT][RX_BUF_SIZE];
+ struct status_block status_blk;
+ struct statistics_block stats_blk;
+} bnx2_bss;
+
+static struct bnx2 bnx2;
+
+static struct flash_spec flash_table[] =
+{
+ /* Slow EEPROM */
+ {0x00000000, 0x40830380, 0x009f0081, 0xa184a053, 0xaf000400,
+ 1, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE,
+ SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE,
+ "EEPROM - slow"},
+ /* Expansion entry 0001 */
+ {0x08000002, 0x4b808201, 0x00050081, 0x03840253, 0xaf020406,
+ 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+ SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
+ "Entry 0001"},
+ /* Saifun SA25F010 (non-buffered flash) */
+ /* strap, cfg1, & write1 need updates */
+ {0x04000001, 0x47808201, 0x00050081, 0x03840253, 0xaf020406,
+ 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+ SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*2,
+ "Non-buffered flash (128kB)"},
+ /* Saifun SA25F020 (non-buffered flash) */
+ /* strap, cfg1, & write1 need updates */
+ {0x0c000003, 0x4f808201, 0x00050081, 0x03840253, 0xaf020406,
+ 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+ SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*4,
+ "Non-buffered flash (256kB)"},
+ /* Expansion entry 0100 */
+ {0x11000000, 0x53808201, 0x00050081, 0x03840253, 0xaf020406,
+ 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+ SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
+ "Entry 0100"},
+ /* Entry 0101: ST M45PE10 (non-buffered flash, TetonII B0) */
+ {0x19000002, 0x5b808201, 0x000500db, 0x03840253, 0xaf020406,
+ 0, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE,
+ ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*2,
+ "Entry 0101: ST M45PE10 (128kB non-bufferred)"},
+ /* Entry 0110: ST M45PE20 (non-buffered flash)*/
+ {0x15000001, 0x57808201, 0x000500db, 0x03840253, 0xaf020406,
+ 0, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE,
+ ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*4,
+ "Entry 0110: ST M45PE20 (256kB non-bufferred)"},
+ /* Saifun SA25F005 (non-buffered flash) */
+ /* strap, cfg1, & write1 need updates */
+ {0x1d000003, 0x5f808201, 0x00050081, 0x03840253, 0xaf020406,
+ 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+ SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE,
+ "Non-buffered flash (64kB)"},
+ /* Fast EEPROM */
+ {0x22000000, 0x62808380, 0x009f0081, 0xa184a053, 0xaf000400,
+ 1, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE,
+ SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE,
+ "EEPROM - fast"},
+ /* Expansion entry 1001 */
+ {0x2a000002, 0x6b808201, 0x00050081, 0x03840253, 0xaf020406,
+ 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+ SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
+ "Entry 1001"},
+ /* Expansion entry 1010 */
+ {0x26000001, 0x67808201, 0x00050081, 0x03840253, 0xaf020406,
+ 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+ SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
+ "Entry 1010"},
+ /* ATMEL AT45DB011B (buffered flash) */
+ {0x2e000003, 0x6e808273, 0x00570081, 0x68848353, 0xaf000400,
+ 1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE,
+ BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE,
+ "Buffered flash (128kB)"},
+ /* Expansion entry 1100 */
+ {0x33000000, 0x73808201, 0x00050081, 0x03840253, 0xaf020406,
+ 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+ SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
+ "Entry 1100"},
+ /* Expansion entry 1101 */
+ {0x3b000002, 0x7b808201, 0x00050081, 0x03840253, 0xaf020406,
+ 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+ SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
+ "Entry 1101"},
+ /* Ateml Expansion entry 1110 */
+ {0x37000001, 0x76808273, 0x00570081, 0x68848353, 0xaf000400,
+ 1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE,
+ BUFFERED_FLASH_BYTE_ADDR_MASK, 0,
+ "Entry 1110 (Atmel)"},
+ /* ATMEL AT45DB021B (buffered flash) */
+ {0x3f000003, 0x7e808273, 0x00570081, 0x68848353, 0xaf000400,
+ 1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE,
+ BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE*2,
+ "Buffered flash (256kB)"},
+};
+
+static u32
+bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset)
+{
+ REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset);
+ return (REG_RD(bp, BNX2_PCICFG_REG_WINDOW));
+}
+
+static void
+bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val)
+{
+ REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset);
+ REG_WR(bp, BNX2_PCICFG_REG_WINDOW, val);
+}
+
+static void
+bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val)
+{
+ offset += cid_addr;
+ REG_WR(bp, BNX2_CTX_DATA_ADR, offset);
+ REG_WR(bp, BNX2_CTX_DATA, val);
+}
+
+static int
+bnx2_read_phy(struct bnx2 *bp, u32 reg, u32 *val)
+{
+ u32 val1;
+ int i, ret;
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+ val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+ val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL;
+
+ REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
+ REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+
+ udelay(40);
+ }
+
+ val1 = (bp->phy_addr << 21) | (reg << 16) |
+ BNX2_EMAC_MDIO_COMM_COMMAND_READ | BNX2_EMAC_MDIO_COMM_DISEXT |
+ BNX2_EMAC_MDIO_COMM_START_BUSY;
+ REG_WR(bp, BNX2_EMAC_MDIO_COMM, val1);
+
+ for (i = 0; i < 50; i++) {
+ udelay(10);
+
+ val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM);
+ if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) {
+ udelay(5);
+
+ val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM);
+ val1 &= BNX2_EMAC_MDIO_COMM_DATA;
+
+ break;
+ }
+ }
+
+ if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY) {
+ *val = 0x0;
+ ret = -EBUSY;
+ }
+ else {
+ *val = val1;
+ ret = 0;
+ }
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+ val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+ val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL;
+
+ REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
+ REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+
+ udelay(40);
+ }
+
+ return ret;
+}
+
+static int
+bnx2_write_phy(struct bnx2 *bp, u32 reg, u32 val)
+{
+ u32 val1;
+ int i, ret;
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+ val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+ val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL;
+
+ REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
+ REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+
+ udelay(40);
+ }
+
+ val1 = (bp->phy_addr << 21) | (reg << 16) | val |
+ BNX2_EMAC_MDIO_COMM_COMMAND_WRITE |
+ BNX2_EMAC_MDIO_COMM_START_BUSY | BNX2_EMAC_MDIO_COMM_DISEXT;
+ REG_WR(bp, BNX2_EMAC_MDIO_COMM, val1);
+
+ for (i = 0; i < 50; i++) {
+ udelay(10);
+
+ val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM);
+ if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) {
+ udelay(5);
+ break;
+ }
+ }
+
+ if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)
+ ret = -EBUSY;
+ else
+ ret = 0;
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+ val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+ val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL;
+
+ REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
+ REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+
+ udelay(40);
+ }
+
+ return ret;
+}
+
+static void
+bnx2_disable_int(struct bnx2 *bp)
+{
+ REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
+ BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
+ REG_RD(bp, BNX2_PCICFG_INT_ACK_CMD);
+
+}
+
+static int
+bnx2_alloc_mem(struct bnx2 *bp)
+{
+ bp->tx_desc_ring = bnx2_bss.tx_desc_ring;
+ bp->tx_desc_mapping = virt_to_bus(bp->tx_desc_ring);
+
+ bp->rx_desc_ring = bnx2_bss.rx_desc_ring;
+ memset(bp->rx_desc_ring, 0, sizeof(struct rx_bd) * RX_DESC_CNT);
+ bp->rx_desc_mapping = virt_to_bus(bp->rx_desc_ring);
+
+ memset(&bnx2_bss.status_blk, 0, sizeof(struct status_block));
+ bp->status_blk = &bnx2_bss.status_blk;
+ bp->status_blk_mapping = virt_to_bus(&bnx2_bss.status_blk);
+
+ bp->stats_blk = &bnx2_bss.stats_blk;
+ memset(&bnx2_bss.stats_blk, 0, sizeof(struct statistics_block));
+ bp->stats_blk_mapping = virt_to_bus(&bnx2_bss.stats_blk);
+
+ return 0;
+}
+
+static void
+bnx2_report_fw_link(struct bnx2 *bp)
+{
+ u32 fw_link_status = 0;
+
+ if (bp->link_up) {
+ u32 bmsr;
+
+ switch (bp->line_speed) {
+ case SPEED_10:
+ if (bp->duplex == DUPLEX_HALF)
+ fw_link_status = BNX2_LINK_STATUS_10HALF;
+ else
+ fw_link_status = BNX2_LINK_STATUS_10FULL;
+ break;
+ case SPEED_100:
+ if (bp->duplex == DUPLEX_HALF)
+ fw_link_status = BNX2_LINK_STATUS_100HALF;
+ else
+ fw_link_status = BNX2_LINK_STATUS_100FULL;
+ break;
+ case SPEED_1000:
+ if (bp->duplex == DUPLEX_HALF)
+ fw_link_status = BNX2_LINK_STATUS_1000HALF;
+ else
+ fw_link_status = BNX2_LINK_STATUS_1000FULL;
+ break;
+ case SPEED_2500:
+ if (bp->duplex == DUPLEX_HALF)
+ fw_link_status = BNX2_LINK_STATUS_2500HALF;
+ else
+ fw_link_status = BNX2_LINK_STATUS_2500FULL;
+ break;
+ }
+
+ fw_link_status |= BNX2_LINK_STATUS_LINK_UP;
+
+ if (bp->autoneg) {
+ fw_link_status |= BNX2_LINK_STATUS_AN_ENABLED;
+
+ bnx2_read_phy(bp, MII_BMSR, &bmsr);
+ bnx2_read_phy(bp, MII_BMSR, &bmsr);
+
+ if (!(bmsr & BMSR_ANEGCOMPLETE) ||
+ bp->phy_flags & PHY_PARALLEL_DETECT_FLAG)
+ fw_link_status |= BNX2_LINK_STATUS_PARALLEL_DET;
+ else
+ fw_link_status |= BNX2_LINK_STATUS_AN_COMPLETE;
+ }
+ }
+ else
+ fw_link_status = BNX2_LINK_STATUS_LINK_DOWN;
+
+ REG_WR_IND(bp, bp->shmem_base + BNX2_LINK_STATUS, fw_link_status);
+}
+
+static void
+bnx2_report_link(struct bnx2 *bp)
+{
+ if (bp->link_up) {
+ printf("NIC Link is Up, ");
+
+ printf("%d Mbps ", bp->line_speed);
+
+ if (bp->duplex == DUPLEX_FULL)
+ printf("full duplex");
+ else
+ printf("half duplex");
+
+ if (bp->flow_ctrl) {
+ if (bp->flow_ctrl & FLOW_CTRL_RX) {
+ printf(", receive ");
+ if (bp->flow_ctrl & FLOW_CTRL_TX)
+ printf("& transmit ");
+ }
+ else {
+ printf(", transmit ");
+ }
+ printf("flow control ON");
+ }
+ printf("\n");
+ }
+ else {
+ printf("NIC Link is Down\n");
+ }
+
+ bnx2_report_fw_link(bp);
+}
+
+static void
+bnx2_resolve_flow_ctrl(struct bnx2 *bp)
+{
+ u32 local_adv, remote_adv;
+
+ bp->flow_ctrl = 0;
+ if ((bp->autoneg & (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) !=
+ (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) {
+
+ if (bp->duplex == DUPLEX_FULL) {
+ bp->flow_ctrl = bp->req_flow_ctrl;
+ }
+ return;
+ }
+
+ if (bp->duplex != DUPLEX_FULL) {
+ return;
+ }
+
+ if ((bp->phy_flags & PHY_SERDES_FLAG) &&
+ (CHIP_NUM(bp) == CHIP_NUM_5708)) {
+ u32 val;
+
+ bnx2_read_phy(bp, BCM5708S_1000X_STAT1, &val);
+ if (val & BCM5708S_1000X_STAT1_TX_PAUSE)
+ bp->flow_ctrl |= FLOW_CTRL_TX;
+ if (val & BCM5708S_1000X_STAT1_RX_PAUSE)
+ bp->flow_ctrl |= FLOW_CTRL_RX;
+ return;
+ }
+
+ bnx2_read_phy(bp, MII_ADVERTISE, &local_adv);
+ bnx2_read_phy(bp, MII_LPA, &remote_adv);
+
+ if (bp->phy_flags & PHY_SERDES_FLAG) {
+ u32 new_local_adv = 0;
+ u32 new_remote_adv = 0;
+
+ if (local_adv & ADVERTISE_1000XPAUSE)
+ new_local_adv |= ADVERTISE_PAUSE_CAP;
+ if (local_adv & ADVERTISE_1000XPSE_ASYM)
+ new_local_adv |= ADVERTISE_PAUSE_ASYM;
+ if (remote_adv & ADVERTISE_1000XPAUSE)
+ new_remote_adv |= ADVERTISE_PAUSE_CAP;
+ if (remote_adv & ADVERTISE_1000XPSE_ASYM)
+ new_remote_adv |= ADVERTISE_PAUSE_ASYM;
+
+ local_adv = new_local_adv;
+ remote_adv = new_remote_adv;
+ }
+
+ /* See Table 28B-3 of 802.3ab-1999 spec. */
+ if (local_adv & ADVERTISE_PAUSE_CAP) {
+ if(local_adv & ADVERTISE_PAUSE_ASYM) {
+ if (remote_adv & ADVERTISE_PAUSE_CAP) {
+ bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX;
+ }
+ else if (remote_adv & ADVERTISE_PAUSE_ASYM) {
+ bp->flow_ctrl = FLOW_CTRL_RX;
+ }
+ }
+ else {
+ if (remote_adv & ADVERTISE_PAUSE_CAP) {
+ bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX;
+ }
+ }
+ }
+ else if (local_adv & ADVERTISE_PAUSE_ASYM) {
+ if ((remote_adv & ADVERTISE_PAUSE_CAP) &&
+ (remote_adv & ADVERTISE_PAUSE_ASYM)) {
+
+ bp->flow_ctrl = FLOW_CTRL_TX;
+ }
+ }
+}
+
+static int
+bnx2_5708s_linkup(struct bnx2 *bp)
+{
+ u32 val;
+
+ bp->link_up = 1;
+ bnx2_read_phy(bp, BCM5708S_1000X_STAT1, &val);
+ switch (val & BCM5708S_1000X_STAT1_SPEED_MASK) {
+ case BCM5708S_1000X_STAT1_SPEED_10:
+ bp->line_speed = SPEED_10;
+ break;
+ case BCM5708S_1000X_STAT1_SPEED_100:
+ bp->line_speed = SPEED_100;
+ break;
+ case BCM5708S_1000X_STAT1_SPEED_1G:
+ bp->line_speed = SPEED_1000;
+ break;
+ case BCM5708S_1000X_STAT1_SPEED_2G5:
+ bp->line_speed = SPEED_2500;
+ break;
+ }
+ if (val & BCM5708S_1000X_STAT1_FD)
+ bp->duplex = DUPLEX_FULL;
+ else
+ bp->duplex = DUPLEX_HALF;
+
+ return 0;
+}
+
+static int
+bnx2_5706s_linkup(struct bnx2 *bp)
+{
+ u32 bmcr, local_adv, remote_adv, common;
+
+ bp->link_up = 1;
+ bp->line_speed = SPEED_1000;
+
+ bnx2_read_phy(bp, MII_BMCR, &bmcr);
+ if (bmcr & BMCR_FULLDPLX) {
+ bp->duplex = DUPLEX_FULL;
+ }
+ else {
+ bp->duplex = DUPLEX_HALF;
+ }
+
+ if (!(bmcr & BMCR_ANENABLE)) {
+ return 0;
+ }
+
+ bnx2_read_phy(bp, MII_ADVERTISE, &local_adv);
+ bnx2_read_phy(bp, MII_LPA, &remote_adv);
+
+ common = local_adv & remote_adv;
+ if (common & (ADVERTISE_1000XHALF | ADVERTISE_1000XFULL)) {
+
+ if (common & ADVERTISE_1000XFULL) {
+ bp->duplex = DUPLEX_FULL;
+ }
+ else {
+ bp->duplex = DUPLEX_HALF;
+ }
+ }
+
+ return 0;
+}
+
+static int
+bnx2_copper_linkup(struct bnx2 *bp)
+{
+ u32 bmcr;
+
+ bnx2_read_phy(bp, MII_BMCR, &bmcr);
+ if (bmcr & BMCR_ANENABLE) {
+ u32 local_adv, remote_adv, common;
+
+ bnx2_read_phy(bp, MII_CTRL1000, &local_adv);
+ bnx2_read_phy(bp, MII_STAT1000, &remote_adv);
+
+ common = local_adv & (remote_adv >> 2);
+ if (common & ADVERTISE_1000FULL) {
+ bp->line_speed = SPEED_1000;
+ bp->duplex = DUPLEX_FULL;
+ }
+ else if (common & ADVERTISE_1000HALF) {
+ bp->line_speed = SPEED_1000;
+ bp->duplex = DUPLEX_HALF;
+ }
+ else {
+ bnx2_read_phy(bp, MII_ADVERTISE, &local_adv);
+ bnx2_read_phy(bp, MII_LPA, &remote_adv);
+
+ common = local_adv & remote_adv;
+ if (common & ADVERTISE_100FULL) {
+ bp->line_speed = SPEED_100;
+ bp->duplex = DUPLEX_FULL;
+ }
+ else if (common & ADVERTISE_100HALF) {
+ bp->line_speed = SPEED_100;
+ bp->duplex = DUPLEX_HALF;
+ }
+ else if (common & ADVERTISE_10FULL) {
+ bp->line_speed = SPEED_10;
+ bp->duplex = DUPLEX_FULL;
+ }
+ else if (common & ADVERTISE_10HALF) {
+ bp->line_speed = SPEED_10;
+ bp->duplex = DUPLEX_HALF;
+ }
+ else {
+ bp->line_speed = 0;
+ bp->link_up = 0;
+ }
+ }
+ }
+ else {
+ if (bmcr & BMCR_SPEED100) {
+ bp->line_speed = SPEED_100;
+ }
+ else {
+ bp->line_speed = SPEED_10;
+ }
+ if (bmcr & BMCR_FULLDPLX) {
+ bp->duplex = DUPLEX_FULL;
+ }
+ else {
+ bp->duplex = DUPLEX_HALF;
+ }
+ }
+
+ return 0;
+}
+
+static int
+bnx2_set_mac_link(struct bnx2 *bp)
+{
+ u32 val;
+
+ REG_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x2620);
+ if (bp->link_up && (bp->line_speed == SPEED_1000) &&
+ (bp->duplex == DUPLEX_HALF)) {
+ REG_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x26ff);
+ }
+
+ /* Configure the EMAC mode register. */
+ val = REG_RD(bp, BNX2_EMAC_MODE);
+
+ val &= ~(BNX2_EMAC_MODE_PORT | BNX2_EMAC_MODE_HALF_DUPLEX |
+ BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK |
+ BNX2_EMAC_MODE_25G);
+
+ if (bp->link_up) {
+ switch (bp->line_speed) {
+ case SPEED_10:
+ if (CHIP_NUM(bp) == CHIP_NUM_5708) {
+ val |= BNX2_EMAC_MODE_PORT_MII_10;
+ break;
+ }
+ /* fall through */
+ case SPEED_100:
+ val |= BNX2_EMAC_MODE_PORT_MII;
+ break;
+ case SPEED_2500:
+ val |= BNX2_EMAC_MODE_25G;
+ /* fall through */
+ case SPEED_1000:
+ val |= BNX2_EMAC_MODE_PORT_GMII;
+ break;
+ }
+ }
+ else {
+ val |= BNX2_EMAC_MODE_PORT_GMII;
+ }
+
+ /* Set the MAC to operate in the appropriate duplex mode. */
+ if (bp->duplex == DUPLEX_HALF)
+ val |= BNX2_EMAC_MODE_HALF_DUPLEX;
+ REG_WR(bp, BNX2_EMAC_MODE, val);
+
+ /* Enable/disable rx PAUSE. */
+ bp->rx_mode &= ~BNX2_EMAC_RX_MODE_FLOW_EN;
+
+ if (bp->flow_ctrl & FLOW_CTRL_RX)
+ bp->rx_mode |= BNX2_EMAC_RX_MODE_FLOW_EN;
+ REG_WR(bp, BNX2_EMAC_RX_MODE, bp->rx_mode);
+
+ /* Enable/disable tx PAUSE. */
+ val = REG_RD(bp, BNX2_EMAC_TX_MODE);
+ val &= ~BNX2_EMAC_TX_MODE_FLOW_EN;
+
+ if (bp->flow_ctrl & FLOW_CTRL_TX)
+ val |= BNX2_EMAC_TX_MODE_FLOW_EN;
+ REG_WR(bp, BNX2_EMAC_TX_MODE, val);
+
+ /* Acknowledge the interrupt. */
+ REG_WR(bp, BNX2_EMAC_STATUS, BNX2_EMAC_STATUS_LINK_CHANGE);
+
+ return 0;
+}
+
+static int
+bnx2_set_link(struct bnx2 *bp)
+{
+ u32 bmsr;
+ u8 link_up;
+
+ if (bp->loopback == MAC_LOOPBACK) {
+ bp->link_up = 1;
+ return 0;
+ }
+
+ link_up = bp->link_up;
+
+ bnx2_read_phy(bp, MII_BMSR, &bmsr);
+ bnx2_read_phy(bp, MII_BMSR, &bmsr);
+
+ if ((bp->phy_flags & PHY_SERDES_FLAG) &&
+ (CHIP_NUM(bp) == CHIP_NUM_5706)) {
+ u32 val;
+
+ val = REG_RD(bp, BNX2_EMAC_STATUS);
+ if (val & BNX2_EMAC_STATUS_LINK)
+ bmsr |= BMSR_LSTATUS;
+ else
+ bmsr &= ~BMSR_LSTATUS;
+ }
+
+ if (bmsr & BMSR_LSTATUS) {
+ bp->link_up = 1;
+
+ if (bp->phy_flags & PHY_SERDES_FLAG) {
+ if (CHIP_NUM(bp) == CHIP_NUM_5706)
+ bnx2_5706s_linkup(bp);
+ else if (CHIP_NUM(bp) == CHIP_NUM_5708)
+ bnx2_5708s_linkup(bp);
+ }
+ else {
+ bnx2_copper_linkup(bp);
+ }
+ bnx2_resolve_flow_ctrl(bp);
+ }
+ else {
+ if ((bp->phy_flags & PHY_SERDES_FLAG) &&
+ (bp->autoneg & AUTONEG_SPEED)) {
+
+ u32 bmcr;
+
+ bnx2_read_phy(bp, MII_BMCR, &bmcr);
+ if (!(bmcr & BMCR_ANENABLE)) {
+ bnx2_write_phy(bp, MII_BMCR, bmcr |
+ BMCR_ANENABLE);
+ }
+ }
+ bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG;
+ bp->link_up = 0;
+ }
+
+ if (bp->link_up != link_up) {
+ bnx2_report_link(bp);
+ }
+
+ bnx2_set_mac_link(bp);
+
+ return 0;
+}
+
+static int
+bnx2_reset_phy(struct bnx2 *bp)
+{
+ int i;
+ u32 reg;
+
+ bnx2_write_phy(bp, MII_BMCR, BMCR_RESET);
+
+#define PHY_RESET_MAX_WAIT 100
+ for (i = 0; i < PHY_RESET_MAX_WAIT; i++) {
+ udelay(10);
+
+ bnx2_read_phy(bp, MII_BMCR, &reg);
+ if (!(reg & BMCR_RESET)) {
+ udelay(20);
+ break;
+ }
+ }
+ if (i == PHY_RESET_MAX_WAIT) {
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static u32
+bnx2_phy_get_pause_adv(struct bnx2 *bp)
+{
+ u32 adv = 0;
+
+ if ((bp->req_flow_ctrl & (FLOW_CTRL_RX | FLOW_CTRL_TX)) ==
+ (FLOW_CTRL_RX | FLOW_CTRL_TX)) {
+
+ if (bp->phy_flags & PHY_SERDES_FLAG) {
+ adv = ADVERTISE_1000XPAUSE;
+ }
+ else {
+ adv = ADVERTISE_PAUSE_CAP;
+ }
+ }
+ else if (bp->req_flow_ctrl & FLOW_CTRL_TX) {
+ if (bp->phy_flags & PHY_SERDES_FLAG) {
+ adv = ADVERTISE_1000XPSE_ASYM;
+ }
+ else {
+ adv = ADVERTISE_PAUSE_ASYM;
+ }
+ }
+ else if (bp->req_flow_ctrl & FLOW_CTRL_RX) {
+ if (bp->phy_flags & PHY_SERDES_FLAG) {
+ adv = ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM;
+ }
+ else {
+ adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+ }
+ }
+ return adv;
+}
+
+static int
+bnx2_setup_serdes_phy(struct bnx2 *bp)
+{
+ u32 adv, bmcr, up1;
+ u32 new_adv = 0;
+
+ if (!(bp->autoneg & AUTONEG_SPEED)) {
+ u32 new_bmcr;
+ int force_link_down = 0;
+
+ if (CHIP_NUM(bp) == CHIP_NUM_5708) {
+ bnx2_read_phy(bp, BCM5708S_UP1, &up1);
+ if (up1 & BCM5708S_UP1_2G5) {
+ up1 &= ~BCM5708S_UP1_2G5;
+ bnx2_write_phy(bp, BCM5708S_UP1, up1);
+ force_link_down = 1;
+ }
+ }
+
+ bnx2_read_phy(bp, MII_ADVERTISE, &adv);
+ adv &= ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF);
+
+ bnx2_read_phy(bp, MII_BMCR, &bmcr);
+ new_bmcr = bmcr & ~BMCR_ANENABLE;
+ new_bmcr |= BMCR_SPEED1000;
+ if (bp->req_duplex == DUPLEX_FULL) {
+ adv |= ADVERTISE_1000XFULL;
+ new_bmcr |= BMCR_FULLDPLX;
+ }
+ else {
+ adv |= ADVERTISE_1000XHALF;
+ new_bmcr &= ~BMCR_FULLDPLX;
+ }
+ if ((new_bmcr != bmcr) || (force_link_down)) {
+ /* Force a link down visible on the other side */
+ if (bp->link_up) {
+ bnx2_write_phy(bp, MII_ADVERTISE, adv &
+ ~(ADVERTISE_1000XFULL |
+ ADVERTISE_1000XHALF));
+ bnx2_write_phy(bp, MII_BMCR, bmcr |
+ BMCR_ANRESTART | BMCR_ANENABLE);
+
+ bp->link_up = 0;
+ bnx2_write_phy(bp, MII_BMCR, new_bmcr);
+ }
+ bnx2_write_phy(bp, MII_ADVERTISE, adv);
+ bnx2_write_phy(bp, MII_BMCR, new_bmcr);
+ }
+ return 0;
+ }
+
+ if (bp->phy_flags & PHY_2_5G_CAPABLE_FLAG) {
+ bnx2_read_phy(bp, BCM5708S_UP1, &up1);
+ up1 |= BCM5708S_UP1_2G5;
+ bnx2_write_phy(bp, BCM5708S_UP1, up1);
+ }
+
+ if (bp->advertising & ADVERTISED_1000baseT_Full)
+ new_adv |= ADVERTISE_1000XFULL;
+
+ new_adv |= bnx2_phy_get_pause_adv(bp);
+
+ bnx2_read_phy(bp, MII_ADVERTISE, &adv);
+ bnx2_read_phy(bp, MII_BMCR, &bmcr);
+
+ bp->serdes_an_pending = 0;
+ if ((adv != new_adv) || ((bmcr & BMCR_ANENABLE) == 0)) {
+ /* Force a link down visible on the other side */
+ if (bp->link_up) {
+ int i;
+
+ bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK);
+ for (i = 0; i < 110; i++) {
+ udelay(100);
+ }
+ }
+
+ bnx2_write_phy(bp, MII_ADVERTISE, new_adv);
+ bnx2_write_phy(bp, MII_BMCR, bmcr | BMCR_ANRESTART |
+ BMCR_ANENABLE);
+#if 0
+ if (CHIP_NUM(bp) == CHIP_NUM_5706) {
+ /* Speed up link-up time when the link partner
+ * does not autonegotiate which is very common
+ * in blade servers. Some blade servers use
+ * IPMI for kerboard input and it's important
+ * to minimize link disruptions. Autoneg. involves
+ * exchanging base pages plus 3 next pages and
+ * normally completes in about 120 msec.
+ */
+ bp->current_interval = SERDES_AN_TIMEOUT;
+ bp->serdes_an_pending = 1;
+ mod_timer(&bp->timer, jiffies + bp->current_interval);
+ }
+#endif
+ }
+
+ return 0;
+}
+
+#define ETHTOOL_ALL_FIBRE_SPEED \
+ (ADVERTISED_1000baseT_Full)
+
+#define ETHTOOL_ALL_COPPER_SPEED \
+ (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \
+ ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \
+ ADVERTISED_1000baseT_Full)
+
+#define PHY_ALL_10_100_SPEED (ADVERTISE_10HALF | ADVERTISE_10FULL | \
+ ADVERTISE_100HALF | ADVERTISE_100FULL | ADVERTISE_CSMA)
+
+#define PHY_ALL_1000_SPEED (ADVERTISE_1000HALF | ADVERTISE_1000FULL)
+
+static int
+bnx2_setup_copper_phy(struct bnx2 *bp)
+{
+ u32 bmcr;
+ u32 new_bmcr;
+
+ bnx2_read_phy(bp, MII_BMCR, &bmcr);
+
+ if (bp->autoneg & AUTONEG_SPEED) {
+ u32 adv_reg, adv1000_reg;
+ u32 new_adv_reg = 0;
+ u32 new_adv1000_reg = 0;
+
+ bnx2_read_phy(bp, MII_ADVERTISE, &adv_reg);
+ adv_reg &= (PHY_ALL_10_100_SPEED | ADVERTISE_PAUSE_CAP |
+ ADVERTISE_PAUSE_ASYM);
+
+ bnx2_read_phy(bp, MII_CTRL1000, &adv1000_reg);
+ adv1000_reg &= PHY_ALL_1000_SPEED;
+
+ if (bp->advertising & ADVERTISED_10baseT_Half)
+ new_adv_reg |= ADVERTISE_10HALF;
+ if (bp->advertising & ADVERTISED_10baseT_Full)
+ new_adv_reg |= ADVERTISE_10FULL;
+ if (bp->advertising & ADVERTISED_100baseT_Half)
+ new_adv_reg |= ADVERTISE_100HALF;
+ if (bp->advertising & ADVERTISED_100baseT_Full)
+ new_adv_reg |= ADVERTISE_100FULL;
+ if (bp->advertising & ADVERTISED_1000baseT_Full)
+ new_adv1000_reg |= ADVERTISE_1000FULL;
+
+ new_adv_reg |= ADVERTISE_CSMA;
+
+ new_adv_reg |= bnx2_phy_get_pause_adv(bp);
+
+ if ((adv1000_reg != new_adv1000_reg) ||
+ (adv_reg != new_adv_reg) ||
+ ((bmcr & BMCR_ANENABLE) == 0)) {
+
+ bnx2_write_phy(bp, MII_ADVERTISE, new_adv_reg);
+ bnx2_write_phy(bp, MII_CTRL1000, new_adv1000_reg);
+ bnx2_write_phy(bp, MII_BMCR, BMCR_ANRESTART |
+ BMCR_ANENABLE);
+ }
+ else if (bp->link_up) {
+ /* Flow ctrl may have changed from auto to forced */
+ /* or vice-versa. */
+
+ bnx2_resolve_flow_ctrl(bp);
+ bnx2_set_mac_link(bp);
+ }
+ return 0;
+ }
+
+ new_bmcr = 0;
+ if (bp->req_line_speed == SPEED_100) {
+ new_bmcr |= BMCR_SPEED100;
+ }
+ if (bp->req_duplex == DUPLEX_FULL) {
+ new_bmcr |= BMCR_FULLDPLX;
+ }
+ if (new_bmcr != bmcr) {
+ u32 bmsr;
+ int i = 0;
+
+ bnx2_read_phy(bp, MII_BMSR, &bmsr);
+ bnx2_read_phy(bp, MII_BMSR, &bmsr);
+
+ if (bmsr & BMSR_LSTATUS) {
+ /* Force link down */
+ bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK);
+ do {
+ udelay(100);
+ bnx2_read_phy(bp, MII_BMSR, &bmsr);
+ bnx2_read_phy(bp, MII_BMSR, &bmsr);
+ i++;
+ } while ((bmsr & BMSR_LSTATUS) && (i < 620));
+ }
+
+ bnx2_write_phy(bp, MII_BMCR, new_bmcr);
+
+ /* Normally, the new speed is setup after the link has
+ * gone down and up again. In some cases, link will not go
+ * down so we need to set up the new speed here.
+ */
+ if (bmsr & BMSR_LSTATUS) {
+ bp->line_speed = bp->req_line_speed;
+ bp->duplex = bp->req_duplex;
+ bnx2_resolve_flow_ctrl(bp);
+ bnx2_set_mac_link(bp);
+ }
+ }
+ return 0;
+}
+
+static int
+bnx2_setup_phy(struct bnx2 *bp)
+{
+ if (bp->loopback == MAC_LOOPBACK)
+ return 0;
+
+ if (bp->phy_flags & PHY_SERDES_FLAG) {
+ return (bnx2_setup_serdes_phy(bp));
+ }
+ else {
+ return (bnx2_setup_copper_phy(bp));
+ }
+}
+
+static int
+bnx2_init_5708s_phy(struct bnx2 *bp)
+{
+ u32 val;
+
+ bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG3);
+ bnx2_write_phy(bp, BCM5708S_DIG_3_0, BCM5708S_DIG_3_0_USE_IEEE);
+ bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG);
+
+ bnx2_read_phy(bp, BCM5708S_1000X_CTL1, &val);
+ val |= BCM5708S_1000X_CTL1_FIBER_MODE | BCM5708S_1000X_CTL1_AUTODET_EN;
+ bnx2_write_phy(bp, BCM5708S_1000X_CTL1, val);
+
+ bnx2_read_phy(bp, BCM5708S_1000X_CTL2, &val);
+ val |= BCM5708S_1000X_CTL2_PLLEL_DET_EN;
+ bnx2_write_phy(bp, BCM5708S_1000X_CTL2, val);
+
+ if (bp->phy_flags & PHY_2_5G_CAPABLE_FLAG) {
+ bnx2_read_phy(bp, BCM5708S_UP1, &val);
+ val |= BCM5708S_UP1_2G5;
+ bnx2_write_phy(bp, BCM5708S_UP1, val);
+ }
+
+ if ((CHIP_ID(bp) == CHIP_ID_5708_A0) ||
+ (CHIP_ID(bp) == CHIP_ID_5708_B0) ||
+ (CHIP_ID(bp) == CHIP_ID_5708_B1)) {
+ /* increase tx signal amplitude */
+ bnx2_write_phy(bp, BCM5708S_BLK_ADDR,
+ BCM5708S_BLK_ADDR_TX_MISC);
+ bnx2_read_phy(bp, BCM5708S_TX_ACTL1, &val);
+ val &= ~BCM5708S_TX_ACTL1_DRIVER_VCM;
+ bnx2_write_phy(bp, BCM5708S_TX_ACTL1, val);
+ bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG);
+ }
+
+ val = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_CONFIG) &
+ BNX2_PORT_HW_CFG_CFG_TXCTL3_MASK;
+
+ if (val) {
+ u32 is_backplane;
+
+ is_backplane = REG_RD_IND(bp, bp->shmem_base +
+ BNX2_SHARED_HW_CFG_CONFIG);
+ if (is_backplane & BNX2_SHARED_HW_CFG_PHY_BACKPLANE) {
+ bnx2_write_phy(bp, BCM5708S_BLK_ADDR,
+ BCM5708S_BLK_ADDR_TX_MISC);
+ bnx2_write_phy(bp, BCM5708S_TX_ACTL3, val);
+ bnx2_write_phy(bp, BCM5708S_BLK_ADDR,
+ BCM5708S_BLK_ADDR_DIG);
+ }
+ }
+ return 0;
+}
+
+static int
+bnx2_init_5706s_phy(struct bnx2 *bp)
+{
+ u32 val;
+
+ bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG;
+
+ if (CHIP_NUM(bp) == CHIP_NUM_5706) {
+ REG_WR(bp, BNX2_MISC_UNUSED0, 0x300);
+ }
+
+
+ bnx2_write_phy(bp, 0x18, 0x7);
+ bnx2_read_phy(bp, 0x18, &val);
+ bnx2_write_phy(bp, 0x18, val & ~0x4007);
+
+ bnx2_write_phy(bp, 0x1c, 0x6c00);
+ bnx2_read_phy(bp, 0x1c, &val);
+ bnx2_write_phy(bp, 0x1c, (val & 0x3fd) | 0xec00);
+
+ return 0;
+}
+
+static int
+bnx2_init_copper_phy(struct bnx2 *bp)
+{
+ u32 val;
+
+ bp->phy_flags |= PHY_CRC_FIX_FLAG;
+
+ if (bp->phy_flags & PHY_CRC_FIX_FLAG) {
+ bnx2_write_phy(bp, 0x18, 0x0c00);
+ bnx2_write_phy(bp, 0x17, 0x000a);
+ bnx2_write_phy(bp, 0x15, 0x310b);
+ bnx2_write_phy(bp, 0x17, 0x201f);
+ bnx2_write_phy(bp, 0x15, 0x9506);
+ bnx2_write_phy(bp, 0x17, 0x401f);
+ bnx2_write_phy(bp, 0x15, 0x14e2);
+ bnx2_write_phy(bp, 0x18, 0x0400);
+ }
+
+ bnx2_write_phy(bp, 0x18, 0x7);
+ bnx2_read_phy(bp, 0x18, &val);
+ bnx2_write_phy(bp, 0x18, val & ~0x4007);
+
+ bnx2_read_phy(bp, 0x10, &val);
+ bnx2_write_phy(bp, 0x10, val & ~0x1);
+
+ /* ethernet@wirespeed */
+ bnx2_write_phy(bp, 0x18, 0x7007);
+ bnx2_read_phy(bp, 0x18, &val);
+ bnx2_write_phy(bp, 0x18, val | (1 << 15) | (1 << 4));
+ return 0;
+}
+
+static int
+bnx2_init_phy(struct bnx2 *bp)
+{
+ u32 val;
+ int rc = 0;
+
+ bp->phy_flags &= ~PHY_INT_MODE_MASK_FLAG;
+ bp->phy_flags |= PHY_INT_MODE_LINK_READY_FLAG;
+
+ REG_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK);
+
+ bnx2_reset_phy(bp);
+
+ bnx2_read_phy(bp, MII_PHYSID1, &val);
+ bp->phy_id = val << 16;
+ bnx2_read_phy(bp, MII_PHYSID2, &val);
+ bp->phy_id |= val & 0xffff;
+
+ if (bp->phy_flags & PHY_SERDES_FLAG) {
+ if (CHIP_NUM(bp) == CHIP_NUM_5706)
+ rc = bnx2_init_5706s_phy(bp);
+ else if (CHIP_NUM(bp) == CHIP_NUM_5708)
+ rc = bnx2_init_5708s_phy(bp);
+ }
+ else {
+ rc = bnx2_init_copper_phy(bp);
+ }
+
+ bnx2_setup_phy(bp);
+
+ return rc;
+}
+
+static int
+bnx2_fw_sync(struct bnx2 *bp, u32 msg_data, int silent)
+{
+ int i;
+ u32 val;
+
+ bp->fw_wr_seq++;
+ msg_data |= bp->fw_wr_seq;
+
+ REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_MB, msg_data);
+
+ /* wait for an acknowledgement. */
+ for (i = 0; i < (FW_ACK_TIME_OUT_MS / 50); i++) {
+ mdelay(50);
+
+ val = REG_RD_IND(bp, bp->shmem_base + BNX2_FW_MB);
+
+ if ((val & BNX2_FW_MSG_ACK) == (msg_data & BNX2_DRV_MSG_SEQ))
+ break;
+ }
+ if ((msg_data & BNX2_DRV_MSG_DATA) == BNX2_DRV_MSG_DATA_WAIT0)
+ return 0;
+
+ /* If we timed out, inform the firmware that this is the case. */
+ if ((val & BNX2_FW_MSG_ACK) != (msg_data & BNX2_DRV_MSG_SEQ)) {
+ if (!silent)
+ printf("fw sync timeout, reset code = %x\n", (unsigned int) msg_data);
+
+ msg_data &= ~BNX2_DRV_MSG_CODE;
+ msg_data |= BNX2_DRV_MSG_CODE_FW_TIMEOUT;
+
+ REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_MB, msg_data);
+
+ return -EBUSY;
+ }
+
+ if ((val & BNX2_FW_MSG_STATUS_MASK) != BNX2_FW_MSG_STATUS_OK)
+ return -EIO;
+
+ return 0;
+}
+
+static void
+bnx2_init_context(struct bnx2 *bp)
+{
+ u32 vcid;
+
+ vcid = 96;
+ while (vcid) {
+ u32 vcid_addr, pcid_addr, offset;
+
+ vcid--;
+
+ if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
+ u32 new_vcid;
+
+ vcid_addr = GET_PCID_ADDR(vcid);
+ if (vcid & 0x8) {
+ new_vcid = 0x60 + (vcid & 0xf0) + (vcid & 0x7);
+ }
+ else {
+ new_vcid = vcid;
+ }
+ pcid_addr = GET_PCID_ADDR(new_vcid);
+ }
+ else {
+ vcid_addr = GET_CID_ADDR(vcid);
+ pcid_addr = vcid_addr;
+ }
+
+ REG_WR(bp, BNX2_CTX_VIRT_ADDR, 0x00);
+ REG_WR(bp, BNX2_CTX_PAGE_TBL, pcid_addr);
+
+ /* Zero out the context. */
+ for (offset = 0; offset < PHY_CTX_SIZE; offset += 4) {
+ CTX_WR(bp, 0x00, offset, 0);
+ }
+
+ REG_WR(bp, BNX2_CTX_VIRT_ADDR, vcid_addr);
+ REG_WR(bp, BNX2_CTX_PAGE_TBL, pcid_addr);
+ }
+}
+
+static int
+bnx2_alloc_bad_rbuf(struct bnx2 *bp)
+{
+ u16 good_mbuf[512];
+ u32 good_mbuf_cnt;
+ u32 val;
+
+ REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS,
+ BNX2_MISC_ENABLE_SET_BITS_RX_MBUF_ENABLE);
+
+ good_mbuf_cnt = 0;
+
+ /* Allocate a bunch of mbufs and save the good ones in an array. */
+ val = REG_RD_IND(bp, BNX2_RBUF_STATUS1);
+ while (val & BNX2_RBUF_STATUS1_FREE_COUNT) {
+ REG_WR_IND(bp, BNX2_RBUF_COMMAND, BNX2_RBUF_COMMAND_ALLOC_REQ);
+
+ val = REG_RD_IND(bp, BNX2_RBUF_FW_BUF_ALLOC);
+
+ val &= BNX2_RBUF_FW_BUF_ALLOC_VALUE;
+
+ /* The addresses with Bit 9 set are bad memory blocks. */
+ if (!(val & (1 << 9))) {
+ good_mbuf[good_mbuf_cnt] = (u16) val;
+ good_mbuf_cnt++;
+ }
+
+ val = REG_RD_IND(bp, BNX2_RBUF_STATUS1);
+ }
+
+ /* Free the good ones back to the mbuf pool thus discarding
+ * all the bad ones. */
+ while (good_mbuf_cnt) {
+ good_mbuf_cnt--;
+
+ val = good_mbuf[good_mbuf_cnt];
+ val = (val << 9) | val | 1;
+
+ REG_WR_IND(bp, BNX2_RBUF_FW_BUF_FREE, val);
+ }
+ return 0;
+}
+
+static void
+bnx2_set_mac_addr(struct bnx2 *bp)
+{
+ u32 val;
+ u8 *mac_addr = bp->nic->node_addr;
+
+ val = (mac_addr[0] << 8) | mac_addr[1];
+
+ REG_WR(bp, BNX2_EMAC_MAC_MATCH0, val);
+
+ val = (mac_addr[2] << 24) | (mac_addr[3] << 16) |
+ (mac_addr[4] << 8) | mac_addr[5];
+
+ REG_WR(bp, BNX2_EMAC_MAC_MATCH1, val);
+}
+
+static void
+bnx2_set_rx_mode(struct nic *nic __unused)
+{
+ struct bnx2 *bp = &bnx2;
+ u32 rx_mode, sort_mode;
+ int i;
+
+ rx_mode = bp->rx_mode & ~(BNX2_EMAC_RX_MODE_PROMISCUOUS |
+ BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG);
+ sort_mode = 1 | BNX2_RPM_SORT_USER0_BC_EN;
+
+ if (!(bp->flags & ASF_ENABLE_FLAG)) {
+ rx_mode |= BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG;
+ }
+
+ /* Accept all multicasts */
+ for (i = 0; i < NUM_MC_HASH_REGISTERS; i++) {
+ REG_WR(bp, BNX2_EMAC_MULTICAST_HASH0 + (i * 4),
+ 0xffffffff);
+ }
+ sort_mode |= BNX2_RPM_SORT_USER0_MC_EN;
+
+ if (rx_mode != bp->rx_mode) {
+ bp->rx_mode = rx_mode;
+ REG_WR(bp, BNX2_EMAC_RX_MODE, rx_mode);
+ }
+
+ REG_WR(bp, BNX2_RPM_SORT_USER0, 0x0);
+ REG_WR(bp, BNX2_RPM_SORT_USER0, sort_mode);
+ REG_WR(bp, BNX2_RPM_SORT_USER0, sort_mode | BNX2_RPM_SORT_USER0_ENA);
+}
+
+static void
+load_rv2p_fw(struct bnx2 *bp, u32 *rv2p_code, u32 rv2p_code_len, u32 rv2p_proc)
+{
+ unsigned int i;
+ u32 val;
+
+
+ for (i = 0; i < rv2p_code_len; i += 8) {
+ REG_WR(bp, BNX2_RV2P_INSTR_HIGH, *rv2p_code);
+ rv2p_code++;
+ REG_WR(bp, BNX2_RV2P_INSTR_LOW, *rv2p_code);
+ rv2p_code++;
+
+ if (rv2p_proc == RV2P_PROC1) {
+ val = (i / 8) | BNX2_RV2P_PROC1_ADDR_CMD_RDWR;
+ REG_WR(bp, BNX2_RV2P_PROC1_ADDR_CMD, val);
+ }
+ else {
+ val = (i / 8) | BNX2_RV2P_PROC2_ADDR_CMD_RDWR;
+ REG_WR(bp, BNX2_RV2P_PROC2_ADDR_CMD, val);
+ }
+ }
+
+ /* Reset the processor, un-stall is done later. */
+ if (rv2p_proc == RV2P_PROC1) {
+ REG_WR(bp, BNX2_RV2P_COMMAND, BNX2_RV2P_COMMAND_PROC1_RESET);
+ }
+ else {
+ REG_WR(bp, BNX2_RV2P_COMMAND, BNX2_RV2P_COMMAND_PROC2_RESET);
+ }
+}
+
+static void
+load_cpu_fw(struct bnx2 *bp, struct cpu_reg *cpu_reg, struct fw_info *fw)
+{
+ u32 offset;
+ u32 val;
+
+ /* Halt the CPU. */
+ val = REG_RD_IND(bp, cpu_reg->mode);
+ val |= cpu_reg->mode_value_halt;
+ REG_WR_IND(bp, cpu_reg->mode, val);
+ REG_WR_IND(bp, cpu_reg->state, cpu_reg->state_value_clear);
+
+ /* Load the Text area. */
+ offset = cpu_reg->spad_base + (fw->text_addr - cpu_reg->mips_view_base);
+ if (fw->text) {
+ unsigned int j;
+
+ for (j = 0; j < (fw->text_len / 4); j++, offset += 4) {
+ REG_WR_IND(bp, offset, fw->text[j]);
+ }
+ }
+
+ /* Load the Data area. */
+ offset = cpu_reg->spad_base + (fw->data_addr - cpu_reg->mips_view_base);
+ if (fw->data) {
+ unsigned int j;
+
+ for (j = 0; j < (fw->data_len / 4); j++, offset += 4) {
+ REG_WR_IND(bp, offset, fw->data[j]);
+ }
+ }
+
+ /* Load the SBSS area. */
+ offset = cpu_reg->spad_base + (fw->sbss_addr - cpu_reg->mips_view_base);
+ if (fw->sbss) {
+ unsigned int j;
+
+ for (j = 0; j < (fw->sbss_len / 4); j++, offset += 4) {
+ REG_WR_IND(bp, offset, fw->sbss[j]);
+ }
+ }
+
+ /* Load the BSS area. */
+ offset = cpu_reg->spad_base + (fw->bss_addr - cpu_reg->mips_view_base);
+ if (fw->bss) {
+ unsigned int j;
+
+ for (j = 0; j < (fw->bss_len/4); j++, offset += 4) {
+ REG_WR_IND(bp, offset, fw->bss[j]);
+ }
+ }
+
+ /* Load the Read-Only area. */
+ offset = cpu_reg->spad_base +
+ (fw->rodata_addr - cpu_reg->mips_view_base);
+ if (fw->rodata) {
+ unsigned int j;
+
+ for (j = 0; j < (fw->rodata_len / 4); j++, offset += 4) {
+ REG_WR_IND(bp, offset, fw->rodata[j]);
+ }
+ }
+
+ /* Clear the pre-fetch instruction. */
+ REG_WR_IND(bp, cpu_reg->inst, 0);
+ REG_WR_IND(bp, cpu_reg->pc, fw->start_addr);
+
+ /* Start the CPU. */
+ val = REG_RD_IND(bp, cpu_reg->mode);
+ val &= ~cpu_reg->mode_value_halt;
+ REG_WR_IND(bp, cpu_reg->state, cpu_reg->state_value_clear);
+ REG_WR_IND(bp, cpu_reg->mode, val);
+}
+
+static void
+bnx2_init_cpus(struct bnx2 *bp)
+{
+ struct cpu_reg cpu_reg;
+ struct fw_info fw;
+
+ /* Unfortunately, it looks like we need to load the firmware
+ * before the card will work properly. That means this driver
+ * will be huge by Etherboot standards (approx. 50K compressed).
+ */
+
+ /* Initialize the RV2P processor. */
+ load_rv2p_fw(bp, bnx2_rv2p_proc1, sizeof(bnx2_rv2p_proc1), RV2P_PROC1);
+ load_rv2p_fw(bp, bnx2_rv2p_proc2, sizeof(bnx2_rv2p_proc2), RV2P_PROC2);
+
+ /* Initialize the RX Processor. */
+ cpu_reg.mode = BNX2_RXP_CPU_MODE;
+ cpu_reg.mode_value_halt = BNX2_RXP_CPU_MODE_SOFT_HALT;
+ cpu_reg.mode_value_sstep = BNX2_RXP_CPU_MODE_STEP_ENA;
+ cpu_reg.state = BNX2_RXP_CPU_STATE;
+ cpu_reg.state_value_clear = 0xffffff;
+ cpu_reg.gpr0 = BNX2_RXP_CPU_REG_FILE;
+ cpu_reg.evmask = BNX2_RXP_CPU_EVENT_MASK;
+ cpu_reg.pc = BNX2_RXP_CPU_PROGRAM_COUNTER;
+ cpu_reg.inst = BNX2_RXP_CPU_INSTRUCTION;
+ cpu_reg.bp = BNX2_RXP_CPU_HW_BREAKPOINT;
+ cpu_reg.spad_base = BNX2_RXP_SCRATCH;
+ cpu_reg.mips_view_base = 0x8000000;
+
+ fw.ver_major = bnx2_RXP_b06FwReleaseMajor;
+ fw.ver_minor = bnx2_RXP_b06FwReleaseMinor;
+ fw.ver_fix = bnx2_RXP_b06FwReleaseFix;
+ fw.start_addr = bnx2_RXP_b06FwStartAddr;
+
+ fw.text_addr = bnx2_RXP_b06FwTextAddr;
+ fw.text_len = bnx2_RXP_b06FwTextLen;
+ fw.text_index = 0;
+ fw.text = bnx2_RXP_b06FwText;
+
+ fw.data_addr = bnx2_RXP_b06FwDataAddr;
+ fw.data_len = bnx2_RXP_b06FwDataLen;
+ fw.data_index = 0;
+ fw.data = bnx2_RXP_b06FwData;
+
+ fw.sbss_addr = bnx2_RXP_b06FwSbssAddr;
+ fw.sbss_len = bnx2_RXP_b06FwSbssLen;
+ fw.sbss_index = 0;
+ fw.sbss = bnx2_RXP_b06FwSbss;
+
+ fw.bss_addr = bnx2_RXP_b06FwBssAddr;
+ fw.bss_len = bnx2_RXP_b06FwBssLen;
+ fw.bss_index = 0;
+ fw.bss = bnx2_RXP_b06FwBss;
+
+ fw.rodata_addr = bnx2_RXP_b06FwRodataAddr;
+ fw.rodata_len = bnx2_RXP_b06FwRodataLen;
+ fw.rodata_index = 0;
+ fw.rodata = bnx2_RXP_b06FwRodata;
+
+ load_cpu_fw(bp, &cpu_reg, &fw);
+
+ /* Initialize the TX Processor. */
+ cpu_reg.mode = BNX2_TXP_CPU_MODE;
+ cpu_reg.mode_value_halt = BNX2_TXP_CPU_MODE_SOFT_HALT;
+ cpu_reg.mode_value_sstep = BNX2_TXP_CPU_MODE_STEP_ENA;
+ cpu_reg.state = BNX2_TXP_CPU_STATE;
+ cpu_reg.state_value_clear = 0xffffff;
+ cpu_reg.gpr0 = BNX2_TXP_CPU_REG_FILE;
+ cpu_reg.evmask = BNX2_TXP_CPU_EVENT_MASK;
+ cpu_reg.pc = BNX2_TXP_CPU_PROGRAM_COUNTER;
+ cpu_reg.inst = BNX2_TXP_CPU_INSTRUCTION;
+ cpu_reg.bp = BNX2_TXP_CPU_HW_BREAKPOINT;
+ cpu_reg.spad_base = BNX2_TXP_SCRATCH;
+ cpu_reg.mips_view_base = 0x8000000;
+
+ fw.ver_major = bnx2_TXP_b06FwReleaseMajor;
+ fw.ver_minor = bnx2_TXP_b06FwReleaseMinor;
+ fw.ver_fix = bnx2_TXP_b06FwReleaseFix;
+ fw.start_addr = bnx2_TXP_b06FwStartAddr;
+
+ fw.text_addr = bnx2_TXP_b06FwTextAddr;
+ fw.text_len = bnx2_TXP_b06FwTextLen;
+ fw.text_index = 0;
+ fw.text = bnx2_TXP_b06FwText;
+
+ fw.data_addr = bnx2_TXP_b06FwDataAddr;
+ fw.data_len = bnx2_TXP_b06FwDataLen;
+ fw.data_index = 0;
+ fw.data = bnx2_TXP_b06FwData;
+
+ fw.sbss_addr = bnx2_TXP_b06FwSbssAddr;
+ fw.sbss_len = bnx2_TXP_b06FwSbssLen;
+ fw.sbss_index = 0;
+ fw.sbss = bnx2_TXP_b06FwSbss;
+
+ fw.bss_addr = bnx2_TXP_b06FwBssAddr;
+ fw.bss_len = bnx2_TXP_b06FwBssLen;
+ fw.bss_index = 0;
+ fw.bss = bnx2_TXP_b06FwBss;
+
+ fw.rodata_addr = bnx2_TXP_b06FwRodataAddr;
+ fw.rodata_len = bnx2_TXP_b06FwRodataLen;
+ fw.rodata_index = 0;
+ fw.rodata = bnx2_TXP_b06FwRodata;
+
+ load_cpu_fw(bp, &cpu_reg, &fw);
+
+ /* Initialize the TX Patch-up Processor. */
+ cpu_reg.mode = BNX2_TPAT_CPU_MODE;
+ cpu_reg.mode_value_halt = BNX2_TPAT_CPU_MODE_SOFT_HALT;
+ cpu_reg.mode_value_sstep = BNX2_TPAT_CPU_MODE_STEP_ENA;
+ cpu_reg.state = BNX2_TPAT_CPU_STATE;
+ cpu_reg.state_value_clear = 0xffffff;
+ cpu_reg.gpr0 = BNX2_TPAT_CPU_REG_FILE;
+ cpu_reg.evmask = BNX2_TPAT_CPU_EVENT_MASK;
+ cpu_reg.pc = BNX2_TPAT_CPU_PROGRAM_COUNTER;
+ cpu_reg.inst = BNX2_TPAT_CPU_INSTRUCTION;
+ cpu_reg.bp = BNX2_TPAT_CPU_HW_BREAKPOINT;
+ cpu_reg.spad_base = BNX2_TPAT_SCRATCH;
+ cpu_reg.mips_view_base = 0x8000000;
+
+ fw.ver_major = bnx2_TPAT_b06FwReleaseMajor;
+ fw.ver_minor = bnx2_TPAT_b06FwReleaseMinor;
+ fw.ver_fix = bnx2_TPAT_b06FwReleaseFix;
+ fw.start_addr = bnx2_TPAT_b06FwStartAddr;
+
+ fw.text_addr = bnx2_TPAT_b06FwTextAddr;
+ fw.text_len = bnx2_TPAT_b06FwTextLen;
+ fw.text_index = 0;
+ fw.text = bnx2_TPAT_b06FwText;
+
+ fw.data_addr = bnx2_TPAT_b06FwDataAddr;
+ fw.data_len = bnx2_TPAT_b06FwDataLen;
+ fw.data_index = 0;
+ fw.data = bnx2_TPAT_b06FwData;
+
+ fw.sbss_addr = bnx2_TPAT_b06FwSbssAddr;
+ fw.sbss_len = bnx2_TPAT_b06FwSbssLen;
+ fw.sbss_index = 0;
+ fw.sbss = bnx2_TPAT_b06FwSbss;
+
+ fw.bss_addr = bnx2_TPAT_b06FwBssAddr;
+ fw.bss_len = bnx2_TPAT_b06FwBssLen;
+ fw.bss_index = 0;
+ fw.bss = bnx2_TPAT_b06FwBss;
+
+ fw.rodata_addr = bnx2_TPAT_b06FwRodataAddr;
+ fw.rodata_len = bnx2_TPAT_b06FwRodataLen;
+ fw.rodata_index = 0;
+ fw.rodata = bnx2_TPAT_b06FwRodata;
+
+ load_cpu_fw(bp, &cpu_reg, &fw);
+
+ /* Initialize the Completion Processor. */
+ cpu_reg.mode = BNX2_COM_CPU_MODE;
+ cpu_reg.mode_value_halt = BNX2_COM_CPU_MODE_SOFT_HALT;
+ cpu_reg.mode_value_sstep = BNX2_COM_CPU_MODE_STEP_ENA;
+ cpu_reg.state = BNX2_COM_CPU_STATE;
+ cpu_reg.state_value_clear = 0xffffff;
+ cpu_reg.gpr0 = BNX2_COM_CPU_REG_FILE;
+ cpu_reg.evmask = BNX2_COM_CPU_EVENT_MASK;
+ cpu_reg.pc = BNX2_COM_CPU_PROGRAM_COUNTER;
+ cpu_reg.inst = BNX2_COM_CPU_INSTRUCTION;
+ cpu_reg.bp = BNX2_COM_CPU_HW_BREAKPOINT;
+ cpu_reg.spad_base = BNX2_COM_SCRATCH;
+ cpu_reg.mips_view_base = 0x8000000;
+
+ fw.ver_major = bnx2_COM_b06FwReleaseMajor;
+ fw.ver_minor = bnx2_COM_b06FwReleaseMinor;
+ fw.ver_fix = bnx2_COM_b06FwReleaseFix;
+ fw.start_addr = bnx2_COM_b06FwStartAddr;
+
+ fw.text_addr = bnx2_COM_b06FwTextAddr;
+ fw.text_len = bnx2_COM_b06FwTextLen;
+ fw.text_index = 0;
+ fw.text = bnx2_COM_b06FwText;
+
+ fw.data_addr = bnx2_COM_b06FwDataAddr;
+ fw.data_len = bnx2_COM_b06FwDataLen;
+ fw.data_index = 0;
+ fw.data = bnx2_COM_b06FwData;
+
+ fw.sbss_addr = bnx2_COM_b06FwSbssAddr;
+ fw.sbss_len = bnx2_COM_b06FwSbssLen;
+ fw.sbss_index = 0;
+ fw.sbss = bnx2_COM_b06FwSbss;
+
+ fw.bss_addr = bnx2_COM_b06FwBssAddr;
+ fw.bss_len = bnx2_COM_b06FwBssLen;
+ fw.bss_index = 0;
+ fw.bss = bnx2_COM_b06FwBss;
+
+ fw.rodata_addr = bnx2_COM_b06FwRodataAddr;
+ fw.rodata_len = bnx2_COM_b06FwRodataLen;
+ fw.rodata_index = 0;
+ fw.rodata = bnx2_COM_b06FwRodata;
+
+ load_cpu_fw(bp, &cpu_reg, &fw);
+
+}
+
+static int
+bnx2_set_power_state_0(struct bnx2 *bp)
+{
+ u16 pmcsr;
+ u32 val;
+
+ pci_read_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, &pmcsr);
+
+ pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL,
+ (pmcsr & ~PCI_PM_CTRL_STATE_MASK) |
+ PCI_PM_CTRL_PME_STATUS);
+
+ if (pmcsr & PCI_PM_CTRL_STATE_MASK)
+ /* delay required during transition out of D3hot */
+ mdelay(20);
+
+ val = REG_RD(bp, BNX2_EMAC_MODE);
+ val |= BNX2_EMAC_MODE_MPKT_RCVD | BNX2_EMAC_MODE_ACPI_RCVD;
+ val &= ~BNX2_EMAC_MODE_MPKT;
+ REG_WR(bp, BNX2_EMAC_MODE, val);
+
+ val = REG_RD(bp, BNX2_RPM_CONFIG);
+ val &= ~BNX2_RPM_CONFIG_ACPI_ENA;
+ REG_WR(bp, BNX2_RPM_CONFIG, val);
+
+ return 0;
+}
+
+static void
+bnx2_enable_nvram_access(struct bnx2 *bp)
+{
+ u32 val;
+
+ val = REG_RD(bp, BNX2_NVM_ACCESS_ENABLE);
+ /* Enable both bits, even on read. */
+ REG_WR(bp, BNX2_NVM_ACCESS_ENABLE,
+ val | BNX2_NVM_ACCESS_ENABLE_EN | BNX2_NVM_ACCESS_ENABLE_WR_EN);
+}
+
+static void
+bnx2_disable_nvram_access(struct bnx2 *bp)
+{
+ u32 val;
+
+ val = REG_RD(bp, BNX2_NVM_ACCESS_ENABLE);
+ /* Disable both bits, even after read. */
+ REG_WR(bp, BNX2_NVM_ACCESS_ENABLE,
+ val & ~(BNX2_NVM_ACCESS_ENABLE_EN |
+ BNX2_NVM_ACCESS_ENABLE_WR_EN));
+}
+
+static int
+bnx2_init_nvram(struct bnx2 *bp)
+{
+ u32 val;
+ int j, entry_count, rc;
+ struct flash_spec *flash;
+
+ /* Determine the selected interface. */
+ val = REG_RD(bp, BNX2_NVM_CFG1);
+
+ entry_count = sizeof(flash_table) / sizeof(struct flash_spec);
+
+ rc = 0;
+ if (val & 0x40000000) {
+ /* Flash interface has been reconfigured */
+ for (j = 0, flash = &flash_table[0]; j < entry_count;
+ j++, flash++) {
+ if ((val & FLASH_BACKUP_STRAP_MASK) ==
+ (flash->config1 & FLASH_BACKUP_STRAP_MASK)) {
+ bp->flash_info = flash;
+ break;
+ }
+ }
+ }
+ else {
+ u32 mask;
+ /* Not yet been reconfigured */
+
+ if (val & (1 << 23))
+ mask = FLASH_BACKUP_STRAP_MASK;
+ else
+ mask = FLASH_STRAP_MASK;
+
+ for (j = 0, flash = &flash_table[0]; j < entry_count;
+ j++, flash++) {
+
+ if ((val & mask) == (flash->strapping & mask)) {
+ bp->flash_info = flash;
+
+ /* Enable access to flash interface */
+ bnx2_enable_nvram_access(bp);
+
+ /* Reconfigure the flash interface */
+ REG_WR(bp, BNX2_NVM_CFG1, flash->config1);
+ REG_WR(bp, BNX2_NVM_CFG2, flash->config2);
+ REG_WR(bp, BNX2_NVM_CFG3, flash->config3);
+ REG_WR(bp, BNX2_NVM_WRITE1, flash->write1);
+
+ /* Disable access to flash interface */
+ bnx2_disable_nvram_access(bp);
+
+ break;
+ }
+ }
+ } /* if (val & 0x40000000) */
+
+ if (j == entry_count) {
+ bp->flash_info = NULL;
+ printf("Unknown flash/EEPROM type.\n");
+ return -ENODEV;
+ }
+
+ val = REG_RD_IND(bp, bp->shmem_base + BNX2_SHARED_HW_CFG_CONFIG2);
+ val &= BNX2_SHARED_HW_CFG2_NVM_SIZE_MASK;
+ if (val) {
+ bp->flash_size = val;
+ }
+ else {
+ bp->flash_size = bp->flash_info->total_size;
+ }
+
+ return rc;
+}
+
+static int
+bnx2_reset_chip(struct bnx2 *bp, u32 reset_code)
+{
+ u32 val;
+ int i, rc = 0;
+
+ /* Wait for the current PCI transaction to complete before
+ * issuing a reset. */
+ REG_WR(bp, BNX2_MISC_ENABLE_CLR_BITS,
+ BNX2_MISC_ENABLE_CLR_BITS_TX_DMA_ENABLE |
+ BNX2_MISC_ENABLE_CLR_BITS_DMA_ENGINE_ENABLE |
+ BNX2_MISC_ENABLE_CLR_BITS_RX_DMA_ENABLE |
+ BNX2_MISC_ENABLE_CLR_BITS_HOST_COALESCE_ENABLE);
+ val = REG_RD(bp, BNX2_MISC_ENABLE_CLR_BITS);
+ udelay(5);
+
+
+ /* Wait for the firmware to tell us it is ok to issue a reset. */
+ bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT0 | reset_code, 1);
+
+ /* Deposit a driver reset signature so the firmware knows that
+ * this is a soft reset. */
+ REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_RESET_SIGNATURE,
+ BNX2_DRV_RESET_SIGNATURE_MAGIC);
+
+ /* Do a dummy read to force the chip to complete all current transaction
+ * before we issue a reset. */
+ val = REG_RD(bp, BNX2_MISC_ID);
+
+ val = BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ |
+ BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA |
+ BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP;
+
+ /* Chip reset. */
+ REG_WR(bp, BNX2_PCICFG_MISC_CONFIG, val);
+
+ if ((CHIP_ID(bp) == CHIP_ID_5706_A0) ||
+ (CHIP_ID(bp) == CHIP_ID_5706_A1))
+ mdelay(15);
+
+ /* Reset takes approximate 30 usec */
+ for (i = 0; i < 10; i++) {
+ val = REG_RD(bp, BNX2_PCICFG_MISC_CONFIG);
+ if ((val & (BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ |
+ BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY)) == 0) {
+ break;
+ }
+ udelay(10);
+ }
+
+ if (val & (BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ |
+ BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY)) {
+ printf("Chip reset did not complete\n");
+ return -EBUSY;
+ }
+
+ /* Make sure byte swapping is properly configured. */
+ val = REG_RD(bp, BNX2_PCI_SWAP_DIAG0);
+ if (val != 0x01020304) {
+ printf("Chip not in correct endian mode\n");
+ return -ENODEV;
+ }
+
+ /* Wait for the firmware to finish its initialization. */
+ rc = bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT1 | reset_code, 0);
+ if (rc) {
+ return rc;
+ }
+
+ if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
+ /* Adjust the voltage regular to two steps lower. The default
+ * of this register is 0x0000000e. */
+ REG_WR(bp, BNX2_MISC_VREG_CONTROL, 0x000000fa);
+
+ /* Remove bad rbuf memory from the free pool. */
+ rc = bnx2_alloc_bad_rbuf(bp);
+ }
+
+ return rc;
+}
+
+static void
+bnx2_disable(struct nic *nic __unused)
+{
+ struct bnx2* bp = &bnx2;
+
+ if (bp->regview) {
+ bnx2_reset_chip(bp, BNX2_DRV_MSG_CODE_UNLOAD);
+ iounmap(bp->regview);
+ }
+}
+
+static int
+bnx2_init_chip(struct bnx2 *bp)
+{
+ u32 val;
+ int rc;
+
+ /* Make sure the interrupt is not active. */
+ REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
+
+ val = BNX2_DMA_CONFIG_DATA_BYTE_SWAP |
+ BNX2_DMA_CONFIG_DATA_WORD_SWAP |
+#if __BYTE_ORDER == __BIG_ENDIAN
+ BNX2_DMA_CONFIG_CNTL_BYTE_SWAP |
+#endif
+ BNX2_DMA_CONFIG_CNTL_WORD_SWAP |
+ DMA_READ_CHANS << 12 |
+ DMA_WRITE_CHANS << 16;
+
+ val |= (0x2 << 20) | (1 << 11);
+
+ if ((bp->flags & PCIX_FLAG) && (bp->bus_speed_mhz == 133))
+ val |= (1 << 23);
+
+ if ((CHIP_NUM(bp) == CHIP_NUM_5706) &&
+ (CHIP_ID(bp) != CHIP_ID_5706_A0) && !(bp->flags & PCIX_FLAG))
+ val |= BNX2_DMA_CONFIG_CNTL_PING_PONG_DMA;
+
+ REG_WR(bp, BNX2_DMA_CONFIG, val);
+
+ if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
+ val = REG_RD(bp, BNX2_TDMA_CONFIG);
+ val |= BNX2_TDMA_CONFIG_ONE_DMA;
+ REG_WR(bp, BNX2_TDMA_CONFIG, val);
+ }
+
+ if (bp->flags & PCIX_FLAG) {
+ u16 val16;
+
+ pci_read_config_word(bp->pdev, bp->pcix_cap + PCI_X_CMD,
+ &val16);
+ pci_write_config_word(bp->pdev, bp->pcix_cap + PCI_X_CMD,
+ val16 & ~PCI_X_CMD_ERO);
+ }
+
+ REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS,
+ BNX2_MISC_ENABLE_SET_BITS_HOST_COALESCE_ENABLE |
+ BNX2_MISC_ENABLE_STATUS_BITS_RX_V2P_ENABLE |
+ BNX2_MISC_ENABLE_STATUS_BITS_CONTEXT_ENABLE);
+
+ /* Initialize context mapping and zero out the quick contexts. The
+ * context block must have already been enabled. */
+ bnx2_init_context(bp);
+
+ bnx2_init_nvram(bp);
+ bnx2_init_cpus(bp);
+
+ bnx2_set_mac_addr(bp);
+
+ val = REG_RD(bp, BNX2_MQ_CONFIG);
+ val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE;
+ val |= BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_256;
+ REG_WR(bp, BNX2_MQ_CONFIG, val);
+
+ val = 0x10000 + (MAX_CID_CNT * MB_KERNEL_CTX_SIZE);
+ REG_WR(bp, BNX2_MQ_KNL_BYP_WIND_START, val);
+ REG_WR(bp, BNX2_MQ_KNL_WIND_END, val);
+
+ val = (BCM_PAGE_BITS - 8) << 24;
+ REG_WR(bp, BNX2_RV2P_CONFIG, val);
+
+ /* Configure page size. */
+ val = REG_RD(bp, BNX2_TBDR_CONFIG);
+ val &= ~BNX2_TBDR_CONFIG_PAGE_SIZE;
+ val |= (BCM_PAGE_BITS - 8) << 24 | 0x40;
+ REG_WR(bp, BNX2_TBDR_CONFIG, val);
+
+ val = bp->mac_addr[0] +
+ (bp->mac_addr[1] << 8) +
+ (bp->mac_addr[2] << 16) +
+ bp->mac_addr[3] +
+ (bp->mac_addr[4] << 8) +
+ (bp->mac_addr[5] << 16);
+ REG_WR(bp, BNX2_EMAC_BACKOFF_SEED, val);
+
+ /* Program the MTU. Also include 4 bytes for CRC32. */
+ val = ETH_MAX_MTU + ETH_HLEN + 4;
+ if (val > (MAX_ETHERNET_PACKET_SIZE + 4))
+ val |= BNX2_EMAC_RX_MTU_SIZE_JUMBO_ENA;
+ REG_WR(bp, BNX2_EMAC_RX_MTU_SIZE, val);
+
+ bp->last_status_idx = 0;
+ bp->rx_mode = BNX2_EMAC_RX_MODE_SORT_MODE;
+
+ /* Set up how to generate a link change interrupt. */
+ REG_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK);
+
+ REG_WR(bp, BNX2_HC_STATUS_ADDR_L,
+ (u64) bp->status_blk_mapping & 0xffffffff);
+ REG_WR(bp, BNX2_HC_STATUS_ADDR_H, (u64) bp->status_blk_mapping >> 32);
+
+ REG_WR(bp, BNX2_HC_STATISTICS_ADDR_L,
+ (u64) bp->stats_blk_mapping & 0xffffffff);
+ REG_WR(bp, BNX2_HC_STATISTICS_ADDR_H,
+ (u64) bp->stats_blk_mapping >> 32);
+
+ REG_WR(bp, BNX2_HC_TX_QUICK_CONS_TRIP,
+ (bp->tx_quick_cons_trip_int << 16) | bp->tx_quick_cons_trip);
+
+ REG_WR(bp, BNX2_HC_RX_QUICK_CONS_TRIP,
+ (bp->rx_quick_cons_trip_int << 16) | bp->rx_quick_cons_trip);
+
+ REG_WR(bp, BNX2_HC_COMP_PROD_TRIP,
+ (bp->comp_prod_trip_int << 16) | bp->comp_prod_trip);
+
+ REG_WR(bp, BNX2_HC_TX_TICKS, (bp->tx_ticks_int << 16) | bp->tx_ticks);
+
+ REG_WR(bp, BNX2_HC_RX_TICKS, (bp->rx_ticks_int << 16) | bp->rx_ticks);
+
+ REG_WR(bp, BNX2_HC_COM_TICKS,
+ (bp->com_ticks_int << 16) | bp->com_ticks);
+
+ REG_WR(bp, BNX2_HC_CMD_TICKS,
+ (bp->cmd_ticks_int << 16) | bp->cmd_ticks);
+
+ REG_WR(bp, BNX2_HC_STATS_TICKS, bp->stats_ticks & 0xffff00);
+ REG_WR(bp, BNX2_HC_STAT_COLLECT_TICKS, 0xbb8); /* 3ms */
+
+ if (CHIP_ID(bp) == CHIP_ID_5706_A1)
+ REG_WR(bp, BNX2_HC_CONFIG, BNX2_HC_CONFIG_COLLECT_STATS);
+ else {
+ REG_WR(bp, BNX2_HC_CONFIG, BNX2_HC_CONFIG_RX_TMR_MODE |
+ BNX2_HC_CONFIG_TX_TMR_MODE |
+ BNX2_HC_CONFIG_COLLECT_STATS);
+ }
+
+ /* Clear internal stats counters. */
+ REG_WR(bp, BNX2_HC_COMMAND, BNX2_HC_COMMAND_CLR_STAT_NOW);
+
+ REG_WR(bp, BNX2_HC_ATTN_BITS_ENABLE, STATUS_ATTN_BITS_LINK_STATE);
+
+ if (REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_FEATURE) &
+ BNX2_PORT_FEATURE_ASF_ENABLED)
+ bp->flags |= ASF_ENABLE_FLAG;
+
+ /* Initialize the receive filter. */
+ bnx2_set_rx_mode(bp->nic);
+
+ rc = bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT2 | BNX2_DRV_MSG_CODE_RESET,
+ 0);
+
+ REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS, 0x5ffffff);
+ REG_RD(bp, BNX2_MISC_ENABLE_SET_BITS);
+
+ udelay(20);
+
+ bp->hc_cmd = REG_RD(bp, BNX2_HC_COMMAND);
+
+ return rc;
+}
+
+static void
+bnx2_init_tx_ring(struct bnx2 *bp)
+{
+ struct tx_bd *txbd;
+ u32 val;
+
+ txbd = &bp->tx_desc_ring[MAX_TX_DESC_CNT];
+
+ /* Etherboot lives below 4GB, so hi is always 0 */
+ txbd->tx_bd_haddr_hi = 0;
+ txbd->tx_bd_haddr_lo = bp->tx_desc_mapping;
+
+ bp->tx_prod = 0;
+ bp->tx_cons = 0;
+ bp->hw_tx_cons = 0;
+ bp->tx_prod_bseq = 0;
+
+ val = BNX2_L2CTX_TYPE_TYPE_L2;
+ val |= BNX2_L2CTX_TYPE_SIZE_L2;
+ CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TYPE, val);
+
+ val = BNX2_L2CTX_CMD_TYPE_TYPE_L2;
+ val |= 8 << 16;
+ CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_CMD_TYPE, val);
+
+ /* Etherboot lives below 4GB, so hi is always 0 */
+ CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TBDR_BHADDR_HI, 0);
+
+ val = (u64) bp->tx_desc_mapping & 0xffffffff;
+ CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TBDR_BHADDR_LO, val);
+}
+
+static void
+bnx2_init_rx_ring(struct bnx2 *bp)
+{
+ struct rx_bd *rxbd;
+ unsigned int i;
+ u16 prod, ring_prod;
+ u32 val;
+
+ bp->rx_buf_use_size = RX_BUF_USE_SIZE;
+ bp->rx_buf_size = RX_BUF_SIZE;
+
+ ring_prod = prod = bp->rx_prod = 0;
+ bp->rx_cons = 0;
+ bp->hw_rx_cons = 0;
+ bp->rx_prod_bseq = 0;
+
+ memset(bnx2_bss.rx_buf, 0, sizeof(bnx2_bss.rx_buf));
+
+ rxbd = &bp->rx_desc_ring[0];
+ for (i = 0; i < MAX_RX_DESC_CNT; i++, rxbd++) {
+ rxbd->rx_bd_len = bp->rx_buf_use_size;
+ rxbd->rx_bd_flags = RX_BD_FLAGS_START | RX_BD_FLAGS_END;
+ }
+ rxbd->rx_bd_haddr_hi = 0;
+ rxbd->rx_bd_haddr_lo = (u64) bp->rx_desc_mapping & 0xffffffff;
+
+ val = BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE;
+ val |= BNX2_L2CTX_CTX_TYPE_SIZE_L2;
+ val |= 0x02 << 8;
+ CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_CTX_TYPE, val);
+
+ /* Etherboot doesn't use memory above 4GB, so this is always 0 */
+ CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_NX_BDHADDR_HI, 0);
+
+ val = bp->rx_desc_mapping & 0xffffffff;
+ CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_NX_BDHADDR_LO, val);
+
+ for (i = 0; (int) i < bp->rx_ring_size; i++) {
+ rxbd = &bp->rx_desc_ring[RX_RING_IDX(ring_prod)];
+ rxbd->rx_bd_haddr_hi = 0;
+ rxbd->rx_bd_haddr_lo = virt_to_bus(&bnx2_bss.rx_buf[ring_prod][0]);
+ bp->rx_prod_bseq += bp->rx_buf_use_size;
+ prod = NEXT_RX_BD(prod);
+ ring_prod = RX_RING_IDX(prod);
+ }
+ bp->rx_prod = prod;
+
+ REG_WR16(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BDIDX, bp->rx_prod);
+
+ REG_WR(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BSEQ, bp->rx_prod_bseq);
+}
+
+static int
+bnx2_reset_nic(struct bnx2 *bp, u32 reset_code)
+{
+ int rc;
+
+ rc = bnx2_reset_chip(bp, reset_code);
+ if (rc) {
+ return rc;
+ }
+
+ bnx2_init_chip(bp);
+ bnx2_init_tx_ring(bp);
+ bnx2_init_rx_ring(bp);
+ return 0;
+}
+
+static int
+bnx2_init_nic(struct bnx2 *bp)
+{
+ int rc;
+
+ if ((rc = bnx2_reset_nic(bp, BNX2_DRV_MSG_CODE_RESET)) != 0)
+ return rc;
+
+ bnx2_init_phy(bp);
+ bnx2_set_link(bp);
+ return 0;
+}
+
+static int
+bnx2_init_board(struct pci_device *pdev, struct nic *nic)
+{
+ unsigned long bnx2reg_base, bnx2reg_len;
+ struct bnx2 *bp = &bnx2;
+ int rc;
+ u32 reg;
+
+ bp->flags = 0;
+ bp->phy_flags = 0;
+
+ /* enable device (incl. PCI PM wakeup), and bus-mastering */
+ adjust_pci_device(pdev);
+
+ nic->ioaddr = pdev->ioaddr & ~3;
+ nic->irqno = 0;
+
+ rc = 0;
+ bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (bp->pm_cap == 0) {
+ printf("Cannot find power management capability, aborting.\n");
+ rc = -EIO;
+ goto err_out_disable;
+ }
+
+ bp->pcix_cap = pci_find_capability(pdev, PCI_CAP_ID_PCIX);
+ if (bp->pcix_cap == 0) {
+ printf("Cannot find PCIX capability, aborting.\n");
+ rc = -EIO;
+ goto err_out_disable;
+ }
+
+ bp->pdev = pdev;
+ bp->nic = nic;
+
+ bnx2reg_base = pci_bar_start(pdev, PCI_BASE_ADDRESS_0);
+ bnx2reg_len = MB_GET_CID_ADDR(17);
+
+ bp->regview = ioremap(bnx2reg_base, bnx2reg_len);
+
+ if (!bp->regview) {
+ printf("Cannot map register space, aborting.\n");
+ rc = -EIO;
+ goto err_out_disable;
+ }
+
+ /* Configure byte swap and enable write to the reg_window registers.
+ * Rely on CPU to do target byte swapping on big endian systems
+ * The chip's target access swapping will not swap all accesses
+ */
+ pci_write_config_dword(bp->pdev, BNX2_PCICFG_MISC_CONFIG,
+ BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA |
+ BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP);
+
+ bnx2_set_power_state_0(bp);
+
+ bp->chip_id = REG_RD(bp, BNX2_MISC_ID);
+
+ /* Get bus information. */
+ reg = REG_RD(bp, BNX2_PCICFG_MISC_STATUS);
+ if (reg & BNX2_PCICFG_MISC_STATUS_PCIX_DET) {
+ u32 clkreg;
+
+ bp->flags |= PCIX_FLAG;
+
+ clkreg = REG_RD(bp, BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS);
+
+ clkreg &= BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET;
+ switch (clkreg) {
+ case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ:
+ bp->bus_speed_mhz = 133;
+ break;
+
+ case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ:
+ bp->bus_speed_mhz = 100;
+ break;
+
+ case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ:
+ case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ:
+ bp->bus_speed_mhz = 66;
+ break;
+
+ case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ:
+ case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ:
+ bp->bus_speed_mhz = 50;
+ break;
+
+ case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW:
+ case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ:
+ case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ:
+ bp->bus_speed_mhz = 33;
+ break;
+ }
+ }
+ else {
+ if (reg & BNX2_PCICFG_MISC_STATUS_M66EN)
+ bp->bus_speed_mhz = 66;
+ else
+ bp->bus_speed_mhz = 33;
+ }
+
+ if (reg & BNX2_PCICFG_MISC_STATUS_32BIT_DET)
+ bp->flags |= PCI_32BIT_FLAG;
+
+ /* 5706A0 may falsely detect SERR and PERR. */
+ if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
+ reg = REG_RD(bp, PCI_COMMAND);
+ reg &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+ REG_WR(bp, PCI_COMMAND, reg);
+ }
+ else if ((CHIP_ID(bp) == CHIP_ID_5706_A1) &&
+ !(bp->flags & PCIX_FLAG)) {
+
+ printf("5706 A1 can only be used in a PCIX bus, aborting.\n");
+ goto err_out_disable;
+ }
+
+ bnx2_init_nvram(bp);
+
+ reg = REG_RD_IND(bp, BNX2_SHM_HDR_SIGNATURE);
+
+ if ((reg & BNX2_SHM_HDR_SIGNATURE_SIG_MASK) ==
+ BNX2_SHM_HDR_SIGNATURE_SIG)
+ bp->shmem_base = REG_RD_IND(bp, BNX2_SHM_HDR_ADDR_0);
+ else
+ bp->shmem_base = HOST_VIEW_SHMEM_BASE;
+
+ /* Get the permanent MAC address. First we need to make sure the
+ * firmware is actually running.
+ */
+ reg = REG_RD_IND(bp, bp->shmem_base + BNX2_DEV_INFO_SIGNATURE);
+
+ if ((reg & BNX2_DEV_INFO_SIGNATURE_MAGIC_MASK) !=
+ BNX2_DEV_INFO_SIGNATURE_MAGIC) {
+ printf("Firmware not running, aborting.\n");
+ rc = -ENODEV;
+ goto err_out_disable;
+ }
+
+ bp->fw_ver = REG_RD_IND(bp, bp->shmem_base + BNX2_DEV_INFO_BC_REV);
+
+ reg = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_MAC_UPPER);
+ bp->mac_addr[0] = (u8) (reg >> 8);
+ bp->mac_addr[1] = (u8) reg;
+
+ reg = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_MAC_LOWER);
+ bp->mac_addr[2] = (u8) (reg >> 24);
+ bp->mac_addr[3] = (u8) (reg >> 16);
+ bp->mac_addr[4] = (u8) (reg >> 8);
+ bp->mac_addr[5] = (u8) reg;
+
+ bp->tx_ring_size = MAX_TX_DESC_CNT;
+ bp->rx_ring_size = RX_BUF_CNT;
+ bp->rx_max_ring_idx = MAX_RX_DESC_CNT;
+
+ bp->rx_offset = RX_OFFSET;
+
+ bp->tx_quick_cons_trip_int = 20;
+ bp->tx_quick_cons_trip = 20;
+ bp->tx_ticks_int = 80;
+ bp->tx_ticks = 80;
+
+ bp->rx_quick_cons_trip_int = 6;
+ bp->rx_quick_cons_trip = 6;
+ bp->rx_ticks_int = 18;
+ bp->rx_ticks = 18;
+
+ bp->stats_ticks = 1000000 & 0xffff00;
+
+ bp->phy_addr = 1;
+
+ /* No need for WOL support in Etherboot */
+ bp->flags |= NO_WOL_FLAG;
+
+ /* Disable WOL support if we are running on a SERDES chip. */
+ if (CHIP_BOND_ID(bp) & CHIP_BOND_ID_SERDES_BIT) {
+ bp->phy_flags |= PHY_SERDES_FLAG;
+ if (CHIP_NUM(bp) == CHIP_NUM_5708) {
+ bp->phy_addr = 2;
+ reg = REG_RD_IND(bp, bp->shmem_base +
+ BNX2_SHARED_HW_CFG_CONFIG);
+ if (reg & BNX2_SHARED_HW_CFG_PHY_2_5G)
+ bp->phy_flags |= PHY_2_5G_CAPABLE_FLAG;
+ }
+ }
+
+ if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
+ bp->tx_quick_cons_trip_int =
+ bp->tx_quick_cons_trip;
+ bp->tx_ticks_int = bp->tx_ticks;
+ bp->rx_quick_cons_trip_int =
+ bp->rx_quick_cons_trip;
+ bp->rx_ticks_int = bp->rx_ticks;
+ bp->comp_prod_trip_int = bp->comp_prod_trip;
+ bp->com_ticks_int = bp->com_ticks;
+ bp->cmd_ticks_int = bp->cmd_ticks;
+ }
+
+ bp->autoneg = AUTONEG_SPEED | AUTONEG_FLOW_CTRL;
+ bp->req_line_speed = 0;
+ if (bp->phy_flags & PHY_SERDES_FLAG) {
+ bp->advertising = ETHTOOL_ALL_FIBRE_SPEED | ADVERTISED_Autoneg;
+
+ reg = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_CONFIG);
+ reg &= BNX2_PORT_HW_CFG_CFG_DFLT_LINK_MASK;
+ if (reg == BNX2_PORT_HW_CFG_CFG_DFLT_LINK_1G) {
+ bp->autoneg = 0;
+ bp->req_line_speed = bp->line_speed = SPEED_1000;
+ bp->req_duplex = DUPLEX_FULL;
+ }
+ }
+ else {
+ bp->advertising = ETHTOOL_ALL_COPPER_SPEED | ADVERTISED_Autoneg;
+ }
+
+ bp->req_flow_ctrl = FLOW_CTRL_RX | FLOW_CTRL_TX;
+
+ /* Disable driver heartbeat checking */
+ REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_PULSE_MB,
+ BNX2_DRV_MSG_DATA_PULSE_CODE_ALWAYS_ALIVE);
+ REG_RD_IND(bp, bp->shmem_base + BNX2_DRV_PULSE_MB);
+
+ return 0;
+
+err_out_disable:
+ bnx2_disable(nic);
+
+ return rc;
+}
+
+static void
+bnx2_transmit(struct nic *nic, const char *dst_addr,
+ unsigned int type, unsigned int size, const char *packet)
+{
+ /* Sometimes the nic will be behind by a frame. Using two transmit
+ * buffers prevents us from timing out in that case.
+ */
+ static struct eth_frame {
+ uint8_t dst_addr[ETH_ALEN];
+ uint8_t src_addr[ETH_ALEN];
+ uint16_t type;
+ uint8_t data [ETH_FRAME_LEN - ETH_HLEN];
+ } frame[2];
+ static int frame_idx = 0;
+
+ /* send the packet to destination */
+ struct tx_bd *txbd;
+ struct bnx2 *bp = &bnx2;
+ u16 prod, ring_prod;
+ u16 hw_cons;
+ int i = 0;
+
+ prod = bp->tx_prod;
+ ring_prod = TX_RING_IDX(prod);
+ hw_cons = bp->status_blk->status_tx_quick_consumer_index0;
+ if ((hw_cons & MAX_TX_DESC_CNT) == MAX_TX_DESC_CNT) {
+ hw_cons++;
+ }
+
+ while((hw_cons != prod) && (hw_cons != (PREV_TX_BD(prod)))) {
+ mdelay(10); /* give the nic a chance */
+ //poll_interruptions();
+ if (++i > 500) { /* timeout 5s for transmit */
+ printf("transmit timed out\n");
+ bnx2_disable(bp->nic);
+ bnx2_init_board(bp->pdev, bp->nic);
+ return;
+ }
+ }
+ if (i != 0) {
+ printf("#");
+ }
+
+ /* Copy the packet to the our local buffer */
+ memcpy(&frame[frame_idx].dst_addr, dst_addr, ETH_ALEN);
+ memcpy(&frame[frame_idx].src_addr, nic->node_addr, ETH_ALEN);
+ frame[frame_idx].type = htons(type);
+ memset(&frame[frame_idx].data, 0, sizeof(frame[frame_idx].data));
+ memcpy(&frame[frame_idx].data, packet, size);
+
+ /* Setup the ring buffer entry to transmit */
+ txbd = &bp->tx_desc_ring[ring_prod];
+ txbd->tx_bd_haddr_hi = 0; /* Etherboot runs under 4GB */
+ txbd->tx_bd_haddr_lo = virt_to_bus(&frame[frame_idx]);
+ txbd->tx_bd_mss_nbytes = (size + ETH_HLEN);
+ txbd->tx_bd_vlan_tag_flags = TX_BD_FLAGS_START | TX_BD_FLAGS_END;
+
+ /* Advance to the next entry */
+ prod = NEXT_TX_BD(prod);
+ frame_idx ^= 1;
+
+ bp->tx_prod_bseq += (size + ETH_HLEN);
+
+ REG_WR16(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BIDX, prod);
+ REG_WR(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BSEQ, bp->tx_prod_bseq);
+
+ wmb();
+
+ bp->tx_prod = prod;
+}
+
+static int
+bnx2_poll_link(struct bnx2 *bp)
+{
+ u32 new_link_state, old_link_state, emac_status;
+
+ new_link_state = bp->status_blk->status_attn_bits &
+ STATUS_ATTN_BITS_LINK_STATE;
+
+ old_link_state = bp->status_blk->status_attn_bits_ack &
+ STATUS_ATTN_BITS_LINK_STATE;
+
+ if (!new_link_state && !old_link_state) {
+ /* For some reason the card doesn't always update the link
+ * status bits properly. Kick the stupid thing and try again.
+ */
+ u32 bmsr;
+
+ bnx2_read_phy(bp, MII_BMSR, &bmsr);
+ bnx2_read_phy(bp, MII_BMSR, &bmsr);
+
+ if ((bp->phy_flags & PHY_SERDES_FLAG) &&
+ (CHIP_NUM(bp) == CHIP_NUM_5706)) {
+ REG_RD(bp, BNX2_EMAC_STATUS);
+ }
+
+ new_link_state = bp->status_blk->status_attn_bits &
+ STATUS_ATTN_BITS_LINK_STATE;
+
+ old_link_state = bp->status_blk->status_attn_bits_ack &
+ STATUS_ATTN_BITS_LINK_STATE;
+
+ /* Okay, for some reason the above doesn't work with some
+ * switches (like HP ProCurve). If the above doesn't work,
+ * check the MAC directly to see if we have a link. Perhaps we
+ * should always check the MAC instead probing the MII.
+ */
+ if (!new_link_state && !old_link_state) {
+ emac_status = REG_RD(bp, BNX2_EMAC_STATUS);
+ if (emac_status & BNX2_EMAC_STATUS_LINK_CHANGE) {
+ /* Acknowledge the link change */
+ REG_WR(bp, BNX2_EMAC_STATUS, BNX2_EMAC_STATUS_LINK_CHANGE);
+ } else if (emac_status & BNX2_EMAC_STATUS_LINK) {
+ new_link_state = !old_link_state;
+ }
+ }
+
+ }
+
+ if (new_link_state != old_link_state) {
+ if (new_link_state) {
+ REG_WR(bp, BNX2_PCICFG_STATUS_BIT_SET_CMD,
+ STATUS_ATTN_BITS_LINK_STATE);
+ }
+ else {
+ REG_WR(bp, BNX2_PCICFG_STATUS_BIT_CLEAR_CMD,
+ STATUS_ATTN_BITS_LINK_STATE);
+ }
+
+ bnx2_set_link(bp);
+
+ /* This is needed to take care of transient status
+ * during link changes.
+ */
+
+ REG_WR(bp, BNX2_HC_COMMAND,
+ bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT);
+ REG_RD(bp, BNX2_HC_COMMAND);
+
+ }
+
+ return bp->link_up;
+}
+
+static int
+bnx2_poll(struct nic* nic, int retrieve)
+{
+ struct bnx2 *bp = &bnx2;
+ struct rx_bd *cons_bd, *prod_bd;
+ u16 hw_cons, sw_cons, sw_ring_cons, sw_prod, sw_ring_prod;
+ struct l2_fhdr *rx_hdr;
+ int result = 0;
+ unsigned int len;
+ unsigned char *data;
+ u32 status;
+
+#if 0
+ if ((bp->status_blk->status_idx == bp->last_status_idx) &&
+ (REG_RD(bp, BNX2_PCICFG_MISC_STATUS) &
+ BNX2_PCICFG_MISC_STATUS_INTA_VALUE)) {
+
+ bp->last_status_idx = bp->status_blk->status_idx;
+ REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
+ BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID |
+ BNX2_PCICFG_INT_ACK_CMD_MASK_INT |
+ bp->last_status_idx);
+ return 0;
+ }
+#endif
+
+ if ((bp->status_blk->status_rx_quick_consumer_index0 != bp->rx_cons) && !retrieve)
+ return 1;
+
+ if (bp->status_blk->status_rx_quick_consumer_index0 != bp->rx_cons) {
+
+ hw_cons = bp->hw_rx_cons = bp->status_blk->status_rx_quick_consumer_index0;
+ if ((hw_cons & MAX_RX_DESC_CNT) == MAX_RX_DESC_CNT) {
+ hw_cons++;
+ }
+ sw_cons = bp->rx_cons;
+ sw_prod = bp->rx_prod;
+
+ rmb();
+ if (sw_cons != hw_cons) {
+
+ sw_ring_cons = RX_RING_IDX(sw_cons);
+ sw_ring_prod = RX_RING_IDX(sw_prod);
+
+ data = bus_to_virt(bp->rx_desc_ring[sw_ring_cons].rx_bd_haddr_lo);
+
+ rx_hdr = (struct l2_fhdr *)data;
+ len = rx_hdr->l2_fhdr_pkt_len - 4;
+ if ((len > (ETH_MAX_MTU + ETH_HLEN)) ||
+ ((status = rx_hdr->l2_fhdr_status) &
+ (L2_FHDR_ERRORS_BAD_CRC |
+ L2_FHDR_ERRORS_PHY_DECODE |
+ L2_FHDR_ERRORS_ALIGNMENT |
+ L2_FHDR_ERRORS_TOO_SHORT |
+ L2_FHDR_ERRORS_GIANT_FRAME))) {
+ result = 0;
+ }
+ else
+ {
+ nic->packetlen = len;
+ memcpy(nic->packet, data + bp->rx_offset, len);
+ result = 1;
+ }
+
+ /* Reuse the buffer */
+ bp->rx_prod_bseq += bp->rx_buf_use_size;
+ if (sw_cons != sw_prod) {
+ cons_bd = &bp->rx_desc_ring[sw_ring_cons];
+ prod_bd = &bp->rx_desc_ring[sw_ring_prod];
+ prod_bd->rx_bd_haddr_hi = 0; /* Etherboot runs under 4GB */
+ prod_bd->rx_bd_haddr_lo = cons_bd->rx_bd_haddr_lo;
+ }
+
+ sw_cons = NEXT_RX_BD(sw_cons);
+ sw_prod = NEXT_RX_BD(sw_prod);
+
+ }
+
+ bp->rx_cons = sw_cons;
+ bp->rx_prod = sw_prod;
+
+ REG_WR16(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BDIDX, bp->rx_prod);
+
+ REG_WR(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BSEQ, bp->rx_prod_bseq);
+
+ wmb();
+
+ }
+
+ bnx2_poll_link(bp);
+
+#if 0
+ bp->last_status_idx = bp->status_blk->status_idx;
+ rmb();
+
+ REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
+ BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID |
+ BNX2_PCICFG_INT_ACK_CMD_MASK_INT |
+ bp->last_status_idx);
+
+ REG_WR(bp, BNX2_HC_COMMAND, bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT);
+#endif
+
+ return result;
+}
+
+static void
+bnx2_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE: break;
+ case ENABLE: break;
+ case FORCE: break;
+ }
+}
+
+static struct nic_operations bnx2_operations = {
+ .connect = dummy_connect,
+ .poll = bnx2_poll,
+ .transmit = bnx2_transmit,
+ .irq = bnx2_irq,
+};
+
+static int
+bnx2_probe(struct nic *nic, struct pci_device *pdev)
+{
+ struct bnx2 *bp = &bnx2;
+ int i, rc;
+
+ if (pdev == 0)
+ return 0;
+
+ memset(bp, 0, sizeof(*bp));
+
+ rc = bnx2_init_board(pdev, nic);
+ if (rc < 0) {
+ return 0;
+ }
+
+ /*
+ nic->disable = bnx2_disable;
+ nic->transmit = bnx2_transmit;
+ nic->poll = bnx2_poll;
+ nic->irq = bnx2_irq;
+ */
+
+ nic->nic_op = &bnx2_operations;
+
+ memcpy(nic->node_addr, bp->mac_addr, ETH_ALEN);
+ printf("Ethernet addr: %s\n", eth_ntoa( nic->node_addr ) );
+ printf("Broadcom NetXtreme II (%c%d) PCI%s %s %dMHz\n",
+ (int) ((CHIP_ID(bp) & 0xf000) >> 12) + 'A',
+ (int) ((CHIP_ID(bp) & 0x0ff0) >> 4),
+ ((bp->flags & PCIX_FLAG) ? "-X" : ""),
+ ((bp->flags & PCI_32BIT_FLAG) ? "32-bit" : "64-bit"),
+ bp->bus_speed_mhz);
+
+ bnx2_set_power_state_0(bp);
+ bnx2_disable_int(bp);
+
+ bnx2_alloc_mem(bp);
+
+ rc = bnx2_init_nic(bp);
+ if (rc) {
+ return 0;
+ }
+
+ bnx2_poll_link(bp);
+ for(i = 0; !bp->link_up && (i < VALID_LINK_TIMEOUT*100); i++) {
+ mdelay(1);
+ bnx2_poll_link(bp);
+ }
+#if 1
+ if (!bp->link_up){
+ printf("Valid link not established\n");
+ goto err_out_disable;
+ }
+#endif
+
+ return 1;
+
+err_out_disable:
+ bnx2_disable(nic);
+ return 0;
+}
+
+static struct pci_device_id bnx2_nics[] = {
+ PCI_ROM(0x14e4, 0x164a, "bnx2-5706", "Broadcom NetXtreme II BCM5706"),
+ PCI_ROM(0x14e4, 0x164c, "bnx2-5708", "Broadcom NetXtreme II BCM5708"),
+ PCI_ROM(0x14e4, 0x16aa, "bnx2-5706S", "Broadcom NetXtreme II BCM5706S"),
+ PCI_ROM(0x14e4, 0x16ac, "bnx2-5708S", "Broadcom NetXtreme II BCM5708S"),
+};
+
+PCI_DRIVER ( bnx2_driver, bnx2_nics, PCI_NO_CLASS );
+
+DRIVER ( "BNX2", nic_driver, pci_driver, bnx2_driver, bnx2_probe, bnx2_disable );
+
+/*
+static struct pci_driver bnx2_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "BNX2",
+ .probe = bnx2_probe,
+ .ids = bnx2_nics,
+ .id_count = sizeof(bnx2_nics)/sizeof(bnx2_nics[0]),
+ .class = 0,
+};
+*/
diff --git a/gpxe/src/drivers/net/bnx2.h b/gpxe/src/drivers/net/bnx2.h
new file mode 100644
index 00000000..57d5fee8
--- /dev/null
+++ b/gpxe/src/drivers/net/bnx2.h
@@ -0,0 +1,4704 @@
+/* bnx2.h: Broadcom NX2 network driver.
+ *
+ * Copyright (c) 2004, 2005, 2006 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Michael Chan (mchan@broadcom.com)
+ */
+
+
+#ifndef BNX2_H
+#define BNX2_H
+
+#define L1_CACHE_BYTES 128 /* Rough approximaition of the cache line size */
+#define L1_CACHE_ALIGN(X) (((X) + L1_CACHE_BYTES-1)&~(L1_CACHE_BYTES -1))
+
+typedef unsigned long dma_addr_t;
+
+/* From pci.h */
+typedef int pci_power_t;
+
+#define PCI_D0 ((pci_power_t) 0)
+#define PCI_D1 ((pci_power_t) 1)
+#define PCI_D2 ((pci_power_t) 2)
+#define PCI_D3hot ((pci_power_t) 3)
+#define PCI_D3cold ((pci_power_t) 4)
+#define PCI_UNKNOWN ((pci_power_t) 5)
+#define PCI_POWER_ERROR ((pci_power_t) -1)
+
+/* From pci_regs.h */
+
+#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */
+#define PCI_X_CMD 2 /* Modes & Features */
+#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */
+
+/* From mii.h */
+
+/* Indicates what features are advertised by the interface. */
+#define ADVERTISED_10baseT_Half (1 << 0)
+#define ADVERTISED_10baseT_Full (1 << 1)
+#define ADVERTISED_100baseT_Half (1 << 2)
+#define ADVERTISED_100baseT_Full (1 << 3)
+#define ADVERTISED_1000baseT_Half (1 << 4)
+#define ADVERTISED_1000baseT_Full (1 << 5)
+#define ADVERTISED_Autoneg (1 << 6)
+#define ADVERTISED_TP (1 << 7)
+#define ADVERTISED_AUI (1 << 8)
+#define ADVERTISED_MII (1 << 9)
+#define ADVERTISED_FIBRE (1 << 10)
+#define ADVERTISED_BNC (1 << 11)
+
+/* The following are all involved in forcing a particular link
+ * mode for the device for setting things. When getting the
+ * devices settings, these indicate the current mode and whether
+ * it was foced up into this mode or autonegotiated.
+ */
+
+/* Duplex, half or full. */
+#define DUPLEX_HALF 0x00
+#define DUPLEX_FULL 0x01
+#define DUPLEX_INVALID 0x02
+
+/* Which connector port. */
+#define PORT_TP 0x00
+#define PORT_AUI 0x01
+#define PORT_MII 0x02
+#define PORT_FIBRE 0x03
+#define PORT_BNC 0x04
+
+/* Which tranceiver to use. */
+#define XCVR_INTERNAL 0x00
+#define XCVR_EXTERNAL 0x01
+#define XCVR_DUMMY1 0x02
+#define XCVR_DUMMY2 0x03
+#define XCVR_DUMMY3 0x04
+
+/* Enable or disable autonegotiation. If this is set to enable,
+ * the forced link modes above are completely ignored.
+ */
+#define AUTONEG_DISABLE 0x00
+#define AUTONEG_ENABLE 0x01
+
+/* Wake-On-Lan options. */
+#define WAKE_PHY (1 << 0)
+#define WAKE_UCAST (1 << 1)
+#define WAKE_MCAST (1 << 2)
+#define WAKE_BCAST (1 << 3)
+#define WAKE_ARP (1 << 4)
+#define WAKE_MAGIC (1 << 5)
+#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */
+
+/* Generic MII registers. */
+
+#define MII_BMCR 0x00 /* Basic mode control register */
+#define MII_BMSR 0x01 /* Basic mode status register */
+#define MII_PHYSID1 0x02 /* PHYS ID 1 */
+#define MII_PHYSID2 0x03 /* PHYS ID 2 */
+#define MII_ADVERTISE 0x04 /* Advertisement control reg */
+#define MII_LPA 0x05 /* Link partner ability reg */
+#define MII_EXPANSION 0x06 /* Expansion register */
+#define MII_CTRL1000 0x09 /* 1000BASE-T control */
+#define MII_STAT1000 0x0a /* 1000BASE-T status */
+#define MII_DCOUNTER 0x12 /* Disconnect counter */
+#define MII_FCSCOUNTER 0x13 /* False carrier counter */
+#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
+#define MII_RERRCOUNTER 0x15 /* Receive error counter */
+#define MII_SREVISION 0x16 /* Silicon revision */
+#define MII_RESV1 0x17 /* Reserved... */
+#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */
+#define MII_PHYADDR 0x19 /* PHY address */
+#define MII_RESV2 0x1a /* Reserved... */
+#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
+#define MII_NCONFIG 0x1c /* Network interface config */
+
+/* Basic mode control register. */
+#define BMCR_RESV 0x007f /* Unused... */
+#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */
+#define BMCR_CTST 0x0080 /* Collision test */
+#define BMCR_FULLDPLX 0x0100 /* Full duplex */
+#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
+#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */
+#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */
+#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */
+#define BMCR_SPEED100 0x2000 /* Select 100Mbps */
+#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */
+#define BMCR_RESET 0x8000 /* Reset the DP83840 */
+
+/* Basic mode status register. */
+#define BMSR_ERCAP 0x0001 /* Ext-reg capability */
+#define BMSR_JCD 0x0002 /* Jabber detected */
+#define BMSR_LSTATUS 0x0004 /* Link status */
+#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */
+#define BMSR_RFAULT 0x0010 /* Remote fault detected */
+#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
+#define BMSR_RESV 0x07c0 /* Unused... */
+#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */
+#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */
+#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */
+#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */
+#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */
+
+/* Advertisement control register. */
+#define ADVERTISE_SLCT 0x001f /* Selector bits */
+#define ADVERTISE_CSMA 0x0001 /* Only selector supported */
+#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
+#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
+#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */
+#define ADVERTISE_RESV 0x1c00 /* Unused... */
+#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */
+#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */
+#define ADVERTISE_NPAGE 0x8000 /* Next page bit */
+#define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */
+#define ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */
+#define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */
+#define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */
+#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */
+
+#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
+ ADVERTISE_CSMA)
+#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
+ ADVERTISE_100HALF | ADVERTISE_100FULL)
+
+/* Link partner ability register. */
+#define LPA_SLCT 0x001f /* Same as advertise selector */
+#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */
+#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */
+#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */
+#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */
+#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */
+#define LPA_RESV 0x1c00 /* Unused... */
+#define LPA_RFAULT 0x2000 /* Link partner faulted */
+#define LPA_LPACK 0x4000 /* Link partner acked us */
+#define LPA_NPAGE 0x8000 /* Next page bit */
+
+#define LPA_DUPLEX (LPA_10FULL | LPA_100FULL)
+#define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4)
+
+/* Expansion register for auto-negotiation. */
+#define EXPANSION_NWAY 0x0001 /* Can do N-way auto-nego */
+#define EXPANSION_LCWP 0x0002 /* Got new RX page code word */
+#define EXPANSION_ENABLENPAGE 0x0004 /* This enables npage words */
+#define EXPANSION_NPCAPABLE 0x0008 /* Link partner supports npage */
+#define EXPANSION_MFAULTS 0x0010 /* Multiple faults detected */
+#define EXPANSION_RESV 0xffe0 /* Unused... */
+
+/* 1000BASE-T Control register */
+#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */
+#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */
+
+/* N-way test register. */
+#define NWAYTEST_RESV1 0x00ff /* Unused... */
+#define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */
+#define NWAYTEST_RESV2 0xfe00 /* Unused... */
+
+/* The following are all involved in forcing a particular link
+ * * mode for the device for setting things. When getting the
+ * * devices settings, these indicate the current mode and whether
+ * * it was foced up into this mode or autonegotiated.
+ * */
+
+/* The forced speed, 10Mb, 100Mb, gigabit. */
+#define SPEED_10 10
+#define SPEED_100 100
+#define SPEED_1000 1000
+#define SPEED_2500 2500
+#define SPEED_INVALID 0 /* XXX was 3 */
+
+
+/* Duplex, half or full. */
+#define DUPLEX_HALF 0x00
+#define DUPLEX_FULL 0x01
+#define DUPLEX_INVALID 0x02
+
+/* Which connector port. */
+#define PORT_TP 0x00
+#define PORT_AUI 0x01
+#define PORT_MII 0x02
+#define PORT_FIBRE 0x03
+#define PORT_BNC 0x04
+
+/* Which tranceiver to use. */
+#define XCVR_INTERNAL 0x00
+#define XCVR_EXTERNAL 0x01
+#define XCVR_DUMMY1 0x02
+#define XCVR_DUMMY2 0x03
+#define XCVR_DUMMY3 0x04
+
+/* Enable or disable autonegotiation. If this is set to enable,
+ * * the forced link modes above are completely ignored.
+ * */
+#define AUTONEG_DISABLE 0x00
+#define AUTONEG_ENABLE 0x01
+
+/* Wake-On-Lan options. */
+#define WAKE_PHY (1 << 0)
+#define WAKE_UCAST (1 << 1)
+#define WAKE_MCAST (1 << 2)
+#define WAKE_BCAST (1 << 3)
+#define WAKE_ARP (1 << 4)
+#define WAKE_MAGIC (1 << 5)
+#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */
+
+/* Hardware data structures and register definitions automatically
+ * generated from RTL code. Do not modify.
+ */
+
+/*
+ * tx_bd definition
+ */
+struct tx_bd {
+ u32 tx_bd_haddr_hi;
+ u32 tx_bd_haddr_lo;
+ u32 tx_bd_mss_nbytes;
+ u32 tx_bd_vlan_tag_flags;
+ #define TX_BD_FLAGS_CONN_FAULT (1<<0)
+ #define TX_BD_FLAGS_TCP_UDP_CKSUM (1<<1)
+ #define TX_BD_FLAGS_IP_CKSUM (1<<2)
+ #define TX_BD_FLAGS_VLAN_TAG (1<<3)
+ #define TX_BD_FLAGS_COAL_NOW (1<<4)
+ #define TX_BD_FLAGS_DONT_GEN_CRC (1<<5)
+ #define TX_BD_FLAGS_END (1<<6)
+ #define TX_BD_FLAGS_START (1<<7)
+ #define TX_BD_FLAGS_SW_OPTION_WORD (0x1f<<8)
+ #define TX_BD_FLAGS_SW_FLAGS (1<<13)
+ #define TX_BD_FLAGS_SW_SNAP (1<<14)
+ #define TX_BD_FLAGS_SW_LSO (1<<15)
+
+};
+
+
+/*
+ * rx_bd definition
+ */
+struct rx_bd {
+ u32 rx_bd_haddr_hi;
+ u32 rx_bd_haddr_lo;
+ u32 rx_bd_len;
+ u32 rx_bd_flags;
+ #define RX_BD_FLAGS_NOPUSH (1<<0)
+ #define RX_BD_FLAGS_DUMMY (1<<1)
+ #define RX_BD_FLAGS_END (1<<2)
+ #define RX_BD_FLAGS_START (1<<3)
+
+};
+
+
+/*
+ * status_block definition
+ */
+struct status_block {
+ u32 status_attn_bits;
+ #define STATUS_ATTN_BITS_LINK_STATE (1L<<0)
+ #define STATUS_ATTN_BITS_TX_SCHEDULER_ABORT (1L<<1)
+ #define STATUS_ATTN_BITS_TX_BD_READ_ABORT (1L<<2)
+ #define STATUS_ATTN_BITS_TX_BD_CACHE_ABORT (1L<<3)
+ #define STATUS_ATTN_BITS_TX_PROCESSOR_ABORT (1L<<4)
+ #define STATUS_ATTN_BITS_TX_DMA_ABORT (1L<<5)
+ #define STATUS_ATTN_BITS_TX_PATCHUP_ABORT (1L<<6)
+ #define STATUS_ATTN_BITS_TX_ASSEMBLER_ABORT (1L<<7)
+ #define STATUS_ATTN_BITS_RX_PARSER_MAC_ABORT (1L<<8)
+ #define STATUS_ATTN_BITS_RX_PARSER_CATCHUP_ABORT (1L<<9)
+ #define STATUS_ATTN_BITS_RX_MBUF_ABORT (1L<<10)
+ #define STATUS_ATTN_BITS_RX_LOOKUP_ABORT (1L<<11)
+ #define STATUS_ATTN_BITS_RX_PROCESSOR_ABORT (1L<<12)
+ #define STATUS_ATTN_BITS_RX_V2P_ABORT (1L<<13)
+ #define STATUS_ATTN_BITS_RX_BD_CACHE_ABORT (1L<<14)
+ #define STATUS_ATTN_BITS_RX_DMA_ABORT (1L<<15)
+ #define STATUS_ATTN_BITS_COMPLETION_ABORT (1L<<16)
+ #define STATUS_ATTN_BITS_HOST_COALESCE_ABORT (1L<<17)
+ #define STATUS_ATTN_BITS_MAILBOX_QUEUE_ABORT (1L<<18)
+ #define STATUS_ATTN_BITS_CONTEXT_ABORT (1L<<19)
+ #define STATUS_ATTN_BITS_CMD_SCHEDULER_ABORT (1L<<20)
+ #define STATUS_ATTN_BITS_CMD_PROCESSOR_ABORT (1L<<21)
+ #define STATUS_ATTN_BITS_MGMT_PROCESSOR_ABORT (1L<<22)
+ #define STATUS_ATTN_BITS_MAC_ABORT (1L<<23)
+ #define STATUS_ATTN_BITS_TIMER_ABORT (1L<<24)
+ #define STATUS_ATTN_BITS_DMAE_ABORT (1L<<25)
+ #define STATUS_ATTN_BITS_FLSH_ABORT (1L<<26)
+ #define STATUS_ATTN_BITS_GRC_ABORT (1L<<27)
+ #define STATUS_ATTN_BITS_PARITY_ERROR (1L<<31)
+
+ u32 status_attn_bits_ack;
+#if __BYTE_ORDER == __BIG_ENDIAN
+ u16 status_tx_quick_consumer_index0;
+ u16 status_tx_quick_consumer_index1;
+ u16 status_tx_quick_consumer_index2;
+ u16 status_tx_quick_consumer_index3;
+ u16 status_rx_quick_consumer_index0;
+ u16 status_rx_quick_consumer_index1;
+ u16 status_rx_quick_consumer_index2;
+ u16 status_rx_quick_consumer_index3;
+ u16 status_rx_quick_consumer_index4;
+ u16 status_rx_quick_consumer_index5;
+ u16 status_rx_quick_consumer_index6;
+ u16 status_rx_quick_consumer_index7;
+ u16 status_rx_quick_consumer_index8;
+ u16 status_rx_quick_consumer_index9;
+ u16 status_rx_quick_consumer_index10;
+ u16 status_rx_quick_consumer_index11;
+ u16 status_rx_quick_consumer_index12;
+ u16 status_rx_quick_consumer_index13;
+ u16 status_rx_quick_consumer_index14;
+ u16 status_rx_quick_consumer_index15;
+ u16 status_completion_producer_index;
+ u16 status_cmd_consumer_index;
+ u16 status_idx;
+ u16 status_unused;
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+ u16 status_tx_quick_consumer_index1;
+ u16 status_tx_quick_consumer_index0;
+ u16 status_tx_quick_consumer_index3;
+ u16 status_tx_quick_consumer_index2;
+ u16 status_rx_quick_consumer_index1;
+ u16 status_rx_quick_consumer_index0;
+ u16 status_rx_quick_consumer_index3;
+ u16 status_rx_quick_consumer_index2;
+ u16 status_rx_quick_consumer_index5;
+ u16 status_rx_quick_consumer_index4;
+ u16 status_rx_quick_consumer_index7;
+ u16 status_rx_quick_consumer_index6;
+ u16 status_rx_quick_consumer_index9;
+ u16 status_rx_quick_consumer_index8;
+ u16 status_rx_quick_consumer_index11;
+ u16 status_rx_quick_consumer_index10;
+ u16 status_rx_quick_consumer_index13;
+ u16 status_rx_quick_consumer_index12;
+ u16 status_rx_quick_consumer_index15;
+ u16 status_rx_quick_consumer_index14;
+ u16 status_cmd_consumer_index;
+ u16 status_completion_producer_index;
+ u16 status_unused;
+ u16 status_idx;
+#endif
+};
+
+
+/*
+ * statistics_block definition
+ */
+struct statistics_block {
+ u32 stat_IfHCInOctets_hi;
+ u32 stat_IfHCInOctets_lo;
+ u32 stat_IfHCInBadOctets_hi;
+ u32 stat_IfHCInBadOctets_lo;
+ u32 stat_IfHCOutOctets_hi;
+ u32 stat_IfHCOutOctets_lo;
+ u32 stat_IfHCOutBadOctets_hi;
+ u32 stat_IfHCOutBadOctets_lo;
+ u32 stat_IfHCInUcastPkts_hi;
+ u32 stat_IfHCInUcastPkts_lo;
+ u32 stat_IfHCInMulticastPkts_hi;
+ u32 stat_IfHCInMulticastPkts_lo;
+ u32 stat_IfHCInBroadcastPkts_hi;
+ u32 stat_IfHCInBroadcastPkts_lo;
+ u32 stat_IfHCOutUcastPkts_hi;
+ u32 stat_IfHCOutUcastPkts_lo;
+ u32 stat_IfHCOutMulticastPkts_hi;
+ u32 stat_IfHCOutMulticastPkts_lo;
+ u32 stat_IfHCOutBroadcastPkts_hi;
+ u32 stat_IfHCOutBroadcastPkts_lo;
+ u32 stat_emac_tx_stat_dot3statsinternalmactransmiterrors;
+ u32 stat_Dot3StatsCarrierSenseErrors;
+ u32 stat_Dot3StatsFCSErrors;
+ u32 stat_Dot3StatsAlignmentErrors;
+ u32 stat_Dot3StatsSingleCollisionFrames;
+ u32 stat_Dot3StatsMultipleCollisionFrames;
+ u32 stat_Dot3StatsDeferredTransmissions;
+ u32 stat_Dot3StatsExcessiveCollisions;
+ u32 stat_Dot3StatsLateCollisions;
+ u32 stat_EtherStatsCollisions;
+ u32 stat_EtherStatsFragments;
+ u32 stat_EtherStatsJabbers;
+ u32 stat_EtherStatsUndersizePkts;
+ u32 stat_EtherStatsOverrsizePkts;
+ u32 stat_EtherStatsPktsRx64Octets;
+ u32 stat_EtherStatsPktsRx65Octetsto127Octets;
+ u32 stat_EtherStatsPktsRx128Octetsto255Octets;
+ u32 stat_EtherStatsPktsRx256Octetsto511Octets;
+ u32 stat_EtherStatsPktsRx512Octetsto1023Octets;
+ u32 stat_EtherStatsPktsRx1024Octetsto1522Octets;
+ u32 stat_EtherStatsPktsRx1523Octetsto9022Octets;
+ u32 stat_EtherStatsPktsTx64Octets;
+ u32 stat_EtherStatsPktsTx65Octetsto127Octets;
+ u32 stat_EtherStatsPktsTx128Octetsto255Octets;
+ u32 stat_EtherStatsPktsTx256Octetsto511Octets;
+ u32 stat_EtherStatsPktsTx512Octetsto1023Octets;
+ u32 stat_EtherStatsPktsTx1024Octetsto1522Octets;
+ u32 stat_EtherStatsPktsTx1523Octetsto9022Octets;
+ u32 stat_XonPauseFramesReceived;
+ u32 stat_XoffPauseFramesReceived;
+ u32 stat_OutXonSent;
+ u32 stat_OutXoffSent;
+ u32 stat_FlowControlDone;
+ u32 stat_MacControlFramesReceived;
+ u32 stat_XoffStateEntered;
+ u32 stat_IfInFramesL2FilterDiscards;
+ u32 stat_IfInRuleCheckerDiscards;
+ u32 stat_IfInFTQDiscards;
+ u32 stat_IfInMBUFDiscards;
+ u32 stat_IfInRuleCheckerP4Hit;
+ u32 stat_CatchupInRuleCheckerDiscards;
+ u32 stat_CatchupInFTQDiscards;
+ u32 stat_CatchupInMBUFDiscards;
+ u32 stat_CatchupInRuleCheckerP4Hit;
+ u32 stat_GenStat00;
+ u32 stat_GenStat01;
+ u32 stat_GenStat02;
+ u32 stat_GenStat03;
+ u32 stat_GenStat04;
+ u32 stat_GenStat05;
+ u32 stat_GenStat06;
+ u32 stat_GenStat07;
+ u32 stat_GenStat08;
+ u32 stat_GenStat09;
+ u32 stat_GenStat10;
+ u32 stat_GenStat11;
+ u32 stat_GenStat12;
+ u32 stat_GenStat13;
+ u32 stat_GenStat14;
+ u32 stat_GenStat15;
+};
+
+
+/*
+ * l2_fhdr definition
+ */
+struct l2_fhdr {
+ u32 l2_fhdr_status;
+ #define L2_FHDR_STATUS_RULE_CLASS (0x7<<0)
+ #define L2_FHDR_STATUS_RULE_P2 (1<<3)
+ #define L2_FHDR_STATUS_RULE_P3 (1<<4)
+ #define L2_FHDR_STATUS_RULE_P4 (1<<5)
+ #define L2_FHDR_STATUS_L2_VLAN_TAG (1<<6)
+ #define L2_FHDR_STATUS_L2_LLC_SNAP (1<<7)
+ #define L2_FHDR_STATUS_RSS_HASH (1<<8)
+ #define L2_FHDR_STATUS_IP_DATAGRAM (1<<13)
+ #define L2_FHDR_STATUS_TCP_SEGMENT (1<<14)
+ #define L2_FHDR_STATUS_UDP_DATAGRAM (1<<15)
+
+ #define L2_FHDR_ERRORS_BAD_CRC (1<<17)
+ #define L2_FHDR_ERRORS_PHY_DECODE (1<<18)
+ #define L2_FHDR_ERRORS_ALIGNMENT (1<<19)
+ #define L2_FHDR_ERRORS_TOO_SHORT (1<<20)
+ #define L2_FHDR_ERRORS_GIANT_FRAME (1<<21)
+ #define L2_FHDR_ERRORS_TCP_XSUM (1<<28)
+ #define L2_FHDR_ERRORS_UDP_XSUM (1<<31)
+
+ u32 l2_fhdr_hash;
+#if __BYTE_ORDER == __BIG_ENDIAN
+ u16 l2_fhdr_pkt_len;
+ u16 l2_fhdr_vlan_tag;
+ u16 l2_fhdr_ip_xsum;
+ u16 l2_fhdr_tcp_udp_xsum;
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+ u16 l2_fhdr_vlan_tag;
+ u16 l2_fhdr_pkt_len;
+ u16 l2_fhdr_tcp_udp_xsum;
+ u16 l2_fhdr_ip_xsum;
+#endif
+};
+
+
+/*
+ * l2_context definition
+ */
+#define BNX2_L2CTX_TYPE 0x00000000
+#define BNX2_L2CTX_TYPE_SIZE_L2 ((0xc0/0x20)<<16)
+#define BNX2_L2CTX_TYPE_TYPE (0xf<<28)
+#define BNX2_L2CTX_TYPE_TYPE_EMPTY (0<<28)
+#define BNX2_L2CTX_TYPE_TYPE_L2 (1<<28)
+
+#define BNX2_L2CTX_TX_HOST_BIDX 0x00000088
+#define BNX2_L2CTX_EST_NBD 0x00000088
+#define BNX2_L2CTX_CMD_TYPE 0x00000088
+#define BNX2_L2CTX_CMD_TYPE_TYPE (0xf<<24)
+#define BNX2_L2CTX_CMD_TYPE_TYPE_L2 (0<<24)
+#define BNX2_L2CTX_CMD_TYPE_TYPE_TCP (1<<24)
+
+#define BNX2_L2CTX_TX_HOST_BSEQ 0x00000090
+#define BNX2_L2CTX_TSCH_BSEQ 0x00000094
+#define BNX2_L2CTX_TBDR_BSEQ 0x00000098
+#define BNX2_L2CTX_TBDR_BOFF 0x0000009c
+#define BNX2_L2CTX_TBDR_BIDX 0x0000009c
+#define BNX2_L2CTX_TBDR_BHADDR_HI 0x000000a0
+#define BNX2_L2CTX_TBDR_BHADDR_LO 0x000000a4
+#define BNX2_L2CTX_TXP_BOFF 0x000000a8
+#define BNX2_L2CTX_TXP_BIDX 0x000000a8
+#define BNX2_L2CTX_TXP_BSEQ 0x000000ac
+
+
+/*
+ * l2_bd_chain_context definition
+ */
+#define BNX2_L2CTX_BD_PRE_READ 0x00000000
+#define BNX2_L2CTX_CTX_SIZE 0x00000000
+#define BNX2_L2CTX_CTX_TYPE 0x00000000
+#define BNX2_L2CTX_CTX_TYPE_SIZE_L2 ((0x20/20)<<16)
+#define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE (0xf<<28)
+#define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_UNDEFINED (0<<28)
+#define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE (1<<28)
+
+#define BNX2_L2CTX_HOST_BDIDX 0x00000004
+#define BNX2_L2CTX_HOST_BSEQ 0x00000008
+#define BNX2_L2CTX_NX_BSEQ 0x0000000c
+#define BNX2_L2CTX_NX_BDHADDR_HI 0x00000010
+#define BNX2_L2CTX_NX_BDHADDR_LO 0x00000014
+#define BNX2_L2CTX_NX_BDIDX 0x00000018
+
+
+/*
+ * pci_config_l definition
+ * offset: 0000
+ */
+#define BNX2_PCICFG_MISC_CONFIG 0x00000068
+#define BNX2_PCICFG_MISC_CONFIG_TARGET_BYTE_SWAP (1L<<2)
+#define BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP (1L<<3)
+#define BNX2_PCICFG_MISC_CONFIG_CLOCK_CTL_ENA (1L<<5)
+#define BNX2_PCICFG_MISC_CONFIG_TARGET_GRC_WORD_SWAP (1L<<6)
+#define BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA (1L<<7)
+#define BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ (1L<<8)
+#define BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY (1L<<9)
+#define BNX2_PCICFG_MISC_CONFIG_ASIC_METAL_REV (0xffL<<16)
+#define BNX2_PCICFG_MISC_CONFIG_ASIC_BASE_REV (0xfL<<24)
+#define BNX2_PCICFG_MISC_CONFIG_ASIC_ID (0xfL<<28)
+
+#define BNX2_PCICFG_MISC_STATUS 0x0000006c
+#define BNX2_PCICFG_MISC_STATUS_INTA_VALUE (1L<<0)
+#define BNX2_PCICFG_MISC_STATUS_32BIT_DET (1L<<1)
+#define BNX2_PCICFG_MISC_STATUS_M66EN (1L<<2)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_DET (1L<<3)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED (0x3L<<4)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_66 (0L<<4)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_100 (1L<<4)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_133 (2L<<4)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_PCI_MODE (3L<<4)
+
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS 0x00000070
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET (0xfL<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ (0L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ (1L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ (2L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ (3L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ (4L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ (5L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ (6L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ (7L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW (0xfL<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_DISABLE (1L<<6)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT (1L<<7)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC (0x7L<<8)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_UNDEF (0L<<8)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_12 (1L<<8)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_6 (2L<<8)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_62 (4L<<8)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PLAY_DEAD (1L<<11)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED (0xfL<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_100 (0L<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_80 (1L<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_50 (2L<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_40 (4L<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_25 (8L<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_STOP (1L<<16)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_PLL_STOP (1L<<17)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_RESERVED_18 (1L<<18)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_USE_SPD_DET (1L<<19)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_RESERVED (0xfffL<<20)
+
+#define BNX2_PCICFG_REG_WINDOW_ADDRESS 0x00000078
+#define BNX2_PCICFG_REG_WINDOW 0x00000080
+#define BNX2_PCICFG_INT_ACK_CMD 0x00000084
+#define BNX2_PCICFG_INT_ACK_CMD_INDEX (0xffffL<<0)
+#define BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID (1L<<16)
+#define BNX2_PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM (1L<<17)
+#define BNX2_PCICFG_INT_ACK_CMD_MASK_INT (1L<<18)
+
+#define BNX2_PCICFG_STATUS_BIT_SET_CMD 0x00000088
+#define BNX2_PCICFG_STATUS_BIT_CLEAR_CMD 0x0000008c
+#define BNX2_PCICFG_MAILBOX_QUEUE_ADDR 0x00000090
+#define BNX2_PCICFG_MAILBOX_QUEUE_DATA 0x00000094
+
+
+/*
+ * pci_reg definition
+ * offset: 0x400
+ */
+#define BNX2_PCI_GRC_WINDOW_ADDR 0x00000400
+#define BNX2_PCI_GRC_WINDOW_ADDR_PCI_GRC_WINDOW_ADDR_VALUE (0x3ffffL<<8)
+
+#define BNX2_PCI_CONFIG_1 0x00000404
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY (0x7L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_OFF (0L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_16 (1L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_32 (2L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_64 (3L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_128 (4L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_256 (5L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_512 (6L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_1024 (7L<<8)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY (0x7L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_OFF (0L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_16 (1L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_32 (2L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_64 (3L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_128 (4L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_256 (5L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_512 (6L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_1024 (7L<<11)
+
+#define BNX2_PCI_CONFIG_2 0x00000408
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE (0xfL<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_DISABLED (0L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_64K (1L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_128K (2L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_256K (3L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_512K (4L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_1M (5L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_2M (6L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_4M (7L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_8M (8L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_16M (9L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_32M (10L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_64M (11L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_128M (12L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_256M (13L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_512M (14L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_1G (15L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_64ENA (1L<<4)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_RETRY (1L<<5)
+#define BNX2_PCI_CONFIG_2_CFG_CYCLE_RETRY (1L<<6)
+#define BNX2_PCI_CONFIG_2_FIRST_CFG_DONE (1L<<7)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE (0xffL<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_DISABLED (0L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_1K (1L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_2K (2L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_4K (3L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_8K (4L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_16K (5L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_32K (6L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_64K (7L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_128K (8L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_256K (9L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_512K (10L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_1M (11L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_2M (12L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_4M (13L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_8M (14L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_16M (15L<<8)
+#define BNX2_PCI_CONFIG_2_MAX_SPLIT_LIMIT (0x1fL<<16)
+#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT (0x3L<<21)
+#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_512 (0L<<21)
+#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_1K (1L<<21)
+#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_2K (2L<<21)
+#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_4K (3L<<21)
+#define BNX2_PCI_CONFIG_2_FORCE_32_BIT_MSTR (1L<<23)
+#define BNX2_PCI_CONFIG_2_FORCE_32_BIT_TGT (1L<<24)
+#define BNX2_PCI_CONFIG_2_KEEP_REQ_ASSERT (1L<<25)
+
+#define BNX2_PCI_CONFIG_3 0x0000040c
+#define BNX2_PCI_CONFIG_3_STICKY_BYTE (0xffL<<0)
+#define BNX2_PCI_CONFIG_3_FORCE_PME (1L<<24)
+#define BNX2_PCI_CONFIG_3_PME_STATUS (1L<<25)
+#define BNX2_PCI_CONFIG_3_PME_ENABLE (1L<<26)
+#define BNX2_PCI_CONFIG_3_PM_STATE (0x3L<<27)
+#define BNX2_PCI_CONFIG_3_VAUX_PRESET (1L<<30)
+#define BNX2_PCI_CONFIG_3_PCI_POWER (1L<<31)
+
+#define BNX2_PCI_PM_DATA_A 0x00000410
+#define BNX2_PCI_PM_DATA_A_PM_DATA_0_PRG (0xffL<<0)
+#define BNX2_PCI_PM_DATA_A_PM_DATA_1_PRG (0xffL<<8)
+#define BNX2_PCI_PM_DATA_A_PM_DATA_2_PRG (0xffL<<16)
+#define BNX2_PCI_PM_DATA_A_PM_DATA_3_PRG (0xffL<<24)
+
+#define BNX2_PCI_PM_DATA_B 0x00000414
+#define BNX2_PCI_PM_DATA_B_PM_DATA_4_PRG (0xffL<<0)
+#define BNX2_PCI_PM_DATA_B_PM_DATA_5_PRG (0xffL<<8)
+#define BNX2_PCI_PM_DATA_B_PM_DATA_6_PRG (0xffL<<16)
+#define BNX2_PCI_PM_DATA_B_PM_DATA_7_PRG (0xffL<<24)
+
+#define BNX2_PCI_SWAP_DIAG0 0x00000418
+#define BNX2_PCI_SWAP_DIAG1 0x0000041c
+#define BNX2_PCI_EXP_ROM_ADDR 0x00000420
+#define BNX2_PCI_EXP_ROM_ADDR_ADDRESS (0x3fffffL<<2)
+#define BNX2_PCI_EXP_ROM_ADDR_REQ (1L<<31)
+
+#define BNX2_PCI_EXP_ROM_DATA 0x00000424
+#define BNX2_PCI_VPD_INTF 0x00000428
+#define BNX2_PCI_VPD_INTF_INTF_REQ (1L<<0)
+
+#define BNX2_PCI_VPD_ADDR_FLAG 0x0000042c
+#define BNX2_PCI_VPD_ADDR_FLAG_ADDRESS (0x1fff<<2)
+#define BNX2_PCI_VPD_ADDR_FLAG_WR (1<<15)
+
+#define BNX2_PCI_VPD_DATA 0x00000430
+#define BNX2_PCI_ID_VAL1 0x00000434
+#define BNX2_PCI_ID_VAL1_DEVICE_ID (0xffffL<<0)
+#define BNX2_PCI_ID_VAL1_VENDOR_ID (0xffffL<<16)
+
+#define BNX2_PCI_ID_VAL2 0x00000438
+#define BNX2_PCI_ID_VAL2_SUBSYSTEM_VENDOR_ID (0xffffL<<0)
+#define BNX2_PCI_ID_VAL2_SUBSYSTEM_ID (0xffffL<<16)
+
+#define BNX2_PCI_ID_VAL3 0x0000043c
+#define BNX2_PCI_ID_VAL3_CLASS_CODE (0xffffffL<<0)
+#define BNX2_PCI_ID_VAL3_REVISION_ID (0xffL<<24)
+
+#define BNX2_PCI_ID_VAL4 0x00000440
+#define BNX2_PCI_ID_VAL4_CAP_ENA (0xfL<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_0 (0L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_1 (1L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_2 (2L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_3 (3L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_4 (4L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_5 (5L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_6 (6L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_7 (7L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_8 (8L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_9 (9L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_10 (10L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_11 (11L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_12 (12L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_13 (13L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_14 (14L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_15 (15L<<0)
+#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG (0x3L<<6)
+#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_0 (0L<<6)
+#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_1 (1L<<6)
+#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_2 (2L<<6)
+#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_3 (3L<<6)
+#define BNX2_PCI_ID_VAL4_MSI_LIMIT (0x7L<<9)
+#define BNX2_PCI_ID_VAL4_MSI_ADVERTIZE (0x7L<<12)
+#define BNX2_PCI_ID_VAL4_MSI_ENABLE (1L<<15)
+#define BNX2_PCI_ID_VAL4_MAX_64_ADVERTIZE (1L<<16)
+#define BNX2_PCI_ID_VAL4_MAX_133_ADVERTIZE (1L<<17)
+#define BNX2_PCI_ID_VAL4_MAX_MEM_READ_SIZE (0x3L<<21)
+#define BNX2_PCI_ID_VAL4_MAX_SPLIT_SIZE (0x7L<<23)
+#define BNX2_PCI_ID_VAL4_MAX_CUMULATIVE_SIZE (0x7L<<26)
+
+#define BNX2_PCI_ID_VAL5 0x00000444
+#define BNX2_PCI_ID_VAL5_D1_SUPPORT (1L<<0)
+#define BNX2_PCI_ID_VAL5_D2_SUPPORT (1L<<1)
+#define BNX2_PCI_ID_VAL5_PME_IN_D0 (1L<<2)
+#define BNX2_PCI_ID_VAL5_PME_IN_D1 (1L<<3)
+#define BNX2_PCI_ID_VAL5_PME_IN_D2 (1L<<4)
+#define BNX2_PCI_ID_VAL5_PME_IN_D3_HOT (1L<<5)
+
+#define BNX2_PCI_PCIX_EXTENDED_STATUS 0x00000448
+#define BNX2_PCI_PCIX_EXTENDED_STATUS_NO_SNOOP (1L<<8)
+#define BNX2_PCI_PCIX_EXTENDED_STATUS_LONG_BURST (1L<<9)
+#define BNX2_PCI_PCIX_EXTENDED_STATUS_SPLIT_COMP_MSG_CLASS (0xfL<<16)
+#define BNX2_PCI_PCIX_EXTENDED_STATUS_SPLIT_COMP_MSG_IDX (0xffL<<24)
+
+#define BNX2_PCI_ID_VAL6 0x0000044c
+#define BNX2_PCI_ID_VAL6_MAX_LAT (0xffL<<0)
+#define BNX2_PCI_ID_VAL6_MIN_GNT (0xffL<<8)
+#define BNX2_PCI_ID_VAL6_BIST (0xffL<<16)
+
+#define BNX2_PCI_MSI_DATA 0x00000450
+#define BNX2_PCI_MSI_DATA_PCI_MSI_DATA (0xffffL<<0)
+
+#define BNX2_PCI_MSI_ADDR_H 0x00000454
+#define BNX2_PCI_MSI_ADDR_L 0x00000458
+
+
+/*
+ * misc_reg definition
+ * offset: 0x800
+ */
+#define BNX2_MISC_COMMAND 0x00000800
+#define BNX2_MISC_COMMAND_ENABLE_ALL (1L<<0)
+#define BNX2_MISC_COMMAND_DISABLE_ALL (1L<<1)
+#define BNX2_MISC_COMMAND_CORE_RESET (1L<<4)
+#define BNX2_MISC_COMMAND_HARD_RESET (1L<<5)
+#define BNX2_MISC_COMMAND_PAR_ERROR (1L<<8)
+#define BNX2_MISC_COMMAND_PAR_ERR_RAM (0x7fL<<16)
+
+#define BNX2_MISC_CFG 0x00000804
+#define BNX2_MISC_CFG_PCI_GRC_TMOUT (1L<<0)
+#define BNX2_MISC_CFG_NVM_WR_EN (0x3L<<1)
+#define BNX2_MISC_CFG_NVM_WR_EN_PROTECT (0L<<1)
+#define BNX2_MISC_CFG_NVM_WR_EN_PCI (1L<<1)
+#define BNX2_MISC_CFG_NVM_WR_EN_ALLOW (2L<<1)
+#define BNX2_MISC_CFG_NVM_WR_EN_ALLOW2 (3L<<1)
+#define BNX2_MISC_CFG_BIST_EN (1L<<3)
+#define BNX2_MISC_CFG_CK25_OUT_ALT_SRC (1L<<4)
+#define BNX2_MISC_CFG_BYPASS_BSCAN (1L<<5)
+#define BNX2_MISC_CFG_BYPASS_EJTAG (1L<<6)
+#define BNX2_MISC_CFG_CLK_CTL_OVERRIDE (1L<<7)
+#define BNX2_MISC_CFG_LEDMODE (0x3L<<8)
+#define BNX2_MISC_CFG_LEDMODE_MAC (0L<<8)
+#define BNX2_MISC_CFG_LEDMODE_GPHY1 (1L<<8)
+#define BNX2_MISC_CFG_LEDMODE_GPHY2 (2L<<8)
+
+#define BNX2_MISC_ID 0x00000808
+#define BNX2_MISC_ID_BOND_ID (0xfL<<0)
+#define BNX2_MISC_ID_CHIP_METAL (0xffL<<4)
+#define BNX2_MISC_ID_CHIP_REV (0xfL<<12)
+#define BNX2_MISC_ID_CHIP_NUM (0xffffL<<16)
+
+#define BNX2_MISC_ENABLE_STATUS_BITS 0x0000080c
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_SCHEDULER_ENABLE (1L<<0)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_BD_READ_ENABLE (1L<<1)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_BD_CACHE_ENABLE (1L<<2)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_PROCESSOR_ENABLE (1L<<3)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_DMA_ENABLE (1L<<4)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_PATCHUP_ENABLE (1L<<5)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_PAYLOAD_Q_ENABLE (1L<<6)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_HEADER_Q_ENABLE (1L<<7)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_ASSEMBLER_ENABLE (1L<<8)
+#define BNX2_MISC_ENABLE_STATUS_BITS_EMAC_ENABLE (1L<<9)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_PARSER_MAC_ENABLE (1L<<10)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_PARSER_CATCHUP_ENABLE (1L<<11)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_MBUF_ENABLE (1L<<12)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_LOOKUP_ENABLE (1L<<13)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_PROCESSOR_ENABLE (1L<<14)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_V2P_ENABLE (1L<<15)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_BD_CACHE_ENABLE (1L<<16)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_DMA_ENABLE (1L<<17)
+#define BNX2_MISC_ENABLE_STATUS_BITS_COMPLETION_ENABLE (1L<<18)
+#define BNX2_MISC_ENABLE_STATUS_BITS_HOST_COALESCE_ENABLE (1L<<19)
+#define BNX2_MISC_ENABLE_STATUS_BITS_MAILBOX_QUEUE_ENABLE (1L<<20)
+#define BNX2_MISC_ENABLE_STATUS_BITS_CONTEXT_ENABLE (1L<<21)
+#define BNX2_MISC_ENABLE_STATUS_BITS_CMD_SCHEDULER_ENABLE (1L<<22)
+#define BNX2_MISC_ENABLE_STATUS_BITS_CMD_PROCESSOR_ENABLE (1L<<23)
+#define BNX2_MISC_ENABLE_STATUS_BITS_MGMT_PROCESSOR_ENABLE (1L<<24)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TIMER_ENABLE (1L<<25)
+#define BNX2_MISC_ENABLE_STATUS_BITS_DMA_ENGINE_ENABLE (1L<<26)
+#define BNX2_MISC_ENABLE_STATUS_BITS_UMP_ENABLE (1L<<27)
+
+#define BNX2_MISC_ENABLE_SET_BITS 0x00000810
+#define BNX2_MISC_ENABLE_SET_BITS_TX_SCHEDULER_ENABLE (1L<<0)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_BD_READ_ENABLE (1L<<1)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_BD_CACHE_ENABLE (1L<<2)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_PROCESSOR_ENABLE (1L<<3)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_DMA_ENABLE (1L<<4)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_PATCHUP_ENABLE (1L<<5)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_PAYLOAD_Q_ENABLE (1L<<6)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_HEADER_Q_ENABLE (1L<<7)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_ASSEMBLER_ENABLE (1L<<8)
+#define BNX2_MISC_ENABLE_SET_BITS_EMAC_ENABLE (1L<<9)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_PARSER_MAC_ENABLE (1L<<10)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_PARSER_CATCHUP_ENABLE (1L<<11)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_MBUF_ENABLE (1L<<12)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_LOOKUP_ENABLE (1L<<13)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_PROCESSOR_ENABLE (1L<<14)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_V2P_ENABLE (1L<<15)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_BD_CACHE_ENABLE (1L<<16)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_DMA_ENABLE (1L<<17)
+#define BNX2_MISC_ENABLE_SET_BITS_COMPLETION_ENABLE (1L<<18)
+#define BNX2_MISC_ENABLE_SET_BITS_HOST_COALESCE_ENABLE (1L<<19)
+#define BNX2_MISC_ENABLE_SET_BITS_MAILBOX_QUEUE_ENABLE (1L<<20)
+#define BNX2_MISC_ENABLE_SET_BITS_CONTEXT_ENABLE (1L<<21)
+#define BNX2_MISC_ENABLE_SET_BITS_CMD_SCHEDULER_ENABLE (1L<<22)
+#define BNX2_MISC_ENABLE_SET_BITS_CMD_PROCESSOR_ENABLE (1L<<23)
+#define BNX2_MISC_ENABLE_SET_BITS_MGMT_PROCESSOR_ENABLE (1L<<24)
+#define BNX2_MISC_ENABLE_SET_BITS_TIMER_ENABLE (1L<<25)
+#define BNX2_MISC_ENABLE_SET_BITS_DMA_ENGINE_ENABLE (1L<<26)
+#define BNX2_MISC_ENABLE_SET_BITS_UMP_ENABLE (1L<<27)
+
+#define BNX2_MISC_ENABLE_CLR_BITS 0x00000814
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_SCHEDULER_ENABLE (1L<<0)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_BD_READ_ENABLE (1L<<1)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_BD_CACHE_ENABLE (1L<<2)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_PROCESSOR_ENABLE (1L<<3)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_DMA_ENABLE (1L<<4)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_PATCHUP_ENABLE (1L<<5)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_PAYLOAD_Q_ENABLE (1L<<6)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_HEADER_Q_ENABLE (1L<<7)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_ASSEMBLER_ENABLE (1L<<8)
+#define BNX2_MISC_ENABLE_CLR_BITS_EMAC_ENABLE (1L<<9)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_PARSER_MAC_ENABLE (1L<<10)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_PARSER_CATCHUP_ENABLE (1L<<11)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_MBUF_ENABLE (1L<<12)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_LOOKUP_ENABLE (1L<<13)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_PROCESSOR_ENABLE (1L<<14)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_V2P_ENABLE (1L<<15)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_BD_CACHE_ENABLE (1L<<16)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_DMA_ENABLE (1L<<17)
+#define BNX2_MISC_ENABLE_CLR_BITS_COMPLETION_ENABLE (1L<<18)
+#define BNX2_MISC_ENABLE_CLR_BITS_HOST_COALESCE_ENABLE (1L<<19)
+#define BNX2_MISC_ENABLE_CLR_BITS_MAILBOX_QUEUE_ENABLE (1L<<20)
+#define BNX2_MISC_ENABLE_CLR_BITS_CONTEXT_ENABLE (1L<<21)
+#define BNX2_MISC_ENABLE_CLR_BITS_CMD_SCHEDULER_ENABLE (1L<<22)
+#define BNX2_MISC_ENABLE_CLR_BITS_CMD_PROCESSOR_ENABLE (1L<<23)
+#define BNX2_MISC_ENABLE_CLR_BITS_MGMT_PROCESSOR_ENABLE (1L<<24)
+#define BNX2_MISC_ENABLE_CLR_BITS_TIMER_ENABLE (1L<<25)
+#define BNX2_MISC_ENABLE_CLR_BITS_DMA_ENGINE_ENABLE (1L<<26)
+#define BNX2_MISC_ENABLE_CLR_BITS_UMP_ENABLE (1L<<27)
+
+#define BNX2_MISC_CLOCK_CONTROL_BITS 0x00000818
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET (0xfL<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ (0L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ (1L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ (2L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ (3L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ (4L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ (5L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ (6L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ (7L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW (0xfL<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_DISABLE (1L<<6)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT (1L<<7)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC (0x7L<<8)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_UNDEF (0L<<8)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_12 (1L<<8)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_6 (2L<<8)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_62 (4L<<8)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PLAY_DEAD (1L<<11)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED (0xfL<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_100 (0L<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_80 (1L<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_50 (2L<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_40 (4L<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_25 (8L<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_STOP (1L<<16)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_PLL_STOP (1L<<17)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_RESERVED_18 (1L<<18)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_USE_SPD_DET (1L<<19)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_RESERVED (0xfffL<<20)
+
+#define BNX2_MISC_GPIO 0x0000081c
+#define BNX2_MISC_GPIO_VALUE (0xffL<<0)
+#define BNX2_MISC_GPIO_SET (0xffL<<8)
+#define BNX2_MISC_GPIO_CLR (0xffL<<16)
+#define BNX2_MISC_GPIO_FLOAT (0xffL<<24)
+
+#define BNX2_MISC_GPIO_INT 0x00000820
+#define BNX2_MISC_GPIO_INT_INT_STATE (0xfL<<0)
+#define BNX2_MISC_GPIO_INT_OLD_VALUE (0xfL<<8)
+#define BNX2_MISC_GPIO_INT_OLD_SET (0xfL<<16)
+#define BNX2_MISC_GPIO_INT_OLD_CLR (0xfL<<24)
+
+#define BNX2_MISC_CONFIG_LFSR 0x00000824
+#define BNX2_MISC_CONFIG_LFSR_DIV (0xffffL<<0)
+
+#define BNX2_MISC_LFSR_MASK_BITS 0x00000828
+#define BNX2_MISC_LFSR_MASK_BITS_TX_SCHEDULER_ENABLE (1L<<0)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_BD_READ_ENABLE (1L<<1)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_BD_CACHE_ENABLE (1L<<2)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_PROCESSOR_ENABLE (1L<<3)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_DMA_ENABLE (1L<<4)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_PATCHUP_ENABLE (1L<<5)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_PAYLOAD_Q_ENABLE (1L<<6)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_HEADER_Q_ENABLE (1L<<7)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_ASSEMBLER_ENABLE (1L<<8)
+#define BNX2_MISC_LFSR_MASK_BITS_EMAC_ENABLE (1L<<9)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_PARSER_MAC_ENABLE (1L<<10)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_PARSER_CATCHUP_ENABLE (1L<<11)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_MBUF_ENABLE (1L<<12)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_LOOKUP_ENABLE (1L<<13)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_PROCESSOR_ENABLE (1L<<14)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_V2P_ENABLE (1L<<15)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_BD_CACHE_ENABLE (1L<<16)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_DMA_ENABLE (1L<<17)
+#define BNX2_MISC_LFSR_MASK_BITS_COMPLETION_ENABLE (1L<<18)
+#define BNX2_MISC_LFSR_MASK_BITS_HOST_COALESCE_ENABLE (1L<<19)
+#define BNX2_MISC_LFSR_MASK_BITS_MAILBOX_QUEUE_ENABLE (1L<<20)
+#define BNX2_MISC_LFSR_MASK_BITS_CONTEXT_ENABLE (1L<<21)
+#define BNX2_MISC_LFSR_MASK_BITS_CMD_SCHEDULER_ENABLE (1L<<22)
+#define BNX2_MISC_LFSR_MASK_BITS_CMD_PROCESSOR_ENABLE (1L<<23)
+#define BNX2_MISC_LFSR_MASK_BITS_MGMT_PROCESSOR_ENABLE (1L<<24)
+#define BNX2_MISC_LFSR_MASK_BITS_TIMER_ENABLE (1L<<25)
+#define BNX2_MISC_LFSR_MASK_BITS_DMA_ENGINE_ENABLE (1L<<26)
+#define BNX2_MISC_LFSR_MASK_BITS_UMP_ENABLE (1L<<27)
+
+#define BNX2_MISC_ARB_REQ0 0x0000082c
+#define BNX2_MISC_ARB_REQ1 0x00000830
+#define BNX2_MISC_ARB_REQ2 0x00000834
+#define BNX2_MISC_ARB_REQ3 0x00000838
+#define BNX2_MISC_ARB_REQ4 0x0000083c
+#define BNX2_MISC_ARB_FREE0 0x00000840
+#define BNX2_MISC_ARB_FREE1 0x00000844
+#define BNX2_MISC_ARB_FREE2 0x00000848
+#define BNX2_MISC_ARB_FREE3 0x0000084c
+#define BNX2_MISC_ARB_FREE4 0x00000850
+#define BNX2_MISC_ARB_REQ_STATUS0 0x00000854
+#define BNX2_MISC_ARB_REQ_STATUS1 0x00000858
+#define BNX2_MISC_ARB_REQ_STATUS2 0x0000085c
+#define BNX2_MISC_ARB_REQ_STATUS3 0x00000860
+#define BNX2_MISC_ARB_REQ_STATUS4 0x00000864
+#define BNX2_MISC_ARB_GNT0 0x00000868
+#define BNX2_MISC_ARB_GNT0_0 (0x7L<<0)
+#define BNX2_MISC_ARB_GNT0_1 (0x7L<<4)
+#define BNX2_MISC_ARB_GNT0_2 (0x7L<<8)
+#define BNX2_MISC_ARB_GNT0_3 (0x7L<<12)
+#define BNX2_MISC_ARB_GNT0_4 (0x7L<<16)
+#define BNX2_MISC_ARB_GNT0_5 (0x7L<<20)
+#define BNX2_MISC_ARB_GNT0_6 (0x7L<<24)
+#define BNX2_MISC_ARB_GNT0_7 (0x7L<<28)
+
+#define BNX2_MISC_ARB_GNT1 0x0000086c
+#define BNX2_MISC_ARB_GNT1_8 (0x7L<<0)
+#define BNX2_MISC_ARB_GNT1_9 (0x7L<<4)
+#define BNX2_MISC_ARB_GNT1_10 (0x7L<<8)
+#define BNX2_MISC_ARB_GNT1_11 (0x7L<<12)
+#define BNX2_MISC_ARB_GNT1_12 (0x7L<<16)
+#define BNX2_MISC_ARB_GNT1_13 (0x7L<<20)
+#define BNX2_MISC_ARB_GNT1_14 (0x7L<<24)
+#define BNX2_MISC_ARB_GNT1_15 (0x7L<<28)
+
+#define BNX2_MISC_ARB_GNT2 0x00000870
+#define BNX2_MISC_ARB_GNT2_16 (0x7L<<0)
+#define BNX2_MISC_ARB_GNT2_17 (0x7L<<4)
+#define BNX2_MISC_ARB_GNT2_18 (0x7L<<8)
+#define BNX2_MISC_ARB_GNT2_19 (0x7L<<12)
+#define BNX2_MISC_ARB_GNT2_20 (0x7L<<16)
+#define BNX2_MISC_ARB_GNT2_21 (0x7L<<20)
+#define BNX2_MISC_ARB_GNT2_22 (0x7L<<24)
+#define BNX2_MISC_ARB_GNT2_23 (0x7L<<28)
+
+#define BNX2_MISC_ARB_GNT3 0x00000874
+#define BNX2_MISC_ARB_GNT3_24 (0x7L<<0)
+#define BNX2_MISC_ARB_GNT3_25 (0x7L<<4)
+#define BNX2_MISC_ARB_GNT3_26 (0x7L<<8)
+#define BNX2_MISC_ARB_GNT3_27 (0x7L<<12)
+#define BNX2_MISC_ARB_GNT3_28 (0x7L<<16)
+#define BNX2_MISC_ARB_GNT3_29 (0x7L<<20)
+#define BNX2_MISC_ARB_GNT3_30 (0x7L<<24)
+#define BNX2_MISC_ARB_GNT3_31 (0x7L<<28)
+
+#define BNX2_MISC_PRBS_CONTROL 0x00000878
+#define BNX2_MISC_PRBS_CONTROL_EN (1L<<0)
+#define BNX2_MISC_PRBS_CONTROL_RSTB (1L<<1)
+#define BNX2_MISC_PRBS_CONTROL_INV (1L<<2)
+#define BNX2_MISC_PRBS_CONTROL_ERR_CLR (1L<<3)
+#define BNX2_MISC_PRBS_CONTROL_ORDER (0x3L<<4)
+#define BNX2_MISC_PRBS_CONTROL_ORDER_7TH (0L<<4)
+#define BNX2_MISC_PRBS_CONTROL_ORDER_15TH (1L<<4)
+#define BNX2_MISC_PRBS_CONTROL_ORDER_23RD (2L<<4)
+#define BNX2_MISC_PRBS_CONTROL_ORDER_31ST (3L<<4)
+
+#define BNX2_MISC_PRBS_STATUS 0x0000087c
+#define BNX2_MISC_PRBS_STATUS_LOCK (1L<<0)
+#define BNX2_MISC_PRBS_STATUS_STKY (1L<<1)
+#define BNX2_MISC_PRBS_STATUS_ERRORS (0x3fffL<<2)
+#define BNX2_MISC_PRBS_STATUS_STATE (0xfL<<16)
+
+#define BNX2_MISC_SM_ASF_CONTROL 0x00000880
+#define BNX2_MISC_SM_ASF_CONTROL_ASF_RST (1L<<0)
+#define BNX2_MISC_SM_ASF_CONTROL_TSC_EN (1L<<1)
+#define BNX2_MISC_SM_ASF_CONTROL_WG_TO (1L<<2)
+#define BNX2_MISC_SM_ASF_CONTROL_HB_TO (1L<<3)
+#define BNX2_MISC_SM_ASF_CONTROL_PA_TO (1L<<4)
+#define BNX2_MISC_SM_ASF_CONTROL_PL_TO (1L<<5)
+#define BNX2_MISC_SM_ASF_CONTROL_RT_TO (1L<<6)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_EVENT (1L<<7)
+#define BNX2_MISC_SM_ASF_CONTROL_RES (0xfL<<8)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_EN (1L<<12)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_BB_EN (1L<<13)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_NO_ADDR_FILT (1L<<14)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_AUTOREAD (1L<<15)
+#define BNX2_MISC_SM_ASF_CONTROL_NIC_SMB_ADDR1 (0x3fL<<16)
+#define BNX2_MISC_SM_ASF_CONTROL_NIC_SMB_ADDR2 (0x3fL<<24)
+#define BNX2_MISC_SM_ASF_CONTROL_EN_NIC_SMB_ADDR_0 (1L<<30)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_EARLY_ATTN (1L<<31)
+
+#define BNX2_MISC_SMB_IN 0x00000884
+#define BNX2_MISC_SMB_IN_DAT_IN (0xffL<<0)
+#define BNX2_MISC_SMB_IN_RDY (1L<<8)
+#define BNX2_MISC_SMB_IN_DONE (1L<<9)
+#define BNX2_MISC_SMB_IN_FIRSTBYTE (1L<<10)
+#define BNX2_MISC_SMB_IN_STATUS (0x7L<<11)
+#define BNX2_MISC_SMB_IN_STATUS_OK (0x0L<<11)
+#define BNX2_MISC_SMB_IN_STATUS_PEC (0x1L<<11)
+#define BNX2_MISC_SMB_IN_STATUS_OFLOW (0x2L<<11)
+#define BNX2_MISC_SMB_IN_STATUS_STOP (0x3L<<11)
+#define BNX2_MISC_SMB_IN_STATUS_TIMEOUT (0x4L<<11)
+
+#define BNX2_MISC_SMB_OUT 0x00000888
+#define BNX2_MISC_SMB_OUT_DAT_OUT (0xffL<<0)
+#define BNX2_MISC_SMB_OUT_RDY (1L<<8)
+#define BNX2_MISC_SMB_OUT_START (1L<<9)
+#define BNX2_MISC_SMB_OUT_LAST (1L<<10)
+#define BNX2_MISC_SMB_OUT_ACC_TYPE (1L<<11)
+#define BNX2_MISC_SMB_OUT_ENB_PEC (1L<<12)
+#define BNX2_MISC_SMB_OUT_GET_RX_LEN (1L<<13)
+#define BNX2_MISC_SMB_OUT_SMB_READ_LEN (0x3fL<<14)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS (0xfL<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_OK (0L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_FIRST_NACK (1L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_SUB_NACK (9L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_UFLOW (2L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_STOP (3L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_TIMEOUT (4L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_FIRST_LOST (5L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_SUB_LOST (0xdL<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_BADACK (0x6L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_SLAVEMODE (1L<<24)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_DAT_EN (1L<<25)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_DAT_IN (1L<<26)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_CLK_EN (1L<<27)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_CLK_IN (1L<<28)
+
+#define BNX2_MISC_SMB_WATCHDOG 0x0000088c
+#define BNX2_MISC_SMB_WATCHDOG_WATCHDOG (0xffffL<<0)
+
+#define BNX2_MISC_SMB_HEARTBEAT 0x00000890
+#define BNX2_MISC_SMB_HEARTBEAT_HEARTBEAT (0xffffL<<0)
+
+#define BNX2_MISC_SMB_POLL_ASF 0x00000894
+#define BNX2_MISC_SMB_POLL_ASF_POLL_ASF (0xffffL<<0)
+
+#define BNX2_MISC_SMB_POLL_LEGACY 0x00000898
+#define BNX2_MISC_SMB_POLL_LEGACY_POLL_LEGACY (0xffffL<<0)
+
+#define BNX2_MISC_SMB_RETRAN 0x0000089c
+#define BNX2_MISC_SMB_RETRAN_RETRAN (0xffL<<0)
+
+#define BNX2_MISC_SMB_TIMESTAMP 0x000008a0
+#define BNX2_MISC_SMB_TIMESTAMP_TIMESTAMP (0xffffffffL<<0)
+
+#define BNX2_MISC_PERR_ENA0 0x000008a4
+#define BNX2_MISC_PERR_ENA0_COM_MISC_CTXC (1L<<0)
+#define BNX2_MISC_PERR_ENA0_COM_MISC_REGF (1L<<1)
+#define BNX2_MISC_PERR_ENA0_COM_MISC_SCPAD (1L<<2)
+#define BNX2_MISC_PERR_ENA0_CP_MISC_CTXC (1L<<3)
+#define BNX2_MISC_PERR_ENA0_CP_MISC_REGF (1L<<4)
+#define BNX2_MISC_PERR_ENA0_CP_MISC_SCPAD (1L<<5)
+#define BNX2_MISC_PERR_ENA0_CS_MISC_TMEM (1L<<6)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM0 (1L<<7)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM1 (1L<<8)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM2 (1L<<9)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM3 (1L<<10)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM4 (1L<<11)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM5 (1L<<12)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_PGTBL (1L<<13)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR0 (1L<<14)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR1 (1L<<15)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR2 (1L<<16)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR3 (1L<<17)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR4 (1L<<18)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DW0 (1L<<19)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DW1 (1L<<20)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DW2 (1L<<21)
+#define BNX2_MISC_PERR_ENA0_HC_MISC_DMA (1L<<22)
+#define BNX2_MISC_PERR_ENA0_MCP_MISC_REGF (1L<<23)
+#define BNX2_MISC_PERR_ENA0_MCP_MISC_SCPAD (1L<<24)
+#define BNX2_MISC_PERR_ENA0_MQ_MISC_CTX (1L<<25)
+#define BNX2_MISC_PERR_ENA0_RBDC_MISC (1L<<26)
+#define BNX2_MISC_PERR_ENA0_RBUF_MISC_MB (1L<<27)
+#define BNX2_MISC_PERR_ENA0_RBUF_MISC_PTR (1L<<28)
+#define BNX2_MISC_PERR_ENA0_RDE_MISC_RPC (1L<<29)
+#define BNX2_MISC_PERR_ENA0_RDE_MISC_RPM (1L<<30)
+#define BNX2_MISC_PERR_ENA0_RV2P_MISC_CB0REGS (1L<<31)
+
+#define BNX2_MISC_PERR_ENA1 0x000008a8
+#define BNX2_MISC_PERR_ENA1_RV2P_MISC_CB1REGS (1L<<0)
+#define BNX2_MISC_PERR_ENA1_RV2P_MISC_P1IRAM (1L<<1)
+#define BNX2_MISC_PERR_ENA1_RV2P_MISC_P2IRAM (1L<<2)
+#define BNX2_MISC_PERR_ENA1_RXP_MISC_CTXC (1L<<3)
+#define BNX2_MISC_PERR_ENA1_RXP_MISC_REGF (1L<<4)
+#define BNX2_MISC_PERR_ENA1_RXP_MISC_SCPAD (1L<<5)
+#define BNX2_MISC_PERR_ENA1_RXP_MISC_RBUFC (1L<<6)
+#define BNX2_MISC_PERR_ENA1_TBDC_MISC (1L<<7)
+#define BNX2_MISC_PERR_ENA1_TDMA_MISC (1L<<8)
+#define BNX2_MISC_PERR_ENA1_THBUF_MISC_MB0 (1L<<9)
+#define BNX2_MISC_PERR_ENA1_THBUF_MISC_MB1 (1L<<10)
+#define BNX2_MISC_PERR_ENA1_TPAT_MISC_REGF (1L<<11)
+#define BNX2_MISC_PERR_ENA1_TPAT_MISC_SCPAD (1L<<12)
+#define BNX2_MISC_PERR_ENA1_TPBUF_MISC_MB (1L<<13)
+#define BNX2_MISC_PERR_ENA1_TSCH_MISC_LR (1L<<14)
+#define BNX2_MISC_PERR_ENA1_TXP_MISC_CTXC (1L<<15)
+#define BNX2_MISC_PERR_ENA1_TXP_MISC_REGF (1L<<16)
+#define BNX2_MISC_PERR_ENA1_TXP_MISC_SCPAD (1L<<17)
+#define BNX2_MISC_PERR_ENA1_UMP_MISC_FIORX (1L<<18)
+#define BNX2_MISC_PERR_ENA1_UMP_MISC_FIOTX (1L<<19)
+#define BNX2_MISC_PERR_ENA1_UMP_MISC_RX (1L<<20)
+#define BNX2_MISC_PERR_ENA1_UMP_MISC_TX (1L<<21)
+#define BNX2_MISC_PERR_ENA1_RDMAQ_MISC (1L<<22)
+#define BNX2_MISC_PERR_ENA1_CSQ_MISC (1L<<23)
+#define BNX2_MISC_PERR_ENA1_CPQ_MISC (1L<<24)
+#define BNX2_MISC_PERR_ENA1_MCPQ_MISC (1L<<25)
+#define BNX2_MISC_PERR_ENA1_RV2PMQ_MISC (1L<<26)
+#define BNX2_MISC_PERR_ENA1_RV2PPQ_MISC (1L<<27)
+#define BNX2_MISC_PERR_ENA1_RV2PTQ_MISC (1L<<28)
+#define BNX2_MISC_PERR_ENA1_RXPQ_MISC (1L<<29)
+#define BNX2_MISC_PERR_ENA1_RXPCQ_MISC (1L<<30)
+#define BNX2_MISC_PERR_ENA1_RLUPQ_MISC (1L<<31)
+
+#define BNX2_MISC_PERR_ENA2 0x000008ac
+#define BNX2_MISC_PERR_ENA2_COMQ_MISC (1L<<0)
+#define BNX2_MISC_PERR_ENA2_COMXQ_MISC (1L<<1)
+#define BNX2_MISC_PERR_ENA2_COMTQ_MISC (1L<<2)
+#define BNX2_MISC_PERR_ENA2_TSCHQ_MISC (1L<<3)
+#define BNX2_MISC_PERR_ENA2_TBDRQ_MISC (1L<<4)
+#define BNX2_MISC_PERR_ENA2_TXPQ_MISC (1L<<5)
+#define BNX2_MISC_PERR_ENA2_TDMAQ_MISC (1L<<6)
+#define BNX2_MISC_PERR_ENA2_TPATQ_MISC (1L<<7)
+#define BNX2_MISC_PERR_ENA2_TASQ_MISC (1L<<8)
+
+#define BNX2_MISC_DEBUG_VECTOR_SEL 0x000008b0
+#define BNX2_MISC_DEBUG_VECTOR_SEL_0 (0xfffL<<0)
+#define BNX2_MISC_DEBUG_VECTOR_SEL_1 (0xfffL<<12)
+
+#define BNX2_MISC_VREG_CONTROL 0x000008b4
+#define BNX2_MISC_VREG_CONTROL_1_2 (0xfL<<0)
+#define BNX2_MISC_VREG_CONTROL_2_5 (0xfL<<4)
+
+#define BNX2_MISC_FINAL_CLK_CTL_VAL 0x000008b8
+#define BNX2_MISC_FINAL_CLK_CTL_VAL_MISC_FINAL_CLK_CTL_VAL (0x3ffffffL<<6)
+
+#define BNX2_MISC_UNUSED0 0x000008bc
+
+
+/*
+ * nvm_reg definition
+ * offset: 0x6400
+ */
+#define BNX2_NVM_COMMAND 0x00006400
+#define BNX2_NVM_COMMAND_RST (1L<<0)
+#define BNX2_NVM_COMMAND_DONE (1L<<3)
+#define BNX2_NVM_COMMAND_DOIT (1L<<4)
+#define BNX2_NVM_COMMAND_WR (1L<<5)
+#define BNX2_NVM_COMMAND_ERASE (1L<<6)
+#define BNX2_NVM_COMMAND_FIRST (1L<<7)
+#define BNX2_NVM_COMMAND_LAST (1L<<8)
+#define BNX2_NVM_COMMAND_WREN (1L<<16)
+#define BNX2_NVM_COMMAND_WRDI (1L<<17)
+#define BNX2_NVM_COMMAND_EWSR (1L<<18)
+#define BNX2_NVM_COMMAND_WRSR (1L<<19)
+
+#define BNX2_NVM_STATUS 0x00006404
+#define BNX2_NVM_STATUS_PI_FSM_STATE (0xfL<<0)
+#define BNX2_NVM_STATUS_EE_FSM_STATE (0xfL<<4)
+#define BNX2_NVM_STATUS_EQ_FSM_STATE (0xfL<<8)
+
+#define BNX2_NVM_WRITE 0x00006408
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE (0xffffffffL<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_BIT_BANG (0L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_EECLK (1L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_EEDATA (2L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_SCLK (4L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_CS_B (8L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_SO (16L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_SI (32L<<0)
+
+#define BNX2_NVM_ADDR 0x0000640c
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE (0xffffffL<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_BIT_BANG (0L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_EECLK (1L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_EEDATA (2L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_SCLK (4L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_CS_B (8L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_SO (16L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_SI (32L<<0)
+
+#define BNX2_NVM_READ 0x00006410
+#define BNX2_NVM_READ_NVM_READ_VALUE (0xffffffffL<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_BIT_BANG (0L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_EECLK (1L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_EEDATA (2L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_SCLK (4L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_CS_B (8L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_SO (16L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_SI (32L<<0)
+
+#define BNX2_NVM_CFG1 0x00006414
+#define BNX2_NVM_CFG1_FLASH_MODE (1L<<0)
+#define BNX2_NVM_CFG1_BUFFER_MODE (1L<<1)
+#define BNX2_NVM_CFG1_PASS_MODE (1L<<2)
+#define BNX2_NVM_CFG1_BITBANG_MODE (1L<<3)
+#define BNX2_NVM_CFG1_STATUS_BIT (0x7L<<4)
+#define BNX2_NVM_CFG1_STATUS_BIT_FLASH_RDY (0L<<4)
+#define BNX2_NVM_CFG1_STATUS_BIT_BUFFER_RDY (7L<<4)
+#define BNX2_NVM_CFG1_SPI_CLK_DIV (0xfL<<7)
+#define BNX2_NVM_CFG1_SEE_CLK_DIV (0x7ffL<<11)
+#define BNX2_NVM_CFG1_PROTECT_MODE (1L<<24)
+#define BNX2_NVM_CFG1_FLASH_SIZE (1L<<25)
+#define BNX2_NVM_CFG1_COMPAT_BYPASSS (1L<<31)
+
+#define BNX2_NVM_CFG2 0x00006418
+#define BNX2_NVM_CFG2_ERASE_CMD (0xffL<<0)
+#define BNX2_NVM_CFG2_DUMMY (0xffL<<8)
+#define BNX2_NVM_CFG2_STATUS_CMD (0xffL<<16)
+
+#define BNX2_NVM_CFG3 0x0000641c
+#define BNX2_NVM_CFG3_BUFFER_RD_CMD (0xffL<<0)
+#define BNX2_NVM_CFG3_WRITE_CMD (0xffL<<8)
+#define BNX2_NVM_CFG3_BUFFER_WRITE_CMD (0xffL<<16)
+#define BNX2_NVM_CFG3_READ_CMD (0xffL<<24)
+
+#define BNX2_NVM_SW_ARB 0x00006420
+#define BNX2_NVM_SW_ARB_ARB_REQ_SET0 (1L<<0)
+#define BNX2_NVM_SW_ARB_ARB_REQ_SET1 (1L<<1)
+#define BNX2_NVM_SW_ARB_ARB_REQ_SET2 (1L<<2)
+#define BNX2_NVM_SW_ARB_ARB_REQ_SET3 (1L<<3)
+#define BNX2_NVM_SW_ARB_ARB_REQ_CLR0 (1L<<4)
+#define BNX2_NVM_SW_ARB_ARB_REQ_CLR1 (1L<<5)
+#define BNX2_NVM_SW_ARB_ARB_REQ_CLR2 (1L<<6)
+#define BNX2_NVM_SW_ARB_ARB_REQ_CLR3 (1L<<7)
+#define BNX2_NVM_SW_ARB_ARB_ARB0 (1L<<8)
+#define BNX2_NVM_SW_ARB_ARB_ARB1 (1L<<9)
+#define BNX2_NVM_SW_ARB_ARB_ARB2 (1L<<10)
+#define BNX2_NVM_SW_ARB_ARB_ARB3 (1L<<11)
+#define BNX2_NVM_SW_ARB_REQ0 (1L<<12)
+#define BNX2_NVM_SW_ARB_REQ1 (1L<<13)
+#define BNX2_NVM_SW_ARB_REQ2 (1L<<14)
+#define BNX2_NVM_SW_ARB_REQ3 (1L<<15)
+
+#define BNX2_NVM_ACCESS_ENABLE 0x00006424
+#define BNX2_NVM_ACCESS_ENABLE_EN (1L<<0)
+#define BNX2_NVM_ACCESS_ENABLE_WR_EN (1L<<1)
+
+#define BNX2_NVM_WRITE1 0x00006428
+#define BNX2_NVM_WRITE1_WREN_CMD (0xffL<<0)
+#define BNX2_NVM_WRITE1_WRDI_CMD (0xffL<<8)
+#define BNX2_NVM_WRITE1_SR_DATA (0xffL<<16)
+
+
+
+/*
+ * dma_reg definition
+ * offset: 0xc00
+ */
+#define BNX2_DMA_COMMAND 0x00000c00
+#define BNX2_DMA_COMMAND_ENABLE (1L<<0)
+
+#define BNX2_DMA_STATUS 0x00000c04
+#define BNX2_DMA_STATUS_PAR_ERROR_STATE (1L<<0)
+#define BNX2_DMA_STATUS_READ_TRANSFERS_STAT (1L<<16)
+#define BNX2_DMA_STATUS_READ_DELAY_PCI_CLKS_STAT (1L<<17)
+#define BNX2_DMA_STATUS_BIG_READ_TRANSFERS_STAT (1L<<18)
+#define BNX2_DMA_STATUS_BIG_READ_DELAY_PCI_CLKS_STAT (1L<<19)
+#define BNX2_DMA_STATUS_BIG_READ_RETRY_AFTER_DATA_STAT (1L<<20)
+#define BNX2_DMA_STATUS_WRITE_TRANSFERS_STAT (1L<<21)
+#define BNX2_DMA_STATUS_WRITE_DELAY_PCI_CLKS_STAT (1L<<22)
+#define BNX2_DMA_STATUS_BIG_WRITE_TRANSFERS_STAT (1L<<23)
+#define BNX2_DMA_STATUS_BIG_WRITE_DELAY_PCI_CLKS_STAT (1L<<24)
+#define BNX2_DMA_STATUS_BIG_WRITE_RETRY_AFTER_DATA_STAT (1L<<25)
+
+#define BNX2_DMA_CONFIG 0x00000c08
+#define BNX2_DMA_CONFIG_DATA_BYTE_SWAP (1L<<0)
+#define BNX2_DMA_CONFIG_DATA_WORD_SWAP (1L<<1)
+#define BNX2_DMA_CONFIG_CNTL_BYTE_SWAP (1L<<4)
+#define BNX2_DMA_CONFIG_CNTL_WORD_SWAP (1L<<5)
+#define BNX2_DMA_CONFIG_ONE_DMA (1L<<6)
+#define BNX2_DMA_CONFIG_CNTL_TWO_DMA (1L<<7)
+#define BNX2_DMA_CONFIG_CNTL_FPGA_MODE (1L<<8)
+#define BNX2_DMA_CONFIG_CNTL_PING_PONG_DMA (1L<<10)
+#define BNX2_DMA_CONFIG_CNTL_PCI_COMP_DLY (1L<<11)
+#define BNX2_DMA_CONFIG_NO_RCHANS_IN_USE (0xfL<<12)
+#define BNX2_DMA_CONFIG_NO_WCHANS_IN_USE (0xfL<<16)
+#define BNX2_DMA_CONFIG_PCI_CLK_CMP_BITS (0x7L<<20)
+#define BNX2_DMA_CONFIG_PCI_FAST_CLK_CMP (1L<<23)
+#define BNX2_DMA_CONFIG_BIG_SIZE (0xfL<<24)
+#define BNX2_DMA_CONFIG_BIG_SIZE_NONE (0x0L<<24)
+#define BNX2_DMA_CONFIG_BIG_SIZE_64 (0x1L<<24)
+#define BNX2_DMA_CONFIG_BIG_SIZE_128 (0x2L<<24)
+#define BNX2_DMA_CONFIG_BIG_SIZE_256 (0x4L<<24)
+#define BNX2_DMA_CONFIG_BIG_SIZE_512 (0x8L<<24)
+
+#define BNX2_DMA_BLACKOUT 0x00000c0c
+#define BNX2_DMA_BLACKOUT_RD_RETRY_BLACKOUT (0xffL<<0)
+#define BNX2_DMA_BLACKOUT_2ND_RD_RETRY_BLACKOUT (0xffL<<8)
+#define BNX2_DMA_BLACKOUT_WR_RETRY_BLACKOUT (0xffL<<16)
+
+#define BNX2_DMA_RCHAN_STAT 0x00000c30
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_0 (0x7L<<0)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_0 (1L<<3)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_1 (0x7L<<4)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_1 (1L<<7)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_2 (0x7L<<8)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_2 (1L<<11)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_3 (0x7L<<12)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_3 (1L<<15)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_4 (0x7L<<16)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_4 (1L<<19)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_5 (0x7L<<20)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_5 (1L<<23)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_6 (0x7L<<24)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_6 (1L<<27)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_7 (0x7L<<28)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_7 (1L<<31)
+
+#define BNX2_DMA_WCHAN_STAT 0x00000c34
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_0 (0x7L<<0)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_0 (1L<<3)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_1 (0x7L<<4)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_1 (1L<<7)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_2 (0x7L<<8)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_2 (1L<<11)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_3 (0x7L<<12)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_3 (1L<<15)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_4 (0x7L<<16)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_4 (1L<<19)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_5 (0x7L<<20)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_5 (1L<<23)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_6 (0x7L<<24)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_6 (1L<<27)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_7 (0x7L<<28)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_7 (1L<<31)
+
+#define BNX2_DMA_RCHAN_ASSIGNMENT 0x00000c38
+#define BNX2_DMA_RCHAN_ASSIGNMENT_0 (0xfL<<0)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_1 (0xfL<<4)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_2 (0xfL<<8)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_3 (0xfL<<12)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_4 (0xfL<<16)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_5 (0xfL<<20)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_6 (0xfL<<24)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_7 (0xfL<<28)
+
+#define BNX2_DMA_WCHAN_ASSIGNMENT 0x00000c3c
+#define BNX2_DMA_WCHAN_ASSIGNMENT_0 (0xfL<<0)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_1 (0xfL<<4)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_2 (0xfL<<8)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_3 (0xfL<<12)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_4 (0xfL<<16)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_5 (0xfL<<20)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_6 (0xfL<<24)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_7 (0xfL<<28)
+
+#define BNX2_DMA_RCHAN_STAT_00 0x00000c40
+#define BNX2_DMA_RCHAN_STAT_00_RCHAN_STA_HOST_ADDR_LOW (0xffffffffL<<0)
+
+#define BNX2_DMA_RCHAN_STAT_01 0x00000c44
+#define BNX2_DMA_RCHAN_STAT_01_RCHAN_STA_HOST_ADDR_HIGH (0xffffffffL<<0)
+
+#define BNX2_DMA_RCHAN_STAT_02 0x00000c48
+#define BNX2_DMA_RCHAN_STAT_02_LENGTH (0xffffL<<0)
+#define BNX2_DMA_RCHAN_STAT_02_WORD_SWAP (1L<<16)
+#define BNX2_DMA_RCHAN_STAT_02_BYTE_SWAP (1L<<17)
+#define BNX2_DMA_RCHAN_STAT_02_PRIORITY_LVL (1L<<18)
+
+#define BNX2_DMA_RCHAN_STAT_10 0x00000c4c
+#define BNX2_DMA_RCHAN_STAT_11 0x00000c50
+#define BNX2_DMA_RCHAN_STAT_12 0x00000c54
+#define BNX2_DMA_RCHAN_STAT_20 0x00000c58
+#define BNX2_DMA_RCHAN_STAT_21 0x00000c5c
+#define BNX2_DMA_RCHAN_STAT_22 0x00000c60
+#define BNX2_DMA_RCHAN_STAT_30 0x00000c64
+#define BNX2_DMA_RCHAN_STAT_31 0x00000c68
+#define BNX2_DMA_RCHAN_STAT_32 0x00000c6c
+#define BNX2_DMA_RCHAN_STAT_40 0x00000c70
+#define BNX2_DMA_RCHAN_STAT_41 0x00000c74
+#define BNX2_DMA_RCHAN_STAT_42 0x00000c78
+#define BNX2_DMA_RCHAN_STAT_50 0x00000c7c
+#define BNX2_DMA_RCHAN_STAT_51 0x00000c80
+#define BNX2_DMA_RCHAN_STAT_52 0x00000c84
+#define BNX2_DMA_RCHAN_STAT_60 0x00000c88
+#define BNX2_DMA_RCHAN_STAT_61 0x00000c8c
+#define BNX2_DMA_RCHAN_STAT_62 0x00000c90
+#define BNX2_DMA_RCHAN_STAT_70 0x00000c94
+#define BNX2_DMA_RCHAN_STAT_71 0x00000c98
+#define BNX2_DMA_RCHAN_STAT_72 0x00000c9c
+#define BNX2_DMA_WCHAN_STAT_00 0x00000ca0
+#define BNX2_DMA_WCHAN_STAT_00_WCHAN_STA_HOST_ADDR_LOW (0xffffffffL<<0)
+
+#define BNX2_DMA_WCHAN_STAT_01 0x00000ca4
+#define BNX2_DMA_WCHAN_STAT_01_WCHAN_STA_HOST_ADDR_HIGH (0xffffffffL<<0)
+
+#define BNX2_DMA_WCHAN_STAT_02 0x00000ca8
+#define BNX2_DMA_WCHAN_STAT_02_LENGTH (0xffffL<<0)
+#define BNX2_DMA_WCHAN_STAT_02_WORD_SWAP (1L<<16)
+#define BNX2_DMA_WCHAN_STAT_02_BYTE_SWAP (1L<<17)
+#define BNX2_DMA_WCHAN_STAT_02_PRIORITY_LVL (1L<<18)
+
+#define BNX2_DMA_WCHAN_STAT_10 0x00000cac
+#define BNX2_DMA_WCHAN_STAT_11 0x00000cb0
+#define BNX2_DMA_WCHAN_STAT_12 0x00000cb4
+#define BNX2_DMA_WCHAN_STAT_20 0x00000cb8
+#define BNX2_DMA_WCHAN_STAT_21 0x00000cbc
+#define BNX2_DMA_WCHAN_STAT_22 0x00000cc0
+#define BNX2_DMA_WCHAN_STAT_30 0x00000cc4
+#define BNX2_DMA_WCHAN_STAT_31 0x00000cc8
+#define BNX2_DMA_WCHAN_STAT_32 0x00000ccc
+#define BNX2_DMA_WCHAN_STAT_40 0x00000cd0
+#define BNX2_DMA_WCHAN_STAT_41 0x00000cd4
+#define BNX2_DMA_WCHAN_STAT_42 0x00000cd8
+#define BNX2_DMA_WCHAN_STAT_50 0x00000cdc
+#define BNX2_DMA_WCHAN_STAT_51 0x00000ce0
+#define BNX2_DMA_WCHAN_STAT_52 0x00000ce4
+#define BNX2_DMA_WCHAN_STAT_60 0x00000ce8
+#define BNX2_DMA_WCHAN_STAT_61 0x00000cec
+#define BNX2_DMA_WCHAN_STAT_62 0x00000cf0
+#define BNX2_DMA_WCHAN_STAT_70 0x00000cf4
+#define BNX2_DMA_WCHAN_STAT_71 0x00000cf8
+#define BNX2_DMA_WCHAN_STAT_72 0x00000cfc
+#define BNX2_DMA_ARB_STAT_00 0x00000d00
+#define BNX2_DMA_ARB_STAT_00_MASTER (0xffffL<<0)
+#define BNX2_DMA_ARB_STAT_00_MASTER_ENC (0xffL<<16)
+#define BNX2_DMA_ARB_STAT_00_CUR_BINMSTR (0xffL<<24)
+
+#define BNX2_DMA_ARB_STAT_01 0x00000d04
+#define BNX2_DMA_ARB_STAT_01_LPR_RPTR (0xfL<<0)
+#define BNX2_DMA_ARB_STAT_01_LPR_WPTR (0xfL<<4)
+#define BNX2_DMA_ARB_STAT_01_LPB_RPTR (0xfL<<8)
+#define BNX2_DMA_ARB_STAT_01_LPB_WPTR (0xfL<<12)
+#define BNX2_DMA_ARB_STAT_01_HPR_RPTR (0xfL<<16)
+#define BNX2_DMA_ARB_STAT_01_HPR_WPTR (0xfL<<20)
+#define BNX2_DMA_ARB_STAT_01_HPB_RPTR (0xfL<<24)
+#define BNX2_DMA_ARB_STAT_01_HPB_WPTR (0xfL<<28)
+
+#define BNX2_DMA_FUSE_CTRL0_CMD 0x00000f00
+#define BNX2_DMA_FUSE_CTRL0_CMD_PWRUP_DONE (1L<<0)
+#define BNX2_DMA_FUSE_CTRL0_CMD_SHIFT_DONE (1L<<1)
+#define BNX2_DMA_FUSE_CTRL0_CMD_SHIFT (1L<<2)
+#define BNX2_DMA_FUSE_CTRL0_CMD_LOAD (1L<<3)
+#define BNX2_DMA_FUSE_CTRL0_CMD_SEL (0xfL<<8)
+
+#define BNX2_DMA_FUSE_CTRL0_DATA 0x00000f04
+#define BNX2_DMA_FUSE_CTRL1_CMD 0x00000f08
+#define BNX2_DMA_FUSE_CTRL1_CMD_PWRUP_DONE (1L<<0)
+#define BNX2_DMA_FUSE_CTRL1_CMD_SHIFT_DONE (1L<<1)
+#define BNX2_DMA_FUSE_CTRL1_CMD_SHIFT (1L<<2)
+#define BNX2_DMA_FUSE_CTRL1_CMD_LOAD (1L<<3)
+#define BNX2_DMA_FUSE_CTRL1_CMD_SEL (0xfL<<8)
+
+#define BNX2_DMA_FUSE_CTRL1_DATA 0x00000f0c
+#define BNX2_DMA_FUSE_CTRL2_CMD 0x00000f10
+#define BNX2_DMA_FUSE_CTRL2_CMD_PWRUP_DONE (1L<<0)
+#define BNX2_DMA_FUSE_CTRL2_CMD_SHIFT_DONE (1L<<1)
+#define BNX2_DMA_FUSE_CTRL2_CMD_SHIFT (1L<<2)
+#define BNX2_DMA_FUSE_CTRL2_CMD_LOAD (1L<<3)
+#define BNX2_DMA_FUSE_CTRL2_CMD_SEL (0xfL<<8)
+
+#define BNX2_DMA_FUSE_CTRL2_DATA 0x00000f14
+
+
+/*
+ * context_reg definition
+ * offset: 0x1000
+ */
+#define BNX2_CTX_COMMAND 0x00001000
+#define BNX2_CTX_COMMAND_ENABLED (1L<<0)
+
+#define BNX2_CTX_STATUS 0x00001004
+#define BNX2_CTX_STATUS_LOCK_WAIT (1L<<0)
+#define BNX2_CTX_STATUS_READ_STAT (1L<<16)
+#define BNX2_CTX_STATUS_WRITE_STAT (1L<<17)
+#define BNX2_CTX_STATUS_ACC_STALL_STAT (1L<<18)
+#define BNX2_CTX_STATUS_LOCK_STALL_STAT (1L<<19)
+
+#define BNX2_CTX_VIRT_ADDR 0x00001008
+#define BNX2_CTX_VIRT_ADDR_VIRT_ADDR (0x7fffL<<6)
+
+#define BNX2_CTX_PAGE_TBL 0x0000100c
+#define BNX2_CTX_PAGE_TBL_PAGE_TBL (0x3fffL<<6)
+
+#define BNX2_CTX_DATA_ADR 0x00001010
+#define BNX2_CTX_DATA_ADR_DATA_ADR (0x7ffffL<<2)
+
+#define BNX2_CTX_DATA 0x00001014
+#define BNX2_CTX_LOCK 0x00001018
+#define BNX2_CTX_LOCK_TYPE (0x7L<<0)
+#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_VOID (0x0L<<0)
+#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_COMPLETE (0x7L<<0)
+#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_PROTOCOL (0x1L<<0)
+#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_TX (0x2L<<0)
+#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_TIMER (0x4L<<0)
+#define BNX2_CTX_LOCK_CID_VALUE (0x3fffL<<7)
+#define BNX2_CTX_LOCK_GRANTED (1L<<26)
+#define BNX2_CTX_LOCK_MODE (0x7L<<27)
+#define BNX2_CTX_LOCK_MODE_UNLOCK (0x0L<<27)
+#define BNX2_CTX_LOCK_MODE_IMMEDIATE (0x1L<<27)
+#define BNX2_CTX_LOCK_MODE_SURE (0x2L<<27)
+#define BNX2_CTX_LOCK_STATUS (1L<<30)
+#define BNX2_CTX_LOCK_REQ (1L<<31)
+
+#define BNX2_CTX_ACCESS_STATUS 0x00001040
+#define BNX2_CTX_ACCESS_STATUS_MASTERENCODED (0xfL<<0)
+#define BNX2_CTX_ACCESS_STATUS_ACCESSMEMORYSM (0x3L<<10)
+#define BNX2_CTX_ACCESS_STATUS_PAGETABLEINITSM (0x3L<<12)
+#define BNX2_CTX_ACCESS_STATUS_ACCESSMEMORYINITSM (0x3L<<14)
+#define BNX2_CTX_ACCESS_STATUS_QUALIFIED_REQUEST (0x7ffL<<17)
+
+#define BNX2_CTX_DBG_LOCK_STATUS 0x00001044
+#define BNX2_CTX_DBG_LOCK_STATUS_SM (0x3ffL<<0)
+#define BNX2_CTX_DBG_LOCK_STATUS_MATCH (0x3ffL<<22)
+
+#define BNX2_CTX_CHNL_LOCK_STATUS_0 0x00001080
+#define BNX2_CTX_CHNL_LOCK_STATUS_0_CID (0x3fffL<<0)
+#define BNX2_CTX_CHNL_LOCK_STATUS_0_TYPE (0x3L<<14)
+#define BNX2_CTX_CHNL_LOCK_STATUS_0_MODE (1L<<16)
+
+#define BNX2_CTX_CHNL_LOCK_STATUS_1 0x00001084
+#define BNX2_CTX_CHNL_LOCK_STATUS_2 0x00001088
+#define BNX2_CTX_CHNL_LOCK_STATUS_3 0x0000108c
+#define BNX2_CTX_CHNL_LOCK_STATUS_4 0x00001090
+#define BNX2_CTX_CHNL_LOCK_STATUS_5 0x00001094
+#define BNX2_CTX_CHNL_LOCK_STATUS_6 0x00001098
+#define BNX2_CTX_CHNL_LOCK_STATUS_7 0x0000109c
+#define BNX2_CTX_CHNL_LOCK_STATUS_8 0x000010a0
+
+
+/*
+ * emac_reg definition
+ * offset: 0x1400
+ */
+#define BNX2_EMAC_MODE 0x00001400
+#define BNX2_EMAC_MODE_RESET (1L<<0)
+#define BNX2_EMAC_MODE_HALF_DUPLEX (1L<<1)
+#define BNX2_EMAC_MODE_PORT (0x3L<<2)
+#define BNX2_EMAC_MODE_PORT_NONE (0L<<2)
+#define BNX2_EMAC_MODE_PORT_MII (1L<<2)
+#define BNX2_EMAC_MODE_PORT_GMII (2L<<2)
+#define BNX2_EMAC_MODE_PORT_MII_10 (3L<<2)
+#define BNX2_EMAC_MODE_MAC_LOOP (1L<<4)
+#define BNX2_EMAC_MODE_25G (1L<<5)
+#define BNX2_EMAC_MODE_TAGGED_MAC_CTL (1L<<7)
+#define BNX2_EMAC_MODE_TX_BURST (1L<<8)
+#define BNX2_EMAC_MODE_MAX_DEFER_DROP_ENA (1L<<9)
+#define BNX2_EMAC_MODE_EXT_LINK_POL (1L<<10)
+#define BNX2_EMAC_MODE_FORCE_LINK (1L<<11)
+#define BNX2_EMAC_MODE_MPKT (1L<<18)
+#define BNX2_EMAC_MODE_MPKT_RCVD (1L<<19)
+#define BNX2_EMAC_MODE_ACPI_RCVD (1L<<20)
+
+#define BNX2_EMAC_STATUS 0x00001404
+#define BNX2_EMAC_STATUS_LINK (1L<<11)
+#define BNX2_EMAC_STATUS_LINK_CHANGE (1L<<12)
+#define BNX2_EMAC_STATUS_MI_COMPLETE (1L<<22)
+#define BNX2_EMAC_STATUS_MI_INT (1L<<23)
+#define BNX2_EMAC_STATUS_AP_ERROR (1L<<24)
+#define BNX2_EMAC_STATUS_PARITY_ERROR_STATE (1L<<31)
+
+#define BNX2_EMAC_ATTENTION_ENA 0x00001408
+#define BNX2_EMAC_ATTENTION_ENA_LINK (1L<<11)
+#define BNX2_EMAC_ATTENTION_ENA_MI_COMPLETE (1L<<22)
+#define BNX2_EMAC_ATTENTION_ENA_MI_INT (1L<<23)
+#define BNX2_EMAC_ATTENTION_ENA_AP_ERROR (1L<<24)
+
+#define BNX2_EMAC_LED 0x0000140c
+#define BNX2_EMAC_LED_OVERRIDE (1L<<0)
+#define BNX2_EMAC_LED_1000MB_OVERRIDE (1L<<1)
+#define BNX2_EMAC_LED_100MB_OVERRIDE (1L<<2)
+#define BNX2_EMAC_LED_10MB_OVERRIDE (1L<<3)
+#define BNX2_EMAC_LED_TRAFFIC_OVERRIDE (1L<<4)
+#define BNX2_EMAC_LED_BLNK_TRAFFIC (1L<<5)
+#define BNX2_EMAC_LED_TRAFFIC (1L<<6)
+#define BNX2_EMAC_LED_1000MB (1L<<7)
+#define BNX2_EMAC_LED_100MB (1L<<8)
+#define BNX2_EMAC_LED_10MB (1L<<9)
+#define BNX2_EMAC_LED_TRAFFIC_STAT (1L<<10)
+#define BNX2_EMAC_LED_BLNK_RATE (0xfffL<<19)
+#define BNX2_EMAC_LED_BLNK_RATE_ENA (1L<<31)
+
+#define BNX2_EMAC_MAC_MATCH0 0x00001410
+#define BNX2_EMAC_MAC_MATCH1 0x00001414
+#define BNX2_EMAC_MAC_MATCH2 0x00001418
+#define BNX2_EMAC_MAC_MATCH3 0x0000141c
+#define BNX2_EMAC_MAC_MATCH4 0x00001420
+#define BNX2_EMAC_MAC_MATCH5 0x00001424
+#define BNX2_EMAC_MAC_MATCH6 0x00001428
+#define BNX2_EMAC_MAC_MATCH7 0x0000142c
+#define BNX2_EMAC_MAC_MATCH8 0x00001430
+#define BNX2_EMAC_MAC_MATCH9 0x00001434
+#define BNX2_EMAC_MAC_MATCH10 0x00001438
+#define BNX2_EMAC_MAC_MATCH11 0x0000143c
+#define BNX2_EMAC_MAC_MATCH12 0x00001440
+#define BNX2_EMAC_MAC_MATCH13 0x00001444
+#define BNX2_EMAC_MAC_MATCH14 0x00001448
+#define BNX2_EMAC_MAC_MATCH15 0x0000144c
+#define BNX2_EMAC_MAC_MATCH16 0x00001450
+#define BNX2_EMAC_MAC_MATCH17 0x00001454
+#define BNX2_EMAC_MAC_MATCH18 0x00001458
+#define BNX2_EMAC_MAC_MATCH19 0x0000145c
+#define BNX2_EMAC_MAC_MATCH20 0x00001460
+#define BNX2_EMAC_MAC_MATCH21 0x00001464
+#define BNX2_EMAC_MAC_MATCH22 0x00001468
+#define BNX2_EMAC_MAC_MATCH23 0x0000146c
+#define BNX2_EMAC_MAC_MATCH24 0x00001470
+#define BNX2_EMAC_MAC_MATCH25 0x00001474
+#define BNX2_EMAC_MAC_MATCH26 0x00001478
+#define BNX2_EMAC_MAC_MATCH27 0x0000147c
+#define BNX2_EMAC_MAC_MATCH28 0x00001480
+#define BNX2_EMAC_MAC_MATCH29 0x00001484
+#define BNX2_EMAC_MAC_MATCH30 0x00001488
+#define BNX2_EMAC_MAC_MATCH31 0x0000148c
+#define BNX2_EMAC_BACKOFF_SEED 0x00001498
+#define BNX2_EMAC_BACKOFF_SEED_EMAC_BACKOFF_SEED (0x3ffL<<0)
+
+#define BNX2_EMAC_RX_MTU_SIZE 0x0000149c
+#define BNX2_EMAC_RX_MTU_SIZE_MTU_SIZE (0xffffL<<0)
+#define BNX2_EMAC_RX_MTU_SIZE_JUMBO_ENA (1L<<31)
+
+#define BNX2_EMAC_SERDES_CNTL 0x000014a4
+#define BNX2_EMAC_SERDES_CNTL_RXR (0x7L<<0)
+#define BNX2_EMAC_SERDES_CNTL_RXG (0x3L<<3)
+#define BNX2_EMAC_SERDES_CNTL_RXCKSEL (1L<<6)
+#define BNX2_EMAC_SERDES_CNTL_TXBIAS (0x7L<<7)
+#define BNX2_EMAC_SERDES_CNTL_BGMAX (1L<<10)
+#define BNX2_EMAC_SERDES_CNTL_BGMIN (1L<<11)
+#define BNX2_EMAC_SERDES_CNTL_TXMODE (1L<<12)
+#define BNX2_EMAC_SERDES_CNTL_TXEDGE (1L<<13)
+#define BNX2_EMAC_SERDES_CNTL_SERDES_MODE (1L<<14)
+#define BNX2_EMAC_SERDES_CNTL_PLLTEST (1L<<15)
+#define BNX2_EMAC_SERDES_CNTL_CDET_EN (1L<<16)
+#define BNX2_EMAC_SERDES_CNTL_TBI_LBK (1L<<17)
+#define BNX2_EMAC_SERDES_CNTL_REMOTE_LBK (1L<<18)
+#define BNX2_EMAC_SERDES_CNTL_REV_PHASE (1L<<19)
+#define BNX2_EMAC_SERDES_CNTL_REGCTL12 (0x3L<<20)
+#define BNX2_EMAC_SERDES_CNTL_REGCTL25 (0x3L<<22)
+
+#define BNX2_EMAC_SERDES_STATUS 0x000014a8
+#define BNX2_EMAC_SERDES_STATUS_RX_STAT (0xffL<<0)
+#define BNX2_EMAC_SERDES_STATUS_COMMA_DET (1L<<8)
+
+#define BNX2_EMAC_MDIO_COMM 0x000014ac
+#define BNX2_EMAC_MDIO_COMM_DATA (0xffffL<<0)
+#define BNX2_EMAC_MDIO_COMM_REG_ADDR (0x1fL<<16)
+#define BNX2_EMAC_MDIO_COMM_PHY_ADDR (0x1fL<<21)
+#define BNX2_EMAC_MDIO_COMM_COMMAND (0x3L<<26)
+#define BNX2_EMAC_MDIO_COMM_COMMAND_UNDEFINED_0 (0L<<26)
+#define BNX2_EMAC_MDIO_COMM_COMMAND_WRITE (1L<<26)
+#define BNX2_EMAC_MDIO_COMM_COMMAND_READ (2L<<26)
+#define BNX2_EMAC_MDIO_COMM_COMMAND_UNDEFINED_3 (3L<<26)
+#define BNX2_EMAC_MDIO_COMM_FAIL (1L<<28)
+#define BNX2_EMAC_MDIO_COMM_START_BUSY (1L<<29)
+#define BNX2_EMAC_MDIO_COMM_DISEXT (1L<<30)
+
+#define BNX2_EMAC_MDIO_STATUS 0x000014b0
+#define BNX2_EMAC_MDIO_STATUS_LINK (1L<<0)
+#define BNX2_EMAC_MDIO_STATUS_10MB (1L<<1)
+
+#define BNX2_EMAC_MDIO_MODE 0x000014b4
+#define BNX2_EMAC_MDIO_MODE_SHORT_PREAMBLE (1L<<1)
+#define BNX2_EMAC_MDIO_MODE_AUTO_POLL (1L<<4)
+#define BNX2_EMAC_MDIO_MODE_BIT_BANG (1L<<8)
+#define BNX2_EMAC_MDIO_MODE_MDIO (1L<<9)
+#define BNX2_EMAC_MDIO_MODE_MDIO_OE (1L<<10)
+#define BNX2_EMAC_MDIO_MODE_MDC (1L<<11)
+#define BNX2_EMAC_MDIO_MODE_MDINT (1L<<12)
+#define BNX2_EMAC_MDIO_MODE_CLOCK_CNT (0x1fL<<16)
+
+#define BNX2_EMAC_MDIO_AUTO_STATUS 0x000014b8
+#define BNX2_EMAC_MDIO_AUTO_STATUS_AUTO_ERR (1L<<0)
+
+#define BNX2_EMAC_TX_MODE 0x000014bc
+#define BNX2_EMAC_TX_MODE_RESET (1L<<0)
+#define BNX2_EMAC_TX_MODE_EXT_PAUSE_EN (1L<<3)
+#define BNX2_EMAC_TX_MODE_FLOW_EN (1L<<4)
+#define BNX2_EMAC_TX_MODE_BIG_BACKOFF (1L<<5)
+#define BNX2_EMAC_TX_MODE_LONG_PAUSE (1L<<6)
+#define BNX2_EMAC_TX_MODE_LINK_AWARE (1L<<7)
+
+#define BNX2_EMAC_TX_STATUS 0x000014c0
+#define BNX2_EMAC_TX_STATUS_XOFFED (1L<<0)
+#define BNX2_EMAC_TX_STATUS_XOFF_SENT (1L<<1)
+#define BNX2_EMAC_TX_STATUS_XON_SENT (1L<<2)
+#define BNX2_EMAC_TX_STATUS_LINK_UP (1L<<3)
+#define BNX2_EMAC_TX_STATUS_UNDERRUN (1L<<4)
+
+#define BNX2_EMAC_TX_LENGTHS 0x000014c4
+#define BNX2_EMAC_TX_LENGTHS_SLOT (0xffL<<0)
+#define BNX2_EMAC_TX_LENGTHS_IPG (0xfL<<8)
+#define BNX2_EMAC_TX_LENGTHS_IPG_CRS (0x3L<<12)
+
+#define BNX2_EMAC_RX_MODE 0x000014c8
+#define BNX2_EMAC_RX_MODE_RESET (1L<<0)
+#define BNX2_EMAC_RX_MODE_FLOW_EN (1L<<2)
+#define BNX2_EMAC_RX_MODE_KEEP_MAC_CONTROL (1L<<3)
+#define BNX2_EMAC_RX_MODE_KEEP_PAUSE (1L<<4)
+#define BNX2_EMAC_RX_MODE_ACCEPT_OVERSIZE (1L<<5)
+#define BNX2_EMAC_RX_MODE_ACCEPT_RUNTS (1L<<6)
+#define BNX2_EMAC_RX_MODE_LLC_CHK (1L<<7)
+#define BNX2_EMAC_RX_MODE_PROMISCUOUS (1L<<8)
+#define BNX2_EMAC_RX_MODE_NO_CRC_CHK (1L<<9)
+#define BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG (1L<<10)
+#define BNX2_EMAC_RX_MODE_FILT_BROADCAST (1L<<11)
+#define BNX2_EMAC_RX_MODE_SORT_MODE (1L<<12)
+
+#define BNX2_EMAC_RX_STATUS 0x000014cc
+#define BNX2_EMAC_RX_STATUS_FFED (1L<<0)
+#define BNX2_EMAC_RX_STATUS_FF_RECEIVED (1L<<1)
+#define BNX2_EMAC_RX_STATUS_N_RECEIVED (1L<<2)
+
+#define BNX2_EMAC_MULTICAST_HASH0 0x000014d0
+#define BNX2_EMAC_MULTICAST_HASH1 0x000014d4
+#define BNX2_EMAC_MULTICAST_HASH2 0x000014d8
+#define BNX2_EMAC_MULTICAST_HASH3 0x000014dc
+#define BNX2_EMAC_MULTICAST_HASH4 0x000014e0
+#define BNX2_EMAC_MULTICAST_HASH5 0x000014e4
+#define BNX2_EMAC_MULTICAST_HASH6 0x000014e8
+#define BNX2_EMAC_MULTICAST_HASH7 0x000014ec
+#define BNX2_EMAC_RX_STAT_IFHCINOCTETS 0x00001500
+#define BNX2_EMAC_RX_STAT_IFHCINBADOCTETS 0x00001504
+#define BNX2_EMAC_RX_STAT_ETHERSTATSFRAGMENTS 0x00001508
+#define BNX2_EMAC_RX_STAT_IFHCINUCASTPKTS 0x0000150c
+#define BNX2_EMAC_RX_STAT_IFHCINMULTICASTPKTS 0x00001510
+#define BNX2_EMAC_RX_STAT_IFHCINBROADCASTPKTS 0x00001514
+#define BNX2_EMAC_RX_STAT_DOT3STATSFCSERRORS 0x00001518
+#define BNX2_EMAC_RX_STAT_DOT3STATSALIGNMENTERRORS 0x0000151c
+#define BNX2_EMAC_RX_STAT_DOT3STATSCARRIERSENSEERRORS 0x00001520
+#define BNX2_EMAC_RX_STAT_XONPAUSEFRAMESRECEIVED 0x00001524
+#define BNX2_EMAC_RX_STAT_XOFFPAUSEFRAMESRECEIVED 0x00001528
+#define BNX2_EMAC_RX_STAT_MACCONTROLFRAMESRECEIVED 0x0000152c
+#define BNX2_EMAC_RX_STAT_XOFFSTATEENTERED 0x00001530
+#define BNX2_EMAC_RX_STAT_DOT3STATSFRAMESTOOLONG 0x00001534
+#define BNX2_EMAC_RX_STAT_ETHERSTATSJABBERS 0x00001538
+#define BNX2_EMAC_RX_STAT_ETHERSTATSUNDERSIZEPKTS 0x0000153c
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS64OCTETS 0x00001540
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS65OCTETSTO127OCTETS 0x00001544
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS128OCTETSTO255OCTETS 0x00001548
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS256OCTETSTO511OCTETS 0x0000154c
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS512OCTETSTO1023OCTETS 0x00001550
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS1024OCTETSTO1522OCTETS 0x00001554
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS1523OCTETSTO9022OCTETS 0x00001558
+#define BNX2_EMAC_RXMAC_DEBUG0 0x0000155c
+#define BNX2_EMAC_RXMAC_DEBUG1 0x00001560
+#define BNX2_EMAC_RXMAC_DEBUG1_LENGTH_NE_BYTE_COUNT (1L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG1_LENGTH_OUT_RANGE (1L<<1)
+#define BNX2_EMAC_RXMAC_DEBUG1_BAD_CRC (1L<<2)
+#define BNX2_EMAC_RXMAC_DEBUG1_RX_ERROR (1L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG1_ALIGN_ERROR (1L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG1_LAST_DATA (1L<<5)
+#define BNX2_EMAC_RXMAC_DEBUG1_ODD_BYTE_START (1L<<6)
+#define BNX2_EMAC_RXMAC_DEBUG1_BYTE_COUNT (0xffffL<<7)
+#define BNX2_EMAC_RXMAC_DEBUG1_SLOT_TIME (0xffL<<23)
+
+#define BNX2_EMAC_RXMAC_DEBUG2 0x00001564
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE (0x7L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_IDLE (0x0L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_SFD (0x1L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_DATA (0x2L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_SKEEP (0x3L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_EXT (0x4L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_DROP (0x5L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_SDROP (0x6L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_FC (0x7L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE (0xfL<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_IDLE (0x0L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA0 (0x1L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA1 (0x2L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA2 (0x3L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA3 (0x4L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_ABORT (0x5L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_WAIT (0x6L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_STATUS (0x7L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_LAST (0x8L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_BYTE_IN (0xffL<<7)
+#define BNX2_EMAC_RXMAC_DEBUG2_FALSEC (1L<<15)
+#define BNX2_EMAC_RXMAC_DEBUG2_TAGGED (1L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG2_PAUSE_STATE (1L<<18)
+#define BNX2_EMAC_RXMAC_DEBUG2_PAUSE_STATE_IDLE (0L<<18)
+#define BNX2_EMAC_RXMAC_DEBUG2_PAUSE_STATE_PAUSED (1L<<18)
+#define BNX2_EMAC_RXMAC_DEBUG2_SE_COUNTER (0xfL<<19)
+#define BNX2_EMAC_RXMAC_DEBUG2_QUANTA (0x1fL<<23)
+
+#define BNX2_EMAC_RXMAC_DEBUG3 0x00001568
+#define BNX2_EMAC_RXMAC_DEBUG3_PAUSE_CTR (0xffffL<<0)
+#define BNX2_EMAC_RXMAC_DEBUG3_TMP_PAUSE_CTR (0xffffL<<16)
+
+#define BNX2_EMAC_RXMAC_DEBUG4 0x0000156c
+#define BNX2_EMAC_RXMAC_DEBUG4_TYPE_FIELD (0xffffL<<0)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE (0x3fL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_IDLE (0x0L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UMAC2 (0x1L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UMAC3 (0x2L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UNI (0x3L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MMAC2 (0x7L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MMAC3 (0x5L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PSA1 (0x6L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PSA2 (0x7L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PSA3 (0x8L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MC2 (0x9L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MC3 (0xaL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MWAIT1 (0xeL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MWAIT2 (0xfL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MCHECK (0x10L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MC (0x11L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BC2 (0x12L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BC3 (0x13L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BSA1 (0x14L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BSA2 (0x15L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BSA3 (0x16L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BTYPE (0x17L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BC (0x18L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PTYPE (0x19L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_CMD (0x1aL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MAC (0x1bL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_LATCH (0x1cL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_XOFF (0x1dL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_XON (0x1eL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PAUSED (0x1fL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_NPAUSED (0x20L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_TTYPE (0x21L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_TVAL (0x22L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_USA1 (0x23L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_USA2 (0x24L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_USA3 (0x25L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UTYPE (0x26L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UTTYPE (0x27L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UTVAL (0x28L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MTYPE (0x29L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_DROP (0x2aL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_DROP_PKT (1L<<22)
+#define BNX2_EMAC_RXMAC_DEBUG4_SLOT_FILLED (1L<<23)
+#define BNX2_EMAC_RXMAC_DEBUG4_FALSE_CARRIER (1L<<24)
+#define BNX2_EMAC_RXMAC_DEBUG4_LAST_DATA (1L<<25)
+#define BNX2_EMAC_RXMAC_DEBUG4_sfd_FOUND (1L<<26)
+#define BNX2_EMAC_RXMAC_DEBUG4_ADVANCE (1L<<27)
+#define BNX2_EMAC_RXMAC_DEBUG4_START (1L<<28)
+
+#define BNX2_EMAC_RXMAC_DEBUG5 0x00001570
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM (0x7L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_IDLE (0L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_WAIT_EOF (1L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_WAIT_STAT (2L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_SET_EOF4FCRC (3L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_SET_EOF4RDE (4L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_SET_EOF4ALL (5L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_1WD_WAIT_STAT (6L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1 (0x7L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_VDW (0x0L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_STAT (0x1L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_AEOF (0x2L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_NEOF (0x3L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_SOF (0x4L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_SAEOF (0x6L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_SNEOF (0x7L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_EOF_DETECTED (1L<<7)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF0 (0x7L<<8)
+#define BNX2_EMAC_RXMAC_DEBUG5_RPM_IDI_FIFO_FULL (1L<<11)
+#define BNX2_EMAC_RXMAC_DEBUG5_LOAD_CCODE (1L<<12)
+#define BNX2_EMAC_RXMAC_DEBUG5_LOAD_DATA (1L<<13)
+#define BNX2_EMAC_RXMAC_DEBUG5_LOAD_STAT (1L<<14)
+#define BNX2_EMAC_RXMAC_DEBUG5_CLR_STAT (1L<<15)
+#define BNX2_EMAC_RXMAC_DEBUG5_IDI_RPM_CCODE (0x3L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG5_IDI_RPM_ACCEPT (1L<<19)
+#define BNX2_EMAC_RXMAC_DEBUG5_FMLEN (0xfffL<<20)
+
+#define BNX2_EMAC_RX_STAT_AC0 0x00001580
+#define BNX2_EMAC_RX_STAT_AC1 0x00001584
+#define BNX2_EMAC_RX_STAT_AC2 0x00001588
+#define BNX2_EMAC_RX_STAT_AC3 0x0000158c
+#define BNX2_EMAC_RX_STAT_AC4 0x00001590
+#define BNX2_EMAC_RX_STAT_AC5 0x00001594
+#define BNX2_EMAC_RX_STAT_AC6 0x00001598
+#define BNX2_EMAC_RX_STAT_AC7 0x0000159c
+#define BNX2_EMAC_RX_STAT_AC8 0x000015a0
+#define BNX2_EMAC_RX_STAT_AC9 0x000015a4
+#define BNX2_EMAC_RX_STAT_AC10 0x000015a8
+#define BNX2_EMAC_RX_STAT_AC11 0x000015ac
+#define BNX2_EMAC_RX_STAT_AC12 0x000015b0
+#define BNX2_EMAC_RX_STAT_AC13 0x000015b4
+#define BNX2_EMAC_RX_STAT_AC14 0x000015b8
+#define BNX2_EMAC_RX_STAT_AC15 0x000015bc
+#define BNX2_EMAC_RX_STAT_AC16 0x000015c0
+#define BNX2_EMAC_RX_STAT_AC17 0x000015c4
+#define BNX2_EMAC_RX_STAT_AC18 0x000015c8
+#define BNX2_EMAC_RX_STAT_AC19 0x000015cc
+#define BNX2_EMAC_RX_STAT_AC20 0x000015d0
+#define BNX2_EMAC_RX_STAT_AC21 0x000015d4
+#define BNX2_EMAC_RX_STAT_AC22 0x000015d8
+#define BNX2_EMAC_RXMAC_SUC_DBG_OVERRUNVEC 0x000015dc
+#define BNX2_EMAC_TX_STAT_IFHCOUTOCTETS 0x00001600
+#define BNX2_EMAC_TX_STAT_IFHCOUTBADOCTETS 0x00001604
+#define BNX2_EMAC_TX_STAT_ETHERSTATSCOLLISIONS 0x00001608
+#define BNX2_EMAC_TX_STAT_OUTXONSENT 0x0000160c
+#define BNX2_EMAC_TX_STAT_OUTXOFFSENT 0x00001610
+#define BNX2_EMAC_TX_STAT_FLOWCONTROLDONE 0x00001614
+#define BNX2_EMAC_TX_STAT_DOT3STATSSINGLECOLLISIONFRAMES 0x00001618
+#define BNX2_EMAC_TX_STAT_DOT3STATSMULTIPLECOLLISIONFRAMES 0x0000161c
+#define BNX2_EMAC_TX_STAT_DOT3STATSDEFERREDTRANSMISSIONS 0x00001620
+#define BNX2_EMAC_TX_STAT_DOT3STATSEXCESSIVECOLLISIONS 0x00001624
+#define BNX2_EMAC_TX_STAT_DOT3STATSLATECOLLISIONS 0x00001628
+#define BNX2_EMAC_TX_STAT_IFHCOUTUCASTPKTS 0x0000162c
+#define BNX2_EMAC_TX_STAT_IFHCOUTMULTICASTPKTS 0x00001630
+#define BNX2_EMAC_TX_STAT_IFHCOUTBROADCASTPKTS 0x00001634
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS64OCTETS 0x00001638
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS65OCTETSTO127OCTETS 0x0000163c
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS128OCTETSTO255OCTETS 0x00001640
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS256OCTETSTO511OCTETS 0x00001644
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS512OCTETSTO1023OCTETS 0x00001648
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS1024OCTETSTO1522OCTETS 0x0000164c
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS1523OCTETSTO9022OCTETS 0x00001650
+#define BNX2_EMAC_TX_STAT_DOT3STATSINTERNALMACTRANSMITERRORS 0x00001654
+#define BNX2_EMAC_TXMAC_DEBUG0 0x00001658
+#define BNX2_EMAC_TXMAC_DEBUG1 0x0000165c
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE (0xfL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_IDLE (0x0L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_START0 (0x1L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA0 (0x4L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA1 (0x5L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA2 (0x6L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA3 (0x7L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_WAIT0 (0x8L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_WAIT1 (0x9L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_CRS_ENABLE (1L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG1_BAD_CRC (1L<<5)
+#define BNX2_EMAC_TXMAC_DEBUG1_SE_COUNTER (0xfL<<6)
+#define BNX2_EMAC_TXMAC_DEBUG1_SEND_PAUSE (1L<<10)
+#define BNX2_EMAC_TXMAC_DEBUG1_LATE_COLLISION (1L<<11)
+#define BNX2_EMAC_TXMAC_DEBUG1_MAX_DEFER (1L<<12)
+#define BNX2_EMAC_TXMAC_DEBUG1_DEFERRED (1L<<13)
+#define BNX2_EMAC_TXMAC_DEBUG1_ONE_BYTE (1L<<14)
+#define BNX2_EMAC_TXMAC_DEBUG1_IPG_TIME (0xfL<<15)
+#define BNX2_EMAC_TXMAC_DEBUG1_SLOT_TIME (0xffL<<19)
+
+#define BNX2_EMAC_TXMAC_DEBUG2 0x00001660
+#define BNX2_EMAC_TXMAC_DEBUG2_BACK_OFF (0x3ffL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG2_BYTE_COUNT (0xffffL<<10)
+#define BNX2_EMAC_TXMAC_DEBUG2_COL_COUNT (0x1fL<<26)
+#define BNX2_EMAC_TXMAC_DEBUG2_COL_BIT (1L<<31)
+
+#define BNX2_EMAC_TXMAC_DEBUG3 0x00001664
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE (0xfL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_IDLE (0x0L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_PRE1 (0x1L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_PRE2 (0x2L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_SFD (0x3L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_DATA (0x4L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_CRC1 (0x5L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_CRC2 (0x6L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_EXT (0x7L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_STATB (0x8L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_STATG (0x9L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_JAM (0xaL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_EJAM (0xbL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_BJAM (0xcL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_SWAIT (0xdL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_BACKOFF (0xeL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE (0x7L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_IDLE (0x0L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_WAIT (0x1L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_UNI (0x2L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_MC (0x3L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_BC2 (0x4L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_BC3 (0x5L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_BC (0x6L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_CRS_DONE (1L<<7)
+#define BNX2_EMAC_TXMAC_DEBUG3_XOFF (1L<<8)
+#define BNX2_EMAC_TXMAC_DEBUG3_SE_COUNTER (0xfL<<9)
+#define BNX2_EMAC_TXMAC_DEBUG3_QUANTA_COUNTER (0x1fL<<13)
+
+#define BNX2_EMAC_TXMAC_DEBUG4 0x00001668
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_COUNTER (0xffffL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE (0xfL<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_IDLE (0x0L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_MCA1 (0x2L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_MCA2 (0x3L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_MCA3 (0x6L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_SRC1 (0x7L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_SRC2 (0x5L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_SRC3 (0x4L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_TYPE (0xcL<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_CMD (0xeL<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_TIME (0xaL<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_CRC1 (0x8L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_CRC2 (0x9L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_WAIT (0xdL<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_STATS0_VALID (1L<<20)
+#define BNX2_EMAC_TXMAC_DEBUG4_APPEND_CRC (1L<<21)
+#define BNX2_EMAC_TXMAC_DEBUG4_SLOT_FILLED (1L<<22)
+#define BNX2_EMAC_TXMAC_DEBUG4_MAX_DEFER (1L<<23)
+#define BNX2_EMAC_TXMAC_DEBUG4_SEND_EXTEND (1L<<24)
+#define BNX2_EMAC_TXMAC_DEBUG4_SEND_PADDING (1L<<25)
+#define BNX2_EMAC_TXMAC_DEBUG4_EOF_LOC (1L<<26)
+#define BNX2_EMAC_TXMAC_DEBUG4_COLLIDING (1L<<27)
+#define BNX2_EMAC_TXMAC_DEBUG4_COL_IN (1L<<28)
+#define BNX2_EMAC_TXMAC_DEBUG4_BURSTING (1L<<29)
+#define BNX2_EMAC_TXMAC_DEBUG4_ADVANCE (1L<<30)
+#define BNX2_EMAC_TXMAC_DEBUG4_GO (1L<<31)
+
+#define BNX2_EMAC_TX_STAT_AC0 0x00001680
+#define BNX2_EMAC_TX_STAT_AC1 0x00001684
+#define BNX2_EMAC_TX_STAT_AC2 0x00001688
+#define BNX2_EMAC_TX_STAT_AC3 0x0000168c
+#define BNX2_EMAC_TX_STAT_AC4 0x00001690
+#define BNX2_EMAC_TX_STAT_AC5 0x00001694
+#define BNX2_EMAC_TX_STAT_AC6 0x00001698
+#define BNX2_EMAC_TX_STAT_AC7 0x0000169c
+#define BNX2_EMAC_TX_STAT_AC8 0x000016a0
+#define BNX2_EMAC_TX_STAT_AC9 0x000016a4
+#define BNX2_EMAC_TX_STAT_AC10 0x000016a8
+#define BNX2_EMAC_TX_STAT_AC11 0x000016ac
+#define BNX2_EMAC_TX_STAT_AC12 0x000016b0
+#define BNX2_EMAC_TX_STAT_AC13 0x000016b4
+#define BNX2_EMAC_TX_STAT_AC14 0x000016b8
+#define BNX2_EMAC_TX_STAT_AC15 0x000016bc
+#define BNX2_EMAC_TX_STAT_AC16 0x000016c0
+#define BNX2_EMAC_TX_STAT_AC17 0x000016c4
+#define BNX2_EMAC_TX_STAT_AC18 0x000016c8
+#define BNX2_EMAC_TX_STAT_AC19 0x000016cc
+#define BNX2_EMAC_TX_STAT_AC20 0x000016d0
+#define BNX2_EMAC_TX_STAT_AC21 0x000016d4
+#define BNX2_EMAC_TXMAC_SUC_DBG_OVERRUNVEC 0x000016d8
+
+
+/*
+ * rpm_reg definition
+ * offset: 0x1800
+ */
+#define BNX2_RPM_COMMAND 0x00001800
+#define BNX2_RPM_COMMAND_ENABLED (1L<<0)
+#define BNX2_RPM_COMMAND_OVERRUN_ABORT (1L<<4)
+
+#define BNX2_RPM_STATUS 0x00001804
+#define BNX2_RPM_STATUS_MBUF_WAIT (1L<<0)
+#define BNX2_RPM_STATUS_FREE_WAIT (1L<<1)
+
+#define BNX2_RPM_CONFIG 0x00001808
+#define BNX2_RPM_CONFIG_NO_PSD_HDR_CKSUM (1L<<0)
+#define BNX2_RPM_CONFIG_ACPI_ENA (1L<<1)
+#define BNX2_RPM_CONFIG_ACPI_KEEP (1L<<2)
+#define BNX2_RPM_CONFIG_MP_KEEP (1L<<3)
+#define BNX2_RPM_CONFIG_SORT_VECT_VAL (0xfL<<4)
+#define BNX2_RPM_CONFIG_IGNORE_VLAN (1L<<31)
+
+#define BNX2_RPM_VLAN_MATCH0 0x00001810
+#define BNX2_RPM_VLAN_MATCH0_RPM_VLAN_MTCH0_VALUE (0xfffL<<0)
+
+#define BNX2_RPM_VLAN_MATCH1 0x00001814
+#define BNX2_RPM_VLAN_MATCH1_RPM_VLAN_MTCH1_VALUE (0xfffL<<0)
+
+#define BNX2_RPM_VLAN_MATCH2 0x00001818
+#define BNX2_RPM_VLAN_MATCH2_RPM_VLAN_MTCH2_VALUE (0xfffL<<0)
+
+#define BNX2_RPM_VLAN_MATCH3 0x0000181c
+#define BNX2_RPM_VLAN_MATCH3_RPM_VLAN_MTCH3_VALUE (0xfffL<<0)
+
+#define BNX2_RPM_SORT_USER0 0x00001820
+#define BNX2_RPM_SORT_USER0_PM_EN (0xffffL<<0)
+#define BNX2_RPM_SORT_USER0_BC_EN (1L<<16)
+#define BNX2_RPM_SORT_USER0_MC_EN (1L<<17)
+#define BNX2_RPM_SORT_USER0_MC_HSH_EN (1L<<18)
+#define BNX2_RPM_SORT_USER0_PROM_EN (1L<<19)
+#define BNX2_RPM_SORT_USER0_VLAN_EN (0xfL<<20)
+#define BNX2_RPM_SORT_USER0_PROM_VLAN (1L<<24)
+#define BNX2_RPM_SORT_USER0_ENA (1L<<31)
+
+#define BNX2_RPM_SORT_USER1 0x00001824
+#define BNX2_RPM_SORT_USER1_PM_EN (0xffffL<<0)
+#define BNX2_RPM_SORT_USER1_BC_EN (1L<<16)
+#define BNX2_RPM_SORT_USER1_MC_EN (1L<<17)
+#define BNX2_RPM_SORT_USER1_MC_HSH_EN (1L<<18)
+#define BNX2_RPM_SORT_USER1_PROM_EN (1L<<19)
+#define BNX2_RPM_SORT_USER1_VLAN_EN (0xfL<<20)
+#define BNX2_RPM_SORT_USER1_PROM_VLAN (1L<<24)
+#define BNX2_RPM_SORT_USER1_ENA (1L<<31)
+
+#define BNX2_RPM_SORT_USER2 0x00001828
+#define BNX2_RPM_SORT_USER2_PM_EN (0xffffL<<0)
+#define BNX2_RPM_SORT_USER2_BC_EN (1L<<16)
+#define BNX2_RPM_SORT_USER2_MC_EN (1L<<17)
+#define BNX2_RPM_SORT_USER2_MC_HSH_EN (1L<<18)
+#define BNX2_RPM_SORT_USER2_PROM_EN (1L<<19)
+#define BNX2_RPM_SORT_USER2_VLAN_EN (0xfL<<20)
+#define BNX2_RPM_SORT_USER2_PROM_VLAN (1L<<24)
+#define BNX2_RPM_SORT_USER2_ENA (1L<<31)
+
+#define BNX2_RPM_SORT_USER3 0x0000182c
+#define BNX2_RPM_SORT_USER3_PM_EN (0xffffL<<0)
+#define BNX2_RPM_SORT_USER3_BC_EN (1L<<16)
+#define BNX2_RPM_SORT_USER3_MC_EN (1L<<17)
+#define BNX2_RPM_SORT_USER3_MC_HSH_EN (1L<<18)
+#define BNX2_RPM_SORT_USER3_PROM_EN (1L<<19)
+#define BNX2_RPM_SORT_USER3_VLAN_EN (0xfL<<20)
+#define BNX2_RPM_SORT_USER3_PROM_VLAN (1L<<24)
+#define BNX2_RPM_SORT_USER3_ENA (1L<<31)
+
+#define BNX2_RPM_STAT_L2_FILTER_DISCARDS 0x00001840
+#define BNX2_RPM_STAT_RULE_CHECKER_DISCARDS 0x00001844
+#define BNX2_RPM_STAT_IFINFTQDISCARDS 0x00001848
+#define BNX2_RPM_STAT_IFINMBUFDISCARD 0x0000184c
+#define BNX2_RPM_STAT_RULE_CHECKER_P4_HIT 0x00001850
+#define BNX2_RPM_STAT_AC0 0x00001880
+#define BNX2_RPM_STAT_AC1 0x00001884
+#define BNX2_RPM_STAT_AC2 0x00001888
+#define BNX2_RPM_STAT_AC3 0x0000188c
+#define BNX2_RPM_STAT_AC4 0x00001890
+#define BNX2_RPM_RC_CNTL_0 0x00001900
+#define BNX2_RPM_RC_CNTL_0_OFFSET (0xffL<<0)
+#define BNX2_RPM_RC_CNTL_0_CLASS (0x7L<<8)
+#define BNX2_RPM_RC_CNTL_0_PRIORITY (1L<<11)
+#define BNX2_RPM_RC_CNTL_0_P4 (1L<<12)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE (0x7L<<13)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_START (0L<<13)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_IP (1L<<13)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_TCP (2L<<13)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_UDP (3L<<13)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_DATA (4L<<13)
+#define BNX2_RPM_RC_CNTL_0_COMP (0x3L<<16)
+#define BNX2_RPM_RC_CNTL_0_COMP_EQUAL (0L<<16)
+#define BNX2_RPM_RC_CNTL_0_COMP_NEQUAL (1L<<16)
+#define BNX2_RPM_RC_CNTL_0_COMP_GREATER (2L<<16)
+#define BNX2_RPM_RC_CNTL_0_COMP_LESS (3L<<16)
+#define BNX2_RPM_RC_CNTL_0_SBIT (1L<<19)
+#define BNX2_RPM_RC_CNTL_0_CMDSEL (0xfL<<20)
+#define BNX2_RPM_RC_CNTL_0_MAP (1L<<24)
+#define BNX2_RPM_RC_CNTL_0_DISCARD (1L<<25)
+#define BNX2_RPM_RC_CNTL_0_MASK (1L<<26)
+#define BNX2_RPM_RC_CNTL_0_P1 (1L<<27)
+#define BNX2_RPM_RC_CNTL_0_P2 (1L<<28)
+#define BNX2_RPM_RC_CNTL_0_P3 (1L<<29)
+#define BNX2_RPM_RC_CNTL_0_NBIT (1L<<30)
+
+#define BNX2_RPM_RC_VALUE_MASK_0 0x00001904
+#define BNX2_RPM_RC_VALUE_MASK_0_VALUE (0xffffL<<0)
+#define BNX2_RPM_RC_VALUE_MASK_0_MASK (0xffffL<<16)
+
+#define BNX2_RPM_RC_CNTL_1 0x00001908
+#define BNX2_RPM_RC_CNTL_1_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_1_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_1 0x0000190c
+#define BNX2_RPM_RC_CNTL_2 0x00001910
+#define BNX2_RPM_RC_CNTL_2_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_2_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_2 0x00001914
+#define BNX2_RPM_RC_CNTL_3 0x00001918
+#define BNX2_RPM_RC_CNTL_3_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_3_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_3 0x0000191c
+#define BNX2_RPM_RC_CNTL_4 0x00001920
+#define BNX2_RPM_RC_CNTL_4_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_4_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_4 0x00001924
+#define BNX2_RPM_RC_CNTL_5 0x00001928
+#define BNX2_RPM_RC_CNTL_5_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_5_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_5 0x0000192c
+#define BNX2_RPM_RC_CNTL_6 0x00001930
+#define BNX2_RPM_RC_CNTL_6_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_6_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_6 0x00001934
+#define BNX2_RPM_RC_CNTL_7 0x00001938
+#define BNX2_RPM_RC_CNTL_7_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_7_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_7 0x0000193c
+#define BNX2_RPM_RC_CNTL_8 0x00001940
+#define BNX2_RPM_RC_CNTL_8_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_8_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_8 0x00001944
+#define BNX2_RPM_RC_CNTL_9 0x00001948
+#define BNX2_RPM_RC_CNTL_9_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_9_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_9 0x0000194c
+#define BNX2_RPM_RC_CNTL_10 0x00001950
+#define BNX2_RPM_RC_CNTL_10_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_10_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_10 0x00001954
+#define BNX2_RPM_RC_CNTL_11 0x00001958
+#define BNX2_RPM_RC_CNTL_11_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_11_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_11 0x0000195c
+#define BNX2_RPM_RC_CNTL_12 0x00001960
+#define BNX2_RPM_RC_CNTL_12_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_12_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_12 0x00001964
+#define BNX2_RPM_RC_CNTL_13 0x00001968
+#define BNX2_RPM_RC_CNTL_13_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_13_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_13 0x0000196c
+#define BNX2_RPM_RC_CNTL_14 0x00001970
+#define BNX2_RPM_RC_CNTL_14_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_14_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_14 0x00001974
+#define BNX2_RPM_RC_CNTL_15 0x00001978
+#define BNX2_RPM_RC_CNTL_15_A (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_15_B (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_15 0x0000197c
+#define BNX2_RPM_RC_CONFIG 0x00001980
+#define BNX2_RPM_RC_CONFIG_RULE_ENABLE (0xffffL<<0)
+#define BNX2_RPM_RC_CONFIG_DEF_CLASS (0x7L<<24)
+
+#define BNX2_RPM_DEBUG0 0x00001984
+#define BNX2_RPM_DEBUG0_FM_BCNT (0xffffL<<0)
+#define BNX2_RPM_DEBUG0_T_DATA_OFST_VLD (1L<<16)
+#define BNX2_RPM_DEBUG0_T_UDP_OFST_VLD (1L<<17)
+#define BNX2_RPM_DEBUG0_T_TCP_OFST_VLD (1L<<18)
+#define BNX2_RPM_DEBUG0_T_IP_OFST_VLD (1L<<19)
+#define BNX2_RPM_DEBUG0_IP_MORE_FRGMT (1L<<20)
+#define BNX2_RPM_DEBUG0_T_IP_NO_TCP_UDP_HDR (1L<<21)
+#define BNX2_RPM_DEBUG0_LLC_SNAP (1L<<22)
+#define BNX2_RPM_DEBUG0_FM_STARTED (1L<<23)
+#define BNX2_RPM_DEBUG0_DONE (1L<<24)
+#define BNX2_RPM_DEBUG0_WAIT_4_DONE (1L<<25)
+#define BNX2_RPM_DEBUG0_USE_TPBUF_CKSUM (1L<<26)
+#define BNX2_RPM_DEBUG0_RX_NO_PSD_HDR_CKSUM (1L<<27)
+#define BNX2_RPM_DEBUG0_IGNORE_VLAN (1L<<28)
+#define BNX2_RPM_DEBUG0_RP_ENA_ACTIVE (1L<<31)
+
+#define BNX2_RPM_DEBUG1 0x00001988
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST (0xffffL<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_IDLE (0L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B6_ALL (1L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B2_IPLLC (2L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B6_IP (4L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B2_IP (8L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_IP_START (16L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_IP (32L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_TCP (64L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_UDP (128L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_AH (256L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ESP (512L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ESP_PAYLOAD (1024L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_DATA (2048L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ADD_CARRY (0x2000L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ADD_CARRYOUT (0x4000L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_LATCH_RESULT (0x8000L<<0)
+#define BNX2_RPM_DEBUG1_HDR_BCNT (0x7ffL<<16)
+#define BNX2_RPM_DEBUG1_UNKNOWN_ETYPE_D (1L<<28)
+#define BNX2_RPM_DEBUG1_VLAN_REMOVED_D2 (1L<<29)
+#define BNX2_RPM_DEBUG1_VLAN_REMOVED_D1 (1L<<30)
+#define BNX2_RPM_DEBUG1_EOF_0XTRA_WD (1L<<31)
+
+#define BNX2_RPM_DEBUG2 0x0000198c
+#define BNX2_RPM_DEBUG2_CMD_HIT_VEC (0xffffL<<0)
+#define BNX2_RPM_DEBUG2_IP_BCNT (0xffL<<16)
+#define BNX2_RPM_DEBUG2_THIS_CMD_M4 (1L<<24)
+#define BNX2_RPM_DEBUG2_THIS_CMD_M3 (1L<<25)
+#define BNX2_RPM_DEBUG2_THIS_CMD_M2 (1L<<26)
+#define BNX2_RPM_DEBUG2_THIS_CMD_M1 (1L<<27)
+#define BNX2_RPM_DEBUG2_IPIPE_EMPTY (1L<<28)
+#define BNX2_RPM_DEBUG2_FM_DISCARD (1L<<29)
+#define BNX2_RPM_DEBUG2_LAST_RULE_IN_FM_D2 (1L<<30)
+#define BNX2_RPM_DEBUG2_LAST_RULE_IN_FM_D1 (1L<<31)
+
+#define BNX2_RPM_DEBUG3 0x00001990
+#define BNX2_RPM_DEBUG3_AVAIL_MBUF_PTR (0x1ffL<<0)
+#define BNX2_RPM_DEBUG3_RDE_RLUPQ_WR_REQ_INT (1L<<9)
+#define BNX2_RPM_DEBUG3_RDE_RBUF_WR_LAST_INT (1L<<10)
+#define BNX2_RPM_DEBUG3_RDE_RBUF_WR_REQ_INT (1L<<11)
+#define BNX2_RPM_DEBUG3_RDE_RBUF_FREE_REQ (1L<<12)
+#define BNX2_RPM_DEBUG3_RDE_RBUF_ALLOC_REQ (1L<<13)
+#define BNX2_RPM_DEBUG3_DFSM_MBUF_NOTAVAIL (1L<<14)
+#define BNX2_RPM_DEBUG3_RBUF_RDE_SOF_DROP (1L<<15)
+#define BNX2_RPM_DEBUG3_DFIFO_VLD_ENTRY_CT (0xfL<<16)
+#define BNX2_RPM_DEBUG3_RDE_SRC_FIFO_ALMFULL (1L<<21)
+#define BNX2_RPM_DEBUG3_DROP_NXT_VLD (1L<<22)
+#define BNX2_RPM_DEBUG3_DROP_NXT (1L<<23)
+#define BNX2_RPM_DEBUG3_FTQ_FSM (0x3L<<24)
+#define BNX2_RPM_DEBUG3_FTQ_FSM_IDLE (0x0L<<24)
+#define BNX2_RPM_DEBUG3_FTQ_FSM_WAIT_ACK (0x1L<<24)
+#define BNX2_RPM_DEBUG3_FTQ_FSM_WAIT_FREE (0x2L<<24)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM (0x3L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_SOF (0x0L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_GET_MBUF (0x1L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_DMA_DATA (0x2L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_DATA (0x3L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_EOF (0x4L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_MF_ACK (0x5L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_DROP_NXT_VLD (0x6L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_DONE (0x7L<<26)
+#define BNX2_RPM_DEBUG3_MBFREE_FSM (1L<<29)
+#define BNX2_RPM_DEBUG3_MBFREE_FSM_IDLE (0L<<29)
+#define BNX2_RPM_DEBUG3_MBFREE_FSM_WAIT_ACK (1L<<29)
+#define BNX2_RPM_DEBUG3_MBALLOC_FSM (1L<<30)
+#define BNX2_RPM_DEBUG3_MBALLOC_FSM_ET_MBUF (0x0L<<30)
+#define BNX2_RPM_DEBUG3_MBALLOC_FSM_IVE_MBUF (0x1L<<30)
+#define BNX2_RPM_DEBUG3_CCODE_EOF_ERROR (1L<<31)
+
+#define BNX2_RPM_DEBUG4 0x00001994
+#define BNX2_RPM_DEBUG4_DFSM_MBUF_CLUSTER (0x1ffffffL<<0)
+#define BNX2_RPM_DEBUG4_DFIFO_CUR_CCODE (0x7L<<25)
+#define BNX2_RPM_DEBUG4_MBWRITE_FSM (0x7L<<28)
+#define BNX2_RPM_DEBUG4_DFIFO_EMPTY (1L<<31)
+
+#define BNX2_RPM_DEBUG5 0x00001998
+#define BNX2_RPM_DEBUG5_RDROP_WPTR (0x1fL<<0)
+#define BNX2_RPM_DEBUG5_RDROP_ACPI_RPTR (0x1fL<<5)
+#define BNX2_RPM_DEBUG5_RDROP_MC_RPTR (0x1fL<<10)
+#define BNX2_RPM_DEBUG5_RDROP_RC_RPTR (0x1fL<<15)
+#define BNX2_RPM_DEBUG5_RDROP_ACPI_EMPTY (1L<<20)
+#define BNX2_RPM_DEBUG5_RDROP_MC_EMPTY (1L<<21)
+#define BNX2_RPM_DEBUG5_RDROP_AEOF_VEC_AT_RDROP_MC_RPTR (1L<<22)
+#define BNX2_RPM_DEBUG5_HOLDREG_WOL_DROP_INT (1L<<23)
+#define BNX2_RPM_DEBUG5_HOLDREG_DISCARD (1L<<24)
+#define BNX2_RPM_DEBUG5_HOLDREG_MBUF_NOTAVAIL (1L<<25)
+#define BNX2_RPM_DEBUG5_HOLDREG_MC_EMPTY (1L<<26)
+#define BNX2_RPM_DEBUG5_HOLDREG_RC_EMPTY (1L<<27)
+#define BNX2_RPM_DEBUG5_HOLDREG_FC_EMPTY (1L<<28)
+#define BNX2_RPM_DEBUG5_HOLDREG_ACPI_EMPTY (1L<<29)
+#define BNX2_RPM_DEBUG5_HOLDREG_FULL_T (1L<<30)
+#define BNX2_RPM_DEBUG5_HOLDREG_RD (1L<<31)
+
+#define BNX2_RPM_DEBUG6 0x0000199c
+#define BNX2_RPM_DEBUG6_ACPI_VEC (0xffffL<<0)
+#define BNX2_RPM_DEBUG6_VEC (0xffffL<<16)
+
+#define BNX2_RPM_DEBUG7 0x000019a0
+#define BNX2_RPM_DEBUG7_RPM_DBG7_LAST_CRC (0xffffffffL<<0)
+
+#define BNX2_RPM_DEBUG8 0x000019a4
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM (0xfL<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_IDLE (0L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_W1_ADDR (1L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_W2_ADDR (2L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_W3_ADDR (3L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_WAIT_THBUF (4L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W3_DATA (5L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W0_ADDR (6L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W1_ADDR (7L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W2_ADDR (8L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W3_ADDR (9L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_WAIT_THBUF (10L<<0)
+#define BNX2_RPM_DEBUG8_COMPARE_AT_W0 (1L<<4)
+#define BNX2_RPM_DEBUG8_COMPARE_AT_W3_DATA (1L<<5)
+#define BNX2_RPM_DEBUG8_COMPARE_AT_SOF_WAIT (1L<<6)
+#define BNX2_RPM_DEBUG8_COMPARE_AT_SOF_W3 (1L<<7)
+#define BNX2_RPM_DEBUG8_COMPARE_AT_SOF_W2 (1L<<8)
+#define BNX2_RPM_DEBUG8_EOF_W_LTEQ6_VLDBYTES (1L<<9)
+#define BNX2_RPM_DEBUG8_EOF_W_LTEQ4_VLDBYTES (1L<<10)
+#define BNX2_RPM_DEBUG8_NXT_EOF_W_12_VLDBYTES (1L<<11)
+#define BNX2_RPM_DEBUG8_EOF_DET (1L<<12)
+#define BNX2_RPM_DEBUG8_SOF_DET (1L<<13)
+#define BNX2_RPM_DEBUG8_WAIT_4_SOF (1L<<14)
+#define BNX2_RPM_DEBUG8_ALL_DONE (1L<<15)
+#define BNX2_RPM_DEBUG8_THBUF_ADDR (0x7fL<<16)
+#define BNX2_RPM_DEBUG8_BYTE_CTR (0xffL<<24)
+
+#define BNX2_RPM_DEBUG9 0x000019a8
+#define BNX2_RPM_DEBUG9_OUTFIFO_COUNT (0x7L<<0)
+#define BNX2_RPM_DEBUG9_RDE_ACPI_RDY (1L<<3)
+#define BNX2_RPM_DEBUG9_VLD_RD_ENTRY_CT (0x7L<<4)
+#define BNX2_RPM_DEBUG9_OUTFIFO_OVERRUN_OCCURRED (1L<<28)
+#define BNX2_RPM_DEBUG9_INFIFO_OVERRUN_OCCURRED (1L<<29)
+#define BNX2_RPM_DEBUG9_ACPI_MATCH_INT (1L<<30)
+#define BNX2_RPM_DEBUG9_ACPI_ENABLE_SYN (1L<<31)
+
+#define BNX2_RPM_ACPI_DBG_BUF_W00 0x000019c0
+#define BNX2_RPM_ACPI_DBG_BUF_W01 0x000019c4
+#define BNX2_RPM_ACPI_DBG_BUF_W02 0x000019c8
+#define BNX2_RPM_ACPI_DBG_BUF_W03 0x000019cc
+#define BNX2_RPM_ACPI_DBG_BUF_W10 0x000019d0
+#define BNX2_RPM_ACPI_DBG_BUF_W11 0x000019d4
+#define BNX2_RPM_ACPI_DBG_BUF_W12 0x000019d8
+#define BNX2_RPM_ACPI_DBG_BUF_W13 0x000019dc
+#define BNX2_RPM_ACPI_DBG_BUF_W20 0x000019e0
+#define BNX2_RPM_ACPI_DBG_BUF_W21 0x000019e4
+#define BNX2_RPM_ACPI_DBG_BUF_W22 0x000019e8
+#define BNX2_RPM_ACPI_DBG_BUF_W23 0x000019ec
+#define BNX2_RPM_ACPI_DBG_BUF_W30 0x000019f0
+#define BNX2_RPM_ACPI_DBG_BUF_W31 0x000019f4
+#define BNX2_RPM_ACPI_DBG_BUF_W32 0x000019f8
+#define BNX2_RPM_ACPI_DBG_BUF_W33 0x000019fc
+
+
+/*
+ * rbuf_reg definition
+ * offset: 0x200000
+ */
+#define BNX2_RBUF_COMMAND 0x00200000
+#define BNX2_RBUF_COMMAND_ENABLED (1L<<0)
+#define BNX2_RBUF_COMMAND_FREE_INIT (1L<<1)
+#define BNX2_RBUF_COMMAND_RAM_INIT (1L<<2)
+#define BNX2_RBUF_COMMAND_OVER_FREE (1L<<4)
+#define BNX2_RBUF_COMMAND_ALLOC_REQ (1L<<5)
+
+#define BNX2_RBUF_STATUS1 0x00200004
+#define BNX2_RBUF_STATUS1_FREE_COUNT (0x3ffL<<0)
+
+#define BNX2_RBUF_STATUS2 0x00200008
+#define BNX2_RBUF_STATUS2_FREE_TAIL (0x3ffL<<0)
+#define BNX2_RBUF_STATUS2_FREE_HEAD (0x3ffL<<16)
+
+#define BNX2_RBUF_CONFIG 0x0020000c
+#define BNX2_RBUF_CONFIG_XOFF_TRIP (0x3ffL<<0)
+#define BNX2_RBUF_CONFIG_XON_TRIP (0x3ffL<<16)
+
+#define BNX2_RBUF_FW_BUF_ALLOC 0x00200010
+#define BNX2_RBUF_FW_BUF_ALLOC_VALUE (0x1ffL<<7)
+
+#define BNX2_RBUF_FW_BUF_FREE 0x00200014
+#define BNX2_RBUF_FW_BUF_FREE_COUNT (0x7fL<<0)
+#define BNX2_RBUF_FW_BUF_FREE_TAIL (0x1ffL<<7)
+#define BNX2_RBUF_FW_BUF_FREE_HEAD (0x1ffL<<16)
+
+#define BNX2_RBUF_FW_BUF_SEL 0x00200018
+#define BNX2_RBUF_FW_BUF_SEL_COUNT (0x7fL<<0)
+#define BNX2_RBUF_FW_BUF_SEL_TAIL (0x1ffL<<7)
+#define BNX2_RBUF_FW_BUF_SEL_HEAD (0x1ffL<<16)
+
+#define BNX2_RBUF_CONFIG2 0x0020001c
+#define BNX2_RBUF_CONFIG2_MAC_DROP_TRIP (0x3ffL<<0)
+#define BNX2_RBUF_CONFIG2_MAC_KEEP_TRIP (0x3ffL<<16)
+
+#define BNX2_RBUF_CONFIG3 0x00200020
+#define BNX2_RBUF_CONFIG3_CU_DROP_TRIP (0x3ffL<<0)
+#define BNX2_RBUF_CONFIG3_CU_KEEP_TRIP (0x3ffL<<16)
+
+#define BNX2_RBUF_PKT_DATA 0x00208000
+#define BNX2_RBUF_CLIST_DATA 0x00210000
+#define BNX2_RBUF_BUF_DATA 0x00220000
+
+
+/*
+ * rv2p_reg definition
+ * offset: 0x2800
+ */
+#define BNX2_RV2P_COMMAND 0x00002800
+#define BNX2_RV2P_COMMAND_ENABLED (1L<<0)
+#define BNX2_RV2P_COMMAND_PROC1_INTRPT (1L<<1)
+#define BNX2_RV2P_COMMAND_PROC2_INTRPT (1L<<2)
+#define BNX2_RV2P_COMMAND_ABORT0 (1L<<4)
+#define BNX2_RV2P_COMMAND_ABORT1 (1L<<5)
+#define BNX2_RV2P_COMMAND_ABORT2 (1L<<6)
+#define BNX2_RV2P_COMMAND_ABORT3 (1L<<7)
+#define BNX2_RV2P_COMMAND_ABORT4 (1L<<8)
+#define BNX2_RV2P_COMMAND_ABORT5 (1L<<9)
+#define BNX2_RV2P_COMMAND_PROC1_RESET (1L<<16)
+#define BNX2_RV2P_COMMAND_PROC2_RESET (1L<<17)
+#define BNX2_RV2P_COMMAND_CTXIF_RESET (1L<<18)
+
+#define BNX2_RV2P_STATUS 0x00002804
+#define BNX2_RV2P_STATUS_ALWAYS_0 (1L<<0)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT0_CNT (1L<<8)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT1_CNT (1L<<9)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT2_CNT (1L<<10)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT3_CNT (1L<<11)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT4_CNT (1L<<12)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT5_CNT (1L<<13)
+
+#define BNX2_RV2P_CONFIG 0x00002808
+#define BNX2_RV2P_CONFIG_STALL_PROC1 (1L<<0)
+#define BNX2_RV2P_CONFIG_STALL_PROC2 (1L<<1)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT0 (1L<<8)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT1 (1L<<9)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT2 (1L<<10)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT3 (1L<<11)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT4 (1L<<12)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT5 (1L<<13)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT0 (1L<<16)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT1 (1L<<17)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT2 (1L<<18)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT3 (1L<<19)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT4 (1L<<20)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT5 (1L<<21)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE (0xfL<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_256 (0L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_512 (1L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_1K (2L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_2K (3L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_4K (4L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_8K (5L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_16K (6L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_32K (7L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_64K (8L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_128K (9L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_256K (10L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_512K (11L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_1M (12L<<24)
+
+#define BNX2_RV2P_GEN_BFR_ADDR_0 0x00002810
+#define BNX2_RV2P_GEN_BFR_ADDR_0_VALUE (0xffffL<<16)
+
+#define BNX2_RV2P_GEN_BFR_ADDR_1 0x00002814
+#define BNX2_RV2P_GEN_BFR_ADDR_1_VALUE (0xffffL<<16)
+
+#define BNX2_RV2P_GEN_BFR_ADDR_2 0x00002818
+#define BNX2_RV2P_GEN_BFR_ADDR_2_VALUE (0xffffL<<16)
+
+#define BNX2_RV2P_GEN_BFR_ADDR_3 0x0000281c
+#define BNX2_RV2P_GEN_BFR_ADDR_3_VALUE (0xffffL<<16)
+
+#define BNX2_RV2P_INSTR_HIGH 0x00002830
+#define BNX2_RV2P_INSTR_HIGH_HIGH (0x1fL<<0)
+
+#define BNX2_RV2P_INSTR_LOW 0x00002834
+#define BNX2_RV2P_PROC1_ADDR_CMD 0x00002838
+#define BNX2_RV2P_PROC1_ADDR_CMD_ADD (0x3ffL<<0)
+#define BNX2_RV2P_PROC1_ADDR_CMD_RDWR (1L<<31)
+
+#define BNX2_RV2P_PROC2_ADDR_CMD 0x0000283c
+#define BNX2_RV2P_PROC2_ADDR_CMD_ADD (0x3ffL<<0)
+#define BNX2_RV2P_PROC2_ADDR_CMD_RDWR (1L<<31)
+
+#define BNX2_RV2P_PROC1_GRC_DEBUG 0x00002840
+#define BNX2_RV2P_PROC2_GRC_DEBUG 0x00002844
+#define BNX2_RV2P_GRC_PROC_DEBUG 0x00002848
+#define BNX2_RV2P_DEBUG_VECT_PEEK 0x0000284c
+#define BNX2_RV2P_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0)
+#define BNX2_RV2P_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11)
+#define BNX2_RV2P_DEBUG_VECT_PEEK_1_SEL (0xfL<<12)
+#define BNX2_RV2P_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16)
+#define BNX2_RV2P_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27)
+#define BNX2_RV2P_DEBUG_VECT_PEEK_2_SEL (0xfL<<28)
+
+#define BNX2_RV2P_PFTQ_DATA 0x00002b40
+#define BNX2_RV2P_PFTQ_CMD 0x00002b78
+#define BNX2_RV2P_PFTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_RV2P_PFTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_RV2P_PFTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_RV2P_PFTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_RV2P_PFTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_RV2P_PFTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_RV2P_PFTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_RV2P_PFTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_RV2P_PFTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_RV2P_PFTQ_CMD_POP (1L<<30)
+#define BNX2_RV2P_PFTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_RV2P_PFTQ_CTL 0x00002b7c
+#define BNX2_RV2P_PFTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_RV2P_PFTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_RV2P_PFTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_RV2P_PFTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_RV2P_PFTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+#define BNX2_RV2P_TFTQ_DATA 0x00002b80
+#define BNX2_RV2P_TFTQ_CMD 0x00002bb8
+#define BNX2_RV2P_TFTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_RV2P_TFTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_RV2P_TFTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_RV2P_TFTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_RV2P_TFTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_RV2P_TFTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_RV2P_TFTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_RV2P_TFTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_RV2P_TFTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_RV2P_TFTQ_CMD_POP (1L<<30)
+#define BNX2_RV2P_TFTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_RV2P_TFTQ_CTL 0x00002bbc
+#define BNX2_RV2P_TFTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_RV2P_TFTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_RV2P_TFTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_RV2P_TFTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_RV2P_TFTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+#define BNX2_RV2P_MFTQ_DATA 0x00002bc0
+#define BNX2_RV2P_MFTQ_CMD 0x00002bf8
+#define BNX2_RV2P_MFTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_RV2P_MFTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_RV2P_MFTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_RV2P_MFTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_RV2P_MFTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_RV2P_MFTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_RV2P_MFTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_RV2P_MFTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_RV2P_MFTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_RV2P_MFTQ_CMD_POP (1L<<30)
+#define BNX2_RV2P_MFTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_RV2P_MFTQ_CTL 0x00002bfc
+#define BNX2_RV2P_MFTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_RV2P_MFTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_RV2P_MFTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_RV2P_MFTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_RV2P_MFTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+
+
+/*
+ * mq_reg definition
+ * offset: 0x3c00
+ */
+#define BNX2_MQ_COMMAND 0x00003c00
+#define BNX2_MQ_COMMAND_ENABLED (1L<<0)
+#define BNX2_MQ_COMMAND_OVERFLOW (1L<<4)
+#define BNX2_MQ_COMMAND_WR_ERROR (1L<<5)
+#define BNX2_MQ_COMMAND_RD_ERROR (1L<<6)
+
+#define BNX2_MQ_STATUS 0x00003c04
+#define BNX2_MQ_STATUS_CTX_ACCESS_STAT (1L<<16)
+#define BNX2_MQ_STATUS_CTX_ACCESS64_STAT (1L<<17)
+#define BNX2_MQ_STATUS_PCI_STALL_STAT (1L<<18)
+
+#define BNX2_MQ_CONFIG 0x00003c08
+#define BNX2_MQ_CONFIG_TX_HIGH_PRI (1L<<0)
+#define BNX2_MQ_CONFIG_HALT_DIS (1L<<1)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE (0x7L<<4)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_256 (0L<<4)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_512 (1L<<4)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_1K (2L<<4)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_2K (3L<<4)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_4K (4L<<4)
+#define BNX2_MQ_CONFIG_MAX_DEPTH (0x7fL<<8)
+#define BNX2_MQ_CONFIG_CUR_DEPTH (0x7fL<<20)
+
+#define BNX2_MQ_ENQUEUE1 0x00003c0c
+#define BNX2_MQ_ENQUEUE1_OFFSET (0x3fL<<2)
+#define BNX2_MQ_ENQUEUE1_CID (0x3fffL<<8)
+#define BNX2_MQ_ENQUEUE1_BYTE_MASK (0xfL<<24)
+#define BNX2_MQ_ENQUEUE1_KNL_MODE (1L<<28)
+
+#define BNX2_MQ_ENQUEUE2 0x00003c10
+#define BNX2_MQ_BAD_WR_ADDR 0x00003c14
+#define BNX2_MQ_BAD_RD_ADDR 0x00003c18
+#define BNX2_MQ_KNL_BYP_WIND_START 0x00003c1c
+#define BNX2_MQ_KNL_BYP_WIND_START_VALUE (0xfffffL<<12)
+
+#define BNX2_MQ_KNL_WIND_END 0x00003c20
+#define BNX2_MQ_KNL_WIND_END_VALUE (0xffffffL<<8)
+
+#define BNX2_MQ_KNL_WRITE_MASK1 0x00003c24
+#define BNX2_MQ_KNL_TX_MASK1 0x00003c28
+#define BNX2_MQ_KNL_CMD_MASK1 0x00003c2c
+#define BNX2_MQ_KNL_COND_ENQUEUE_MASK1 0x00003c30
+#define BNX2_MQ_KNL_RX_V2P_MASK1 0x00003c34
+#define BNX2_MQ_KNL_WRITE_MASK2 0x00003c38
+#define BNX2_MQ_KNL_TX_MASK2 0x00003c3c
+#define BNX2_MQ_KNL_CMD_MASK2 0x00003c40
+#define BNX2_MQ_KNL_COND_ENQUEUE_MASK2 0x00003c44
+#define BNX2_MQ_KNL_RX_V2P_MASK2 0x00003c48
+#define BNX2_MQ_KNL_BYP_WRITE_MASK1 0x00003c4c
+#define BNX2_MQ_KNL_BYP_TX_MASK1 0x00003c50
+#define BNX2_MQ_KNL_BYP_CMD_MASK1 0x00003c54
+#define BNX2_MQ_KNL_BYP_COND_ENQUEUE_MASK1 0x00003c58
+#define BNX2_MQ_KNL_BYP_RX_V2P_MASK1 0x00003c5c
+#define BNX2_MQ_KNL_BYP_WRITE_MASK2 0x00003c60
+#define BNX2_MQ_KNL_BYP_TX_MASK2 0x00003c64
+#define BNX2_MQ_KNL_BYP_CMD_MASK2 0x00003c68
+#define BNX2_MQ_KNL_BYP_COND_ENQUEUE_MASK2 0x00003c6c
+#define BNX2_MQ_KNL_BYP_RX_V2P_MASK2 0x00003c70
+#define BNX2_MQ_MEM_WR_ADDR 0x00003c74
+#define BNX2_MQ_MEM_WR_ADDR_VALUE (0x3fL<<0)
+
+#define BNX2_MQ_MEM_WR_DATA0 0x00003c78
+#define BNX2_MQ_MEM_WR_DATA0_VALUE (0xffffffffL<<0)
+
+#define BNX2_MQ_MEM_WR_DATA1 0x00003c7c
+#define BNX2_MQ_MEM_WR_DATA1_VALUE (0xffffffffL<<0)
+
+#define BNX2_MQ_MEM_WR_DATA2 0x00003c80
+#define BNX2_MQ_MEM_WR_DATA2_VALUE (0x3fffffffL<<0)
+
+#define BNX2_MQ_MEM_RD_ADDR 0x00003c84
+#define BNX2_MQ_MEM_RD_ADDR_VALUE (0x3fL<<0)
+
+#define BNX2_MQ_MEM_RD_DATA0 0x00003c88
+#define BNX2_MQ_MEM_RD_DATA0_VALUE (0xffffffffL<<0)
+
+#define BNX2_MQ_MEM_RD_DATA1 0x00003c8c
+#define BNX2_MQ_MEM_RD_DATA1_VALUE (0xffffffffL<<0)
+
+#define BNX2_MQ_MEM_RD_DATA2 0x00003c90
+#define BNX2_MQ_MEM_RD_DATA2_VALUE (0x3fffffffL<<0)
+
+
+
+/*
+ * tbdr_reg definition
+ * offset: 0x5000
+ */
+#define BNX2_TBDR_COMMAND 0x00005000
+#define BNX2_TBDR_COMMAND_ENABLE (1L<<0)
+#define BNX2_TBDR_COMMAND_SOFT_RST (1L<<1)
+#define BNX2_TBDR_COMMAND_MSTR_ABORT (1L<<4)
+
+#define BNX2_TBDR_STATUS 0x00005004
+#define BNX2_TBDR_STATUS_DMA_WAIT (1L<<0)
+#define BNX2_TBDR_STATUS_FTQ_WAIT (1L<<1)
+#define BNX2_TBDR_STATUS_FIFO_OVERFLOW (1L<<2)
+#define BNX2_TBDR_STATUS_FIFO_UNDERFLOW (1L<<3)
+#define BNX2_TBDR_STATUS_SEARCHMISS_ERROR (1L<<4)
+#define BNX2_TBDR_STATUS_FTQ_ENTRY_CNT (1L<<5)
+#define BNX2_TBDR_STATUS_BURST_CNT (1L<<6)
+
+#define BNX2_TBDR_CONFIG 0x00005008
+#define BNX2_TBDR_CONFIG_MAX_BDS (0xffL<<0)
+#define BNX2_TBDR_CONFIG_SWAP_MODE (1L<<8)
+#define BNX2_TBDR_CONFIG_PRIORITY (1L<<9)
+#define BNX2_TBDR_CONFIG_CACHE_NEXT_PAGE_PTRS (1L<<10)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE (0xfL<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_256 (0L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_512 (1L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_1K (2L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_2K (3L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_4K (4L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_8K (5L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_16K (6L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_32K (7L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_64K (8L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_128K (9L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_256K (10L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_512K (11L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_1M (12L<<24)
+
+#define BNX2_TBDR_DEBUG_VECT_PEEK 0x0000500c
+#define BNX2_TBDR_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0)
+#define BNX2_TBDR_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11)
+#define BNX2_TBDR_DEBUG_VECT_PEEK_1_SEL (0xfL<<12)
+#define BNX2_TBDR_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16)
+#define BNX2_TBDR_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27)
+#define BNX2_TBDR_DEBUG_VECT_PEEK_2_SEL (0xfL<<28)
+
+#define BNX2_TBDR_FTQ_DATA 0x000053c0
+#define BNX2_TBDR_FTQ_CMD 0x000053f8
+#define BNX2_TBDR_FTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_TBDR_FTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_TBDR_FTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_TBDR_FTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_TBDR_FTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_TBDR_FTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_TBDR_FTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_TBDR_FTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_TBDR_FTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_TBDR_FTQ_CMD_POP (1L<<30)
+#define BNX2_TBDR_FTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_TBDR_FTQ_CTL 0x000053fc
+#define BNX2_TBDR_FTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_TBDR_FTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_TBDR_FTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_TBDR_FTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_TBDR_FTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+
+
+/*
+ * tdma_reg definition
+ * offset: 0x5c00
+ */
+#define BNX2_TDMA_COMMAND 0x00005c00
+#define BNX2_TDMA_COMMAND_ENABLED (1L<<0)
+#define BNX2_TDMA_COMMAND_MASTER_ABORT (1L<<4)
+#define BNX2_TDMA_COMMAND_BAD_L2_LENGTH_ABORT (1L<<7)
+
+#define BNX2_TDMA_STATUS 0x00005c04
+#define BNX2_TDMA_STATUS_DMA_WAIT (1L<<0)
+#define BNX2_TDMA_STATUS_PAYLOAD_WAIT (1L<<1)
+#define BNX2_TDMA_STATUS_PATCH_FTQ_WAIT (1L<<2)
+#define BNX2_TDMA_STATUS_LOCK_WAIT (1L<<3)
+#define BNX2_TDMA_STATUS_FTQ_ENTRY_CNT (1L<<16)
+#define BNX2_TDMA_STATUS_BURST_CNT (1L<<17)
+
+#define BNX2_TDMA_CONFIG 0x00005c08
+#define BNX2_TDMA_CONFIG_ONE_DMA (1L<<0)
+#define BNX2_TDMA_CONFIG_ONE_RECORD (1L<<1)
+#define BNX2_TDMA_CONFIG_LIMIT_SZ (0xfL<<4)
+#define BNX2_TDMA_CONFIG_LIMIT_SZ_64 (0L<<4)
+#define BNX2_TDMA_CONFIG_LIMIT_SZ_128 (0x4L<<4)
+#define BNX2_TDMA_CONFIG_LIMIT_SZ_256 (0x6L<<4)
+#define BNX2_TDMA_CONFIG_LIMIT_SZ_512 (0x8L<<4)
+#define BNX2_TDMA_CONFIG_LINE_SZ (0xfL<<8)
+#define BNX2_TDMA_CONFIG_LINE_SZ_64 (0L<<8)
+#define BNX2_TDMA_CONFIG_LINE_SZ_128 (4L<<8)
+#define BNX2_TDMA_CONFIG_LINE_SZ_256 (6L<<8)
+#define BNX2_TDMA_CONFIG_LINE_SZ_512 (8L<<8)
+#define BNX2_TDMA_CONFIG_ALIGN_ENA (1L<<15)
+#define BNX2_TDMA_CONFIG_CHK_L2_BD (1L<<16)
+#define BNX2_TDMA_CONFIG_FIFO_CMP (0xfL<<20)
+
+#define BNX2_TDMA_PAYLOAD_PROD 0x00005c0c
+#define BNX2_TDMA_PAYLOAD_PROD_VALUE (0x1fffL<<3)
+
+#define BNX2_TDMA_DBG_WATCHDOG 0x00005c10
+#define BNX2_TDMA_DBG_TRIGGER 0x00005c14
+#define BNX2_TDMA_DMAD_FSM 0x00005c80
+#define BNX2_TDMA_DMAD_FSM_BD_INVLD (1L<<0)
+#define BNX2_TDMA_DMAD_FSM_PUSH (0xfL<<4)
+#define BNX2_TDMA_DMAD_FSM_ARB_TBDC (0x3L<<8)
+#define BNX2_TDMA_DMAD_FSM_ARB_CTX (1L<<12)
+#define BNX2_TDMA_DMAD_FSM_DR_INTF (1L<<16)
+#define BNX2_TDMA_DMAD_FSM_DMAD (0x7L<<20)
+#define BNX2_TDMA_DMAD_FSM_BD (0xfL<<24)
+
+#define BNX2_TDMA_DMAD_STATUS 0x00005c84
+#define BNX2_TDMA_DMAD_STATUS_RHOLD_PUSH_ENTRY (0x3L<<0)
+#define BNX2_TDMA_DMAD_STATUS_RHOLD_DMAD_ENTRY (0x3L<<4)
+#define BNX2_TDMA_DMAD_STATUS_RHOLD_BD_ENTRY (0x3L<<8)
+#define BNX2_TDMA_DMAD_STATUS_IFTQ_ENUM (0xfL<<12)
+
+#define BNX2_TDMA_DR_INTF_FSM 0x00005c88
+#define BNX2_TDMA_DR_INTF_FSM_L2_COMP (0x3L<<0)
+#define BNX2_TDMA_DR_INTF_FSM_TPATQ (0x7L<<4)
+#define BNX2_TDMA_DR_INTF_FSM_TPBUF (0x3L<<8)
+#define BNX2_TDMA_DR_INTF_FSM_DR_BUF (0x7L<<12)
+#define BNX2_TDMA_DR_INTF_FSM_DMAD (0x7L<<16)
+
+#define BNX2_TDMA_DR_INTF_STATUS 0x00005c8c
+#define BNX2_TDMA_DR_INTF_STATUS_HOLE_PHASE (0x7L<<0)
+#define BNX2_TDMA_DR_INTF_STATUS_DATA_AVAIL (0x3L<<4)
+#define BNX2_TDMA_DR_INTF_STATUS_SHIFT_ADDR (0x7L<<8)
+#define BNX2_TDMA_DR_INTF_STATUS_NXT_PNTR (0xfL<<12)
+#define BNX2_TDMA_DR_INTF_STATUS_BYTE_COUNT (0x7L<<16)
+
+#define BNX2_TDMA_FTQ_DATA 0x00005fc0
+#define BNX2_TDMA_FTQ_CMD 0x00005ff8
+#define BNX2_TDMA_FTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_TDMA_FTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_TDMA_FTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_TDMA_FTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_TDMA_FTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_TDMA_FTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_TDMA_FTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_TDMA_FTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_TDMA_FTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_TDMA_FTQ_CMD_POP (1L<<30)
+#define BNX2_TDMA_FTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_TDMA_FTQ_CTL 0x00005ffc
+#define BNX2_TDMA_FTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_TDMA_FTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_TDMA_FTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_TDMA_FTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_TDMA_FTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+
+
+/*
+ * hc_reg definition
+ * offset: 0x6800
+ */
+#define BNX2_HC_COMMAND 0x00006800
+#define BNX2_HC_COMMAND_ENABLE (1L<<0)
+#define BNX2_HC_COMMAND_SKIP_ABORT (1L<<4)
+#define BNX2_HC_COMMAND_COAL_NOW (1L<<16)
+#define BNX2_HC_COMMAND_COAL_NOW_WO_INT (1L<<17)
+#define BNX2_HC_COMMAND_STATS_NOW (1L<<18)
+#define BNX2_HC_COMMAND_FORCE_INT (0x3L<<19)
+#define BNX2_HC_COMMAND_FORCE_INT_NULL (0L<<19)
+#define BNX2_HC_COMMAND_FORCE_INT_HIGH (1L<<19)
+#define BNX2_HC_COMMAND_FORCE_INT_LOW (2L<<19)
+#define BNX2_HC_COMMAND_FORCE_INT_FREE (3L<<19)
+#define BNX2_HC_COMMAND_CLR_STAT_NOW (1L<<21)
+
+#define BNX2_HC_STATUS 0x00006804
+#define BNX2_HC_STATUS_MASTER_ABORT (1L<<0)
+#define BNX2_HC_STATUS_PARITY_ERROR_STATE (1L<<1)
+#define BNX2_HC_STATUS_PCI_CLK_CNT_STAT (1L<<16)
+#define BNX2_HC_STATUS_CORE_CLK_CNT_STAT (1L<<17)
+#define BNX2_HC_STATUS_NUM_STATUS_BLOCKS_STAT (1L<<18)
+#define BNX2_HC_STATUS_NUM_INT_GEN_STAT (1L<<19)
+#define BNX2_HC_STATUS_NUM_INT_MBOX_WR_STAT (1L<<20)
+#define BNX2_HC_STATUS_CORE_CLKS_TO_HW_INTACK_STAT (1L<<23)
+#define BNX2_HC_STATUS_CORE_CLKS_TO_SW_INTACK_STAT (1L<<24)
+#define BNX2_HC_STATUS_CORE_CLKS_DURING_SW_INTACK_STAT (1L<<25)
+
+#define BNX2_HC_CONFIG 0x00006808
+#define BNX2_HC_CONFIG_COLLECT_STATS (1L<<0)
+#define BNX2_HC_CONFIG_RX_TMR_MODE (1L<<1)
+#define BNX2_HC_CONFIG_TX_TMR_MODE (1L<<2)
+#define BNX2_HC_CONFIG_COM_TMR_MODE (1L<<3)
+#define BNX2_HC_CONFIG_CMD_TMR_MODE (1L<<4)
+#define BNX2_HC_CONFIG_STATISTIC_PRIORITY (1L<<5)
+#define BNX2_HC_CONFIG_STATUS_PRIORITY (1L<<6)
+#define BNX2_HC_CONFIG_STAT_MEM_ADDR (0xffL<<8)
+
+#define BNX2_HC_ATTN_BITS_ENABLE 0x0000680c
+#define BNX2_HC_STATUS_ADDR_L 0x00006810
+#define BNX2_HC_STATUS_ADDR_H 0x00006814
+#define BNX2_HC_STATISTICS_ADDR_L 0x00006818
+#define BNX2_HC_STATISTICS_ADDR_H 0x0000681c
+#define BNX2_HC_TX_QUICK_CONS_TRIP 0x00006820
+#define BNX2_HC_TX_QUICK_CONS_TRIP_VALUE (0xffL<<0)
+#define BNX2_HC_TX_QUICK_CONS_TRIP_INT (0xffL<<16)
+
+#define BNX2_HC_COMP_PROD_TRIP 0x00006824
+#define BNX2_HC_COMP_PROD_TRIP_VALUE (0xffL<<0)
+#define BNX2_HC_COMP_PROD_TRIP_INT (0xffL<<16)
+
+#define BNX2_HC_RX_QUICK_CONS_TRIP 0x00006828
+#define BNX2_HC_RX_QUICK_CONS_TRIP_VALUE (0xffL<<0)
+#define BNX2_HC_RX_QUICK_CONS_TRIP_INT (0xffL<<16)
+
+#define BNX2_HC_RX_TICKS 0x0000682c
+#define BNX2_HC_RX_TICKS_VALUE (0x3ffL<<0)
+#define BNX2_HC_RX_TICKS_INT (0x3ffL<<16)
+
+#define BNX2_HC_TX_TICKS 0x00006830
+#define BNX2_HC_TX_TICKS_VALUE (0x3ffL<<0)
+#define BNX2_HC_TX_TICKS_INT (0x3ffL<<16)
+
+#define BNX2_HC_COM_TICKS 0x00006834
+#define BNX2_HC_COM_TICKS_VALUE (0x3ffL<<0)
+#define BNX2_HC_COM_TICKS_INT (0x3ffL<<16)
+
+#define BNX2_HC_CMD_TICKS 0x00006838
+#define BNX2_HC_CMD_TICKS_VALUE (0x3ffL<<0)
+#define BNX2_HC_CMD_TICKS_INT (0x3ffL<<16)
+
+#define BNX2_HC_PERIODIC_TICKS 0x0000683c
+#define BNX2_HC_PERIODIC_TICKS_HC_PERIODIC_TICKS (0xffffL<<0)
+
+#define BNX2_HC_STAT_COLLECT_TICKS 0x00006840
+#define BNX2_HC_STAT_COLLECT_TICKS_HC_STAT_COLL_TICKS (0xffL<<4)
+
+#define BNX2_HC_STATS_TICKS 0x00006844
+#define BNX2_HC_STATS_TICKS_HC_STAT_TICKS (0xffffL<<8)
+
+#define BNX2_HC_STAT_MEM_DATA 0x0000684c
+#define BNX2_HC_STAT_GEN_SEL_0 0x00006850
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0 (0x7fL<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT0 (0L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT1 (1L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT2 (2L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT3 (3L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT4 (4L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT5 (5L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT6 (6L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT7 (7L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT8 (8L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT9 (9L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT10 (10L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT11 (11L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT0 (12L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT1 (13L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT2 (14L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT3 (15L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT4 (16L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT5 (17L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT6 (18L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT7 (19L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT0 (20L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT1 (21L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT2 (22L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT3 (23L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT4 (24L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT5 (25L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT6 (26L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT7 (27L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT8 (28L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT9 (29L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT10 (30L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT11 (31L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT0 (32L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT1 (33L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT2 (34L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT3 (35L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT0 (36L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT1 (37L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT2 (38L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT3 (39L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT4 (40L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT5 (41L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT6 (42L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT7 (43L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT0 (44L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT1 (45L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT2 (46L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT3 (47L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT4 (48L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT5 (49L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT6 (50L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT7 (51L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_PCI_CLK_CNT (52L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CORE_CLK_CNT (53L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_NUM_STATUS_BLOCKS (54L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_NUM_INT_GEN (55L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_NUM_INT_MBOX_WR (56L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_CORE_CLKS_TO_HW_INTACK (59L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_CORE_CLKS_TO_SW_INTACK (60L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_CORE_CLKS_DURING_SW_INTACK (61L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TSCH_CMD_CNT (62L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TSCH_SLOT_CNT (63L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CSCH_CMD_CNT (64L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CSCH_SLOT_CNT (65L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RLUPQ_VALID_CNT (66L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXPQ_VALID_CNT (67L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXPCQ_VALID_CNT (68L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2PPQ_VALID_CNT (69L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2PMQ_VALID_CNT (70L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2PTQ_VALID_CNT (71L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RDMAQ_VALID_CNT (72L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TSCHQ_VALID_CNT (73L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TBDRQ_VALID_CNT (74L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXPQ_VALID_CNT (75L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TDMAQ_VALID_CNT (76L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPATQ_VALID_CNT (77L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TASQ_VALID_CNT (78L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CSQ_VALID_CNT (79L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CPQ_VALID_CNT (80L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COMXQ_VALID_CNT (81L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COMTQ_VALID_CNT (82L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COMQ_VALID_CNT (83L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MGMQ_VALID_CNT (84L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_READ_TRANSFERS_CNT (85L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_READ_DELAY_PCI_CLKS_CNT (86L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_READ_TRANSFERS_CNT (87L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_READ_DELAY_PCI_CLKS_CNT (88L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_READ_RETRY_AFTER_DATA_CNT (89L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_WRITE_TRANSFERS_CNT (90L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_WRITE_DELAY_PCI_CLKS_CNT (91L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_WRITE_TRANSFERS_CNT (92L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_WRITE_DELAY_PCI_CLKS_CNT (93L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_WRITE_RETRY_AFTER_DATA_CNT (94L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_WR_CNT64 (95L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_RD_CNT64 (96L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_ACC_STALL_CLKS (97L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_LOCK_STALL_CLKS (98L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MBQ_CTX_ACCESS_STAT (99L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MBQ_CTX_ACCESS64_STAT (100L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MBQ_PCI_STALL_STAT (101L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TBDR_FTQ_ENTRY_CNT (102L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TBDR_BURST_CNT (103L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TDMA_FTQ_ENTRY_CNT (104L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TDMA_BURST_CNT (105L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RDMA_FTQ_ENTRY_CNT (106L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RDMA_BURST_CNT (107L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RLUP_MATCH_CNT (108L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_POLL_PASS_CNT (109L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR1_CNT (110L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR2_CNT (111L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR3_CNT (112L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR4_CNT (113L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR5_CNT (114L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT0 (115L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT1 (116L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT2 (117L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT3 (118L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT4 (119L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT5 (120L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RBDC_PROC1_MISS (121L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RBDC_PROC2_MISS (122L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RBDC_BURST_CNT (127L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_1 (0x7fL<<8)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_2 (0x7fL<<16)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_3 (0x7fL<<24)
+
+#define BNX2_HC_STAT_GEN_SEL_1 0x00006854
+#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_4 (0x7fL<<0)
+#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_5 (0x7fL<<8)
+#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_6 (0x7fL<<16)
+#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_7 (0x7fL<<24)
+
+#define BNX2_HC_STAT_GEN_SEL_2 0x00006858
+#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_8 (0x7fL<<0)
+#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_9 (0x7fL<<8)
+#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_10 (0x7fL<<16)
+#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_11 (0x7fL<<24)
+
+#define BNX2_HC_STAT_GEN_SEL_3 0x0000685c
+#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_12 (0x7fL<<0)
+#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_13 (0x7fL<<8)
+#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_14 (0x7fL<<16)
+#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_15 (0x7fL<<24)
+
+#define BNX2_HC_STAT_GEN_STAT0 0x00006888
+#define BNX2_HC_STAT_GEN_STAT1 0x0000688c
+#define BNX2_HC_STAT_GEN_STAT2 0x00006890
+#define BNX2_HC_STAT_GEN_STAT3 0x00006894
+#define BNX2_HC_STAT_GEN_STAT4 0x00006898
+#define BNX2_HC_STAT_GEN_STAT5 0x0000689c
+#define BNX2_HC_STAT_GEN_STAT6 0x000068a0
+#define BNX2_HC_STAT_GEN_STAT7 0x000068a4
+#define BNX2_HC_STAT_GEN_STAT8 0x000068a8
+#define BNX2_HC_STAT_GEN_STAT9 0x000068ac
+#define BNX2_HC_STAT_GEN_STAT10 0x000068b0
+#define BNX2_HC_STAT_GEN_STAT11 0x000068b4
+#define BNX2_HC_STAT_GEN_STAT12 0x000068b8
+#define BNX2_HC_STAT_GEN_STAT13 0x000068bc
+#define BNX2_HC_STAT_GEN_STAT14 0x000068c0
+#define BNX2_HC_STAT_GEN_STAT15 0x000068c4
+#define BNX2_HC_STAT_GEN_STAT_AC0 0x000068c8
+#define BNX2_HC_STAT_GEN_STAT_AC1 0x000068cc
+#define BNX2_HC_STAT_GEN_STAT_AC2 0x000068d0
+#define BNX2_HC_STAT_GEN_STAT_AC3 0x000068d4
+#define BNX2_HC_STAT_GEN_STAT_AC4 0x000068d8
+#define BNX2_HC_STAT_GEN_STAT_AC5 0x000068dc
+#define BNX2_HC_STAT_GEN_STAT_AC6 0x000068e0
+#define BNX2_HC_STAT_GEN_STAT_AC7 0x000068e4
+#define BNX2_HC_STAT_GEN_STAT_AC8 0x000068e8
+#define BNX2_HC_STAT_GEN_STAT_AC9 0x000068ec
+#define BNX2_HC_STAT_GEN_STAT_AC10 0x000068f0
+#define BNX2_HC_STAT_GEN_STAT_AC11 0x000068f4
+#define BNX2_HC_STAT_GEN_STAT_AC12 0x000068f8
+#define BNX2_HC_STAT_GEN_STAT_AC13 0x000068fc
+#define BNX2_HC_STAT_GEN_STAT_AC14 0x00006900
+#define BNX2_HC_STAT_GEN_STAT_AC15 0x00006904
+#define BNX2_HC_VIS 0x00006908
+#define BNX2_HC_VIS_STAT_BUILD_STATE (0xfL<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_IDLE (0L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_START (1L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_REQUEST (2L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_UPDATE64 (3L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_UPDATE32 (4L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_UPDATE_DONE (5L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_DMA (6L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_CONTROL (7L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_LOW (8L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_HIGH (9L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_DATA (10L<<0)
+#define BNX2_HC_VIS_DMA_STAT_STATE (0xfL<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_IDLE (0L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_STATUS_PARAM (1L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_STATUS_DMA (2L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_WRITE_COMP (3L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_COMP (4L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_STATISTIC_PARAM (5L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_STATISTIC_DMA (6L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_WRITE_COMP_1 (7L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_WRITE_COMP_2 (8L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_WAIT (9L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_ABORT (15L<<8)
+#define BNX2_HC_VIS_DMA_MSI_STATE (0x7L<<12)
+#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE (0x3L<<15)
+#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE_IDLE (0L<<15)
+#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE_COUNT (1L<<15)
+#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE_START (2L<<15)
+
+#define BNX2_HC_VIS_1 0x0000690c
+#define BNX2_HC_VIS_1_HW_INTACK_STATE (1L<<4)
+#define BNX2_HC_VIS_1_HW_INTACK_STATE_IDLE (0L<<4)
+#define BNX2_HC_VIS_1_HW_INTACK_STATE_COUNT (1L<<4)
+#define BNX2_HC_VIS_1_SW_INTACK_STATE (1L<<5)
+#define BNX2_HC_VIS_1_SW_INTACK_STATE_IDLE (0L<<5)
+#define BNX2_HC_VIS_1_SW_INTACK_STATE_COUNT (1L<<5)
+#define BNX2_HC_VIS_1_DURING_SW_INTACK_STATE (1L<<6)
+#define BNX2_HC_VIS_1_DURING_SW_INTACK_STATE_IDLE (0L<<6)
+#define BNX2_HC_VIS_1_DURING_SW_INTACK_STATE_COUNT (1L<<6)
+#define BNX2_HC_VIS_1_MAILBOX_COUNT_STATE (1L<<7)
+#define BNX2_HC_VIS_1_MAILBOX_COUNT_STATE_IDLE (0L<<7)
+#define BNX2_HC_VIS_1_MAILBOX_COUNT_STATE_COUNT (1L<<7)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE (0xfL<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_IDLE (0L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_DMA (1L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_UPDATE (2L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_ASSIGN (3L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_WAIT (4L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_REG_UPDATE (5L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_REG_ASSIGN (6L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_REG_WAIT (7L<<17)
+#define BNX2_HC_VIS_1_RAM_WR_ARB_STATE (0x3L<<21)
+#define BNX2_HC_VIS_1_RAM_WR_ARB_STATE_NORMAL (0L<<21)
+#define BNX2_HC_VIS_1_RAM_WR_ARB_STATE_CLEAR (1L<<21)
+#define BNX2_HC_VIS_1_INT_GEN_STATE (1L<<23)
+#define BNX2_HC_VIS_1_INT_GEN_STATE_DLE (0L<<23)
+#define BNX2_HC_VIS_1_INT_GEN_STATE_NTERRUPT (1L<<23)
+#define BNX2_HC_VIS_1_STAT_CHAN_ID (0x7L<<24)
+#define BNX2_HC_VIS_1_INT_B (1L<<27)
+
+#define BNX2_HC_DEBUG_VECT_PEEK 0x00006910
+#define BNX2_HC_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0)
+#define BNX2_HC_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11)
+#define BNX2_HC_DEBUG_VECT_PEEK_1_SEL (0xfL<<12)
+#define BNX2_HC_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16)
+#define BNX2_HC_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27)
+#define BNX2_HC_DEBUG_VECT_PEEK_2_SEL (0xfL<<28)
+
+
+
+/*
+ * txp_reg definition
+ * offset: 0x40000
+ */
+#define BNX2_TXP_CPU_MODE 0x00045000
+#define BNX2_TXP_CPU_MODE_LOCAL_RST (1L<<0)
+#define BNX2_TXP_CPU_MODE_STEP_ENA (1L<<1)
+#define BNX2_TXP_CPU_MODE_PAGE_0_DATA_ENA (1L<<2)
+#define BNX2_TXP_CPU_MODE_PAGE_0_INST_ENA (1L<<3)
+#define BNX2_TXP_CPU_MODE_MSG_BIT1 (1L<<6)
+#define BNX2_TXP_CPU_MODE_INTERRUPT_ENA (1L<<7)
+#define BNX2_TXP_CPU_MODE_SOFT_HALT (1L<<10)
+#define BNX2_TXP_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11)
+#define BNX2_TXP_CPU_MODE_BAD_INST_HALT_ENA (1L<<12)
+#define BNX2_TXP_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13)
+#define BNX2_TXP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15)
+
+#define BNX2_TXP_CPU_STATE 0x00045004
+#define BNX2_TXP_CPU_STATE_BREAKPOINT (1L<<0)
+#define BNX2_TXP_CPU_STATE_BAD_INST_HALTED (1L<<2)
+#define BNX2_TXP_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3)
+#define BNX2_TXP_CPU_STATE_PAGE_0_INST_HALTED (1L<<4)
+#define BNX2_TXP_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5)
+#define BNX2_TXP_CPU_STATE_BAD_pc_HALTED (1L<<6)
+#define BNX2_TXP_CPU_STATE_ALIGN_HALTED (1L<<7)
+#define BNX2_TXP_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
+#define BNX2_TXP_CPU_STATE_SOFT_HALTED (1L<<10)
+#define BNX2_TXP_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
+#define BNX2_TXP_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_TXP_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
+#define BNX2_TXP_CPU_STATE_INST_FETCH_STALL (1L<<15)
+#define BNX2_TXP_CPU_STATE_BLOCKED_READ (1L<<31)
+
+#define BNX2_TXP_CPU_EVENT_MASK 0x00045008
+#define BNX2_TXP_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0)
+#define BNX2_TXP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2)
+#define BNX2_TXP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3)
+#define BNX2_TXP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4)
+#define BNX2_TXP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5)
+#define BNX2_TXP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6)
+#define BNX2_TXP_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7)
+#define BNX2_TXP_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8)
+#define BNX2_TXP_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10)
+#define BNX2_TXP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11)
+#define BNX2_TXP_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12)
+
+#define BNX2_TXP_CPU_PROGRAM_COUNTER 0x0004501c
+#define BNX2_TXP_CPU_INSTRUCTION 0x00045020
+#define BNX2_TXP_CPU_DATA_ACCESS 0x00045024
+#define BNX2_TXP_CPU_INTERRUPT_ENABLE 0x00045028
+#define BNX2_TXP_CPU_INTERRUPT_VECTOR 0x0004502c
+#define BNX2_TXP_CPU_INTERRUPT_SAVED_PC 0x00045030
+#define BNX2_TXP_CPU_HW_BREAKPOINT 0x00045034
+#define BNX2_TXP_CPU_HW_BREAKPOINT_DISABLE (1L<<0)
+#define BNX2_TXP_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2)
+
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK 0x00045038
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0)
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11)
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12)
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16)
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27)
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28)
+
+#define BNX2_TXP_CPU_LAST_BRANCH_ADDR 0x00045048
+#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1)
+#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1)
+#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1)
+#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2)
+
+#define BNX2_TXP_CPU_REG_FILE 0x00045200
+#define BNX2_TXP_FTQ_DATA 0x000453c0
+#define BNX2_TXP_FTQ_CMD 0x000453f8
+#define BNX2_TXP_FTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_TXP_FTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_TXP_FTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_TXP_FTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_TXP_FTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_TXP_FTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_TXP_FTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_TXP_FTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_TXP_FTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_TXP_FTQ_CMD_POP (1L<<30)
+#define BNX2_TXP_FTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_TXP_FTQ_CTL 0x000453fc
+#define BNX2_TXP_FTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_TXP_FTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_TXP_FTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_TXP_FTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_TXP_FTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+#define BNX2_TXP_SCRATCH 0x00060000
+
+
+/*
+ * tpat_reg definition
+ * offset: 0x80000
+ */
+#define BNX2_TPAT_CPU_MODE 0x00085000
+#define BNX2_TPAT_CPU_MODE_LOCAL_RST (1L<<0)
+#define BNX2_TPAT_CPU_MODE_STEP_ENA (1L<<1)
+#define BNX2_TPAT_CPU_MODE_PAGE_0_DATA_ENA (1L<<2)
+#define BNX2_TPAT_CPU_MODE_PAGE_0_INST_ENA (1L<<3)
+#define BNX2_TPAT_CPU_MODE_MSG_BIT1 (1L<<6)
+#define BNX2_TPAT_CPU_MODE_INTERRUPT_ENA (1L<<7)
+#define BNX2_TPAT_CPU_MODE_SOFT_HALT (1L<<10)
+#define BNX2_TPAT_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11)
+#define BNX2_TPAT_CPU_MODE_BAD_INST_HALT_ENA (1L<<12)
+#define BNX2_TPAT_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13)
+#define BNX2_TPAT_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15)
+
+#define BNX2_TPAT_CPU_STATE 0x00085004
+#define BNX2_TPAT_CPU_STATE_BREAKPOINT (1L<<0)
+#define BNX2_TPAT_CPU_STATE_BAD_INST_HALTED (1L<<2)
+#define BNX2_TPAT_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3)
+#define BNX2_TPAT_CPU_STATE_PAGE_0_INST_HALTED (1L<<4)
+#define BNX2_TPAT_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5)
+#define BNX2_TPAT_CPU_STATE_BAD_pc_HALTED (1L<<6)
+#define BNX2_TPAT_CPU_STATE_ALIGN_HALTED (1L<<7)
+#define BNX2_TPAT_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
+#define BNX2_TPAT_CPU_STATE_SOFT_HALTED (1L<<10)
+#define BNX2_TPAT_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
+#define BNX2_TPAT_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_TPAT_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
+#define BNX2_TPAT_CPU_STATE_INST_FETCH_STALL (1L<<15)
+#define BNX2_TPAT_CPU_STATE_BLOCKED_READ (1L<<31)
+
+#define BNX2_TPAT_CPU_EVENT_MASK 0x00085008
+#define BNX2_TPAT_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0)
+#define BNX2_TPAT_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2)
+#define BNX2_TPAT_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3)
+#define BNX2_TPAT_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4)
+#define BNX2_TPAT_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5)
+#define BNX2_TPAT_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6)
+#define BNX2_TPAT_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7)
+#define BNX2_TPAT_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8)
+#define BNX2_TPAT_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10)
+#define BNX2_TPAT_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11)
+#define BNX2_TPAT_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12)
+
+#define BNX2_TPAT_CPU_PROGRAM_COUNTER 0x0008501c
+#define BNX2_TPAT_CPU_INSTRUCTION 0x00085020
+#define BNX2_TPAT_CPU_DATA_ACCESS 0x00085024
+#define BNX2_TPAT_CPU_INTERRUPT_ENABLE 0x00085028
+#define BNX2_TPAT_CPU_INTERRUPT_VECTOR 0x0008502c
+#define BNX2_TPAT_CPU_INTERRUPT_SAVED_PC 0x00085030
+#define BNX2_TPAT_CPU_HW_BREAKPOINT 0x00085034
+#define BNX2_TPAT_CPU_HW_BREAKPOINT_DISABLE (1L<<0)
+#define BNX2_TPAT_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2)
+
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK 0x00085038
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0)
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11)
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12)
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16)
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27)
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28)
+
+#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR 0x00085048
+#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1)
+#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1)
+#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1)
+#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2)
+
+#define BNX2_TPAT_CPU_REG_FILE 0x00085200
+#define BNX2_TPAT_FTQ_DATA 0x000853c0
+#define BNX2_TPAT_FTQ_CMD 0x000853f8
+#define BNX2_TPAT_FTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_TPAT_FTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_TPAT_FTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_TPAT_FTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_TPAT_FTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_TPAT_FTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_TPAT_FTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_TPAT_FTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_TPAT_FTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_TPAT_FTQ_CMD_POP (1L<<30)
+#define BNX2_TPAT_FTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_TPAT_FTQ_CTL 0x000853fc
+#define BNX2_TPAT_FTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_TPAT_FTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_TPAT_FTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_TPAT_FTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_TPAT_FTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+#define BNX2_TPAT_SCRATCH 0x000a0000
+
+
+/*
+ * rxp_reg definition
+ * offset: 0xc0000
+ */
+#define BNX2_RXP_CPU_MODE 0x000c5000
+#define BNX2_RXP_CPU_MODE_LOCAL_RST (1L<<0)
+#define BNX2_RXP_CPU_MODE_STEP_ENA (1L<<1)
+#define BNX2_RXP_CPU_MODE_PAGE_0_DATA_ENA (1L<<2)
+#define BNX2_RXP_CPU_MODE_PAGE_0_INST_ENA (1L<<3)
+#define BNX2_RXP_CPU_MODE_MSG_BIT1 (1L<<6)
+#define BNX2_RXP_CPU_MODE_INTERRUPT_ENA (1L<<7)
+#define BNX2_RXP_CPU_MODE_SOFT_HALT (1L<<10)
+#define BNX2_RXP_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11)
+#define BNX2_RXP_CPU_MODE_BAD_INST_HALT_ENA (1L<<12)
+#define BNX2_RXP_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13)
+#define BNX2_RXP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15)
+
+#define BNX2_RXP_CPU_STATE 0x000c5004
+#define BNX2_RXP_CPU_STATE_BREAKPOINT (1L<<0)
+#define BNX2_RXP_CPU_STATE_BAD_INST_HALTED (1L<<2)
+#define BNX2_RXP_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3)
+#define BNX2_RXP_CPU_STATE_PAGE_0_INST_HALTED (1L<<4)
+#define BNX2_RXP_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5)
+#define BNX2_RXP_CPU_STATE_BAD_pc_HALTED (1L<<6)
+#define BNX2_RXP_CPU_STATE_ALIGN_HALTED (1L<<7)
+#define BNX2_RXP_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
+#define BNX2_RXP_CPU_STATE_SOFT_HALTED (1L<<10)
+#define BNX2_RXP_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
+#define BNX2_RXP_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_RXP_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
+#define BNX2_RXP_CPU_STATE_INST_FETCH_STALL (1L<<15)
+#define BNX2_RXP_CPU_STATE_BLOCKED_READ (1L<<31)
+
+#define BNX2_RXP_CPU_EVENT_MASK 0x000c5008
+#define BNX2_RXP_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0)
+#define BNX2_RXP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2)
+#define BNX2_RXP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3)
+#define BNX2_RXP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4)
+#define BNX2_RXP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5)
+#define BNX2_RXP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6)
+#define BNX2_RXP_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7)
+#define BNX2_RXP_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8)
+#define BNX2_RXP_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10)
+#define BNX2_RXP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11)
+#define BNX2_RXP_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12)
+
+#define BNX2_RXP_CPU_PROGRAM_COUNTER 0x000c501c
+#define BNX2_RXP_CPU_INSTRUCTION 0x000c5020
+#define BNX2_RXP_CPU_DATA_ACCESS 0x000c5024
+#define BNX2_RXP_CPU_INTERRUPT_ENABLE 0x000c5028
+#define BNX2_RXP_CPU_INTERRUPT_VECTOR 0x000c502c
+#define BNX2_RXP_CPU_INTERRUPT_SAVED_PC 0x000c5030
+#define BNX2_RXP_CPU_HW_BREAKPOINT 0x000c5034
+#define BNX2_RXP_CPU_HW_BREAKPOINT_DISABLE (1L<<0)
+#define BNX2_RXP_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2)
+
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK 0x000c5038
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0)
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11)
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12)
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16)
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27)
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28)
+
+#define BNX2_RXP_CPU_LAST_BRANCH_ADDR 0x000c5048
+#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1)
+#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1)
+#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1)
+#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2)
+
+#define BNX2_RXP_CPU_REG_FILE 0x000c5200
+#define BNX2_RXP_CFTQ_DATA 0x000c5380
+#define BNX2_RXP_CFTQ_CMD 0x000c53b8
+#define BNX2_RXP_CFTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_RXP_CFTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_RXP_CFTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_RXP_CFTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_RXP_CFTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_RXP_CFTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_RXP_CFTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_RXP_CFTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_RXP_CFTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_RXP_CFTQ_CMD_POP (1L<<30)
+#define BNX2_RXP_CFTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_RXP_CFTQ_CTL 0x000c53bc
+#define BNX2_RXP_CFTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_RXP_CFTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_RXP_CFTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_RXP_CFTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_RXP_CFTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+#define BNX2_RXP_FTQ_DATA 0x000c53c0
+#define BNX2_RXP_FTQ_CMD 0x000c53f8
+#define BNX2_RXP_FTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_RXP_FTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_RXP_FTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_RXP_FTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_RXP_FTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_RXP_FTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_RXP_FTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_RXP_FTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_RXP_FTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_RXP_FTQ_CMD_POP (1L<<30)
+#define BNX2_RXP_FTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_RXP_FTQ_CTL 0x000c53fc
+#define BNX2_RXP_FTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_RXP_FTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_RXP_FTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_RXP_FTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_RXP_FTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+#define BNX2_RXP_SCRATCH 0x000e0000
+
+
+/*
+ * com_reg definition
+ * offset: 0x100000
+ */
+#define BNX2_COM_CPU_MODE 0x00105000
+#define BNX2_COM_CPU_MODE_LOCAL_RST (1L<<0)
+#define BNX2_COM_CPU_MODE_STEP_ENA (1L<<1)
+#define BNX2_COM_CPU_MODE_PAGE_0_DATA_ENA (1L<<2)
+#define BNX2_COM_CPU_MODE_PAGE_0_INST_ENA (1L<<3)
+#define BNX2_COM_CPU_MODE_MSG_BIT1 (1L<<6)
+#define BNX2_COM_CPU_MODE_INTERRUPT_ENA (1L<<7)
+#define BNX2_COM_CPU_MODE_SOFT_HALT (1L<<10)
+#define BNX2_COM_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11)
+#define BNX2_COM_CPU_MODE_BAD_INST_HALT_ENA (1L<<12)
+#define BNX2_COM_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13)
+#define BNX2_COM_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15)
+
+#define BNX2_COM_CPU_STATE 0x00105004
+#define BNX2_COM_CPU_STATE_BREAKPOINT (1L<<0)
+#define BNX2_COM_CPU_STATE_BAD_INST_HALTED (1L<<2)
+#define BNX2_COM_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3)
+#define BNX2_COM_CPU_STATE_PAGE_0_INST_HALTED (1L<<4)
+#define BNX2_COM_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5)
+#define BNX2_COM_CPU_STATE_BAD_pc_HALTED (1L<<6)
+#define BNX2_COM_CPU_STATE_ALIGN_HALTED (1L<<7)
+#define BNX2_COM_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
+#define BNX2_COM_CPU_STATE_SOFT_HALTED (1L<<10)
+#define BNX2_COM_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
+#define BNX2_COM_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_COM_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
+#define BNX2_COM_CPU_STATE_INST_FETCH_STALL (1L<<15)
+#define BNX2_COM_CPU_STATE_BLOCKED_READ (1L<<31)
+
+#define BNX2_COM_CPU_EVENT_MASK 0x00105008
+#define BNX2_COM_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0)
+#define BNX2_COM_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2)
+#define BNX2_COM_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3)
+#define BNX2_COM_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4)
+#define BNX2_COM_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5)
+#define BNX2_COM_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6)
+#define BNX2_COM_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7)
+#define BNX2_COM_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8)
+#define BNX2_COM_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10)
+#define BNX2_COM_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11)
+#define BNX2_COM_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12)
+
+#define BNX2_COM_CPU_PROGRAM_COUNTER 0x0010501c
+#define BNX2_COM_CPU_INSTRUCTION 0x00105020
+#define BNX2_COM_CPU_DATA_ACCESS 0x00105024
+#define BNX2_COM_CPU_INTERRUPT_ENABLE 0x00105028
+#define BNX2_COM_CPU_INTERRUPT_VECTOR 0x0010502c
+#define BNX2_COM_CPU_INTERRUPT_SAVED_PC 0x00105030
+#define BNX2_COM_CPU_HW_BREAKPOINT 0x00105034
+#define BNX2_COM_CPU_HW_BREAKPOINT_DISABLE (1L<<0)
+#define BNX2_COM_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2)
+
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK 0x00105038
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0)
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11)
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12)
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16)
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27)
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28)
+
+#define BNX2_COM_CPU_LAST_BRANCH_ADDR 0x00105048
+#define BNX2_COM_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1)
+#define BNX2_COM_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1)
+#define BNX2_COM_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1)
+#define BNX2_COM_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2)
+
+#define BNX2_COM_CPU_REG_FILE 0x00105200
+#define BNX2_COM_COMXQ_FTQ_DATA 0x00105340
+#define BNX2_COM_COMXQ_FTQ_CMD 0x00105378
+#define BNX2_COM_COMXQ_FTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_COM_COMXQ_FTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_COM_COMXQ_FTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_COM_COMXQ_FTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_COM_COMXQ_FTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_COM_COMXQ_FTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_COM_COMXQ_FTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_COM_COMXQ_FTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_COM_COMXQ_FTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_COM_COMXQ_FTQ_CMD_POP (1L<<30)
+#define BNX2_COM_COMXQ_FTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_COM_COMXQ_FTQ_CTL 0x0010537c
+#define BNX2_COM_COMXQ_FTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_COM_COMXQ_FTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_COM_COMXQ_FTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_COM_COMXQ_FTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_COM_COMXQ_FTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+#define BNX2_COM_COMTQ_FTQ_DATA 0x00105380
+#define BNX2_COM_COMTQ_FTQ_CMD 0x001053b8
+#define BNX2_COM_COMTQ_FTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_COM_COMTQ_FTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_COM_COMTQ_FTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_COM_COMTQ_FTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_COM_COMTQ_FTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_COM_COMTQ_FTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_COM_COMTQ_FTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_COM_COMTQ_FTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_COM_COMTQ_FTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_COM_COMTQ_FTQ_CMD_POP (1L<<30)
+#define BNX2_COM_COMTQ_FTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_COM_COMTQ_FTQ_CTL 0x001053bc
+#define BNX2_COM_COMTQ_FTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_COM_COMTQ_FTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_COM_COMTQ_FTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_COM_COMTQ_FTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_COM_COMTQ_FTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+#define BNX2_COM_COMQ_FTQ_DATA 0x001053c0
+#define BNX2_COM_COMQ_FTQ_CMD 0x001053f8
+#define BNX2_COM_COMQ_FTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_COM_COMQ_FTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_COM_COMQ_FTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_COM_COMQ_FTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_COM_COMQ_FTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_COM_COMQ_FTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_COM_COMQ_FTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_COM_COMQ_FTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_COM_COMQ_FTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_COM_COMQ_FTQ_CMD_POP (1L<<30)
+#define BNX2_COM_COMQ_FTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_COM_COMQ_FTQ_CTL 0x001053fc
+#define BNX2_COM_COMQ_FTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_COM_COMQ_FTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_COM_COMQ_FTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_COM_COMQ_FTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_COM_COMQ_FTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+#define BNX2_COM_SCRATCH 0x00120000
+
+
+/*
+ * cp_reg definition
+ * offset: 0x180000
+ */
+#define BNX2_CP_CPU_MODE 0x00185000
+#define BNX2_CP_CPU_MODE_LOCAL_RST (1L<<0)
+#define BNX2_CP_CPU_MODE_STEP_ENA (1L<<1)
+#define BNX2_CP_CPU_MODE_PAGE_0_DATA_ENA (1L<<2)
+#define BNX2_CP_CPU_MODE_PAGE_0_INST_ENA (1L<<3)
+#define BNX2_CP_CPU_MODE_MSG_BIT1 (1L<<6)
+#define BNX2_CP_CPU_MODE_INTERRUPT_ENA (1L<<7)
+#define BNX2_CP_CPU_MODE_SOFT_HALT (1L<<10)
+#define BNX2_CP_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11)
+#define BNX2_CP_CPU_MODE_BAD_INST_HALT_ENA (1L<<12)
+#define BNX2_CP_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13)
+#define BNX2_CP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15)
+
+#define BNX2_CP_CPU_STATE 0x00185004
+#define BNX2_CP_CPU_STATE_BREAKPOINT (1L<<0)
+#define BNX2_CP_CPU_STATE_BAD_INST_HALTED (1L<<2)
+#define BNX2_CP_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3)
+#define BNX2_CP_CPU_STATE_PAGE_0_INST_HALTED (1L<<4)
+#define BNX2_CP_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5)
+#define BNX2_CP_CPU_STATE_BAD_pc_HALTED (1L<<6)
+#define BNX2_CP_CPU_STATE_ALIGN_HALTED (1L<<7)
+#define BNX2_CP_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
+#define BNX2_CP_CPU_STATE_SOFT_HALTED (1L<<10)
+#define BNX2_CP_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
+#define BNX2_CP_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_CP_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
+#define BNX2_CP_CPU_STATE_INST_FETCH_STALL (1L<<15)
+#define BNX2_CP_CPU_STATE_BLOCKED_READ (1L<<31)
+
+#define BNX2_CP_CPU_EVENT_MASK 0x00185008
+#define BNX2_CP_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0)
+#define BNX2_CP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2)
+#define BNX2_CP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3)
+#define BNX2_CP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4)
+#define BNX2_CP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5)
+#define BNX2_CP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6)
+#define BNX2_CP_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7)
+#define BNX2_CP_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8)
+#define BNX2_CP_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10)
+#define BNX2_CP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11)
+#define BNX2_CP_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12)
+
+#define BNX2_CP_CPU_PROGRAM_COUNTER 0x0018501c
+#define BNX2_CP_CPU_INSTRUCTION 0x00185020
+#define BNX2_CP_CPU_DATA_ACCESS 0x00185024
+#define BNX2_CP_CPU_INTERRUPT_ENABLE 0x00185028
+#define BNX2_CP_CPU_INTERRUPT_VECTOR 0x0018502c
+#define BNX2_CP_CPU_INTERRUPT_SAVED_PC 0x00185030
+#define BNX2_CP_CPU_HW_BREAKPOINT 0x00185034
+#define BNX2_CP_CPU_HW_BREAKPOINT_DISABLE (1L<<0)
+#define BNX2_CP_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2)
+
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK 0x00185038
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0)
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11)
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12)
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16)
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27)
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28)
+
+#define BNX2_CP_CPU_LAST_BRANCH_ADDR 0x00185048
+#define BNX2_CP_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1)
+#define BNX2_CP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1)
+#define BNX2_CP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1)
+#define BNX2_CP_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2)
+
+#define BNX2_CP_CPU_REG_FILE 0x00185200
+#define BNX2_CP_CPQ_FTQ_DATA 0x001853c0
+#define BNX2_CP_CPQ_FTQ_CMD 0x001853f8
+#define BNX2_CP_CPQ_FTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_CP_CPQ_FTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_CP_CPQ_FTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_CP_CPQ_FTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_CP_CPQ_FTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_CP_CPQ_FTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_CP_CPQ_FTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_CP_CPQ_FTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_CP_CPQ_FTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_CP_CPQ_FTQ_CMD_POP (1L<<30)
+#define BNX2_CP_CPQ_FTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_CP_CPQ_FTQ_CTL 0x001853fc
+#define BNX2_CP_CPQ_FTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_CP_CPQ_FTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_CP_CPQ_FTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_CP_CPQ_FTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_CP_CPQ_FTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+#define BNX2_CP_SCRATCH 0x001a0000
+
+
+/*
+ * mcp_reg definition
+ * offset: 0x140000
+ */
+#define BNX2_MCP_CPU_MODE 0x00145000
+#define BNX2_MCP_CPU_MODE_LOCAL_RST (1L<<0)
+#define BNX2_MCP_CPU_MODE_STEP_ENA (1L<<1)
+#define BNX2_MCP_CPU_MODE_PAGE_0_DATA_ENA (1L<<2)
+#define BNX2_MCP_CPU_MODE_PAGE_0_INST_ENA (1L<<3)
+#define BNX2_MCP_CPU_MODE_MSG_BIT1 (1L<<6)
+#define BNX2_MCP_CPU_MODE_INTERRUPT_ENA (1L<<7)
+#define BNX2_MCP_CPU_MODE_SOFT_HALT (1L<<10)
+#define BNX2_MCP_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11)
+#define BNX2_MCP_CPU_MODE_BAD_INST_HALT_ENA (1L<<12)
+#define BNX2_MCP_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13)
+#define BNX2_MCP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15)
+
+#define BNX2_MCP_CPU_STATE 0x00145004
+#define BNX2_MCP_CPU_STATE_BREAKPOINT (1L<<0)
+#define BNX2_MCP_CPU_STATE_BAD_INST_HALTED (1L<<2)
+#define BNX2_MCP_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3)
+#define BNX2_MCP_CPU_STATE_PAGE_0_INST_HALTED (1L<<4)
+#define BNX2_MCP_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5)
+#define BNX2_MCP_CPU_STATE_BAD_pc_HALTED (1L<<6)
+#define BNX2_MCP_CPU_STATE_ALIGN_HALTED (1L<<7)
+#define BNX2_MCP_CPU_STATE_FIO_ABORT_HALTED (1L<<8)
+#define BNX2_MCP_CPU_STATE_SOFT_HALTED (1L<<10)
+#define BNX2_MCP_CPU_STATE_SPAD_UNDERFLOW (1L<<11)
+#define BNX2_MCP_CPU_STATE_INTERRRUPT (1L<<12)
+#define BNX2_MCP_CPU_STATE_DATA_ACCESS_STALL (1L<<14)
+#define BNX2_MCP_CPU_STATE_INST_FETCH_STALL (1L<<15)
+#define BNX2_MCP_CPU_STATE_BLOCKED_READ (1L<<31)
+
+#define BNX2_MCP_CPU_EVENT_MASK 0x00145008
+#define BNX2_MCP_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0)
+#define BNX2_MCP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2)
+#define BNX2_MCP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3)
+#define BNX2_MCP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4)
+#define BNX2_MCP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5)
+#define BNX2_MCP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6)
+#define BNX2_MCP_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7)
+#define BNX2_MCP_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8)
+#define BNX2_MCP_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10)
+#define BNX2_MCP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11)
+#define BNX2_MCP_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12)
+
+#define BNX2_MCP_CPU_PROGRAM_COUNTER 0x0014501c
+#define BNX2_MCP_CPU_INSTRUCTION 0x00145020
+#define BNX2_MCP_CPU_DATA_ACCESS 0x00145024
+#define BNX2_MCP_CPU_INTERRUPT_ENABLE 0x00145028
+#define BNX2_MCP_CPU_INTERRUPT_VECTOR 0x0014502c
+#define BNX2_MCP_CPU_INTERRUPT_SAVED_PC 0x00145030
+#define BNX2_MCP_CPU_HW_BREAKPOINT 0x00145034
+#define BNX2_MCP_CPU_HW_BREAKPOINT_DISABLE (1L<<0)
+#define BNX2_MCP_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2)
+
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK 0x00145038
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0)
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11)
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12)
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16)
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27)
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28)
+
+#define BNX2_MCP_CPU_LAST_BRANCH_ADDR 0x00145048
+#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1)
+#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1)
+#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1)
+#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2)
+
+#define BNX2_MCP_CPU_REG_FILE 0x00145200
+#define BNX2_MCP_MCPQ_FTQ_DATA 0x001453c0
+#define BNX2_MCP_MCPQ_FTQ_CMD 0x001453f8
+#define BNX2_MCP_MCPQ_FTQ_CMD_OFFSET (0x3ffL<<0)
+#define BNX2_MCP_MCPQ_FTQ_CMD_WR_TOP (1L<<10)
+#define BNX2_MCP_MCPQ_FTQ_CMD_WR_TOP_0 (0L<<10)
+#define BNX2_MCP_MCPQ_FTQ_CMD_WR_TOP_1 (1L<<10)
+#define BNX2_MCP_MCPQ_FTQ_CMD_SFT_RESET (1L<<25)
+#define BNX2_MCP_MCPQ_FTQ_CMD_RD_DATA (1L<<26)
+#define BNX2_MCP_MCPQ_FTQ_CMD_ADD_INTERVEN (1L<<27)
+#define BNX2_MCP_MCPQ_FTQ_CMD_ADD_DATA (1L<<28)
+#define BNX2_MCP_MCPQ_FTQ_CMD_INTERVENE_CLR (1L<<29)
+#define BNX2_MCP_MCPQ_FTQ_CMD_POP (1L<<30)
+#define BNX2_MCP_MCPQ_FTQ_CMD_BUSY (1L<<31)
+
+#define BNX2_MCP_MCPQ_FTQ_CTL 0x001453fc
+#define BNX2_MCP_MCPQ_FTQ_CTL_INTERVENE (1L<<0)
+#define BNX2_MCP_MCPQ_FTQ_CTL_OVERFLOW (1L<<1)
+#define BNX2_MCP_MCPQ_FTQ_CTL_FORCE_INTERVENE (1L<<2)
+#define BNX2_MCP_MCPQ_FTQ_CTL_MAX_DEPTH (0x3ffL<<12)
+#define BNX2_MCP_MCPQ_FTQ_CTL_CUR_DEPTH (0x3ffL<<22)
+
+#define BNX2_MCP_ROM 0x00150000
+#define BNX2_MCP_SCRATCH 0x00160000
+
+#define BNX2_SHM_HDR_SIGNATURE BNX2_MCP_SCRATCH
+#define BNX2_SHM_HDR_SIGNATURE_SIG_MASK 0xffff0000
+#define BNX2_SHM_HDR_SIGNATURE_SIG 0x53530000
+#define BNX2_SHM_HDR_SIGNATURE_VER_MASK 0x000000ff
+#define BNX2_SHM_HDR_SIGNATURE_VER_ONE 0x00000001
+
+#define BNX2_SHM_HDR_ADDR_0 BNX2_MCP_SCRATCH + 4
+#define BNX2_SHM_HDR_ADDR_1 BNX2_MCP_SCRATCH + 8
+
+
+#define NUM_MC_HASH_REGISTERS 8
+
+
+/* PHY_ID1: bits 31-16; PHY_ID2: bits 15-0. */
+#define PHY_BCM5706_PHY_ID 0x00206160
+
+#define PHY_ID(id) ((id) & 0xfffffff0)
+#define PHY_REV_ID(id) ((id) & 0xf)
+
+/* 5708 Serdes PHY registers */
+
+#define BCM5708S_UP1 0xb
+
+#define BCM5708S_UP1_2G5 0x1
+
+#define BCM5708S_BLK_ADDR 0x1f
+
+#define BCM5708S_BLK_ADDR_DIG 0x0000
+#define BCM5708S_BLK_ADDR_DIG3 0x0002
+#define BCM5708S_BLK_ADDR_TX_MISC 0x0005
+
+/* Digital Block */
+#define BCM5708S_1000X_CTL1 0x10
+
+#define BCM5708S_1000X_CTL1_FIBER_MODE 0x0001
+#define BCM5708S_1000X_CTL1_AUTODET_EN 0x0010
+
+#define BCM5708S_1000X_CTL2 0x11
+
+#define BCM5708S_1000X_CTL2_PLLEL_DET_EN 0x0001
+
+#define BCM5708S_1000X_STAT1 0x14
+
+#define BCM5708S_1000X_STAT1_SGMII 0x0001
+#define BCM5708S_1000X_STAT1_LINK 0x0002
+#define BCM5708S_1000X_STAT1_FD 0x0004
+#define BCM5708S_1000X_STAT1_SPEED_MASK 0x0018
+#define BCM5708S_1000X_STAT1_SPEED_10 0x0000
+#define BCM5708S_1000X_STAT1_SPEED_100 0x0008
+#define BCM5708S_1000X_STAT1_SPEED_1G 0x0010
+#define BCM5708S_1000X_STAT1_SPEED_2G5 0x0018
+#define BCM5708S_1000X_STAT1_TX_PAUSE 0x0020
+#define BCM5708S_1000X_STAT1_RX_PAUSE 0x0040
+
+/* Digital3 Block */
+#define BCM5708S_DIG_3_0 0x10
+
+#define BCM5708S_DIG_3_0_USE_IEEE 0x0001
+
+/* Tx/Misc Block */
+#define BCM5708S_TX_ACTL1 0x15
+
+#define BCM5708S_TX_ACTL1_DRIVER_VCM 0x30
+
+#define BCM5708S_TX_ACTL3 0x17
+
+#define MIN_ETHERNET_PACKET_SIZE 60
+#define MAX_ETHERNET_PACKET_SIZE 1514
+#define MAX_ETHERNET_JUMBO_PACKET_SIZE 9014
+
+#define RX_COPY_THRESH 92
+
+#define DMA_READ_CHANS 5
+#define DMA_WRITE_CHANS 3
+
+#define BCM_PAGE_BITS 12
+#define BCM_PAGE_SIZE (1 << BCM_PAGE_BITS)
+
+#define TX_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct tx_bd))
+#define MAX_TX_DESC_CNT (TX_DESC_CNT - 1)
+
+#define MAX_RX_RINGS 4
+#define RX_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct rx_bd))
+#define MAX_RX_DESC_CNT (RX_DESC_CNT - 1)
+#define MAX_TOTAL_RX_DESC_CNT (MAX_RX_DESC_CNT * MAX_RX_RINGS)
+
+#define NEXT_TX_BD(x) (((x) & (MAX_TX_DESC_CNT - 1)) == \
+ (MAX_TX_DESC_CNT - 1)) ? \
+ (x) + 2 : (x) + 1
+
+#define PREV_TX_BD(x) ((((x)-1) & (MAX_TX_DESC_CNT)) == \
+ (MAX_TX_DESC_CNT)) ? \
+ (x) - 2 : (x) - 1
+
+#define TX_RING_IDX(x) ((x) & MAX_TX_DESC_CNT)
+
+#define NEXT_RX_BD(x) (((x) & (MAX_RX_DESC_CNT - 1)) == \
+ (MAX_RX_DESC_CNT - 1)) ? \
+ (x) + 2 : (x) + 1
+
+#define RX_RING_IDX(x) ((x) & bp->rx_max_ring_idx)
+
+//#define RX_RING(x) (((x) & ~MAX_RX_DESC_CNT) >> 8)
+#define RX_IDX(x) ((x) & MAX_RX_DESC_CNT)
+
+/* Context size. */
+#define CTX_SHIFT 7
+#define CTX_SIZE (1 << CTX_SHIFT)
+#define CTX_MASK (CTX_SIZE - 1)
+#define GET_CID_ADDR(_cid) ((_cid) << CTX_SHIFT)
+#define GET_CID(_cid_addr) ((_cid_addr) >> CTX_SHIFT)
+
+#define PHY_CTX_SHIFT 6
+#define PHY_CTX_SIZE (1 << PHY_CTX_SHIFT)
+#define PHY_CTX_MASK (PHY_CTX_SIZE - 1)
+#define GET_PCID_ADDR(_pcid) ((_pcid) << PHY_CTX_SHIFT)
+#define GET_PCID(_pcid_addr) ((_pcid_addr) >> PHY_CTX_SHIFT)
+
+#define MB_KERNEL_CTX_SHIFT 8
+#define MB_KERNEL_CTX_SIZE (1 << MB_KERNEL_CTX_SHIFT)
+#define MB_KERNEL_CTX_MASK (MB_KERNEL_CTX_SIZE - 1)
+#define MB_GET_CID_ADDR(_cid) (0x10000 + ((_cid) << MB_KERNEL_CTX_SHIFT))
+
+#define MAX_CID_CNT 0x4000
+#define MAX_CID_ADDR (GET_CID_ADDR(MAX_CID_CNT))
+#define INVALID_CID_ADDR 0xffffffff
+
+#define TX_CID 16
+#define RX_CID 0
+
+#define MB_TX_CID_ADDR MB_GET_CID_ADDR(TX_CID)
+#define MB_RX_CID_ADDR MB_GET_CID_ADDR(RX_CID)
+
+#if 0
+struct sw_bd {
+ struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(mapping)
+};
+#endif
+
+/* Buffered flash (Atmel: AT45DB011B) specific information */
+#define SEEPROM_PAGE_BITS 2
+#define SEEPROM_PHY_PAGE_SIZE (1 << SEEPROM_PAGE_BITS)
+#define SEEPROM_BYTE_ADDR_MASK (SEEPROM_PHY_PAGE_SIZE-1)
+#define SEEPROM_PAGE_SIZE 4
+#define SEEPROM_TOTAL_SIZE 65536
+
+#define BUFFERED_FLASH_PAGE_BITS 9
+#define BUFFERED_FLASH_PHY_PAGE_SIZE (1 << BUFFERED_FLASH_PAGE_BITS)
+#define BUFFERED_FLASH_BYTE_ADDR_MASK (BUFFERED_FLASH_PHY_PAGE_SIZE-1)
+#define BUFFERED_FLASH_PAGE_SIZE 264
+#define BUFFERED_FLASH_TOTAL_SIZE 0x21000
+
+#define SAIFUN_FLASH_PAGE_BITS 8
+#define SAIFUN_FLASH_PHY_PAGE_SIZE (1 << SAIFUN_FLASH_PAGE_BITS)
+#define SAIFUN_FLASH_BYTE_ADDR_MASK (SAIFUN_FLASH_PHY_PAGE_SIZE-1)
+#define SAIFUN_FLASH_PAGE_SIZE 256
+#define SAIFUN_FLASH_BASE_TOTAL_SIZE 65536
+
+#define ST_MICRO_FLASH_PAGE_BITS 8
+#define ST_MICRO_FLASH_PHY_PAGE_SIZE (1 << ST_MICRO_FLASH_PAGE_BITS)
+#define ST_MICRO_FLASH_BYTE_ADDR_MASK (ST_MICRO_FLASH_PHY_PAGE_SIZE-1)
+#define ST_MICRO_FLASH_PAGE_SIZE 256
+#define ST_MICRO_FLASH_BASE_TOTAL_SIZE 65536
+
+#define NVRAM_TIMEOUT_COUNT 30000
+
+
+#define FLASH_STRAP_MASK (BNX2_NVM_CFG1_FLASH_MODE | \
+ BNX2_NVM_CFG1_BUFFER_MODE | \
+ BNX2_NVM_CFG1_PROTECT_MODE | \
+ BNX2_NVM_CFG1_FLASH_SIZE)
+
+#define FLASH_BACKUP_STRAP_MASK (0xf << 26)
+
+struct flash_spec {
+ u32 strapping;
+ u32 config1;
+ u32 config2;
+ u32 config3;
+ u32 write1;
+ u32 buffered;
+ u32 page_bits;
+ u32 page_size;
+ u32 addr_mask;
+ u32 total_size;
+ char *name;
+};
+
+struct bnx2 {
+ /* Fields used in the tx and intr/napi performance paths are grouped */
+ /* together in the beginning of the structure. */
+ void /*__iomem*/ *regview;
+
+ struct nic *nic;
+ struct pci_device *pdev;
+
+ /* atomic_t intr_sem; */
+
+ struct status_block *status_blk;
+ u32 last_status_idx;
+
+ u32 flags;
+#define PCIX_FLAG 1
+#define PCI_32BIT_FLAG 2
+#define ONE_TDMA_FLAG 4 /* no longer used */
+#define NO_WOL_FLAG 8
+#define USING_DAC_FLAG 0x10
+#define USING_MSI_FLAG 0x20
+#define ASF_ENABLE_FLAG 0x40
+
+ /* Put tx producer and consumer fields in separate cache lines. */
+ u32 tx_prod_bseq __attribute__((aligned(L1_CACHE_BYTES)));
+ u16 tx_prod;
+
+ struct tx_bd *tx_desc_ring;
+ struct sw_bd *tx_buf_ring;
+ int tx_ring_size;
+
+ u16 tx_cons __attribute__((aligned(L1_CACHE_BYTES)));
+ u16 hw_tx_cons;
+
+#ifdef BCM_VLAN
+ struct vlan_group *vlgrp;
+#endif
+
+ u32 rx_offset;
+ u32 rx_buf_use_size; /* useable size */
+ u32 rx_buf_size; /* with alignment */
+ u32 rx_max_ring_idx;
+
+ u32 rx_prod_bseq;
+ u16 rx_prod;
+ u16 rx_cons;
+ u16 hw_rx_cons;
+
+ u32 rx_csum;
+
+#if 0
+ struct rx_bd *rx_desc_ring[MAX_RX_RINGS];
+#endif
+ struct rx_bd *rx_desc_ring;
+
+ /* End of fields used in the performance code paths. */
+
+ char *name;
+
+#if 0
+ int timer_interval;
+ int current_interval;
+ struct timer_list timer;
+ struct work_struct reset_task;
+ int in_reset_task;
+
+ /* Used to synchronize phy accesses. */
+ spinlock_t phy_lock;
+#endif
+
+ u32 phy_flags;
+#define PHY_SERDES_FLAG 1
+#define PHY_CRC_FIX_FLAG 2
+#define PHY_PARALLEL_DETECT_FLAG 4
+#define PHY_2_5G_CAPABLE_FLAG 8
+#define PHY_INT_MODE_MASK_FLAG 0x300
+#define PHY_INT_MODE_AUTO_POLLING_FLAG 0x100
+#define PHY_INT_MODE_LINK_READY_FLAG 0x200
+
+ u32 chip_id;
+ /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */
+#define CHIP_NUM(bp) (((bp)->chip_id) & 0xffff0000)
+#define CHIP_NUM_5706 0x57060000
+#define CHIP_NUM_5708 0x57080000
+
+#define CHIP_REV(bp) (((bp)->chip_id) & 0x0000f000)
+#define CHIP_REV_Ax 0x00000000
+#define CHIP_REV_Bx 0x00001000
+#define CHIP_REV_Cx 0x00002000
+
+#define CHIP_METAL(bp) (((bp)->chip_id) & 0x00000ff0)
+#define CHIP_BONDING(bp) (((bp)->chip_id) & 0x0000000f)
+
+#define CHIP_ID(bp) (((bp)->chip_id) & 0xfffffff0)
+#define CHIP_ID_5706_A0 0x57060000
+#define CHIP_ID_5706_A1 0x57060010
+#define CHIP_ID_5706_A2 0x57060020
+#define CHIP_ID_5708_A0 0x57080000
+#define CHIP_ID_5708_B0 0x57081000
+#define CHIP_ID_5708_B1 0x57081010
+
+#define CHIP_BOND_ID(bp) (((bp)->chip_id) & 0xf)
+
+/* A serdes chip will have the first bit of the bond id set. */
+#define CHIP_BOND_ID_SERDES_BIT 0x01
+
+ u32 phy_addr;
+ u32 phy_id;
+
+ u16 bus_speed_mhz;
+ u8 wol;
+
+ u8 pad;
+
+ u16 fw_wr_seq;
+ u16 fw_drv_pulse_wr_seq;
+
+ dma_addr_t tx_desc_mapping;
+
+
+ int rx_max_ring;
+ int rx_ring_size;
+#if 0
+ dma_addr_t rx_desc_mapping[MAX_RX_RINGS];
+#endif
+ dma_addr_t rx_desc_mapping;
+
+ u16 tx_quick_cons_trip;
+ u16 tx_quick_cons_trip_int;
+ u16 rx_quick_cons_trip;
+ u16 rx_quick_cons_trip_int;
+ u16 comp_prod_trip;
+ u16 comp_prod_trip_int;
+ u16 tx_ticks;
+ u16 tx_ticks_int;
+ u16 com_ticks;
+ u16 com_ticks_int;
+ u16 cmd_ticks;
+ u16 cmd_ticks_int;
+ u16 rx_ticks;
+ u16 rx_ticks_int;
+
+ u32 stats_ticks;
+
+ dma_addr_t status_blk_mapping;
+
+ struct statistics_block *stats_blk;
+ dma_addr_t stats_blk_mapping;
+
+ u32 hc_cmd;
+ u32 rx_mode;
+
+ u16 req_line_speed;
+ u8 req_duplex;
+
+ u8 link_up;
+
+ u16 line_speed;
+ u8 duplex;
+ u8 flow_ctrl; /* actual flow ctrl settings */
+ /* may be different from */
+ /* req_flow_ctrl if autoneg */
+#define FLOW_CTRL_TX 1
+#define FLOW_CTRL_RX 2
+
+ u32 advertising;
+
+ u8 req_flow_ctrl; /* flow ctrl advertisement */
+ /* settings or forced */
+ /* settings */
+ u8 autoneg;
+#define AUTONEG_SPEED 1
+#define AUTONEG_FLOW_CTRL 2
+
+ u8 loopback;
+#define MAC_LOOPBACK 1
+#define PHY_LOOPBACK 2
+
+ u8 serdes_an_pending;
+#define SERDES_AN_TIMEOUT (HZ / 3)
+
+ u8 mac_addr[8];
+
+ u32 shmem_base;
+
+ u32 fw_ver;
+
+ int pm_cap;
+ int pcix_cap;
+
+ /* struct net_device_stats net_stats; */
+
+ struct flash_spec *flash_info;
+ u32 flash_size;
+
+ int status_stats_size;
+};
+
+static u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset);
+static void bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val);
+
+#define REG_RD(bp, offset) \
+ readl(bp->regview + offset)
+
+#define REG_WR(bp, offset, val) \
+ writel(val, bp->regview + offset)
+
+#define REG_WR16(bp, offset, val) \
+ writew(val, bp->regview + offset)
+
+#define REG_RD_IND(bp, offset) \
+ bnx2_reg_rd_ind(bp, offset)
+
+#define REG_WR_IND(bp, offset, val) \
+ bnx2_reg_wr_ind(bp, offset, val)
+
+/* Indirect context access. Unlike the MBQ_WR, these macros will not
+ * trigger a chip event. */
+static void bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val);
+
+#define CTX_WR(bp, cid_addr, offset, val) \
+ bnx2_ctx_wr(bp, cid_addr, offset, val)
+
+struct cpu_reg {
+ u32 mode;
+ u32 mode_value_halt;
+ u32 mode_value_sstep;
+
+ u32 state;
+ u32 state_value_clear;
+
+ u32 gpr0;
+ u32 evmask;
+ u32 pc;
+ u32 inst;
+ u32 bp;
+
+ u32 spad_base;
+
+ u32 mips_view_base;
+};
+
+struct fw_info {
+ u32 ver_major;
+ u32 ver_minor;
+ u32 ver_fix;
+
+ u32 start_addr;
+
+ /* Text section. */
+ u32 text_addr;
+ u32 text_len;
+ u32 text_index;
+ u32 *text;
+
+ /* Data section. */
+ u32 data_addr;
+ u32 data_len;
+ u32 data_index;
+ u32 *data;
+
+ /* SBSS section. */
+ u32 sbss_addr;
+ u32 sbss_len;
+ u32 sbss_index;
+ u32 *sbss;
+
+ /* BSS section. */
+ u32 bss_addr;
+ u32 bss_len;
+ u32 bss_index;
+ u32 *bss;
+
+ /* Read-only section. */
+ u32 rodata_addr;
+ u32 rodata_len;
+ u32 rodata_index;
+ u32 *rodata;
+};
+
+#define RV2P_PROC1 0
+#define RV2P_PROC2 1
+
+
+/* This value (in milliseconds) determines the frequency of the driver
+ * issuing the PULSE message code. The firmware monitors this periodic
+ * pulse to determine when to switch to an OS-absent mode. */
+#define DRV_PULSE_PERIOD_MS 250
+
+/* This value (in milliseconds) determines how long the driver should
+ * wait for an acknowledgement from the firmware before timing out. Once
+ * the firmware has timed out, the driver will assume there is no firmware
+ * running and there won't be any firmware-driver synchronization during a
+ * driver reset. */
+#define FW_ACK_TIME_OUT_MS 100
+
+
+#define BNX2_DRV_RESET_SIGNATURE 0x00000000
+#define BNX2_DRV_RESET_SIGNATURE_MAGIC 0x4841564b /* HAVK */
+//#define DRV_RESET_SIGNATURE_MAGIC 0x47495352 /* RSIG */
+
+#define BNX2_DRV_MB 0x00000004
+#define BNX2_DRV_MSG_CODE 0xff000000
+#define BNX2_DRV_MSG_CODE_RESET 0x01000000
+#define BNX2_DRV_MSG_CODE_UNLOAD 0x02000000
+#define BNX2_DRV_MSG_CODE_SHUTDOWN 0x03000000
+#define BNX2_DRV_MSG_CODE_SUSPEND_WOL 0x04000000
+#define BNX2_DRV_MSG_CODE_FW_TIMEOUT 0x05000000
+#define BNX2_DRV_MSG_CODE_PULSE 0x06000000
+#define BNX2_DRV_MSG_CODE_DIAG 0x07000000
+#define BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL 0x09000000
+
+#define BNX2_DRV_MSG_DATA 0x00ff0000
+#define BNX2_DRV_MSG_DATA_WAIT0 0x00010000
+#define BNX2_DRV_MSG_DATA_WAIT1 0x00020000
+#define BNX2_DRV_MSG_DATA_WAIT2 0x00030000
+#define BNX2_DRV_MSG_DATA_WAIT3 0x00040000
+
+#define BNX2_DRV_MSG_SEQ 0x0000ffff
+
+#define BNX2_FW_MB 0x00000008
+#define BNX2_FW_MSG_ACK 0x0000ffff
+#define BNX2_FW_MSG_STATUS_MASK 0x00ff0000
+#define BNX2_FW_MSG_STATUS_OK 0x00000000
+#define BNX2_FW_MSG_STATUS_FAILURE 0x00ff0000
+
+#define BNX2_LINK_STATUS 0x0000000c
+#define BNX2_LINK_STATUS_INIT_VALUE 0xffffffff
+#define BNX2_LINK_STATUS_LINK_UP 0x1
+#define BNX2_LINK_STATUS_LINK_DOWN 0x0
+#define BNX2_LINK_STATUS_SPEED_MASK 0x1e
+#define BNX2_LINK_STATUS_AN_INCOMPLETE (0<<1)
+#define BNX2_LINK_STATUS_10HALF (1<<1)
+#define BNX2_LINK_STATUS_10FULL (2<<1)
+#define BNX2_LINK_STATUS_100HALF (3<<1)
+#define BNX2_LINK_STATUS_100BASE_T4 (4<<1)
+#define BNX2_LINK_STATUS_100FULL (5<<1)
+#define BNX2_LINK_STATUS_1000HALF (6<<1)
+#define BNX2_LINK_STATUS_1000FULL (7<<1)
+#define BNX2_LINK_STATUS_2500HALF (8<<1)
+#define BNX2_LINK_STATUS_2500FULL (9<<1)
+#define BNX2_LINK_STATUS_AN_ENABLED (1<<5)
+#define BNX2_LINK_STATUS_AN_COMPLETE (1<<6)
+#define BNX2_LINK_STATUS_PARALLEL_DET (1<<7)
+#define BNX2_LINK_STATUS_RESERVED (1<<8)
+#define BNX2_LINK_STATUS_PARTNER_AD_1000FULL (1<<9)
+#define BNX2_LINK_STATUS_PARTNER_AD_1000HALF (1<<10)
+#define BNX2_LINK_STATUS_PARTNER_AD_100BT4 (1<<11)
+#define BNX2_LINK_STATUS_PARTNER_AD_100FULL (1<<12)
+#define BNX2_LINK_STATUS_PARTNER_AD_100HALF (1<<13)
+#define BNX2_LINK_STATUS_PARTNER_AD_10FULL (1<<14)
+#define BNX2_LINK_STATUS_PARTNER_AD_10HALF (1<<15)
+#define BNX2_LINK_STATUS_TX_FC_ENABLED (1<<16)
+#define BNX2_LINK_STATUS_RX_FC_ENABLED (1<<17)
+#define BNX2_LINK_STATUS_PARTNER_SYM_PAUSE_CAP (1<<18)
+#define BNX2_LINK_STATUS_PARTNER_ASYM_PAUSE_CAP (1<<19)
+#define BNX2_LINK_STATUS_SERDES_LINK (1<<20)
+#define BNX2_LINK_STATUS_PARTNER_AD_2500FULL (1<<21)
+#define BNX2_LINK_STATUS_PARTNER_AD_2500HALF (1<<22)
+
+#define BNX2_DRV_PULSE_MB 0x00000010
+#define BNX2_DRV_PULSE_SEQ_MASK 0x00007fff
+
+/* Indicate to the firmware not to go into the
+ * OS absent when it is not getting driver pulse.
+ * This is used for debugging. */
+#define BNX2_DRV_MSG_DATA_PULSE_CODE_ALWAYS_ALIVE 0x00080000
+
+#define BNX2_DEV_INFO_SIGNATURE 0x00000020
+#define BNX2_DEV_INFO_SIGNATURE_MAGIC 0x44564900
+#define BNX2_DEV_INFO_SIGNATURE_MAGIC_MASK 0xffffff00
+#define BNX2_DEV_INFO_FEATURE_CFG_VALID 0x01
+#define BNX2_DEV_INFO_SECONDARY_PORT 0x80
+#define BNX2_DEV_INFO_DRV_ALWAYS_ALIVE 0x40
+
+#define BNX2_SHARED_HW_CFG_PART_NUM 0x00000024
+
+#define BNX2_SHARED_HW_CFG_POWER_DISSIPATED 0x00000034
+#define BNX2_SHARED_HW_CFG_POWER_STATE_D3_MASK 0xff000000
+#define BNX2_SHARED_HW_CFG_POWER_STATE_D2_MASK 0xff0000
+#define BNX2_SHARED_HW_CFG_POWER_STATE_D1_MASK 0xff00
+#define BNX2_SHARED_HW_CFG_POWER_STATE_D0_MASK 0xff
+
+#define BNX2_SHARED_HW_CFG POWER_CONSUMED 0x00000038
+#define BNX2_SHARED_HW_CFG_CONFIG 0x0000003c
+#define BNX2_SHARED_HW_CFG_DESIGN_NIC 0
+#define BNX2_SHARED_HW_CFG_DESIGN_LOM 0x1
+#define BNX2_SHARED_HW_CFG_PHY_COPPER 0
+#define BNX2_SHARED_HW_CFG_PHY_FIBER 0x2
+#define BNX2_SHARED_HW_CFG_PHY_2_5G 0x20
+#define BNX2_SHARED_HW_CFG_PHY_BACKPLANE 0x40
+#define BNX2_SHARED_HW_CFG_LED_MODE_SHIFT_BITS 8
+#define BNX2_SHARED_HW_CFG_LED_MODE_MASK 0x300
+#define BNX2_SHARED_HW_CFG_LED_MODE_MAC 0
+#define BNX2_SHARED_HW_CFG_LED_MODE_GPHY1 0x100
+#define BNX2_SHARED_HW_CFG_LED_MODE_GPHY2 0x200
+
+#define BNX2_SHARED_HW_CFG_CONFIG2 0x00000040
+#define BNX2_SHARED_HW_CFG2_NVM_SIZE_MASK 0x00fff000
+
+#define BNX2_DEV_INFO_BC_REV 0x0000004c
+
+#define BNX2_PORT_HW_CFG_MAC_UPPER 0x00000050
+#define BNX2_PORT_HW_CFG_UPPERMAC_MASK 0xffff
+
+#define BNX2_PORT_HW_CFG_MAC_LOWER 0x00000054
+#define BNX2_PORT_HW_CFG_CONFIG 0x00000058
+#define BNX2_PORT_HW_CFG_CFG_TXCTL3_MASK 0x0000ffff
+#define BNX2_PORT_HW_CFG_CFG_DFLT_LINK_MASK 0x001f0000
+#define BNX2_PORT_HW_CFG_CFG_DFLT_LINK_AN 0x00000000
+#define BNX2_PORT_HW_CFG_CFG_DFLT_LINK_1G 0x00030000
+#define BNX2_PORT_HW_CFG_CFG_DFLT_LINK_2_5G 0x00040000
+
+#define BNX2_PORT_HW_CFG_IMD_MAC_A_UPPER 0x00000068
+#define BNX2_PORT_HW_CFG_IMD_MAC_A_LOWER 0x0000006c
+#define BNX2_PORT_HW_CFG_IMD_MAC_B_UPPER 0x00000070
+#define BNX2_PORT_HW_CFG_IMD_MAC_B_LOWER 0x00000074
+#define BNX2_PORT_HW_CFG_ISCSI_MAC_UPPER 0x00000078
+#define BNX2_PORT_HW_CFG_ISCSI_MAC_LOWER 0x0000007c
+
+#define BNX2_DEV_INFO_PER_PORT_HW_CONFIG2 0x000000b4
+
+#define BNX2_DEV_INFO_FORMAT_REV 0x000000c4
+#define BNX2_DEV_INFO_FORMAT_REV_MASK 0xff000000
+#define BNX2_DEV_INFO_FORMAT_REV_ID ('A' << 24)
+
+#define BNX2_SHARED_FEATURE 0x000000c8
+#define BNX2_SHARED_FEATURE_MASK 0xffffffff
+
+#define BNX2_PORT_FEATURE 0x000000d8
+#define BNX2_PORT2_FEATURE 0x00000014c
+#define BNX2_PORT_FEATURE_WOL_ENABLED 0x01000000
+#define BNX2_PORT_FEATURE_MBA_ENABLED 0x02000000
+#define BNX2_PORT_FEATURE_ASF_ENABLED 0x04000000
+#define BNX2_PORT_FEATURE_IMD_ENABLED 0x08000000
+#define BNX2_PORT_FEATURE_BAR1_SIZE_MASK 0xf
+#define BNX2_PORT_FEATURE_BAR1_SIZE_DISABLED 0x0
+#define BNX2_PORT_FEATURE_BAR1_SIZE_64K 0x1
+#define BNX2_PORT_FEATURE_BAR1_SIZE_128K 0x2
+#define BNX2_PORT_FEATURE_BAR1_SIZE_256K 0x3
+#define BNX2_PORT_FEATURE_BAR1_SIZE_512K 0x4
+#define BNX2_PORT_FEATURE_BAR1_SIZE_1M 0x5
+#define BNX2_PORT_FEATURE_BAR1_SIZE_2M 0x6
+#define BNX2_PORT_FEATURE_BAR1_SIZE_4M 0x7
+#define BNX2_PORT_FEATURE_BAR1_SIZE_8M 0x8
+#define BNX2_PORT_FEATURE_BAR1_SIZE_16M 0x9
+#define BNX2_PORT_FEATURE_BAR1_SIZE_32M 0xa
+#define BNX2_PORT_FEATURE_BAR1_SIZE_64M 0xb
+#define BNX2_PORT_FEATURE_BAR1_SIZE_128M 0xc
+#define BNX2_PORT_FEATURE_BAR1_SIZE_256M 0xd
+#define BNX2_PORT_FEATURE_BAR1_SIZE_512M 0xe
+#define BNX2_PORT_FEATURE_BAR1_SIZE_1G 0xf
+
+#define BNX2_PORT_FEATURE_WOL 0xdc
+#define BNX2_PORT2_FEATURE_WOL 0x150
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_SHIFT_BITS 4
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_MASK 0x30
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_DISABLE 0
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_MAGIC 0x10
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_ACPI 0x20
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_MAGIC_AND_ACPI 0x30
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_MASK 0xf
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_AUTONEG 0
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_10HALF 1
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_10FULL 2
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_100HALF 3
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_100FULL 4
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_1000HALF 5
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_1000FULL 6
+#define BNX2_PORT_FEATURE_WOL_AUTONEG_ADVERTISE_1000 0x40
+#define BNX2_PORT_FEATURE_WOL_RESERVED_PAUSE_CAP 0x400
+#define BNX2_PORT_FEATURE_WOL_RESERVED_ASYM_PAUSE_CAP 0x800
+
+#define BNX2_PORT_FEATURE_MBA 0xe0
+#define BNX2_PORT2_FEATURE_MBA 0x154
+#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_SHIFT_BITS 0
+#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_MASK 0x3
+#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_PXE 0
+#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_RPL 1
+#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_BOOTP 2
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_SHIFT_BITS 2
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_MASK 0x3c
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_AUTONEG 0
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_10HALF 0x4
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_10FULL 0x8
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_100HALF 0xc
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_100FULL 0x10
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_1000HALF 0x14
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_1000FULL 0x18
+#define BNX2_PORT_FEATURE_MBA_SETUP_PROMPT_ENABLE 0x40
+#define BNX2_PORT_FEATURE_MBA_HOTKEY_CTRL_S 0
+#define BNX2_PORT_FEATURE_MBA_HOTKEY_CTRL_B 0x80
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_SHIFT_BITS 8
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_MASK 0xff00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_DISABLED 0
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_1K 0x100
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_2K 0x200
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_4K 0x300
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_8K 0x400
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_16K 0x500
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_32K 0x600
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_64K 0x700
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_128K 0x800
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_256K 0x900
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_512K 0xa00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_1M 0xb00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_2M 0xc00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_4M 0xd00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_8M 0xe00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_16M 0xf00
+#define BNX2_PORT_FEATURE_MBA_MSG_TIMEOUT_SHIFT_BITS 16
+#define BNX2_PORT_FEATURE_MBA_MSG_TIMEOUT_MASK 0xf0000
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_SHIFT_BITS 20
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_MASK 0x300000
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_AUTO 0
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_BBS 0x100000
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_INT18H 0x200000
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_INT19H 0x300000
+
+#define BNX2_PORT_FEATURE_IMD 0xe4
+#define BNX2_PORT2_FEATURE_IMD 0x158
+#define BNX2_PORT_FEATURE_IMD_LINK_OVERRIDE_DEFAULT 0
+#define BNX2_PORT_FEATURE_IMD_LINK_OVERRIDE_ENABLE 1
+
+#define BNX2_PORT_FEATURE_VLAN 0xe8
+#define BNX2_PORT2_FEATURE_VLAN 0x15c
+#define BNX2_PORT_FEATURE_MBA_VLAN_TAG_MASK 0xffff
+#define BNX2_PORT_FEATURE_MBA_VLAN_ENABLE 0x10000
+
+#define BNX2_BC_STATE_RESET_TYPE 0x000001c0
+#define BNX2_BC_STATE_RESET_TYPE_SIG 0x00005254
+#define BNX2_BC_STATE_RESET_TYPE_SIG_MASK 0x0000ffff
+#define BNX2_BC_STATE_RESET_TYPE_NONE (BNX2_BC_STATE_RESET_TYPE_SIG | \
+ 0x00010000)
+#define BNX2_BC_STATE_RESET_TYPE_PCI (BNX2_BC_STATE_RESET_TYPE_SIG | \
+ 0x00020000)
+#define BNX2_BC_STATE_RESET_TYPE_VAUX (BNX2_BC_STATE_RESET_TYPE_SIG | \
+ 0x00030000)
+#define BNX2_BC_STATE_RESET_TYPE_DRV_MASK DRV_MSG_CODE
+#define BNX2_BC_STATE_RESET_TYPE_DRV_RESET (BNX2_BC_STATE_RESET_TYPE_SIG | \
+ DRV_MSG_CODE_RESET)
+#define BNX2_BC_STATE_RESET_TYPE_DRV_UNLOAD (BNX2_BC_STATE_RESET_TYPE_SIG | \
+ DRV_MSG_CODE_UNLOAD)
+#define BNX2_BC_STATE_RESET_TYPE_DRV_SHUTDOWN (BNX2_BC_STATE_RESET_TYPE_SIG | \
+ DRV_MSG_CODE_SHUTDOWN)
+#define BNX2_BC_STATE_RESET_TYPE_DRV_WOL (BNX2_BC_STATE_RESET_TYPE_SIG | \
+ DRV_MSG_CODE_WOL)
+#define BNX2_BC_STATE_RESET_TYPE_DRV_DIAG (BNX2_BC_STATE_RESET_TYPE_SIG | \
+ DRV_MSG_CODE_DIAG)
+#define BNX2_BC_STATE_RESET_TYPE_VALUE(msg) (BNX2_BC_STATE_RESET_TYPE_SIG | \
+ (msg))
+
+#define BNX2_BC_STATE 0x000001c4
+#define BNX2_BC_STATE_ERR_MASK 0x0000ff00
+#define BNX2_BC_STATE_SIGN 0x42530000
+#define BNX2_BC_STATE_SIGN_MASK 0xffff0000
+#define BNX2_BC_STATE_BC1_START (BNX2_BC_STATE_SIGN | 0x1)
+#define BNX2_BC_STATE_GET_NVM_CFG1 (BNX2_BC_STATE_SIGN | 0x2)
+#define BNX2_BC_STATE_PROG_BAR (BNX2_BC_STATE_SIGN | 0x3)
+#define BNX2_BC_STATE_INIT_VID (BNX2_BC_STATE_SIGN | 0x4)
+#define BNX2_BC_STATE_GET_NVM_CFG2 (BNX2_BC_STATE_SIGN | 0x5)
+#define BNX2_BC_STATE_APPLY_WKARND (BNX2_BC_STATE_SIGN | 0x6)
+#define BNX2_BC_STATE_LOAD_BC2 (BNX2_BC_STATE_SIGN | 0x7)
+#define BNX2_BC_STATE_GOING_BC2 (BNX2_BC_STATE_SIGN | 0x8)
+#define BNX2_BC_STATE_GOING_DIAG (BNX2_BC_STATE_SIGN | 0x9)
+#define BNX2_BC_STATE_RT_FINAL_INIT (BNX2_BC_STATE_SIGN | 0x81)
+#define BNX2_BC_STATE_RT_WKARND (BNX2_BC_STATE_SIGN | 0x82)
+#define BNX2_BC_STATE_RT_DRV_PULSE (BNX2_BC_STATE_SIGN | 0x83)
+#define BNX2_BC_STATE_RT_FIOEVTS (BNX2_BC_STATE_SIGN | 0x84)
+#define BNX2_BC_STATE_RT_DRV_CMD (BNX2_BC_STATE_SIGN | 0x85)
+#define BNX2_BC_STATE_RT_LOW_POWER (BNX2_BC_STATE_SIGN | 0x86)
+#define BNX2_BC_STATE_RT_SET_WOL (BNX2_BC_STATE_SIGN | 0x87)
+#define BNX2_BC_STATE_RT_OTHER_FW (BNX2_BC_STATE_SIGN | 0x88)
+#define BNX2_BC_STATE_RT_GOING_D3 (BNX2_BC_STATE_SIGN | 0x89)
+#define BNX2_BC_STATE_ERR_BAD_VERSION (BNX2_BC_STATE_SIGN | 0x0100)
+#define BNX2_BC_STATE_ERR_BAD_BC2_CRC (BNX2_BC_STATE_SIGN | 0x0200)
+#define BNX2_BC_STATE_ERR_BC1_LOOP (BNX2_BC_STATE_SIGN | 0x0300)
+#define BNX2_BC_STATE_ERR_UNKNOWN_CMD (BNX2_BC_STATE_SIGN | 0x0400)
+#define BNX2_BC_STATE_ERR_DRV_DEAD (BNX2_BC_STATE_SIGN | 0x0500)
+#define BNX2_BC_STATE_ERR_NO_RXP (BNX2_BC_STATE_SIGN | 0x0600)
+#define BNX2_BC_STATE_ERR_TOO_MANY_RBUF (BNX2_BC_STATE_SIGN | 0x0700)
+
+#define BNX2_BC_STATE_DEBUG_CMD 0x1dc
+#define BNX2_BC_STATE_BC_DBG_CMD_SIGNATURE 0x42440000
+#define BNX2_BC_STATE_BC_DBG_CMD_SIGNATURE_MASK 0xffff0000
+#define BNX2_BC_STATE_BC_DBG_CMD_LOOP_CNT_MASK 0xffff
+#define BNX2_BC_STATE_BC_DBG_CMD_LOOP_INFINITE 0xffff
+
+#define HOST_VIEW_SHMEM_BASE 0x167c00
+
+/* Enable or disable autonegotiation. If this is set to enable,
+ * the forced link modes above are completely ignored.
+ */
+#define AUTONEG_DISABLE 0x00
+#define AUTONEG_ENABLE 0x01
+
+#define RX_OFFSET (sizeof(struct l2_fhdr) + 2)
+
+#define RX_BUF_CNT 20
+
+/* 8 for CRC and VLAN */
+#define RX_BUF_USE_SIZE (ETH_MAX_MTU + ETH_HLEN + RX_OFFSET + 8)
+
+/* 8 for alignment */
+//#define RX_BUF_SIZE (RX_BUF_USE_SIZE + 8)
+#define RX_BUF_SIZE (L1_CACHE_ALIGN(RX_BUF_USE_SIZE + 8))
+
+
+#endif
diff --git a/gpxe/src/drivers/net/bnx2_fw.h b/gpxe/src/drivers/net/bnx2_fw.h
new file mode 100644
index 00000000..8158974c
--- /dev/null
+++ b/gpxe/src/drivers/net/bnx2_fw.h
@@ -0,0 +1,3494 @@
+/* bnx2_fw.h: Broadcom NX2 network driver.
+ *
+ * Copyright (c) 2004, 2005, 2006 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, except as noted below.
+ *
+ * This file contains firmware data derived from proprietary unpublished
+ * source code, Copyright (c) 2004, 2005 Broadcom Corporation.
+ *
+ * Permission is hereby granted for the distribution of this firmware data
+ * in hexadecimal or equivalent format, provided this copyright notice is
+ * accompanying it.
+ */
+
+static const int bnx2_COM_b06FwReleaseMajor = 0x1;
+static const int bnx2_COM_b06FwReleaseMinor = 0x0;
+static const int bnx2_COM_b06FwReleaseFix = 0x0;
+static const u32 bnx2_COM_b06FwStartAddr = 0x080008b4;
+static const u32 bnx2_COM_b06FwTextAddr = 0x08000000;
+static const int bnx2_COM_b06FwTextLen = 0x57bc;
+static const u32 bnx2_COM_b06FwDataAddr = 0x08005840;
+static const int bnx2_COM_b06FwDataLen = 0x0;
+static const u32 bnx2_COM_b06FwRodataAddr = 0x080057c0;
+static const int bnx2_COM_b06FwRodataLen = 0x58;
+static const u32 bnx2_COM_b06FwBssAddr = 0x08005860;
+static const int bnx2_COM_b06FwBssLen = 0x88;
+static const u32 bnx2_COM_b06FwSbssAddr = 0x08005840;
+static const int bnx2_COM_b06FwSbssLen = 0x1c;
+static u32 bnx2_COM_b06FwText[(0x57bc/4) + 1] = {
+ 0x0a00022d, 0x00000000, 0x00000000, 0x0000000d, 0x636f6d20, 0x322e352e,
+ 0x38000000, 0x02050802, 0x00000000, 0x00000003, 0x00000014, 0x00000032,
+ 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000010, 0x000003e8, 0x0000ea60, 0x00000001, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x0000ffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000002, 0x00000020, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c020800, 0x24425840,
+ 0x3c030800, 0x246358e8, 0xac400000, 0x0043202b, 0x1480fffd, 0x24420004,
+ 0x3c1d0800, 0x37bd7ffc, 0x03a0f021, 0x3c100800, 0x261008b4, 0x3c1c0800,
+ 0x279c5840, 0x0e0002f7, 0x00000000, 0x0000000d, 0x27bdffe8, 0x3c1a8000,
+ 0x3c020008, 0x0342d825, 0x3c036010, 0xafbf0010, 0x8c655000, 0x3c020800,
+ 0x24470f30, 0x3c040800, 0x24865860, 0x2402ff7f, 0x00a22824, 0x34a5380c,
+ 0xac655000, 0x00002821, 0x24020037, 0x24030c80, 0xaf420008, 0xaf430024,
+ 0xacc70000, 0x24a50001, 0x2ca20016, 0x1440fffc, 0x24c60004, 0x24845860,
+ 0x3c020800, 0x24420f3c, 0x3c030800, 0x24630e2c, 0xac820004, 0x3c020800,
+ 0x24420a2c, 0x3c050800, 0x24a51268, 0xac82000c, 0x3c020800, 0x244243dc,
+ 0xac830008, 0x3c030800, 0x24633698, 0xac820014, 0x3c020800, 0x24423c24,
+ 0xac830018, 0xac83001c, 0x3c030800, 0x24630f44, 0xac820024, 0x3c020800,
+ 0x244243ac, 0xac83002c, 0x3c030800, 0x246343cc, 0xac820030, 0x3c020800,
+ 0x244242f0, 0xac830034, 0x3c030800, 0x24633d78, 0xac82003c, 0x3c020800,
+ 0x24420fd4, 0xac850010, 0xac850020, 0xac830040, 0x0e0010b7, 0xac820050,
+ 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x27bdffe0, 0xafb00010, 0x27500100,
+ 0xafbf0018, 0xafb10014, 0x9203000b, 0x24020003, 0x1462005b, 0x96110008,
+ 0x32220001, 0x10400009, 0x27430080, 0x8e020000, 0x96040014, 0x000211c2,
+ 0x00021040, 0x00621821, 0xa4640000, 0x0a0002d0, 0x3c020800, 0x3c020800,
+ 0x8c430020, 0x1060002a, 0x3c030800, 0x0e00148e, 0x00000000, 0x97420108,
+ 0x8f850018, 0x9743010c, 0x3042003e, 0x00021400, 0x00621825, 0xaca30000,
+ 0x8f840018, 0x8f420100, 0xac820004, 0x97430116, 0x9742010e, 0x8f840018,
+ 0x00031c00, 0x00431025, 0xac820008, 0x97430110, 0x97440112, 0x8f850018,
+ 0x00031c00, 0x00832025, 0xaca4000c, 0x97420114, 0x8f840018, 0x3042ffff,
+ 0xac820010, 0x8f830018, 0xac600014, 0x8f820018, 0x3c030800, 0xac400018,
+ 0x946258ce, 0x8f840018, 0x3c032000, 0x00431025, 0xac82001c, 0x0e0014cc,
+ 0x24040001, 0x3c030800, 0x8c620040, 0x24420001, 0xac620040, 0x3c020800,
+ 0x8c430044, 0x32240004, 0x24630001, 0x10800017, 0xac430044, 0x8f4202b8,
+ 0x04430007, 0x8e020020, 0x3c040800, 0x8c830060, 0x24020001, 0x24630001,
+ 0x0a0002f2, 0xac830060, 0x3c060800, 0x8cc4005c, 0xaf420280, 0x96030016,
+ 0x00001021, 0xa7430284, 0x8e050004, 0x24840001, 0x3c031000, 0xaf450288,
+ 0xaf4302b8, 0x0a0002f2, 0xacc4005c, 0x32220002, 0x0a0002f2, 0x0002102b,
+ 0x3c026000, 0xac400808, 0x0000000d, 0x00001021, 0x8fbf0018, 0x8fb10014,
+ 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffc8, 0xafbf0034, 0xafbe0030,
+ 0xafb7002c, 0xafb60028, 0xafb50024, 0xafb40020, 0xafb3001c, 0xafb20018,
+ 0xafb10014, 0x0e000244, 0xafb00010, 0x3c170800, 0x3c160800, 0x24110020,
+ 0x24150030, 0x2794000c, 0x27930008, 0x3c124000, 0x3c1e0800, 0x8f820004,
+ 0x3c040800, 0x8c830020, 0x10430005, 0x8ee200a4, 0xaf830004, 0x0e001593,
+ 0x00000000, 0x8ee200a4, 0x8ec300a0, 0x10430004, 0x26c400a0, 0x94820002,
+ 0xa742009e, 0xaee300a4, 0x8f500000, 0x32020007, 0x1040ffee, 0x32020001,
+ 0x1040002c, 0x32020002, 0x8f420100, 0xaf420020, 0x8f430104, 0xaf4300a8,
+ 0x9342010b, 0x93630000, 0x306300ff, 0x10710005, 0x304400ff, 0x10750006,
+ 0x2c820016, 0x0a000333, 0x00000000, 0xaf940000, 0x0a000334, 0x2c820016,
+ 0xaf930000, 0x0a000334, 0x00000000, 0xaf800000, 0x14400005, 0x00041880,
+ 0x0e0003cc, 0x00000000, 0x0a000340, 0x00000000, 0x3c020800, 0x24425860,
+ 0x00621821, 0x8c620000, 0x0040f809, 0x00000000, 0x10400005, 0x3c030800,
+ 0x8f420104, 0x3c016020, 0xac220014, 0x3c030800, 0x8c620034, 0xaf520138,
+ 0x24420001, 0xac620034, 0x32020002, 0x1040001a, 0x32020004, 0x8f420140,
+ 0xaf420020, 0x93630000, 0x306300ff, 0x10710005, 0x00000000, 0x10750006,
+ 0x00000000, 0x0a00035d, 0x00000000, 0xaf940000, 0x0a00035e, 0x00000000,
+ 0xaf930000, 0x0a00035e, 0x00000000, 0xaf800000, 0x0e000c7b, 0x00000000,
+ 0x3c040800, 0x8c820038, 0xaf520178, 0x24420001, 0xac820038, 0x32020004,
+ 0x1040ffa4, 0x00000000, 0x8f420180, 0xaf420020, 0x93630000, 0x306300ff,
+ 0x10710005, 0x00000000, 0x10750006, 0x00000000, 0x0a000378, 0x00000000,
+ 0xaf940000, 0x0a000379, 0x00000000, 0xaf930000, 0x0a000379, 0x00000000,
+ 0xaf800000, 0x8f430180, 0x24020f00, 0x14620005, 0x00000000, 0x8f420188,
+ 0xa742009c, 0x0a000387, 0x8fc2003c, 0x93620000, 0x14510004, 0x8fc2003c,
+ 0x0e000bad, 0x00000000, 0x8fc2003c, 0xaf5201b8, 0x24420001, 0x0a00030b,
+ 0xafc2003c, 0x27bdffe8, 0xafbf0010, 0x97420108, 0x24033000, 0x30447000,
+ 0x10830016, 0x28823001, 0x10400007, 0x24024000, 0x1080000b, 0x24022000,
+ 0x1082000c, 0x00000000, 0x0a0003b3, 0x00000000, 0x10820010, 0x24025000,
+ 0x10820012, 0x00000000, 0x0a0003b3, 0x00000000, 0x0000000d, 0x0a0003b5,
+ 0x00001021, 0x0e000442, 0x00000000, 0x0a0003b6, 0x8fbf0010, 0x0e00041a,
+ 0x00000000, 0x0a0003b5, 0x00001021, 0x0e000669, 0x00000000, 0x0a0003b5,
+ 0x00001021, 0x0e001467, 0x00000000, 0x0a0003b5, 0x00001021, 0x0000000d,
+ 0x00001021, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x93620000, 0x24030020,
+ 0x304400ff, 0x10830005, 0x24020030, 0x10820007, 0x00000000, 0x0a0003c9,
+ 0x00000000, 0x2782000c, 0xaf820000, 0x03e00008, 0x00000000, 0x27820008,
+ 0xaf820000, 0x03e00008, 0x00000000, 0xaf800000, 0x03e00008, 0x00000000,
+ 0x0000000d, 0x03e00008, 0x00001021, 0x03e00008, 0x00001021, 0x27440100,
+ 0x94830008, 0x30620004, 0x10400017, 0x30620002, 0x8f4202b8, 0x04430007,
+ 0x8c820020, 0x3c040800, 0x8c830060, 0x24020001, 0x24630001, 0x03e00008,
+ 0xac830060, 0xaf420280, 0x94830016, 0x3c060800, 0xa7430284, 0x8c850004,
+ 0x8cc4005c, 0x00001021, 0x3c031000, 0x24840001, 0xaf450288, 0xaf4302b8,
+ 0x03e00008, 0xacc4005c, 0x14400003, 0x3c040800, 0x03e00008, 0x00001021,
+ 0x8c830084, 0x24020001, 0x24630001, 0x03e00008, 0xac830084, 0x27450100,
+ 0x3c040800, 0x8c820088, 0x94a3000c, 0x24420001, 0x007a1821, 0xac820088,
+ 0x8ca40018, 0x90664000, 0xaf440038, 0x8ca2001c, 0x2403fff8, 0x00063600,
+ 0x00431024, 0x34420004, 0x3c030005, 0xaf42003c, 0xaf430030, 0x00000000,
+ 0x00000000, 0x00000000, 0xaf460404, 0x00000000, 0x00000000, 0x00000000,
+ 0x3c020006, 0x34420001, 0xaf420030, 0x00000000, 0x00000000, 0x00000000,
+ 0x8f420000, 0x30420010, 0x1040fffd, 0x00001021, 0x03e00008, 0x00000000,
+ 0x3c020800, 0x8c430020, 0x27bdffe8, 0xafb00010, 0x27500100, 0x1060001e,
+ 0xafbf0014, 0x0e00148e, 0x00000000, 0x8f830018, 0x8e020018, 0xac620000,
+ 0x8f840018, 0x9602000c, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018,
+ 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018, 0xac400014, 0x8f840018,
+ 0x3c026000, 0x8c434448, 0xac830018, 0x96020008, 0x3c030800, 0x946458ce,
+ 0x8f850018, 0x00021400, 0x00441025, 0x24040001, 0x0e0014cc, 0xaca2001c,
+ 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x27bdffe8, 0xafb00010,
+ 0x27500100, 0xafbf0014, 0x92020009, 0x14400003, 0x3c020800, 0x0a00046c,
+ 0x24020001, 0x8c430020, 0x1060001f, 0x00001021, 0x0e00148e, 0x00000000,
+ 0x8f830018, 0x8e020018, 0xac620000, 0x8f840018, 0x9602000c, 0xac820004,
+ 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010,
+ 0x8f820018, 0xac400014, 0x8f840018, 0x3c026000, 0x8c434448, 0xac830018,
+ 0x96020008, 0x3c030800, 0x946458ce, 0x8f850018, 0x00021400, 0x00441025,
+ 0x24040001, 0x0e0014cc, 0xaca2001c, 0x00001021, 0x8fbf0014, 0x8fb00010,
+ 0x03e00008, 0x27bd0018, 0x3c0b0800, 0x8d6808b0, 0x3c070800, 0x24e700b0,
+ 0x00084900, 0x01271821, 0xac640000, 0x93620005, 0x97660008, 0x00e95021,
+ 0x93630023, 0x9364003f, 0x25080001, 0x00021600, 0x00063400, 0x00461025,
+ 0x00031a00, 0x00431025, 0x00822025, 0xad440004, 0x9362007e, 0x9366007f,
+ 0x8f630178, 0x9364007a, 0x00021600, 0x00063400, 0x00461025, 0x00031a00,
+ 0x00431025, 0x00822025, 0xad440008, 0x93620080, 0x9363007d, 0x3108007f,
+ 0x01403821, 0xad6808b0, 0x00021600, 0x00031c00, 0x00431025, 0x00451025,
+ 0x03e00008, 0xace2000c, 0x27bdffb8, 0xafb3002c, 0x00009821, 0xafbe0040,
+ 0x0000f021, 0xafb50034, 0x27550100, 0xafbf0044, 0xafb7003c, 0xafb60038,
+ 0xafb40030, 0xafb20028, 0xafb10024, 0xafb00020, 0xafa00010, 0xafa00014,
+ 0x96a20008, 0x8f540100, 0x8eb10018, 0x30420001, 0x10400037, 0x02a0b821,
+ 0x8f630054, 0x2622ffff, 0x00431023, 0x18400006, 0x00000000, 0x0000000d,
+ 0x00000000, 0x2400015c, 0x0a0004e5, 0x00002021, 0x8f62004c, 0x02221023,
+ 0x18400028, 0x00002021, 0x93650120, 0x93640121, 0x3c030800, 0x8c62008c,
+ 0x308400ff, 0x24420001, 0x30a500ff, 0x00803821, 0x1485000b, 0xac62008c,
+ 0x3c040800, 0x8c830090, 0x24630001, 0xac830090, 0x93620122, 0x30420001,
+ 0x00021023, 0x30420005, 0x0a0004e5, 0x34440004, 0x27660100, 0x00041080,
+ 0x00c21021, 0x8c430000, 0x02231823, 0x04600004, 0x24820001, 0x30440007,
+ 0x1485fff9, 0x00041080, 0x10870007, 0x3c030800, 0xa3640121, 0x8c620094,
+ 0x24040005, 0x24420001, 0x0a0004e5, 0xac620094, 0x24040004, 0x00809821,
+ 0x9362003f, 0x304400ff, 0x38830016, 0x2c630001, 0x38820010, 0x2c420001,
+ 0x00621825, 0x1460000c, 0x24020001, 0x38830008, 0x2c630001, 0x38820014,
+ 0x2c420001, 0x00621825, 0x14600005, 0x24020001, 0x24020012, 0x14820002,
+ 0x00001021, 0x24020001, 0x10400009, 0x00000000, 0x8ea20020, 0x8f630040,
+ 0x0040b021, 0x00431023, 0x5c400010, 0x8f760040, 0x0a000511, 0x00000000,
+ 0x9343010b, 0x24020004, 0x1462000a, 0x8eb60020, 0x8f630040, 0x3c021000,
+ 0x00761823, 0x0043102a, 0x10400004, 0x00000000, 0x0000000d, 0x00000000,
+ 0x240002fa, 0x9343010b, 0x24020004, 0x5462000b, 0x96a20008, 0x24020001,
+ 0xafa20010, 0x96a20008, 0x24030001, 0xafa30018, 0x8eb2001c, 0x36730002,
+ 0x30420020, 0x0a000526, 0xafa20014, 0x36730080, 0x30420002, 0x10400003,
+ 0xafa00018, 0x0a000526, 0x8eb2001c, 0x8eb20014, 0x2402fffb, 0x02628024,
+ 0x1200002a, 0x3c030800, 0x8c620030, 0x02021024, 0x10400026, 0x3c020800,
+ 0x8c430020, 0x10600024, 0x32620004, 0x0e00148e, 0x00000000, 0x8f830018,
+ 0x8f420100, 0xac620000, 0x8f840018, 0x02401821, 0x32620002, 0xac900004,
+ 0x8f840018, 0x54400001, 0x02c01821, 0xac830008, 0x8f830018, 0x8ee20020,
+ 0xac62000c, 0x8f840018, 0x8f620040, 0xac820010, 0x8f830018, 0x8ee20018,
+ 0xac620014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800,
+ 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024010, 0x00621825, 0x0e0014cc,
+ 0xaca3001c, 0x32620004, 0x10400063, 0x00003821, 0x3c029000, 0x34420001,
+ 0x3c038000, 0x02821025, 0xa360007c, 0xaf420020, 0x8f420020, 0x00431024,
+ 0x1440fffd, 0x00000000, 0x93620023, 0x30420080, 0x10400011, 0x00000000,
+ 0x8f65005c, 0x8f63004c, 0x9764003c, 0x8f620064, 0x00a32823, 0x00852821,
+ 0x00a2102b, 0x54400006, 0x3c023fff, 0x93620023, 0x3042007f, 0xa3620023,
+ 0xaf710064, 0x3c023fff, 0x0a000580, 0x3442ffff, 0x8f62005c, 0x02221023,
+ 0x04400011, 0x00000000, 0x8f65005c, 0x8f630064, 0x9764003c, 0x3c023fff,
+ 0x3442ffff, 0xaf710064, 0x00a32823, 0x00852821, 0x0045102b, 0x10400004,
+ 0x02251021, 0x3c053fff, 0x34a5ffff, 0x02251021, 0xaf62005c, 0x24070001,
+ 0xaf71004c, 0x8f620054, 0x16220005, 0x00000000, 0x93620023, 0x30420040,
+ 0x10400017, 0x24020001, 0x9762006a, 0x00022880, 0x50a00001, 0x24050001,
+ 0x97630068, 0x93640081, 0x3c020800, 0x8c46004c, 0x00652821, 0x00852804,
+ 0x00c5102b, 0x54400001, 0x00a03021, 0x3c020800, 0x8c440050, 0x00c4182b,
+ 0x54600001, 0x00c02021, 0x8f420074, 0x2403fffe, 0x00832824, 0x00a21021,
+ 0xaf62000c, 0x93620082, 0x30420080, 0x50400001, 0xa3600081, 0x3c028000,
+ 0x34420001, 0x02821025, 0xaf420020, 0x9363007e, 0x9362007a, 0x10620004,
+ 0x00000000, 0x0e0013c4, 0x00000000, 0x00403821, 0x54e00001, 0x241e0001,
+ 0x8f700040, 0x8f620040, 0x14520003, 0x00521023, 0x0a0005bf, 0x00001021,
+ 0x28420001, 0x10400041, 0x8fa20010, 0x0e000fae, 0x02402021, 0xaf720040,
+ 0x9362003e, 0x30420001, 0x1440000b, 0x3c029000, 0x93620022, 0x24420001,
+ 0xa3620022, 0x93630022, 0x3c020800, 0x8c440098, 0x0064182b, 0x14600027,
+ 0x3c020800, 0x3c029000, 0x34420001, 0x02821025, 0xaf420020, 0x3c038000,
+ 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000,
+ 0x34420001, 0xa362007d, 0x8f640074, 0x34630001, 0x02831825, 0xaf430020,
+ 0x04810006, 0x3c038000, 0x02802021, 0x0e000470, 0x24050273, 0x0a0005f2,
+ 0x24050001, 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000,
+ 0xaf5401c0, 0xa34201c4, 0xaf4301f8, 0x24050001, 0x24020001, 0xa7620012,
+ 0xa3600022, 0x0a0005fe, 0x2ca20001, 0x9743007a, 0x9444002a, 0x00002821,
+ 0x00641821, 0x3063fffe, 0xa7630012, 0x2ca20001, 0x00021023, 0x03c2f024,
+ 0x8fa20010, 0x10400004, 0x8fa30014, 0x0e0013c1, 0x00000000, 0x8fa30014,
+ 0x10600003, 0x00000000, 0x0e0010eb, 0x00000000, 0x13c0001f, 0x3c029000,
+ 0x34420001, 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024,
+ 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, 0xa362007d, 0x8f640074,
+ 0x34630001, 0x02831825, 0xaf430020, 0x04810006, 0x3c038000, 0x02802021,
+ 0x0e000470, 0x2405036c, 0x0a00062b, 0x8fa20018, 0x8f4201f8, 0x00431024,
+ 0x1440fffd, 0x24020002, 0x3c031000, 0xaf5401c0, 0xa34201c4, 0xaf4301f8,
+ 0x8fa20018, 0x5040002f, 0x96a20008, 0x8f620048, 0x8f630024, 0x00761821,
+ 0xaf630048, 0x9764003c, 0x00501023, 0x0044102b, 0x10400025, 0x3c029000,
+ 0x34420001, 0x3c040800, 0x8c830080, 0x8f450100, 0x3c068000, 0x24630001,
+ 0x00a21025, 0xac830080, 0xaf420020, 0x8f420020, 0x00461024, 0x1440fffd,
+ 0x00000000, 0x9362007d, 0x3c038000, 0x34420004, 0xa362007d, 0x8f640074,
+ 0x34630001, 0x00a31825, 0xaf430020, 0x04810006, 0x3c038000, 0x00a02021,
+ 0x0e000470, 0x2405038a, 0x0a00065b, 0x96a20008, 0x8f4201f8, 0x00431024,
+ 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4501c0, 0xa34201c4, 0xaf4301f8,
+ 0x96a20008, 0x8fbf0044, 0x8fbe0040, 0x8fb7003c, 0x8fb60038, 0x8fb50034,
+ 0x8fb40030, 0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, 0x00021042,
+ 0x30420001, 0x03e00008, 0x27bd0048, 0x27bdffe0, 0xafbf0018, 0x97420108,
+ 0x24030019, 0x304400ff, 0x10830065, 0x2882001a, 0x1040001a, 0x2882000a,
+ 0x1040000f, 0x28820008, 0x10400040, 0x24020001, 0x1082003a, 0x28820002,
+ 0x50400005, 0x24020006, 0x10800032, 0x3c026000, 0x0a0006fb, 0x00000000,
+ 0x1082003d, 0x00000000, 0x0a0006fb, 0x00000000, 0x2402000b, 0x10820044,
+ 0x2882000b, 0x1440004b, 0x2402000e, 0x10820045, 0x00000000, 0x0a0006fb,
+ 0x00000000, 0x24020020, 0x10820062, 0x28820021, 0x1040000e, 0x2402001c,
+ 0x1082004c, 0x2882001d, 0x10400005, 0x2402001b, 0x10820043, 0x00000000,
+ 0x0a0006fb, 0x00000000, 0x2402001f, 0x10820050, 0x00000000, 0x0a0006fb,
+ 0x00000000, 0x240200c1, 0x10820042, 0x288200c2, 0x10400005, 0x24020080,
+ 0x10820021, 0x00000000, 0x0a0006fb, 0x00000000, 0x240200c2, 0x1082003d,
+ 0x240200c9, 0x50820049, 0xafa00010, 0x0a0006fb, 0x00000000, 0x0e001163,
+ 0xac400808, 0x0a0006fd, 0x8fbf0018, 0x3c026000, 0x8c444448, 0x3c030800,
+ 0xac640064, 0x0e001163, 0x00000000, 0x3c026000, 0x8c444448, 0x3c030800,
+ 0x0a0006fc, 0xac640068, 0x8f440100, 0x0e0006ff, 0x00000000, 0x3c026000,
+ 0x8c444448, 0x3c030800, 0x0a0006fc, 0xac64006c, 0x0e001191, 0x00000000,
+ 0x0a0006fd, 0x8fbf0018, 0x8f440100, 0x0e0011bb, 0x00000000, 0x0a0006fd,
+ 0x8fbf0018, 0x0e001202, 0x00000000, 0x0a0006fd, 0x8fbf0018, 0x0000000d,
+ 0x0a0006fd, 0x8fbf0018, 0x0e000826, 0x00000000, 0x0a0006fd, 0x8fbf0018,
+ 0x8f440100, 0x0e001264, 0x00000000, 0x0a0006fd, 0x8fbf0018, 0x0e00134e,
+ 0x00000000, 0x0a0006fd, 0x8fbf0018, 0x0e00087c, 0x27440100, 0x0a0006fd,
+ 0x8fbf0018, 0x8f640040, 0x0e000fae, 0x00000000, 0x0a0006fd, 0x8fbf0018,
+ 0x8f440100, 0x0e001059, 0x00000000, 0x0a0006fd, 0x8fbf0018, 0x0e001417,
+ 0x00000000, 0x0a0006fd, 0x8fbf0018, 0xafa00014, 0x8f440100, 0x8f450118,
+ 0x8f46011c, 0x0e001439, 0x8f470120, 0x0a0006fd, 0x8fbf0018, 0x0000000d,
+ 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0xafbf0010, 0x9742010c,
+ 0x1440005e, 0x00803821, 0x3c029000, 0x34420001, 0x00e21025, 0xaf420020,
+ 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620023,
+ 0x30420010, 0x14400026, 0x3c030800, 0x8f630074, 0x3c027fff, 0x3442ffff,
+ 0x00621824, 0xaf630074, 0x93620005, 0x34420001, 0xa3620005, 0x8f63004c,
+ 0x8f620054, 0x10620021, 0x24040001, 0x9762006a, 0x00022880, 0x50a00001,
+ 0x24050001, 0x97630068, 0x93640081, 0x3c020800, 0x8c46004c, 0x00652821,
+ 0x00852804, 0x00c5102b, 0x54400001, 0x00a03021, 0x3c020800, 0x8c440050,
+ 0x00c4182b, 0x54600001, 0x00c02021, 0x8f420074, 0x2403fffe, 0x00832824,
+ 0x00a21021, 0xaf62000c, 0x0a00073d, 0x24040001, 0x8c6200a8, 0x00002021,
+ 0x24420001, 0xac6200a8, 0x0000000d, 0x00000000, 0x2400044d, 0x3c028000,
+ 0x34420001, 0x00e21025, 0xaf420020, 0x1080001f, 0x3c029000, 0x34420001,
+ 0x00e21025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd,
+ 0x00000000, 0x9362007d, 0x3c038000, 0xa362007d, 0x8f640074, 0x34630001,
+ 0x00e31825, 0xaf430020, 0x04810006, 0x3c038000, 0x00e02021, 0x0e000470,
+ 0x24050455, 0x0a000761, 0x00000000, 0x8f4201f8, 0x00431024, 0x1440fffd,
+ 0x24020002, 0x3c031000, 0xaf4701c0, 0xa34201c4, 0xaf4301f8, 0x0e001163,
+ 0x00000000, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x27bdffd8, 0xafbf0024,
+ 0xafb40020, 0xafb3001c, 0xafb20018, 0xafb10014, 0xafb00010, 0x93630005,
+ 0x00809821, 0x24020030, 0x30630030, 0x146200ac, 0x00a0a021, 0x3c020800,
+ 0x8c430020, 0x106000a6, 0x00000000, 0x0e00148e, 0x00000000, 0x8f830018,
+ 0xac730000, 0x936200c4, 0x30420002, 0x10400004, 0x24020001, 0x8f830018,
+ 0x0a000784, 0x00000000, 0x8f830018, 0x24020003, 0xac620004, 0x8f6200dc,
+ 0x8f630040, 0x00431023, 0x18400004, 0x00000000, 0x0000000d, 0x00000000,
+ 0x24000509, 0x8f840018, 0x8f6200dc, 0xac820008, 0x8f830018, 0xac60000c,
+ 0x8f820018, 0xac400010, 0x8f830018, 0x8f62004c, 0x3c100800, 0xac620014,
+ 0x8f850018, 0x3c026000, 0x8c434448, 0x261258c0, 0x00002021, 0xaca30018,
+ 0x9642000e, 0x8f850018, 0x3c034010, 0x00431025, 0x0e0014cc, 0xaca2001c,
+ 0x8f830018, 0xac730000, 0x9362003e, 0x9363003f, 0x8f840018, 0x00021200,
+ 0x00621825, 0xac830004, 0x93620081, 0x93630082, 0x8f840018, 0x00021600,
+ 0x00031c00, 0x00431025, 0xac820008, 0x8f830018, 0x8f620040, 0xac62000c,
+ 0x8f840018, 0x8f620048, 0xac820010, 0x8f71004c, 0x8f820018, 0xac510014,
+ 0x8f620050, 0x8f850018, 0x00401821, 0x02221023, 0x5c400001, 0x02201821,
+ 0x00002021, 0xaca30018, 0x9642000e, 0x8f850018, 0x3c03c00b, 0x00431025,
+ 0x0e0014cc, 0xaca2001c, 0x8f620054, 0x8f840018, 0x00401821, 0x02221023,
+ 0x5c400001, 0x02201821, 0xac830000, 0x8f840018, 0x8f630058, 0xac830004,
+ 0x93620023, 0x30420010, 0x10400004, 0x00000000, 0x8f830018, 0x0a0007dd,
+ 0x8f620148, 0x8f830018, 0x8f62005c, 0xac620008, 0x8f830018, 0x8f620060,
+ 0xac62000c, 0x8f840018, 0x8f620064, 0xac820010, 0x97630068, 0x9762006a,
+ 0x8f840018, 0x00031c00, 0x00431025, 0xac820014, 0x8f850018, 0x00002021,
+ 0x2402ffff, 0x260358c0, 0xaca20018, 0x9462000e, 0x8f850018, 0x3c03c00c,
+ 0x00431025, 0x0e0014cc, 0xaca2001c, 0x8f840018, 0x8f630018, 0xac830000,
+ 0x936200c4, 0x30420002, 0x10400006, 0x00000000, 0x976200c8, 0x8f830018,
+ 0x3042ffff, 0x0a000803, 0xac620004, 0x8f820018, 0xac400004, 0x8f830018,
+ 0x8f62006c, 0xac620008, 0x8f840018, 0x8f6200dc, 0xac82000c, 0x8f830018,
+ 0xac600010, 0x93620005, 0x8f830018, 0x00021600, 0x00541025, 0xac620014,
+ 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x260258c0, 0xaca30018,
+ 0x9443000e, 0x8f850018, 0x3c02400d, 0x00621825, 0x0e0014cc, 0xaca3001c,
+ 0x0e00122e, 0x02602021, 0x8fbf0024, 0x8fb40020, 0x8fb3001c, 0x8fb20018,
+ 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0028, 0x27bdffe0, 0xafb00010,
+ 0x27500100, 0xafbf0018, 0xafb10014, 0x9603000c, 0x240200c1, 0x54620024,
+ 0x8e040000, 0x3c029000, 0x8f450100, 0x34420001, 0x3c038000, 0x00a21025,
+ 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x9362007d,
+ 0x3c038000, 0x34420004, 0xa362007d, 0x8f640074, 0x34630001, 0x00a31825,
+ 0xaf430020, 0x04810006, 0x3c038000, 0x00a02021, 0x0e000470, 0x240505b2,
+ 0x0a000878, 0x8fbf0018, 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002,
+ 0x3c031000, 0xaf4501c0, 0xa34201c4, 0xaf4301f8, 0x0a000878, 0x8fbf0018,
+ 0x8f65004c, 0x24060001, 0x0e0012a3, 0x240705be, 0x3c020800, 0x8c430020,
+ 0x9611000c, 0x1060001d, 0x8e100000, 0x0e00148e, 0x00000000, 0x8f820018,
+ 0xac500000, 0x8f840018, 0x00111400, 0xac820004, 0x8f830018, 0xac600008,
+ 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f840018, 0x240205c1,
+ 0xac820014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800,
+ 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024019, 0x00621825, 0x0e0014cc,
+ 0xaca3001c, 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020,
+ 0x27bdffb0, 0xafb5003c, 0x0000a821, 0xafbe0048, 0x0000f021, 0xafb70044,
+ 0x0000b821, 0xafb30034, 0x00009821, 0xafb60040, 0x0080b021, 0xafbf004c,
+ 0xafb40038, 0xafb20030, 0xafb1002c, 0xafb00028, 0xafa00010, 0x8f620040,
+ 0x8ec30014, 0x96d1000c, 0x00431023, 0x04410025, 0x8ed40000, 0x32220401,
+ 0x1040030c, 0x3c029000, 0x34420001, 0x02821025, 0xaf420020, 0x3c038000,
+ 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000,
+ 0x34420004, 0xa362007d, 0x8f640074, 0x34630001, 0x02831825, 0xaf430020,
+ 0x04810006, 0x3c038000, 0x02802021, 0x0e000470, 0x24050664, 0x0a000ba2,
+ 0x8fbf004c, 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000,
+ 0xaf5401c0, 0xa34201c4, 0xaf4301f8, 0x0a000ba2, 0x8fbf004c, 0x32220010,
+ 0x1040006b, 0x00003021, 0x9362003f, 0x92c6000f, 0x304500ff, 0x24c3fff8,
+ 0x2c62000f, 0x10400057, 0x3c020800, 0x244257c0, 0x00031880, 0x00621821,
+ 0x8c640000, 0x00800008, 0x00000000, 0x38a20012, 0x0a000924, 0x0002a82b,
+ 0x2402000e, 0x14a20004, 0x2402000c, 0x24150001, 0x0a000924, 0x24060010,
+ 0x10a20049, 0x38a30010, 0x2c630001, 0x38a20016, 0x2c420001, 0x00621825,
+ 0x1460004d, 0x0000a821, 0x24020014, 0x10a2004a, 0x00000000, 0x0000000d,
+ 0x00000000, 0x2400069c, 0x0a000924, 0x0000a821, 0x24020016, 0x14a20005,
+ 0x2402000c, 0x24150001, 0x24060010, 0x0a000924, 0x3231fffd, 0x10a20032,
+ 0x38a30010, 0x2c630001, 0x38a2000e, 0x2c420001, 0x00621825, 0x14600036,
+ 0x0000a821, 0x24020014, 0x14a20003, 0x24150001, 0x0a000924, 0x24060012,
+ 0x0000000d, 0x00000000, 0x240006bc, 0x0a000924, 0x0000a821, 0x2402000e,
+ 0x14a20004, 0x24020016, 0x24150001, 0x0a000924, 0x3231fffb, 0x14a20004,
+ 0x24020014, 0x24150001, 0x0a000924, 0x3231fffd, 0x54a20013, 0x92c2000e,
+ 0x24150001, 0x24060012, 0x0a000924, 0x3231fffd, 0x2402000c, 0x54a2000c,
+ 0x92c2000e, 0x92c3000e, 0x2402000a, 0x10620005, 0x24150001, 0x0000000d,
+ 0x00000000, 0x240006e8, 0x24150001, 0x0a000924, 0x24060014, 0x92c2000e,
+ 0x14a20003, 0x00000000, 0x0a000924, 0x24150001, 0x10a6ffc1, 0x24020012,
+ 0x10a20005, 0x0000a821, 0x0000000d, 0x00000000, 0x24000704, 0x0000a821,
+ 0x12a00022, 0x32220004, 0x10400002, 0x24020001, 0xafa20010, 0x32230102,
+ 0x24020002, 0x1462000f, 0x00000000, 0x92c2000a, 0x30420020, 0x1440000b,
+ 0x00000000, 0x8f630048, 0x8f620040, 0x14620004, 0x00000000, 0x8f620048,
+ 0x24420001, 0xaf620048, 0x8f620040, 0x24420001, 0xaf620040, 0xa366003f,
+ 0x38c30012, 0x2c630001, 0x38c20010, 0x2c420001, 0x00621825, 0x10600005,
+ 0x3c030800, 0x8c620074, 0x24420001, 0x0e00140d, 0xac620074, 0x32220040,
+ 0x32230020, 0xafa30020, 0x32230080, 0xafa30024, 0x32230001, 0xafa30018,
+ 0x32230008, 0xafa3001c, 0x32230100, 0x104000c4, 0xafa30014, 0x8ec60010,
+ 0x8f630054, 0x24c2ffff, 0x00431023, 0x18400006, 0x00000000, 0x0000000d,
+ 0x00000000, 0x2400015c, 0x0a000989, 0x00009021, 0x8f62004c, 0x00c21023,
+ 0x18400028, 0x00009021, 0x93650120, 0x93640121, 0x3c030800, 0x8c62008c,
+ 0x308400ff, 0x24420001, 0x30a500ff, 0x00804021, 0x1485000b, 0xac62008c,
+ 0x3c040800, 0x8c830090, 0x24630001, 0xac830090, 0x93620122, 0x30420001,
+ 0x00021023, 0x30420005, 0x0a000989, 0x34520004, 0x27670100, 0x00041080,
+ 0x00e21021, 0x8c430000, 0x00c31823, 0x04600004, 0x24820001, 0x30440007,
+ 0x1485fff9, 0x00041080, 0x10880007, 0x3c030800, 0xa3640121, 0x8c620094,
+ 0x24120005, 0x24420001, 0x0a000989, 0xac620094, 0x24120004, 0x32420001,
+ 0x10400021, 0x3c020800, 0x8c430020, 0x8ed00000, 0x1060001c, 0x8ed30010,
+ 0x0e00148e, 0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x24020001,
+ 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018,
+ 0xac600010, 0x8f820018, 0xac530014, 0x8f850018, 0x3c026000, 0x8c434448,
+ 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024010,
+ 0x00621825, 0x0e0014cc, 0xaca3001c, 0x24130001, 0x32420004, 0x10400068,
+ 0x00003821, 0x3c029000, 0x8ec60010, 0x34420001, 0x3c038000, 0x02821025,
+ 0xa360007c, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000,
+ 0x93620023, 0x30420080, 0x10400011, 0x00000000, 0x8f65005c, 0x8f63004c,
+ 0x9764003c, 0x8f620064, 0x00a32823, 0x00852821, 0x00a2102b, 0x54400006,
+ 0x3c023fff, 0x93620023, 0x3042007f, 0xa3620023, 0xaf660064, 0x3c023fff,
+ 0x0a0009da, 0x3442ffff, 0x8f62005c, 0x00c21023, 0x04400011, 0x00000000,
+ 0x8f65005c, 0x8f630064, 0x9764003c, 0x3c023fff, 0x3442ffff, 0xaf660064,
+ 0x00a32823, 0x00852821, 0x0045102b, 0x10400004, 0x00c51021, 0x3c053fff,
+ 0x34a5ffff, 0x00c51021, 0xaf62005c, 0x24070001, 0xaf66004c, 0x8fa20010,
+ 0x10400003, 0x00000000, 0xaf660050, 0xaf660054, 0x8f620054, 0x14c20005,
+ 0x00000000, 0x93620023, 0x30420040, 0x10400017, 0x24020001, 0x9762006a,
+ 0x00022880, 0x50a00001, 0x24050001, 0x97630068, 0x93640081, 0x3c020800,
+ 0x8c46004c, 0x00652821, 0x00852804, 0x00c5102b, 0x54400001, 0x00a03021,
+ 0x3c020800, 0x8c440050, 0x00c4182b, 0x54600001, 0x00c02021, 0x8f420074,
+ 0x2403fffe, 0x00832824, 0x00a21021, 0xaf62000c, 0x93620082, 0x30420080,
+ 0x50400001, 0xa3600081, 0x3c028000, 0x34420001, 0x02821025, 0xaf420020,
+ 0x9363007e, 0x9362007a, 0x10620005, 0x00e0b821, 0x0e0013c4, 0x00000000,
+ 0x00403821, 0x00e0b821, 0x8fa30020, 0x10600009, 0x8fa20010, 0x8ec20018,
+ 0xaf620018, 0x8ec3001c, 0xaf63001c, 0x8ec20020, 0x24170001, 0xaf620058,
+ 0x8fa20010, 0x10400057, 0x8fa30024, 0x93620023, 0x30420040, 0x10400053,
+ 0x00000000, 0x16600021, 0x3c120800, 0x8e420020, 0x8f70004c, 0x1040001e,
+ 0x24130001, 0x0e00148e, 0x00000000, 0x8f820018, 0xac540000, 0x8f840018,
+ 0x24020001, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c,
+ 0x8f830018, 0xac600010, 0x8f820018, 0xac500014, 0x8f850018, 0x3c026000,
+ 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018,
+ 0x3c024010, 0x00621825, 0xaca3001c, 0x0e0014cc, 0x24130001, 0x8e420020,
+ 0x1040001c, 0x8ed00000, 0x0e00148e, 0x00000000, 0x8f820018, 0xac500000,
+ 0x8f830018, 0xac600004, 0x8f820018, 0xac400008, 0x8f830018, 0xac60000c,
+ 0x8f820018, 0xac400010, 0x8f830018, 0x24020798, 0xac620014, 0x8f850018,
+ 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce,
+ 0x8f850018, 0x3c024019, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x3c029000,
+ 0x34420001, 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024,
+ 0x1440fffd, 0x24020001, 0xaf62000c, 0x93630023, 0x3c028000, 0x34420001,
+ 0x02821025, 0x306300bf, 0xa3630023, 0xaf420020, 0x8fa30024, 0x10600012,
+ 0x8fa30018, 0x9362007c, 0x24420001, 0xa362007c, 0x9363007e, 0x9362007a,
+ 0x1462000b, 0x8fa30018, 0x9362007c, 0x3c030800, 0x8c640024, 0x0044102b,
+ 0x14400005, 0x8fa30018, 0x0e0013c4, 0x00000000, 0x02e2b825, 0x8fa30018,
+ 0x3062ffff, 0x10400003, 0x32220200, 0x0a000a94, 0x241e0004, 0x10400003,
+ 0x00000000, 0x241e0040, 0x24170001, 0x12a000d0, 0x32220002, 0x104000cf,
+ 0x8fa2001c, 0x92c2000a, 0x30420002, 0x5040003b, 0x92c2000a, 0x93620023,
+ 0x30420008, 0x54400037, 0x92c2000a, 0x3c020800, 0x8c430020, 0x10600023,
+ 0x3c029000, 0x0e00148e, 0x00000000, 0x8f840018, 0x8ec30000, 0xac830000,
+ 0x92c2000a, 0x8f830018, 0x00021600, 0xac620004, 0x8f840018, 0x8f620040,
+ 0xac820008, 0x8f850018, 0x8f63004c, 0xaca3000c, 0x9362003f, 0x8f840018,
+ 0x304200ff, 0xac820010, 0x8f830018, 0x3c026000, 0xac600014, 0x8f850018,
+ 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018,
+ 0x3c02401a, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x3c029000, 0x34420001,
+ 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd,
+ 0x00000000, 0x93630023, 0x3c028000, 0x34420001, 0x02821025, 0x34630008,
+ 0xa3630023, 0xaf420020, 0x92c2000a, 0x30420020, 0x1040008e, 0x8fa2001c,
+ 0x93620023, 0x30420001, 0x14400035, 0x3c020800, 0x8c430020, 0x10600023,
+ 0x3c029000, 0x0e00148e, 0x00000000, 0x8f840018, 0x8ec30000, 0xac830000,
+ 0x92c2000a, 0x8f830018, 0x00021600, 0xac620004, 0x8f840018, 0x8f620040,
+ 0xac820008, 0x8f850018, 0x8f63004c, 0xaca3000c, 0x9362003f, 0x8f840018,
+ 0x304200ff, 0xac820010, 0x8f830018, 0x3c026000, 0xac600014, 0x8f850018,
+ 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018,
+ 0x3c02401a, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x3c029000, 0x34420001,
+ 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd,
+ 0x00000000, 0x93630023, 0x3c028000, 0x34420001, 0x02821025, 0x34630001,
+ 0xa3630023, 0xaf420020, 0x93620023, 0x30420040, 0x10400052, 0x8fa2001c,
+ 0x16600020, 0x3c120800, 0x8e420020, 0x8f70004c, 0x1040003c, 0x3c029000,
+ 0x0e00148e, 0x00000000, 0x8f820018, 0xac540000, 0x8f840018, 0x24020001,
+ 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018,
+ 0xac600010, 0x8f820018, 0xac500014, 0x8f850018, 0x3c026000, 0x8c434448,
+ 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024010,
+ 0x00621825, 0x0e0014cc, 0xaca3001c, 0x8e420020, 0x1040001e, 0x3c029000,
+ 0x0e00148e, 0x00000000, 0x8f820018, 0xac540000, 0x8f840018, 0x3c02008d,
+ 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018,
+ 0xac600010, 0x8f840018, 0x240207ee, 0xac820014, 0x8f850018, 0x3c026000,
+ 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018,
+ 0x3c024019, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x3c029000, 0x34420001,
+ 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd,
+ 0x00000000, 0x93630023, 0x3c028000, 0x34420001, 0x02821025, 0x306300bf,
+ 0xa3630023, 0xaf420020, 0x8fa2001c, 0x1040000e, 0x8fa20014, 0x92c2000a,
+ 0xa3620082, 0x57c00005, 0x37de0008, 0x8fa30014, 0x10600004, 0x00000000,
+ 0x37de0008, 0x0a000b75, 0x24170001, 0x0e0012cf, 0x02802021, 0x8fa20014,
+ 0x10400003, 0x00000000, 0x37de0010, 0x24170001, 0x12e00020, 0x3c029000,
+ 0x34420001, 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024,
+ 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, 0x03c21025, 0xa362007d,
+ 0x8f640074, 0x34630001, 0x02831825, 0xaf430020, 0x04810006, 0x3c038000,
+ 0x02802021, 0x0e000470, 0x2405082a, 0x0a000b9b, 0x00000000, 0x8f4201f8,
+ 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf5401c0, 0xa34201c4,
+ 0xaf4301f8, 0x9363003f, 0x24020012, 0x14620004, 0x8fbf004c, 0x0e00140d,
+ 0x00000000, 0x8fbf004c, 0x8fbe0048, 0x8fb70044, 0x8fb60040, 0x8fb5003c,
+ 0x8fb40038, 0x8fb30034, 0x8fb20030, 0x8fb1002c, 0x8fb00028, 0x03e00008,
+ 0x27bd0050, 0x27bdffe8, 0xafbf0014, 0xafb00010, 0x8f500180, 0x97420184,
+ 0x30420200, 0x14400015, 0x00000000, 0x8f430188, 0x3c02ff00, 0x00621824,
+ 0x3c020200, 0x10620031, 0x0043102b, 0x14400007, 0x3c020300, 0x1060000b,
+ 0x3c020100, 0x1062000d, 0x00000000, 0x0a000c2c, 0x00000000, 0x10620027,
+ 0x3c020400, 0x1062003e, 0x02002021, 0x0a000c2c, 0x00000000, 0x0e000c31,
+ 0x02002021, 0x0a000c2e, 0x8fbf0014, 0x93620005, 0x30420020, 0x1440005e,
+ 0x8fbf0014, 0x3c029000, 0x34420001, 0x02021025, 0xaf420020, 0x3c038000,
+ 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620005, 0x3c038000,
+ 0x34630001, 0x02031825, 0x34420020, 0xa3620005, 0xaf430020, 0x93620005,
+ 0x30420020, 0x14400003, 0x02002021, 0x0000000d, 0x02002021, 0x0e000766,
+ 0x24055854, 0x0a000c2e, 0x8fbf0014, 0x93620005, 0x30420001, 0x1040003f,
+ 0x3c029000, 0x34420001, 0x02021025, 0xaf420020, 0x3c038000, 0x8f420020,
+ 0x00431024, 0x1440fffd, 0x00000000, 0x93620023, 0x34420004, 0xa3620023,
+ 0x93630005, 0x3c048000, 0x3c020800, 0x306300fe, 0xa3630005, 0x8c430020,
+ 0x34840001, 0x02042025, 0x0a000c0a, 0xaf440020, 0x00002821, 0x00003021,
+ 0x0e000fb1, 0x240708d9, 0x3c020800, 0x8c430020, 0x10600023, 0x8fbf0014,
+ 0x0e00148e, 0x00000000, 0x8f820018, 0xac500000, 0x93630082, 0x9362003f,
+ 0x8f840018, 0x00031a00, 0x00431025, 0xac820004, 0x8f830018, 0xac600008,
+ 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018, 0xac400014,
+ 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018,
+ 0x944358ce, 0x8f850018, 0x3c02400a, 0x00621825, 0x0e0014cc, 0xaca3001c,
+ 0x0a000c2e, 0x8fbf0014, 0x0000000d, 0x8fbf0014, 0x8fb00010, 0x03e00008,
+ 0x27bd0018, 0x27bdffe8, 0xafbf0010, 0x8f420188, 0x00803021, 0x93640000,
+ 0x24030020, 0x00021402, 0x10830008, 0x304500ff, 0x3c036018, 0x8c625000,
+ 0x34420400, 0xac625000, 0x0000000d, 0x00000000, 0x24000955, 0x9363003f,
+ 0x24020012, 0x14620023, 0x3c029000, 0x34420001, 0x3c038000, 0x00c21025,
+ 0xaf650178, 0xa365007a, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd,
+ 0x00000000, 0x9362007d, 0x3c038000, 0xa362007d, 0x8f640074, 0x34630001,
+ 0x00c31825, 0xaf430020, 0x04810006, 0x3c038000, 0x00c02021, 0x0e000470,
+ 0x24050963, 0x0a000c79, 0x8fbf0010, 0x8f4201f8, 0x00431024, 0x1440fffd,
+ 0x24020002, 0x3c031000, 0xaf4601c0, 0xa34201c4, 0xaf4301f8, 0x0a000c79,
+ 0x8fbf0010, 0x9362007e, 0x1445000e, 0x00000000, 0x8f620178, 0x1045000b,
+ 0x00000000, 0x8f820000, 0xaf650178, 0x8f660178, 0x8f440180, 0x8f65004c,
+ 0x8c430000, 0x0060f809, 0x30c600ff, 0x0a000c79, 0x8fbf0010, 0xaf650178,
+ 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x27bdffe8, 0xafbf0010, 0x93630000,
+ 0x24020020, 0x10620005, 0x00000000, 0x93630000, 0x24020030, 0x1462004d,
+ 0x8fbf0010, 0x93420148, 0x2444ffff, 0x2c830005, 0x10600047, 0x3c020800,
+ 0x24425800, 0x00041880, 0x00621821, 0x8c640000, 0x00800008, 0x00000000,
+ 0x8f430144, 0x8f62000c, 0x14620006, 0x24020001, 0xaf62000c, 0x0e000d59,
+ 0x00000000, 0x0a000cd1, 0x8fbf0010, 0x8f62000c, 0x0a000cca, 0x00000000,
+ 0x97630010, 0x8f420144, 0x14430006, 0x24020001, 0xa7620010, 0x0e00137a,
+ 0x00000000, 0x0a000cd1, 0x8fbf0010, 0x97620010, 0x0a000cca, 0x00000000,
+ 0x97630012, 0x8f420144, 0x14430006, 0x24020001, 0xa7620012, 0x0e001395,
+ 0x00000000, 0x0a000cd1, 0x8fbf0010, 0x97620012, 0x0a000cca, 0x00000000,
+ 0x97630014, 0x8f420144, 0x14430006, 0x24020001, 0xa7620014, 0x0e0013bb,
+ 0x00000000, 0x0a000cd1, 0x8fbf0010, 0x97620014, 0x0a000cca, 0x00000000,
+ 0x97630016, 0x8f420144, 0x14430006, 0x24020001, 0xa7620016, 0x0e0013be,
+ 0x00000000, 0x0a000cd1, 0x8fbf0010, 0x97620016, 0x14400006, 0x8fbf0010,
+ 0x3c030800, 0x8c620070, 0x24420001, 0xac620070, 0x8fbf0010, 0x03e00008,
+ 0x27bd0018, 0x27bdffe0, 0x3c029000, 0xafbf001c, 0xafb20018, 0xafb10014,
+ 0xafb00010, 0x8f500140, 0x34420001, 0x3c038000, 0x02021025, 0xaf420020,
+ 0x8f420020, 0x00431024, 0x1440fffd, 0x24020012, 0x24030080, 0xa362003f,
+ 0xa3630082, 0x93620023, 0x30420040, 0x10400007, 0x00008821, 0x93620023,
+ 0x24110001, 0x304200bf, 0xa3620023, 0x0a000cf0, 0x3c028000, 0x3c028000,
+ 0x34420001, 0x3c039000, 0x34630001, 0x3c048000, 0x02021025, 0x02031825,
+ 0xaf420020, 0xaf430020, 0x8f420020, 0x00441024, 0x1440fffd, 0x00000000,
+ 0x9362007d, 0x3c038000, 0x34420020, 0xa362007d, 0x8f640074, 0x34630001,
+ 0x02031825, 0xaf430020, 0x04810006, 0x3c038000, 0x02002021, 0x0e000470,
+ 0x24050a63, 0x0a000d13, 0x00000000, 0x8f4201f8, 0x00431024, 0x1440fffd,
+ 0x24020002, 0x3c031000, 0xaf5001c0, 0xa34201c4, 0xaf4301f8, 0x1220003f,
+ 0x3c120800, 0x8e420020, 0x8f71004c, 0x1040003c, 0x8fbf001c, 0x0e00148e,
+ 0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x24020001, 0xac820004,
+ 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010,
+ 0x8f820018, 0xac510014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001,
+ 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024010, 0x00621825,
+ 0x0e0014cc, 0xaca3001c, 0x8e420020, 0x1040001e, 0x8fbf001c, 0x0e00148e,
+ 0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x3c02008d, 0xac820004,
+ 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010,
+ 0x8f840018, 0x24020a6a, 0xac820014, 0x8f850018, 0x3c026000, 0x8c434448,
+ 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024019,
+ 0x00621825, 0x0e0014cc, 0xaca3001c, 0x8fbf001c, 0x8fb20018, 0x8fb10014,
+ 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0xafbf0010, 0x93620081,
+ 0x3c030800, 0x8c640048, 0x0044102b, 0x14400005, 0x00000000, 0x0e000cd3,
+ 0x00000000, 0x0a000da4, 0x8fbf0010, 0x93620081, 0x24420001, 0x0e0013c4,
+ 0xa3620081, 0x9763006a, 0x00032880, 0x14a00002, 0x00403821, 0x24050001,
+ 0x97630068, 0x93640081, 0x3c020800, 0x8c46004c, 0x00652821, 0x00852804,
+ 0x00c5102b, 0x54400001, 0x00a03021, 0x3c020800, 0x8c440050, 0x00c4182b,
+ 0x54600001, 0x00c02021, 0x8f420074, 0x2403fffe, 0x00832824, 0x00a21021,
+ 0xaf62000c, 0x10e00021, 0x3c029000, 0x8f450140, 0x34420001, 0x3c038000,
+ 0x00a21025, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000,
+ 0x9362007d, 0x3c038000, 0x34420004, 0xa362007d, 0x8f640074, 0x34630001,
+ 0x00a31825, 0xaf430020, 0x04810006, 0x3c038000, 0x00a02021, 0x0e000470,
+ 0x24050a92, 0x0a000da4, 0x8fbf0010, 0x8f4201f8, 0x00431024, 0x1440fffd,
+ 0x24020002, 0x3c031000, 0xaf4501c0, 0xa34201c4, 0xaf4301f8, 0x8fbf0010,
+ 0x03e00008, 0x27bd0018, 0x27bdffd8, 0xafb3001c, 0x27530100, 0xafbf0024,
+ 0xafb40020, 0xafb20018, 0xafb10014, 0xafb00010, 0x96620008, 0x3c140800,
+ 0x8f520100, 0x30420001, 0x104000da, 0x00000000, 0x8e700018, 0x8f630054,
+ 0x2602ffff, 0x00431023, 0x18400006, 0x00000000, 0x0000000d, 0x00000000,
+ 0x2400015c, 0x0a000dea, 0x00008821, 0x8f62004c, 0x02021023, 0x18400028,
+ 0x00008821, 0x93650120, 0x93640121, 0x3c030800, 0x8c62008c, 0x308400ff,
+ 0x24420001, 0x30a500ff, 0x00803821, 0x1485000b, 0xac62008c, 0x3c040800,
+ 0x8c830090, 0x24630001, 0xac830090, 0x93620122, 0x30420001, 0x00021023,
+ 0x30420005, 0x0a000dea, 0x34510004, 0x27660100, 0x00041080, 0x00c21021,
+ 0x8c430000, 0x02031823, 0x04600004, 0x24820001, 0x30440007, 0x1485fff9,
+ 0x00041080, 0x10870007, 0x3c030800, 0xa3640121, 0x8c620094, 0x24110005,
+ 0x24420001, 0x0a000dea, 0xac620094, 0x24110004, 0x32220001, 0x1040001e,
+ 0x8e820020, 0x1040001d, 0x32220004, 0x0e00148e, 0x00000000, 0x8f820018,
+ 0xac520000, 0x8f840018, 0x24020001, 0xac820004, 0x8f830018, 0xac600008,
+ 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018, 0xac500014,
+ 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018,
+ 0x944358ce, 0x8f850018, 0x3c024010, 0x00621825, 0x0e0014cc, 0xaca3001c,
+ 0x32220004, 0x10400081, 0x00003821, 0x3c029000, 0x34420001, 0x3c038000,
+ 0x02421025, 0xa360007c, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd,
+ 0x00000000, 0x93620023, 0x30420080, 0x10400011, 0x00000000, 0x8f65005c,
+ 0x8f63004c, 0x9764003c, 0x8f620064, 0x00a32823, 0x00852821, 0x00a2102b,
+ 0x54400006, 0x3c023fff, 0x93620023, 0x3042007f, 0xa3620023, 0xaf700064,
+ 0x3c023fff, 0x0a000e37, 0x3442ffff, 0x8f62005c, 0x02021023, 0x04400011,
+ 0x00000000, 0x8f65005c, 0x8f630064, 0x9764003c, 0x3c023fff, 0x3442ffff,
+ 0xaf700064, 0x00a32823, 0x00852821, 0x0045102b, 0x10400004, 0x02051021,
+ 0x3c053fff, 0x34a5ffff, 0x02051021, 0xaf62005c, 0x24070001, 0xaf70004c,
+ 0x8f620054, 0x16020005, 0x00000000, 0x93620023, 0x30420040, 0x10400017,
+ 0x24020001, 0x9762006a, 0x00022880, 0x50a00001, 0x24050001, 0x97630068,
+ 0x93640081, 0x3c020800, 0x8c46004c, 0x00652821, 0x00852804, 0x00c5102b,
+ 0x54400001, 0x00a03021, 0x3c020800, 0x8c440050, 0x00c4182b, 0x54600001,
+ 0x00c02021, 0x8f420074, 0x2403fffe, 0x00832824, 0x00a21021, 0xaf62000c,
+ 0x93620082, 0x30420080, 0x50400001, 0xa3600081, 0x3c028000, 0x34420001,
+ 0x02421025, 0xaf420020, 0x9363007e, 0x9362007a, 0x10620004, 0x00000000,
+ 0x0e0013c4, 0x00000000, 0x00403821, 0x10e0001f, 0x3c029000, 0x34420001,
+ 0x02421025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd,
+ 0x00000000, 0x9362007d, 0x3c038000, 0xa362007d, 0x8f640074, 0x34630001,
+ 0x02431825, 0xaf430020, 0x04810006, 0x3c038000, 0x02402021, 0x0e000470,
+ 0x24050b3d, 0x0a000e8d, 0x00000000, 0x8f4201f8, 0x00431024, 0x1440fffd,
+ 0x24020002, 0x3c031000, 0xaf5201c0, 0xa34201c4, 0xaf4301f8, 0x9342010b,
+ 0x9343010b, 0x8e820020, 0x27500100, 0x38630006, 0x10400029, 0x2c710001,
+ 0x0e00148e, 0x00000000, 0x8f830018, 0x8e020000, 0xac620000, 0x8f840018,
+ 0x96020008, 0xac820004, 0x8f830018, 0x8e020014, 0xac620008, 0x8f850018,
+ 0x3c026000, 0x8c434448, 0xaca3000c, 0x8f840018, 0x96020012, 0xac820010,
+ 0x8f850018, 0x8e030020, 0xaca30014, 0x9602000c, 0x9603000e, 0x8f840018,
+ 0x00021400, 0x00431025, 0xac820018, 0x12200005, 0x3c020800, 0x944358ce,
+ 0x8f840018, 0x0a000eb8, 0x3c024013, 0x944358ce, 0x8f840018, 0x3c024014,
+ 0x00621825, 0xac83001c, 0x0e0014cc, 0x24040001, 0x8e700014, 0x8f620040,
+ 0x14500003, 0x00501023, 0x0a000ec3, 0x00001021, 0x28420001, 0x1040003a,
+ 0x00000000, 0x0e000fae, 0x02002021, 0xaf700040, 0x9362003e, 0x30420001,
+ 0x1440000b, 0x3c029000, 0x93620022, 0x24420001, 0xa3620022, 0x93630022,
+ 0x3c020800, 0x8c440098, 0x0064182b, 0x14600025, 0x3c020800, 0x3c029000,
+ 0x34420001, 0x02421025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024,
+ 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, 0x34420001, 0xa362007d,
+ 0x8f640074, 0x34630001, 0x02431825, 0xaf430020, 0x04810006, 0x3c038000,
+ 0x02402021, 0x0e000470, 0x24050273, 0x0a000ef6, 0x24020001, 0x8f4201f8,
+ 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf5201c0, 0xa34201c4,
+ 0xaf4301f8, 0x24020001, 0xa7620012, 0x0a000efe, 0xa3600022, 0x9743007a,
+ 0x9444002a, 0x00641821, 0x3063fffe, 0xa7630012, 0x97420108, 0x8fbf0024,
+ 0x8fb40020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x00021042,
+ 0x30420001, 0x03e00008, 0x27bd0028, 0x27bdffe0, 0xafb20018, 0x3c120800,
+ 0x8e420020, 0xafb00010, 0x27500100, 0xafbf001c, 0x10400046, 0xafb10014,
+ 0x0e00148e, 0x00000000, 0x8f840018, 0x8e020000, 0xac820000, 0x936300b1,
+ 0x936200c5, 0x8f850018, 0x00031e00, 0x00021400, 0x34420100, 0x00621825,
+ 0xaca30004, 0x8f840018, 0x8e02001c, 0xac820008, 0x8f830018, 0x8f620048,
+ 0xac62000c, 0x8f840018, 0x96020012, 0xac820010, 0x8f830018, 0x8f620040,
+ 0x24040001, 0xac620014, 0x8f850018, 0x3c026000, 0x8c434448, 0x3c020800,
+ 0x245158c0, 0xaca30018, 0x9623000e, 0x8f850018, 0x3c024016, 0x00621825,
+ 0x0e0014cc, 0xaca3001c, 0x96030008, 0x30630010, 0x1060001c, 0x8e420020,
+ 0x1040001a, 0x8e100000, 0x0e00148e, 0x00000000, 0x8f820018, 0xac500000,
+ 0x8f830018, 0xac600004, 0x8f820018, 0xac400008, 0x8f830018, 0xac60000c,
+ 0x8f820018, 0xac400010, 0x8f830018, 0xac600014, 0x8f850018, 0x3c036000,
+ 0x8c634448, 0x24040001, 0xaca30018, 0x9622000e, 0x8f850018, 0x3c034015,
+ 0x00431025, 0x0e0014cc, 0xaca2001c, 0x00001021, 0x8fbf001c, 0x8fb20018,
+ 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe0, 0xafb20018,
+ 0x3c120800, 0x8e420020, 0xafb00010, 0x27500100, 0xafbf001c, 0x10400041,
+ 0xafb10014, 0x0e00148e, 0x00000000, 0x8f830018, 0x8e020000, 0xac620000,
+ 0x8f840018, 0x24020100, 0xac820004, 0x8f830018, 0x8e02001c, 0xac620008,
+ 0x8f840018, 0x8e020018, 0xac82000c, 0x8f830018, 0x96020012, 0xac620010,
+ 0x8f840018, 0x96020008, 0xac820014, 0x8f850018, 0x3c026000, 0x8c434448,
+ 0x24040001, 0x3c020800, 0x245158c0, 0xaca30018, 0x9623000e, 0x8f850018,
+ 0x3c024017, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x96030008, 0x30630010,
+ 0x1060001c, 0x8e420020, 0x1040001a, 0x8e100000, 0x0e00148e, 0x00000000,
+ 0x8f820018, 0xac500000, 0x8f830018, 0xac600004, 0x8f820018, 0xac400008,
+ 0x8f830018, 0xac60000c, 0x8f820018, 0xac400010, 0x8f830018, 0xac600014,
+ 0x8f850018, 0x3c036000, 0x8c634448, 0x24040001, 0xaca30018, 0x9622000e,
+ 0x8f850018, 0x3c034015, 0x00431025, 0x0e0014cc, 0xaca2001c, 0x00001021,
+ 0x8fbf001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020,
+ 0x27bdfff0, 0x03e00008, 0x27bd0010, 0x27bdffd0, 0xafb10014, 0x00808821,
+ 0xafb40020, 0x00c0a021, 0xafbf0028, 0xafb50024, 0xafb3001c, 0xafb20018,
+ 0xafb00010, 0x93620023, 0x00e0a821, 0x30420040, 0x1040003e, 0x30b3ffff,
+ 0x3c120800, 0x8e420020, 0x1040003a, 0x8f70004c, 0x0e00148e, 0x00000000,
+ 0x8f820018, 0xac510000, 0x8f840018, 0x24020001, 0xac820004, 0x8f830018,
+ 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018,
+ 0x24040001, 0xac500014, 0x8f850018, 0x3c026000, 0x8c434448, 0x3c020800,
+ 0x245058c0, 0xaca30018, 0x9603000e, 0x8f850018, 0x3c024010, 0x00621825,
+ 0x0e0014cc, 0xaca3001c, 0x8e430020, 0x1060001b, 0x00000000, 0x0e00148e,
+ 0x00000000, 0x8f820018, 0xac510000, 0x8f840018, 0x3c02008d, 0xac820004,
+ 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010,
+ 0x8f820018, 0xac550014, 0x8f850018, 0x3c036000, 0x8c634448, 0x24040001,
+ 0xaca30018, 0x9602000e, 0x8f850018, 0x3c034019, 0x00431025, 0x0e0014cc,
+ 0xaca2001c, 0x93620023, 0x30420020, 0x14400003, 0x3c120800, 0x1280003f,
+ 0x3c029000, 0x8e420020, 0x8f70004c, 0x1040003b, 0x3c029000, 0x0e00148e,
+ 0x00000000, 0x8f820018, 0xac510000, 0x8f840018, 0x24020001, 0xac820004,
+ 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010,
+ 0x8f820018, 0x24040001, 0xac500014, 0x8f850018, 0x3c026000, 0x8c434448,
+ 0x3c020800, 0x245058c0, 0xaca30018, 0x9603000e, 0x8f850018, 0x3c024010,
+ 0x00621825, 0x0e0014cc, 0xaca3001c, 0x8e430020, 0x1060001c, 0x3c029000,
+ 0x0e00148e, 0x00000000, 0x8f820018, 0xac510000, 0x8f840018, 0x00131400,
+ 0xac820004, 0x8f830018, 0xac750008, 0x8f820018, 0xac40000c, 0x8f830018,
+ 0xac600010, 0x8f820018, 0xac400014, 0x8f850018, 0x3c036000, 0x8c634448,
+ 0x24040001, 0xaca30018, 0x9602000e, 0x8f850018, 0x3c03401b, 0x00431025,
+ 0x0e0014cc, 0xaca2001c, 0x3c029000, 0x34420001, 0x02221025, 0xaf420020,
+ 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93630023,
+ 0x3c028000, 0x34420001, 0x02221025, 0x8fbf0028, 0x8fb50024, 0x8fb40020,
+ 0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x3063009f, 0xa3630023,
+ 0xaf420020, 0x03e00008, 0x27bd0030, 0x27bdffe0, 0xafb10014, 0x27510100,
+ 0x3c029000, 0x34420001, 0xafb00010, 0x00808021, 0x02021025, 0x3c038000,
+ 0xafbf0018, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000,
+ 0xa7600008, 0x8f63005c, 0x3c028000, 0x34420001, 0xaf630148, 0x8f640050,
+ 0x02021025, 0x3c039000, 0xaf64017c, 0xaf420020, 0x8f450100, 0x34630001,
+ 0x3c048000, 0x00a31825, 0xaf430020, 0x8f420020, 0x00441024, 0x1440fffd,
+ 0x00000000, 0x9362007d, 0x3c038000, 0x34420001, 0xa362007d, 0x8f640074,
+ 0x34630001, 0x00a31825, 0xaf430020, 0x04810006, 0x3c038000, 0x00a02021,
+ 0x0e000470, 0x24050de5, 0x0a001093, 0x3c020800, 0x8f4201f8, 0x00431024,
+ 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4501c0, 0xa34201c4, 0xaf4301f8,
+ 0x3c020800, 0x8c430020, 0x1060001e, 0x8fbf0018, 0x0e00148e, 0x00000000,
+ 0x8f830018, 0xac700000, 0x9622000c, 0x8f840018, 0x00021400, 0xac820004,
+ 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010,
+ 0x8f820018, 0xac400014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001,
+ 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c02401f, 0x00621825,
+ 0x0e0014cc, 0xaca3001c, 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x03e00008,
+ 0x27bd0020, 0x3c020800, 0x24424c3c, 0xaf82000c, 0x03e00008, 0x00000000,
+ 0x27bdffe8, 0xafb00010, 0x27500100, 0xafbf0014, 0x8e02001c, 0x14400003,
+ 0x3c020800, 0x0000000d, 0x3c020800, 0x8c430020, 0x10600020, 0x00001021,
+ 0x0e00148e, 0x00000000, 0x8f830018, 0x8e020000, 0xac620000, 0x8f840018,
+ 0x8e02001c, 0xac820004, 0x8f830018, 0xac600008, 0x8f840018, 0x8e020018,
+ 0xac82000c, 0x8f850018, 0x96020012, 0xaca20010, 0x8f830018, 0x3c026000,
+ 0xac600014, 0x8f840018, 0x8c434448, 0x3c020800, 0xac830018, 0x944358ce,
+ 0x8f840018, 0x3c024012, 0x00621825, 0xac83001c, 0x0e0014cc, 0x24040001,
+ 0x00001021, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c020800,
+ 0x97430078, 0x9444002e, 0x00001021, 0x00641821, 0x3063fffe, 0x03e00008,
+ 0xa7630010, 0x27bdfff0, 0x00001021, 0x03e00008, 0x27bd0010, 0x8f420100,
+ 0x34420001, 0xaf4200a4, 0x03e00008, 0x00001021, 0x27bdffe0, 0xafbf0018,
+ 0xafb10014, 0xafb00010, 0x9362007e, 0x30d000ff, 0x16020031, 0x00808821,
+ 0x8f620178, 0x1602002e, 0x00000000, 0x9362007f, 0x1602002b, 0x00000000,
+ 0x9362007a, 0x16020004, 0x00000000, 0x0000000d, 0x00000000, 0x240009d2,
+ 0x0e0013e6, 0x00000000, 0x3c039000, 0x34630001, 0x3c048000, 0x02231825,
+ 0xa370007a, 0xaf430020, 0x8f420020, 0x00441024, 0x1440fffd, 0x00000000,
+ 0x9362007d, 0x3c038000, 0xa362007d, 0x8f640074, 0x34630001, 0x02231825,
+ 0xaf430020, 0x04810006, 0x3c038000, 0x02202021, 0x0e000470, 0x240509dd,
+ 0x0a001138, 0x8fbf0018, 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002,
+ 0x3c031000, 0xaf5101c0, 0xa34201c4, 0xaf4301f8, 0x0a001138, 0x8fbf0018,
+ 0x0000000d, 0x00000000, 0x240009e2, 0x8fbf0018, 0x8fb10014, 0x8fb00010,
+ 0x03e00008, 0x27bd0020, 0x27bdffe8, 0x30a500ff, 0x3c029000, 0x34420001,
+ 0x00803821, 0x00e21025, 0x3c038000, 0xafbf0010, 0xaf420020, 0x8f420020,
+ 0x00431024, 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, 0x00a21025,
+ 0xa362007d, 0x8f640074, 0x34630001, 0x00e31825, 0xaf430020, 0x04810006,
+ 0x3c038000, 0x00e02021, 0x0e000470, 0x00c02821, 0x0a001161, 0x8fbf0010,
+ 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4701c0,
+ 0xa34201c4, 0xaf4301f8, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x3c020800,
+ 0x8c430020, 0x27bdffe8, 0xafb00010, 0x27500100, 0x10600024, 0xafbf0014,
+ 0x0e00148e, 0x00000000, 0x8f830018, 0x8e020000, 0xac620000, 0x8f840018,
+ 0x8e020004, 0xac820004, 0x8f830018, 0x8e020018, 0xac620008, 0x8f840018,
+ 0x8e03001c, 0xac83000c, 0x9602000c, 0x9203000a, 0x8f840018, 0x00021400,
+ 0x00431025, 0xac820010, 0x8f830018, 0x3c026000, 0xac600014, 0x8f840018,
+ 0x8c434448, 0xac830018, 0x96020008, 0x3c030800, 0x946458ce, 0x8f850018,
+ 0x00021400, 0x00441025, 0x24040001, 0x0e0014cc, 0xaca2001c, 0x8fbf0014,
+ 0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c020800, 0x8c430020, 0x27bdffe8,
+ 0xafb00010, 0x27500100, 0x10600020, 0xafbf0014, 0x0e00148e, 0x00000000,
+ 0x8f820018, 0xac400000, 0x8f830018, 0xac600004, 0x8f820018, 0xac400008,
+ 0x8f830018, 0xac60000c, 0x9602000c, 0x9603000e, 0x8f840018, 0x00021400,
+ 0x00431025, 0xac820010, 0x8f830018, 0x3c026000, 0xac600014, 0x8f840018,
+ 0x8c434448, 0xac830018, 0x96020008, 0x3c030800, 0x946458ce, 0x8f850018,
+ 0x00021400, 0x00441025, 0x24040001, 0x0e0014cc, 0xaca2001c, 0x8fbf0014,
+ 0x8fb00010, 0x03e00008, 0x27bd0018, 0x27bdffe8, 0xafb00010, 0x27500100,
+ 0xafbf0014, 0x9602000c, 0x10400024, 0x00802821, 0x3c020800, 0x8c430020,
+ 0x1060003a, 0x8fbf0014, 0x0e00148e, 0x00000000, 0x8f840018, 0x8e030000,
+ 0xac830000, 0x9602000c, 0x8f840018, 0x00021400, 0xac820004, 0x8f830018,
+ 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018,
+ 0xac400014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800,
+ 0xaca30018, 0x944358ce, 0x8f850018, 0x3c02400b, 0x00621825, 0x0e0014cc,
+ 0xaca3001c, 0x0a0011ff, 0x8fbf0014, 0x93620005, 0x30420010, 0x14400015,
+ 0x3c029000, 0x34420001, 0x00a21025, 0xaf420020, 0x3c038000, 0x8f420020,
+ 0x00431024, 0x1440fffd, 0x00000000, 0x3c038000, 0x93620005, 0x34630001,
+ 0x00a02021, 0x00a31825, 0x24055852, 0x34420010, 0xa3620005, 0x0e000766,
+ 0xaf430020, 0x0a0011ff, 0x8fbf0014, 0x0000000d, 0x8fbf0014, 0x8fb00010,
+ 0x03e00008, 0x27bd0018, 0x3c020800, 0x8c430020, 0x27bdffe8, 0xafb00010,
+ 0x27500100, 0x10600022, 0xafbf0014, 0x0e00148e, 0x00000000, 0x8f840018,
+ 0x8e020004, 0xac820000, 0x9603000c, 0x9762002c, 0x8f840018, 0x00031c00,
+ 0x00431025, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c,
+ 0x8f830018, 0xac600010, 0x8f820018, 0xac400014, 0x8f850018, 0x3c026000,
+ 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018,
+ 0x3c02400e, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x0e00122e, 0x8e040000,
+ 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c038000, 0x8f420278,
+ 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf440240, 0xa3420244,
+ 0x03e00008, 0xaf430278, 0x3c020800, 0x8c430020, 0x27bdffe0, 0xafb10014,
+ 0x00808821, 0xafb20018, 0x00c09021, 0xafb00010, 0x30b0ffff, 0x1060001c,
+ 0xafbf001c, 0x0e00148e, 0x00000000, 0x8f820018, 0xac510000, 0x8f840018,
+ 0x00101400, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c,
+ 0x8f830018, 0xac600010, 0x8f820018, 0xac520014, 0x8f840018, 0x3c026000,
+ 0x8c434448, 0x3c020800, 0xac830018, 0x944358ce, 0x8f840018, 0x3c024019,
+ 0x00621825, 0xac83001c, 0x0e0014cc, 0x24040001, 0x8fbf001c, 0x8fb20018,
+ 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0x27450100,
+ 0xafbf0010, 0x94a3000c, 0x240200c1, 0x14620031, 0x00803021, 0x3c029000,
+ 0x34420001, 0x00c21025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024,
+ 0x1440fffd, 0x3c028000, 0x34420001, 0x3c049000, 0x34840001, 0x3c058000,
+ 0x24030012, 0x00c21025, 0x00c42025, 0xa363003f, 0xaf420020, 0xaf440020,
+ 0x8f420020, 0x00451024, 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000,
+ 0x34420020, 0xa362007d, 0x8f640074, 0x34630001, 0x00c31825, 0xaf430020,
+ 0x04810006, 0x3c038000, 0x00c02021, 0x0e000470, 0x24050906, 0x0a0012a1,
+ 0x8fbf0010, 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000,
+ 0xaf4601c0, 0xa34201c4, 0xaf4301f8, 0x0a0012a1, 0x8fbf0010, 0x00c02021,
+ 0x94a5000c, 0x24060001, 0x0e000fb1, 0x2407090e, 0x8fbf0010, 0x03e00008,
+ 0x27bd0018, 0x3c020800, 0x8c430020, 0x27bdffe0, 0xafb00010, 0x00808021,
+ 0xafb20018, 0x00a09021, 0xafb10014, 0x30d100ff, 0x1060001c, 0xafbf001c,
+ 0x0e00148e, 0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x24020001,
+ 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018,
+ 0xac600010, 0x8f820018, 0xac520014, 0x8f840018, 0x3c026000, 0x8c434448,
+ 0x3c020800, 0xac830018, 0x944358ce, 0x8f840018, 0x3c024010, 0x00621825,
+ 0xac83001c, 0x0e0014cc, 0x02202021, 0x8fbf001c, 0x8fb20018, 0x8fb10014,
+ 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0xafbf0014, 0xafb00010,
+ 0x93620005, 0x30420001, 0x10400036, 0x00808021, 0x3c029000, 0x34420001,
+ 0x02021025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd,
+ 0x00000000, 0x93620023, 0x34420004, 0xa3620023, 0x93630005, 0x3c048000,
+ 0x3c020800, 0x306300fe, 0xa3630005, 0x8c430020, 0x34840001, 0x02042025,
+ 0xaf440020, 0x10600020, 0x8fbf0014, 0x0e00148e, 0x00000000, 0x8f820018,
+ 0xac500000, 0x93630082, 0x9362003f, 0x8f840018, 0x00031a00, 0x00431025,
+ 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018,
+ 0xac600010, 0x8f820018, 0xac400014, 0x8f840018, 0x3c026000, 0x8c434448,
+ 0x3c020800, 0xac830018, 0x944358ce, 0x8f840018, 0x3c02400a, 0x00621825,
+ 0xac83001c, 0x0e0014cc, 0x24040001, 0x8fbf0014, 0x8fb00010, 0x03e00008,
+ 0x27bd0018, 0x3c020800, 0x8c430020, 0x27bdffe0, 0xafb10014, 0x00808821,
+ 0xafb20018, 0x00a09021, 0xafb00010, 0x30d000ff, 0x1060002f, 0xafbf001c,
+ 0x0e00148e, 0x00000000, 0x8f820018, 0xac510000, 0x8f830018, 0xac700004,
+ 0x8f820018, 0xac520008, 0x8f830018, 0xac60000c, 0x8f820018, 0xac400010,
+ 0x9763006a, 0x00032880, 0x50a00001, 0x24050001, 0x97630068, 0x93640081,
+ 0x3c020800, 0x8c46004c, 0x00652821, 0x00852804, 0x00c5102b, 0x54400001,
+ 0x00a03021, 0x3c020800, 0x8c440050, 0x00c4182b, 0x54600001, 0x00c02021,
+ 0x8f830018, 0x2402fffe, 0x00822824, 0x3c026000, 0xac650014, 0x8f840018,
+ 0x8c434448, 0x3c020800, 0xac830018, 0x944358ce, 0x8f840018, 0x3c024011,
+ 0x00621825, 0xac83001c, 0x0e0014cc, 0x24040001, 0x8fbf001c, 0x8fb20018,
+ 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0xafbf0014,
+ 0xafb00010, 0x8f440100, 0x27500100, 0x8f650050, 0x0e0010fc, 0x9206001b,
+ 0x3c020800, 0x8c430020, 0x1060001d, 0x8e100018, 0x0e00148e, 0x00000000,
+ 0x8f840018, 0x8f420100, 0xac820000, 0x8f830018, 0xac700004, 0x8f840018,
+ 0x8f620050, 0xac820008, 0x8f830018, 0xac60000c, 0x8f820018, 0xac400010,
+ 0x8f830018, 0x3c026000, 0xac600014, 0x8f850018, 0x8c434448, 0x24040001,
+ 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c02401c, 0x00621825,
+ 0x0e0014cc, 0xaca3001c, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018,
+ 0x8f430238, 0x3c020800, 0x04610013, 0x8c44009c, 0x2406fffe, 0x3c050800,
+ 0x3c038000, 0x2484ffff, 0x14800009, 0x00000000, 0x97420078, 0x8ca3007c,
+ 0x24420001, 0x00461024, 0x24630001, 0xa7620010, 0x03e00008, 0xaca3007c,
+ 0x8f420238, 0x00431024, 0x1440fff3, 0x2484ffff, 0x8f420140, 0x3c031000,
+ 0xaf420200, 0x03e00008, 0xaf430238, 0x27bdffe8, 0x3c029000, 0xafbf0010,
+ 0x8f450140, 0x34420001, 0x3c038000, 0x00a21025, 0xaf420020, 0x8f420020,
+ 0x00431024, 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, 0x34420001,
+ 0xa362007d, 0x8f640074, 0x34630001, 0x00a31825, 0xaf430020, 0x04810006,
+ 0x3c038000, 0x00a02021, 0x0e000470, 0x24050ac7, 0x0a0013b9, 0x8fbf0010,
+ 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4501c0,
+ 0xa34201c4, 0xaf4301f8, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x0000000d,
+ 0x03e00008, 0x00000000, 0x0000000d, 0x03e00008, 0x00000000, 0x24020001,
+ 0x03e00008, 0xa7620010, 0x9362003f, 0x304400ff, 0x3883000e, 0x2c630001,
+ 0x38820010, 0x2c420001, 0x00621825, 0x14600003, 0x24020012, 0x14820003,
+ 0x00000000, 0x03e00008, 0x00001021, 0x9363007e, 0x9362007a, 0x14620006,
+ 0x00000000, 0x9363007e, 0x24020001, 0x24630001, 0x03e00008, 0xa363007e,
+ 0x9362007e, 0x8f630178, 0x304200ff, 0x14430006, 0x00000000, 0x9363000b,
+ 0x24020001, 0x24630001, 0x03e00008, 0xa363000b, 0x03e00008, 0x00001021,
+ 0x9362000b, 0x10400023, 0x00001021, 0xa360000b, 0x9362003f, 0x304400ff,
+ 0x3883000e, 0x2c630001, 0x38820010, 0x2c420001, 0x00621825, 0x14600017,
+ 0x00001821, 0x24020012, 0x10820014, 0x00000000, 0x9363007e, 0x9362007a,
+ 0x14620007, 0x00000000, 0x9362007e, 0x24030001, 0x24420001, 0xa362007e,
+ 0x03e00008, 0x00601021, 0x9362007e, 0x8f630178, 0x304200ff, 0x14430005,
+ 0x00001821, 0x9362000b, 0x24030001, 0x24420001, 0xa362000b, 0x03e00008,
+ 0x00601021, 0x03e00008, 0x00000000, 0x24040001, 0xaf64000c, 0x8f6300dc,
+ 0x8f6200cc, 0x50620001, 0xa7640010, 0xa7640012, 0xa7640014, 0x03e00008,
+ 0xa7640016, 0x3c020800, 0x8c430020, 0x27bdffe8, 0x1060001b, 0xafbf0010,
+ 0x0e00148e, 0x00000000, 0x8f820018, 0xac400000, 0x8f830018, 0xac600004,
+ 0x8f820018, 0xac400008, 0x8f830018, 0xac60000c, 0x8f820018, 0xac400010,
+ 0x8f830018, 0x3c026000, 0xac600014, 0x8f840018, 0x8c434448, 0x3c020800,
+ 0xac830018, 0x944358ce, 0x8f840018, 0x3c024020, 0x00621825, 0xac83001c,
+ 0x0e0014cc, 0x24040001, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x3c020800,
+ 0x8c430020, 0x27bdffe0, 0xafb00010, 0x00a08021, 0xafb10014, 0x00c08821,
+ 0xafb20018, 0x00e09021, 0x1060001e, 0xafbf001c, 0x0e00148e, 0x00000000,
+ 0x8f840018, 0x8f420100, 0xac820000, 0x8f830018, 0xac700004, 0x8f820018,
+ 0xac510008, 0x8f830018, 0xac72000c, 0x8f840018, 0x8fa20030, 0xac820010,
+ 0x8f830018, 0x8fa20034, 0xac620014, 0x8f840018, 0x3c026000, 0x8c434448,
+ 0x3c020800, 0xac830018, 0x944358ce, 0x8f840018, 0x3c0240c9, 0x00621825,
+ 0xac83001c, 0x0e0014cc, 0x24040001, 0x8fbf001c, 0x8fb20018, 0x8fb10014,
+ 0x8fb00010, 0x03e00008, 0x27bd0020, 0x3c020800, 0x8c430020, 0x27bdffe8,
+ 0xafb00010, 0x27500100, 0x1060001d, 0xafbf0014, 0x0e00148e, 0x00000000,
+ 0x8f830018, 0x8e020004, 0xac620000, 0x8f840018, 0x8e020018, 0xac820004,
+ 0x8f850018, 0x8e020000, 0xaca20008, 0x8f830018, 0xac60000c, 0x8f820018,
+ 0xac400010, 0x8f830018, 0xac600014, 0x8f820018, 0xac400018, 0x96030008,
+ 0x3c020800, 0x944458ce, 0x8f850018, 0x00031c00, 0x00641825, 0x24040001,
+ 0x0e0014cc, 0xaca3001c, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018,
+ 0x3c060800, 0x24c558c0, 0x3c02000a, 0x03421821, 0x94640006, 0x94a2000a,
+ 0x00441023, 0x00021400, 0x00021c03, 0x04610006, 0xa4a40006, 0x0000000d,
+ 0x00000000, 0x2400005a, 0x0a0014a3, 0x24020001, 0x8f820014, 0x0062102b,
+ 0x14400002, 0x00001021, 0x24020001, 0x304200ff, 0x1040001c, 0x274a0400,
+ 0x3c07000a, 0x3c020800, 0x244558c0, 0x94a9000a, 0x8f880014, 0x03471021,
+ 0x94430006, 0x00402021, 0xa4a30006, 0x94820006, 0xa4a20006, 0x01221023,
+ 0x00021400, 0x00021403, 0x04410006, 0x0048102b, 0x0000000d, 0x00000000,
+ 0x2400005a, 0x0a0014be, 0x24020001, 0x14400002, 0x00001021, 0x24020001,
+ 0x304200ff, 0x1440ffec, 0x03471021, 0x24c458c0, 0x8c820010, 0xaf420038,
+ 0x8c830014, 0x3c020005, 0xaf43003c, 0xaf420030, 0xaf800010, 0xaf8a0018,
+ 0x03e00008, 0x00000000, 0x27bdffe0, 0x8f820010, 0x8f850018, 0x3c070800,
+ 0x24e858c0, 0xafbf001c, 0xafb20018, 0xafb10014, 0xafb00010, 0x9503000a,
+ 0x8d060014, 0x00009021, 0x309000ff, 0x00e08821, 0x24420001, 0x24a50020,
+ 0x24630001, 0xaf820010, 0xaf850018, 0xa503000a, 0x24c30020, 0x3c028000,
+ 0x04c10007, 0xad030014, 0x00621024, 0x14400005, 0x262258c0, 0x8d020010,
+ 0x24420001, 0xad020010, 0x262258c0, 0x9444000a, 0x94450018, 0x0010102b,
+ 0x00a41826, 0x2c630001, 0x00621825, 0x1060001c, 0x3c030006, 0x8f820010,
+ 0x24120001, 0x00021140, 0x00431025, 0xaf420030, 0x00000000, 0x00000000,
+ 0x00000000, 0x27450400, 0x8f420000, 0x30420010, 0x1040fffd, 0x262258c0,
+ 0x9444000a, 0x94430018, 0xaf800010, 0xaf850018, 0x14830012, 0x262758c0,
+ 0x0e00155a, 0x00000000, 0x1600000e, 0x262758c0, 0x0e00148e, 0x00000000,
+ 0x0a001517, 0x262758c0, 0x00041c00, 0x00031c03, 0x00051400, 0x00021403,
+ 0x00621823, 0x18600002, 0x3c026000, 0xac400808, 0x262758c0, 0x94e2000e,
+ 0x94e3000c, 0x24420001, 0xa4e2000e, 0x3042ffff, 0x50430001, 0xa4e0000e,
+ 0x12000005, 0x3c02000a, 0x94e2000a, 0xa74200a2, 0x0a001554, 0x02401021,
+ 0x03421821, 0x94640006, 0x94e2000a, 0x00441023, 0x00021400, 0x00021c03,
+ 0x04610006, 0xa4e40006, 0x0000000d, 0x00000000, 0x2400005a, 0x0a001536,
+ 0x24020001, 0x8f820014, 0x0062102b, 0x14400002, 0x00001021, 0x24020001,
+ 0x304200ff, 0x1040001b, 0x3c020800, 0x3c06000a, 0x244558c0, 0x94a8000a,
+ 0x8f870014, 0x03461021, 0x94430006, 0x00402021, 0xa4a30006, 0x94820006,
+ 0xa4a20006, 0x01021023, 0x00021400, 0x00021403, 0x04410006, 0x0047102b,
+ 0x0000000d, 0x00000000, 0x2400005a, 0x0a001550, 0x24020001, 0x14400002,
+ 0x00001021, 0x24020001, 0x304200ff, 0x1440ffec, 0x03461021, 0x02401021,
+ 0x8fbf001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020,
+ 0x3c020800, 0x244558c0, 0x94a3001a, 0x8ca40024, 0x00403021, 0x000318c0,
+ 0x00832021, 0xaf44003c, 0x8ca20020, 0xaf420038, 0x3c020050, 0x34420008,
+ 0xaf420030, 0x00000000, 0x00000000, 0x00000000, 0x8f420000, 0x30420020,
+ 0x1040fffd, 0x00000000, 0x8f430400, 0x24c658c0, 0xacc30010, 0x8f420404,
+ 0x3c030020, 0xacc20014, 0xaf430030, 0x94c40018, 0x94c3001c, 0x94c2001a,
+ 0x94c5001e, 0x00832021, 0x24420001, 0xa4c2001a, 0x3042ffff, 0x14450002,
+ 0xa4c40018, 0xa4c0001a, 0x03e00008, 0x00000000, 0x8f820010, 0x3c030006,
+ 0x00021140, 0x00431025, 0xaf420030, 0x00000000, 0x00000000, 0x00000000,
+ 0x27430400, 0x8f420000, 0x30420010, 0x1040fffd, 0x00000000, 0xaf800010,
+ 0xaf830018, 0x03e00008, 0x00000000, 0x27bdffe8, 0xafb00010, 0x3c100800,
+ 0x261058c0, 0x3c05000a, 0x02002021, 0x03452821, 0xafbf0014, 0x0e0015b0,
+ 0x2406000a, 0x96020002, 0x9603001e, 0x3042000f, 0x24420003, 0x00431804,
+ 0x24027fff, 0x0043102b, 0xaf830014, 0x10400004, 0x00000000, 0x0000000d,
+ 0x00000000, 0x24000043, 0x0e00155a, 0x00000000, 0x8fbf0014, 0x8fb00010,
+ 0x03e00008, 0x27bd0018, 0x10c00007, 0x00000000, 0x8ca20000, 0x24c6ffff,
+ 0x24a50004, 0xac820000, 0x14c0fffb, 0x24840004, 0x03e00008, 0x00000000,
+ 0x0a0015c1, 0x00a01021, 0xac860000, 0x00000000, 0x00000000, 0x24840004,
+ 0x00a01021, 0x1440fffa, 0x24a5ffff, 0x03e00008, 0x00000000, 0x3c036000,
+ 0x8c642b7c, 0x3c036010, 0x8c6553fc, 0x00041582, 0x00042302, 0x308403ff,
+ 0x00052d82, 0x00441026, 0x0002102b, 0x0005282b, 0x00451025, 0x1440000d,
+ 0x3c020050, 0x34420004, 0xaf400038, 0xaf40003c, 0xaf420030, 0x00000000,
+ 0x00000000, 0x8f420000, 0x30420020, 0x1040fffd, 0x3c020020, 0xaf420030,
+ 0x0000000d, 0x03e00008, 0x00000000, 0x3c020050, 0x34420004, 0xaf440038,
+ 0xaf45003c, 0xaf420030, 0x00000000, 0x00000000, 0x8f420000, 0x30420020,
+ 0x1040fffd, 0x3c020020, 0xaf420030, 0x03e00008, 0x00000000, 0x00000000};
+
+static u32 bnx2_COM_b06FwData[(0x0/4) + 1] = { 0x0 };
+static u32 bnx2_COM_b06FwRodata[(0x58/4) + 1] = {
+ 0x08002428, 0x0800245c, 0x0800245c, 0x0800245c, 0x0800245c, 0x0800245c,
+ 0x08002380, 0x0800245c, 0x080023e4, 0x0800245c, 0x0800231c, 0x0800245c,
+ 0x0800245c, 0x0800245c, 0x08002328, 0x00000000, 0x08003240, 0x08003270,
+ 0x080032a0, 0x080032d0, 0x08003300, 0x00000000, 0x00000000 };
+static u32 bnx2_COM_b06FwBss[(0x88/4) + 1] = { 0x0 };
+static u32 bnx2_COM_b06FwSbss[(0x1c/4) + 1] = { 0x0 };
+
+static int bnx2_RXP_b06FwReleaseMajor = 0x1;
+static int bnx2_RXP_b06FwReleaseMinor = 0x0;
+static int bnx2_RXP_b06FwReleaseFix = 0x0;
+static u32 bnx2_RXP_b06FwStartAddr = 0x08003184;
+static u32 bnx2_RXP_b06FwTextAddr = 0x08000000;
+static int bnx2_RXP_b06FwTextLen = 0x588c;
+static u32 bnx2_RXP_b06FwDataAddr = 0x080058e0;
+static int bnx2_RXP_b06FwDataLen = 0x0;
+static u32 bnx2_RXP_b06FwRodataAddr = 0x08005890;
+static int bnx2_RXP_b06FwRodataLen = 0x28;
+static u32 bnx2_RXP_b06FwBssAddr = 0x08005900;
+static int bnx2_RXP_b06FwBssLen = 0x13a4;
+static u32 bnx2_RXP_b06FwSbssAddr = 0x080058e0;
+static int bnx2_RXP_b06FwSbssLen = 0x1c;
+static u32 bnx2_RXP_b06FwText[(0x588c/4) + 1] = {
+ 0x0a000c61, 0x00000000, 0x00000000, 0x0000000d, 0x72787020, 0x322e362e,
+ 0x31000000, 0x02060103, 0x00000000, 0x0000000d, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d,
+ 0x3c020800, 0x244258e0, 0x3c030800, 0x24636ca4, 0xac400000, 0x0043202b,
+ 0x1480fffd, 0x24420004, 0x3c1d0800, 0x37bd7ffc, 0x03a0f021, 0x3c100800,
+ 0x26103184, 0x3c1c0800, 0x279c58e0, 0x0e00104a, 0x00000000, 0x0000000d,
+ 0x27bdffe8, 0xafb00010, 0xafbf0014, 0x0e000f1d, 0x00808021, 0x1440000d,
+ 0x00000000, 0x8f820010, 0x10400005, 0x00000000, 0x9743011c, 0x9742011e,
+ 0x0a000c89, 0x00021400, 0x9743011e, 0x9742011c, 0x00021400, 0x00621825,
+ 0xaf830004, 0x8f840008, 0x3c020020, 0x34424000, 0x00821824, 0x54620004,
+ 0x3c020020, 0x8f820014, 0x0a000c9a, 0x34421000, 0x34428000, 0x00821824,
+ 0x14620004, 0x00000000, 0x8f820014, 0x34428000, 0xaf820014, 0x8f820008,
+ 0x9743010c, 0x00403021, 0x30421000, 0x10400010, 0x3069ffff, 0x30c20020,
+ 0x1440000e, 0x24070005, 0x3c021000, 0x00c21024, 0x10400009, 0x3c030dff,
+ 0x3463ffff, 0x3c020e00, 0x00c21024, 0x0062182b, 0x50600004, 0x24070001,
+ 0x0a000cb2, 0x3c020800, 0x24070001, 0x3c020800, 0x8c430034, 0x1460001d,
+ 0x00405821, 0x8f820014, 0x30424000, 0x1440001a, 0x3c020001, 0x3c021f01,
+ 0x00c24024, 0x3c031000, 0x15030015, 0x3c020001, 0x31220200, 0x14400012,
+ 0x3c020001, 0x9744010e, 0x24020003, 0xa342018b, 0x97850016, 0x24020002,
+ 0x34e30002, 0xaf400180, 0xa742018c, 0xa7430188, 0x24840004, 0x30a5bfff,
+ 0xa744018e, 0xa74501a6, 0xaf4801b8, 0x0a000f19, 0x00001021, 0x3c020001,
+ 0x00c21024, 0x1040002f, 0x00000000, 0x9742010e, 0x3c038000, 0x3046ffff,
+ 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b, 0x9784000a,
+ 0x8f850004, 0x8f870014, 0x24020080, 0x24030002, 0xaf420180, 0x24020003,
+ 0xa743018c, 0xa746018e, 0xa7420188, 0x30e28000, 0xa7440190, 0x1040000c,
+ 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021, 0x24424004, 0x8c430000,
+ 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, 0x00e21024, 0xaf820014,
+ 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff,
+ 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x0a000f19,
+ 0x00001021, 0x8f820014, 0x30434000, 0x10600016, 0x00404021, 0x3c020f00,
+ 0x00c21024, 0x14400012, 0x00000000, 0x93420116, 0x34424000, 0x03421821,
+ 0x94650002, 0x2ca21389, 0x1040000b, 0x3c020800, 0x24425900, 0x00051942,
+ 0x00031880, 0x00621821, 0x30a5001f, 0x8c640000, 0x24020001, 0x00a21004,
+ 0x00822024, 0x02048025, 0x12000030, 0x3c021000, 0x9742010e, 0x34e80002,
+ 0x3c038000, 0x24420004, 0x3046ffff, 0x8f4201b8, 0x00431024, 0x1440fffd,
+ 0x24020003, 0xa342018b, 0x9784000a, 0x8f850004, 0x8f870014, 0x24020180,
+ 0x24030002, 0xaf420180, 0xa743018c, 0xa746018e, 0xa7480188, 0x30e28000,
+ 0xa7440190, 0x1040000c, 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021,
+ 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff,
+ 0x00e21024, 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff,
+ 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000,
+ 0xaf4201b8, 0x0a000f19, 0x00001021, 0x00c21024, 0x104000c0, 0x3c020800,
+ 0x8c430030, 0x10600037, 0x31024000, 0x10400035, 0x3c030f00, 0x00c31824,
+ 0x3c020100, 0x0043102b, 0x14400031, 0x3c030800, 0x9742010e, 0x34e80002,
+ 0x3c038000, 0x24420004, 0x3046ffff, 0x8f4201b8, 0x00431024, 0x1440fffd,
+ 0x24020003, 0xa342018b, 0x9784000a, 0x8f850004, 0x8f870014, 0x24020080,
+ 0x24030002, 0xaf420180, 0xa743018c, 0xa746018e, 0xa7480188, 0x30e28000,
+ 0xa7440190, 0x1040000c, 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021,
+ 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff,
+ 0x00e21024, 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff,
+ 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000,
+ 0xaf4201b8, 0x0a000f19, 0x00001021, 0x3c030800, 0x8c620024, 0x30420008,
+ 0x10400035, 0x34ea0002, 0x3c020f00, 0x00c21024, 0x14400032, 0x8d620034,
+ 0x31220200, 0x1040002f, 0x8d620034, 0x9742010e, 0x30e8fffb, 0x3c038000,
+ 0x24420004, 0x3046ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003,
+ 0xa342018b, 0x9784000a, 0x8f850004, 0x8f870014, 0x24020180, 0x24030002,
+ 0xaf420180, 0xa743018c, 0xa746018e, 0xa7480188, 0x30e28000, 0xa7440190,
+ 0x1040000c, 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021, 0x24424004,
+ 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, 0x00e21024,
+ 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00,
+ 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8,
+ 0x8d620034, 0x8f860008, 0x10400012, 0x30c20100, 0x10400010, 0x3c020f00,
+ 0x00c21024, 0x3c030200, 0x1043000c, 0x3c020800, 0x8c430038, 0x8f840004,
+ 0x3c020800, 0x2442003c, 0x2463ffff, 0x00832024, 0x00822021, 0x90830000,
+ 0x24630004, 0x0a000de1, 0x000329c0, 0x00000000, 0x00061602, 0x3042000f,
+ 0x000229c0, 0x3c04fc00, 0x00441021, 0x3c030300, 0x0062182b, 0x50600001,
+ 0x24050800, 0x9742010e, 0x3148ffff, 0x3c038000, 0x24420004, 0x3046ffff,
+ 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b, 0x9783000a,
+ 0x8f840004, 0x8f870014, 0x24020002, 0xaf450180, 0xa742018c, 0xa746018e,
+ 0xa7480188, 0x30e28000, 0xa7430190, 0x1040000c, 0xaf4401a8, 0x93420116,
+ 0x304200fc, 0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004,
+ 0x3c02ffff, 0x34427fff, 0x00e21024, 0xaf820014, 0x97820016, 0x9743010c,
+ 0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6,
+ 0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x0a000f19, 0x00001021, 0x8f424000,
+ 0x30420100, 0x104000d5, 0x3c020800, 0x8c440024, 0x24030001, 0x1483002f,
+ 0x00405021, 0x9742010e, 0x34e70002, 0x3c038000, 0x24420004, 0x3045ffff,
+ 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b, 0x9783000a,
+ 0x8f840004, 0x8f860014, 0x24020002, 0xaf400180, 0xa742018c, 0xa745018e,
+ 0xa7470188, 0x30c28000, 0xa7430190, 0x1040000c, 0xaf4401a8, 0x93420116,
+ 0x304200fc, 0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004,
+ 0x3c02ffff, 0x34427fff, 0x00c21024, 0xaf820014, 0x97820016, 0x9743010c,
+ 0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6,
+ 0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x0a000f19, 0x00001021, 0x30820001,
+ 0x1040002e, 0x30eb0004, 0x9742010e, 0x30e9fffb, 0x3c038000, 0x24420004,
+ 0x3045ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b,
+ 0x9783000a, 0x8f840004, 0x8f860014, 0x24020002, 0xaf400180, 0xa742018c,
+ 0xa745018e, 0xa7470188, 0x30c28000, 0xa7430190, 0x1040000c, 0xaf4401a8,
+ 0x93420116, 0x304200fc, 0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff,
+ 0x14600004, 0x3c02ffff, 0x34427fff, 0x00c21024, 0xaf820014, 0x97820016,
+ 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825,
+ 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x3127ffff, 0x8d420024,
+ 0x30420004, 0x10400030, 0x8d420024, 0x9742010e, 0x30e9fffb, 0x3c038000,
+ 0x24420004, 0x3046ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003,
+ 0xa342018b, 0x9784000a, 0x8f850004, 0x8f880014, 0x24020100, 0x24030002,
+ 0xaf420180, 0xa743018c, 0xa746018e, 0xa7470188, 0x31028000, 0xa7440190,
+ 0x1040000c, 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021, 0x24424004,
+ 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, 0x01021024,
+ 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00,
+ 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8,
+ 0x3127ffff, 0x8d420024, 0x30420008, 0x1040002d, 0x00000000, 0x9742010e,
+ 0x3c038000, 0x24420004, 0x3046ffff, 0x8f4201b8, 0x00431024, 0x1440fffd,
+ 0x24020003, 0xa342018b, 0x9784000a, 0x8f850004, 0x8f880014, 0x24020180,
+ 0x24030002, 0xaf420180, 0xa743018c, 0xa746018e, 0xa7470188, 0x31028000,
+ 0xa7440190, 0x1040000c, 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021,
+ 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff,
+ 0x01021024, 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff,
+ 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000,
+ 0xaf4201b8, 0x15600041, 0x00001021, 0x27440180, 0x3c038000, 0x8f4201b8,
+ 0x00431024, 0x1440fffd, 0x24022000, 0x24030002, 0xa4820008, 0xa083000b,
+ 0xa4800010, 0x3c021000, 0xaf4201b8, 0x0a000f19, 0x00001021, 0x3c030800,
+ 0x8c620024, 0x30420001, 0x1040002e, 0x00001021, 0x9742010e, 0x34e70002,
+ 0x3c038000, 0x24420004, 0x3045ffff, 0x8f4201b8, 0x00431024, 0x1440fffd,
+ 0x24020003, 0xa342018b, 0x9783000a, 0x8f840004, 0x8f860014, 0x24020002,
+ 0xaf400180, 0xa742018c, 0xa745018e, 0xa7470188, 0x30c28000, 0xa7430190,
+ 0x1040000c, 0xaf4401a8, 0x93420116, 0x304200fc, 0x005a1021, 0x24424004,
+ 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, 0x00c21024,
+ 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00,
+ 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8,
+ 0x00001021, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x8f4b0070,
+ 0x93420112, 0x8f840008, 0x00022882, 0x30820100, 0x14400003, 0x24a30003,
+ 0x03e00008, 0x00001021, 0x30824000, 0x10400010, 0x27424000, 0x00031880,
+ 0x00431021, 0x8c470000, 0x24a30004, 0x00031880, 0x27424000, 0x00431021,
+ 0x8c490000, 0x93430116, 0x27424000, 0x306300fc, 0x00431021, 0x8c4a0000,
+ 0x0a000f45, 0x3c030800, 0x30822000, 0x1040ffea, 0x00031880, 0x27424000,
+ 0x00431021, 0x8c470000, 0x24a30004, 0x00031880, 0x27424000, 0x00431021,
+ 0x8c490000, 0x00005021, 0x3c030800, 0x24680100, 0x00071602, 0x00021080,
+ 0x00481021, 0x8c460000, 0x00071b82, 0x306303fc, 0x01031821, 0x8c640400,
+ 0x00071182, 0x304203fc, 0x01021021, 0x8c450800, 0x30e300ff, 0x00031880,
+ 0x01031821, 0x00091602, 0x00021080, 0x01021021, 0x00c43026, 0x8c640c00,
+ 0x8c431000, 0x00c53026, 0x00091382, 0x304203fc, 0x01021021, 0x8c451400,
+ 0x312200ff, 0x00021080, 0x01021021, 0x00c43026, 0x00c33026, 0x00091982,
+ 0x306303fc, 0x01031821, 0x8c641800, 0x8c431c00, 0x00c53026, 0x00c43026,
+ 0x11400015, 0x00c33026, 0x000a1602, 0x00021080, 0x01021021, 0x8c432000,
+ 0x000a1382, 0x304203fc, 0x01021021, 0x8c452400, 0x314200ff, 0x00021080,
+ 0x01021021, 0x00c33026, 0x000a1982, 0x306303fc, 0x01031821, 0x8c642800,
+ 0x8c432c00, 0x00c53026, 0x00c43026, 0x00c33026, 0x8f430070, 0x3c050800,
+ 0x8ca43100, 0x2c820020, 0x10400008, 0x006b5823, 0x3c020800, 0x24423104,
+ 0x00041880, 0x00621821, 0x24820001, 0xac6b0000, 0xaca23100, 0xaf860004,
+ 0x03e00008, 0x24020001, 0x27bdffe8, 0xafbf0010, 0x8f460128, 0x8f840010,
+ 0xaf460020, 0x8f450104, 0x8f420100, 0x24030800, 0xaf850008, 0xaf820014,
+ 0xaf4301b8, 0x1080000a, 0x3c020800, 0x8c430034, 0x10600007, 0x30a22000,
+ 0x10400005, 0x34a30100, 0x8f82000c, 0xaf830008, 0x24420001, 0xaf82000c,
+ 0x3c020800, 0x8c4300c0, 0x10600006, 0x3c030800, 0x8c6200c4, 0x24040001,
+ 0x24420001, 0x0a000fd5, 0xac6200c4, 0x8f820008, 0x3c030010, 0x00431024,
+ 0x14400009, 0x3c02001f, 0x3c030800, 0x8c620020, 0x00002021, 0x24420001,
+ 0x0e000c78, 0xac620020, 0x0a000fd5, 0x00402021, 0x3442ff00, 0x14c20009,
+ 0x2403bfff, 0x3c030800, 0x8c620020, 0x24040001, 0x24420001, 0x0e000c78,
+ 0xac620020, 0x0a000fd5, 0x00402021, 0x8f820014, 0x00431024, 0x14400006,
+ 0x00000000, 0xaf400048, 0x0e0011a9, 0xaf400040, 0x0a000fd5, 0x00402021,
+ 0x0e001563, 0x00000000, 0x00402021, 0x10800005, 0x3c024000, 0x8f430124,
+ 0x3c026020, 0xac430014, 0x3c024000, 0xaf420138, 0x00000000, 0x8fbf0010,
+ 0x03e00008, 0x27bd0018, 0x27bdffe0, 0xafbf0018, 0xafb10014, 0xafb00010,
+ 0x8f420140, 0xaf420020, 0x8f430148, 0x3c027000, 0x00621824, 0x3c023000,
+ 0x10620021, 0x0043102b, 0x14400006, 0x3c024000, 0x3c022000, 0x10620009,
+ 0x3c024000, 0x0a001040, 0x00000000, 0x10620045, 0x3c025000, 0x10620047,
+ 0x3c024000, 0x0a001040, 0x00000000, 0x27440180, 0x3c038000, 0x8f4201b8,
+ 0x00431024, 0x1440fffd, 0x00000000, 0x8f420148, 0x24030002, 0xa083000b,
+ 0x00021402, 0xa4820008, 0x8f430148, 0xa4830010, 0x8f420144, 0x3c031000,
+ 0xac820024, 0xaf4301b8, 0x0a001040, 0x3c024000, 0x8f420148, 0x24030002,
+ 0x3044ffff, 0x00021402, 0x305000ff, 0x1203000c, 0x27510180, 0x2a020003,
+ 0x10400005, 0x24020003, 0x0600001d, 0x36053000, 0x0a001027, 0x3c038000,
+ 0x12020007, 0x00000000, 0x0a001034, 0x00000000, 0x0e00112c, 0x00000000,
+ 0x0a001025, 0x00402021, 0x0e00113e, 0x00000000, 0x00402021, 0x36053000,
+ 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020002, 0xa6250008,
+ 0xa222000b, 0xa6240010, 0x8f420144, 0x3c031000, 0xae220024, 0xaf4301b8,
+ 0x0a001040, 0x3c024000, 0x0000000d, 0x00000000, 0x240002bf, 0x0a001040,
+ 0x3c024000, 0x0e001441, 0x00000000, 0x0a001040, 0x3c024000, 0x0e0015ea,
+ 0x00000000, 0x3c024000, 0xaf420178, 0x00000000, 0x8fbf0018, 0x8fb10014,
+ 0x8fb00010, 0x03e00008, 0x27bd0020, 0x24020800, 0x03e00008, 0xaf4201b8,
+ 0x27bdffe8, 0x3c04600c, 0xafbf0014, 0xafb00010, 0x8c825000, 0x3c1a8000,
+ 0x2403ff7f, 0x3c106000, 0x00431024, 0x3442380c, 0x24030003, 0xac825000,
+ 0x3c020008, 0xaf430008, 0x8e040808, 0x0342d825, 0x8e020808, 0x3c030800,
+ 0xac600020, 0x3084fff0, 0x2c840001, 0x3042fff0, 0x38420010, 0x2c420001,
+ 0xaf840010, 0xaf820000, 0x0e00160c, 0x00000000, 0x0e001561, 0x00000000,
+ 0x3c020400, 0x3442000c, 0x3c03ffff, 0x34630806, 0xae021948, 0xae03194c,
+ 0x8e021980, 0x34420200, 0xae021980, 0x8f500000, 0x32020003, 0x1040fffd,
+ 0x32020001, 0x10400004, 0x32020002, 0x0e000f92, 0x00000000, 0x32020002,
+ 0x1040fff6, 0x00000000, 0x0e000fe0, 0x00000000, 0x0a001071, 0x00000000,
+ 0x27bdffe8, 0x3c04600c, 0xafbf0014, 0xafb00010, 0x8c825000, 0x3c1a8000,
+ 0x2403ff7f, 0x3c106000, 0x00431024, 0x3442380c, 0x24030003, 0xac825000,
+ 0x3c020008, 0xaf430008, 0x8e040808, 0x0342d825, 0x8e020808, 0x3c030800,
+ 0xac600020, 0x3084fff0, 0x2c840001, 0x3042fff0, 0x38420010, 0x2c420001,
+ 0xaf840010, 0xaf820000, 0x0e00160c, 0x00000000, 0x0e001561, 0x00000000,
+ 0x3c020400, 0x3442000c, 0x3c03ffff, 0x34630806, 0xae021948, 0xae03194c,
+ 0x8e021980, 0x8fbf0014, 0x34420200, 0xae021980, 0x8fb00010, 0x03e00008,
+ 0x27bd0018, 0x00804821, 0x30a5ffff, 0x30c6ffff, 0x30e7ffff, 0x3c038000,
+ 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b, 0x9783000a,
+ 0x8f840004, 0x8f880014, 0xaf490180, 0xa745018c, 0xa746018e, 0xa7470188,
+ 0x31028000, 0xa7430190, 0x1040000c, 0xaf4401a8, 0x93420116, 0x304200fc,
+ 0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff,
+ 0x34427fff, 0x01021024, 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104,
+ 0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac,
+ 0x3c021000, 0xaf4201b8, 0x03e00008, 0x00000000, 0x27440180, 0x3c038000,
+ 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24022000, 0x24030002, 0xa4820008,
+ 0xa083000b, 0xa4800010, 0x3c021000, 0xaf4201b8, 0x03e00008, 0x00000000,
+ 0x27440180, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000,
+ 0x8f420148, 0x24030002, 0xa083000b, 0x00021402, 0xa4820008, 0x8f430148,
+ 0xa4830010, 0x8f420144, 0x3c031000, 0xac820024, 0x03e00008, 0xaf4301b8,
+ 0x27bdffe0, 0xafbf0018, 0xafb10014, 0xafb00010, 0x8f420148, 0x24030002,
+ 0x3044ffff, 0x00021402, 0x305000ff, 0x1203000c, 0x27510180, 0x2a020003,
+ 0x10400005, 0x24020003, 0x0600001d, 0x36053000, 0x0a001117, 0x3c038000,
+ 0x12020007, 0x00000000, 0x0a001124, 0x00000000, 0x0e00112c, 0x00000000,
+ 0x0a001115, 0x00402021, 0x0e00113e, 0x00000000, 0x00402021, 0x36053000,
+ 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020002, 0xa6250008,
+ 0xa222000b, 0xa6240010, 0x8f420144, 0x3c031000, 0xae220024, 0xaf4301b8,
+ 0x0a001128, 0x8fbf0018, 0x0000000d, 0x00000000, 0x240002bf, 0x8fbf0018,
+ 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x3084ffff, 0x2c821389,
+ 0x1040000d, 0x00001021, 0x3c030800, 0x24635900, 0x00042942, 0x00052880,
+ 0x00a32821, 0x3086001f, 0x8ca40000, 0x24030001, 0x00c31804, 0x00832025,
+ 0x03e00008, 0xaca40000, 0x03e00008, 0x24020091, 0x3084ffff, 0x2c821389,
+ 0x1040000e, 0x00001021, 0x3c030800, 0x24635900, 0x00042942, 0x00052880,
+ 0x00a32821, 0x3086001f, 0x24030001, 0x8ca40000, 0x00c31804, 0x00031827,
+ 0x00832024, 0x03e00008, 0xaca40000, 0x03e00008, 0x24020091, 0x9482000c,
+ 0x24870014, 0x00021302, 0x00021080, 0x00824021, 0x00e8182b, 0x1060004f,
+ 0x00000000, 0x90e30000, 0x2c620009, 0x10400047, 0x3c020800, 0x24425890,
+ 0x00031880, 0x00621821, 0x8c640000, 0x00800008, 0x00000000, 0x0a0011a4,
+ 0x24e70001, 0x90e30001, 0x2402000a, 0x54620024, 0x01003821, 0x01071023,
+ 0x2c42000a, 0x54400020, 0x01003821, 0x3c050800, 0x8ca26c98, 0x24e70002,
+ 0x34420100, 0xaca26c98, 0x90e30000, 0x90e20001, 0x90e40002, 0x90e60003,
+ 0x24e70004, 0x24a56c98, 0x00031e00, 0x00021400, 0x00621825, 0x00042200,
+ 0x00641825, 0x00661825, 0xaca30004, 0x90e20000, 0x90e30001, 0x90e40002,
+ 0x90e60003, 0x24e70004, 0x00021600, 0x00031c00, 0x00431025, 0x00042200,
+ 0x00441025, 0x00461025, 0x0a0011a4, 0xaca20008, 0x90e30001, 0x24020004,
+ 0x1062000e, 0x00601021, 0x0a00119e, 0x01001021, 0x90e30001, 0x24020003,
+ 0x10620008, 0x00601021, 0x0a00119e, 0x01001021, 0x90e30001, 0x24020002,
+ 0x14620003, 0x01001021, 0x00601021, 0x00e21021, 0x0a0011a4, 0x00403821,
+ 0x90e20001, 0x0a0011a4, 0x00e23821, 0x01003821, 0x00e8102b, 0x5440ffb4,
+ 0x90e30000, 0x03e00008, 0x24020001, 0x27bdff90, 0x3c030800, 0xafbf006c,
+ 0xafbe0068, 0xafb70064, 0xafb60060, 0xafb5005c, 0xafb40058, 0xafb30054,
+ 0xafb20050, 0xafb1004c, 0xafb00048, 0xac606c98, 0x93620023, 0x30420010,
+ 0x1440027c, 0x24020001, 0x93420116, 0x93630005, 0x34424000, 0x30630001,
+ 0x14600005, 0x0342b021, 0x0e0015e0, 0x00000000, 0x0a001436, 0x8fbf006c,
+ 0x93420112, 0x8f430104, 0x3c040020, 0x34424000, 0x00641824, 0x10600012,
+ 0x03422821, 0x27450180, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd,
+ 0x00000000, 0x8f420128, 0xaca20000, 0x8f640040, 0x24030008, 0x240240c1,
+ 0xa4a20008, 0x24020002, 0xa0a2000b, 0x3c021000, 0x0a0011f1, 0xa0a3000a,
+ 0x8f420104, 0x3c030040, 0x00431024, 0x1040001d, 0x3c038000, 0x27450180,
+ 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x8f420128, 0xaca20000,
+ 0x8f640040, 0x24030010, 0x240240c1, 0xa4a20008, 0x24020002, 0xa0a3000a,
+ 0x24030008, 0xa0a2000b, 0x3c021000, 0xa4a30010, 0xa0a00012, 0xa0a00013,
+ 0xaca00014, 0xaca00024, 0xaca00028, 0xaca0002c, 0xaca40018, 0x0e0015e0,
+ 0xaf4201b8, 0x0a001436, 0x8fbf006c, 0x8f820000, 0x10400016, 0x00000000,
+ 0x8f420104, 0x3c030001, 0x00431024, 0x10400011, 0x00000000, 0x8ca3000c,
+ 0x8f620030, 0x1462022d, 0x24020001, 0x8ca30010, 0x8f62002c, 0x14620229,
+ 0x24020001, 0x9763003a, 0x96c20000, 0x14430225, 0x24020001, 0x97630038,
+ 0x96c20002, 0x14430221, 0x24020001, 0xaf400048, 0xaf400054, 0xaf400040,
+ 0x8f740040, 0x8f650048, 0x00b43023, 0x04c10004, 0x00000000, 0x0000000d,
+ 0x00000000, 0x240001af, 0x9742011a, 0x3052ffff, 0x12400004, 0x8ed30004,
+ 0x02721021, 0x0a001228, 0x2451ffff, 0x02608821, 0x92d7000d, 0xa7a00020,
+ 0xa3a0001a, 0xafa00028, 0x9362003f, 0x32e30004, 0x1060003a, 0x305000ff,
+ 0x24040012, 0x16040006, 0x24020001, 0x3c040800, 0x8c830028, 0x24630001,
+ 0x0a001328, 0xac830028, 0x8f620044, 0x16620010, 0x27a60010, 0x27450180,
+ 0x3c038000, 0x2402001a, 0xa7a20020, 0x24020020, 0xafb40028, 0xa3b00022,
+ 0xa3a40023, 0xa3a2001a, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000,
+ 0x0a00130d, 0x00000000, 0x8f620044, 0x02621023, 0x0440001a, 0x02651023,
+ 0x044100d9, 0x24020001, 0x3c020800, 0x8c4300d8, 0x10600004, 0x24020001,
+ 0xa7a20020, 0x0a00125e, 0xafb40028, 0x2402001a, 0xa7a20020, 0x24020020,
+ 0xafb40028, 0xa3b00022, 0xa3a40023, 0xa3a2001a, 0x27a60010, 0x27450180,
+ 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x0a00130d,
+ 0x00000000, 0x0a001328, 0x24020001, 0x0293f023, 0x1bc00016, 0x025e102a,
+ 0x54400007, 0x32f700fe, 0x57d2000f, 0x027e9821, 0x32e20001, 0x5440000c,
+ 0x027e9821, 0x32f700fe, 0x0240f021, 0x3c040800, 0x8c8300c8, 0x00009021,
+ 0x24020001, 0xa7a20020, 0xafb40028, 0x24630001, 0x0a001282, 0xac8300c8,
+ 0x025e1023, 0x0a001282, 0x3052ffff, 0x0000f021, 0x24a2ffff, 0x02221823,
+ 0x1860001f, 0x0072102a, 0x54400019, 0x00a08821, 0x97a20020, 0x3c040800,
+ 0x8c8300cc, 0xafb40028, 0x34420001, 0x24630001, 0xa7a20020, 0x02741026,
+ 0x2c420001, 0xac8300cc, 0x2cc30001, 0x00431024, 0x1440000a, 0x02401821,
+ 0x27a60010, 0x27450180, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd,
+ 0x00000000, 0x0a00130d, 0x00000000, 0x00a08821, 0x02431023, 0x3052ffff,
+ 0x0a0012ae, 0x32f700f6, 0x02741023, 0x18400008, 0x97a20020, 0x3c040800,
+ 0x8c8300d4, 0xafb30028, 0x34420400, 0x24630001, 0xa7a20020, 0xac8300d4,
+ 0x32e20002, 0x1040001c, 0x32e20010, 0x8f620044, 0x1662000d, 0x27a60010,
+ 0x97a20020, 0x27450180, 0x3c038000, 0xafb40028, 0x34420001, 0xa7a20020,
+ 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x0a00130d, 0x00000000,
+ 0x97a20020, 0x27450180, 0x3c038000, 0xafb40028, 0x34420001, 0xa7a20020,
+ 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x0a00130d, 0x00000000,
+ 0x54400003, 0x8ed50008, 0x0a001328, 0x24020001, 0x8f630054, 0x26a2ffff,
+ 0x00431023, 0x18400011, 0x27a60010, 0x97a20020, 0x3c040800, 0x8c8300d0,
+ 0x27450180, 0x3c078000, 0xafb40028, 0x34420001, 0x24630001, 0xa7a20020,
+ 0xac8300d0, 0x8f4201b8, 0x00471024, 0x1440fffd, 0x00000000, 0x0a00130d,
+ 0x00000000, 0x32e20020, 0x10400011, 0x00000000, 0x96c20012, 0x0052102b,
+ 0x10400008, 0x97a20020, 0x96d20012, 0x12400003, 0x02721021, 0x0a0012f2,
+ 0x2451ffff, 0x02608821, 0x97a20020, 0x93a3001a, 0x34420008, 0x34630004,
+ 0xa7a20020, 0xa3a3001a, 0x8f420104, 0x3c030080, 0x00431024, 0x10400037,
+ 0x3a03000a, 0x0e001151, 0x02c02021, 0x24030002, 0x1443002b, 0x3c030800,
+ 0x27a60010, 0x97a20020, 0x27450180, 0x3c038000, 0xafb40028, 0x34420001,
+ 0xa7a20020, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x8f420128,
+ 0xaca20000, 0x8cc30018, 0x240240c1, 0xa4a20008, 0xaca30018, 0x90c4000a,
+ 0x24020002, 0xa0a2000b, 0xa0a4000a, 0x94c20010, 0xa4a20010, 0x90c30012,
+ 0xa0a30012, 0x90c20013, 0xa0a20013, 0x8cc30014, 0xaca30014, 0x8cc20024,
+ 0xaca20024, 0x8cc30028, 0xaca30028, 0x8cc4002c, 0x24020001, 0x3c031000,
+ 0xaca4002c, 0xaf4301b8, 0xaf400044, 0xaf400050, 0x0a001436, 0x8fbf006c,
+ 0x8c626c98, 0x30420100, 0x10400003, 0x24636c98, 0x8c620004, 0xaf62017c,
+ 0x3a03000a, 0x2c630001, 0x3a02000c, 0x2c420001, 0x00621825, 0x14600003,
+ 0x2402000e, 0x56020030, 0x00009021, 0x52400008, 0x96c4000e, 0x12400004,
+ 0xa7b20040, 0x02721021, 0x0a001343, 0x2451ffff, 0x02608821, 0x96c4000e,
+ 0x93630035, 0x8f62004c, 0x00642004, 0x00952021, 0x00821023, 0x18400015,
+ 0x00000000, 0x8f620018, 0x02621023, 0x1c400015, 0x97a20020, 0x8f620018,
+ 0x1662001c, 0x00000000, 0x8f62001c, 0x02a21023, 0x1c40000e, 0x97a20020,
+ 0x8f62001c, 0x16a20015, 0x00000000, 0x8f620058, 0x00821023, 0x18400011,
+ 0x97a20020, 0x0a001364, 0xafb10028, 0x8f620058, 0x00821023, 0x0441000b,
+ 0x97a20020, 0xafb10028, 0xafb30034, 0xafb50038, 0xafa4003c, 0x34420020,
+ 0x0a00136d, 0xa7a20020, 0x02809821, 0x02608821, 0x8f640058, 0x8f62004c,
+ 0x02a21023, 0x18400009, 0x00000000, 0x8f620054, 0x02a21023, 0x1c400005,
+ 0x97a20020, 0xafb10028, 0xafb50024, 0x0a001385, 0x34420040, 0x9742011a,
+ 0x1440000c, 0x24020014, 0x8f620058, 0x14820009, 0x24020014, 0x8f63004c,
+ 0x8f620054, 0x10620004, 0x97a20020, 0xafb10028, 0x34420080, 0xa7a20020,
+ 0x24020014, 0x1202000a, 0x2a020015, 0x10400005, 0x2402000c, 0x12020006,
+ 0x32e20001, 0x0a0013c6, 0x00000000, 0x24020016, 0x16020035, 0x32e20001,
+ 0x8f620084, 0x24420001, 0x16a20031, 0x32e20001, 0x24020014, 0x12020021,
+ 0x2a020015, 0x10400005, 0x2402000c, 0x12020008, 0x32e20001, 0x0a0013c6,
+ 0x00000000, 0x24020016, 0x1202000c, 0x32e20001, 0x0a0013c6, 0x00000000,
+ 0x97a30020, 0x2402000e, 0xafb10028, 0xa3b00022, 0xa3a20023, 0xafb50024,
+ 0x34630054, 0x0a0013c5, 0xa7a30020, 0x97a20020, 0x93a4001a, 0x24030010,
+ 0xafb10028, 0xa3b00022, 0xa3a30023, 0xafb50024, 0x3442005d, 0x34840002,
+ 0xa7a20020, 0x0a0013c5, 0xa3a4001a, 0x97a20020, 0x24030012, 0xa3a30023,
+ 0x93a3001a, 0xafb10028, 0xa3b00022, 0xafb50024, 0x3042fffe, 0x3442005c,
+ 0x34630002, 0xa7a20020, 0xa3a3001a, 0x32e20001, 0x10400030, 0x2402000c,
+ 0x12020013, 0x2a02000d, 0x10400005, 0x2402000a, 0x12020008, 0x97a20020,
+ 0x0a0013f8, 0x32e20009, 0x2402000e, 0x1202001b, 0x32e20009, 0x0a0013f9,
+ 0x0002102b, 0x93a4001a, 0x24030008, 0xafb10028, 0xa3b00022, 0xa3a30023,
+ 0x0a0013f4, 0x34420013, 0x97a30020, 0x30620004, 0x14400005, 0x93a2001a,
+ 0x3463001b, 0xa7a30020, 0x0a0013e7, 0x24030016, 0x3463001b, 0xa7a30020,
+ 0x24030010, 0xafb10028, 0xa3b00022, 0xa3a30023, 0x34420002, 0x0a0013f7,
+ 0xa3a2001a, 0x97a20020, 0x93a4001a, 0x24030010, 0xafb10028, 0xa3b00022,
+ 0xa3a30023, 0x3442001b, 0x34840002, 0xa7a20020, 0xa3a4001a, 0x32e20009,
+ 0x0002102b, 0x00021023, 0x30420007, 0x12400015, 0x34450003, 0x8f820018,
+ 0x24030800, 0x27440180, 0x24420001, 0xaf820018, 0x24020004, 0xaf4301b8,
+ 0xa4850008, 0xa082000b, 0x93430120, 0x00003021, 0x3c021000, 0xa492000e,
+ 0xac950024, 0xac930028, 0x007e1821, 0xa483000c, 0xaf4201b8, 0x0a001413,
+ 0x97a20020, 0x24060001, 0x97a20020, 0x10400020, 0x27450180, 0x3c038000,
+ 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x8f420128, 0xaca20000,
+ 0x8fa30028, 0x240240c1, 0xa4a20008, 0xaca30018, 0x93a4001a, 0x24020002,
+ 0xa0a2000b, 0xa0a4000a, 0x97a20020, 0xa4a20010, 0x93a30022, 0xa0a30012,
+ 0x93a20023, 0xa0a20013, 0x8fa30024, 0xaca30014, 0x8fa20034, 0xaca20024,
+ 0x8fa30038, 0xaca30028, 0x8fa2003c, 0x3c031000, 0xaca2002c, 0xaf4301b8,
+ 0x00c01021, 0x8fbf006c, 0x8fbe0068, 0x8fb70064, 0x8fb60060, 0x8fb5005c,
+ 0x8fb40058, 0x8fb30054, 0x8fb20050, 0x8fb1004c, 0x8fb00048, 0x03e00008,
+ 0x27bd0070, 0x8f470140, 0x8f460148, 0x3c028000, 0x00c24024, 0x00062c02,
+ 0x30a300ff, 0x24020019, 0x106200e7, 0x27440180, 0x2862001a, 0x1040001f,
+ 0x24020008, 0x106200be, 0x28620009, 0x1040000d, 0x24020001, 0x10620046,
+ 0x28620002, 0x50400005, 0x24020006, 0x1060002e, 0x00a01821, 0x0a00155e,
+ 0x00000000, 0x1062005b, 0x00a01821, 0x0a00155e, 0x00000000, 0x2402000b,
+ 0x10620084, 0x2862000c, 0x10400005, 0x24020009, 0x106200bc, 0x00061c02,
+ 0x0a00155e, 0x00000000, 0x2402000e, 0x106200b7, 0x00061c02, 0x0a00155e,
+ 0x00000000, 0x28620021, 0x10400009, 0x2862001f, 0x104000c1, 0x2402001b,
+ 0x106200bf, 0x2402001c, 0x1062009a, 0x00061c02, 0x0a00155e, 0x00000000,
+ 0x240200c2, 0x106200ca, 0x286200c3, 0x10400005, 0x24020080, 0x1062005a,
+ 0x00a01821, 0x0a00155e, 0x00000000, 0x240200c9, 0x106200cd, 0x30c5ffff,
+ 0x0a00155e, 0x00000000, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd,
+ 0x24020001, 0xa4830008, 0x24030002, 0xac870000, 0xac800004, 0xa082000a,
+ 0xa083000b, 0xa4860010, 0x8f430144, 0x3c021000, 0xac800028, 0xac830024,
+ 0x3c036000, 0xaf4201b8, 0x03e00008, 0xac600808, 0x11000009, 0x00a01821,
+ 0x3c020800, 0x24030002, 0xa0436c88, 0x24426c88, 0xac470008, 0x8f430144,
+ 0x03e00008, 0xac430004, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd,
+ 0x24020002, 0xac800000, 0xac870004, 0xa4830008, 0xa082000a, 0xa082000b,
+ 0xa4860010, 0xac800024, 0x8f420144, 0x3c031000, 0xac820028, 0x3c026000,
+ 0xaf4301b8, 0x03e00008, 0xac400808, 0x3c080800, 0x3c058000, 0x8f4201b8,
+ 0x00451024, 0x1440fffd, 0x00000000, 0xac870000, 0x91026c88, 0x00002821,
+ 0x10400002, 0x25076c88, 0x8ce50008, 0xac850004, 0xa4830008, 0x91036c88,
+ 0x24020002, 0xa082000b, 0xa4860010, 0x34630001, 0xa083000a, 0x8f420144,
+ 0xac820024, 0x91036c88, 0x10600002, 0x00001021, 0x8ce20004, 0xac820028,
+ 0x3c021000, 0xaf4201b8, 0x3c026000, 0xa1006c88, 0x03e00008, 0xac400808,
+ 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020002, 0xa082000b,
+ 0xa4830008, 0xa4860010, 0x8f420144, 0x3c031000, 0xa4820012, 0x03e00008,
+ 0xaf4301b8, 0x30c2ffff, 0x14400028, 0x00061c02, 0x93620005, 0x30420004,
+ 0x14400020, 0x3c029000, 0x34420001, 0x00e21025, 0xaf420020, 0x3c038000,
+ 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620005, 0x3c038000,
+ 0x34630001, 0x00e31825, 0x34420004, 0xa3620005, 0xaf430020, 0x93620005,
+ 0x30420004, 0x14400003, 0x3c038000, 0x0000000d, 0x3c038000, 0x8f4201b8,
+ 0x00431024, 0x1440fffd, 0x24020005, 0x3c031000, 0xac870000, 0xa082000b,
+ 0xaf4301b8, 0x0a00150d, 0x00061c02, 0x0000000d, 0x03e00008, 0x00000000,
+ 0x00061c02, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020001,
+ 0xa4830008, 0x24030002, 0xac870000, 0xac800004, 0xa082000a, 0xa083000b,
+ 0xa4860010, 0x8f430144, 0x3c021000, 0xac800028, 0xac830024, 0x03e00008,
+ 0xaf4201b8, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020002,
+ 0xac800000, 0xac870004, 0xa4830008, 0xa082000a, 0xa082000b, 0xa4860010,
+ 0xac800024, 0x8f420144, 0x3c031000, 0xac820028, 0x03e00008, 0xaf4301b8,
+ 0x00061c02, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020001,
+ 0xa4830008, 0x24030002, 0xa082000a, 0x3c021000, 0xac870000, 0xac800004,
+ 0xa083000b, 0xa4860010, 0xac800024, 0xac800028, 0x03e00008, 0xaf4201b8,
+ 0x00a01821, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020002,
+ 0xac870000, 0xac800004, 0xa4830008, 0xa080000a, 0x0a001518, 0xa082000b,
+ 0x8f440144, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020002,
+ 0x240340c9, 0xaf470180, 0xa342018b, 0x3c021000, 0xa7430188, 0xaf4401a4,
+ 0xaf4501a8, 0xaf4001ac, 0x03e00008, 0xaf4201b8, 0x0000000d, 0x03e00008,
+ 0x00000000, 0x03e00008, 0x00000000, 0x8f420100, 0x3042003e, 0x14400011,
+ 0x24020001, 0xaf400048, 0x8f420100, 0x304207c0, 0x10400005, 0x00000000,
+ 0xaf40004c, 0xaf400050, 0x03e00008, 0x24020001, 0xaf400054, 0xaf400040,
+ 0x8f420100, 0x30423800, 0x54400001, 0xaf400044, 0x24020001, 0x03e00008,
+ 0x00000000, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020002,
+ 0x240340c9, 0xaf440180, 0xa342018b, 0x3c021000, 0xa7430188, 0xaf4501a4,
+ 0xaf4601a8, 0xaf4701ac, 0x03e00008, 0xaf4201b8, 0x3c029000, 0x34420001,
+ 0x00822025, 0xaf440020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd,
+ 0x00000000, 0x03e00008, 0x00000000, 0x3c028000, 0x34420001, 0x00822025,
+ 0x03e00008, 0xaf440020, 0x308600ff, 0x27450180, 0x3c038000, 0x8f4201b8,
+ 0x00431024, 0x1440fffd, 0x00000000, 0x8f420128, 0xaca20000, 0x8f640040,
+ 0x24030008, 0x240240c1, 0xa4a20008, 0x24020002, 0xa0a2000b, 0x3c021000,
+ 0xa0a6000a, 0xa4a30010, 0xa0a00012, 0xa0a00013, 0xaca00014, 0xaca00024,
+ 0xaca00028, 0xaca0002c, 0xaca40018, 0x03e00008, 0xaf4201b8, 0x24020001,
+ 0xacc40000, 0x03e00008, 0xa4e50000, 0x24020001, 0xaf400044, 0x03e00008,
+ 0xaf400050, 0x00803021, 0x27450180, 0x3c038000, 0x8f4201b8, 0x00431024,
+ 0x1440fffd, 0x00000000, 0x8f420128, 0xaca20000, 0x8cc30018, 0x240240c1,
+ 0xa4a20008, 0xaca30018, 0x90c4000a, 0x24020002, 0xa0a2000b, 0xa0a4000a,
+ 0x94c20010, 0xa4a20010, 0x90c30012, 0xa0a30012, 0x90c20013, 0xa0a20013,
+ 0x8cc30014, 0xaca30014, 0x8cc20024, 0xaca20024, 0x8cc30028, 0xaca30028,
+ 0x8cc2002c, 0x3c031000, 0xaca2002c, 0x24020001, 0xaf4301b8, 0xaf400044,
+ 0x03e00008, 0xaf400050, 0x27bdffe8, 0xafbf0010, 0x0e001047, 0x00000000,
+ 0x00002021, 0x0e000c78, 0xaf400180, 0x8fbf0010, 0x03e00008, 0x27bd0018,
+ 0x8f460148, 0x27450180, 0x3c038000, 0x00061402, 0x304700ff, 0x8f4201b8,
+ 0x00431024, 0x1440fffd, 0x00000000, 0x8f440140, 0x00061202, 0x304200ff,
+ 0x00061c02, 0xaca20004, 0x24020002, 0xa4a30008, 0x30c300ff, 0xa0a2000b,
+ 0xaca30024, 0x10e0000a, 0xaca40000, 0x28e20004, 0x14400005, 0x24020001,
+ 0x24020005, 0x54e20005, 0xa0a0000a, 0x24020001, 0x0a001609, 0xa0a2000a,
+ 0xa0a0000a, 0x3c021000, 0x03e00008, 0xaf4201b8, 0x03e00008, 0x00001021,
+ 0x10c00007, 0x00000000, 0x8ca20000, 0x24c6ffff, 0x24a50004, 0xac820000,
+ 0x14c0fffb, 0x24840004, 0x03e00008, 0x00000000, 0x0a00161f, 0x00a01021,
+ 0xac860000, 0x00000000, 0x00000000, 0x24840004, 0x00a01021, 0x1440fffa,
+ 0x24a5ffff, 0x03e00008, 0x00000000, 0x00000000 };
+
+static u32 bnx2_RXP_b06FwData[(0x0/4) + 1] = { 0x0 };
+static u32 bnx2_RXP_b06FwRodata[(0x28/4) + 1] = {
+ 0x0800468c, 0x0800458c, 0x08004630, 0x08004648, 0x08004660, 0x08004680,
+ 0x0800468c, 0x0800468c, 0x08004594, 0x00000000, 0x00000000 };
+static u32 bnx2_RXP_b06FwBss[(0x13a4/4) + 1] = { 0x0 };
+static u32 bnx2_RXP_b06FwSbss[(0x1c/4) + 1] = { 0x0 };
+
+static u32 bnx2_rv2p_proc1[] = {
+ 0x00000008, 0xac000001, 0x0000000c, 0x2f800001, 0x00000010, 0x213f0004,
+ 0x00000010, 0x20bf002c, 0x00000010, 0x203f0143, 0x00000018, 0x8000fffd,
+ 0x00000010, 0xb1b8b017, 0x0000000b, 0x2fdf0002, 0x00000000, 0x03d80000,
+ 0x00000000, 0x2c380000, 0x00000008, 0x2c800000, 0x00000008, 0x2d000000,
+ 0x00000010, 0x91d40000, 0x00000008, 0x2d800108, 0x00000008, 0x02000002,
+ 0x00000010, 0x91de0000, 0x0000000f, 0x42e0001c, 0x00000010, 0x91840a08,
+ 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008, 0x00000008, 0x2d800150,
+ 0x00000000, 0x00000000, 0x00000010, 0x91de0000, 0x00000010, 0x2c620002,
+ 0x00000018, 0x80000012, 0x0000000b, 0x2fdf0002, 0x0000000c, 0x1f800002,
+ 0x00000000, 0x2c070000, 0x00000018, 0x8000ffe6, 0x00000008, 0x02000002,
+ 0x0000000f, 0x42e0001c, 0x00000010, 0x91840a08, 0x00000008, 0x2c8000b0,
+ 0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800108,
+ 0x00000000, 0x00000000, 0x00000010, 0x91de0000, 0x00000018, 0x80000004,
+ 0x0000000c, 0x1f800002, 0x00000000, 0x00000000, 0x00000018, 0x8000ffd9,
+ 0x0000000c, 0x29800002, 0x0000000c, 0x1f800002, 0x00000000, 0x2adf0000,
+ 0x00000008, 0x2a000005, 0x00000018, 0x8000ffd4, 0x00000008, 0x02240030,
+ 0x00000018, 0x00040000, 0x00000018, 0x80000015, 0x00000018, 0x80000017,
+ 0x00000018, 0x8000001b, 0x00000018, 0x8000004c, 0x00000018, 0x8000008c,
+ 0x00000018, 0x8000000f, 0x00000018, 0x8000000e, 0x00000018, 0x8000000d,
+ 0x00000018, 0x8000000c, 0x00000018, 0x800000c2, 0x00000018, 0x8000000a,
+ 0x00000018, 0x80000009, 0x00000018, 0x80000008, 0x00000018, 0x800000fd,
+ 0x00000018, 0x80000006, 0x00000018, 0x80000005, 0x00000018, 0x800000ff,
+ 0x00000018, 0x80000104, 0x00000018, 0x80000002, 0x00000018, 0x80000098,
+ 0x00000018, 0x80000000, 0x0000000c, 0x1f800001, 0x00000000, 0x00000000,
+ 0x00000018, 0x8000ffba, 0x00000010, 0x91d40000, 0x0000000c, 0x29800001,
+ 0x0000000c, 0x1f800001, 0x00000008, 0x2a000002, 0x00000018, 0x8000ffb5,
+ 0x00000010, 0xb1a0b012, 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c200000,
+ 0x00000008, 0x2c800000, 0x00000008, 0x2d000000, 0x00000010, 0x91d40000,
+ 0x00000008, 0x2d80011c, 0x00000000, 0x00000000, 0x00000010, 0x91de0000,
+ 0x0000000f, 0x47600008, 0x0000000f, 0x060e0001, 0x00000010, 0x001f0000,
+ 0x00000000, 0x0f580000, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000,
+ 0x00000000, 0x0b660000, 0x00000000, 0x0d610000, 0x00000018, 0x80000013,
+ 0x0000000f, 0x47600008, 0x0000000b, 0x2fdf0002, 0x00000008, 0x2c800000,
+ 0x00000008, 0x2d000000, 0x00000010, 0x91d40000, 0x00000008, 0x2d80011c,
+ 0x0000000f, 0x060e0001, 0x00000010, 0x001f0000, 0x00000000, 0x0f580000,
+ 0x00000010, 0x91de0000, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000,
+ 0x00000000, 0x0b660000, 0x00000000, 0x0d610000, 0x00000000, 0x02620000,
+ 0x0000000b, 0x2fdf0002, 0x00000000, 0x309a0000, 0x00000000, 0x31040000,
+ 0x00000000, 0x0c961800, 0x00000009, 0x0c99ffff, 0x00000004, 0xcc993400,
+ 0x00000010, 0xb1963202, 0x00000008, 0x0f800000, 0x0000000c, 0x29800001,
+ 0x00000010, 0x00220002, 0x0000000c, 0x29520001, 0x0000000c, 0x29520000,
+ 0x00000008, 0x22000001, 0x0000000c, 0x1f800001, 0x00000000, 0x2adf0000,
+ 0x00000008, 0x2a000003, 0x00000018, 0x8000ff83, 0x00000010, 0xb1a0b01d,
+ 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c200000, 0x00000008, 0x2c8000b0,
+ 0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800150,
+ 0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000008, 0x2c800000,
+ 0x00000008, 0x2d000000, 0x00000008, 0x2d800108, 0x00000000, 0x00000000,
+ 0x00000010, 0x91de0000, 0x0000000f, 0x47600008, 0x00000000, 0x060e0000,
+ 0x00000010, 0x001f0000, 0x00000000, 0x0f580000, 0x00000010, 0x91de0000,
+ 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000, 0x00000000, 0x0b670000,
+ 0x00000000, 0x0d620000, 0x00000000, 0x0ce71800, 0x00000009, 0x0c99ffff,
+ 0x00000004, 0xcc993400, 0x00000010, 0xb1963220, 0x00000008, 0x0f800000,
+ 0x00000018, 0x8000001e, 0x0000000f, 0x47600008, 0x0000000b, 0x2fdf0002,
+ 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008, 0x00000010, 0x91d40000,
+ 0x00000008, 0x2d80012c, 0x0000000f, 0x060e0001, 0x00000010, 0x001f0000,
+ 0x00000000, 0x0f580000, 0x00000010, 0x91de0000, 0x00000000, 0x0a640000,
+ 0x00000000, 0x0ae50000, 0x00000000, 0x0b670000, 0x00000000, 0x0d620000,
+ 0x00000000, 0x02630000, 0x0000000f, 0x47620010, 0x00000000, 0x0ce71800,
+ 0x0000000b, 0x2fdf0002, 0x00000000, 0x311a0000, 0x00000000, 0x31840000,
+ 0x0000000b, 0xc20000ff, 0x00000002, 0x42040000, 0x00000001, 0x31620800,
+ 0x0000000f, 0x020e0010, 0x00000002, 0x31620800, 0x00000009, 0x0c99ffff,
+ 0x00000004, 0xcc993400, 0x00000010, 0xb1963202, 0x00000008, 0x0f800000,
+ 0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x0000000c, 0x61420006,
+ 0x00000008, 0x22000008, 0x00000000, 0x2adf0000, 0x00000008, 0x2a000004,
+ 0x00000018, 0x8000ff42, 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008,
+ 0x00000010, 0x91a0b008, 0x00000010, 0x91d40000, 0x0000000c, 0x31620018,
+ 0x00000008, 0x2d800001, 0x00000000, 0x00000000, 0x00000010, 0x91de0000,
+ 0x00000008, 0xac000001, 0x00000018, 0x8000000e, 0x00000000, 0x0380b000,
+ 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c004000, 0x00000010, 0x91d40000,
+ 0x00000008, 0x2d800101, 0x00000000, 0x00000000, 0x00000010, 0x91de0000,
+ 0x0000000c, 0x31620018, 0x00000008, 0x2d800001, 0x00000000, 0x00000000,
+ 0x00000010, 0x91de0000, 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c000e00,
+ 0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x00000008, 0x2a000007,
+ 0x00000018, 0x8000ff27, 0x00000010, 0xb1a0b016, 0x0000000b, 0x2fdf0002,
+ 0x00000000, 0x03d80000, 0x00000000, 0x2c200000, 0x00000008, 0x2c8000b0,
+ 0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800150,
+ 0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000008, 0x2c800000,
+ 0x00000008, 0x2d000000, 0x00000008, 0x2d800108, 0x00000008, 0x07000001,
+ 0x00000010, 0xb5de1c00, 0x00000010, 0x2c620002, 0x00000018, 0x8000000a,
+ 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c070000, 0x0000000c, 0x1f800001,
+ 0x00000010, 0x91de0000, 0x00000018, 0x8000ff11, 0x00000008, 0x2c8000b0,
+ 0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800108,
+ 0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x00000010, 0x91de0000,
+ 0x00000000, 0x2adf0000, 0x00000008, 0x2a00000a, 0x00000018, 0x8000ff07,
+ 0x00000000, 0x82265600, 0x0000000f, 0x47220008, 0x00000009, 0x070e000f,
+ 0x00000008, 0x070e0008, 0x00000008, 0x02800001, 0x00000007, 0x02851c00,
+ 0x00000008, 0x82850001, 0x00000000, 0x02840a00, 0x00000007, 0x42851c00,
+ 0x00000003, 0xc3aa5200, 0x00000000, 0x03b10e00, 0x00000010, 0x001f0000,
+ 0x0000000f, 0x0f280007, 0x00000007, 0x4b071c00, 0x00000000, 0x00000000,
+ 0x0000000f, 0x0a960003, 0x00000000, 0x0a955c00, 0x00000000, 0x4a005a00,
+ 0x00000000, 0x0c960a00, 0x00000009, 0x0c99ffff, 0x00000008, 0x0d00ffff,
+ 0x00000010, 0xb1963202, 0x00000008, 0x0f800005, 0x00000010, 0x00220020,
+ 0x00000000, 0x02a70000, 0x00000010, 0xb1850002, 0x00000008, 0x82850200,
+ 0x00000000, 0x02000000, 0x00000000, 0x03a60000, 0x00000018, 0x8000004e,
+ 0x00000000, 0x072b0000, 0x00000001, 0x878c1c00, 0x00000000, 0x870e1e00,
+ 0x00000000, 0x860c1e00, 0x00000000, 0x03061e00, 0x00000010, 0xb18e0003,
+ 0x00000018, 0x80000047, 0x00000018, 0x8000fffa, 0x00000010, 0x918c0003,
+ 0x00000010, 0xb1870002, 0x00000018, 0x80000043, 0x00000010, 0x91d40000,
+ 0x0000000c, 0x29800001, 0x00000000, 0x2a860000, 0x00000000, 0x230c0000,
+ 0x00000000, 0x2b070000, 0x00000010, 0xb187000e, 0x00000008, 0x2a000008,
+ 0x00000018, 0x8000003b, 0x00000010, 0x91d40000, 0x00000000, 0x28d18c00,
+ 0x00000000, 0x2a860000, 0x00000000, 0x230c0000, 0x00000000, 0x2b070000,
+ 0x00000018, 0x8000fff8, 0x00000010, 0x91d40000, 0x0000000c, 0x29800001,
+ 0x00000000, 0x2aab0000, 0x00000000, 0xa3265600, 0x00000000, 0x2b000000,
+ 0x0000000c, 0x1f800001, 0x00000008, 0x2a000008, 0x00000018, 0x8000fec8,
+ 0x00000010, 0x91d40000, 0x0000000c, 0x29800001, 0x0000000c, 0x1f800001,
+ 0x00000008, 0x2a000009, 0x00000018, 0x8000fec3, 0x00000010, 0x91d40000,
+ 0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x00000000, 0x29420000,
+ 0x00000008, 0x2a000002, 0x00000018, 0x8000febd, 0x00000018, 0x8000febc,
+ 0x00000010, 0xb1bcb016, 0x0000000b, 0x2fdf0002, 0x00000000, 0x03d80000,
+ 0x00000000, 0x2c3c0000, 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008,
+ 0x00000010, 0x91d40000, 0x00000008, 0x2d800150, 0x00000000, 0x00000000,
+ 0x00000010, 0x205f0000, 0x00000008, 0x2c800000, 0x00000008, 0x2d000000,
+ 0x00000008, 0x2d800108, 0x00000008, 0x07000001, 0x00000010, 0xb5de1c00,
+ 0x00000010, 0x2c620002, 0x00000018, 0x8000000a, 0x0000000b, 0x2fdf0002,
+ 0x00000000, 0x2c070000, 0x0000000c, 0x1f800000, 0x00000010, 0x91de0000,
+ 0x00000018, 0x8000fea6, 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008,
+ 0x00000010, 0x91d40000, 0x00000008, 0x2d800108, 0x0000000c, 0x29800000,
+ 0x0000000c, 0x1f800000, 0x00000010, 0x91de0000, 0x00000000, 0x2adf0000,
+ 0x00000008, 0x2a000006, 0x00000018, 0x8000fe9c, 0x00000008, 0x03050004,
+ 0x00000006, 0x83040c00, 0x00000008, 0x02850200, 0x00000000, 0x86050c00,
+ 0x00000001, 0x860c0e00, 0x00000008, 0x02040004, 0x00000000, 0x02041800,
+ 0x00000000, 0x83871800, 0x00000018, 0x00020000 };
+
+static u32 bnx2_rv2p_proc2[] = {
+ 0x00000000, 0x2a000000, 0x00000010, 0xb1d40000, 0x00000008, 0x02540003,
+ 0x00000018, 0x00040000, 0x00000018, 0x8000000a, 0x00000018, 0x8000000a,
+ 0x00000018, 0x8000000e, 0x00000018, 0x80000056, 0x00000018, 0x800001b9,
+ 0x00000018, 0x800001e1, 0x00000018, 0x8000019b, 0x00000018, 0x800001f9,
+ 0x00000018, 0x8000019f, 0x00000018, 0x800001a6, 0x00000018, 0x80000000,
+ 0x0000000c, 0x29800001, 0x00000000, 0x2a000000, 0x0000000c, 0x29800000,
+ 0x00000010, 0x20530000, 0x00000018, 0x8000ffee, 0x0000000c, 0x29800001,
+ 0x00000010, 0x91de0000, 0x00000010, 0x001f0000, 0x00000000, 0x2f80aa00,
+ 0x00000000, 0x2a000000, 0x00000000, 0x0d610000, 0x00000000, 0x03620000,
+ 0x00000000, 0x2c400000, 0x00000000, 0x02638c00, 0x00000000, 0x26460000,
+ 0x00000010, 0x00420002, 0x00000008, 0x02040012, 0x00000010, 0xb9060836,
+ 0x00000000, 0x0f580000, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000,
+ 0x00000000, 0x0b660000, 0x00000000, 0x0c000000, 0x00000000, 0x0b800000,
+ 0x00000010, 0x00420009, 0x00000008, 0x0cc60012, 0x00000008, 0x0f800003,
+ 0x00000000, 0x00000000, 0x00000010, 0x009f0000, 0x00000008, 0x27110012,
+ 0x00000000, 0x66900000, 0x00000008, 0xa31b0012, 0x00000018, 0x80000008,
+ 0x00000000, 0x0cc60000, 0x00000008, 0x0f800003, 0x00000000, 0x00000000,
+ 0x00000010, 0x009f0000, 0x00000000, 0x27110000, 0x00000000, 0x66900000,
+ 0x00000000, 0x231b0000, 0x00000010, 0xb197320e, 0x00000000, 0x25960000,
+ 0x00000000, 0x021b0000, 0x00000010, 0x001f0000, 0x00000008, 0x0f800003,
+ 0x0000000c, 0x29800000, 0x00000010, 0x20530000, 0x00000000, 0x22c50800,
+ 0x00000010, 0x009f0000, 0x00000000, 0x27002200, 0x00000000, 0x26802000,
+ 0x00000000, 0x231b0000, 0x0000000c, 0x69520001, 0x00000018, 0x8000fff3,
+ 0x00000010, 0x01130002, 0x00000010, 0xb1980003, 0x00000010, 0x001f0000,
+ 0x00000008, 0x0f800004, 0x00000008, 0x22000003, 0x00000008, 0x2c80000c,
+ 0x00000008, 0x2d00000c, 0x00000010, 0x009f0000, 0x00000000, 0x25960000,
+ 0x0000000c, 0x29800000, 0x00000000, 0x32140000, 0x00000000, 0x32950000,
+ 0x00000000, 0x33160000, 0x00000000, 0x31e32e00, 0x00000008, 0x2d800010,
+ 0x00000010, 0x20530000, 0x00000018, 0x8000ffac, 0x00000000, 0x23000000,
+ 0x00000000, 0x25e60000, 0x00000008, 0x2200000b, 0x0000000c, 0x69520000,
+ 0x0000000c, 0x29800000, 0x00000010, 0x20530000, 0x00000018, 0x8000ffa5,
+ 0x0000000c, 0x29800001, 0x00000010, 0x91de0000, 0x00000000, 0x2fd50000,
+ 0x00000010, 0x001f0000, 0x00000000, 0x02700000, 0x00000000, 0x0d620000,
+ 0x00000000, 0xbb630800, 0x00000000, 0x2a000000, 0x00000009, 0x076000ff,
+ 0x0000000f, 0x2c0e0007, 0x00000008, 0x2c800000, 0x00000008, 0x2d000064,
+ 0x00000008, 0x2d80011c, 0x00000009, 0x06420002, 0x0000000c, 0x61420001,
+ 0x00000000, 0x0f400000, 0x00000000, 0x02d08c00, 0x00000000, 0x23000000,
+ 0x00000004, 0x826da000, 0x00000000, 0x8304a000, 0x00000000, 0x22c50c00,
+ 0x00000000, 0x03760000, 0x00000004, 0x83860a00, 0x00000000, 0x83870c00,
+ 0x00000010, 0x91de0000, 0x00000000, 0x037c0000, 0x00000000, 0x837b0c00,
+ 0x00000001, 0x83060e00, 0x00000000, 0x83870c00, 0x00000000, 0x82850e00,
+ 0x00000010, 0xb1860016, 0x0000000f, 0x47610018, 0x00000000, 0x068e0000,
+ 0x0000000f, 0x47670010, 0x0000000f, 0x47e20010, 0x00000000, 0x870e1e00,
+ 0x00000010, 0xb70e1a10, 0x00000010, 0x0ce7000e, 0x00000008, 0x22000009,
+ 0x00000000, 0x286d0000, 0x0000000f, 0x65680010, 0x00000003, 0xf66c9400,
+ 0x00000010, 0xb972a003, 0x0000000c, 0x73e70019, 0x0000000c, 0x21420004,
+ 0x00000018, 0x8000023f, 0x00000000, 0x37ed0000, 0x0000000c, 0x73e7001a,
+ 0x00000010, 0x20530000, 0x00000008, 0x22000008, 0x0000000c, 0x61420004,
+ 0x00000000, 0x02f60000, 0x00000004, 0x82840a00, 0x00000010, 0xb1840a2b,
+ 0x00000010, 0x2d67000a, 0x00000010, 0xb96d0804, 0x00000004, 0xb6ed0a00,
+ 0x00000000, 0x37ed0000, 0x00000018, 0x80000029, 0x0000000c, 0x61420000,
+ 0x00000000, 0x37040000, 0x00000000, 0x37850000, 0x0000000c, 0x33e7001a,
+ 0x00000018, 0x80000024, 0x00000010, 0xb96d0809, 0x00000004, 0xb6ed0a00,
+ 0x00000000, 0x036d0000, 0x00000004, 0xb76e0c00, 0x00000010, 0x91ee0c1f,
+ 0x0000000c, 0x73e7001a, 0x00000004, 0xb6ef0c00, 0x00000000, 0x37ed0000,
+ 0x00000018, 0x8000001b, 0x0000000c, 0x61420000, 0x00000010, 0xb7ee0a05,
+ 0x00000010, 0xb96f0815, 0x00000003, 0xb76e0800, 0x00000004, 0xb7ef0a00,
+ 0x00000018, 0x80000015, 0x00000010, 0x0ce7000c, 0x00000008, 0x22000009,
+ 0x00000000, 0x286d0000, 0x0000000f, 0x65680010, 0x00000003, 0xf66c9400,
+ 0x00000010, 0xb972a003, 0x0000000c, 0x73e70019, 0x0000000c, 0x21420004,
+ 0x00000018, 0x80000215, 0x00000010, 0x20530000, 0x00000008, 0x22000008,
+ 0x0000000c, 0x61420004, 0x00000000, 0x37040000, 0x00000000, 0x37850000,
+ 0x00000000, 0x036d0000, 0x00000003, 0xb8f10c00, 0x00000018, 0x80000004,
+ 0x00000000, 0x02840000, 0x00000002, 0x21421800, 0x0000000c, 0x61420000,
+ 0x00000000, 0x286d0000, 0x0000000f, 0x65ed0010, 0x00000009, 0x266dffff,
+ 0x00000000, 0x23000000, 0x00000010, 0xb1840a3d, 0x00000010, 0x01420002,
+ 0x00000004, 0xb8f10a00, 0x00000003, 0x83760a00, 0x00000010, 0xb8040c39,
+ 0x00000010, 0xb7e6080a, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000,
+ 0x00000009, 0x0c68ffff, 0x00000009, 0x0b67ffff, 0x00000000, 0x0be60000,
+ 0x00000000, 0x0c840000, 0x00000010, 0xb197320c, 0x00000008, 0x0f800002,
+ 0x00000018, 0x8000000a, 0x00000000, 0x0a6a0000, 0x00000000, 0x0aeb0000,
+ 0x00000000, 0x0c000000, 0x00000009, 0x0b6cffff, 0x00000000, 0x0be90000,
+ 0x00000000, 0x0c840000, 0x00000010, 0xb1973203, 0x00000008, 0x0f800002,
+ 0x00000018, 0x80000001, 0x00000010, 0x001f0000, 0x00000000, 0x0c860000,
+ 0x00000000, 0x06980000, 0x00000008, 0x0f800003, 0x00000000, 0x00000000,
+ 0x00000010, 0x009f0000, 0x00000010, 0xb1973210, 0x00000000, 0x231b0000,
+ 0x00000000, 0x02043600, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010,
+ 0x00000009, 0x2607ffff, 0x00000000, 0x27111a00, 0x00000000, 0x66900000,
+ 0x0000000c, 0x29000000, 0x00000018, 0x800001de, 0x00000000, 0x06980000,
+ 0x00000010, 0x20530000, 0x00000000, 0x22c58c00, 0x00000010, 0x001f0000,
+ 0x00000008, 0x0f800003, 0x00000018, 0x8000fff0, 0x00000000, 0x02043600,
+ 0x00000000, 0x231b0000, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010,
+ 0x00000009, 0x2607ffff, 0x00000000, 0x27111a00, 0x00000000, 0x66900000,
+ 0x0000000c, 0x29000000, 0x00000010, 0x91840a02, 0x00000002, 0x21421800,
+ 0x00000000, 0x32140000, 0x00000000, 0x32950000, 0x00000005, 0x73e72c00,
+ 0x00000005, 0x74683000, 0x00000000, 0x33170000, 0x00000018, 0x80000138,
+ 0x00000010, 0x91c60004, 0x00000008, 0x07000004, 0x00000010, 0xb1c41c02,
+ 0x00000010, 0x91840a04, 0x00000018, 0x800001c3, 0x00000010, 0x20530000,
+ 0x00000000, 0x22c58c00, 0x00000010, 0xb1840a8e, 0x0000000c, 0x21420006,
+ 0x00000010, 0x0ce7001a, 0x0000000f, 0x43680010, 0x00000000, 0x03f30c00,
+ 0x00000010, 0x91870850, 0x0000000f, 0x46ec0010, 0x00000010, 0xb68d0c4e,
+ 0x00000000, 0x838d0c00, 0x00000000, 0xa3050800, 0x00000001, 0xa3460e00,
+ 0x00000000, 0x02048c00, 0x00000010, 0x91840a02, 0x00000002, 0x21421800,
+ 0x00000010, 0x001f0000, 0x00000008, 0x22000008, 0x00000003, 0x8384a000,
+ 0x0000000f, 0x65870010, 0x00000009, 0x2607ffff, 0x00000000, 0x27750c00,
+ 0x00000000, 0x66f40000, 0x0000000c, 0x29000000, 0x00000018, 0x800001aa,
+ 0x00000000, 0x03068c00, 0x00000003, 0xf4680c00, 0x00000010, 0x20530000,
+ 0x00000000, 0x22c58c00, 0x00000018, 0x8000ffe5, 0x00000000, 0x39760000,
+ 0x00000000, 0x39840000, 0x0000000c, 0x33e70019, 0x00000010, 0x001f0000,
+ 0x00000000, 0x031e0000, 0x00000000, 0x0760fe00, 0x0000000f, 0x0f0e0007,
+ 0x00000000, 0x83850800, 0x00000000, 0x0a7d0000, 0x00000000, 0x0afe0000,
+ 0x00000000, 0x0b7f0000, 0x00000000, 0x0d7a0000, 0x00000000, 0x0c000000,
+ 0x00000000, 0x0bfc0000, 0x00000000, 0x0c970e00, 0x00000008, 0x0f800003,
+ 0x0000000f, 0x47670010, 0x00000008, 0x070e0001, 0x0000000b, 0xc38000ff,
+ 0x00000002, 0x43870000, 0x00000001, 0x33e70e00, 0x0000000f, 0x038e0010,
+ 0x00000002, 0x33e70e00, 0x00000000, 0x28f30000, 0x00000010, 0x009f0000,
+ 0x00000000, 0x02043600, 0x00000010, 0x91840a02, 0x00000002, 0x21421800,
+ 0x00000008, 0x22000006, 0x00000000, 0x231b0000, 0x00000000, 0x23ff0000,
+ 0x00000000, 0x241b0000, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010,
+ 0x00000009, 0x2607ffff, 0x00000000, 0x27110000, 0x00000000, 0x26900000,
+ 0x0000000c, 0x29000000, 0x00000018, 0x8000017e, 0x00000003, 0xf4683600,
+ 0x00000000, 0x3a100000, 0x00000000, 0x3a910000, 0x00000003, 0xf66c2400,
+ 0x00000010, 0x001f0000, 0x00000010, 0xb1923604, 0x00000008, 0x0f800004,
+ 0x00000000, 0x00000000, 0x00000010, 0x009f0000, 0x00000000, 0x3e170000,
+ 0x00000000, 0x3e940000, 0x00000000, 0x3f150000, 0x00000000, 0x3f960000,
+ 0x00000010, 0x001f0000, 0x00000000, 0x0f060000, 0x00000010, 0x20530000,
+ 0x00000000, 0x22c53600, 0x00000018, 0x8000ffac, 0x00000010, 0x001f0000,
+ 0x00000000, 0x031e0000, 0x00000000, 0x83850800, 0x00000009, 0x076000ff,
+ 0x0000000f, 0x0f0e0007, 0x00000000, 0x0c000000, 0x00000000, 0x0a7d0000,
+ 0x00000000, 0x0afe0000, 0x00000000, 0x0b7f0000, 0x00000000, 0x0d7a0000,
+ 0x00000000, 0x0bfc0000, 0x00000000, 0x0c970e00, 0x00000008, 0x0f800003,
+ 0x0000000f, 0x47670010, 0x00000008, 0x070e0001, 0x0000000b, 0xc38000ff,
+ 0x00000002, 0x43870000, 0x00000001, 0x33e70e00, 0x0000000f, 0x038e0010,
+ 0x00000002, 0x33e70e00, 0x00000000, 0x39840000, 0x00000003, 0xb9720800,
+ 0x00000000, 0x28f30000, 0x0000000f, 0x65680010, 0x00000010, 0x009f0000,
+ 0x00000000, 0x02043600, 0x00000010, 0x91840a02, 0x00000002, 0x21421800,
+ 0x00000008, 0x22000007, 0x00000000, 0x231b0000, 0x00000000, 0x23ff0000,
+ 0x00000000, 0x241b0000, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010,
+ 0x00000009, 0x2607ffff, 0x00000000, 0x27110000, 0x00000000, 0x26900000,
+ 0x0000000c, 0x29000000, 0x00000018, 0x80000145, 0x00000003, 0xf4683600,
+ 0x00000000, 0x3a100000, 0x00000000, 0x3a910000, 0x00000003, 0xf66c2400,
+ 0x00000010, 0x001f0000, 0x00000010, 0xb1923604, 0x00000008, 0x0f800004,
+ 0x00000000, 0x00000000, 0x00000010, 0x009f0000, 0x00000000, 0x3e170000,
+ 0x00000000, 0x3e940000, 0x00000000, 0x3f150000, 0x00000000, 0x3f960000,
+ 0x00000010, 0x001f0000, 0x00000000, 0x0f060000, 0x00000010, 0x20530000,
+ 0x00000000, 0x22c53600, 0x00000018, 0x8000ff73, 0x00000010, 0x0ce70005,
+ 0x00000008, 0x2c80000c, 0x00000008, 0x2d000070, 0x00000008, 0x2d800010,
+ 0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000018, 0x8000011d,
+ 0x00000000, 0x2c1e0000, 0x00000008, 0x2c8000b8, 0x00000008, 0x2d000010,
+ 0x00000008, 0x2d800048, 0x00000000, 0x00000000, 0x00000010, 0x91de0000,
+ 0x00000018, 0x8000fe5d, 0x0000000c, 0x29800001, 0x00000000, 0x2a000000,
+ 0x00000010, 0x001f0000, 0x00000000, 0x0f008000, 0x00000008, 0x0f800007,
+ 0x00000018, 0x80000006, 0x0000000c, 0x29800001, 0x00000000, 0x2a000000,
+ 0x00000010, 0x001f0000, 0x0000000f, 0x0f470007, 0x00000008, 0x0f800008,
+ 0x00000018, 0x80000119, 0x00000010, 0x20530000, 0x00000018, 0x8000fe4f,
+ 0x0000000c, 0x29800001, 0x00000010, 0x91de0000, 0x00000000, 0x2fd50000,
+ 0x00000000, 0x2a000000, 0x00000009, 0x0261ffff, 0x0000000d, 0x70e10001,
+ 0x00000018, 0x80000101, 0x00000000, 0x2c400000, 0x00000008, 0x2c8000c4,
+ 0x00000008, 0x2d00001c, 0x00000008, 0x2d800001, 0x00000005, 0x70e10800,
+ 0x00000010, 0x91de0000, 0x00000018, 0x8000fe41, 0x0000000c, 0x29800001,
+ 0x00000010, 0x91de0000, 0x00000000, 0x2fd50000, 0x00000010, 0x001f0000,
+ 0x00000000, 0x02700000, 0x00000000, 0x0d620000, 0x00000000, 0xbb630800,
+ 0x00000000, 0x2a000000, 0x00000000, 0x0f400000, 0x00000000, 0x2c400000,
+ 0x0000000c, 0x73e7001b, 0x00000010, 0x0ce7000e, 0x00000000, 0x286d0000,
+ 0x0000000f, 0x65ed0010, 0x00000009, 0x266dffff, 0x00000018, 0x80000069,
+ 0x00000008, 0x02000004, 0x00000010, 0x91c40803, 0x00000018, 0x800000f6,
+ 0x00000010, 0x20530000, 0x00000018, 0x800000e5, 0x00000008, 0x2c8000b8,
+ 0x00000008, 0x2d000010, 0x00000008, 0x2d800048, 0x00000018, 0x80000005,
+ 0x00000008, 0x2c8000c4, 0x00000008, 0x2d00001c, 0x00000008, 0x2d800001,
+ 0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000008, 0x2c800048,
+ 0x00000008, 0x2d000068, 0x00000008, 0x2d800104, 0x00000000, 0x00000000,
+ 0x00000010, 0x91de0000, 0x00000000, 0x27f60000, 0x00000010, 0xb87a9e04,
+ 0x00000008, 0x2200000d, 0x00000018, 0x800000e2, 0x00000010, 0x20530000,
+ 0x00000018, 0x8000fe18, 0x0000000c, 0x29800001, 0x00000010, 0x91de0000,
+ 0x00000000, 0x2fd50000, 0x00000010, 0x001f0000, 0x00000000, 0x02700000,
+ 0x00000000, 0x0d620000, 0x00000000, 0xbb630800, 0x00000000, 0x2a000000,
+ 0x00000010, 0x0e670011, 0x00000000, 0x286d0000, 0x0000000f, 0x65ed0010,
+ 0x00000009, 0x266dffff, 0x00000004, 0xb8f1a000, 0x00000000, 0x0f400000,
+ 0x0000000c, 0x73e7001c, 0x00000018, 0x80000040, 0x00000008, 0x02000004,
+ 0x00000010, 0x91c40802, 0x00000018, 0x800000cd, 0x00000000, 0x2c1e0000,
+ 0x00000008, 0x2c8000b8, 0x00000008, 0x2d000010, 0x00000008, 0x2d800048,
+ 0x00000010, 0x20530000, 0x00000010, 0x91de0000, 0x00000018, 0x8000fdfe,
+ 0x0000000c, 0x29800001, 0x00000000, 0x03550000, 0x00000000, 0x06460000,
+ 0x00000000, 0x03d60000, 0x00000000, 0x2a000000, 0x0000000f, 0x0f480007,
+ 0x00000010, 0xb18c0027, 0x0000000f, 0x47420008, 0x00000009, 0x070e000f,
+ 0x00000008, 0x070e0008, 0x00000010, 0x001f0000, 0x00000008, 0x09000001,
+ 0x00000007, 0x09121c00, 0x00000003, 0xcbca9200, 0x00000000, 0x0b97a200,
+ 0x00000007, 0x4b171c00, 0x0000000f, 0x0a960003, 0x00000000, 0x0a959c00,
+ 0x00000000, 0x4a009a00, 0x00000008, 0x82120001, 0x00000001, 0x0c170800,
+ 0x00000000, 0x02180000, 0x00000000, 0x0c971800, 0x00000008, 0x0d00ffff,
+ 0x00000008, 0x0f800006, 0x0000000c, 0x29000000, 0x00000008, 0x22000001,
+ 0x00000000, 0x22c50c00, 0x00000010, 0x009f0000, 0x00000010, 0xb197320b,
+ 0x00000000, 0x231b0000, 0x00000000, 0x27110800, 0x00000000, 0x66900000,
+ 0x00000018, 0x800000a4, 0x00000000, 0x02180000, 0x00000010, 0x20530000,
+ 0x00000000, 0x22c53600, 0x00000010, 0x001f0000, 0x00000008, 0x0f800006,
+ 0x00000018, 0x8000fff5, 0x00000010, 0x91870002, 0x00000008, 0x2200000a,
+ 0x00000000, 0x231b0000, 0x00000000, 0x27110800, 0x00000000, 0x66900000,
+ 0x00000018, 0x80000098, 0x00000008, 0x0200000a, 0x00000010, 0x91c40804,
+ 0x00000010, 0x02c20003, 0x00000010, 0x001f0000, 0x00000008, 0x0f800008,
+ 0x00000010, 0x20530000, 0x00000018, 0x8000fdc9, 0x00000000, 0x06820000,
+ 0x00000010, 0x001f0000, 0x00000010, 0x0ce70028, 0x00000000, 0x03720000,
+ 0x00000000, 0xa8760c00, 0x00000000, 0x0cf60000, 0x00000010, 0xb8723224,
+ 0x00000000, 0x03440000, 0x00000008, 0x22000010, 0x00000000, 0x03ca0000,
+ 0x0000000f, 0x65680010, 0x00000000, 0x0bcf0000, 0x00000000, 0x27f20000,
+ 0x00000010, 0xb7ef3203, 0x0000000c, 0x21420004, 0x0000000c, 0x73e70019,
+ 0x00000000, 0x07520000, 0x00000000, 0x29000000, 0x00000018, 0x8000007e,
+ 0x00000004, 0xb9723200, 0x00000010, 0x20530000, 0x00000000, 0x22060000,
+ 0x0000000c, 0x61420004, 0x00000000, 0x25070000, 0x00000000, 0x27970000,
+ 0x00000000, 0x290e0000, 0x00000010, 0x0ce70010, 0x00000010, 0xb873320f,
+ 0x0000000f, 0x436c0010, 0x00000000, 0x03f30c00, 0x00000000, 0x03f30000,
+ 0x00000000, 0x83990e00, 0x00000001, 0x83860e00, 0x00000000, 0x83060e00,
+ 0x00000003, 0xf66c0c00, 0x00000000, 0x39f30e00, 0x00000000, 0x3af50e00,
+ 0x00000000, 0x7a740000, 0x0000000f, 0x43680010, 0x00000001, 0x83860e00,
+ 0x00000000, 0x83060e00, 0x00000003, 0xf4680c00, 0x00000000, 0x286d0000,
+ 0x00000000, 0x03690000, 0x00000010, 0xb1f60c54, 0x00000000, 0x0a6a0000,
+ 0x00000000, 0x0aeb0000, 0x00000009, 0x0b6cffff, 0x00000000, 0x0c000000,
+ 0x00000000, 0x0be90000, 0x00000003, 0x8cf6a000, 0x0000000c, 0x09800002,
+ 0x00000010, 0x009f0000, 0x00000010, 0xb8173209, 0x00000000, 0x35140000,
+ 0x00000000, 0x35950000, 0x00000005, 0x766c2c00, 0x00000000, 0x34970000,
+ 0x00000004, 0xb8f12e00, 0x00000010, 0x001f0000, 0x00000008, 0x0f800004,
+ 0x00000018, 0x8000fff7, 0x00000000, 0x03e90000, 0x00000010, 0xb8f6a01a,
+ 0x00000010, 0x20130019, 0x00000010, 0xb1f10e18, 0x00000000, 0x83973200,
+ 0x00000000, 0x38700e00, 0x00000000, 0xbb760e00, 0x00000000, 0x37d00000,
+ 0x0000000c, 0x73e7001a, 0x00000003, 0xb8f1a000, 0x00000000, 0x32140000,
+ 0x00000000, 0x32950000, 0x00000005, 0x73e72c00, 0x00000000, 0x33190000,
+ 0x00000005, 0x74680000, 0x00000010, 0x0ce7000d, 0x00000008, 0x22000009,
+ 0x00000000, 0x07520000, 0x00000000, 0x29000000, 0x0000000c, 0x73e70019,
+ 0x0000000f, 0x65680010, 0x0000000c, 0x21420004, 0x00000018, 0x8000003c,
+ 0x00000010, 0x20530000, 0x0000000c, 0x61420004, 0x00000000, 0x290e0000,
+ 0x00000018, 0x80000002, 0x00000010, 0x91973206, 0x00000000, 0x35140000,
+ 0x00000000, 0x35950000, 0x00000005, 0x766c2c00, 0x00000000, 0x34990000,
+ 0x00000004, 0xb8f13200, 0x00000000, 0x83690c00, 0x00000010, 0xb1860013,
+ 0x00000000, 0x28e90000, 0x00000008, 0x22000004, 0x00000000, 0x23ec0000,
+ 0x00000000, 0x03690000, 0x00000010, 0xb8660c07, 0x00000009, 0x036cffff,
+ 0x00000000, 0x326a0000, 0x00000000, 0x32eb0000, 0x00000005, 0x73e70c00,
+ 0x00000000, 0x33690000, 0x00000005, 0x74680000, 0x0000000c, 0x73e7001c,
+ 0x00000000, 0x03690000, 0x00000010, 0xb1f60c12, 0x00000010, 0xb1d00c11,
+ 0x0000000c, 0x21420005, 0x0000000c, 0x33e7001c, 0x00000018, 0x8000000e,
+ 0x00000010, 0x2e67000d, 0x00000000, 0x03690000, 0x00000010, 0xb1f60c0b,
+ 0x00000010, 0xb1d00c0a, 0x00000000, 0x03440000, 0x00000008, 0x2200000c,
+ 0x00000000, 0x07520000, 0x00000000, 0x29000000, 0x00000018, 0x80000015,
+ 0x0000000c, 0x33e7001c, 0x00000010, 0x20530000, 0x00000000, 0x22060000,
+ 0x00000000, 0x290e0000, 0x00000018, 0x000d0000, 0x00000000, 0x06820000,
+ 0x00000010, 0x2de7000d, 0x00000010, 0x0ce7000c, 0x00000000, 0x27f20000,
+ 0x00000010, 0xb96d9e0a, 0x00000000, 0xa86d9e00, 0x00000009, 0x0361ffff,
+ 0x00000010, 0xb7500c07, 0x00000008, 0x2200000f, 0x0000000f, 0x65680010,
+ 0x00000000, 0x29000000, 0x00000018, 0x80000004, 0x0000000c, 0x33e7001b,
+ 0x00000010, 0x20530000, 0x00000018, 0x000d0000, 0x00000000, 0x2b820000,
+ 0x00000010, 0x20d2002f, 0x00000010, 0x0052002e, 0x00000009, 0x054e0007,
+ 0x00000010, 0xb18a002c, 0x00000000, 0x050a8c00, 0x00000008, 0x850a0008,
+ 0x00000010, 0x918a0029, 0x00000003, 0xc5008800, 0x00000008, 0xa3460001,
+ 0x00000010, 0xb1c60007, 0x00000008, 0x22000001, 0x0000000c, 0x29800000,
+ 0x00000010, 0x20530000, 0x00000000, 0x274e8c00, 0x00000000, 0x66cd0000,
+ 0x00000000, 0x22c58c00, 0x00000008, 0x22000014, 0x00000003, 0x22c58e00,
+ 0x00000003, 0x23c58e00, 0x00000003, 0x22c58e00, 0x00000003, 0x26cd9e00,
+ 0x00000003, 0x27cd9e00, 0x00000003, 0x26cd9e00, 0x00000003, 0x274ea000,
+ 0x00000003, 0x284ea000, 0x00000003, 0x274ea000, 0x0000000c, 0x69520000,
+ 0x0000000c, 0x29800000, 0x00000010, 0x20530000, 0x00000003, 0x22c58e00,
+ 0x00000003, 0x23c58e00, 0x00000003, 0x22c58e00, 0x00000003, 0x26cd9e00,
+ 0x00000003, 0x27cd9e00, 0x00000003, 0x26cd9e00, 0x00000003, 0x274ea000,
+ 0x00000003, 0x284ea000, 0x00000003, 0x274ea000, 0x00000000, 0xa2c58c00,
+ 0x00000000, 0xa74e8c00, 0x00000000, 0xe6cd0000, 0x0000000f, 0x620a0010,
+ 0x00000008, 0x23460001, 0x0000000c, 0x29800000, 0x00000010, 0x20530000,
+ 0x0000000c, 0x29520000, 0x00000018, 0x80000002, 0x0000000c, 0x29800000,
+ 0x00000018, 0x00570000 };
+
+static const int bnx2_TPAT_b06FwReleaseMajor = 0x1;
+static const int bnx2_TPAT_b06FwReleaseMinor = 0x0;
+static const int bnx2_TPAT_b06FwReleaseFix = 0x0;
+static const u32 bnx2_TPAT_b06FwStartAddr = 0x08000860;
+static const u32 bnx2_TPAT_b06FwTextAddr = 0x08000800;
+static const int bnx2_TPAT_b06FwTextLen = 0x122c;
+static const u32 bnx2_TPAT_b06FwDataAddr = 0x08001a60;
+static const int bnx2_TPAT_b06FwDataLen = 0x0;
+static const u32 bnx2_TPAT_b06FwRodataAddr = 0x00000000;
+static const int bnx2_TPAT_b06FwRodataLen = 0x0;
+static const u32 bnx2_TPAT_b06FwBssAddr = 0x08001aa0;
+static const int bnx2_TPAT_b06FwBssLen = 0x250;
+static const u32 bnx2_TPAT_b06FwSbssAddr = 0x08001a60;
+static const int bnx2_TPAT_b06FwSbssLen = 0x34;
+static u32 bnx2_TPAT_b06FwText[(0x122c/4) + 1] = {
+ 0x0a000218, 0x00000000, 0x00000000, 0x0000000d, 0x74706174, 0x20322e35,
+ 0x2e313100, 0x02050b01, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c020800,
+ 0x24421a60, 0x3c030800, 0x24631cf0, 0xac400000, 0x0043202b, 0x1480fffd,
+ 0x24420004, 0x3c1d0800, 0x37bd2ffc, 0x03a0f021, 0x3c100800, 0x26100860,
+ 0x3c1c0800, 0x279c1a60, 0x0e000546, 0x00000000, 0x0000000d, 0x8f820010,
+ 0x8c450008, 0x24030800, 0xaf430178, 0x97430104, 0x3c020008, 0xaf420140,
+ 0x8f820024, 0x30420001, 0x10400007, 0x3069ffff, 0x24020002, 0x2523fffe,
+ 0xa7420146, 0xa7430148, 0x0a000242, 0x3c020800, 0xa7400146, 0x3c020800,
+ 0x8c43083c, 0x1460000e, 0x24020f00, 0x8f820024, 0x30430020, 0x0003182b,
+ 0x00031823, 0x30650009, 0x30420c00, 0x24030400, 0x14430002, 0x34a40001,
+ 0x34a40005, 0xa744014a, 0x0a000264, 0x3c020800, 0x8f830014, 0x14620008,
+ 0x00000000, 0x8f820024, 0x30420020, 0x0002102b, 0x00021023, 0x3042000d,
+ 0x0a000262, 0x34420005, 0x8f820024, 0x30420020, 0x0002102b, 0x00021023,
+ 0x30420009, 0x34420001, 0xa742014a, 0x3c020800, 0x8c430820, 0x8f840024,
+ 0x3c020048, 0x00621825, 0x30840006, 0x24020002, 0x1082000d, 0x2c820003,
+ 0x50400005, 0x24020004, 0x10800012, 0x3c020001, 0x0a000284, 0x00000000,
+ 0x10820007, 0x24020006, 0x1482000f, 0x3c020111, 0x0a00027c, 0x00621025,
+ 0x0a00027b, 0x3c020101, 0x3c020011, 0x00621025, 0x24030001, 0xaf421000,
+ 0xaf830020, 0x0a000284, 0x00000000, 0x00621025, 0xaf421000, 0xaf800020,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8f830020, 0x1060003f,
+ 0x3c048000, 0x8f421000, 0x00441024, 0x1040fffd, 0x00000000, 0x10600039,
+ 0x00000000, 0x8f421000, 0x3c030020, 0x00431024, 0x10400034, 0x00000000,
+ 0x97421014, 0x14400031, 0x00000000, 0x97421008, 0x8f840010, 0x24420006,
+ 0x00024082, 0x00081880, 0x00643821, 0x8ce50000, 0x30430003, 0x30420001,
+ 0x10400004, 0x00000000, 0x0000000d, 0x0a0002c3, 0x00081080, 0x5460000f,
+ 0x30a5ffff, 0x3c06ffff, 0x00a62824, 0x0005182b, 0x00a61026, 0x0002102b,
+ 0x00621824, 0x10600004, 0x00000000, 0x0000000d, 0x00000000, 0x240001fb,
+ 0x8ce20000, 0x0a0002c2, 0x00462825, 0x0005182b, 0x38a2ffff, 0x0002102b,
+ 0x00621824, 0x10600004, 0x00000000, 0x0000000d, 0x00000000, 0x24000205,
+ 0x8ce20000, 0x3445ffff, 0x00081080, 0x00441021, 0x3c030800, 0xac450000,
+ 0x8c620830, 0x24420001, 0xac620830, 0x8f840018, 0x01202821, 0x24820008,
+ 0x30421fff, 0x24434000, 0x0343d821, 0x30a30007, 0xaf84000c, 0xaf820018,
+ 0xaf420084, 0x10600002, 0x24a20007, 0x3045fff8, 0x8f820030, 0x8f840000,
+ 0x00451821, 0xaf82001c, 0x0064102b, 0xaf830030, 0x14400002, 0x00641023,
+ 0xaf820030, 0x8f840030, 0x34028000, 0x00821021, 0x03421821, 0x3c021000,
+ 0xaf830010, 0xaf440080, 0x03e00008, 0xaf420178, 0x8f830024, 0x27bdffe0,
+ 0xafbf0018, 0xafb10014, 0x30620200, 0x14400004, 0xafb00010, 0x0000000d,
+ 0x00000000, 0x24000242, 0x00031a82, 0x30630003, 0x000310c0, 0x00431021,
+ 0x00021080, 0x00431021, 0x00021080, 0x3c030800, 0x24631aa0, 0x00438821,
+ 0x8e240000, 0x10800004, 0x00000000, 0x0000000d, 0x00000000, 0x2400024d,
+ 0x8f850010, 0x24020001, 0xae220000, 0x8ca70008, 0xa2200007, 0x8f620004,
+ 0x26300014, 0x02002021, 0x00021402, 0xa2220004, 0x304600ff, 0x24c60005,
+ 0x0e000673, 0x00063082, 0x8f620004, 0xa6220008, 0x8f430108, 0x3c021000,
+ 0x00621824, 0x10600008, 0x00000000, 0x97420104, 0x92230007, 0x2442ffec,
+ 0x3045ffff, 0x34630002, 0x0a000321, 0xa2230007, 0x97420104, 0x2442fff0,
+ 0x3045ffff, 0x8f620004, 0x3042ffff, 0x2c420013, 0x54400005, 0x92230007,
+ 0x92220007, 0x34420001, 0xa2220007, 0x92230007, 0x24020001, 0x10620009,
+ 0x28620002, 0x14400014, 0x24020002, 0x10620012, 0x24020003, 0x1062000a,
+ 0x00000000, 0x0a000342, 0x00000000, 0x8f820010, 0x8c43000c, 0x3c04ffff,
+ 0x00641824, 0x00651825, 0x0a000342, 0xac43000c, 0x8f820010, 0x8c430010,
+ 0x3c04ffff, 0x00641824, 0x00651825, 0xac430010, 0x8f620004, 0x3042ffff,
+ 0x24420002, 0x00021083, 0xa2220005, 0x304500ff, 0x8f820010, 0x3c04ffff,
+ 0x00052880, 0x00a22821, 0x8ca70000, 0x96220008, 0x97430104, 0x00e42024,
+ 0x24420002, 0x00621823, 0x00833825, 0xaca70000, 0x92240005, 0x00041080,
+ 0x02021021, 0x90430000, 0x3c05fff6, 0x34a5ffff, 0x3063000f, 0x00832021,
+ 0xa2240006, 0x308200ff, 0x24420003, 0x00021080, 0x02021021, 0x8c460000,
+ 0x308300ff, 0x8f820010, 0x3c04ff3f, 0x00031880, 0x00c53824, 0x00621821,
+ 0xae26000c, 0xac67000c, 0x8e22000c, 0x92230006, 0x3484ffff, 0x00441024,
+ 0x24630003, 0x00031880, 0x02031821, 0x00e42024, 0xae22000c, 0xac640000,
+ 0x92220006, 0x24420004, 0x00021080, 0x02021021, 0x94470002, 0xac470000,
+ 0x92230006, 0x8f820010, 0x00031880, 0x00621821, 0x24020010, 0xac670010,
+ 0x24030002, 0xa7420140, 0xa7400142, 0xa7400144, 0xa7430146, 0x97420104,
+ 0x24030001, 0x2442fffe, 0xa7420148, 0xa743014a, 0x8f820024, 0x24030002,
+ 0x30440006, 0x1083000d, 0x2c820003, 0x10400005, 0x24020004, 0x10800011,
+ 0x3c020009, 0x0a0003a5, 0x00000000, 0x10820007, 0x24020006, 0x1482000d,
+ 0x3c020119, 0x0a00039f, 0x24030001, 0x0a00039e, 0x3c020109, 0x3c020019,
+ 0x24030001, 0xaf421000, 0xaf830020, 0x0a0003a5, 0x00000000, 0xaf421000,
+ 0xaf800020, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x92220004,
+ 0x24030008, 0x8f840020, 0x24420002, 0x30420007, 0x00621823, 0x30630007,
+ 0x10800006, 0xae230010, 0x3c038000, 0x8f421000, 0x00431024, 0x1040fffd,
+ 0x00000000, 0x8f820018, 0xaf82000c, 0x24420010, 0x30421fff, 0xaf820018,
+ 0xaf420084, 0x97430104, 0x24424000, 0x0342d821, 0x3063ffff, 0x30620007,
+ 0x10400002, 0x24620007, 0x3043fff8, 0x8f820030, 0x8f840000, 0x00431821,
+ 0xaf82001c, 0x0064102b, 0xaf830030, 0x14400002, 0x00641023, 0xaf820030,
+ 0x8f840030, 0x34028000, 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x00821021,
+ 0x03421821, 0x3c021000, 0xaf830010, 0xaf440080, 0xaf420178, 0x03e00008,
+ 0x27bd0020, 0x8f830024, 0x27bdffe0, 0xafbf0018, 0xafb10014, 0x30620200,
+ 0x14400004, 0xafb00010, 0x0000000d, 0x00000000, 0x240002e4, 0x00031a82,
+ 0x30630003, 0x000310c0, 0x00431021, 0x00021080, 0x00431021, 0x00021080,
+ 0x3c030800, 0x24631aa0, 0x00438021, 0x8e040000, 0x14800004, 0x00000000,
+ 0x0000000d, 0x00000000, 0x240002e9, 0x8f620004, 0x04410008, 0x26050014,
+ 0x92020006, 0x8e03000c, 0x24420003, 0x00021080, 0x00a21021, 0xac430000,
+ 0xae000000, 0x92020005, 0x24420001, 0x00021080, 0x00a21021, 0x8c430000,
+ 0x3c040001, 0x00641821, 0xac430000, 0x92060004, 0x27710008, 0x02202021,
+ 0x24c60005, 0x0e000673, 0x00063082, 0x92040006, 0x3c057fff, 0x8f620004,
+ 0x00042080, 0x00912021, 0x8c830004, 0x34a5ffff, 0x00451024, 0x00621821,
+ 0xac830004, 0x92050005, 0x3c07ffff, 0x92040004, 0x00052880, 0x00b12821,
+ 0x8ca30000, 0x97420104, 0x96060008, 0x00671824, 0x00441021, 0x00461023,
+ 0x3042ffff, 0x00621825, 0xaca30000, 0x92030007, 0x24020001, 0x1062000a,
+ 0x28620002, 0x1440001d, 0x2402000a, 0x24020002, 0x10620019, 0x24020003,
+ 0x1062000e, 0x2402000a, 0x0a000447, 0x00000000, 0x92020004, 0x97430104,
+ 0x8e24000c, 0x00621821, 0x2463fff2, 0x3063ffff, 0x00872024, 0x00832025,
+ 0xae24000c, 0x0a000447, 0x2402000a, 0x92020004, 0x97430104, 0x8e240010,
+ 0x00621821, 0x2463ffee, 0x3063ffff, 0x00872024, 0x00832025, 0xae240010,
+ 0x2402000a, 0xa7420140, 0x96030012, 0x8f840024, 0xa7430142, 0x92020004,
+ 0xa7420144, 0xa7400146, 0x97430104, 0x30840006, 0x24020001, 0xa7430148,
+ 0xa742014a, 0x24020002, 0x1082000d, 0x2c820003, 0x10400005, 0x24020004,
+ 0x10800011, 0x3c020041, 0x0a00046c, 0x00000000, 0x10820007, 0x24020006,
+ 0x1482000d, 0x3c020151, 0x0a000466, 0x24030001, 0x0a000465, 0x3c020141,
+ 0x3c020051, 0x24030001, 0xaf421000, 0xaf830020, 0x0a00046c, 0x00000000,
+ 0xaf421000, 0xaf800020, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x8f820020, 0x8f840018, 0x10400006, 0x92030004, 0x3c058000, 0x8f421000,
+ 0x00451024, 0x1040fffd, 0x00000000, 0x2463000a, 0x30620007, 0x10400002,
+ 0x24620007, 0x304303f8, 0x00831021, 0x30421fff, 0xaf84000c, 0xaf820018,
+ 0xaf420084, 0x97430104, 0x24424000, 0x0342d821, 0x3063ffff, 0x30620007,
+ 0x10400002, 0x24620007, 0x3043fff8, 0x8f820030, 0x8f840000, 0x00431821,
+ 0xaf82001c, 0x0064102b, 0xaf830030, 0x14400002, 0x00641023, 0xaf820030,
+ 0x8f840030, 0x34028000, 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x00821021,
+ 0x03421821, 0x3c021000, 0xaf830010, 0xaf440080, 0xaf420178, 0x03e00008,
+ 0x27bd0020, 0x8f620000, 0x97430104, 0x3c048000, 0x3045ffff, 0x3066ffff,
+ 0x8f420178, 0x00441024, 0x1440fffd, 0x2402000a, 0x30a30007, 0xa7420140,
+ 0x24020008, 0x00431023, 0x30420007, 0x24a3fffe, 0xa7420142, 0xa7430144,
+ 0xa7400146, 0xa7460148, 0x8f420108, 0x8f830024, 0x30420020, 0x0002102b,
+ 0x00021023, 0x30420009, 0x34420001, 0x30630006, 0xa742014a, 0x24020002,
+ 0x1062000d, 0x2c620003, 0x10400005, 0x24020004, 0x10600011, 0x3c020041,
+ 0x0a0004d6, 0x00000000, 0x10620007, 0x24020006, 0x1462000d, 0x3c020151,
+ 0x0a0004d0, 0x24030001, 0x0a0004cf, 0x3c020141, 0x3c020051, 0x24030001,
+ 0xaf421000, 0xaf830020, 0x0a0004d6, 0x00000000, 0xaf421000, 0xaf800020,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8f820020, 0x24a30008,
+ 0x8f850018, 0x10400006, 0x30c6ffff, 0x3c048000, 0x8f421000, 0x00441024,
+ 0x1040fffd, 0x00000000, 0x3063ffff, 0x30620007, 0x10400002, 0x24620007,
+ 0x3043fff8, 0x00a31021, 0x30421fff, 0x24434000, 0x0343d821, 0x00c02021,
+ 0x30830007, 0xaf85000c, 0xaf820018, 0xaf420084, 0x10600002, 0x24820007,
+ 0x3044fff8, 0x8f820030, 0x8f850000, 0x00441821, 0xaf82001c, 0x0065102b,
+ 0xaf830030, 0x14400002, 0x00651023, 0xaf820030, 0x8f840030, 0x34028000,
+ 0x3c030800, 0x8c650834, 0x00821021, 0x03421821, 0xaf830010, 0xaf440080,
+ 0x10a00006, 0x2402000e, 0x9383002f, 0x14620004, 0x3c021000, 0x2402043f,
+ 0xa7420148, 0x3c021000, 0x03e00008, 0xaf420178, 0x8f820024, 0x30424000,
+ 0x10400005, 0x24020800, 0x0000000d, 0x00000000, 0x2400040e, 0x24020800,
+ 0xaf420178, 0x97440104, 0x3c030008, 0xaf430140, 0x8f820024, 0x30420001,
+ 0x10400006, 0x3085ffff, 0x24020002, 0x24a3fffe, 0xa7420146, 0x0a000526,
+ 0xa7430148, 0xa7400146, 0x8f840018, 0x2402000d, 0xa742014a, 0x24830008,
+ 0x30631fff, 0x24624000, 0x0342d821, 0x30a20007, 0xaf84000c, 0xaf830018,
+ 0xaf430084, 0x10400002, 0x24a20007, 0x3045fff8, 0x8f820030, 0x8f840000,
+ 0x00451821, 0xaf82001c, 0x0064102b, 0xaf830030, 0x14400002, 0x00641023,
+ 0xaf820030, 0x8f840030, 0x34028000, 0x00821021, 0x03421821, 0x3c021000,
+ 0xaf830010, 0xaf440080, 0x03e00008, 0xaf420178, 0x27bdffe8, 0x3c046008,
+ 0xafbf0014, 0xafb00010, 0x8c825000, 0x3c1a8000, 0x2403ff7f, 0x375b4000,
+ 0x00431024, 0x3442380c, 0xac825000, 0x8f430008, 0x3c100800, 0x37428000,
+ 0x34630001, 0xaf430008, 0xaf820010, 0x3c02601c, 0xaf800018, 0xaf400080,
+ 0xaf400084, 0x8c450008, 0x3c036000, 0x8c620808, 0x3c040800, 0x3c030080,
+ 0xac830820, 0x3042fff0, 0x38420010, 0x2c420001, 0xaf850000, 0xaf820004,
+ 0x0e000658, 0x00000000, 0x8f420000, 0x30420001, 0x1040fffb, 0x00000000,
+ 0x8f430108, 0x8f440100, 0x30622000, 0xaf830024, 0xaf840014, 0x10400004,
+ 0x8e02082c, 0x24420001, 0x0a0005c6, 0xae02082c, 0x30620200, 0x14400003,
+ 0x24020f00, 0x14820027, 0x24020d00, 0x97420104, 0x1040001c, 0x30624000,
+ 0x14400005, 0x00000000, 0x0e00022f, 0x00000000, 0x0a0005bb, 0x00000000,
+ 0x8f620008, 0x8f630000, 0x24020030, 0x00031e02, 0x306300f0, 0x10620007,
+ 0x28620031, 0x1440002f, 0x24020040, 0x10620007, 0x00000000, 0x0a0005bb,
+ 0x00000000, 0x0e0002e8, 0x00000000, 0x0a0005bb, 0x00000000, 0x0e0003db,
+ 0x00000000, 0x0a0005bb, 0x00000000, 0x30620040, 0x1440002b, 0x00000000,
+ 0x0000000d, 0x00000000, 0x240004b2, 0x0a0005c6, 0x00000000, 0x1482000f,
+ 0x30620006, 0x97420104, 0x10400005, 0x30620040, 0x0e000510, 0x00000000,
+ 0x0a0005bb, 0x00000000, 0x1440001b, 0x00000000, 0x0000000d, 0x00000000,
+ 0x240004c4, 0x0a0005c6, 0x00000000, 0x1040000e, 0x30621000, 0x10400005,
+ 0x00000000, 0x0e000688, 0x00000000, 0x0a0005bb, 0x00000000, 0x0e0004a1,
+ 0x00000000, 0x8f82002c, 0x24420001, 0xaf82002c, 0x0a0005c6, 0x00000000,
+ 0x30620040, 0x14400004, 0x00000000, 0x0000000d, 0x00000000, 0x240004db,
+ 0x8f420138, 0x3c034000, 0x00431025, 0xaf420138, 0x0a000566, 0x00000000,
+ 0x3c046008, 0x8c835000, 0x3c1a8000, 0x2402ff7f, 0x375b4000, 0x00621824,
+ 0x3463380c, 0xac835000, 0x8f420008, 0x3c056000, 0x3c03601c, 0x34420001,
+ 0xaf420008, 0x37428000, 0xaf800018, 0xaf820010, 0xaf400080, 0xaf400084,
+ 0x8c660008, 0x8ca20808, 0x3c040800, 0x3c030080, 0xac830820, 0x3042fff0,
+ 0x38420010, 0x2c420001, 0xaf860000, 0xaf820004, 0x03e00008, 0x00000000,
+ 0x3084ffff, 0x30820007, 0x10400002, 0x24820007, 0x3044fff8, 0x8f820018,
+ 0x00441821, 0x30631fff, 0x24644000, 0x0344d821, 0xaf82000c, 0xaf830018,
+ 0x03e00008, 0xaf430084, 0x3084ffff, 0x30820007, 0x10400002, 0x24820007,
+ 0x3044fff8, 0x8f820030, 0x8f830000, 0x00442021, 0xaf82001c, 0x0083102b,
+ 0xaf840030, 0x14400002, 0x00831023, 0xaf820030, 0x8f820030, 0x34038000,
+ 0x00431821, 0x03432021, 0xaf840010, 0x03e00008, 0xaf420080, 0x8f830024,
+ 0x24020002, 0x30630006, 0x1062000d, 0x2c620003, 0x50400005, 0x24020004,
+ 0x10600012, 0x3c020001, 0x0a00062a, 0x00000000, 0x10620007, 0x24020006,
+ 0x1462000f, 0x3c020111, 0x0a000622, 0x00821025, 0x0a000621, 0x3c020101,
+ 0x3c020011, 0x00821025, 0x24030001, 0xaf421000, 0xaf830020, 0x0a00062a,
+ 0x00000000, 0x00821025, 0xaf421000, 0xaf800020, 0x00000000, 0x00000000,
+ 0x00000000, 0x03e00008, 0x00000000, 0x8f820020, 0x10400005, 0x3c038000,
+ 0x8f421000, 0x00431024, 0x1040fffd, 0x00000000, 0x03e00008, 0x00000000,
+ 0x8f820024, 0x27bdffe8, 0x30424000, 0x14400005, 0xafbf0010, 0x0e00022f,
+ 0x00000000, 0x0a000656, 0x8fbf0010, 0x8f620008, 0x8f630000, 0x24020030,
+ 0x00031e02, 0x306300f0, 0x10620008, 0x28620031, 0x1440000d, 0x8fbf0010,
+ 0x24020040, 0x10620007, 0x00000000, 0x0a000656, 0x00000000, 0x0e0002e8,
+ 0x00000000, 0x0a000656, 0x8fbf0010, 0x0e0003db, 0x00000000, 0x8fbf0010,
+ 0x03e00008, 0x27bd0018, 0x8f840028, 0x1080000f, 0x3c026000, 0x8c430c3c,
+ 0x30630fff, 0xaf830008, 0x14600011, 0x3082000f, 0x10400005, 0x308200f0,
+ 0x10400003, 0x30820f00, 0x14400006, 0x00000000, 0x0000000d, 0x00000000,
+ 0x2400051a, 0x03e00008, 0x00000000, 0x0000000d, 0x00000000, 0x2400051f,
+ 0x03e00008, 0x00000000, 0xaf830028, 0x03e00008, 0x00000000, 0x10c00007,
+ 0x00000000, 0x8ca20000, 0x24c6ffff, 0x24a50004, 0xac820000, 0x14c0fffb,
+ 0x24840004, 0x03e00008, 0x00000000, 0x0a000684, 0x00a01021, 0xac860000,
+ 0x00000000, 0x00000000, 0x24840004, 0x00a01021, 0x1440fffa, 0x24a5ffff,
+ 0x03e00008, 0x00000000, 0x0000000d, 0x03e00008, 0x00000000, 0x00000000};
+
+static u32 bnx2_TPAT_b06FwData[(0x0/4) + 1] = { 0x0 };
+static u32 bnx2_TPAT_b06FwRodata[(0x0/4) + 1] = { 0x0 };
+static u32 bnx2_TPAT_b06FwBss[(0x250/4) + 1] = { 0x0 };
+static u32 bnx2_TPAT_b06FwSbss[(0x34/4) + 1] = { 0x0 };
+
+static const int bnx2_TXP_b06FwReleaseMajor = 0x1;
+static const int bnx2_TXP_b06FwReleaseMinor = 0x0;
+static const int bnx2_TXP_b06FwReleaseFix = 0x0;
+static const u32 bnx2_TXP_b06FwStartAddr = 0x080034b0;
+static const u32 bnx2_TXP_b06FwTextAddr = 0x08000000;
+static const int bnx2_TXP_b06FwTextLen = 0x5748;
+static const u32 bnx2_TXP_b06FwDataAddr = 0x08005760;
+static const int bnx2_TXP_b06FwDataLen = 0x0;
+static const u32 bnx2_TXP_b06FwRodataAddr = 0x00000000;
+static const int bnx2_TXP_b06FwRodataLen = 0x0;
+static const u32 bnx2_TXP_b06FwBssAddr = 0x080057a0;
+static const int bnx2_TXP_b06FwBssLen = 0x1c4;
+static const u32 bnx2_TXP_b06FwSbssAddr = 0x08005760;
+static const int bnx2_TXP_b06FwSbssLen = 0x38;
+static u32 bnx2_TXP_b06FwText[(0x5748/4) + 1] = {
+ 0x0a000d2c, 0x00000000, 0x00000000, 0x0000000d, 0x74787020, 0x322e352e,
+ 0x38000000, 0x02050800, 0x0000000a, 0x000003e8, 0x0000ea60, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c020800,
+ 0x24425760, 0x3c030800, 0x24635964, 0xac400000, 0x0043202b, 0x1480fffd,
+ 0x24420004, 0x3c1d0800, 0x37bd7ffc, 0x03a0f021, 0x3c100800, 0x261034b0,
+ 0x3c1c0800, 0x279c5760, 0x0e000f5b, 0x00000000, 0x0000000d, 0x8f840014,
+ 0x27bdffe8, 0xafb10014, 0xafb00010, 0x8f460104, 0x8f830008, 0x8c8500ac,
+ 0xaf430080, 0x948200a8, 0xa7420e10, 0x948300aa, 0xa7430e12, 0x8c8200ac,
+ 0xaf420e18, 0x97430e10, 0xa7430e14, 0x97420e12, 0x00008021, 0xa7420e16,
+ 0x8f430e18, 0x00006021, 0x00c53023, 0xaf430e1c, 0x10c001a2, 0x2d820001,
+ 0x3c0e1000, 0x2419fff8, 0x24110010, 0x240f0f00, 0x3c188100, 0x93620008,
+ 0x10400009, 0x00000000, 0x97620010, 0x00c2102b, 0x14400005, 0x00000000,
+ 0x97620010, 0x3042ffff, 0x0a000d6d, 0xaf420e00, 0xaf460e00, 0x8f420000,
+ 0x30420008, 0x1040fffd, 0x00000000, 0x97420e08, 0x8f450e04, 0x3044ffff,
+ 0x30820001, 0x14400005, 0x00000000, 0x14a00005, 0x3083a040, 0x0a000f34,
+ 0x00000000, 0x0000000d, 0x3083a040, 0x24020040, 0x1462004f, 0x3082a000,
+ 0x308a0036, 0x8f88000c, 0x30890008, 0x24020800, 0xaf420178, 0x01001821,
+ 0x9742008a, 0x00431023, 0x2442ffff, 0x30421fff, 0x2c420008, 0x1440fffa,
+ 0x00a06021, 0x8f820018, 0x00cc3023, 0x24070001, 0x8f830008, 0x304b00ff,
+ 0x24420001, 0xaf820018, 0x25024000, 0x106f0005, 0x03422021, 0x93820012,
+ 0x30420007, 0x00021240, 0x34470001, 0x000b1400, 0x3c030100, 0x00431025,
+ 0xac820000, 0x8f830018, 0x00ea3825, 0x1120000f, 0xac830004, 0x97430e0a,
+ 0x8f84000c, 0x00ee3825, 0x2402000e, 0x00781825, 0xaf430160, 0x25830006,
+ 0x24840008, 0x30841fff, 0xa742015a, 0xa7430158, 0xaf84000c, 0x0a000db7,
+ 0x00000000, 0x8f83000c, 0x25820002, 0xa7420158, 0x24630008, 0x30631fff,
+ 0xaf83000c, 0x54c0000f, 0x8f420e14, 0x8f820008, 0x504f0002, 0x24100001,
+ 0x34e70040, 0x97420e10, 0x97430e12, 0x8f850014, 0x00021400, 0x00621825,
+ 0xaca300a8, 0x8f840014, 0x8f420e18, 0xac8200ac, 0x8f420e14, 0x8f430e1c,
+ 0xaf420144, 0xaf430148, 0xa34b0152, 0xaf470154, 0x0a000efb, 0xaf4e0178,
+ 0x10400165, 0x00000000, 0x93620008, 0x50400008, 0xafa60008, 0x97620010,
+ 0x00a2102b, 0x10400003, 0x30820040, 0x1040015c, 0x00000000, 0xafa60008,
+ 0xa7840010, 0xaf850004, 0x93620008, 0x1440005f, 0x27ac0008, 0xaf60000c,
+ 0x97820010, 0x30424000, 0x10400002, 0x2403000e, 0x24030016, 0xa363000a,
+ 0x24034007, 0xaf630014, 0x93820012, 0x8f630014, 0x30420007, 0x00021240,
+ 0x00621825, 0xaf630014, 0x97820010, 0x8f630014, 0x30420010, 0x00621825,
+ 0xaf630014, 0x97820010, 0x30420008, 0x5040000e, 0x00002821, 0x8f620014,
+ 0x004e1025, 0xaf620014, 0x97430e0a, 0x2402000e, 0x00781825, 0xaf630004,
+ 0xa3620002, 0x9363000a, 0x3405fffc, 0x24630004, 0x0a000e06, 0xa363000a,
+ 0xaf600004, 0xa3600002, 0x97820010, 0x9363000a, 0x30421f00, 0x00021182,
+ 0x24420028, 0x00621821, 0xa3630009, 0x97420e0c, 0xa7620010, 0x93630009,
+ 0x24020008, 0x24630002, 0x30630007, 0x00431023, 0x30420007, 0xa362000b,
+ 0x93640009, 0x97620010, 0x8f890004, 0x97830010, 0x00441021, 0x00a21021,
+ 0x30630040, 0x10600007, 0x3045ffff, 0x00a9102b, 0x14400005, 0x0125102b,
+ 0x3c068000, 0x0a000e3a, 0x00005821, 0x0125102b, 0x544000c7, 0x00006021,
+ 0x97420e14, 0xa7420e10, 0x97430e16, 0xa7430e12, 0x8f420e1c, 0xaf420e18,
+ 0xaf450e00, 0x8f420000, 0x30420008, 0x1040fffd, 0x00000000, 0x97420e08,
+ 0x00a04821, 0xa7820010, 0x8f430e04, 0x00003021, 0x240b0001, 0xaf830004,
+ 0x97620010, 0x0a000e4c, 0x304dffff, 0x8f890004, 0x97820010, 0x30420040,
+ 0x10400004, 0x01206821, 0x3c068000, 0x0a000e4c, 0x00005821, 0x97630010,
+ 0x8f820004, 0x10430003, 0x00003021, 0x0a000eee, 0x00006021, 0x240b0001,
+ 0x8d820000, 0x00491023, 0x1440000d, 0xad820000, 0x8f620014, 0x34420040,
+ 0xaf620014, 0x97430e10, 0x97420e12, 0x8f840014, 0x00031c00, 0x00431025,
+ 0xac8200a8, 0x8f830014, 0x8f420e18, 0xac6200ac, 0x93620008, 0x1440003e,
+ 0x00000000, 0x25260002, 0x8f84000c, 0x9743008a, 0x3063ffff, 0xafa30000,
+ 0x8fa20000, 0x00441023, 0x2442ffff, 0x30421fff, 0x2c420010, 0x1440fff7,
+ 0x00000000, 0x8f82000c, 0x8f830018, 0x00021082, 0x00021080, 0x24424000,
+ 0x03422821, 0x00605021, 0x24630001, 0x314200ff, 0x00021400, 0xaf830018,
+ 0x3c033200, 0x00431025, 0xaca20000, 0x93630009, 0x9362000a, 0x00031c00,
+ 0x00431025, 0xaca20004, 0x8f830018, 0xaca30008, 0x97820010, 0x30420008,
+ 0x10400002, 0x00c04021, 0x25280006, 0x97430e14, 0x93640002, 0x8f450e1c,
+ 0x8f660004, 0x8f670014, 0x3063ffff, 0xa7430144, 0x97420e16, 0xa7420146,
+ 0xaf450148, 0xa34a0152, 0x8f82000c, 0x308400ff, 0xa744015a, 0xaf460160,
+ 0xa7480158, 0xaf470154, 0xaf4e0178, 0x00511021, 0x30421fff, 0xaf82000c,
+ 0x0a000ed9, 0x8d820000, 0x93620009, 0x9363000b, 0x8f85000c, 0x2463000a,
+ 0x00435021, 0x25440007, 0x00992024, 0x9743008a, 0x3063ffff, 0xafa30000,
+ 0x8fa20000, 0x00451023, 0x2442ffff, 0x30421fff, 0x0044102b, 0x1440fff7,
+ 0x00000000, 0x8f82000c, 0x8f840018, 0x00021082, 0x00021080, 0x24424000,
+ 0x03422821, 0x00804021, 0x24840001, 0xaf840018, 0x93630009, 0x310200ff,
+ 0x00022400, 0x3c024100, 0x24630002, 0x00621825, 0x00832025, 0xaca40000,
+ 0x8f62000c, 0x00461025, 0xaca20004, 0x97430e14, 0x93640002, 0x8f450e1c,
+ 0x8f660004, 0x8f670014, 0x3063ffff, 0xa7430144, 0x97420e16, 0x308400ff,
+ 0xa7420146, 0xaf450148, 0xa3480152, 0x8f83000c, 0x25420007, 0x00591024,
+ 0xa744015a, 0xaf460160, 0xa7490158, 0xaf470154, 0xaf4e0178, 0x00621821,
+ 0x30631fff, 0xaf83000c, 0x8d820000, 0x14400005, 0x00000000, 0x8f620014,
+ 0x2403ffbf, 0x00431024, 0xaf620014, 0x8f62000c, 0x004d1021, 0xaf62000c,
+ 0x93630008, 0x14600008, 0x00000000, 0x11600006, 0x00000000, 0x8f630014,
+ 0x3c02efff, 0x3442fffe, 0x00621824, 0xaf630014, 0xa36b0008, 0x01206021,
+ 0x1580000c, 0x8fa60008, 0x97420e14, 0x97430e16, 0x8f850014, 0x00021400,
+ 0x00621825, 0xaca300a8, 0x8f840014, 0x8f420e1c, 0xac8200ac, 0x0a000efd,
+ 0x2d820001, 0x14c0fe65, 0x2d820001, 0x00501025, 0x10400058, 0x24020f00,
+ 0x8f830008, 0x14620023, 0x3c048000, 0x11800009, 0x3c038000, 0x97420e08,
+ 0x30420040, 0x14400005, 0x00000000, 0x0000000d, 0x00000000, 0x2400032c,
+ 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, 0x00000000, 0x97420e10,
+ 0x3c030500, 0x00431025, 0xaf42014c, 0x97430e14, 0xa7430144, 0x97420e16,
+ 0xa7420146, 0x8f430e1c, 0x24022000, 0xaf430148, 0x3c031000, 0xa3400152,
+ 0xa740015a, 0xaf400160, 0xa7400158, 0xaf420154, 0xaf430178, 0x8f830008,
+ 0x3c048000, 0x8f420178, 0x00441024, 0x1440fffd, 0x24020f00, 0x10620016,
+ 0x00000000, 0x97420e14, 0xa7420144, 0x97430e16, 0xa7430146, 0x8f420e1c,
+ 0x3c031000, 0xaf420148, 0x0a000f51, 0x24020240, 0x97420e14, 0x97430e16,
+ 0x8f840014, 0x00021400, 0x00621825, 0xac8300a8, 0x8f850014, 0x8f420e1c,
+ 0x00006021, 0xaca200ac, 0x0a000efd, 0x2d820001, 0xaf40014c, 0x11800007,
+ 0x00000000, 0x97420e10, 0xa7420144, 0x97430e12, 0xa7430146, 0x0a000f4e,
+ 0x8f420e18, 0x97420e14, 0xa7420144, 0x97430e16, 0xa7430146, 0x8f420e1c,
+ 0xaf420148, 0x24020040, 0x3c031000, 0xa3400152, 0xa740015a, 0xaf400160,
+ 0xa7400158, 0xaf420154, 0xaf430178, 0x8fb10014, 0x8fb00010, 0x03e00008,
+ 0x27bd0018, 0x27bdffd0, 0x3c1a8000, 0x3c0420ff, 0x3484fffd, 0x3c020008,
+ 0x03421821, 0xafbf002c, 0xafb60028, 0xafb50024, 0xafb40020, 0xafb3001c,
+ 0xafb20018, 0xafb10014, 0xafb00010, 0xaf830014, 0xaf440e00, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3c0200ff, 0x3442fffd,
+ 0x3c046004, 0xaf420e00, 0x8c835000, 0x24160800, 0x24150d00, 0x3c140800,
+ 0x24130f00, 0x3c120800, 0x3c114000, 0x2402ff7f, 0x00621824, 0x3463380c,
+ 0x24020009, 0xac835000, 0xaf420008, 0xaf800018, 0xaf80000c, 0x0e001559,
+ 0x00000000, 0x0e000ff0, 0x00000000, 0x3c020800, 0x245057c0, 0x8f420000,
+ 0x30420001, 0x1040fffd, 0x00000000, 0x8f440100, 0xaf840008, 0xaf440020,
+ 0xaf560178, 0x93430108, 0xa3830012, 0x93820012, 0x30420001, 0x10400008,
+ 0x00000000, 0x93820012, 0x30420006, 0x00021100, 0x0e000d43, 0x0050d821,
+ 0x0a000fac, 0x00000000, 0x14950005, 0x00000000, 0x0e000d43, 0x269b5840,
+ 0x0a000fac, 0x00000000, 0x14930005, 0x00000000, 0x0e000d43, 0x265b5860,
+ 0x0a000fac, 0x00000000, 0x0e0010ea, 0x00000000, 0xaf510138, 0x0a000f89,
+ 0x00000000, 0x27bdfff8, 0x3084ffff, 0x24820007, 0x3044fff8, 0x8f85000c,
+ 0x9743008a, 0x3063ffff, 0xafa30000, 0x8fa20000, 0x00451023, 0x2442ffff,
+ 0x30421fff, 0x0044102b, 0x1440fff7, 0x00000000, 0x8f82000c, 0x00021082,
+ 0x00021080, 0x24424000, 0x03421021, 0x03e00008, 0x27bd0008, 0x3084ffff,
+ 0x8f82000c, 0x24840007, 0x3084fff8, 0x00441021, 0x30421fff, 0xaf82000c,
+ 0x03e00008, 0x00000000, 0x27bdffe8, 0x3c1a8000, 0x3c0420ff, 0x3484fffd,
+ 0x3c020008, 0x03421821, 0xafbf0010, 0xaf830014, 0xaf440e00, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3c0200ff, 0x3442fffd,
+ 0x3c046004, 0xaf420e00, 0x8c825000, 0x2403ff7f, 0x00431024, 0x3442380c,
+ 0x24030009, 0xac825000, 0xaf430008, 0xaf800018, 0xaf80000c, 0x0e001559,
+ 0x00000000, 0x0e000ff0, 0x00000000, 0x8fbf0010, 0x03e00008, 0x27bd0018,
+ 0x27bdffe8, 0x3c02000a, 0x03421821, 0x3c040800, 0x24845880, 0x24050019,
+ 0xafbf0010, 0xaf830024, 0x0e001565, 0x00003021, 0x3c050800, 0x3c020800,
+ 0x24425330, 0xaca258e8, 0x24a558e8, 0x3c020800, 0x244254f8, 0x3c030800,
+ 0x2463550c, 0x3c040800, 0xaca20004, 0x3c020800, 0x24425338, 0xaca30008,
+ 0xac825900, 0x24845900, 0x3c020800, 0x244253c4, 0x3c070800, 0x24e75404,
+ 0x3c060800, 0x24c65520, 0x3c050800, 0x24a55438, 0x3c030800, 0xac820004,
+ 0x3c020800, 0x24425528, 0xac870008, 0xac86000c, 0xac850010, 0xac625920,
+ 0x24635920, 0x8fbf0010, 0x3c020800, 0x24425540, 0xac620004, 0x3c020800,
+ 0xac670008, 0xac66000c, 0xac650010, 0xac400048, 0x03e00008, 0x27bd0018,
+ 0x974309da, 0x00804021, 0xad030000, 0x8f4209dc, 0xad020004, 0x8f4309e0,
+ 0xad030008, 0x934409d9, 0x24020001, 0x30840003, 0x1082001f, 0x30a900ff,
+ 0x28820002, 0x10400005, 0x24020002, 0x10800009, 0x3c0a0800, 0x0a001078,
+ 0x93420934, 0x1082000b, 0x24020003, 0x10820026, 0x3c0a0800, 0x0a001078,
+ 0x93420934, 0x974209e4, 0x00021400, 0x34420800, 0xad02000c, 0x0a001077,
+ 0x25080010, 0x974209e4, 0x00021400, 0x34428100, 0xad02000c, 0x974309e8,
+ 0x3c0a0800, 0x00031c00, 0x34630800, 0xad030010, 0x0a001077, 0x25080014,
+ 0x974409e4, 0x3c050800, 0x24a25880, 0x9443001c, 0x94460014, 0x94470010,
+ 0x00a05021, 0x24020800, 0xad000010, 0xad020014, 0x00042400, 0x00661821,
+ 0x00671823, 0x2463fff2, 0x00832025, 0xad04000c, 0x0a001077, 0x25080018,
+ 0x974209e4, 0x3c050800, 0x00021400, 0x34428100, 0xad02000c, 0x974409e8,
+ 0x24a25880, 0x9443001c, 0x94460014, 0x94470010, 0x00a05021, 0x24020800,
+ 0xad000014, 0xad020018, 0x00042400, 0x00661821, 0x00671823, 0x2463ffee,
+ 0x00832025, 0xad040010, 0x2508001c, 0x93420934, 0x93450921, 0x3c074000,
+ 0x25445880, 0x94830018, 0x94860014, 0x00021082, 0x00021600, 0x00052c00,
+ 0x00a72825, 0x00451025, 0x00661821, 0x00431025, 0xad020000, 0x9783002c,
+ 0x974209ea, 0x00621821, 0x00031c00, 0xad030004, 0x9782002c, 0x24420001,
+ 0x30427fff, 0xa782002c, 0x93430920, 0x3c020006, 0x00031e00, 0x00621825,
+ 0xad030008, 0x8f42092c, 0xad02000c, 0x8f430930, 0xad030010, 0x8f440938,
+ 0x25080014, 0xad040000, 0x8f820020, 0x11200004, 0xad020004, 0x8f420940,
+ 0x0a0010a1, 0x2442ffff, 0x8f420940, 0xad020008, 0x8f440948, 0x8f420940,
+ 0x93430936, 0x00823023, 0x00663006, 0x3402ffff, 0x0046102b, 0x54400001,
+ 0x3406ffff, 0x93420937, 0x25445880, 0x90830024, 0xad000010, 0x00021700,
+ 0x34630010, 0x00031c00, 0x00431025, 0x00461025, 0xad02000c, 0x8c830008,
+ 0x14600031, 0x25080014, 0x3c020800, 0x8c430048, 0x1060002d, 0x00000000,
+ 0x9342010b, 0xad020000, 0x8f830000, 0x8c6200b0, 0xad020004, 0x8f830000,
+ 0x8c6200b4, 0xad020008, 0x8f830000, 0x8c6200c0, 0xad02000c, 0x8f830000,
+ 0x8c6200c4, 0xad020010, 0x8f830000, 0x8c6200c8, 0xad020014, 0x8f830000,
+ 0x8c6200cc, 0xad020018, 0x8f830000, 0x8c6200e0, 0xad02001c, 0x8f830000,
+ 0x8c6200e8, 0xad020020, 0x8f830000, 0x8c6200f0, 0x3c04600e, 0xad020024,
+ 0x8c8200d0, 0xad020028, 0x8c8300d4, 0xad03002c, 0x8f820028, 0x3c046012,
+ 0xad020030, 0x8c8200a8, 0xad020034, 0x8c8300ac, 0x3c026000, 0xad030038,
+ 0x8c434448, 0xad03003c, 0x03e00008, 0x01001021, 0x27bdffa8, 0x3c020008,
+ 0x03423021, 0xafbf0054, 0xafbe0050, 0xafb7004c, 0xafb60048, 0xafb50044,
+ 0xafb40040, 0xafb3003c, 0xafb20038, 0xafb10034, 0xafb00030, 0xaf860000,
+ 0x24020040, 0xaf420814, 0xaf400810, 0x8f420944, 0x8f430950, 0x8f440954,
+ 0x8f45095c, 0xaf820034, 0xaf830020, 0xaf84001c, 0xaf850030, 0x90c20000,
+ 0x24030020, 0x304400ff, 0x10830005, 0x24020030, 0x10820022, 0x3c030800,
+ 0x0a001139, 0x8c62002c, 0x24020088, 0xaf420818, 0x3c020800, 0x244258e8,
+ 0xafa20020, 0x93430109, 0x3c020800, 0x10600009, 0x24575900, 0x3c026000,
+ 0x24030100, 0xac43081c, 0x3c030001, 0xac43081c, 0x0000000d, 0x00000000,
+ 0x24000376, 0x9342010a, 0x30420080, 0x14400021, 0x24020800, 0x3c026000,
+ 0x24030100, 0xac43081c, 0x3c030001, 0xac43081c, 0x0000000d, 0x00000000,
+ 0x2400037d, 0x0a001141, 0x24020800, 0x93430109, 0x3063007f, 0x00031140,
+ 0x000318c0, 0x00431021, 0x24430088, 0xaf430818, 0x0000000d, 0x3c020800,
+ 0x24425940, 0x3c030800, 0x24775950, 0x0a001140, 0xafa20020, 0x24420001,
+ 0xac62002c, 0x0000000d, 0x00000000, 0x24000395, 0x0a0014c1, 0x8fbf0054,
+ 0x24020800, 0xaf420178, 0x8f450104, 0x8f420988, 0x00a21023, 0x58400005,
+ 0x8f4309a0, 0x0000000d, 0x00000000, 0x240003b1, 0x8f4309a0, 0x3c100800,
+ 0xae0358b0, 0x8f4209a4, 0x8f830020, 0x260458b0, 0x2491ffd0, 0xae220034,
+ 0x00a21023, 0xae230028, 0xac82ffd0, 0x8fa30020, 0x8c620000, 0x0040f809,
+ 0x0200b021, 0x00409021, 0x32440010, 0x32420002, 0x10400007, 0xafa40024,
+ 0x8e220020, 0x32530040, 0x2403ffbf, 0x00431024, 0x0a001493, 0xae220020,
+ 0x32420020, 0x10400002, 0x3c020800, 0x24575920, 0x32420001, 0x14400007,
+ 0x00000000, 0x8f820008, 0xaf420080, 0x8ec358b0, 0xaf430e10, 0x8e220034,
+ 0xaf420e18, 0x9343010b, 0x93420905, 0x30420008, 0x1040003c, 0x307400ff,
+ 0x8f820000, 0x8c430074, 0x0460000a, 0x00000000, 0x3c026000, 0x24030100,
+ 0xac43081c, 0x3c030001, 0xac43081c, 0x0000000d, 0x00000000, 0x240003ed,
+ 0x8f820000, 0x9044007b, 0x9343010a, 0x14830027, 0x32530040, 0x00003821,
+ 0x24052000, 0x3c090800, 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd,
+ 0x8ec258b0, 0x26c458b0, 0x2484ffd0, 0xaf420144, 0x8c820034, 0x3c030100,
+ 0xaf420148, 0x24020047, 0xaf43014c, 0xa3420152, 0x8d230030, 0x3c021000,
+ 0xa7470158, 0xaf450154, 0xaf420178, 0x8c860034, 0x24630001, 0xad230030,
+ 0x9342010a, 0x3c030047, 0xafa50014, 0x00021600, 0x00431025, 0x00471025,
+ 0xafa20010, 0x9343010b, 0xafa30018, 0x8f440100, 0x8f450104, 0x0e00159b,
+ 0x3c070100, 0x3c050800, 0x24a25880, 0x0a001250, 0x8c430020, 0x32820002,
+ 0x10400050, 0x00000000, 0x0e0015b9, 0x32530040, 0x3c039000, 0x34630001,
+ 0x8f820008, 0x3c048000, 0x00431025, 0xaf420020, 0x8f420020, 0x00441024,
+ 0x1440fffd, 0x00000000, 0x8f830000, 0x90620005, 0x34420008, 0xa0620005,
+ 0x8f840000, 0x8c820074, 0x3c038000, 0x00431025, 0xac820074, 0x90830000,
+ 0x24020020, 0x10620004, 0x00000000, 0x0000000d, 0x00000000, 0x2400040b,
+ 0x8f830008, 0x3c028000, 0x34420001, 0x00621825, 0xaf430020, 0x9084007b,
+ 0x9342010a, 0x14820028, 0x3c030800, 0x00003821, 0x24052000, 0x3c090800,
+ 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, 0x8ec258b0, 0x26c458b0,
+ 0x2484ffd0, 0xaf420144, 0x8c820034, 0x3c030100, 0xaf420148, 0x24020046,
+ 0xaf43014c, 0xa3420152, 0x8d230030, 0x3c021000, 0xa7470158, 0xaf450154,
+ 0xaf420178, 0x8c860034, 0x24630001, 0xad230030, 0x9342010a, 0x3c030046,
+ 0xafa50014, 0x00021600, 0x00431025, 0x00471025, 0xafa20010, 0x9343010b,
+ 0xafa30018, 0x8f440100, 0x8f450104, 0x0e00159b, 0x3c070100, 0x3c030800,
+ 0x24625880, 0x0a001250, 0x8c430020, 0x93420108, 0x30420010, 0x50400056,
+ 0x9343093f, 0x8f860000, 0x90c2007f, 0x8cc30178, 0x304800ff, 0x15030004,
+ 0x00000000, 0x0000000d, 0x00000000, 0x24000425, 0x90c2007e, 0x90c40080,
+ 0x00081c00, 0x00021600, 0x00431025, 0x00042200, 0x90c3007a, 0x90c5000a,
+ 0x00441025, 0x11050028, 0x00623825, 0xa0c8000a, 0x00004021, 0x24056000,
+ 0x3c090800, 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, 0x8ec258b0,
+ 0x26c458b0, 0x2484ffd0, 0xaf420144, 0x8c820034, 0xaf420148, 0x24020052,
+ 0xaf47014c, 0xa3420152, 0x8d230030, 0x3c021000, 0xa7480158, 0xaf450154,
+ 0xaf420178, 0x8c860034, 0x24630001, 0xad230030, 0x9342010a, 0x3c030052,
+ 0xafa50014, 0x00021600, 0x00431025, 0x00481025, 0xafa20010, 0x9343010b,
+ 0xafa30018, 0x8f440100, 0x0e00159b, 0x8f450104, 0x0a00124a, 0x00000000,
+ 0x3c026000, 0x24030100, 0xac43081c, 0x3c030001, 0xac43081c, 0x0000000d,
+ 0x00000000, 0x2400043e, 0x16800009, 0x3c050800, 0x3c040800, 0x24825880,
+ 0x8c430020, 0x32530040, 0x2404ffbf, 0x00641824, 0x0a001493, 0xac430020,
+ 0x8ca25880, 0x10400005, 0x3c030800, 0x8c620034, 0xaca05880, 0x24420001,
+ 0xac620034, 0x9343093f, 0x24020012, 0x5462000e, 0x97420908, 0x32820038,
+ 0x14400009, 0x3c030800, 0x8f830000, 0x8c62004c, 0xac62005c, 0x3c020800,
+ 0x24445880, 0x8c820020, 0x0a001285, 0x32530040, 0xac605880, 0x97420908,
+ 0x5440001c, 0x97420908, 0x3c039000, 0x34630001, 0x8f820008, 0x32530040,
+ 0x3c048000, 0x00431025, 0xaf420020, 0x8f420020, 0x00441024, 0x1440fffd,
+ 0x3c028000, 0x8f840000, 0x8f850008, 0x8c830050, 0x34420001, 0x00a22825,
+ 0xaf830020, 0xac830070, 0xac83005c, 0xaf450020, 0x3c050800, 0x24a45880,
+ 0x8c820020, 0x2403ffbf, 0x00431024, 0x0a001493, 0xac820020, 0x000211c0,
+ 0xaf420024, 0x97420908, 0x3c030080, 0x34630003, 0x000211c0, 0xaf42080c,
+ 0xaf43081c, 0x974209ec, 0x8f4309a4, 0xa782002c, 0x3c020800, 0x24445880,
+ 0xac83002c, 0x93420937, 0x93430934, 0x00021080, 0x00621821, 0xa4830018,
+ 0x934209d8, 0x32850038, 0xafa50028, 0x00621821, 0xa483001a, 0x934209d8,
+ 0x93430934, 0x3c1e0800, 0x00809821, 0x00431021, 0x24420010, 0xa4820016,
+ 0x24020006, 0xae620020, 0x8fa20028, 0x10400003, 0x0000a821, 0x0a0012f0,
+ 0x24120008, 0x8f420958, 0x8f830020, 0x8f840030, 0x00431023, 0x00832023,
+ 0x04800003, 0xae620004, 0x04410003, 0x0082102b, 0x0a0012bc, 0xae600004,
+ 0x54400001, 0xae640004, 0x8ee20000, 0x0040f809, 0x00000000, 0x00409021,
+ 0x32420001, 0x5440001e, 0x8ee20004, 0x8e630008, 0x1060002b, 0x3c02c000,
+ 0x00621025, 0xaf420e00, 0x8f420000, 0x30420008, 0x1040fffd, 0x00000000,
+ 0x97420e08, 0xa7820010, 0x8f430e04, 0x8e620008, 0xaf830004, 0x8f840004,
+ 0x0044102b, 0x1040000b, 0x24150001, 0x24020100, 0x3c016000, 0xac22081c,
+ 0x3c020001, 0x3c016000, 0xac22081c, 0x0000000d, 0x00000000, 0x240004cd,
+ 0x24150001, 0x8ee20004, 0x0040f809, 0x00000000, 0x02429025, 0x32420002,
+ 0x5040001d, 0x8f470940, 0x12a00006, 0x8ec258b0, 0x8f830000, 0xac6200a8,
+ 0x8f840000, 0x8e620034, 0xac8200ac, 0x32420004, 0x50400013, 0x8f470940,
+ 0x3c020800, 0x3283007d, 0x10600110, 0x24575920, 0x32820001, 0x50400006,
+ 0x36520002, 0x8f830034, 0x8f420940, 0x10620109, 0x00000000, 0x36520002,
+ 0x24020008, 0xa6600010, 0xa6620012, 0xae600008, 0xa2600024, 0x8f470940,
+ 0x3c030800, 0x24685880, 0x8d02002c, 0x8d050008, 0x95040010, 0x9506000a,
+ 0x95030026, 0x00451021, 0x00862021, 0x00641821, 0xaf870034, 0xad02002c,
+ 0x32820030, 0x10400008, 0xa5030014, 0x91020024, 0x32910040, 0x34420004,
+ 0xa1020024, 0xaf400048, 0x0a001345, 0x3c040800, 0x93420923, 0x30420002,
+ 0x10400029, 0x32910040, 0x8f830000, 0x8f840020, 0x8c620084, 0x00441023,
+ 0x0442000a, 0x3c039000, 0x95020014, 0x8c630084, 0x00821021, 0x00621823,
+ 0x1c600004, 0x3c039000, 0x91020024, 0x34420001, 0xa1020024, 0x34630001,
+ 0x8f820008, 0x32910040, 0x3c048000, 0x00431025, 0xaf420020, 0x8f420020,
+ 0x00441024, 0x1440fffd, 0x00000000, 0x8f840000, 0x9083003f, 0x2402000a,
+ 0x10620005, 0x2402000c, 0x9083003f, 0x24020008, 0x14620002, 0x24020014,
+ 0xa082003f, 0x8f830008, 0x3c028000, 0x34420001, 0x00621825, 0xaf430020,
+ 0x3c040800, 0x24865880, 0x94c20010, 0x94c3001a, 0x8cc40008, 0x00432821,
+ 0x14800006, 0xa4c5001c, 0x3c020800, 0x8c430048, 0x10600002, 0x24a20040,
+ 0xa4c2001c, 0x27d05880, 0x9604001c, 0x96020012, 0x00822021, 0x24840002,
+ 0x0e000faf, 0x3084ffff, 0x8f850018, 0x00a01821, 0xa2030025, 0x8ee60008,
+ 0x00402021, 0x24a50001, 0xaf850018, 0x00c0f809, 0x00000000, 0x00402021,
+ 0x0e001026, 0x02202821, 0x8ee3000c, 0x0060f809, 0x00402021, 0x9604001c,
+ 0x96020012, 0x00822021, 0x24840002, 0x0e000fc5, 0x3084ffff, 0x8fc25880,
+ 0x8e030008, 0x00431023, 0x14400012, 0xafc25880, 0x54600006, 0x8e020020,
+ 0x3243004a, 0x24020002, 0x14620005, 0x00000000, 0x8e020020, 0x34420040,
+ 0x0a001382, 0xae020020, 0x52a00006, 0x36520002, 0x8e020030, 0xaf420e10,
+ 0x8e030034, 0xaf430e18, 0x36520002, 0x52a00008, 0x96670014, 0x8f830000,
+ 0x8f420e10, 0xac6200a8, 0x8f840000, 0x8f420e18, 0xac8200ac, 0x96670014,
+ 0x92680024, 0x24020040, 0xaf420814, 0x8f830020, 0x8f82001c, 0x00671821,
+ 0x00621023, 0xaf830020, 0x18400008, 0x00000000, 0x8f820000, 0xaf83001c,
+ 0xac430054, 0x54e00005, 0xaf400040, 0x0a0013a0, 0x8f42095c, 0x54e00001,
+ 0xaf400044, 0x8f42095c, 0x31030008, 0xaf820030, 0x1060001a, 0x00000000,
+ 0x8f840000, 0x90820120, 0x90830121, 0x304600ff, 0x00c31823, 0x30630007,
+ 0x24020007, 0x1062000e, 0x00000000, 0x90820122, 0x304200fe, 0xa0820122,
+ 0x8f850000, 0x00061880, 0x8f840020, 0x24a20100, 0x00431021, 0x24c30001,
+ 0x30630007, 0xac440000, 0x0a0013bd, 0xa0a30120, 0x90820122, 0x34420001,
+ 0xa0820122, 0x14e00003, 0x31020001, 0x10400031, 0x32510002, 0x8f820000,
+ 0x8c43000c, 0x30630001, 0x1060002c, 0x32510002, 0x3c029000, 0x8f830008,
+ 0x34420001, 0x3c048000, 0x00621825, 0xaf430020, 0x8f420020, 0x00441024,
+ 0x1440fffd, 0x00000000, 0x8f870000, 0x8ce2000c, 0x30420001, 0x10400018,
+ 0x00000000, 0x94e2006a, 0x00022880, 0x50a00001, 0x24050001, 0x94e30068,
+ 0x90e40081, 0x3c020800, 0x8c460024, 0x00652821, 0x00852804, 0x00c5102b,
+ 0x54400001, 0x00a03021, 0x3c020800, 0x8c440028, 0x00c4182b, 0x54600001,
+ 0x00c02021, 0x8f430074, 0x2402fffe, 0x00822824, 0x00a31821, 0xace3000c,
+ 0x8f830008, 0x3c028000, 0x34420001, 0x00621825, 0xaf430020, 0x8f820020,
+ 0x3c050800, 0x24b05880, 0xae020028, 0x8ee30010, 0x0060f809, 0x00000000,
+ 0x8f820028, 0x24420001, 0xaf820028, 0x12a00005, 0xaf40004c, 0x8f420e10,
+ 0xae020030, 0x8f430e18, 0xae030034, 0x1220fea7, 0x24020006, 0x8f870024,
+ 0x9786002c, 0x8f830000, 0x8f820034, 0x8f840020, 0x8f85001c, 0x32530040,
+ 0xa4e6002c, 0xac620044, 0x32420008, 0xac640050, 0xac650054, 0x1040007a,
+ 0x32820020, 0x10400027, 0x32910010, 0x00003821, 0x24052000, 0x3c090800,
+ 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, 0x8ec258b0, 0x26c458b0,
+ 0x2484ffd0, 0xaf420144, 0x8c820034, 0x3c030400, 0xaf420148, 0x24020041,
+ 0xaf43014c, 0xa3420152, 0x8d230030, 0x3c021000, 0xa7470158, 0xaf450154,
+ 0xaf420178, 0x8c860034, 0x24630001, 0xad230030, 0x9342010a, 0x3c030041,
+ 0xafa50014, 0x00021600, 0x00431025, 0x00471025, 0xafa20010, 0x9343010b,
+ 0xafa30018, 0x8f440100, 0x8f450104, 0x0e00159b, 0x3c070400, 0x12200028,
+ 0x00003821, 0x24052000, 0x3c090800, 0x3c038000, 0x8f420178, 0x00431024,
+ 0x1440fffd, 0x8ec258b0, 0x26c458b0, 0x2484ffd0, 0xaf420144, 0x8c820034,
+ 0x3c030300, 0xaf420148, 0x2402004e, 0xaf43014c, 0xa3420152, 0x8d230030,
+ 0x3c021000, 0xa7470158, 0xaf450154, 0xaf420178, 0x8c860034, 0x24630001,
+ 0xad230030, 0x9342010a, 0x3c03004e, 0xafa50014, 0x00021600, 0x00431025,
+ 0x00471025, 0xafa20010, 0x9343010b, 0xafa30018, 0x8f440100, 0x8f450104,
+ 0x0e00159b, 0x3c070300, 0x0a00148b, 0x8fa20024, 0x32820008, 0x10400026,
+ 0x24052000, 0x00003821, 0x3c090800, 0x3c038000, 0x8f420178, 0x00431024,
+ 0x1440fffd, 0x8ec258b0, 0x26c458b0, 0x2484ffd0, 0xaf420144, 0x8c820034,
+ 0x3c030200, 0xaf420148, 0x2402004b, 0xaf43014c, 0xa3420152, 0x8d230030,
+ 0x3c021000, 0xa7470158, 0xaf450154, 0xaf420178, 0x8c860034, 0x24630001,
+ 0xad230030, 0x9342010a, 0x3c03004b, 0xafa50014, 0x00021600, 0x00431025,
+ 0x00471025, 0xafa20010, 0x9343010b, 0xafa30018, 0x8f440100, 0x8f450104,
+ 0x0e00159b, 0x3c070200, 0x8fa20024, 0x14400004, 0x8fa30020, 0x32420010,
+ 0x10400004, 0x00000000, 0x8c620004, 0x0040f809, 0x00000000, 0x12600006,
+ 0x8fa40020, 0x8c820008, 0x0040f809, 0x00000000, 0x0a0014c1, 0x8fbf0054,
+ 0x3c030800, 0x8c6258a0, 0x30420040, 0x14400023, 0x8fbf0054, 0x00002821,
+ 0x24040040, 0x8f870020, 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd,
+ 0x8ec258b0, 0x26c358b0, 0x2463ffd0, 0xaf420144, 0x8c620034, 0xaf420148,
+ 0x24020049, 0xaf47014c, 0xa3420152, 0x3c021000, 0xa7450158, 0xaf440154,
+ 0xaf420178, 0x8c660034, 0x9342010a, 0x3c030049, 0xafa40014, 0x00021600,
+ 0x00431025, 0x00451025, 0xafa20010, 0x9343010b, 0xafa30018, 0x8f440100,
+ 0x0e00159b, 0x8f450104, 0x8fbf0054, 0x8fbe0050, 0x8fb7004c, 0x8fb60048,
+ 0x8fb50044, 0x8fb40040, 0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030,
+ 0x03e00008, 0x27bd0058, 0x03e00008, 0x00001021, 0x3c020800, 0x24435880,
+ 0x8c650004, 0x8c445880, 0x0085182b, 0x10600002, 0x00403021, 0x00802821,
+ 0x9744093c, 0x00a4102b, 0x54400001, 0x00a02021, 0x93420923, 0x0004182b,
+ 0x00021042, 0x30420001, 0x00431024, 0x1040000d, 0x24c25880, 0x8f850000,
+ 0x8f830020, 0x8ca20084, 0x00431023, 0x04420007, 0x24c25880, 0x8ca20084,
+ 0x00641821, 0x00431023, 0x28420001, 0x00822023, 0x24c25880, 0xac440008,
+ 0xa4400026, 0x03e00008, 0x00001021, 0x8f850004, 0x97840010, 0x3c030800,
+ 0x24635880, 0x24020008, 0xa4620012, 0x8f820004, 0xa4600010, 0x000420c2,
+ 0x30840008, 0x2c420001, 0x00021023, 0x30420006, 0xac650008, 0x03e00008,
+ 0xa0640024, 0x3c020800, 0x24425880, 0x90450025, 0x9443001c, 0x3c021100,
+ 0xac800004, 0x00052c00, 0x24630002, 0x00621825, 0x00a32825, 0x24820008,
+ 0x03e00008, 0xac850000, 0x27bdffd8, 0x3c020800, 0x24425880, 0xafbf0020,
+ 0x90480025, 0x8c440008, 0x8c460020, 0x8f870020, 0x3c030800, 0x3c058000,
+ 0x8f420178, 0x00451024, 0x1440fffd, 0x8c6258b0, 0x246358b0, 0x2469ffd0,
+ 0xaf420144, 0x8d220034, 0x30c32000, 0xaf420148, 0x3c021000, 0xaf47014c,
+ 0xa3480152, 0xa7440158, 0xaf460154, 0xaf420178, 0x10600004, 0x3c030800,
+ 0x8c620030, 0x24420001, 0xac620030, 0x9342010a, 0x00081c00, 0x3084ffff,
+ 0xafa60014, 0x00021600, 0x00431025, 0x00441025, 0xafa20010, 0x9343010b,
+ 0xafa30018, 0x8f440100, 0x8f450104, 0x0e00159b, 0x8d260034, 0x8fbf0020,
+ 0x03e00008, 0x27bd0028, 0x0000000d, 0x00000000, 0x2400019d, 0x03e00008,
+ 0x00000000, 0x0000000d, 0x00000000, 0x240001a9, 0x03e00008, 0x00000000,
+ 0x03e00008, 0x00000000, 0x3c020800, 0x24425880, 0xac400008, 0xa4400026,
+ 0x03e00008, 0x24020001, 0x3c020800, 0x24425880, 0x24030008, 0xac400008,
+ 0xa4400010, 0xa4430012, 0xa0400024, 0x03e00008, 0x24020004, 0x03e00008,
+ 0x00001021, 0x10c00007, 0x00000000, 0x8ca20000, 0x24c6ffff, 0x24a50004,
+ 0xac820000, 0x14c0fffb, 0x24840004, 0x03e00008, 0x00000000, 0x0a00156c,
+ 0x00a01021, 0xac860000, 0x00000000, 0x00000000, 0x24840004, 0x00a01021,
+ 0x1440fffa, 0x24a5ffff, 0x03e00008, 0x00000000, 0x3c0a0800, 0x8d490068,
+ 0x3c050800, 0x24a52098, 0x00093140, 0x00c51021, 0xac440000, 0x8f440e04,
+ 0x00a61021, 0xac440004, 0x97430e08, 0x97420e0c, 0x00a62021, 0x00031c00,
+ 0x00431025, 0xac820008, 0x8f430e10, 0x00801021, 0xac43000c, 0x8f440e14,
+ 0xac440010, 0x8f430e18, 0x3c0800ff, 0xac430014, 0x8f470e1c, 0x3508ffff,
+ 0x25290001, 0xac470018, 0x3c070800, 0x8ce3006c, 0x9344010a, 0x3c026000,
+ 0x24630001, 0xace3006c, 0x8c434448, 0x3129007f, 0x00a62821, 0xad490068,
+ 0x00042600, 0x00681824, 0x00832025, 0x03e00008, 0xaca4001c, 0x8fac0010,
+ 0x8fad0014, 0x8fae0018, 0x3c0b0800, 0x8d6a0060, 0x3c080800, 0x25080080,
+ 0x000a4940, 0x01281021, 0x01091821, 0xac440000, 0x00601021, 0xac650004,
+ 0xac460008, 0xac67000c, 0xac4c0010, 0xac6d0014, 0x3c036000, 0xac4e0018,
+ 0x8c654448, 0x3c040800, 0x8c820064, 0x254a0001, 0x314a00ff, 0x01094021,
+ 0xad6a0060, 0x24420001, 0xac820064, 0x03e00008, 0xad05001c, 0x3c030800,
+ 0x3c090800, 0x8d250070, 0x246330b0, 0x8f460100, 0x00053900, 0x00e31021,
+ 0xac460000, 0x8f440104, 0x00671021, 0xac440004, 0x8f460108, 0x8f840014,
+ 0x24a50001, 0xac460008, 0x8c880074, 0x3c060800, 0x8cc20074, 0x30a5003f,
+ 0x00671821, 0xad250070, 0x24420001, 0xacc20074, 0x03e00008, 0xac68000c,
+ 0x00000000 };
+
+static u32 bnx2_TXP_b06FwData[(0x0/4) + 1] = { 0x0 };
+static u32 bnx2_TXP_b06FwRodata[(0x0/4) + 1] = { 0x0 };
+static u32 bnx2_TXP_b06FwBss[(0x1c4/4) + 1] = { 0x0 };
+static u32 bnx2_TXP_b06FwSbss[(0x38/4) + 1] = { 0x0 };
diff --git a/gpxe/src/drivers/net/cs89x0.c b/gpxe/src/drivers/net/cs89x0.c
new file mode 100644
index 00000000..11988add
--- /dev/null
+++ b/gpxe/src/drivers/net/cs89x0.c
@@ -0,0 +1,737 @@
+#ifdef ALLMULTI
+#error multicast support is not yet implemented
+#endif
+
+/**
+ Per an email message from Russ Nelson <nelson@crynwr.com> on
+ 18 March 2008 this file is now licensed under GPL Version 2.
+
+ From: Russ Nelson <nelson@crynwr.com>
+ Date: Tue, 18 Mar 2008 12:42:00 -0400
+ Subject: Re: [Etherboot-developers] cs89x0 driver in etherboot
+ -- quote from email
+ As copyright holder, if I say it doesn't conflict with the GPL,
+ then it doesn't conflict with the GPL.
+
+ However, there's no point in causing people's brains to overheat,
+ so yes, I grant permission for the code to be relicensed under the
+ GPLv2. Please make sure that this change in licensing makes its
+ way upstream. -russ
+ -- quote from email
+**/
+
+/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for etherboot. */
+/*
+ Permission is granted to distribute the enclosed cs89x0.[ch] driver
+ only in conjunction with the Etherboot package. The code is
+ ordinarily distributed under the GPL.
+
+ Russ Nelson, January 2000
+
+ ChangeLog:
+
+ Thu Dec 6 22:40:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * disabled all "advanced" features; this should make the code more reliable
+
+ * reorganized the reset function
+
+ * always reset the address port, so that autoprobing will continue working
+
+ * some cosmetic changes
+
+ * 2.5
+
+ Thu Dec 5 21:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * tested the code against a CS8900 card
+
+ * lots of minor bug fixes and adjustments
+
+ * this is the first release, that actually works! it still requires some
+ changes in order to be more tolerant to different environments
+
+ * 4
+
+ Fri Nov 22 23:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * read the manuals for the CS89x0 chipsets and took note of all the
+ changes that will be neccessary in order to adapt Russel Nelson's code
+ to the requirements of a BOOT-Prom
+
+ * 6
+
+ Thu Nov 19 22:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * Synched with Russel Nelson's current code (v1.00)
+
+ * 2
+
+ Thu Nov 12 18:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * Cleaned up some of the code and tried to optimize the code size.
+
+ * 1.5
+
+ Sun Nov 10 16:30:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * First experimental release. This code compiles fine, but I
+ have no way of testing whether it actually works.
+
+ * I did not (yet) bother to make the code 16bit aware, so for
+ the time being, it will only work for Etherboot/32.
+
+ * 12
+
+ */
+
+#include <errno.h>
+#include <gpxe/ethernet.h>
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/isa.h>
+#include "console.h"
+#include "cs89x0.h"
+
+static unsigned short eth_nic_base;
+static unsigned long eth_mem_start;
+static unsigned short eth_irqno;
+static unsigned short eth_cs_type; /* one of: CS8900, CS8920, CS8920M */
+static unsigned short eth_auto_neg_cnf;
+static unsigned short eth_adapter_cnf;
+static unsigned short eth_linectl;
+
+/*************************************************************************
+ CS89x0 - specific routines
+**************************************************************************/
+
+static inline int readreg(int portno)
+{
+ outw(portno, eth_nic_base + ADD_PORT);
+ return inw(eth_nic_base + DATA_PORT);
+}
+
+static inline void writereg(int portno, int value)
+{
+ outw(portno, eth_nic_base + ADD_PORT);
+ outw(value, eth_nic_base + DATA_PORT);
+ return;
+}
+
+/*************************************************************************
+EEPROM access
+**************************************************************************/
+
+static int wait_eeprom_ready(void)
+{
+ unsigned long tmo = currticks() + 4*TICKS_PER_SEC;
+
+ /* check to see if the EEPROM is ready, a timeout is used -
+ just in case EEPROM is ready when SI_BUSY in the
+ PP_SelfST is clear */
+ while(readreg(PP_SelfST) & SI_BUSY) {
+ if (currticks() >= tmo)
+ return -1; }
+ return 0;
+}
+
+static int get_eeprom_data(int off, int len, unsigned short *buffer)
+{
+ int i;
+
+#ifdef EDEBUG
+ printf("\ncs: EEPROM data from %hX for %hX:",off,len);
+#endif
+ for (i = 0; i < len; i++) {
+ if (wait_eeprom_ready() < 0)
+ return -1;
+ /* Now send the EEPROM read command and EEPROM location
+ to read */
+ writereg(PP_EECMD, (off + i) | EEPROM_READ_CMD);
+ if (wait_eeprom_ready() < 0)
+ return -1;
+ buffer[i] = readreg(PP_EEData);
+#ifdef EDEBUG
+ if (!(i%10))
+ printf("\ncs: ");
+ printf("%hX ", buffer[i]);
+#endif
+ }
+#ifdef EDEBUG
+ putchar('\n');
+#endif
+
+ return(0);
+}
+
+static int get_eeprom_chksum(int off __unused, int len, unsigned short *buffer)
+{
+ int i, cksum;
+
+ cksum = 0;
+ for (i = 0; i < len; i++)
+ cksum += buffer[i];
+ cksum &= 0xffff;
+ if (cksum == 0)
+ return 0;
+ return -1;
+}
+
+/*************************************************************************
+Activate all of the available media and probe for network
+**************************************************************************/
+
+static void clrline(void)
+{
+ int i;
+
+ putchar('\r');
+ for (i = 79; i--; ) putchar(' ');
+ printf("\rcs: ");
+ return;
+}
+
+static void control_dc_dc(int on_not_off)
+{
+ unsigned int selfcontrol;
+ unsigned long tmo = currticks() + TICKS_PER_SEC;
+
+ /* control the DC to DC convertor in the SelfControl register. */
+ selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */
+ if (((eth_adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off)
+ selfcontrol |= HCB1;
+ else
+ selfcontrol &= ~HCB1;
+ writereg(PP_SelfCTL, selfcontrol);
+
+ /* Wait for the DC/DC converter to power up - 1000ms */
+ while (currticks() < tmo);
+
+ return;
+}
+
+static int detect_tp(void)
+{
+ unsigned long tmo;
+
+ /* Turn on the chip auto detection of 10BT/ AUI */
+
+ clrline(); printf("attempting %s:","TP");
+
+ /* If connected to another full duplex capable 10-Base-T card
+ the link pulses seem to be lost when the auto detect bit in
+ the LineCTL is set. To overcome this the auto detect bit
+ will be cleared whilst testing the 10-Base-T interface.
+ This would not be necessary for the sparrow chip but is
+ simpler to do it anyway. */
+ writereg(PP_LineCTL, eth_linectl &~ AUI_ONLY);
+ control_dc_dc(0);
+
+ /* Delay for the hardware to work out if the TP cable is
+ present - 150ms */
+ for (tmo = currticks() + 4; currticks() < tmo; );
+
+ if ((readreg(PP_LineST) & LINK_OK) == 0)
+ return 0;
+
+ if (eth_cs_type != CS8900) {
+
+ writereg(PP_AutoNegCTL, eth_auto_neg_cnf & AUTO_NEG_MASK);
+
+ if ((eth_auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
+ printf(" negotiating duplex... ");
+ while (readreg(PP_AutoNegST) & AUTO_NEG_BUSY) {
+ if (currticks() - tmo > 40*TICKS_PER_SEC) {
+ printf("time out ");
+ break;
+ }
+ }
+ }
+ if (readreg(PP_AutoNegST) & FDX_ACTIVE)
+ printf("using full duplex");
+ else
+ printf("using half duplex");
+ }
+
+ return A_CNF_MEDIA_10B_T;
+}
+
+/* send a test packet - return true if carrier bits are ok */
+static int send_test_pkt(struct nic *nic)
+{
+ static unsigned char testpacket[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
+ 0, 46, /*A 46 in network order */
+ 0, 0, /*DSAP=0 & SSAP=0 fields */
+ 0xf3,0 /*Control (Test Req+P bit set)*/ };
+ unsigned long tmo;
+
+ writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_TX_ON);
+
+ memcpy(testpacket, nic->node_addr, ETH_ALEN);
+ memcpy(testpacket+ETH_ALEN, nic->node_addr, ETH_ALEN);
+
+ outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT);
+ outw(ETH_ZLEN, eth_nic_base + TX_LEN_PORT);
+
+ /* Test to see if the chip has allocated memory for the packet */
+ for (tmo = currticks() + 2;
+ (readreg(PP_BusST) & READY_FOR_TX_NOW) == 0; )
+ if (currticks() >= tmo)
+ return(0);
+
+ /* Write the contents of the packet */
+ outsw(eth_nic_base + TX_FRAME_PORT, testpacket,
+ (ETH_ZLEN+1)>>1);
+
+ printf(" sending test packet ");
+ /* wait a couple of timer ticks for packet to be received */
+ for (tmo = currticks() + 2; currticks() < tmo; );
+
+ if ((readreg(PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
+ printf("succeeded");
+ return 1;
+ }
+ printf("failed");
+ return 0;
+}
+
+
+static int detect_aui(struct nic *nic)
+{
+ clrline(); printf("attempting %s:","AUI");
+ control_dc_dc(0);
+
+ writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
+
+ if (send_test_pkt(nic)) {
+ return A_CNF_MEDIA_AUI; }
+ else
+ return 0;
+}
+
+static int detect_bnc(struct nic *nic)
+{
+ clrline(); printf("attempting %s:","BNC");
+ control_dc_dc(1);
+
+ writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
+
+ if (send_test_pkt(nic)) {
+ return A_CNF_MEDIA_10B_2; }
+ else
+ return 0;
+}
+
+/**************************************************************************
+ETH_RESET - Reset adapter
+***************************************************************************/
+
+static void cs89x0_reset(struct nic *nic)
+{
+ int i;
+ unsigned long reset_tmo;
+
+ writereg(PP_SelfCTL, readreg(PP_SelfCTL) | POWER_ON_RESET);
+
+ /* wait for two ticks; that is 2*55ms */
+ for (reset_tmo = currticks() + 2; currticks() < reset_tmo; );
+
+ if (eth_cs_type != CS8900) {
+ /* Hardware problem requires PNP registers to be reconfigured
+ after a reset */
+ if (eth_irqno != 0xFFFF) {
+ outw(PP_CS8920_ISAINT, eth_nic_base + ADD_PORT);
+ outb(eth_irqno, eth_nic_base + DATA_PORT);
+ outb(0, eth_nic_base + DATA_PORT + 1); }
+
+ if (eth_mem_start) {
+ outw(PP_CS8920_ISAMemB, eth_nic_base + ADD_PORT);
+ outb((eth_mem_start >> 8) & 0xff, eth_nic_base + DATA_PORT);
+ outb((eth_mem_start >> 24) & 0xff, eth_nic_base + DATA_PORT + 1); } }
+
+ /* Wait until the chip is reset */
+ for (reset_tmo = currticks() + 2;
+ (readreg(PP_SelfST) & INIT_DONE) == 0 &&
+ currticks() < reset_tmo; );
+
+ /* disable interrupts and memory accesses */
+ writereg(PP_BusCTL, 0);
+
+ /* set the ethernet address */
+ for (i=0; i < ETH_ALEN/2; i++)
+ writereg(PP_IA+i*2,
+ nic->node_addr[i*2] |
+ (nic->node_addr[i*2+1] << 8));
+
+ /* receive only error free packets addressed to this card */
+ writereg(PP_RxCTL, DEF_RX_ACCEPT);
+
+ /* do not generate any interrupts on receive operations */
+ writereg(PP_RxCFG, 0);
+
+ /* do not generate any interrupts on transmit operations */
+ writereg(PP_TxCFG, 0);
+
+ /* do not generate any interrupts on buffer operations */
+ writereg(PP_BufCFG, 0);
+
+ /* reset address port, so that autoprobing will keep working */
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+
+ return;
+}
+
+/**************************************************************************
+ETH_TRANSMIT - Transmit a frame
+***************************************************************************/
+
+static void cs89x0_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ unsigned long tmo;
+ int sr;
+
+ /* does this size have to be rounded??? please,
+ somebody have a look in the specs */
+ if ((sr = ((s + ETH_HLEN + 1)&~1)) < ETH_ZLEN)
+ sr = ETH_ZLEN;
+
+retry:
+ /* initiate a transmit sequence */
+ outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT);
+ outw(sr, eth_nic_base + TX_LEN_PORT);
+
+ /* Test to see if the chip has allocated memory for the packet */
+ if ((readreg(PP_BusST) & READY_FOR_TX_NOW) == 0) {
+ /* Oops... this should not happen! */
+ printf("cs: unable to send packet; retrying...\n");
+ for (tmo = currticks() + 5*TICKS_PER_SEC; currticks() < tmo; );
+ cs89x0_reset(nic);
+ goto retry; }
+
+ /* Write the contents of the packet */
+ outsw(eth_nic_base + TX_FRAME_PORT, d, ETH_ALEN/2);
+ outsw(eth_nic_base + TX_FRAME_PORT, nic->node_addr,
+ ETH_ALEN/2);
+ outw(((t >> 8)&0xFF)|(t << 8), eth_nic_base + TX_FRAME_PORT);
+ outsw(eth_nic_base + TX_FRAME_PORT, p, (s+1)/2);
+ for (sr = sr/2 - (s+1)/2 - ETH_ALEN - 1; sr-- > 0;
+ outw(0, eth_nic_base + TX_FRAME_PORT));
+
+ /* wait for transfer to succeed */
+ for (tmo = currticks()+5*TICKS_PER_SEC;
+ (s = readreg(PP_TxEvent)&~0x1F) == 0 && currticks() < tmo;)
+ /* nothing */ ;
+ if ((s & TX_SEND_OK_BITS) != TX_OK) {
+ printf("\ntransmission error %#hX\n", s);
+ }
+
+ return;
+}
+
+/**************************************************************************
+ETH_POLL - Wait for a frame
+***************************************************************************/
+
+static int cs89x0_poll(struct nic *nic, int retrieve)
+{
+ int status;
+
+ status = readreg(PP_RxEvent);
+
+ if ((status & RX_OK) == 0)
+ return(0);
+
+ if ( ! retrieve ) return 1;
+
+ status = inw(eth_nic_base + RX_FRAME_PORT);
+ nic->packetlen = inw(eth_nic_base + RX_FRAME_PORT);
+ insw(eth_nic_base + RX_FRAME_PORT, nic->packet, nic->packetlen >> 1);
+ if (nic->packetlen & 1)
+ nic->packet[nic->packetlen-1] = inw(eth_nic_base + RX_FRAME_PORT);
+ return 1;
+}
+
+static void cs89x0_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct nic_operations cs89x0_operations = {
+ .connect = dummy_connect,
+ .poll = cs89x0_poll,
+ .transmit = cs89x0_transmit,
+ .irq = cs89x0_irq,
+};
+
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+***************************************************************************/
+
+static int cs89x0_probe_addr ( isa_probe_addr_t ioaddr ) {
+ /* if they give us an odd I/O address, then do ONE write to
+ the address port, to get it back to address zero, where we
+ expect to find the EISA signature word. */
+ if (ioaddr & 1) {
+ ioaddr &= ~1;
+ if ((inw(ioaddr + ADD_PORT) & ADD_MASK) != ADD_SIG)
+ return 0;
+ outw(PP_ChipID, ioaddr + ADD_PORT);
+ }
+
+ if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG)
+ return 0;
+
+ return 1;
+}
+
+static int cs89x0_probe ( struct nic *nic, struct isa_device *isa __unused ) {
+ int i, result = -1;
+ unsigned rev_type = 0, isa_cnf, cs_revision;
+ unsigned short eeprom_buff[CHKSUM_LEN];
+
+ nic->ioaddr &= ~1; /* LSB = 1 indicates a more aggressive probe */
+ eth_nic_base = nic->ioaddr;
+
+ /* get the chip type */
+ rev_type = readreg(PRODUCT_ID_ADD);
+ eth_cs_type = rev_type &~ REVISON_BITS;
+ cs_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
+
+ printf("\ncs: cs89%c0%s rev %c, base %#hX",
+ eth_cs_type==CS8900?'0':'2',
+ eth_cs_type==CS8920M?"M":"",
+ cs_revision,
+ eth_nic_base);
+#ifndef EMBEDDED
+ /* First check to see if an EEPROM is attached*/
+ if ((readreg(PP_SelfST) & EEPROM_PRESENT) == 0) {
+ printf("\ncs: no EEPROM...\n");
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+ return 0;
+ } else if (get_eeprom_data(START_EEPROM_DATA,CHKSUM_LEN,
+ eeprom_buff) < 0) {
+ printf("\ncs: EEPROM read failed...\n");
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+ return 0;
+ } else if (get_eeprom_chksum(START_EEPROM_DATA,CHKSUM_LEN,
+ eeprom_buff) < 0) {
+ printf("\ncs: EEPROM checksum bad...\n");
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+ return 0;
+ }
+
+ /* get transmission control word but keep the
+ autonegotiation bits */
+ eth_auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
+ /* Store adapter configuration */
+ eth_adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2];
+ /* Store ISA configuration */
+ isa_cnf = eeprom_buff[ISA_CNF_OFFSET/2];
+
+ /* store the initial memory base address */
+ eth_mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8;
+
+ printf("%s%s%s, addr ",
+ (eth_adapter_cnf & A_CNF_10B_T)?", RJ-45":"",
+ (eth_adapter_cnf & A_CNF_AUI)?", AUI":"",
+ (eth_adapter_cnf & A_CNF_10B_2)?", BNC":"");
+
+ /* If this is a CS8900 then no pnp soft */
+ if (eth_cs_type != CS8900 &&
+ /* Check if the ISA IRQ has been set */
+ (i = readreg(PP_CS8920_ISAINT) & 0xff,
+ (i != 0 && i < CS8920_NO_INTS)))
+ eth_irqno = i;
+ else {
+ i = isa_cnf & INT_NO_MASK;
+ if (eth_cs_type == CS8900) {
+ /* the table that follows is dependent
+ upon how you wired up your cs8900
+ in your system. The table is the
+ same as the cs8900 engineering demo
+ board. irq_map also depends on the
+ contents of the table. Also see
+ write_irq, which is the reverse
+ mapping of the table below. */
+ if (i < 4) i = "\012\013\014\005"[i];
+ else printf("\ncs: BUG: isa_config is %d\n", i); }
+ eth_irqno = i; }
+
+ nic->irqno = eth_irqno;
+
+ /* Retrieve and print the ethernet address. */
+ for (i=0; i<ETH_ALEN; i++) {
+ nic->node_addr[i] = ((unsigned char *)eeprom_buff)[i];
+ }
+
+ DBG ( "%s\n", eth_ntoa ( nic->node_addr ) );
+
+#endif
+#ifdef EMBEDDED
+ /* Retrieve and print the ethernet address. */
+ {
+ unsigned char MAC_HW_ADDR[6]={MAC_HW_ADDR_DRV};
+ memcpy(nic->node_addr, MAC_HW_ADDR, 6);
+ }
+
+ DBG ( "%s\n", eth_ntoa ( nic->node_addr ) );
+
+ eth_adapter_cnf = A_CNF_10B_T | A_CNF_MEDIA_10B_T;
+ eth_auto_neg_cnf = EE_AUTO_NEG_ENABLE | IMM_BIT;
+#endif
+#ifndef EMBEDDED
+ /* Set the LineCTL quintuplet based on adapter
+ configuration read from EEPROM */
+ if ((eth_adapter_cnf & A_CNF_EXTND_10B_2) &&
+ (eth_adapter_cnf & A_CNF_LOW_RX_SQUELCH))
+ eth_linectl = LOW_RX_SQUELCH;
+ else
+ eth_linectl = 0;
+
+ /* check to make sure that they have the "right"
+ hardware available */
+ switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) {
+ case A_CNF_MEDIA_10B_T: result = eth_adapter_cnf & A_CNF_10B_T;
+ break;
+ case A_CNF_MEDIA_AUI: result = eth_adapter_cnf & A_CNF_AUI;
+ break;
+ case A_CNF_MEDIA_10B_2: result = eth_adapter_cnf & A_CNF_10B_2;
+ break;
+ default: result = eth_adapter_cnf & (A_CNF_10B_T | A_CNF_AUI |
+ A_CNF_10B_2);
+ }
+ if (!result) {
+ printf("cs: EEPROM is configured for unavailable media\n");
+ error:
+ writereg(PP_LineCTL, readreg(PP_LineCTL) &
+ ~(SERIAL_TX_ON | SERIAL_RX_ON));
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+ return 0;
+ }
+#endif
+ /* Initialize the card for probing of the attached media */
+ cs89x0_reset(nic);
+
+ /* set the hardware to the configured choice */
+ switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) {
+ case A_CNF_MEDIA_10B_T:
+ result = detect_tp();
+ if (!result) {
+ clrline();
+ printf("10Base-T (RJ-45%s",
+ ") has no cable\n"); }
+ /* check "ignore missing media" bit */
+ if (eth_auto_neg_cnf & IMM_BIT)
+ /* Yes! I don't care if I see a link pulse */
+ result = A_CNF_MEDIA_10B_T;
+ break;
+ case A_CNF_MEDIA_AUI:
+ result = detect_aui(nic);
+ if (!result) {
+ clrline();
+ printf("10Base-5 (AUI%s",
+ ") has no cable\n"); }
+ /* check "ignore missing media" bit */
+ if (eth_auto_neg_cnf & IMM_BIT)
+ /* Yes! I don't care if I see a carrrier */
+ result = A_CNF_MEDIA_AUI;
+ break;
+ case A_CNF_MEDIA_10B_2:
+ result = detect_bnc(nic);
+ if (!result) {
+ clrline();
+ printf("10Base-2 (BNC%s",
+ ") has no cable\n"); }
+ /* check "ignore missing media" bit */
+ if (eth_auto_neg_cnf & IMM_BIT)
+ /* Yes! I don't care if I can xmit a packet */
+ result = A_CNF_MEDIA_10B_2;
+ break;
+ case A_CNF_MEDIA_AUTO:
+ writereg(PP_LineCTL, eth_linectl | AUTO_AUI_10BASET);
+ if (eth_adapter_cnf & A_CNF_10B_T)
+ if ((result = detect_tp()) != 0)
+ break;
+ if (eth_adapter_cnf & A_CNF_AUI)
+ if ((result = detect_aui(nic)) != 0)
+ break;
+ if (eth_adapter_cnf & A_CNF_10B_2)
+ if ((result = detect_bnc(nic)) != 0)
+ break;
+ clrline(); printf("no media detected\n");
+ goto error;
+ }
+ clrline();
+ switch(result) {
+ case 0: printf("no network cable attached to configured media\n");
+ goto error;
+ case A_CNF_MEDIA_10B_T: printf("using 10Base-T (RJ-45)\n");
+ break;
+ case A_CNF_MEDIA_AUI: printf("using 10Base-5 (AUI)\n");
+ break;
+ case A_CNF_MEDIA_10B_2: printf("using 10Base-2 (BNC)\n");
+ break;
+ }
+
+ /* Turn on both receive and transmit operations */
+ writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_RX_ON |
+ SERIAL_TX_ON);
+
+ return 0;
+#ifdef EMBEDDED
+ error:
+ writereg(PP_LineCTL, readreg(PP_LineCTL) &
+ ~(SERIAL_TX_ON | SERIAL_RX_ON));
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+ return 0;
+#endif
+
+ nic->nic_op = &cs89x0_operations;
+ return 1;
+}
+
+static void cs89x0_disable ( struct nic *nic,
+ struct isa_device *isa __unused ) {
+ cs89x0_reset(nic);
+}
+
+static isa_probe_addr_t cs89x0_probe_addrs[] = {
+#ifndef EMBEDDED
+ /* use "conservative" default values for autoprobing */
+ 0x300, 0x320, 0x340, 0x200, 0x220, 0x240,
+ 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0,
+ /* if that did not work, then be more aggressive */
+ 0x301, 0x321, 0x341, 0x201, 0x221, 0x241,
+ 0x261, 0x281, 0x2a1, 0x2c1, 0x2e1,
+#else
+ 0x01000300,
+#endif
+};
+
+ISA_DRIVER ( cs89x0_driver, cs89x0_probe_addrs, cs89x0_probe_addr,
+ ISAPNP_VENDOR('C','S','C'), 0x0007 );
+
+DRIVER ( "cs89x0", nic_driver, isa_driver, cs89x0_driver,
+ cs89x0_probe, cs89x0_disable );
+
+ISA_ROM ( "cs89x0", "Crystal Semiconductor CS89x0" );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/cs89x0.h b/gpxe/src/drivers/net/cs89x0.h
new file mode 100644
index 00000000..696191cf
--- /dev/null
+++ b/gpxe/src/drivers/net/cs89x0.h
@@ -0,0 +1,479 @@
+/**
+ Per an email message from Russ Nelson <nelson@crynwr.com> on
+ 18 March 2008 this file is now licensed under GPL Version 2.
+
+ From: Russ Nelson <nelson@crynwr.com>
+ Date: Tue, 18 Mar 2008 12:42:00 -0400
+ Subject: Re: [Etherboot-developers] cs89x0 driver in etherboot
+ -- quote from email
+ As copyright holder, if I say it doesn't conflict with the GPL,
+ then it doesn't conflict with the GPL.
+
+ However, there's no point in causing people's brains to overheat,
+ so yes, I grant permission for the code to be relicensed under the
+ GPLv2. Please make sure that this change in licensing makes its
+ way upstream. -russ
+ -- quote from email
+**/
+
+/* Copyright, 1988-1992, Russell Nelson, Crynwr Software
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 1.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define PP_ChipID 0x0000 /* offset 0h -> Corp -ID */
+ /* offset 2h -> Model/Product Number */
+ /* offset 3h -> Chip Revision Number */
+
+#define PP_ISAIOB 0x0020 /* IO base address */
+#define PP_CS8900_ISAINT 0x0022 /* ISA interrupt select */
+#define PP_CS8920_ISAINT 0x0370 /* ISA interrupt select */
+#define PP_CS8900_ISADMA 0x0024 /* ISA Rec DMA channel */
+#define PP_CS8920_ISADMA 0x0374 /* ISA Rec DMA channel */
+#define PP_ISASOF 0x0026 /* ISA DMA offset */
+#define PP_DmaFrameCnt 0x0028 /* ISA DMA Frame count */
+#define PP_DmaByteCnt 0x002A /* ISA DMA Byte count */
+#define PP_CS8900_ISAMemB 0x002C /* Memory base */
+#define PP_CS8920_ISAMemB 0x0348 /* */
+
+#define PP_ISABootBase 0x0030 /* Boot Prom base */
+#define PP_ISABootMask 0x0034 /* Boot Prom Mask */
+
+/* EEPROM data and command registers */
+#define PP_EECMD 0x0040 /* NVR Interface Command register */
+#define PP_EEData 0x0042 /* NVR Interface Data Register */
+#define PP_DebugReg 0x0044 /* Debug Register */
+
+#define PP_RxCFG 0x0102 /* Rx Bus config */
+#define PP_RxCTL 0x0104 /* Receive Control Register */
+#define PP_TxCFG 0x0106 /* Transmit Config Register */
+#define PP_TxCMD 0x0108 /* Transmit Command Register */
+#define PP_BufCFG 0x010A /* Bus configuration Register */
+#define PP_LineCTL 0x0112 /* Line Config Register */
+#define PP_SelfCTL 0x0114 /* Self Command Register */
+#define PP_BusCTL 0x0116 /* ISA bus control Register */
+#define PP_TestCTL 0x0118 /* Test Register */
+#define PP_AutoNegCTL 0x011C /* Auto Negotiation Ctrl */
+
+#define PP_ISQ 0x0120 /* Interrupt Status */
+#define PP_RxEvent 0x0124 /* Rx Event Register */
+#define PP_TxEvent 0x0128 /* Tx Event Register */
+#define PP_BufEvent 0x012C /* Bus Event Register */
+#define PP_RxMiss 0x0130 /* Receive Miss Count */
+#define PP_TxCol 0x0132 /* Transmit Collision Count */
+#define PP_LineST 0x0134 /* Line State Register */
+#define PP_SelfST 0x0136 /* Self State register */
+#define PP_BusST 0x0138 /* Bus Status */
+#define PP_TDR 0x013C /* Time Domain Reflectometry */
+#define PP_AutoNegST 0x013E /* Auto Neg Status */
+#define PP_TxCommand 0x0144 /* Tx Command */
+#define PP_TxLength 0x0146 /* Tx Length */
+#define PP_LAF 0x0150 /* Hash Table */
+#define PP_IA 0x0158 /* Physical Address Register */
+
+#define PP_RxStatus 0x0400 /* Receive start of frame */
+#define PP_RxLength 0x0402 /* Receive Length of frame */
+#define PP_RxFrame 0x0404 /* Receive frame pointer */
+#define PP_TxFrame 0x0A00 /* Transmit frame pointer */
+
+/* Primary I/O Base Address. If no I/O base is supplied by the user, then this */
+/* can be used as the default I/O base to access the PacketPage Area. */
+#define DEFAULTIOBASE 0x0300
+#define FIRST_IO 0x020C /* First I/O port to check */
+#define LAST_IO 0x037C /* Last I/O port to check (+10h) */
+#define ADD_MASK 0x3000 /* Mask it use of the ADD_PORT register */
+#define ADD_SIG 0x3000 /* Expected ID signature */
+
+#define CHIP_EISA_ID_SIG 0x630E /* Product ID Code for Crystal Chip (CS8900 spec 4.3) */
+
+#ifdef IBMEIPKT
+#define EISA_ID_SIG 0x4D24 /* IBM */
+#define PART_NO_SIG 0x1010 /* IBM */
+#define MONGOOSE_BIT 0x0000 /* IBM */
+#else
+#define EISA_ID_SIG 0x630E /* PnP Vendor ID (same as chip id for Crystal board) */
+#define PART_NO_SIG 0x4000 /* ID code CS8920 board (PnP Vendor Product code) */
+#define MONGOOSE_BIT 0x2000 /* PART_NO_SIG + MONGOOSE_BUT => ID of mongoose */
+#endif
+
+#define PRODUCT_ID_ADD 0x0002 /* Address of product ID */
+
+/* Mask to find out the types of registers */
+#define REG_TYPE_MASK 0x001F
+
+/* Eeprom Commands */
+#define ERSE_WR_ENBL 0x00F0
+#define ERSE_WR_DISABLE 0x0000
+
+/* Defines Control/Config register quintuplet numbers */
+#define RX_BUF_CFG 0x0003
+#define RX_CONTROL 0x0005
+#define TX_CFG 0x0007
+#define TX_COMMAND 0x0009
+#define BUF_CFG 0x000B
+#define LINE_CONTROL 0x0013
+#define SELF_CONTROL 0x0015
+#define BUS_CONTROL 0x0017
+#define TEST_CONTROL 0x0019
+
+/* Defines Status/Count registers quintuplet numbers */
+#define RX_EVENT 0x0004
+#define TX_EVENT 0x0008
+#define BUF_EVENT 0x000C
+#define RX_MISS_COUNT 0x0010
+#define TX_COL_COUNT 0x0012
+#define LINE_STATUS 0x0014
+#define SELF_STATUS 0x0016
+#define BUS_STATUS 0x0018
+#define TDR 0x001C
+
+/* PP_RxCFG - Receive Configuration and Interrupt Mask bit definition - Read/write */
+#define SKIP_1 0x0040
+#define RX_STREAM_ENBL 0x0080
+#define RX_OK_ENBL 0x0100
+#define RX_DMA_ONLY 0x0200
+#define AUTO_RX_DMA 0x0400
+#define BUFFER_CRC 0x0800
+#define RX_CRC_ERROR_ENBL 0x1000
+#define RX_RUNT_ENBL 0x2000
+#define RX_EXTRA_DATA_ENBL 0x4000
+
+/* PP_RxCTL - Receive Control bit definition - Read/write */
+#define RX_IA_HASH_ACCEPT 0x0040
+#define RX_PROM_ACCEPT 0x0080
+#define RX_OK_ACCEPT 0x0100
+#define RX_MULTCAST_ACCEPT 0x0200
+#define RX_IA_ACCEPT 0x0400
+#define RX_BROADCAST_ACCEPT 0x0800
+#define RX_BAD_CRC_ACCEPT 0x1000
+#define RX_RUNT_ACCEPT 0x2000
+#define RX_EXTRA_DATA_ACCEPT 0x4000
+#define RX_ALL_ACCEPT (RX_PROM_ACCEPT|RX_BAD_CRC_ACCEPT|RX_RUNT_ACCEPT|RX_EXTRA_DATA_ACCEPT)
+/* Default receive mode - individually addressed, broadcast, and error free */
+#define DEF_RX_ACCEPT (RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT)
+
+/* PP_TxCFG - Transmit Configuration Interrupt Mask bit definition - Read/write */
+#define TX_LOST_CRS_ENBL 0x0040
+#define TX_SQE_ERROR_ENBL 0x0080
+#define TX_OK_ENBL 0x0100
+#define TX_LATE_COL_ENBL 0x0200
+#define TX_JBR_ENBL 0x0400
+#define TX_ANY_COL_ENBL 0x0800
+#define TX_16_COL_ENBL 0x8000
+
+/* PP_TxCMD - Transmit Command bit definition - Read-only */
+#define TX_START_4_BYTES 0x0000
+#define TX_START_64_BYTES 0x0040
+#define TX_START_128_BYTES 0x0080
+#define TX_START_ALL_BYTES 0x00C0
+#define TX_FORCE 0x0100
+#define TX_ONE_COL 0x0200
+#define TX_TWO_PART_DEFF_DISABLE 0x0400
+#define TX_NO_CRC 0x1000
+#define TX_RUNT 0x2000
+
+/* PP_BufCFG - Buffer Configuration Interrupt Mask bit definition - Read/write */
+#define GENERATE_SW_INTERRUPT 0x0040
+#define RX_DMA_ENBL 0x0080
+#define READY_FOR_TX_ENBL 0x0100
+#define TX_UNDERRUN_ENBL 0x0200
+#define RX_MISS_ENBL 0x0400
+#define RX_128_BYTE_ENBL 0x0800
+#define TX_COL_COUNT_OVRFLOW_ENBL 0x1000
+#define RX_MISS_COUNT_OVRFLOW_ENBL 0x2000
+#define RX_DEST_MATCH_ENBL 0x8000
+
+/* PP_LineCTL - Line Control bit definition - Read/write */
+#define SERIAL_RX_ON 0x0040
+#define SERIAL_TX_ON 0x0080
+#define AUI_ONLY 0x0100
+#define AUTO_AUI_10BASET 0x0200
+#define MODIFIED_BACKOFF 0x0800
+#define NO_AUTO_POLARITY 0x1000
+#define TWO_PART_DEFDIS 0x2000
+#define LOW_RX_SQUELCH 0x4000
+
+/* PP_SelfCTL - Software Self Control bit definition - Read/write */
+#define POWER_ON_RESET 0x0040
+#define SW_STOP 0x0100
+#define SLEEP_ON 0x0200
+#define AUTO_WAKEUP 0x0400
+#define HCB0_ENBL 0x1000
+#define HCB1_ENBL 0x2000
+#define HCB0 0x4000
+#define HCB1 0x8000
+
+/* PP_BusCTL - ISA Bus Control bit definition - Read/write */
+#define RESET_RX_DMA 0x0040
+#define MEMORY_ON 0x0400
+#define DMA_BURST_MODE 0x0800
+#define IO_CHANNEL_READY_ON 0x1000
+#define RX_DMA_SIZE_64K 0x2000
+#define ENABLE_IRQ 0x8000
+
+/* PP_TestCTL - Test Control bit definition - Read/write */
+#define LINK_OFF 0x0080
+#define ENDEC_LOOPBACK 0x0200
+#define AUI_LOOPBACK 0x0400
+#define BACKOFF_OFF 0x0800
+#define FAST_TEST 0x8000
+
+/* PP_RxEvent - Receive Event Bit definition - Read-only */
+#define RX_IA_HASHED 0x0040
+#define RX_DRIBBLE 0x0080
+#define RX_OK 0x0100
+#define RX_HASHED 0x0200
+#define RX_IA 0x0400
+#define RX_BROADCAST 0x0800
+#define RX_CRC_ERROR 0x1000
+#define RX_RUNT 0x2000
+#define RX_EXTRA_DATA 0x4000
+
+#define HASH_INDEX_MASK 0x0FC00
+
+/* PP_TxEvent - Transmit Event Bit definition - Read-only */
+#define TX_LOST_CRS 0x0040
+#define TX_SQE_ERROR 0x0080
+#define TX_OK 0x0100
+#define TX_LATE_COL 0x0200
+#define TX_JBR 0x0400
+#define TX_16_COL 0x8000
+#define TX_SEND_OK_BITS (TX_OK|TX_LOST_CRS)
+#define TX_COL_COUNT_MASK 0x7800
+
+/* PP_BufEvent - Buffer Event Bit definition - Read-only */
+#define SW_INTERRUPT 0x0040
+#define RX_DMA 0x0080
+#define READY_FOR_TX 0x0100
+#define TX_UNDERRUN 0x0200
+#define RX_MISS 0x0400
+#define RX_128_BYTE 0x0800
+#define TX_COL_OVRFLW 0x1000
+#define RX_MISS_OVRFLW 0x2000
+#define RX_DEST_MATCH 0x8000
+
+/* PP_LineST - Ethernet Line Status bit definition - Read-only */
+#define LINK_OK 0x0080
+#define AUI_ON 0x0100
+#define TENBASET_ON 0x0200
+#define POLARITY_OK 0x1000
+#define CRS_OK 0x4000
+
+/* PP_SelfST - Chip Software Status bit definition */
+#define ACTIVE_33V 0x0040
+#define INIT_DONE 0x0080
+#define SI_BUSY 0x0100
+#define EEPROM_PRESENT 0x0200
+#define EEPROM_OK 0x0400
+#define EL_PRESENT 0x0800
+#define EE_SIZE_64 0x1000
+
+/* PP_BusST - ISA Bus Status bit definition */
+#define TX_BID_ERROR 0x0080
+#define READY_FOR_TX_NOW 0x0100
+
+/* PP_AutoNegCTL - Auto Negotiation Control bit definition */
+#define RE_NEG_NOW 0x0040
+#define ALLOW_FDX 0x0080
+#define AUTO_NEG_ENABLE 0x0100
+#define NLP_ENABLE 0x0200
+#define FORCE_FDX 0x8000
+#define AUTO_NEG_BITS (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE)
+#define AUTO_NEG_MASK (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE|ALLOW_FDX|RE_NEG_NOW)
+
+/* PP_AutoNegST - Auto Negotiation Status bit definition */
+#define AUTO_NEG_BUSY 0x0080
+#define FLP_LINK 0x0100
+#define FLP_LINK_GOOD 0x0800
+#define LINK_FAULT 0x1000
+#define HDX_ACTIVE 0x4000
+#define FDX_ACTIVE 0x8000
+
+/* The following block defines the ISQ event types */
+#define ISQ_RECEIVER_EVENT 0x04
+#define ISQ_TRANSMITTER_EVENT 0x08
+#define ISQ_BUFFER_EVENT 0x0c
+#define ISQ_RX_MISS_EVENT 0x10
+#define ISQ_TX_COL_EVENT 0x12
+
+#define ISQ_EVENT_MASK 0x003F /* ISQ mask to find out type of event */
+#define ISQ_HIST 16 /* small history buffer */
+#define AUTOINCREMENT 0x8000 /* Bit mask to set bit-15 for autoincrement */
+
+#define TXRXBUFSIZE 0x0600
+#define RXDMABUFSIZE 0x8000
+#define RXDMASIZE 0x4000
+#define TXRX_LENGTH_MASK 0x07FF
+
+/* rx options bits */
+#define RCV_WITH_RXON 1 /* Set SerRx ON */
+#define RCV_COUNTS 2 /* Use Framecnt1 */
+#define RCV_PONG 4 /* Pong respondent */
+#define RCV_DONG 8 /* Dong operation */
+#define RCV_POLLING 0x10 /* Poll RxEvent */
+#define RCV_ISQ 0x20 /* Use ISQ, int */
+#define RCV_AUTO_DMA 0x100 /* Set AutoRxDMAE */
+#define RCV_DMA 0x200 /* Set RxDMA only */
+#define RCV_DMA_ALL 0x400 /* Copy all DMA'ed */
+#define RCV_FIXED_DATA 0x800 /* Every frame same */
+#define RCV_IO 0x1000 /* Use ISA IO only */
+#define RCV_MEMORY 0x2000 /* Use ISA Memory */
+
+#define RAM_SIZE 0x1000 /* The card has 4k bytes or RAM */
+#define PKT_START PP_TxFrame /* Start of packet RAM */
+
+#define RX_FRAME_PORT 0x0000
+#define TX_FRAME_PORT RX_FRAME_PORT
+#define TX_CMD_PORT 0x0004
+#define TX_NOW 0x0000 /* Tx packet after 5 bytes copied */
+#define TX_AFTER_381 0x0020 /* Tx packet after 381 bytes copied */
+#define TX_AFTER_ALL 0x00C0 /* Tx packet after all bytes copied */
+#define TX_LEN_PORT 0x0006
+#define ISQ_PORT 0x0008
+#define ADD_PORT 0x000A
+#define DATA_PORT 0x000C
+
+#define EEPROM_WRITE_EN 0x00F0
+#define EEPROM_WRITE_DIS 0x0000
+#define EEPROM_WRITE_CMD 0x0100
+#define EEPROM_READ_CMD 0x0200
+
+/* Receive Header */
+/* Description of header of each packet in receive area of memory */
+#define RBUF_EVENT_LOW 0 /* Low byte of RxEvent - status of received frame */
+#define RBUF_EVENT_HIGH 1 /* High byte of RxEvent - status of received frame */
+#define RBUF_LEN_LOW 2 /* Length of received data - low byte */
+#define RBUF_LEN_HI 3 /* Length of received data - high byte */
+#define RBUF_HEAD_LEN 4 /* Length of this header */
+
+#define CHIP_READ 0x1 /* Used to mark state of the repins code (chip or dma) */
+#define DMA_READ 0x2 /* Used to mark state of the repins code (chip or dma) */
+
+/* for bios scan */
+/* */
+#ifdef CSDEBUG
+/* use these values for debugging bios scan */
+#define BIOS_START_SEG 0x00000
+#define BIOS_OFFSET_INC 0x0010
+#else
+#define BIOS_START_SEG 0x0c000
+#define BIOS_OFFSET_INC 0x0200
+#endif
+
+#define BIOS_LAST_OFFSET 0x0fc00
+
+/* Byte offsets into the EEPROM configuration buffer */
+#define ISA_CNF_OFFSET 0x6
+#define TX_CTL_OFFSET (ISA_CNF_OFFSET + 8) /* 8900 eeprom */
+#define AUTO_NEG_CNF_OFFSET (ISA_CNF_OFFSET + 8) /* 8920 eeprom */
+
+ /* the assumption here is that the bits in the eeprom are generally */
+ /* in the same position as those in the autonegctl register. */
+ /* Of course the IMM bit is not in that register so it must be */
+ /* masked out */
+#define EE_FORCE_FDX 0x8000
+#define EE_NLP_ENABLE 0x0200
+#define EE_AUTO_NEG_ENABLE 0x0100
+#define EE_ALLOW_FDX 0x0080
+#define EE_AUTO_NEG_CNF_MASK (EE_FORCE_FDX|EE_NLP_ENABLE|EE_AUTO_NEG_ENABLE|EE_ALLOW_FDX)
+
+#define IMM_BIT 0x0040 /* ignore missing media */
+
+#define ADAPTER_CNF_OFFSET (AUTO_NEG_CNF_OFFSET + 2)
+#define A_CNF_10B_T 0x0001
+#define A_CNF_AUI 0x0002
+#define A_CNF_10B_2 0x0004
+#define A_CNF_MEDIA_TYPE 0x0060
+#define A_CNF_MEDIA_AUTO 0x0000
+#define A_CNF_MEDIA_10B_T 0x0020
+#define A_CNF_MEDIA_AUI 0x0040
+#define A_CNF_MEDIA_10B_2 0x0060
+#define A_CNF_DC_DC_POLARITY 0x0080
+#define A_CNF_NO_AUTO_POLARITY 0x2000
+#define A_CNF_LOW_RX_SQUELCH 0x4000
+#define A_CNF_EXTND_10B_2 0x8000
+
+#define PACKET_PAGE_OFFSET 0x8
+
+/* Bit definitions for the ISA configuration word from the EEPROM */
+#define INT_NO_MASK 0x000F
+#define DMA_NO_MASK 0x0070
+#define ISA_DMA_SIZE 0x0200
+#define ISA_AUTO_RxDMA 0x0400
+#define ISA_RxDMA 0x0800
+#define DMA_BURST 0x1000
+#define STREAM_TRANSFER 0x2000
+#define ANY_ISA_DMA (ISA_AUTO_RxDMA | ISA_RxDMA)
+
+/* DMA controller registers */
+#define DMA_BASE 0x00 /* DMA controller base */
+#define DMA_BASE_2 0x0C0 /* DMA controller base */
+
+#define DMA_STAT 0x0D0 /* DMA controller status register */
+#define DMA_MASK 0x0D4 /* DMA controller mask register */
+#define DMA_MODE 0x0D6 /* DMA controller mode register */
+#define DMA_RESETFF 0x0D8 /* DMA controller first/last flip flop */
+
+/* DMA data */
+#define DMA_DISABLE 0x04 /* Disable channel n */
+#define DMA_ENABLE 0x00 /* Enable channel n */
+/* Demand transfers, incr. address, auto init, writes, ch. n */
+#define DMA_RX_MODE 0x14
+/* Demand transfers, incr. address, auto init, reads, ch. n */
+#define DMA_TX_MODE 0x18
+
+#define DMA_SIZE (16*1024) /* Size of dma buffer - 16k */
+
+#define CS8900 0x0000
+#define CS8920 0x4000
+#define CS8920M 0x6000
+#define REVISON_BITS 0x1F00
+#define EEVER_NUMBER 0x12
+#define CHKSUM_LEN 0x14
+#define CHKSUM_VAL 0x0000
+#define START_EEPROM_DATA 0x001c /* Offset into eeprom for start of data */
+#define IRQ_MAP_EEPROM_DATA 0x0046 /* Offset into eeprom for the IRQ map */
+#define IRQ_MAP_LEN 0x0004 /* No of bytes to read for the IRQ map */
+#define PNP_IRQ_FRMT 0x0022 /* PNP small item IRQ format */
+#define CS8900_IRQ_MAP 0x1c20 /* This IRQ map is fixed */
+
+#define CS8920_NO_INTS 0x0F /* Max CS8920 interrupt select # */
+
+#define PNP_ADD_PORT 0x0279
+#define PNP_WRITE_PORT 0x0A79
+
+#define GET_PNP_ISA_STRUCT 0x40
+#define PNP_ISA_STRUCT_LEN 0x06
+#define PNP_CSN_CNT_OFF 0x01
+#define PNP_RD_PORT_OFF 0x02
+#define PNP_FUNCTION_OK 0x00
+#define PNP_WAKE 0x03
+#define PNP_RSRC_DATA 0x04
+#define PNP_RSRC_READY 0x01
+#define PNP_STATUS 0x05
+#define PNP_ACTIVATE 0x30
+#define PNP_CNF_IO_H 0x60
+#define PNP_CNF_IO_L 0x61
+#define PNP_CNF_INT 0x70
+#define PNP_CNF_DMA 0x74
+#define PNP_CNF_MEM 0x48
+
+#define BIT0 1
+#define BIT15 0x8000
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/gpxe/src/drivers/net/cs89x0.txt b/gpxe/src/drivers/net/cs89x0.txt
new file mode 100644
index 00000000..72b7df28
--- /dev/null
+++ b/gpxe/src/drivers/net/cs89x0.txt
@@ -0,0 +1,45 @@
+/**
+ Per an email message from Russ Nelson <nelson@crynwr.com> on
+ 18 March 2008 the files cs89x0.[ch] are now licensed under GPL
+ Version 2.
+
+ From: Russ Nelson <nelson@crynwr.com>
+ Date: Tue, 18 Mar 2008 12:42:00 -0400
+ Subject: Re: [Etherboot-developers] cs89x0 driver in etherboot
+ -- quote from email
+ As copyright holder, if I say it doesn't conflict with the GPL,
+ then it doesn't conflict with the GPL.
+
+ However, there's no point in causing people's brains to overheat,
+ so yes, I grant permission for the code to be relicensed under the
+ GPLv2. Please make sure that this change in licensing makes its
+ way upstream. -russ
+ -- quote from email
+**/
+
+Permission is granted to distribute the enclosed cs89x0.[ch] driver
+only in conjunction with the Etherboot package. The code is
+ordinarily distributed under the GPL.
+
+Russ Nelson, January 2000
+
+CREDITS
+
+I want to thank
+
+ Mike Cruse <mcruse@cti-ltd.com>
+ for providing an evaluation NIC and for sponsoring the
+ development of this driver.
+
+ Randall Sears <sears@crystal.cirrus.com>
+ Deva Bodas <bodas@crystal.cirrus.com>
+ Andreas Kraemer <akraemer@crystal.cirrus.com>
+ Wolfgang Krause <100303.2673@compuserve.com>
+ for excellent technical support and for providing the required
+ programming information. I appreciate Crystal Semiconductor's
+ commitment towards free software.
+
+ Russell Nelson <nelson@crynwr.com>
+ for writing the Linux device driver for the CS89x0
+ chipset. Russel's code is very well designed and simplified my
+ job a lot.
diff --git a/gpxe/src/drivers/net/davicom.c b/gpxe/src/drivers/net/davicom.c
new file mode 100644
index 00000000..079e647e
--- /dev/null
+++ b/gpxe/src/drivers/net/davicom.c
@@ -0,0 +1,725 @@
+#ifdef ALLMULTI
+#error multicast support is not yet implemented
+#endif
+/*
+ DAVICOM DM9009/DM9102/DM9102A Etherboot Driver V1.00
+
+ This driver was ported from Marty Connor's Tulip Etherboot driver.
+ Thanks Marty Connor (mdc@etherboot.org)
+
+ This davicom etherboot driver supports DM9009/DM9102/DM9102A/
+ DM9102A+DM9801/DM9102A+DM9802 NICs.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+*/
+
+/*********************************************************************/
+/* Revision History */
+/*********************************************************************/
+
+/*
+ 19 OCT 2000 Sten 1.00
+ Different half and full duplex mode
+ Do the different programming for DM9801/DM9802
+
+ 12 OCT 2000 Sten 0.90
+ This driver was ported from tulip driver and it
+ has the following difference.
+ Changed symbol tulip/TULIP to davicom/DAVICOM
+ Deleted some code that did not use in this driver.
+ Used chain-strcture to replace ring structure
+ for both TX/RX descriptor.
+ Allocated two tx descriptor.
+ According current media mode to set operating
+ register(CR6)
+*/
+
+
+/*********************************************************************/
+/* Declarations */
+/*********************************************************************/
+
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+
+#undef DAVICOM_DEBUG
+#undef DAVICOM_DEBUG_WHERE
+
+#define TX_TIME_OUT 2*TICKS_PER_SEC
+
+/* Register offsets for davicom device */
+enum davicom_offsets {
+ CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+ CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+ CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x80, CSR20=0xA0
+};
+
+/* EEPROM Address width definitions */
+#define EEPROM_ADDRLEN 6
+#define EEPROM_SIZE 32 /* 1 << EEPROM_ADDRLEN */
+/* Used to be 128, but we only need to read enough to get the MAC
+ address at bytes 20..25 */
+
+/* Data Read from the EEPROM */
+static unsigned char ee_data[EEPROM_SIZE];
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << addr_len)
+#define EE_READ_CMD (6 << addr_len)
+#define EE_ERASE_CMD (7 << addr_len)
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
+#define EE_CS 0x01 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x01
+#define EE_WRITE_1 0x05
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+#define EE_ENB (0x4800 | EE_CS)
+
+/* Sten 10/11 for phyxcer */
+#define PHY_DATA_0 0x0
+#define PHY_DATA_1 0x20000
+#define MDCLKH 0x10000
+
+/* Delay between EEPROM clock transitions. Even at 33Mhz current PCI
+ implementations don't overrun the EEPROM clock. We add a bus
+ turn-around to insure that this remains true. */
+#define eeprom_delay() inl(ee_addr)
+
+/* helpful macro if on a big_endian machine for changing byte order.
+ not strictly needed on Intel
+ Already defined in Etherboot includes
+#define le16_to_cpu(val) (val)
+*/
+
+/* transmit and receive descriptor format */
+struct txdesc {
+ volatile unsigned long status; /* owner, status */
+ unsigned long buf1sz:11, /* size of buffer 1 */
+ buf2sz:11, /* size of buffer 2 */
+ control:10; /* control bits */
+ const unsigned char *buf1addr; /* buffer 1 address */
+ const unsigned char *buf2addr; /* buffer 2 address */
+};
+
+struct rxdesc {
+ volatile unsigned long status; /* owner, status */
+ unsigned long buf1sz:11, /* size of buffer 1 */
+ buf2sz:11, /* size of buffer 2 */
+ control:10; /* control bits */
+ unsigned char *buf1addr; /* buffer 1 address */
+ unsigned char *buf2addr; /* buffer 2 address */
+};
+
+/* Size of transmit and receive buffers */
+#define BUFLEN 1536
+
+/*********************************************************************/
+/* Global Storage */
+/*********************************************************************/
+
+static struct nic_operations davicom_operations;
+
+/* PCI Bus parameters */
+static unsigned short vendor, dev_id;
+static unsigned long ioaddr;
+
+/* Note: transmit and receive buffers must be longword aligned and
+ longword divisable */
+
+/* transmit descriptor and buffer */
+#define NTXD 2
+#define NRXD 4
+struct {
+ struct txdesc txd[NTXD] __attribute__ ((aligned(4)));
+ unsigned char txb[BUFLEN] __attribute__ ((aligned(4)));
+ struct rxdesc rxd[NRXD] __attribute__ ((aligned(4)));
+ unsigned char rxb[NRXD * BUFLEN] __attribute__ ((aligned(4)));
+} davicom_bufs __shared;
+#define txd davicom_bufs.txd
+#define txb davicom_bufs.txb
+#define rxd davicom_bufs.rxd
+#define rxb davicom_bufs.rxb
+static int rxd_tail;
+static int TxPtr;
+
+
+/*********************************************************************/
+/* Function Prototypes */
+/*********************************************************************/
+static void whereami(const char *str);
+static int read_eeprom(unsigned long ioaddr, int location, int addr_len);
+static int davicom_probe(struct nic *nic,struct pci_device *pci);
+static void davicom_init_chain(struct nic *nic); /* Sten 10/9 */
+static void davicom_reset(struct nic *nic);
+static void davicom_transmit(struct nic *nic, const char *d, unsigned int t,
+ unsigned int s, const char *p);
+static int davicom_poll(struct nic *nic, int retrieve);
+static void davicom_disable(struct nic *nic);
+#ifdef DAVICOM_DEBUG
+static void davicom_more(void);
+#endif /* DAVICOM_DEBUG */
+static void davicom_wait(unsigned int nticks);
+static int phy_read(int);
+static void phy_write(int, u16);
+static void phy_write_1bit(u32, u32);
+static int phy_read_1bit(u32);
+static void davicom_media_chk(struct nic *);
+
+
+/*********************************************************************/
+/* Utility Routines */
+/*********************************************************************/
+static inline void whereami(const char *str)
+{
+ printf("%s\n", str);
+ /* sleep(2); */
+}
+
+#ifdef DAVICOM_DEBUG
+static void davicom_more()
+{
+ printf("\n\n-- more --");
+ while (!iskey())
+ /* wait */;
+ getchar();
+ printf("\n\n");
+}
+#endif /* DAVICOM_DEBUG */
+
+static void davicom_wait(unsigned int nticks)
+{
+ unsigned int to = currticks() + nticks;
+ while (currticks() < to)
+ /* wait */ ;
+}
+
+
+/*********************************************************************/
+/* For DAVICOM phyxcer register by MII interface */
+/*********************************************************************/
+/*
+ Read a word data from phy register
+*/
+static int phy_read(int location)
+{
+ int i, phy_addr=1;
+ u16 phy_data;
+ u32 io_dcr9;
+
+ whereami("phy_read\n");
+
+ io_dcr9 = ioaddr + CSR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i=0; i<34; i++)
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+
+ /* Send start command(01) to Phy */
+ phy_write_1bit(io_dcr9, PHY_DATA_0);
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+
+ /* Send read command(10) to Phy */
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+ phy_write_1bit(io_dcr9, PHY_DATA_0);
+
+ /* Send Phy addres */
+ for (i=0x10; i>0; i=i>>1)
+ phy_write_1bit(io_dcr9, phy_addr&i ? PHY_DATA_1: PHY_DATA_0);
+
+ /* Send register addres */
+ for (i=0x10; i>0; i=i>>1)
+ phy_write_1bit(io_dcr9, location&i ? PHY_DATA_1: PHY_DATA_0);
+
+ /* Skip transition state */
+ phy_read_1bit(io_dcr9);
+
+ /* read 16bit data */
+ for (phy_data=0, i=0; i<16; i++) {
+ phy_data<<=1;
+ phy_data|=phy_read_1bit(io_dcr9);
+ }
+
+ return phy_data;
+}
+
+/*
+ Write a word to Phy register
+*/
+static void phy_write(int location, u16 phy_data)
+{
+ u16 i, phy_addr=1;
+ u32 io_dcr9;
+
+ whereami("phy_write\n");
+
+ io_dcr9 = ioaddr + CSR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i=0; i<34; i++)
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+
+ /* Send start command(01) to Phy */
+ phy_write_1bit(io_dcr9, PHY_DATA_0);
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+
+ /* Send write command(01) to Phy */
+ phy_write_1bit(io_dcr9, PHY_DATA_0);
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+
+ /* Send Phy addres */
+ for (i=0x10; i>0; i=i>>1)
+ phy_write_1bit(io_dcr9, phy_addr&i ? PHY_DATA_1: PHY_DATA_0);
+
+ /* Send register addres */
+ for (i=0x10; i>0; i=i>>1)
+ phy_write_1bit(io_dcr9, location&i ? PHY_DATA_1: PHY_DATA_0);
+
+ /* written trasnition */
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+ phy_write_1bit(io_dcr9, PHY_DATA_0);
+
+ /* Write a word data to PHY controller */
+ for (i=0x8000; i>0; i>>=1)
+ phy_write_1bit(io_dcr9, phy_data&i ? PHY_DATA_1: PHY_DATA_0);
+}
+
+/*
+ Write one bit data to Phy Controller
+*/
+static void phy_write_1bit(u32 ee_addr, u32 phy_data)
+{
+ whereami("phy_write_1bit\n");
+ outl(phy_data, ee_addr); /* MII Clock Low */
+ eeprom_delay();
+ outl(phy_data|MDCLKH, ee_addr); /* MII Clock High */
+ eeprom_delay();
+ outl(phy_data, ee_addr); /* MII Clock Low */
+ eeprom_delay();
+}
+
+/*
+ Read one bit phy data from PHY controller
+*/
+static int phy_read_1bit(u32 ee_addr)
+{
+ int phy_data;
+
+ whereami("phy_read_1bit\n");
+
+ outl(0x50000, ee_addr);
+ eeprom_delay();
+
+ phy_data=(inl(ee_addr)>>19) & 0x1;
+
+ outl(0x40000, ee_addr);
+ eeprom_delay();
+
+ return phy_data;
+}
+
+/*
+ DM9801/DM9802 present check and program
+*/
+static void HPNA_process(void)
+{
+
+ if ( (phy_read(3) & 0xfff0) == 0xb900 ) {
+ if ( phy_read(31) == 0x4404 ) {
+ /* DM9801 present */
+ if (phy_read(3) == 0xb901)
+ phy_write(16, 0x5); /* DM9801 E4 */
+ else
+ phy_write(16, 0x1005); /* DM9801 E3 and others */
+ phy_write(25, ((phy_read(24) + 3) & 0xff) | 0xf000);
+ } else {
+ /* DM9802 present */
+ phy_write(16, 0x5);
+ phy_write(25, (phy_read(25) & 0xff00) + 2);
+ }
+ }
+}
+
+/*
+ Sense media mode and set CR6
+*/
+static void davicom_media_chk(struct nic * nic __unused)
+{
+ unsigned long to, csr6;
+
+ csr6 = 0x00200000; /* SF */
+ outl(csr6, ioaddr + CSR6);
+
+#define PCI_DEVICE_ID_DM9009 0x9009
+ if (vendor == PCI_VENDOR_ID_DAVICOM && dev_id == PCI_DEVICE_ID_DM9009) {
+ /* Set to 10BaseT mode for DM9009 */
+ phy_write(0, 0);
+ } else {
+ /* For DM9102/DM9102A */
+ to = currticks() + 2 * TICKS_PER_SEC;
+ while ( ((phy_read(1) & 0x24)!=0x24) && (currticks() < to))
+ /* wait */ ;
+
+ if ( (phy_read(1) & 0x24) == 0x24 ) {
+ if (phy_read(17) & 0xa000)
+ csr6 |= 0x00000200; /* Full Duplex mode */
+ } else
+ csr6 |= 0x00040000; /* Select DM9801/DM9802 when Ethernet link failed */
+ }
+
+ /* set the chip's operating mode */
+ outl(csr6, ioaddr + CSR6);
+
+ /* DM9801/DM9802 present check & program */
+ if (csr6 & 0x40000)
+ HPNA_process();
+}
+
+
+/*********************************************************************/
+/* EEPROM Reading Code */
+/*********************************************************************/
+/* EEPROM routines adapted from the Linux Tulip Code */
+/* Reading a serial EEPROM is a "bit" grungy, but we work our way
+ through:->.
+*/
+static int read_eeprom(unsigned long ioaddr, int location, int addr_len)
+{
+ int i;
+ unsigned short retval = 0;
+ long ee_addr = ioaddr + CSR9;
+ int read_cmd = location | EE_READ_CMD;
+
+ whereami("read_eeprom\n");
+
+ outl(EE_ENB & ~EE_CS, ee_addr);
+ outl(EE_ENB, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 4 + addr_len; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ outl(EE_ENB | dataval, ee_addr);
+ eeprom_delay();
+ outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ }
+ outl(EE_ENB, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ outl(EE_ENB, ee_addr);
+ eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ outl(EE_ENB & ~EE_CS, ee_addr);
+ return retval;
+}
+
+/*********************************************************************/
+/* davicom_init_chain - setup the tx and rx descriptors */
+/* Sten 10/9 */
+/*********************************************************************/
+static void davicom_init_chain(struct nic *nic)
+{
+ int i;
+
+ /* setup the transmit descriptor */
+ /* Sten: Set 2 TX descriptor but use one TX buffer because
+ it transmit a packet and wait complete every time. */
+ for (i=0; i<NTXD; i++) {
+ txd[i].buf1addr = (void *)virt_to_bus(&txb[0]); /* Used same TX buffer */
+ txd[i].buf2addr = (void *)virt_to_bus(&txd[i+1]); /* Point to Next TX desc */
+ txd[i].buf1sz = 0;
+ txd[i].buf2sz = 0;
+ txd[i].control = 0x184; /* Begin/End/Chain */
+ txd[i].status = 0x00000000; /* give ownership to Host */
+ }
+
+ /* construct perfect filter frame with mac address as first match
+ and broadcast address for all others */
+ for (i=0; i<192; i++) txb[i] = 0xFF;
+ txb[0] = nic->node_addr[0];
+ txb[1] = nic->node_addr[1];
+ txb[4] = nic->node_addr[2];
+ txb[5] = nic->node_addr[3];
+ txb[8] = nic->node_addr[4];
+ txb[9] = nic->node_addr[5];
+
+ /* setup receive descriptor */
+ for (i=0; i<NRXD; i++) {
+ rxd[i].buf1addr = (void *)virt_to_bus(&rxb[i * BUFLEN]);
+ rxd[i].buf2addr = (void *)virt_to_bus(&rxd[i+1]); /* Point to Next RX desc */
+ rxd[i].buf1sz = BUFLEN;
+ rxd[i].buf2sz = 0; /* not used */
+ rxd[i].control = 0x4; /* Chain Structure */
+ rxd[i].status = 0x80000000; /* give ownership to device */
+ }
+
+ /* Chain the last descriptor to first */
+ txd[NTXD - 1].buf2addr = (void *)virt_to_bus(&txd[0]);
+ rxd[NRXD - 1].buf2addr = (void *)virt_to_bus(&rxd[0]);
+ TxPtr = 0;
+ rxd_tail = 0;
+}
+
+
+/*********************************************************************/
+/* davicom_reset - Reset adapter */
+/*********************************************************************/
+static void davicom_reset(struct nic *nic)
+{
+ unsigned long to;
+
+ whereami("davicom_reset\n");
+
+ /* Stop Tx and RX */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
+ outl(0x00000001, ioaddr + CSR0);
+
+ davicom_wait(TICKS_PER_SEC);
+
+ /* TX/RX descriptor burst */
+ outl(0x0C00000, ioaddr + CSR0); /* Sten 10/9 */
+
+ /* set up transmit and receive descriptors */
+ davicom_init_chain(nic); /* Sten 10/9 */
+
+ /* Point to receive descriptor */
+ outl(virt_to_bus(&rxd[0]), ioaddr + CSR3);
+ outl(virt_to_bus(&txd[0]), ioaddr + CSR4); /* Sten 10/9 */
+
+ /* According phyxcer media mode to set CR6,
+ DM9102/A phyxcer can auto-detect media mode */
+ davicom_media_chk(nic);
+
+ /* Prepare Setup Frame Sten 10/9 */
+ txd[TxPtr].buf1sz = 192;
+ txd[TxPtr].control = 0x024; /* SF/CE */
+ txd[TxPtr].status = 0x80000000; /* Give ownership to device */
+
+ /* Start Tx */
+ outl(inl(ioaddr + CSR6) | 0x00002000, ioaddr + CSR6);
+ /* immediate transmit demand */
+ outl(0, ioaddr + CSR1);
+
+ to = currticks() + TX_TIME_OUT;
+ while ((txd[TxPtr].status & 0x80000000) && (currticks() < to)) /* Sten 10/9 */
+ /* wait */ ;
+
+ if (currticks() >= to) {
+ printf ("TX Setup Timeout!\n");
+ }
+ /* Point to next TX descriptor */
+ TxPtr = (++TxPtr >= NTXD) ? 0:TxPtr; /* Sten 10/9 */
+
+#ifdef DAVICOM_DEBUG
+ printf("txd.status = %X\n", txd.status);
+ printf("ticks = %d\n", currticks() - (to - TX_TIME_OUT));
+ davicom_more();
+#endif
+
+ /* enable RX */
+ outl(inl(ioaddr + CSR6) | 0x00000002, ioaddr + CSR6);
+ /* immediate poll demand */
+ outl(0, ioaddr + CSR2);
+}
+
+
+/*********************************************************************/
+/* eth_transmit - Transmit a frame */
+/*********************************************************************/
+static void davicom_transmit(struct nic *nic, const char *d, unsigned int t,
+ unsigned int s, const char *p)
+{
+ unsigned long to;
+
+ whereami("davicom_transmit\n");
+
+ /* Stop Tx */
+ /* outl(inl(ioaddr + CSR6) & ~0x00002000, ioaddr + CSR6); */
+
+ /* setup ethernet header */
+ memcpy(&txb[0], d, ETH_ALEN); /* DA 6byte */
+ memcpy(&txb[ETH_ALEN], nic->node_addr, ETH_ALEN); /* SA 6byte*/
+ txb[ETH_ALEN*2] = (t >> 8) & 0xFF; /* Frame type: 2byte */
+ txb[ETH_ALEN*2+1] = t & 0xFF;
+ memcpy(&txb[ETH_HLEN], p, s); /* Frame data */
+
+ /* setup the transmit descriptor */
+ txd[TxPtr].buf1sz = ETH_HLEN+s;
+ txd[TxPtr].control = 0x00000184; /* LS+FS+CE */
+ txd[TxPtr].status = 0x80000000; /* give ownership to device */
+
+ /* immediate transmit demand */
+ outl(0, ioaddr + CSR1);
+
+ to = currticks() + TX_TIME_OUT;
+ while ((txd[TxPtr].status & 0x80000000) && (currticks() < to))
+ /* wait */ ;
+
+ if (currticks() >= to) {
+ printf ("TX Timeout!\n");
+ }
+
+ /* Point to next TX descriptor */
+ TxPtr = (++TxPtr >= NTXD) ? 0:TxPtr; /* Sten 10/9 */
+
+}
+
+/*********************************************************************/
+/* eth_poll - Wait for a frame */
+/*********************************************************************/
+static int davicom_poll(struct nic *nic, int retrieve)
+{
+ whereami("davicom_poll\n");
+
+ if (rxd[rxd_tail].status & 0x80000000)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ whereami("davicom_poll got one\n");
+
+ nic->packetlen = (rxd[rxd_tail].status & 0x3FFF0000) >> 16;
+
+ if( rxd[rxd_tail].status & 0x00008000){
+ rxd[rxd_tail].status = 0x80000000;
+ rxd_tail++;
+ if (rxd_tail == NRXD) rxd_tail = 0;
+ return 0;
+ }
+
+ /* copy packet to working buffer */
+ /* XXX - this copy could be avoided with a little more work
+ but for now we are content with it because the optimised
+ memcpy is quite fast */
+
+ memcpy(nic->packet, rxb + rxd_tail * BUFLEN, nic->packetlen);
+
+ /* return the descriptor and buffer to receive ring */
+ rxd[rxd_tail].status = 0x80000000;
+ rxd_tail++;
+ if (rxd_tail == NRXD) rxd_tail = 0;
+
+ return 1;
+}
+
+/*********************************************************************/
+/* eth_disable - Disable the interface */
+/*********************************************************************/
+static void davicom_disable ( struct nic *nic ) {
+
+ whereami("davicom_disable\n");
+
+ davicom_reset(nic);
+
+ /* disable interrupts */
+ outl(0x00000000, ioaddr + CSR7);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* Clear the missed-packet counter. */
+ (volatile unsigned long)inl(ioaddr + CSR8);
+}
+
+
+/*********************************************************************/
+/* eth_irq - enable, disable and force interrupts */
+/*********************************************************************/
+static void davicom_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+
+/*********************************************************************/
+/* eth_probe - Look for an adapter */
+/*********************************************************************/
+static int davicom_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ unsigned int i;
+
+ whereami("davicom_probe\n");
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ vendor = pci->vendor;
+ dev_id = pci->device;
+ ioaddr = pci->ioaddr;
+
+ nic->ioaddr = pci->ioaddr;
+ nic->irqno = 0;
+
+ /* wakeup chip */
+ pci_write_config_dword(pci, 0x40, 0x00000000);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* Clear the missed-packet counter. */
+ (volatile unsigned long)inl(ioaddr + CSR8);
+
+ /* Get MAC Address */
+ /* read EEPROM data */
+ for (i = 0; i < sizeof(ee_data)/2; i++)
+ ((unsigned short *)ee_data)[i] =
+ le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
+
+ /* extract MAC address from EEPROM buffer */
+ for (i=0; i<ETH_ALEN; i++)
+ nic->node_addr[i] = ee_data[20+i];
+
+ DBG ( "Davicom %s at IOADDR %4.4lx\n", eth_ntoa ( nic->node_addr ), ioaddr );
+
+ /* initialize device */
+ davicom_reset(nic);
+ nic->nic_op = &davicom_operations;
+ return 1;
+}
+
+static struct nic_operations davicom_operations = {
+ .connect = dummy_connect,
+ .poll = davicom_poll,
+ .transmit = davicom_transmit,
+ .irq = davicom_irq,
+
+};
+
+static struct pci_device_id davicom_nics[] = {
+PCI_ROM(0x1282, 0x9100, "davicom9100", "Davicom 9100"),
+PCI_ROM(0x1282, 0x9102, "davicom9102", "Davicom 9102"),
+PCI_ROM(0x1282, 0x9009, "davicom9009", "Davicom 9009"),
+PCI_ROM(0x1282, 0x9132, "davicom9132", "Davicom 9132"), /* Needs probably some fixing */
+};
+
+PCI_DRIVER ( davicom_driver, davicom_nics, PCI_NO_CLASS );
+
+DRIVER ( "DAVICOM", nic_driver, pci_driver, davicom_driver,
+ davicom_probe, davicom_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/depca.c b/gpxe/src/drivers/net/depca.c
new file mode 100644
index 00000000..7372e604
--- /dev/null
+++ b/gpxe/src/drivers/net/depca.c
@@ -0,0 +1,803 @@
+/* #warning "depca.c: FIXME: fix relocation" */
+
+#if 0
+/* Not fixed for relocation yet. Probably won't work relocated above 16MB */
+#ifdef ALLMULTI
+#error multicast support is not yet implemented
+#endif
+/* Etherboot: depca.h merged, comments from Linux driver retained */
+/* depca.c: A DIGITAL DEPCA & EtherWORKS ethernet driver for linux.
+
+ Written 1994, 1995 by David C. Davies.
+
+
+ Copyright 1994 David C. Davies
+ and
+ United States Government
+ (as represented by the Director, National Security Agency).
+
+ Copyright 1995 Digital Equipment Corporation.
+
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+
+ This driver is written for the Digital Equipment Corporation series
+ of DEPCA and EtherWORKS ethernet cards:
+
+ DEPCA (the original)
+ DE100
+ DE101
+ DE200 Turbo
+ DE201 Turbo
+ DE202 Turbo (TP BNC)
+ DE210
+ DE422 (EISA)
+
+ The driver has been tested on DE100, DE200 and DE202 cards in a
+ relatively busy network. The DE422 has been tested a little.
+
+ This driver will NOT work for the DE203, DE204 and DE205 series of
+ cards, since they have a new custom ASIC in place of the AMD LANCE
+ chip. See the 'ewrk3.c' driver in the Linux source tree for running
+ those cards.
+
+ I have benchmarked the driver with a DE100 at 595kB/s to (542kB/s from)
+ a DECstation 5000/200.
+
+ The author may be reached at davies@maniac.ultranet.com
+
+ =========================================================================
+
+ The driver was originally based on the 'lance.c' driver from Donald
+ Becker which is included with the standard driver distribution for
+ linux. V0.4 is a complete re-write with only the kernel interface
+ remaining from the original code.
+
+ 1) Lance.c code in /linux/drivers/net/
+ 2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook",
+ AMD, 1992 [(800) 222-9323].
+ 3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)",
+ AMD, Pub. #17881, May 1993.
+ 4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA",
+ AMD, Pub. #16907, May 1992
+ 5) "DEC EtherWORKS LC Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003
+ 6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003
+ 7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR
+ Digital Equipment Corporation, 1989
+ 8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001
+
+
+ Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this
+ driver.
+
+ The original DEPCA card requires that the ethernet ROM address counter
+ be enabled to count and has an 8 bit NICSR. The ROM counter enabling is
+ only done when a 0x08 is read as the first address octet (to minimise
+ the chances of writing over some other hardware's I/O register). The
+ NICSR accesses have been changed to byte accesses for all the cards
+ supported by this driver, since there is only one useful bit in the MSB
+ (remote boot timeout) and it is not used. Also, there is a maximum of
+ only 48kB network RAM for this card. My thanks to Torbjorn Lindh for
+ help debugging all this (and holding my feet to the fire until I got it
+ right).
+
+ The DE200 series boards have on-board 64kB RAM for use as a shared
+ memory network buffer. Only the DE100 cards make use of a 2kB buffer
+ mode which has not been implemented in this driver (only the 32kB and
+ 64kB modes are supported [16kB/48kB for the original DEPCA]).
+
+ At the most only 2 DEPCA cards can be supported on the ISA bus because
+ there is only provision for two I/O base addresses on each card (0x300
+ and 0x200). The I/O address is detected by searching for a byte sequence
+ in the Ethernet station address PROM at the expected I/O address for the
+ Ethernet PROM. The shared memory base address is 'autoprobed' by
+ looking for the self test PROM and detecting the card name. When a
+ second DEPCA is detected, information is placed in the base_addr
+ variable of the next device structure (which is created if necessary),
+ thus enabling ethif_probe initialization for the device. More than 2
+ EISA cards can be supported, but care will be needed assigning the
+ shared memory to ensure that each slot has the correct IRQ, I/O address
+ and shared memory address assigned.
+
+ ************************************************************************
+
+ NOTE: If you are using two ISA DEPCAs, it is important that you assign
+ the base memory addresses correctly. The driver autoprobes I/O 0x300
+ then 0x200. The base memory address for the first device must be less
+ than that of the second so that the auto probe will correctly assign the
+ I/O and memory addresses on the same card. I can't think of a way to do
+ this unambiguously at the moment, since there is nothing on the cards to
+ tie I/O and memory information together.
+
+ I am unable to test 2 cards together for now, so this code is
+ unchecked. All reports, good or bad, are welcome.
+
+ ************************************************************************
+
+ The board IRQ setting must be at an unused IRQ which is auto-probed
+ using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are
+ {2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is
+ really IRQ9 in machines with 16 IRQ lines.
+
+ No 16MB memory limitation should exist with this driver as DMA is not
+ used and the common memory area is in low memory on the network card (my
+ current system has 20MB and I've not had problems yet).
+
+ The ability to load this driver as a loadable module has been added. To
+ utilise this ability, you have to do <8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy depca.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) if you wish, edit the source code near line 1530 to reflect the I/O
+ address and IRQ you're using (see also 5).
+ 3) compile depca.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the depca configuration turned off and reboot.
+ 5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100]
+ [Alan Cox: Changed the code to allow command line irq/io assignments]
+ [Dave Davies: Changed the code to allow command line mem/name
+ assignments]
+ 6) run the net startup bits for your eth?? interface manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ Note that autoprobing is not allowed in loadable modules - the system is
+ already up and running and you're messing with interrupts.
+
+ To unload a module, turn off the associated interface
+ 'ifconfig eth?? down' then 'rmmod depca'.
+
+ To assign a base memory address for the shared memory when running as a
+ loadable module, see 5 above. To include the adapter name (if you have
+ no PROM but know the card name) also see 5 above. Note that this last
+ option will not work with kernel built-in depca's.
+
+ The shared memory assignment for a loadable module makes sense to avoid
+ the 'memory autoprobe' picking the wrong shared memory (for the case of
+ 2 depca's in a PC).
+
+ ************************************************************************
+ Support for MCA EtherWORKS cards added 11-3-98.
+ Verified to work with up to 2 DE212 cards in a system (although not
+ fully stress-tested).
+
+ Currently known bugs/limitations:
+
+ Note: with the MCA stuff as a module, it trusts the MCA configuration,
+ not the command line for IRQ and memory address. You can
+ specify them if you want, but it will throw your values out.
+ You still have to pass the IO address it was configured as
+ though.
+
+ ************************************************************************
+ TO DO:
+ ------
+
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 25-jan-94 Initial writing.
+ 0.2 27-jan-94 Added LANCE TX hardware buffer chaining.
+ 0.3 1-feb-94 Added multiple DEPCA support.
+ 0.31 4-feb-94 Added DE202 recognition.
+ 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support.
+ 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable.
+ Add jabber packet fix from murf@perftech.com
+ and becker@super.org
+ 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access.
+ 0.35 8-mar-94 Added DE201 recognition. Tidied up.
+ 0.351 30-apr-94 Added EISA support. Added DE422 recognition.
+ 0.36 16-may-94 DE422 fix released.
+ 0.37 22-jul-94 Added MODULE support
+ 0.38 15-aug-94 Added DBR ROM switch in depca_close().
+ Multi DEPCA bug fix.
+ 0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0.
+ 0.381 12-dec-94 Added DE101 recognition, fix multicast bug.
+ 0.382 9-feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ 0.383 22-feb-95 Fix for conflict with VESA SCSI reported by
+ <stromain@alf.dec.com>
+ 0.384 17-mar-95 Fix a ring full bug reported by <bkm@star.rl.ac.uk>
+ 0.385 3-apr-95 Fix a recognition bug reported by
+ <ryan.niemi@lastfrontier.com>
+ 0.386 21-apr-95 Fix the last fix...sorry, must be galloping senility
+ 0.40 25-May-95 Rewrite for portability & updated.
+ ALPHA support from <jestabro@amt.tay1.dec.com>
+ 0.41 26-Jun-95 Added verify_area() calls in depca_ioctl() from
+ suggestion by <heiko@colossus.escape.de>
+ 0.42 27-Dec-95 Add 'mem' shared memory assignment for loadable
+ modules.
+ Add 'adapter_name' for loadable modules when no PROM.
+ Both above from a suggestion by
+ <pchen@woodruffs121.residence.gatech.edu>.
+ Add new multicasting code.
+ 0.421 22-Apr-96 Fix alloc_device() bug <jari@markkus2.fimr.fi>
+ 0.422 29-Apr-96 Fix depca_hw_init() bug <jari@markkus2.fimr.fi>
+ 0.423 7-Jun-96 Fix module load bug <kmg@barco.be>
+ 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c
+ 0.44 1-Sep-97 Fix *_probe() to test check_region() first - bug
+ reported by <mmogilvi@elbert.uccs.edu>
+ 0.45 3-Nov-98 Added support for MCA EtherWORKS (DE210/DE212) cards
+ by <tymm@computer.org>
+ 0.451 5-Nov-98 Fixed mca stuff cuz I'm a dummy. <tymm@computer.org>
+ 0.5 14-Nov-98 Re-spin for 2.1.x kernels.
+ 0.51 27-Jun-99 Correct received packet length for CRC from
+ report by <worm@dkik.dk>
+
+ =========================================================================
+*/
+
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/isa.h>
+#include "console.h"
+#include <gpxe/ethernet.h>
+
+/*
+** I/O addresses. Note that the 2k buffer option is not supported in
+** this driver.
+*/
+#define DEPCA_NICSR 0x00 /* Network interface CSR */
+#define DEPCA_RBI 0x02 /* RAM buffer index (2k buffer mode) */
+#define DEPCA_DATA 0x04 /* LANCE registers' data port */
+#define DEPCA_ADDR 0x06 /* LANCE registers' address port */
+#define DEPCA_HBASE 0x08 /* EISA high memory base address reg. */
+#define DEPCA_PROM 0x0c /* Ethernet address ROM data port */
+#define DEPCA_CNFG 0x0c /* EISA Configuration port */
+#define DEPCA_RBSA 0x0e /* RAM buffer starting address (2k buff.) */
+
+/*
+** These are LANCE registers addressable through nic->ioaddr + DEPCA_ADDR
+*/
+#define CSR0 0
+#define CSR1 1
+#define CSR2 2
+#define CSR3 3
+
+/*
+** NETWORK INTERFACE CSR (NI_CSR) bit definitions
+*/
+
+#define TO 0x0100 /* Time Out for remote boot */
+#define SHE 0x0080 /* SHadow memory Enable */
+#define BS 0x0040 /* Bank Select */
+#define BUF 0x0020 /* BUFfer size (1->32k, 0->64k) */
+#define RBE 0x0010 /* Remote Boot Enable (1->net boot) */
+#define AAC 0x0008 /* Address ROM Address Counter (1->enable) */
+#define _128KB 0x0008 /* 128kB Network RAM (1->enable) */
+#define IM 0x0004 /* Interrupt Mask (1->mask) */
+#define IEN 0x0002 /* Interrupt tristate ENable (1->enable) */
+#define LED 0x0001 /* LED control */
+
+/*
+** Control and Status Register 0 (CSR0) bit definitions
+*/
+
+#define ERR 0x8000 /* Error summary */
+#define BABL 0x4000 /* Babble transmitter timeout error */
+#define CERR 0x2000 /* Collision Error */
+#define MISS 0x1000 /* Missed packet */
+#define MERR 0x0800 /* Memory Error */
+#define RINT 0x0400 /* Receiver Interrupt */
+#define TINT 0x0200 /* Transmit Interrupt */
+#define IDON 0x0100 /* Initialization Done */
+#define INTR 0x0080 /* Interrupt Flag */
+#define INEA 0x0040 /* Interrupt Enable */
+#define RXON 0x0020 /* Receiver on */
+#define TXON 0x0010 /* Transmitter on */
+#define TDMD 0x0008 /* Transmit Demand */
+#define STOP 0x0004 /* Stop */
+#define STRT 0x0002 /* Start */
+#define INIT 0x0001 /* Initialize */
+#define INTM 0xff00 /* Interrupt Mask */
+#define INTE 0xfff0 /* Interrupt Enable */
+
+/*
+** CONTROL AND STATUS REGISTER 3 (CSR3)
+*/
+
+#define BSWP 0x0004 /* Byte SWaP */
+#define ACON 0x0002 /* ALE control */
+#define BCON 0x0001 /* Byte CONtrol */
+
+/*
+** Initialization Block Mode Register
+*/
+
+#define PROM 0x8000 /* Promiscuous Mode */
+#define EMBA 0x0080 /* Enable Modified Back-off Algorithm */
+#define INTL 0x0040 /* Internal Loopback */
+#define DRTY 0x0020 /* Disable Retry */
+#define COLL 0x0010 /* Force Collision */
+#define DTCR 0x0008 /* Disable Transmit CRC */
+#define LOOP 0x0004 /* Loopback */
+#define DTX 0x0002 /* Disable the Transmitter */
+#define DRX 0x0001 /* Disable the Receiver */
+
+/*
+** Receive Message Descriptor 1 (RMD1) bit definitions.
+*/
+
+#define R_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
+#define R_ERR 0x4000 /* Error Summary */
+#define R_FRAM 0x2000 /* Framing Error */
+#define R_OFLO 0x1000 /* Overflow Error */
+#define R_CRC 0x0800 /* CRC Error */
+#define R_BUFF 0x0400 /* Buffer Error */
+#define R_STP 0x0200 /* Start of Packet */
+#define R_ENP 0x0100 /* End of Packet */
+
+/*
+** Transmit Message Descriptor 1 (TMD1) bit definitions.
+*/
+
+#define T_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
+#define T_ERR 0x4000 /* Error Summary */
+#define T_ADD_FCS 0x2000 /* More the 1 retry needed to Xmit */
+#define T_MORE 0x1000 /* >1 retry to transmit packet */
+#define T_ONE 0x0800 /* 1 try needed to transmit the packet */
+#define T_DEF 0x0400 /* Deferred */
+#define T_STP 0x02000000 /* Start of Packet */
+#define T_ENP 0x01000000 /* End of Packet */
+#define T_FLAGS 0xff000000 /* TX Flags Field */
+
+/*
+** Transmit Message Descriptor 3 (TMD3) bit definitions.
+*/
+
+#define TMD3_BUFF 0x8000 /* BUFFer error */
+#define TMD3_UFLO 0x4000 /* UnderFLOw error */
+#define TMD3_RES 0x2000 /* REServed */
+#define TMD3_LCOL 0x1000 /* Late COLlision */
+#define TMD3_LCAR 0x0800 /* Loss of CARrier */
+#define TMD3_RTRY 0x0400 /* ReTRY error */
+
+/*
+** Ethernet PROM defines
+*/
+#define PROBE_LENGTH 32
+
+/*
+** Set the number of Tx and Rx buffers. Ensure that the memory requested
+** here is <= to the amount of shared memory set up by the board switches.
+** The number of descriptors MUST BE A POWER OF 2.
+**
+** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ)
+*/
+#define NUM_RX_DESC 2 /* Number of RX descriptors */
+#define NUM_TX_DESC 2 /* Number of TX descriptors */
+#define RX_BUFF_SZ 1536 /* Buffer size for each Rx buffer */
+#define TX_BUFF_SZ 1536 /* Buffer size for each Tx buffer */
+
+/*
+** ISA Bus defines
+*/
+#ifndef DEPCA_MODEL
+#define DEPCA_MODEL DEPCA
+#endif
+
+static enum {
+ DEPCA, DE100, DE101, DE200, DE201, DE202, DE210, DE212, DE422, unknown
+} adapter = DEPCA_MODEL;
+
+/*
+** Name <-> Adapter mapping
+*/
+
+static char *adapter_name[] = {
+ "DEPCA",
+ "DE100","DE101",
+ "DE200","DE201","DE202",
+ "DE210","DE212",
+ "DE422",
+ ""
+};
+
+#ifndef DEPCA_RAM_BASE
+#define DEPCA_RAM_BASE 0xd0000
+#endif
+
+/*
+** Memory Alignment. Each descriptor is 4 longwords long. To force a
+** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and
+** DESC_ALIGN. ALIGN aligns the start address of the private memory area
+** and hence the RX descriptor ring's first entry.
+*/
+#define ALIGN4 ((u32)4 - 1) /* 1 longword align */
+#define ALIGN8 ((u32)8 - 1) /* 2 longword (quadword) align */
+#define ALIGN ALIGN8 /* Keep the LANCE happy... */
+
+/*
+** The DEPCA Rx and Tx ring descriptors.
+*/
+struct depca_rx_desc {
+ volatile s32 base;
+ s16 buf_length; /* This length is negative 2's complement! */
+ s16 msg_length; /* This length is "normal". */
+};
+
+struct depca_tx_desc {
+ volatile s32 base;
+ s16 length; /* This length is negative 2's complement! */
+ s16 misc; /* Errors and TDR info */
+};
+
+#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM
+ to LANCE memory address space */
+
+/*
+** The Lance initialization block, described in databook, in common memory.
+*/
+struct depca_init {
+ u16 mode; /* Mode register */
+ u8 phys_addr[ETH_ALEN]; /* Physical ethernet address */
+ u8 mcast_table[8]; /* Multicast Hash Table. */
+ u32 rx_ring; /* Rx ring base pointer & ring length */
+ u32 tx_ring; /* Tx ring base pointer & ring length */
+};
+
+struct depca_private {
+ struct depca_rx_desc *rx_ring;
+ struct depca_tx_desc *tx_ring;
+ struct depca_init init_block; /* Shadow init block */
+ char *rx_memcpy[NUM_RX_DESC];
+ char *tx_memcpy[NUM_TX_DESC];
+ u32 bus_offset; /* ISA bus address offset */
+ u32 sh_mem; /* address of shared mem */
+ u32 dma_buffs; /* Rx & Tx buffer start */
+ int rx_cur, tx_cur; /* Next free ring entry */
+ int txRingMask, rxRingMask;
+ s32 rx_rlen, tx_rlen;
+ /* log2([rt]xRingMask+1) for the descriptors */
+};
+
+static Address mem_start = DEPCA_RAM_BASE;
+static Address mem_len, offset;
+static struct depca_private lp;
+
+/*
+** Miscellaneous defines...
+*/
+#define STOP_DEPCA(ioaddr) \
+ outw(CSR0, ioaddr + DEPCA_ADDR);\
+ outw(STOP, ioaddr + DEPCA_DATA)
+
+/* Initialize the lance Rx and Tx descriptor rings. */
+static void depca_init_ring(struct nic *nic)
+{
+ int i;
+ u32 p;
+
+ lp.rx_cur = lp.tx_cur = 0;
+ /* Initialize the base addresses and length of each buffer in the ring */
+ for (i = 0; i <= lp.rxRingMask; i++) {
+ writel((p = lp.dma_buffs + i * RX_BUFF_SZ) | R_OWN, &lp.rx_ring[i].base);
+ writew(-RX_BUFF_SZ, &lp.rx_ring[i].buf_length);
+ lp.rx_memcpy[i] = (char *) (p + lp.bus_offset);
+ }
+ for (i = 0; i <= lp.txRingMask; i++) {
+ writel((p = lp.dma_buffs + (i + lp.txRingMask + 1) * TX_BUFF_SZ) & 0x00ffffff, &lp.tx_ring[i].base);
+ lp.tx_memcpy[i] = (char *) (p + lp.bus_offset);
+ }
+
+ /* Set up the initialization block */
+ lp.init_block.rx_ring = ((u32) ((u32) lp.rx_ring) & LA_MASK) | lp.rx_rlen;
+ lp.init_block.tx_ring = ((u32) ((u32) lp.tx_ring) & LA_MASK) | lp.tx_rlen;
+ for (i = 0; i < ETH_ALEN; i++)
+ lp.init_block.phys_addr[i] = nic->node_addr[i];
+ lp.init_block.mode = 0x0000; /* Enable the Tx and Rx */
+ memset(lp.init_block.mcast_table, 0, sizeof(lp.init_block.mcast_table));
+}
+
+static inline void LoadCSRs(struct nic *nic)
+{
+ outw(CSR1, nic->ioaddr + DEPCA_ADDR); /* initialisation block address LSW */
+ outw((u16) (lp.sh_mem & LA_MASK), nic->ioaddr + DEPCA_DATA);
+ outw(CSR2, nic->ioaddr + DEPCA_ADDR); /* initialisation block address MSW */
+ outw((u16) ((lp.sh_mem & LA_MASK) >> 16), nic->ioaddr + DEPCA_DATA);
+ outw(CSR3, nic->ioaddr + DEPCA_ADDR); /* ALE control */
+ outw(ACON, nic->ioaddr + DEPCA_DATA);
+ outw(CSR0, nic->ioaddr + DEPCA_ADDR); /* Point back to CSR0 */
+}
+
+static inline int InitRestartDepca(struct nic *nic)
+{
+ int i;
+
+ /* Copy the shadow init_block to shared memory */
+ memcpy_toio((char *)lp.sh_mem, &lp.init_block, sizeof(struct depca_init));
+ outw(CSR0, nic->ioaddr + DEPCA_ADDR); /* point back to CSR0 */
+ outw(INIT, nic->ioaddr + DEPCA_DATA); /* initialise DEPCA */
+
+ for (i = 0; i < 100 && !(inw(nic->ioaddr + DEPCA_DATA) & IDON); i++)
+ ;
+ if (i < 100) {
+ /* clear IDON by writing a 1, and start LANCE */
+ outw(IDON | STRT, nic->ioaddr + DEPCA_DATA);
+ } else {
+ printf("DEPCA not initialised\n");
+ return (1);
+ }
+ return (0);
+}
+
+/**************************************************************************
+RESET - Reset adapter
+***************************************************************************/
+static void depca_reset(struct nic *nic)
+{
+ s16 nicsr;
+ int i, j;
+
+ STOP_DEPCA(nic->ioaddr);
+ nicsr = inb(nic->ioaddr + DEPCA_NICSR);
+ nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM);
+ outb(nicsr, nic->ioaddr + DEPCA_NICSR);
+ if (inw(nic->ioaddr + DEPCA_DATA) != STOP)
+ {
+ printf("depca: Cannot stop NIC\n");
+ return;
+ }
+
+ /* Initialisation block */
+ lp.sh_mem = mem_start;
+ mem_start += sizeof(struct depca_init);
+ /* Tx & Rx descriptors (aligned to a quadword boundary) */
+ mem_start = (mem_start + ALIGN) & ~ALIGN;
+ lp.rx_ring = (struct depca_rx_desc *) mem_start;
+ mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC);
+ lp.tx_ring = (struct depca_tx_desc *) mem_start;
+ mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC);
+
+ lp.bus_offset = mem_start & 0x00ff0000;
+ /* LANCE re-mapped start address */
+ lp.dma_buffs = mem_start & LA_MASK;
+
+ /* Finish initialising the ring information. */
+ lp.rxRingMask = NUM_RX_DESC - 1;
+ lp.txRingMask = NUM_TX_DESC - 1;
+
+ /* Calculate Tx/Rx RLEN size for the descriptors. */
+ for (i = 0, j = lp.rxRingMask; j > 0; i++) {
+ j >>= 1;
+ }
+ lp.rx_rlen = (s32) (i << 29);
+ for (i = 0, j = lp.txRingMask; j > 0; i++) {
+ j >>= 1;
+ }
+ lp.tx_rlen = (s32) (i << 29);
+
+ /* Load the initialisation block */
+ depca_init_ring(nic);
+ LoadCSRs(nic);
+ InitRestartDepca(nic);
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int depca_poll(struct nic *nic, int retrieve)
+{
+ int entry;
+ u32 status;
+
+ entry = lp.rx_cur;
+ if ((status = readl(&lp.rx_ring[entry].base) & R_OWN))
+ return (0);
+
+ if ( ! retrieve ) return 1;
+
+ memcpy(nic->packet, lp.rx_memcpy[entry], nic->packetlen = lp.rx_ring[entry].msg_length);
+ lp.rx_ring[entry].base |= R_OWN;
+ lp.rx_cur = (++lp.rx_cur) & lp.rxRingMask;
+ return (1);
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void depca_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ int entry, len;
+ char *mem;
+
+ /* send the packet to destination */
+ /*
+ ** Caution: the right order is important here... dont
+ ** setup the ownership rights until all the other
+ ** information is in place
+ */
+ mem = lp.tx_memcpy[entry = lp.tx_cur];
+ memcpy_toio(mem, d, ETH_ALEN);
+ memcpy_toio(mem + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ mem[ETH_ALEN * 2] = t >> 8;
+ mem[ETH_ALEN * 2 + 1] = t;
+ memcpy_toio(mem + ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ len = (s < ETH_ZLEN ? ETH_ZLEN : s);
+ /* clean out flags */
+ writel(readl(&lp.tx_ring[entry].base) & ~T_FLAGS, &lp.tx_ring[entry].base);
+ /* clears other error flags */
+ writew(0x0000, &lp.tx_ring[entry].misc);
+ /* packet length in buffer */
+ writew(-len, &lp.tx_ring[entry].length);
+ /* start and end of packet, ownership */
+ writel(readl(&lp.tx_ring[entry].base) | (T_STP|T_ENP|T_OWN), &lp.tx_ring[entry].base);
+ /* update current pointers */
+ lp.tx_cur = (++lp.tx_cur) & lp.txRingMask;
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void depca_disable ( struct nic *nic ) {
+ depca_reset(nic);
+
+ STOP_DEPCA(nic->ioaddr);
+}
+
+/**************************************************************************
+IRQ - Interrupt Control
+***************************************************************************/
+static void depca_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/*
+** Look for a special sequence in the Ethernet station address PROM that
+** is common across all DEPCA products. Note that the original DEPCA needs
+** its ROM address counter to be initialized and enabled. Only enable
+** if the first address octet is a 0x08 - this minimises the chances of
+** messing around with some other hardware, but it assumes that this DEPCA
+** card initialized itself correctly.
+**
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
+*/
+
+
+/*
+ * Ugly, ugly, ugly. I can't quite make out where the split should be
+ * between probe1 and probe()...
+ *
+ */
+static u8 nicsr;
+
+
+static int depca_probe1 ( isa_probe_addr_t ioaddr ) {
+ u8 data;
+ /* This is only correct for little endian machines, but then
+ Etherboot doesn't work on anything but a PC */
+ u8 sig[] = { 0xFF, 0x00, 0x55, 0xAA, 0xFF, 0x00, 0x55, 0xAA };
+ int i, j;
+
+ data = inb(ioaddr + DEPCA_PROM); /* clear counter on DEPCA */
+ data = inb(ioaddr + DEPCA_PROM); /* read data */
+ if (data == 0x8) {
+ nicsr = inb(ioaddr + DEPCA_NICSR);
+ nicsr |= AAC;
+ outb(nicsr, ioaddr + DEPCA_NICSR);
+ }
+ for (i = 0, j = 0; j < (int)sizeof(sig) && i < PROBE_LENGTH+((int)sizeof(sig))-1; ++i) {
+ data = inb(ioaddr + DEPCA_PROM);
+ if (data == sig[j]) /* track signature */
+ ++j;
+ else
+ j = (data == sig[0]) ? 1 : 0;
+ }
+ if (j != sizeof(sig))
+ return (0);
+ /* put the card in its initial state */
+ STOP_DEPCA(ioaddr);
+ nicsr = ((inb(ioaddr + DEPCA_NICSR) & ~SHE & ~RBE & ~IEN) | IM);
+ outb(nicsr, ioaddr + DEPCA_NICSR);
+ if (inw(ioaddr + DEPCA_DATA) != STOP)
+ return (0);
+ memcpy((char *)mem_start, sig, sizeof(sig));
+ if (memcmp((char *)mem_start, sig, sizeof(sig)) != 0)
+ return (0);
+
+ return 1;
+}
+
+static struct nic_operations depca_operations = {
+ .connect = dummy_connect,
+ .poll = depca_poll,
+ .transmit = depca_transmit,
+ .irq = depca_irq,
+
+};
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int depca_probe ( struct nic *nic, struct isa_device *isa ) {
+
+ int i, j;
+ long sum, chksum;
+
+ nic->irqno = 0;
+ nic->ioaddr = isa->ioaddr;
+
+ for (i = 0, j = 0, sum = 0; j < 3; j++) {
+ sum <<= 1;
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ sum += (u8)(nic->node_addr[i++] = inb(nic->ioaddr + DEPCA_PROM));
+ sum += (u16)((nic->node_addr[i++] = inb(nic->ioaddr + DEPCA_PROM)) << 8);
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+ if (sum == 0xFFFF)
+ sum = 0;
+ chksum = (u8)inb(nic->ioaddr + DEPCA_PROM);
+ chksum |= (u16)(inb(nic->ioaddr + DEPCA_PROM) << 8);
+ mem_len = (adapter == DEPCA) ? (48 << 10) : (64 << 10);
+ offset = 0;
+ if (nicsr & BUF) {
+ offset = 0x8000;
+ nicsr &= ~BS;
+ mem_len -= (32 << 10);
+ }
+ if (adapter != DEPCA) /* enable shadow RAM */
+ outb(nicsr |= SHE, nic->ioaddr + DEPCA_NICSR);
+ DBG ( "%s base %4.4x, memory [%4.4lx-%4.4lx] addr %s",
+ adapter_name[adapter], nic->ioaddr, mem_start,
+ mem_start + mem_len, eth_ntoa ( nic->node_addr ) );
+ if (sum != chksum)
+ printf(" (bad checksum)");
+ putchar('\n');
+
+ depca_reset(nic);
+
+ /* point to NIC specific routines */
+ nic->nic_op = &depca_operations;
+ return 1;
+}
+
+static isa_probe_addr_t depca_probe_addrs[] = {
+ 0x300, 0x200,
+};
+
+ISA_DRIVER ( depca_driver, depca_probe_addrs, depca_probe1,
+ GENERIC_ISAPNP_VENDOR, 0x80f7 );
+
+DRIVER ( "depce", nic_driver, isa_driver, depca_driver,
+ depca_probe, depca_disable );
+
+ISA_ROM ( "depca", "Digital DE100 and DE200" );
+
+#endif
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/dmfe.c b/gpxe/src/drivers/net/dmfe.c
new file mode 100644
index 00000000..9cf50418
--- /dev/null
+++ b/gpxe/src/drivers/net/dmfe.c
@@ -0,0 +1,1224 @@
+/**************************************************************************
+*
+* dmfe.c -- Etherboot device driver for the Davicom
+* DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802 NIC fast ethernet card
+*
+* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+*
+* dmfe.c: A Davicom DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802
+* NIC fast ethernet driver for Linux.
+* Copyright (C) 1997 Sten Wang
+* (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
+*
+*
+* REVISION HISTORY:
+* ================
+* v1.0 10-02-2004 timlegge Boots ltsp needs cleanup
+*
+* Indent Options: indent -kr -i8
+*
+*
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+
+/* #define EDEBUG 1 */
+#ifdef EDEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+/* Board/System/Debug information/definition ---------------- */
+#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */
+#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */
+#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */
+#define PCI_DM9009_ID 0x90091282 /* Davicom DM9009 ID */
+
+#define DM9102_IO_SIZE 0x80
+#define DM9102A_IO_SIZE 0x100
+#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */
+#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */
+#define RX_DESC_CNT 0x20 /* Allocated Rx descriptors */
+#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */
+#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */
+#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT)
+#define TX_BUF_ALLOC 0x600
+#define RX_ALLOC_SIZE 0x620
+#define DM910X_RESET 1
+#define CR0_DEFAULT 0x00E00000 /* TX & RX burst mode */
+#define CR6_DEFAULT 0x00080000 /* HD */
+#define CR7_DEFAULT 0x180c1
+#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */
+#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */
+#define MAX_PACKET_SIZE 1514
+#define DMFE_MAX_MULTICAST 14
+#define RX_COPY_SIZE 100
+#define MAX_CHECK_PACKET 0x8000
+#define DM9801_NOISE_FLOOR 8
+#define DM9802_NOISE_FLOOR 5
+
+#define DMFE_10MHF 0
+#define DMFE_100MHF 1
+#define DMFE_10MFD 4
+#define DMFE_100MFD 5
+#define DMFE_AUTO 8
+#define DMFE_1M_HPNA 0x10
+
+#define DMFE_TXTH_72 0x400000 /* TX TH 72 byte */
+#define DMFE_TXTH_96 0x404000 /* TX TH 96 byte */
+#define DMFE_TXTH_128 0x0000 /* TX TH 128 byte */
+#define DMFE_TXTH_256 0x4000 /* TX TH 256 byte */
+#define DMFE_TXTH_512 0x8000 /* TX TH 512 byte */
+#define DMFE_TXTH_1K 0xC000 /* TX TH 1K byte */
+
+#define DMFE_TIMER_WUT (jiffies + HZ * 1) /* timer wakeup time : 1 second */
+#define DMFE_TX_TIMEOUT ((3*HZ)/2) /* tx packet time-out time 1.5 s" */
+#define DMFE_TX_KICK (HZ/2) /* tx packet Kick-out time 0.5 s" */
+
+#define DMFE_DBUG(dbug_now, msg, value) if (dmfe_debug || (dbug_now)) printk(KERN_ERR DRV_NAME ": %s %lx\n", (msg), (long) (value))
+
+#define SHOW_MEDIA_TYPE(mode) printk(KERN_ERR DRV_NAME ": Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
+
+
+/* CR9 definition: SROM/MII */
+#define CR9_SROM_READ 0x4800
+#define CR9_SRCS 0x1
+#define CR9_SRCLK 0x2
+#define CR9_CRDOUT 0x8
+#define SROM_DATA_0 0x0
+#define SROM_DATA_1 0x4
+#define PHY_DATA_1 0x20000
+#define PHY_DATA_0 0x00000
+#define MDCLKH 0x10000
+
+#define PHY_POWER_DOWN 0x800
+
+#define SROM_V41_CODE 0x14
+
+#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5);
+
+#define __CHK_IO_SIZE(pci_id, dev_rev) ( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE
+#define CHK_IO_SIZE(pci_dev, dev_rev) __CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, dev_rev)
+
+/* Sten Check */
+#define DEVICE net_device
+
+/* Structure/enum declaration ------------------------------- */
+struct tx_desc {
+ u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */
+ u32 tx_buf_ptr; /* Data for us */
+ u32 /* struct tx_desc * */ next_tx_desc;
+} __attribute__ ((aligned(32)));
+
+struct rx_desc {
+ u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */
+ u32 rx_skb_ptr; /* Data for us */
+ u32 /* struct rx_desc * */ next_rx_desc;
+} __attribute__ ((aligned(32)));
+
+static struct dmfe_private {
+ u32 chip_id; /* Chip vendor/Device ID */
+ u32 chip_revision; /* Chip revision */
+ u32 cr0_data;
+// u32 cr5_data;
+ u32 cr6_data;
+ u32 cr7_data;
+ u32 cr15_data;
+
+ u16 HPNA_command; /* For HPNA register 16 */
+ u16 HPNA_timer; /* For HPNA remote device check */
+ u16 NIC_capability; /* NIC media capability */
+ u16 PHY_reg4; /* Saved Phyxcer register 4 value */
+
+ u8 HPNA_present; /* 0:none, 1:DM9801, 2:DM9802 */
+ u8 chip_type; /* Keep DM9102A chip type */
+ u8 media_mode; /* user specify media mode */
+ u8 op_mode; /* real work media mode */
+ u8 phy_addr;
+ u8 dm910x_chk_mode; /* Operating mode check */
+
+ /* NIC SROM data */
+ unsigned char srom[128];
+ /* Etherboot Only */
+ u8 cur_tx;
+ u8 cur_rx;
+} dfx;
+
+static struct dmfe_private *db;
+
+enum dmfe_offsets {
+ DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20,
+ DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48,
+ DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 =
+ 0x70,
+ DCR15 = 0x78
+};
+
+enum dmfe_CR6_bits {
+ CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80,
+ CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000,
+ CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000
+};
+
+/* Global variable declaration ----------------------------- */
+static struct nic_operations dmfe_operations;
+
+static unsigned char dmfe_media_mode = DMFE_AUTO;
+static u32 dmfe_cr6_user_set;
+
+/* For module input parameter */
+static u8 chkmode = 1;
+static u8 HPNA_mode; /* Default: Low Power/High Speed */
+static u8 HPNA_rx_cmd; /* Default: Disable Rx remote command */
+static u8 HPNA_tx_cmd; /* Default: Don't issue remote command */
+static u8 HPNA_NoiseFloor; /* Default: HPNA NoiseFloor */
+static u8 SF_mode; /* Special Function: 1:VLAN, 2:RX Flow Control
+ 4: TX pause packet */
+
+
+/**********************************************
+* Descriptor Ring and Buffer defination
+***********************************************/
+struct {
+ struct tx_desc txd[TX_DESC_CNT] __attribute__ ((aligned(32)));
+ unsigned char txb[TX_BUF_ALLOC * TX_DESC_CNT]
+ __attribute__ ((aligned(32)));
+ struct rx_desc rxd[RX_DESC_CNT] __attribute__ ((aligned(32)));
+ unsigned char rxb[RX_ALLOC_SIZE * RX_DESC_CNT]
+ __attribute__ ((aligned(32)));
+} dmfe_bufs __shared;
+#define txd dmfe_bufs.txd
+#define txb dmfe_bufs.txb
+#define rxd dmfe_bufs.rxd
+#define rxb dmfe_bufs.rxb
+
+/* NIC specific static variables go here */
+static long int BASE;
+
+static u16 read_srom_word(long ioaddr, int offset);
+static void dmfe_init_dm910x(struct nic *nic);
+static void dmfe_descriptor_init(struct nic *, unsigned long ioaddr);
+static void update_cr6(u32, unsigned long);
+static void send_filter_frame(struct nic *nic);
+static void dm9132_id_table(struct nic *nic);
+
+static u16 phy_read(unsigned long, u8, u8, u32);
+static void phy_write(unsigned long, u8, u8, u16, u32);
+static void phy_write_1bit(unsigned long, u32);
+static u16 phy_read_1bit(unsigned long);
+static void dmfe_set_phyxcer(struct nic *nic);
+
+static void dmfe_parse_srom(struct nic *nic);
+static void dmfe_program_DM9801(struct nic *nic, int);
+static void dmfe_program_DM9802(struct nic *nic);
+
+static void dmfe_reset(struct nic *nic)
+{
+ /* system variable init */
+ db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
+
+ db->NIC_capability = 0xf; /* All capability */
+ db->PHY_reg4 = 0x1e0;
+
+ /* CR6 operation mode decision */
+ if (!chkmode || (db->chip_id == PCI_DM9132_ID) ||
+ (db->chip_revision >= 0x02000030)) {
+ db->cr6_data |= DMFE_TXTH_256;
+ db->cr0_data = CR0_DEFAULT;
+ db->dm910x_chk_mode = 4; /* Enter the normal mode */
+ } else {
+ db->cr6_data |= CR6_SFT; /* Store & Forward mode */
+ db->cr0_data = 0;
+ db->dm910x_chk_mode = 1; /* Enter the check mode */
+ }
+ /* Initilize DM910X board */
+ dmfe_init_dm910x(nic);
+
+ return;
+}
+
+/* Initilize DM910X board
+ * Reset DM910X board
+ * Initilize TX/Rx descriptor chain structure
+ * Send the set-up frame
+ * Enable Tx/Rx machine
+ */
+
+static void dmfe_init_dm910x(struct nic *nic)
+{
+ unsigned long ioaddr = BASE;
+
+ /* Reset DM910x MAC controller */
+ outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */
+ udelay(100);
+ outl(db->cr0_data, ioaddr + DCR0);
+ udelay(5);
+
+ /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */
+ db->phy_addr = 1;
+
+ /* Parser SROM and media mode */
+ dmfe_parse_srom(nic);
+ db->media_mode = dmfe_media_mode;
+
+ /* RESET Phyxcer Chip by GPR port bit 7 */
+ outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */
+ if (db->chip_id == PCI_DM9009_ID) {
+ outl(0x80, ioaddr + DCR12); /* Issue RESET signal */
+ mdelay(300); /* Delay 300 ms */
+ }
+ outl(0x0, ioaddr + DCR12); /* Clear RESET signal */
+
+ /* Process Phyxcer Media Mode */
+ if (!(db->media_mode & 0x10)) /* Force 1M mode */
+ dmfe_set_phyxcer(nic);
+
+ /* Media Mode Process */
+ if (!(db->media_mode & DMFE_AUTO))
+ db->op_mode = db->media_mode; /* Force Mode */
+
+ /* Initiliaze Transmit/Receive decriptor and CR3/4 */
+ dmfe_descriptor_init(nic, ioaddr);
+
+ /* tx descriptor start pointer */
+ outl(virt_to_le32desc(&txd[0]), ioaddr + DCR4); /* TX DESC address */
+
+ /* rx descriptor start pointer */
+ outl(virt_to_le32desc(&rxd[0]), ioaddr + DCR3); /* RX DESC address */
+
+ /* Init CR6 to program DM910x operation */
+ update_cr6(db->cr6_data, ioaddr);
+
+ /* Send setup frame */
+ if (db->chip_id == PCI_DM9132_ID) {
+ dm9132_id_table(nic); /* DM9132 */
+ } else {
+ send_filter_frame(nic); /* DM9102/DM9102A */
+ }
+
+ /* Init CR7, interrupt active bit */
+ db->cr7_data = CR7_DEFAULT;
+ outl(db->cr7_data, ioaddr + DCR7);
+ /* Init CR15, Tx jabber and Rx watchdog timer */
+ outl(db->cr15_data, ioaddr + DCR15);
+ /* Enable DM910X Tx/Rx function */
+ db->cr6_data |= CR6_RXSC | CR6_TXSC | 0x40000;
+ update_cr6(db->cr6_data, ioaddr);
+}
+#ifdef EDEBUG
+void hex_dump(const char *data, const unsigned int len);
+#endif
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int dmfe_poll(struct nic *nic, int retrieve)
+{
+ u32 rdes0;
+ int entry = db->cur_rx % RX_DESC_CNT;
+ int rxlen;
+ rdes0 = le32_to_cpu(rxd[entry].rdes0);
+ if (rdes0 & 0x80000000)
+ return 0;
+
+ if (!retrieve)
+ return 1;
+
+ if ((rdes0 & 0x300) != 0x300) {
+ /* A packet without First/Last flag */
+ printf("strange Packet\n");
+ rxd[entry].rdes0 = cpu_to_le32(0x80000000);
+ return 0;
+ } else {
+ /* A packet with First/Last flag */
+ rxlen = ((rdes0 >> 16) & 0x3fff) - 4;
+ /* error summary bit check */
+ if (rdes0 & 0x8000) {
+ printf("Error\n");
+ return 0;
+ }
+ if (!(rdes0 & 0x8000) ||
+ ((db->cr6_data & CR6_PM) && (rxlen > 6))) {
+ if (db->dm910x_chk_mode & 1)
+ printf("Silly check mode\n");
+
+ nic->packetlen = rxlen;
+ memcpy(nic->packet, rxb + (entry * RX_ALLOC_SIZE),
+ nic->packetlen);
+ }
+ }
+ rxd[entry].rdes0 = cpu_to_le32(0x80000000);
+ db->cur_rx++;
+ return 1;
+}
+
+static void dmfe_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void dmfe_transmit(struct nic *nic,
+ const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ unsigned int size, /* size */
+ const char *packet) /* Packet */
+{
+ u16 nstype;
+ u8 *ptxb;
+
+ ptxb = &txb[db->cur_tx];
+
+ /* Stop Tx */
+ outl(0, BASE + DCR7);
+ memcpy(ptxb, dest, ETH_ALEN);
+ memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) type);
+ memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2);
+ memcpy(ptxb + ETH_HLEN, packet, size);
+
+ size += ETH_HLEN;
+ while (size < ETH_ZLEN)
+ ptxb[size++] = '\0';
+
+ /* setup the transmit descriptor */
+ txd[db->cur_tx].tdes1 = cpu_to_le32(0xe1000000 | size);
+ txd[db->cur_tx].tdes0 = cpu_to_le32(0x80000000); /* give ownership to device */
+
+ /* immediate transmit demand */
+ outl(0x1, BASE + DCR1);
+ outl(db->cr7_data, BASE + DCR7);
+
+ /* Point to next TX descriptor */
+ db->cur_tx++;
+ db->cur_tx = db->cur_tx % TX_DESC_CNT;
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void dmfe_disable ( struct nic *nic __unused ) {
+ /* Reset & stop DM910X board */
+ outl(DM910X_RESET, BASE + DCR0);
+ udelay(5);
+ phy_write(BASE, db->phy_addr, 0, 0x8000, db->chip_id);
+
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+#define board_found 1
+#define valid_link 0
+static int dmfe_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ uint32_t dev_rev, pci_pmr;
+ int i;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ BASE = pci->ioaddr;
+ printf("dmfe.c: Found %s Vendor=0x%hX Device=0x%hX\n",
+ pci->driver_name, pci->vendor, pci->device);
+
+ /* Read Chip revision */
+ pci_read_config_dword(pci, PCI_REVISION_ID, &dev_rev);
+ dprintf(("Revision %lX\n", dev_rev));
+
+ /* point to private storage */
+ db = &dfx;
+
+ db->chip_id = ((u32) pci->device << 16) | pci->vendor;
+ BASE = pci_bar_start(pci, PCI_BASE_ADDRESS_0);
+ db->chip_revision = dev_rev;
+
+ pci_read_config_dword(pci, 0x50, &pci_pmr);
+ pci_pmr &= 0x70000;
+ if ((pci_pmr == 0x10000) && (dev_rev == 0x02000031))
+ db->chip_type = 1; /* DM9102A E3 */
+ else
+ db->chip_type = 0;
+
+ dprintf(("Chip type : %d\n", db->chip_type));
+
+ /* read 64 word srom data */
+ for (i = 0; i < 64; i++)
+ ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(BASE, i));
+
+ /* Set Node address */
+ for (i = 0; i < 6; i++)
+ nic->node_addr[i] = db->srom[20 + i];
+
+ /* Print out some hardware info */
+ DBG ( "%s: %s at ioaddr %4.4lx\n", pci->driver_name, eth_ntoa ( nic->node_addr ), BASE );
+
+ /* Set the card as PCI Bus Master */
+ adjust_pci_device(pci);
+
+ dmfe_reset(nic);
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr;
+
+ /* point to NIC specific routines */
+ nic->nic_op = &dmfe_operations;
+
+ return 1;
+}
+
+/*
+ * Initialize transmit/Receive descriptor
+ * Using Chain structure, and allocate Tx/Rx buffer
+ */
+
+static void dmfe_descriptor_init(struct nic *nic __unused, unsigned long ioaddr)
+{
+ int i;
+ db->cur_tx = 0;
+ db->cur_rx = 0;
+
+ /* tx descriptor start pointer */
+ outl(virt_to_le32desc(&txd[0]), ioaddr + DCR4); /* TX DESC address */
+
+ /* rx descriptor start pointer */
+ outl(virt_to_le32desc(&rxd[0]), ioaddr + DCR3); /* RX DESC address */
+
+ /* Init Transmit chain */
+ for (i = 0; i < TX_DESC_CNT; i++) {
+ txd[i].tx_buf_ptr = (u32) & txb[i];
+ txd[i].tdes0 = cpu_to_le32(0);
+ txd[i].tdes1 = cpu_to_le32(0x81000000); /* IC, chain */
+ txd[i].tdes2 = cpu_to_le32(virt_to_bus(&txb[i]));
+ txd[i].tdes3 = cpu_to_le32(virt_to_bus(&txd[i + 1]));
+ txd[i].next_tx_desc = virt_to_le32desc(&txd[i + 1]);
+ }
+ /* Mark the last entry as wrapping the ring */
+ txd[i - 1].tdes3 = virt_to_le32desc(&txd[0]);
+ txd[i - 1].next_tx_desc = (u32) & txd[0];
+
+ /* receive descriptor chain */
+ for (i = 0; i < RX_DESC_CNT; i++) {
+ rxd[i].rx_skb_ptr = (u32) & rxb[i * RX_ALLOC_SIZE];
+ rxd[i].rdes0 = cpu_to_le32(0x80000000);
+ rxd[i].rdes1 = cpu_to_le32(0x01000600);
+ rxd[i].rdes2 =
+ cpu_to_le32(virt_to_bus(&rxb[i * RX_ALLOC_SIZE]));
+ rxd[i].rdes3 = cpu_to_le32(virt_to_bus(&rxd[i + 1]));
+ rxd[i].next_rx_desc = virt_to_le32desc(&rxd[i + 1]);
+ }
+ /* Mark the last entry as wrapping the ring */
+ rxd[i - 1].rdes3 = cpu_to_le32(virt_to_bus(&rxd[0]));
+ rxd[i - 1].next_rx_desc = virt_to_le32desc(&rxd[0]);
+
+}
+
+/*
+ * Update CR6 value
+ * Firstly stop DM910X , then written value and start
+ */
+
+static void update_cr6(u32 cr6_data, unsigned long ioaddr)
+{
+ u32 cr6_tmp;
+
+ cr6_tmp = cr6_data & ~0x2002; /* stop Tx/Rx */
+ outl(cr6_tmp, ioaddr + DCR6);
+ udelay(5);
+ outl(cr6_data, ioaddr + DCR6);
+ udelay(5);
+}
+
+
+/*
+ * Send a setup frame for DM9132
+ * This setup frame initilize DM910X addres filter mode
+*/
+
+static void dm9132_id_table(struct nic *nic __unused)
+{
+#ifdef LINUX
+ u16 *addrptr;
+ u8 dmi_addr[8];
+ unsigned long ioaddr = BASE + 0xc0; /* ID Table */
+ u32 hash_val;
+ u16 i, hash_table[4];
+#endif
+ dprintf(("dm9132_id_table\n"));
+
+ printf("FIXME: This function is broken. If you have this card contact "
+ "Timothy Legge at the etherboot-user list\n");
+
+#ifdef LINUX
+ //DMFE_DBUG(0, "dm9132_id_table()", 0);
+
+ /* Node address */
+ addrptr = (u16 *) nic->node_addr;
+ outw(addrptr[0], ioaddr);
+ ioaddr += 4;
+ outw(addrptr[1], ioaddr);
+ ioaddr += 4;
+ outw(addrptr[2], ioaddr);
+ ioaddr += 4;
+
+ /* Clear Hash Table */
+ for (i = 0; i < 4; i++)
+ hash_table[i] = 0x0;
+
+ /* broadcast address */
+ hash_table[3] = 0x8000;
+
+ /* the multicast address in Hash Table : 64 bits */
+ for (mcptr = mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
+ hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f;
+ hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
+ }
+
+ /* Write the hash table to MAC MD table */
+ for (i = 0; i < 4; i++, ioaddr += 4)
+ outw(hash_table[i], ioaddr);
+#endif
+}
+
+
+/*
+ * Send a setup frame for DM9102/DM9102A
+ * This setup frame initilize DM910X addres filter mode
+ */
+
+static void send_filter_frame(struct nic *nic)
+{
+
+ u8 *ptxb;
+ int i;
+
+ dprintf(("send_filter_frame\n"));
+ /* point to the current txb incase multiple tx_rings are used */
+ ptxb = &txb[db->cur_tx];
+
+ /* construct perfect filter frame with mac address as first match
+ and broadcast address for all others */
+ for (i = 0; i < 192; i++)
+ ptxb[i] = 0xFF;
+ ptxb[0] = nic->node_addr[0];
+ ptxb[1] = nic->node_addr[1];
+ ptxb[4] = nic->node_addr[2];
+ ptxb[5] = nic->node_addr[3];
+ ptxb[8] = nic->node_addr[4];
+ ptxb[9] = nic->node_addr[5];
+
+ /* prepare the setup frame */
+ txd[db->cur_tx].tdes1 = cpu_to_le32(0x890000c0);
+ txd[db->cur_tx].tdes0 = cpu_to_le32(0x80000000);
+ update_cr6(db->cr6_data | 0x2000, BASE);
+ outl(0x1, BASE + DCR1); /* Issue Tx polling */
+ update_cr6(db->cr6_data, BASE);
+ db->cur_tx++;
+}
+
+/*
+ * Read one word data from the serial ROM
+ */
+
+static u16 read_srom_word(long ioaddr, int offset)
+{
+ int i;
+ u16 srom_data = 0;
+ long cr9_ioaddr = ioaddr + DCR9;
+
+ outl(CR9_SROM_READ, cr9_ioaddr);
+ outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+ /* Send the Read Command 110b */
+ SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+ SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+ SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr);
+
+ /* Send the offset */
+ for (i = 5; i >= 0; i--) {
+ srom_data =
+ (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0;
+ SROM_CLK_WRITE(srom_data, cr9_ioaddr);
+ }
+
+ outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+ for (i = 16; i > 0; i--) {
+ outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr);
+ udelay(5);
+ srom_data =
+ (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1
+ : 0);
+ outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+ udelay(5);
+ }
+
+ outl(CR9_SROM_READ, cr9_ioaddr);
+ return srom_data;
+}
+
+
+/*
+ * Auto sense the media mode
+ */
+
+#if 0 /* not used */
+static u8 dmfe_sense_speed(struct nic *nic __unused)
+{
+ u8 ErrFlag = 0;
+ u16 phy_mode;
+
+ /* CR6 bit18=0, select 10/100M */
+ update_cr6((db->cr6_data & ~0x40000), BASE);
+
+ phy_mode = phy_read(BASE, db->phy_addr, 1, db->chip_id);
+ phy_mode = phy_read(BASE, db->phy_addr, 1, db->chip_id);
+
+ if ((phy_mode & 0x24) == 0x24) {
+ if (db->chip_id == PCI_DM9132_ID) /* DM9132 */
+ phy_mode =
+ phy_read(BASE, db->phy_addr, 7,
+ db->chip_id) & 0xf000;
+ else /* DM9102/DM9102A */
+ phy_mode =
+ phy_read(BASE, db->phy_addr, 17,
+ db->chip_id) & 0xf000;
+ /* printk(DRV_NAME ": Phy_mode %x ",phy_mode); */
+ switch (phy_mode) {
+ case 0x1000:
+ db->op_mode = DMFE_10MHF;
+ break;
+ case 0x2000:
+ db->op_mode = DMFE_10MFD;
+ break;
+ case 0x4000:
+ db->op_mode = DMFE_100MHF;
+ break;
+ case 0x8000:
+ db->op_mode = DMFE_100MFD;
+ break;
+ default:
+ db->op_mode = DMFE_10MHF;
+ ErrFlag = 1;
+ break;
+ }
+ } else {
+ db->op_mode = DMFE_10MHF;
+ //DMFE_DBUG(0, "Link Failed :", phy_mode);
+ ErrFlag = 1;
+ }
+
+ return ErrFlag;
+}
+#endif
+
+/*
+ * Set 10/100 phyxcer capability
+ * AUTO mode : phyxcer register4 is NIC capability
+ * Force mode: phyxcer register4 is the force media
+ */
+
+static void dmfe_set_phyxcer(struct nic *nic __unused)
+{
+ u16 phy_reg;
+
+ /* Select 10/100M phyxcer */
+ db->cr6_data &= ~0x40000;
+ update_cr6(db->cr6_data, BASE);
+
+ /* DM9009 Chip: Phyxcer reg18 bit12=0 */
+ if (db->chip_id == PCI_DM9009_ID) {
+ phy_reg =
+ phy_read(BASE, db->phy_addr, 18,
+ db->chip_id) & ~0x1000;
+ phy_write(BASE, db->phy_addr, 18, phy_reg, db->chip_id);
+ }
+
+ /* Phyxcer capability setting */
+ phy_reg = phy_read(BASE, db->phy_addr, 4, db->chip_id) & ~0x01e0;
+
+ if (db->media_mode & DMFE_AUTO) {
+ /* AUTO Mode */
+ phy_reg |= db->PHY_reg4;
+ } else {
+ /* Force Mode */
+ switch (db->media_mode) {
+ case DMFE_10MHF:
+ phy_reg |= 0x20;
+ break;
+ case DMFE_10MFD:
+ phy_reg |= 0x40;
+ break;
+ case DMFE_100MHF:
+ phy_reg |= 0x80;
+ break;
+ case DMFE_100MFD:
+ phy_reg |= 0x100;
+ break;
+ }
+ if (db->chip_id == PCI_DM9009_ID)
+ phy_reg &= 0x61;
+ }
+
+ /* Write new capability to Phyxcer Reg4 */
+ if (!(phy_reg & 0x01e0)) {
+ phy_reg |= db->PHY_reg4;
+ db->media_mode |= DMFE_AUTO;
+ }
+ phy_write(BASE, db->phy_addr, 4, phy_reg, db->chip_id);
+
+ /* Restart Auto-Negotiation */
+ if (db->chip_type && (db->chip_id == PCI_DM9102_ID))
+ phy_write(BASE, db->phy_addr, 0, 0x1800, db->chip_id);
+ if (!db->chip_type)
+ phy_write(BASE, db->phy_addr, 0, 0x1200, db->chip_id);
+}
+
+
+/*
+ * Process op-mode
+ * AUTO mode : PHY controller in Auto-negotiation Mode
+ * Force mode: PHY controller in force mode with HUB
+ * N-way force capability with SWITCH
+ */
+
+#if 0 /* not used */
+static void dmfe_process_mode(struct nic *nic __unused)
+{
+ u16 phy_reg;
+
+ /* Full Duplex Mode Check */
+ if (db->op_mode & 0x4)
+ db->cr6_data |= CR6_FDM; /* Set Full Duplex Bit */
+ else
+ db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */
+
+ /* Transciver Selection */
+ if (db->op_mode & 0x10) /* 1M HomePNA */
+ db->cr6_data |= 0x40000; /* External MII select */
+ else
+ db->cr6_data &= ~0x40000; /* Internal 10/100 transciver */
+
+ update_cr6(db->cr6_data, BASE);
+
+ /* 10/100M phyxcer force mode need */
+ if (!(db->media_mode & 0x18)) {
+ /* Forece Mode */
+ phy_reg = phy_read(BASE, db->phy_addr, 6, db->chip_id);
+ if (!(phy_reg & 0x1)) {
+ /* parter without N-Way capability */
+ phy_reg = 0x0;
+ switch (db->op_mode) {
+ case DMFE_10MHF:
+ phy_reg = 0x0;
+ break;
+ case DMFE_10MFD:
+ phy_reg = 0x100;
+ break;
+ case DMFE_100MHF:
+ phy_reg = 0x2000;
+ break;
+ case DMFE_100MFD:
+ phy_reg = 0x2100;
+ break;
+ }
+ phy_write(BASE, db->phy_addr, 0, phy_reg,
+ db->chip_id);
+ if (db->chip_type
+ && (db->chip_id == PCI_DM9102_ID))
+ mdelay(20);
+ phy_write(BASE, db->phy_addr, 0, phy_reg,
+ db->chip_id);
+ }
+ }
+}
+#endif
+
+/*
+ * Write a word to Phy register
+ */
+
+static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset,
+ u16 phy_data, u32 chip_id)
+{
+ u16 i;
+ unsigned long ioaddr;
+
+ if (chip_id == PCI_DM9132_ID) {
+ ioaddr = iobase + 0x80 + offset * 4;
+ outw(phy_data, ioaddr);
+ } else {
+ /* DM9102/DM9102A Chip */
+ ioaddr = iobase + DCR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+
+ /* Send write command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+
+ /* Send Phy addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr,
+ phy_addr & i ? PHY_DATA_1 :
+ PHY_DATA_0);
+
+ /* Send register addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr,
+ offset & i ? PHY_DATA_1 :
+ PHY_DATA_0);
+
+ /* written trasnition */
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+
+ /* Write a word data to PHY controller */
+ for (i = 0x8000; i > 0; i >>= 1)
+ phy_write_1bit(ioaddr,
+ phy_data & i ? PHY_DATA_1 :
+ PHY_DATA_0);
+ }
+}
+
+
+/*
+ * Read a word data from phy register
+ */
+
+static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset,
+ u32 chip_id)
+{
+ int i;
+ u16 phy_data;
+ unsigned long ioaddr;
+
+ if (chip_id == PCI_DM9132_ID) {
+ /* DM9132 Chip */
+ ioaddr = iobase + 0x80 + offset * 4;
+ phy_data = inw(ioaddr);
+ } else {
+ /* DM9102/DM9102A Chip */
+ ioaddr = iobase + DCR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+
+ /* Send read command(10) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+
+ /* Send Phy addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr,
+ phy_addr & i ? PHY_DATA_1 :
+ PHY_DATA_0);
+
+ /* Send register addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr,
+ offset & i ? PHY_DATA_1 :
+ PHY_DATA_0);
+
+ /* Skip transition state */
+ phy_read_1bit(ioaddr);
+
+ /* read 16bit data */
+ for (phy_data = 0, i = 0; i < 16; i++) {
+ phy_data <<= 1;
+ phy_data |= phy_read_1bit(ioaddr);
+ }
+ }
+
+ return phy_data;
+}
+
+
+/*
+ * Write one bit data to Phy Controller
+ */
+
+static void phy_write_1bit(unsigned long ioaddr, u32 phy_data)
+{
+ outl(phy_data, ioaddr); /* MII Clock Low */
+ udelay(1);
+ outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */
+ udelay(1);
+ outl(phy_data, ioaddr); /* MII Clock Low */
+ udelay(1);
+}
+
+
+/*
+ * Read one bit phy data from PHY controller
+ */
+
+static u16 phy_read_1bit(unsigned long ioaddr)
+{
+ u16 phy_data;
+
+ outl(0x50000, ioaddr);
+ udelay(1);
+ phy_data = (inl(ioaddr) >> 19) & 0x1;
+ outl(0x40000, ioaddr);
+ udelay(1);
+
+ return phy_data;
+}
+
+
+/*
+ * Parser SROM and media mode
+ */
+
+static void dmfe_parse_srom(struct nic *nic)
+{
+ unsigned char *srom = db->srom;
+ int dmfe_mode, tmp_reg;
+
+ /* Init CR15 */
+ db->cr15_data = CR15_DEFAULT;
+
+ /* Check SROM Version */
+ if (((int) srom[18] & 0xff) == SROM_V41_CODE) {
+ /* SROM V4.01 */
+ /* Get NIC support media mode */
+ db->NIC_capability = *(u16 *) (srom + 34);
+ db->PHY_reg4 = 0;
+ for (tmp_reg = 1; tmp_reg < 0x10; tmp_reg <<= 1) {
+ switch (db->NIC_capability & tmp_reg) {
+ case 0x1:
+ db->PHY_reg4 |= 0x0020;
+ break;
+ case 0x2:
+ db->PHY_reg4 |= 0x0040;
+ break;
+ case 0x4:
+ db->PHY_reg4 |= 0x0080;
+ break;
+ case 0x8:
+ db->PHY_reg4 |= 0x0100;
+ break;
+ }
+ }
+
+ /* Media Mode Force or not check */
+ dmfe_mode = *((int *) srom + 34) & *((int *) srom + 36);
+ switch (dmfe_mode) {
+ case 0x4:
+ dmfe_media_mode = DMFE_100MHF;
+ break; /* 100MHF */
+ case 0x2:
+ dmfe_media_mode = DMFE_10MFD;
+ break; /* 10MFD */
+ case 0x8:
+ dmfe_media_mode = DMFE_100MFD;
+ break; /* 100MFD */
+ case 0x100:
+ case 0x200:
+ dmfe_media_mode = DMFE_1M_HPNA;
+ break; /* HomePNA */
+ }
+
+ /* Special Function setting */
+ /* VLAN function */
+ if ((SF_mode & 0x1) || (srom[43] & 0x80))
+ db->cr15_data |= 0x40;
+
+ /* Flow Control */
+ if ((SF_mode & 0x2) || (srom[40] & 0x1))
+ db->cr15_data |= 0x400;
+
+ /* TX pause packet */
+ if ((SF_mode & 0x4) || (srom[40] & 0xe))
+ db->cr15_data |= 0x9800;
+ }
+
+ /* Parse HPNA parameter */
+ db->HPNA_command = 1;
+
+ /* Accept remote command or not */
+ if (HPNA_rx_cmd == 0)
+ db->HPNA_command |= 0x8000;
+
+ /* Issue remote command & operation mode */
+ if (HPNA_tx_cmd == 1)
+ switch (HPNA_mode) { /* Issue Remote Command */
+ case 0:
+ db->HPNA_command |= 0x0904;
+ break;
+ case 1:
+ db->HPNA_command |= 0x0a00;
+ break;
+ case 2:
+ db->HPNA_command |= 0x0506;
+ break;
+ case 3:
+ db->HPNA_command |= 0x0602;
+ break;
+ } else
+ switch (HPNA_mode) { /* Don't Issue */
+ case 0:
+ db->HPNA_command |= 0x0004;
+ break;
+ case 1:
+ db->HPNA_command |= 0x0000;
+ break;
+ case 2:
+ db->HPNA_command |= 0x0006;
+ break;
+ case 3:
+ db->HPNA_command |= 0x0002;
+ break;
+ }
+
+ /* Check DM9801 or DM9802 present or not */
+ db->HPNA_present = 0;
+ update_cr6(db->cr6_data | 0x40000, BASE);
+ tmp_reg = phy_read(BASE, db->phy_addr, 3, db->chip_id);
+ if ((tmp_reg & 0xfff0) == 0xb900) {
+ /* DM9801 or DM9802 present */
+ db->HPNA_timer = 8;
+ if (phy_read(BASE, db->phy_addr, 31, db->chip_id) ==
+ 0x4404) {
+ /* DM9801 HomeRun */
+ db->HPNA_present = 1;
+ dmfe_program_DM9801(nic, tmp_reg);
+ } else {
+ /* DM9802 LongRun */
+ db->HPNA_present = 2;
+ dmfe_program_DM9802(nic);
+ }
+ }
+
+}
+
+/*
+ * Init HomeRun DM9801
+ */
+
+static void dmfe_program_DM9801(struct nic *nic __unused, int HPNA_rev)
+{
+ u32 reg17, reg25;
+
+ if (!HPNA_NoiseFloor)
+ HPNA_NoiseFloor = DM9801_NOISE_FLOOR;
+ switch (HPNA_rev) {
+ case 0xb900: /* DM9801 E3 */
+ db->HPNA_command |= 0x1000;
+ reg25 = phy_read(BASE, db->phy_addr, 24, db->chip_id);
+ reg25 = ((reg25 + HPNA_NoiseFloor) & 0xff) | 0xf000;
+ reg17 = phy_read(BASE, db->phy_addr, 17, db->chip_id);
+ break;
+ case 0xb901: /* DM9801 E4 */
+ reg25 = phy_read(BASE, db->phy_addr, 25, db->chip_id);
+ reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor;
+ reg17 = phy_read(BASE, db->phy_addr, 17, db->chip_id);
+ reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor + 3;
+ break;
+ case 0xb902: /* DM9801 E5 */
+ case 0xb903: /* DM9801 E6 */
+ default:
+ db->HPNA_command |= 0x1000;
+ reg25 = phy_read(BASE, db->phy_addr, 25, db->chip_id);
+ reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor - 5;
+ reg17 = phy_read(BASE, db->phy_addr, 17, db->chip_id);
+ reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor;
+ break;
+ }
+ phy_write(BASE, db->phy_addr, 16, db->HPNA_command, db->chip_id);
+ phy_write(BASE, db->phy_addr, 17, reg17, db->chip_id);
+ phy_write(BASE, db->phy_addr, 25, reg25, db->chip_id);
+}
+
+
+/*
+ * Init HomeRun DM9802
+ */
+
+static void dmfe_program_DM9802(struct nic *nic __unused)
+{
+ u32 phy_reg;
+
+ if (!HPNA_NoiseFloor)
+ HPNA_NoiseFloor = DM9802_NOISE_FLOOR;
+ phy_write(BASE, db->phy_addr, 16, db->HPNA_command, db->chip_id);
+ phy_reg = phy_read(BASE, db->phy_addr, 25, db->chip_id);
+ phy_reg = (phy_reg & 0xff00) + HPNA_NoiseFloor;
+ phy_write(BASE, db->phy_addr, 25, phy_reg, db->chip_id);
+}
+
+static struct nic_operations dmfe_operations = {
+ .connect = dummy_connect,
+ .poll = dmfe_poll,
+ .transmit = dmfe_transmit,
+ .irq = dmfe_irq,
+
+};
+
+static struct pci_device_id dmfe_nics[] = {
+ PCI_ROM(0x1282, 0x9100, "dmfe9100", "Davicom 9100"),
+ PCI_ROM(0x1282, 0x9102, "dmfe9102", "Davicom 9102"),
+ PCI_ROM(0x1282, 0x9009, "dmfe9009", "Davicom 9009"),
+ PCI_ROM(0x1282, 0x9132, "dmfe9132", "Davicom 9132"), /* Needs probably some fixing */
+};
+
+PCI_DRIVER ( dmfe_driver, dmfe_nics, PCI_NO_CLASS );
+
+DRIVER ( "DMFE/PCI", nic_driver, pci_driver, dmfe_driver,
+ dmfe_probe, dmfe_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/e1000/e1000.c b/gpxe/src/drivers/net/e1000/e1000.c
new file mode 100644
index 00000000..739217cf
--- /dev/null
+++ b/gpxe/src/drivers/net/e1000/e1000.c
@@ -0,0 +1,1116 @@
+/*
+ * gPXE driver for Intel eepro1000 ethernet cards
+ *
+ * Written by Marty Connor
+ *
+ * Copyright Entity Cyber, Inc. 2007
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License (GPL), incorporated herein by
+ * reference. Drivers based on or derived from this code fall under
+ * the GPL and must retain the authorship, copyright and license
+ * notice.
+ *
+ */
+
+/*******************************************************************************
+
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "e1000.h"
+
+/**
+ * e1000_get_hw_control - get control of the h/w from f/w
+ *
+ * @v adapter e1000 private structure
+ *
+ * e1000_get_hw_control sets {CTRL_EXT|FWSM}:DRV_LOAD bit.
+ * For ASF and Pass Through versions of f/w this means that
+ * the driver is loaded. For AMT version (only with 82573)
+ * of the f/w this means that the network i/f is open.
+ *
+ **/
+static void
+e1000_get_hw_control ( struct e1000_adapter *adapter )
+{
+ uint32_t ctrl_ext;
+ uint32_t swsm;
+
+ DBG ( "e1000_get_hw_control\n" );
+
+ /* Let firmware know the driver has taken over */
+ switch (adapter->hw.mac_type) {
+ case e1000_82573:
+ swsm = E1000_READ_REG(&adapter->hw, SWSM);
+ E1000_WRITE_REG(&adapter->hw, SWSM,
+ swsm | E1000_SWSM_DRV_LOAD);
+ break;
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_80003es2lan:
+ case e1000_ich8lan:
+ ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT);
+ E1000_WRITE_REG(&adapter->hw, CTRL_EXT,
+ ctrl_ext | E1000_CTRL_EXT_DRV_LOAD);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * e1000_irq_enable - Enable default interrupt generation settings
+ *
+ * @v adapter e1000 private structure
+ **/
+static void
+e1000_irq_enable ( struct e1000_adapter *adapter )
+{
+ E1000_WRITE_REG ( &adapter->hw, IMS, E1000_IMS_RXDMT0 |
+ E1000_IMS_RXSEQ );
+ E1000_WRITE_FLUSH ( &adapter->hw );
+}
+
+/**
+ * e1000_irq_disable - Mask off interrupt generation on the NIC
+ *
+ * @v adapter e1000 private structure
+ **/
+static void
+e1000_irq_disable ( struct e1000_adapter *adapter )
+{
+ E1000_WRITE_REG ( &adapter->hw, IMC, ~0 );
+ E1000_WRITE_FLUSH ( &adapter->hw );
+}
+
+/**
+ * e1000_irq_force - trigger interrupt
+ *
+ * @v adapter e1000 private structure
+ **/
+static void
+e1000_irq_force ( struct e1000_adapter *adapter )
+{
+ E1000_WRITE_REG ( &adapter->hw, ICS, E1000_ICS_RXDMT0 );
+ E1000_WRITE_FLUSH ( &adapter->hw );
+}
+
+/**
+ * e1000_sw_init - Initialize general software structures (struct e1000_adapter)
+ *
+ * @v adapter e1000 private structure
+ *
+ * e1000_sw_init initializes the Adapter private data structure.
+ * Fields are initialized based on PCI device information and
+ * OS network device settings (MTU size).
+ **/
+static int
+e1000_sw_init ( struct e1000_adapter *adapter )
+{
+ struct e1000_hw *hw = &adapter->hw;
+ struct pci_device *pdev = adapter->pdev;
+
+ /* PCI config space info */
+
+ hw->vendor_id = pdev->vendor;
+ hw->device_id = pdev->device;
+
+ pci_read_config_word ( pdev, PCI_COMMAND, &hw->pci_cmd_word );
+
+ /* Disable Flow Control */
+ hw->fc = E1000_FC_NONE;
+
+ adapter->eeprom_wol = 0;
+ adapter->wol = adapter->eeprom_wol;
+ adapter->en_mng_pt = 0;
+ adapter->rx_int_delay = 0;
+ adapter->rx_abs_int_delay = 0;
+
+ adapter->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE;
+ adapter->rx_ps_bsize0 = E1000_RXBUFFER_128;
+ hw->max_frame_size = MAXIMUM_ETHERNET_VLAN_SIZE +
+ ENET_HEADER_SIZE + ETHERNET_FCS_SIZE;
+ hw->min_frame_size = MINIMUM_ETHERNET_FRAME_SIZE;
+
+ /* identify the MAC */
+
+ if ( e1000_set_mac_type ( hw ) ) {
+ DBG ( "Unknown MAC Type\n" );
+ return -EIO;
+ }
+
+ switch ( hw->mac_type ) {
+ default:
+ break;
+ case e1000_82541:
+ case e1000_82547:
+ case e1000_82541_rev_2:
+ case e1000_82547_rev_2:
+ hw->phy_init_script = 1;
+ break;
+ }
+
+ e1000_set_media_type ( hw );
+
+ hw->autoneg = TRUE;
+ hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+ hw->wait_autoneg_complete = TRUE;
+
+ hw->tbi_compatibility_en = TRUE;
+ hw->adaptive_ifs = TRUE;
+
+ /* Copper options */
+
+ if ( hw->media_type == e1000_media_type_copper ) {
+ hw->mdix = AUTO_ALL_MODES;
+ hw->disable_polarity_correction = FALSE;
+ hw->master_slave = E1000_MASTER_SLAVE;
+ }
+
+ e1000_irq_disable ( adapter );
+
+ return 0;
+}
+
+/**
+ * e1000_setup_tx_resources - allocate Tx resources (Descriptors)
+ *
+ * @v adapter e1000 private structure
+ *
+ * @ret rc Returns 0 on success, negative on failure
+ **/
+static int
+e1000_setup_tx_resources ( struct e1000_adapter *adapter )
+{
+ DBG ( "e1000_setup_tx_resources\n" );
+
+ /* Allocate transmit descriptor ring memory.
+ It must not cross a 64K boundary because of hardware errata #23
+ so we use malloc_dma() requesting a 128 byte block that is
+ 128 byte aligned. This should guarantee that the memory
+ allocated will not cross a 64K boundary, because 128 is an
+ even multiple of 65536 ( 65536 / 128 == 512 ), so all possible
+ allocations of 128 bytes on a 128 byte boundary will not
+ cross 64K bytes.
+ */
+
+ adapter->tx_base =
+ malloc_dma ( adapter->tx_ring_size, adapter->tx_ring_size );
+
+ if ( ! adapter->tx_base ) {
+ return -ENOMEM;
+ }
+
+ memset ( adapter->tx_base, 0, adapter->tx_ring_size );
+
+ DBG ( "adapter->tx_base = %#08lx\n", virt_to_bus ( adapter->tx_base ) );
+
+ return 0;
+}
+
+static void
+e1000_free_tx_resources ( struct e1000_adapter *adapter )
+{
+ DBG ( "e1000_free_tx_resources\n" );
+
+ free_dma ( adapter->tx_base, adapter->tx_ring_size );
+}
+
+/**
+ * e1000_configure_tx - Configure 8254x Transmit Unit after Reset
+ * @adapter: board private structure
+ *
+ * Configure the Tx unit of the MAC after a reset.
+ **/
+static void
+e1000_configure_tx ( struct e1000_adapter *adapter )
+{
+ struct e1000_hw *hw = &adapter->hw;
+ uint32_t tctl;
+
+ DBG ( "e1000_configure_tx\n" );
+
+ E1000_WRITE_REG ( hw, TDBAH, 0 );
+ E1000_WRITE_REG ( hw, TDBAL, virt_to_bus ( adapter->tx_base ) );
+ E1000_WRITE_REG ( hw, TDLEN, adapter->tx_ring_size );
+
+ DBG ( "TDBAL: %#08lx\n", E1000_READ_REG ( hw, TDBAL ) );
+ DBG ( "TDLEN: %ld\n", E1000_READ_REG ( hw, TDLEN ) );
+
+ /* Setup the HW Tx Head and Tail descriptor pointers */
+ E1000_WRITE_REG ( hw, TDH, 0 );
+ E1000_WRITE_REG ( hw, TDT, 0 );
+
+ adapter->tx_head = 0;
+ adapter->tx_tail = 0;
+ adapter->tx_fill_ctr = 0;
+
+ /* Setup Transmit Descriptor Settings for eop descriptor */
+ tctl = E1000_TCTL_PSP | E1000_TCTL_EN |
+ (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT) |
+ (E1000_HDX_COLLISION_DISTANCE << E1000_COLD_SHIFT);
+
+ e1000_config_collision_dist ( hw );
+
+ E1000_WRITE_REG ( hw, TCTL, tctl );
+ E1000_WRITE_FLUSH ( hw );
+}
+
+/**
+ * e1000_setup_rx_resources - allocate Rx resources (Descriptors)
+ *
+ * @v adapter e1000 private structure
+ *
+ * @ret rc Returns 0 on success, negative on failure
+ **/
+static int
+e1000_setup_rx_resources ( struct e1000_adapter *adapter )
+{
+ int i, j;
+ struct e1000_rx_desc *rx_curr_desc;
+
+ DBG ( "e1000_setup_rx_resources\n" );
+
+ /* Allocate receive descriptor ring memory.
+ It must not cross a 64K boundary because of hardware errata
+ */
+
+ adapter->rx_base =
+ malloc_dma ( adapter->rx_ring_size, adapter->rx_ring_size );
+
+ if ( ! adapter->rx_base ) {
+ return -ENOMEM;
+ }
+ memset ( adapter->rx_base, 0, adapter->rx_ring_size );
+
+ for ( i = 0; i < NUM_RX_DESC; i++ ) {
+
+ adapter->rx_iobuf[i] = alloc_iob ( MAXIMUM_ETHERNET_VLAN_SIZE );
+
+ /* If unable to allocate all iobufs, free any that
+ * were successfully allocated, and return an error
+ */
+ if ( ! adapter->rx_iobuf[i] ) {
+ for ( j = 0; j < i; j++ ) {
+ free_iob ( adapter->rx_iobuf[j] );
+ }
+ return -ENOMEM;
+ }
+
+ rx_curr_desc = ( void * ) ( adapter->rx_base ) +
+ ( i * sizeof ( *adapter->rx_base ) );
+
+ rx_curr_desc->buffer_addr = virt_to_bus ( adapter->rx_iobuf[i]->data );
+
+ DBG ( "i = %d rx_curr_desc->buffer_addr = %#16llx\n",
+ i, rx_curr_desc->buffer_addr );
+
+ }
+ return 0;
+}
+
+static void
+e1000_free_rx_resources ( struct e1000_adapter *adapter )
+{
+ int i;
+
+ DBG ( "e1000_free_rx_resources\n" );
+
+ free_dma ( adapter->rx_base, adapter->rx_ring_size );
+
+ for ( i = 0; i < NUM_RX_DESC; i++ ) {
+ free_iob ( adapter->rx_iobuf[i] );
+ }
+}
+
+/**
+ * e1000_configure_rx - Configure 8254x Receive Unit after Reset
+ * @adapter: board private structure
+ *
+ * Configure the Rx unit of the MAC after a reset.
+ **/
+static void
+e1000_configure_rx ( struct e1000_adapter *adapter )
+{
+ struct e1000_hw *hw = &adapter->hw;
+ uint32_t rctl;
+
+ DBG ( "e1000_configure_rx\n" );
+
+ /* disable receives while setting up the descriptors */
+ rctl = E1000_READ_REG ( hw, RCTL );
+ E1000_WRITE_REG ( hw, RCTL, rctl & ~E1000_RCTL_EN );
+
+ adapter->rx_curr = 0;
+
+ /* Setup the HW Rx Head and Tail Descriptor Pointers and
+ * the Base and Length of the Rx Descriptor Ring */
+
+ E1000_WRITE_REG ( hw, RDBAL, virt_to_bus ( adapter->rx_base ) );
+ E1000_WRITE_REG ( hw, RDBAH, 0 );
+ E1000_WRITE_REG ( hw, RDLEN, adapter->rx_ring_size );
+
+ E1000_WRITE_REG ( hw, RDH, 0 );
+ E1000_WRITE_REG ( hw, RDT, NUM_RX_DESC - 1 );
+
+ /* Enable Receives */
+ rctl = ( E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SZ_2048 |
+ E1000_RCTL_MPE
+ );
+
+ E1000_WRITE_REG ( hw, RCTL, rctl );
+ E1000_WRITE_FLUSH ( hw );
+
+ DBG ( "RDBAL: %#08lx\n", E1000_READ_REG ( hw, RDBAL ) );
+ DBG ( "RDLEN: %ld\n", E1000_READ_REG ( hw, RDLEN ) );
+ DBG ( "RCTL: %#08lx\n", E1000_READ_REG ( hw, RCTL ) );
+}
+
+/**
+ * e1000_reset - Put e1000 NIC in known initial state
+ *
+ * @v adapter e1000 private structure
+ **/
+static void
+e1000_reset ( struct e1000_adapter *adapter )
+{
+ uint32_t pba = 0;
+ uint16_t fc_high_water_mark = E1000_FC_HIGH_DIFF;
+
+ DBG ( "e1000_reset\n" );
+
+ switch (adapter->hw.mac_type) {
+ case e1000_82542_rev2_0:
+ case e1000_82542_rev2_1:
+ case e1000_82543:
+ case e1000_82544:
+ case e1000_82540:
+ case e1000_82541:
+ case e1000_82541_rev_2:
+ pba = E1000_PBA_48K;
+ break;
+ case e1000_82545:
+ case e1000_82545_rev_3:
+ case e1000_82546:
+ case e1000_82546_rev_3:
+ pba = E1000_PBA_48K;
+ break;
+ case e1000_82547:
+ case e1000_82547_rev_2:
+ pba = E1000_PBA_30K;
+ break;
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_80003es2lan:
+ pba = E1000_PBA_38K;
+ break;
+ case e1000_82573:
+ pba = E1000_PBA_20K;
+ break;
+ case e1000_ich8lan:
+ pba = E1000_PBA_8K;
+ case e1000_undefined:
+ case e1000_num_macs:
+ break;
+ }
+
+ E1000_WRITE_REG ( &adapter->hw, PBA, pba );
+
+ /* flow control settings */
+ /* Set the FC high water mark to 90% of the FIFO size.
+ * Required to clear last 3 LSB */
+ fc_high_water_mark = ((pba * 9216)/10) & 0xFFF8;
+ /* We can't use 90% on small FIFOs because the remainder
+ * would be less than 1 full frame. In this case, we size
+ * it to allow at least a full frame above the high water
+ * mark. */
+ if (pba < E1000_PBA_16K)
+ fc_high_water_mark = (pba * 1024) - 1600;
+
+ adapter->hw.fc_high_water = fc_high_water_mark;
+ adapter->hw.fc_low_water = fc_high_water_mark - 8;
+ if (adapter->hw.mac_type == e1000_80003es2lan)
+ adapter->hw.fc_pause_time = 0xFFFF;
+ else
+ adapter->hw.fc_pause_time = E1000_FC_PAUSE_TIME;
+ adapter->hw.fc_send_xon = 1;
+ adapter->hw.fc = adapter->hw.original_fc;
+ /* Allow time for pending master requests to run */
+
+ e1000_reset_hw ( &adapter->hw );
+
+ if ( adapter->hw.mac_type >= e1000_82544 )
+ E1000_WRITE_REG ( &adapter->hw, WUC, 0 );
+
+ if ( e1000_init_hw ( &adapter->hw ) )
+ DBG ( "Hardware Error\n" );
+
+ /* if (adapter->hwflags & HWFLAGS_PHY_PWR_BIT) { */
+ if (adapter->hw.mac_type >= e1000_82544 &&
+ adapter->hw.mac_type <= e1000_82547_rev_2 &&
+ adapter->hw.autoneg == 1 &&
+ adapter->hw.autoneg_advertised == ADVERTISE_1000_FULL) {
+ uint32_t ctrl = E1000_READ_REG(&adapter->hw, CTRL);
+ /* clear phy power management bit if we are in gig only mode,
+ * which if enabled will attempt negotiation to 100Mb, which
+ * can cause a loss of link at power off or driver unload */
+ ctrl &= ~E1000_CTRL_SWDPIN3;
+ E1000_WRITE_REG(&adapter->hw, CTRL, ctrl);
+ }
+
+ e1000_phy_get_info ( &adapter->hw, &adapter->phy_info );
+
+ if (!adapter->smart_power_down &&
+ (adapter->hw.mac_type == e1000_82571 ||
+ adapter->hw.mac_type == e1000_82572)) {
+ uint16_t phy_data = 0;
+ /* speed up time to link by disabling smart power down, ignore
+ * the return value of this function because there is nothing
+ * different we would do if it failed */
+ e1000_read_phy_reg(&adapter->hw, IGP02E1000_PHY_POWER_MGMT,
+ &phy_data);
+ phy_data &= ~IGP02E1000_PM_SPD;
+ e1000_write_phy_reg(&adapter->hw, IGP02E1000_PHY_POWER_MGMT,
+ phy_data);
+ }
+}
+
+/** Functions that implement the gPXE driver API **/
+
+/**
+ * e1000_close - Disables a network interface
+ *
+ * @v netdev network interface device structure
+ *
+ **/
+static void
+e1000_close ( struct net_device *netdev )
+{
+ struct e1000_adapter *adapter = netdev_priv ( netdev );
+ struct e1000_hw *hw = &adapter->hw;
+ uint32_t rctl;
+ uint32_t icr;
+
+ DBG ( "e1000_close\n" );
+
+ /* Acknowledge interrupts */
+ icr = E1000_READ_REG ( hw, ICR );
+
+ e1000_irq_disable ( adapter );
+
+ /* disable receives */
+ rctl = E1000_READ_REG ( hw, RCTL );
+ E1000_WRITE_REG ( hw, RCTL, rctl & ~E1000_RCTL_EN );
+ E1000_WRITE_FLUSH ( hw );
+
+ e1000_reset_hw ( hw );
+
+ e1000_free_tx_resources ( adapter );
+ e1000_free_rx_resources ( adapter );
+}
+
+/**
+ * e1000_transmit - Transmit a packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ *
+ * @ret rc Returns 0 on success, negative on failure
+ */
+static int
+e1000_transmit ( struct net_device *netdev, struct io_buffer *iobuf )
+{
+ struct e1000_adapter *adapter = netdev_priv( netdev );
+ struct e1000_hw *hw = &adapter->hw;
+ uint32_t tx_curr = adapter->tx_tail;
+ struct e1000_tx_desc *tx_curr_desc;
+
+ DBG ("e1000_transmit\n");
+
+ if ( adapter->tx_fill_ctr == NUM_TX_DESC ) {
+ DBG ("TX overflow\n");
+ return -ENOBUFS;
+ }
+
+ /* Save pointer to iobuf we have been given to transmit,
+ netdev_tx_complete() will need it later
+ */
+ adapter->tx_iobuf[tx_curr] = iobuf;
+
+ tx_curr_desc = ( void * ) ( adapter->tx_base ) +
+ ( tx_curr * sizeof ( *adapter->tx_base ) );
+
+ DBG ( "tx_curr_desc = %#08lx\n", virt_to_bus ( tx_curr_desc ) );
+ DBG ( "tx_curr_desc + 16 = %#08lx\n", virt_to_bus ( tx_curr_desc ) + 16 );
+ DBG ( "iobuf->data = %#08lx\n", virt_to_bus ( iobuf->data ) );
+
+ /* Add the packet to TX ring
+ */
+ tx_curr_desc->buffer_addr =
+ virt_to_bus ( iobuf->data );
+ tx_curr_desc->lower.data =
+ E1000_TXD_CMD_RPS | E1000_TXD_CMD_EOP |
+ E1000_TXD_CMD_IFCS | iob_len ( iobuf );
+ tx_curr_desc->upper.data = 0;
+
+ DBG ( "TX fill: %ld tx_curr: %ld addr: %#08lx len: %zd\n", adapter->tx_fill_ctr,
+ tx_curr, virt_to_bus ( iobuf->data ), iob_len ( iobuf ) );
+
+ /* Point to next free descriptor */
+ adapter->tx_tail = ( adapter->tx_tail + 1 ) % NUM_TX_DESC;
+ adapter->tx_fill_ctr++;
+
+ /* Write new tail to NIC, making packet available for transmit
+ */
+ E1000_WRITE_REG ( hw, TDT, adapter->tx_tail );
+
+ return 0;
+}
+
+/**
+ * e1000_poll - Poll for received packets
+ *
+ * @v netdev Network device
+ */
+static void
+e1000_poll ( struct net_device *netdev )
+{
+ struct e1000_adapter *adapter = netdev_priv( netdev );
+ struct e1000_hw *hw = &adapter->hw;
+
+ uint32_t icr;
+ uint32_t tx_status;
+ uint32_t rx_status;
+ uint32_t rx_len;
+ uint32_t rx_err;
+ struct io_buffer *rx_iob;
+ struct e1000_tx_desc *tx_curr_desc;
+ struct e1000_rx_desc *rx_curr_desc;
+ uint32_t i;
+ uint64_t tmp_buffer_addr;
+
+ DBGP ( "e1000_poll\n" );
+
+ /* Acknowledge interrupts */
+ icr = E1000_READ_REG ( hw, ICR );
+ if ( ! icr )
+ return;
+
+ DBG ( "e1000_poll: intr_status = %#08lx\n", icr );
+
+ /* Check status of transmitted packets
+ */
+ while ( ( i = adapter->tx_head ) != adapter->tx_tail ) {
+
+ tx_curr_desc = ( void * ) ( adapter->tx_base ) +
+ ( i * sizeof ( *adapter->tx_base ) );
+
+ tx_status = tx_curr_desc->upper.data;
+
+ /* if the packet at tx_head is not owned by hardware it is for us */
+ if ( ! ( tx_status & E1000_TXD_STAT_DD ) )
+ break;
+
+ DBG ( "Sent packet. tx_head: %ld tx_tail: %ld tx_status: %#08lx\n",
+ adapter->tx_head, adapter->tx_tail, tx_status );
+
+ if ( tx_status & ( E1000_TXD_STAT_EC | E1000_TXD_STAT_LC |
+ E1000_TXD_STAT_TU ) ) {
+ netdev_tx_complete_err ( netdev, adapter->tx_iobuf[i], -EINVAL );
+ DBG ( "Error transmitting packet, tx_status: %#08lx\n",
+ tx_status );
+ } else {
+ netdev_tx_complete ( netdev, adapter->tx_iobuf[i] );
+ DBG ( "Success transmitting packet, tx_status: %#08lx\n",
+ tx_status );
+ }
+
+ /* Decrement count of used descriptors, clear this descriptor
+ */
+ adapter->tx_fill_ctr--;
+ memset ( tx_curr_desc, 0, sizeof ( *tx_curr_desc ) );
+
+ adapter->tx_head = ( adapter->tx_head + 1 ) % NUM_TX_DESC;
+ }
+
+ /* Process received packets
+ */
+ while ( 1 ) {
+
+ i = adapter->rx_curr;
+
+ rx_curr_desc = ( void * ) ( adapter->rx_base ) +
+ ( i * sizeof ( *adapter->rx_base ) );
+ rx_status = rx_curr_desc->status;
+
+ DBG2 ( "Before DD Check RX_status: %#08lx\n", rx_status );
+
+ if ( ! ( rx_status & E1000_RXD_STAT_DD ) )
+ break;
+
+ DBG ( "RCTL = %#08lx\n", E1000_READ_REG ( &adapter->hw, RCTL ) );
+
+ rx_len = rx_curr_desc->length;
+
+ DBG ( "Received packet, rx_curr: %ld rx_status: %#08lx rx_len: %ld\n",
+ i, rx_status, rx_len );
+
+ rx_err = rx_curr_desc->errors;
+
+ if ( rx_err & E1000_RXD_ERR_FRAME_ERR_MASK ) {
+
+ netdev_rx_err ( netdev, NULL, -EINVAL );
+ DBG ( "e1000_poll: Corrupted packet received!"
+ " rx_err: %#08lx\n", rx_err );
+ } else {
+
+ /* If unable allocate space for this packet,
+ * try again next poll
+ */
+ rx_iob = alloc_iob ( rx_len );
+ if ( ! rx_iob )
+ break;
+
+ memcpy ( iob_put ( rx_iob, rx_len ),
+ adapter->rx_iobuf[i]->data, rx_len );
+
+ /* Add this packet to the receive queue.
+ */
+ netdev_rx ( netdev, rx_iob );
+ }
+
+ tmp_buffer_addr = rx_curr_desc->buffer_addr;
+ memset ( rx_curr_desc, 0, sizeof ( *rx_curr_desc ) );
+ rx_curr_desc->buffer_addr = tmp_buffer_addr;
+
+ E1000_WRITE_REG ( hw, RDT, adapter->rx_curr );
+
+ adapter->rx_curr = ( adapter->rx_curr + 1 ) % NUM_RX_DESC;
+ }
+}
+
+/**
+ * e1000_irq - enable or Disable interrupts
+ *
+ * @v adapter e1000 adapter
+ * @v action requested interrupt action
+ **/
+static void
+e1000_irq ( struct net_device *netdev, int enable )
+{
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+
+ DBG ( "e1000_irq\n" );
+
+ switch ( enable ) {
+ case 0 :
+ e1000_irq_disable ( adapter );
+ break;
+ case 1 :
+ e1000_irq_enable ( adapter );
+ break;
+ case 2 :
+ e1000_irq_force ( adapter );
+ break;
+ }
+}
+
+static struct net_device_operations e1000_operations;
+
+/**
+ * e1000_probe - Initial configuration of e1000 NIC
+ *
+ * @v pci PCI device
+ * @v id PCI IDs
+ *
+ * @ret rc Return status code
+ **/
+static int
+e1000_probe ( struct pci_device *pdev,
+ const struct pci_device_id *id __unused )
+{
+ int i, err;
+ struct net_device *netdev;
+ struct e1000_adapter *adapter;
+ unsigned long mmio_start, mmio_len;
+ unsigned long flash_start, flash_len;
+
+ DBG ( "e1000_probe\n" );
+
+ err = -ENOMEM;
+
+ /* Allocate net device ( also allocates memory for netdev->priv
+ and makes netdev-priv point to it ) */
+ netdev = alloc_etherdev ( sizeof ( struct e1000_adapter ) );
+ if ( ! netdev )
+ goto err_alloc_etherdev;
+
+ /* Associate e1000-specific network operations operations with
+ * generic network device layer */
+ netdev_init ( netdev, &e1000_operations );
+
+ /* Associate this network device with given PCI device */
+ pci_set_drvdata ( pdev, netdev );
+ netdev->dev = &pdev->dev;
+
+ /* Initialize driver private storage */
+ adapter = netdev_priv ( netdev );
+ memset ( adapter, 0, ( sizeof ( *adapter ) ) );
+
+ adapter->hw.io_base = pdev->ioaddr;
+ adapter->ioaddr = pdev->ioaddr;
+ adapter->irqno = pdev->irq;
+ adapter->netdev = netdev;
+ adapter->pdev = pdev;
+ adapter->hw.back = adapter;
+
+ adapter->tx_ring_size = sizeof ( *adapter->tx_base ) * NUM_TX_DESC;
+ adapter->rx_ring_size = sizeof ( *adapter->rx_base ) * NUM_RX_DESC;
+
+ mmio_start = pci_bar_start ( pdev, PCI_BASE_ADDRESS_0 );
+ mmio_len = pci_bar_size ( pdev, PCI_BASE_ADDRESS_0 );
+
+ DBG ( "mmio_start: %#08lx\n", mmio_start );
+ DBG ( "mmio_len: %#08lx\n", mmio_len );
+
+ /* Fix up PCI device */
+ adjust_pci_device ( pdev );
+
+ err = -EIO;
+
+ adapter->hw.hw_addr = ioremap ( mmio_start, mmio_len );
+ DBG ( "adapter->hw.hw_addr: %p\n", adapter->hw.hw_addr );
+
+ if ( ! adapter->hw.hw_addr )
+ goto err_ioremap;
+
+ /* setup the private structure */
+ if ( ( err = e1000_sw_init ( adapter ) ) )
+ goto err_sw_init;
+
+ DBG ( "adapter->hw.mac_type: %#08x\n", adapter->hw.mac_type );
+
+ /* Flash BAR mapping must happen after e1000_sw_init
+ * because it depends on mac_type
+ */
+ if ( ( adapter->hw.mac_type == e1000_ich8lan ) && ( pdev->ioaddr ) ) {
+ flash_start = pci_bar_start ( pdev, 1 );
+ flash_len = pci_bar_size ( pdev, 1 );
+ adapter->hw.flash_address = ioremap ( flash_start, flash_len );
+ if ( ! adapter->hw.flash_address )
+ goto err_flashmap;
+ }
+
+ /* initialize eeprom parameters */
+ if ( e1000_init_eeprom_params ( &adapter->hw ) ) {
+ DBG ( "EEPROM initialization failed\n" );
+ goto err_eeprom;
+ }
+
+ /* before reading the EEPROM, reset the controller to
+ * put the device in a known good starting state
+ */
+ err = e1000_reset_hw ( &adapter->hw );
+ if ( err < 0 ) {
+ DBG ( "Hardware Initialization Failed\n" );
+ goto err_reset;
+ }
+
+ /* make sure the EEPROM is good */
+ if ( e1000_validate_eeprom_checksum( &adapter->hw ) < 0 ) {
+ DBG ( "The EEPROM Checksum Is Not Valid\n" );
+ goto err_eeprom;
+ }
+
+ /* copy the MAC address out of the EEPROM */
+ if ( e1000_read_mac_addr ( &adapter->hw ) )
+ DBG ( "EEPROM Read Error\n" );
+
+ memcpy ( netdev->ll_addr, adapter->hw.mac_addr, ETH_ALEN );
+
+ /* print bus type/speed/width info */
+ {
+ struct e1000_hw *hw = &adapter->hw;
+ DBG ( "(PCI%s:%s:%s) ",
+ ((hw->bus_type == e1000_bus_type_pcix) ? "-X" :
+ (hw->bus_type == e1000_bus_type_pci_express ? " Express":"")),
+ ((hw->bus_speed == e1000_bus_speed_2500) ? "2.5Gb/s" :
+ (hw->bus_speed == e1000_bus_speed_133) ? "133MHz" :
+ (hw->bus_speed == e1000_bus_speed_120) ? "120MHz" :
+ (hw->bus_speed == e1000_bus_speed_100) ? "100MHz" :
+ (hw->bus_speed == e1000_bus_speed_66) ? "66MHz" : "33MHz"),
+ ((hw->bus_width == e1000_bus_width_64) ? "64-bit" :
+ (hw->bus_width == e1000_bus_width_pciex_4) ? "Width x4" :
+ (hw->bus_width == e1000_bus_width_pciex_1) ? "Width x1" :
+ "32-bit"));
+ }
+ for (i = 0; i < 6; i++)
+ DBG ("%02x%s", netdev->ll_addr[i], i == 5 ? "\n" : ":");
+
+ /* reset the hardware with the new settings */
+ e1000_reset ( adapter );
+
+ e1000_get_hw_control ( adapter );
+
+ if ( ( err = register_netdev ( netdev ) ) != 0)
+ goto err_register;
+
+ DBG ( "e1000_probe succeeded!\n" );
+
+ /* No errors, return success */
+ return 0;
+
+/* Error return paths */
+err_reset:
+err_register:
+err_eeprom:
+ if ( ! e1000_check_phy_reset_block ( &adapter->hw ) )
+ e1000_phy_hw_reset ( &adapter->hw );
+ if ( adapter->hw.flash_address )
+ iounmap ( adapter->hw.flash_address );
+err_flashmap:
+err_sw_init:
+ iounmap ( adapter->hw.hw_addr );
+err_ioremap:
+ netdev_put ( netdev );
+err_alloc_etherdev:
+ return err;
+}
+
+/**
+ * e1000_remove - Device Removal Routine
+ *
+ * @v pdev PCI device information struct
+ *
+ **/
+static void
+e1000_remove ( struct pci_device *pdev )
+{
+ struct net_device *netdev = pci_get_drvdata ( pdev );
+ struct e1000_adapter *adapter = netdev_priv ( netdev );
+
+ DBG ( "e1000_remove\n" );
+
+ if ( adapter->hw.flash_address )
+ iounmap ( adapter->hw.flash_address );
+ if ( adapter->hw.hw_addr )
+ iounmap ( adapter->hw.hw_addr );
+
+ unregister_netdev ( netdev );
+ e1000_reset_hw ( &adapter->hw );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/**
+ * e1000_open - Called when a network interface is made active
+ *
+ * @v netdev network interface device structure
+ * @ret rc Return status code, 0 on success, negative value on failure
+ *
+ **/
+static int
+e1000_open ( struct net_device *netdev )
+{
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+ int err;
+
+ DBG ( "e1000_open\n" );
+
+ /* allocate transmit descriptors */
+ err = e1000_setup_tx_resources ( adapter );
+ if ( err ) {
+ DBG ( "Error setting up TX resources!\n" );
+ goto err_setup_tx;
+ }
+
+ /* allocate receive descriptors */
+ err = e1000_setup_rx_resources ( adapter );
+ if ( err ) {
+ DBG ( "Error setting up RX resources!\n" );
+ goto err_setup_rx;
+ }
+
+ e1000_configure_tx ( adapter );
+
+ e1000_configure_rx ( adapter );
+
+ DBG ( "RXDCTL: %#08lx\n", E1000_READ_REG ( &adapter->hw, RXDCTL ) );
+
+ return 0;
+
+err_setup_rx:
+ e1000_free_tx_resources ( adapter );
+err_setup_tx:
+ e1000_reset ( adapter );
+
+ return err;
+}
+
+/** e1000 net device operations */
+static struct net_device_operations e1000_operations = {
+ .open = e1000_open,
+ .close = e1000_close,
+ .transmit = e1000_transmit,
+ .poll = e1000_poll,
+ .irq = e1000_irq,
+};
+
+int32_t
+e1000_read_pcie_cap_reg(struct e1000_hw *hw, uint32_t reg, uint16_t *value)
+{
+ struct e1000_adapter *adapter = hw->back;
+ uint16_t cap_offset;
+
+#define PCI_CAP_ID_EXP 0x10 /* PCI Express */
+ cap_offset = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP);
+ if (!cap_offset)
+ return -E1000_ERR_CONFIG;
+
+ pci_read_config_word(adapter->pdev, cap_offset + reg, value);
+
+ return 0;
+}
+
+void
+e1000_pci_clear_mwi ( struct e1000_hw *hw )
+{
+ struct e1000_adapter *adapter = hw->back;
+
+ pci_write_config_word ( adapter->pdev, PCI_COMMAND,
+ hw->pci_cmd_word & ~PCI_COMMAND_INVALIDATE );
+}
+
+void
+e1000_pci_set_mwi ( struct e1000_hw *hw )
+{
+ struct e1000_adapter *adapter = hw->back;
+
+ pci_write_config_word ( adapter->pdev, PCI_COMMAND, hw->pci_cmd_word );
+}
+
+void
+e1000_read_pci_cfg ( struct e1000_hw *hw, uint32_t reg, uint16_t *value )
+{
+ struct e1000_adapter *adapter = hw->back;
+
+ pci_read_config_word ( adapter->pdev, reg, value );
+}
+
+void
+e1000_write_pci_cfg ( struct e1000_hw *hw, uint32_t reg, uint16_t *value )
+{
+ struct e1000_adapter *adapter = hw->back;
+
+ pci_write_config_word ( adapter->pdev, reg, *value );
+}
+
+void
+e1000_io_write ( struct e1000_hw *hw __unused, unsigned long port, uint32_t value )
+{
+ outl ( value, port );
+}
+
+static struct pci_device_id e1000_nics[] = {
+ PCI_ROM(0x8086, 0x1000, "e1000-0x1000", "e1000-0x1000"),
+ PCI_ROM(0x8086, 0x1001, "e1000-0x1001", "e1000-0x1001"),
+ PCI_ROM(0x8086, 0x1004, "e1000-0x1004", "e1000-0x1004"),
+ PCI_ROM(0x8086, 0x1008, "e1000-0x1008", "e1000-0x1008"),
+ PCI_ROM(0x8086, 0x1009, "e1000-0x1009", "e1000-0x1009"),
+ PCI_ROM(0x8086, 0x100c, "e1000-0x100c", "e1000-0x100c"),
+ PCI_ROM(0x8086, 0x100d, "e1000-0x100d", "e1000-0x100d"),
+ PCI_ROM(0x8086, 0x100e, "e1000-0x100e", "e1000-0x100e"),
+ PCI_ROM(0x8086, 0x100f, "e1000-0x100f", "e1000-0x100f"),
+ PCI_ROM(0x8086, 0x1010, "e1000-0x1010", "e1000-0x1010"),
+ PCI_ROM(0x8086, 0x1011, "e1000-0x1011", "e1000-0x1011"),
+ PCI_ROM(0x8086, 0x1012, "e1000-0x1012", "e1000-0x1012"),
+ PCI_ROM(0x8086, 0x1013, "e1000-0x1013", "e1000-0x1013"),
+ PCI_ROM(0x8086, 0x1014, "e1000-0x1014", "e1000-0x1014"),
+ PCI_ROM(0x8086, 0x1015, "e1000-0x1015", "e1000-0x1015"),
+ PCI_ROM(0x8086, 0x1016, "e1000-0x1016", "e1000-0x1016"),
+ PCI_ROM(0x8086, 0x1017, "e1000-0x1017", "e1000-0x1017"),
+ PCI_ROM(0x8086, 0x1018, "e1000-0x1018", "e1000-0x1018"),
+ PCI_ROM(0x8086, 0x1019, "e1000-0x1019", "e1000-0x1019"),
+ PCI_ROM(0x8086, 0x101a, "e1000-0x101a", "e1000-0x101a"),
+ PCI_ROM(0x8086, 0x101d, "e1000-0x101d", "e1000-0x101d"),
+ PCI_ROM(0x8086, 0x101e, "e1000-0x101e", "e1000-0x101e"),
+ PCI_ROM(0x8086, 0x1026, "e1000-0x1026", "e1000-0x1026"),
+ PCI_ROM(0x8086, 0x1027, "e1000-0x1027", "e1000-0x1027"),
+ PCI_ROM(0x8086, 0x1028, "e1000-0x1028", "e1000-0x1028"),
+ PCI_ROM(0x8086, 0x1049, "e1000-0x1049", "e1000-0x1049"),
+ PCI_ROM(0x8086, 0x104a, "e1000-0x104a", "e1000-0x104a"),
+ PCI_ROM(0x8086, 0x104b, "e1000-0x104b", "e1000-0x104b"),
+ PCI_ROM(0x8086, 0x104c, "e1000-0x104c", "e1000-0x104c"),
+ PCI_ROM(0x8086, 0x104d, "e1000-0x104d", "e1000-0x104d"),
+ PCI_ROM(0x8086, 0x105e, "e1000-0x105e", "e1000-0x105e"),
+ PCI_ROM(0x8086, 0x105f, "e1000-0x105f", "e1000-0x105f"),
+ PCI_ROM(0x8086, 0x1060, "e1000-0x1060", "e1000-0x1060"),
+ PCI_ROM(0x8086, 0x1075, "e1000-0x1075", "e1000-0x1075"),
+ PCI_ROM(0x8086, 0x1076, "e1000-0x1076", "e1000-0x1076"),
+ PCI_ROM(0x8086, 0x1077, "e1000-0x1077", "e1000-0x1077"),
+ PCI_ROM(0x8086, 0x1078, "e1000-0x1078", "e1000-0x1078"),
+ PCI_ROM(0x8086, 0x1079, "e1000-0x1079", "e1000-0x1079"),
+ PCI_ROM(0x8086, 0x107a, "e1000-0x107a", "e1000-0x107a"),
+ PCI_ROM(0x8086, 0x107b, "e1000-0x107b", "e1000-0x107b"),
+ PCI_ROM(0x8086, 0x107c, "e1000-0x107c", "e1000-0x107c"),
+ PCI_ROM(0x8086, 0x107d, "e1000-0x107d", "e1000-0x107d"),
+ PCI_ROM(0x8086, 0x107e, "e1000-0x107e", "e1000-0x107e"),
+ PCI_ROM(0x8086, 0x107f, "e1000-0x107f", "e1000-0x107f"),
+ PCI_ROM(0x8086, 0x108a, "e1000-0x108a", "e1000-0x108a"),
+ PCI_ROM(0x8086, 0x108b, "e1000-0x108b", "e1000-0x108b"),
+ PCI_ROM(0x8086, 0x108c, "e1000-0x108c", "e1000-0x108c"),
+ PCI_ROM(0x8086, 0x1096, "e1000-0x1096", "e1000-0x1096"),
+ PCI_ROM(0x8086, 0x1098, "e1000-0x1098", "e1000-0x1098"),
+ PCI_ROM(0x8086, 0x1099, "e1000-0x1099", "e1000-0x1099"),
+ PCI_ROM(0x8086, 0x109a, "e1000-0x109a", "e1000-0x109a"),
+ PCI_ROM(0x8086, 0x10a4, "e1000-0x10a4", "e1000-0x10a4"),
+ PCI_ROM(0x8086, 0x10a5, "e1000-0x10a5", "e1000-0x10a5"),
+ PCI_ROM(0x8086, 0x10b5, "e1000-0x10b5", "e1000-0x10b5"),
+ PCI_ROM(0x8086, 0x10b9, "e1000-0x10b9", "e1000-0x10b9"),
+ PCI_ROM(0x8086, 0x10ba, "e1000-0x10ba", "e1000-0x10ba"),
+ PCI_ROM(0x8086, 0x10bb, "e1000-0x10bb", "e1000-0x10bb"),
+ PCI_ROM(0x8086, 0x10bc, "e1000-0x10bc", "e1000-0x10bc"),
+ PCI_ROM(0x8086, 0x10c4, "e1000-0x10c4", "e1000-0x10c4"),
+ PCI_ROM(0x8086, 0x10c5, "e1000-0x10c5", "e1000-0x10c5"),
+ PCI_ROM(0x8086, 0x10d9, "e1000-0x10d9", "e1000-0x10d9"),
+ PCI_ROM(0x8086, 0x10da, "e1000-0x10da", "e1000-0x10da"),
+};
+
+struct pci_driver e1000_driver __pci_driver = {
+ .ids = e1000_nics,
+ .id_count = (sizeof (e1000_nics) / sizeof (e1000_nics[0])),
+ .probe = e1000_probe,
+ .remove = e1000_remove,
+};
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/e1000/e1000.h b/gpxe/src/drivers/net/e1000/e1000.h
new file mode 100644
index 00000000..4ae41451
--- /dev/null
+++ b/gpxe/src/drivers/net/e1000/e1000.h
@@ -0,0 +1,303 @@
+/*******************************************************************************
+
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+/* Linux PRO/1000 Ethernet Driver main header file */
+
+#ifndef _E1000_H_
+#define _E1000_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <io.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/pci.h>
+#include <gpxe/malloc.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+
+#define BAR_0 0
+#define BAR_1 1
+#define BAR_5 5
+
+struct e1000_adapter;
+
+#include "e1000_hw.h"
+
+/* Supported Rx Buffer Sizes */
+#define E1000_RXBUFFER_128 128 /* Used for packet split */
+#define E1000_RXBUFFER_256 256 /* Used for packet split */
+#define E1000_RXBUFFER_512 512
+#define E1000_RXBUFFER_1024 1024
+#define E1000_RXBUFFER_2048 2048
+#define E1000_RXBUFFER_4096 4096
+#define E1000_RXBUFFER_8192 8192
+#define E1000_RXBUFFER_16384 16384
+
+/* SmartSpeed delimiters */
+#define E1000_SMARTSPEED_DOWNSHIFT 3
+#define E1000_SMARTSPEED_MAX 15
+
+/* Packet Buffer allocations */
+#define E1000_PBA_BYTES_SHIFT 0xA
+#define E1000_TX_HEAD_ADDR_SHIFT 7
+#define E1000_PBA_TX_MASK 0xFFFF0000
+
+/* Flow Control Watermarks */
+#define E1000_FC_HIGH_DIFF 0x1638 /* High: 5688 bytes below Rx FIFO size */
+#define E1000_FC_LOW_DIFF 0x1640 /* Low: 5696 bytes below Rx FIFO size */
+
+#define E1000_FC_PAUSE_TIME 0x0680 /* 858 usec */
+
+/* this is the size past which hardware will drop packets when setting LPE=0 */
+#define MAXIMUM_ETHERNET_VLAN_SIZE 1522
+
+/* How many Tx Descriptors do we need to call netif_wake_queue ? */
+#define E1000_TX_QUEUE_WAKE 16
+/* How many Rx Buffers do we bundle into one write to the hardware ? */
+#define E1000_RX_BUFFER_WRITE 16 /* Must be power of 2 */
+
+#define AUTO_ALL_MODES 0
+#define E1000_EEPROM_82544_APM 0x0004
+#define E1000_EEPROM_ICH8_APME 0x0004
+#define E1000_EEPROM_APME 0x0400
+
+#ifndef E1000_MASTER_SLAVE
+/* Switch to override PHY master/slave setting */
+#define E1000_MASTER_SLAVE e1000_ms_hw_default
+#endif
+
+/* wrapper around a pointer to a socket buffer,
+ * so a DMA handle can be stored along with the buffer */
+struct e1000_buffer {
+ struct sk_buff *skb;
+ unsigned long time_stamp;
+ uint16_t length;
+ uint16_t next_to_watch;
+};
+
+struct e1000_tx_ring {
+ /* pointer to the descriptor ring memory */
+ void *desc;
+ /* length of descriptor ring in bytes */
+ unsigned int size;
+ /* number of descriptors in the ring */
+ unsigned int count;
+ /* next descriptor to associate a buffer with */
+ unsigned int next_to_use;
+ /* next descriptor to check for DD status bit */
+ unsigned int next_to_clean;
+ /* array of buffer information structs */
+ struct e1000_buffer *buffer_info;
+
+ uint16_t tdh;
+ uint16_t tdt;
+ boolean_t last_tx_tso;
+};
+
+struct e1000_rx_ring {
+ /* pointer to the descriptor ring memory */
+ void *desc;
+ /* length of descriptor ring in bytes */
+ unsigned int size;
+ /* number of descriptors in the ring */
+ unsigned int count;
+ /* next descriptor to associate a buffer with */
+ unsigned int next_to_use;
+ /* next descriptor to check for DD status bit */
+ unsigned int next_to_clean;
+ /* array of buffer information structs */
+ struct e1000_buffer *buffer_info;
+ /* arrays of page information for packet split */
+ struct e1000_ps_page *ps_page;
+ struct e1000_ps_page_dma *ps_page_dma;
+
+ /* cpu for rx queue */
+ int cpu;
+
+ uint16_t rdh;
+ uint16_t rdt;
+};
+
+#define E1000_DESC_UNUSED(R) \
+ ((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
+ (R)->next_to_clean - (R)->next_to_use - 1)
+
+#define E1000_RX_DESC_PS(R, i) \
+ (&(((union e1000_rx_desc_packet_split *)((R).desc))[i]))
+#define E1000_RX_DESC_EXT(R, i) \
+ (&(((union e1000_rx_desc_extended *)((R).desc))[i]))
+#define E1000_GET_DESC(R, i, type) (&(((struct type *)((R).desc))[i]))
+#define E1000_RX_DESC(R, i) E1000_GET_DESC(R, i, e1000_rx_desc)
+#define E1000_TX_DESC(R, i) E1000_GET_DESC(R, i, e1000_tx_desc)
+#define E1000_CONTEXT_DESC(R, i) E1000_GET_DESC(R, i, e1000_context_desc)
+
+/* board specific private data structure */
+
+struct e1000_adapter {
+ struct vlan_group *vlgrp;
+ uint16_t mng_vlan_id;
+ uint32_t bd_number;
+ uint32_t rx_buffer_len;
+ uint32_t wol;
+ uint32_t smartspeed;
+ uint32_t en_mng_pt;
+ uint16_t link_speed;
+ uint16_t link_duplex;
+
+ unsigned int total_tx_bytes;
+ unsigned int total_tx_packets;
+ unsigned int total_rx_bytes;
+ unsigned int total_rx_packets;
+ /* Interrupt Throttle Rate */
+ uint32_t itr;
+ uint32_t itr_setting;
+ uint16_t tx_itr;
+ uint16_t rx_itr;
+
+ uint8_t fc_autoneg;
+
+ unsigned long led_status;
+
+ /* TX */
+ struct e1000_tx_ring *tx_ring; /* One per active queue */
+ unsigned int restart_queue;
+ unsigned long tx_queue_len;
+ uint32_t txd_cmd;
+ uint32_t tx_int_delay;
+ uint32_t tx_abs_int_delay;
+ uint32_t gotcl;
+ uint64_t gotcl_old;
+ uint64_t tpt_old;
+ uint64_t colc_old;
+ uint32_t tx_timeout_count;
+ uint32_t tx_fifo_head;
+ uint32_t tx_head_addr;
+ uint32_t tx_fifo_size;
+ uint8_t tx_timeout_factor;
+ boolean_t pcix_82544;
+ boolean_t detect_tx_hung;
+
+ /* RX */
+ boolean_t (*clean_rx) (struct e1000_adapter *adapter,
+ struct e1000_rx_ring *rx_ring);
+ void (*alloc_rx_buf) (struct e1000_adapter *adapter,
+ struct e1000_rx_ring *rx_ring,
+ int cleaned_count);
+ struct e1000_rx_ring *rx_ring; /* One per active queue */
+ int num_tx_queues;
+ int num_rx_queues;
+
+ uint64_t hw_csum_err;
+ uint64_t hw_csum_good;
+ uint64_t rx_hdr_split;
+ uint32_t alloc_rx_buff_failed;
+ uint32_t rx_int_delay;
+ uint32_t rx_abs_int_delay;
+ boolean_t rx_csum;
+ unsigned int rx_ps_pages;
+ uint32_t gorcl;
+ uint64_t gorcl_old;
+ uint16_t rx_ps_bsize0;
+
+
+ /* OS defined structs */
+ struct net_device *netdev;
+ struct pci_device *pdev;
+ struct net_device_stats net_stats;
+
+ /* structs defined in e1000_hw.h */
+ struct e1000_hw hw;
+ struct e1000_hw_stats stats;
+ struct e1000_phy_info phy_info;
+ struct e1000_phy_stats phy_stats;
+
+ uint32_t test_icr;
+ struct e1000_tx_ring test_tx_ring;
+ struct e1000_rx_ring test_rx_ring;
+
+ int msg_enable;
+ boolean_t have_msi;
+
+ /* to not mess up cache alignment, always add to the bottom */
+ boolean_t tso_force;
+ boolean_t smart_power_down; /* phy smart power down */
+ boolean_t quad_port_a;
+ unsigned long flags;
+ uint32_t eeprom_wol;
+
+#define NUM_TX_DESC 8
+#define NUM_RX_DESC 8
+
+ struct io_buffer *tx_iobuf[NUM_TX_DESC];
+ struct io_buffer *rx_iobuf[NUM_RX_DESC];
+
+ struct e1000_tx_desc *tx_base;
+ struct e1000_rx_desc *rx_base;
+
+ uint32_t tx_ring_size;
+ uint32_t rx_ring_size;
+
+ uint32_t tx_head;
+ uint32_t tx_tail;
+ uint32_t tx_fill_ctr;
+
+ uint32_t rx_curr;
+
+ uint32_t ioaddr;
+ uint32_t irqno;
+
+};
+
+enum e1000_state_t {
+ __E1000_TESTING,
+ __E1000_RESETTING,
+ __E1000_DOWN
+};
+
+#define E1000_MNG2HOST_PORT_623 (1 << 5)
+#define E1000_MNG2HOST_PORT_664 (1 << 6)
+
+#define E1000_ERT_2048 0x100
+
+#define IORESOURCE_IO 0x00000100
+#define IORESOURCE_MEM 0x00000200
+#define IORESOURCE_PREFETCH 0x00001000
+
+#endif /* _E1000_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/e1000/e1000_hw.c b/gpxe/src/drivers/net/e1000/e1000_hw.c
new file mode 100644
index 00000000..0667ad61
--- /dev/null
+++ b/gpxe/src/drivers/net/e1000/e1000_hw.c
@@ -0,0 +1,9050 @@
+/*******************************************************************************
+
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+/* e1000_hw.c
+ * Shared functions for accessing and configuring the MAC
+ */
+
+
+#include "e1000_hw.h"
+
+static int32_t e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask);
+static void e1000_swfw_sync_release(struct e1000_hw *hw, uint16_t mask);
+static int32_t e1000_read_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *data);
+static int32_t e1000_write_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t data);
+static int32_t e1000_get_software_semaphore(struct e1000_hw *hw);
+static void e1000_release_software_semaphore(struct e1000_hw *hw);
+
+static uint8_t e1000_arc_subsystem_valid(struct e1000_hw *hw);
+static int32_t e1000_check_downshift(struct e1000_hw *hw);
+static int32_t e1000_check_polarity(struct e1000_hw *hw, e1000_rev_polarity *polarity);
+static void e1000_clear_hw_cntrs(struct e1000_hw *hw);
+static void e1000_clear_vfta(struct e1000_hw *hw);
+static int32_t e1000_commit_shadow_ram(struct e1000_hw *hw);
+static int32_t e1000_config_dsp_after_link_change(struct e1000_hw *hw, boolean_t link_up);
+static int32_t e1000_config_fc_after_link_up(struct e1000_hw *hw);
+static int32_t e1000_detect_gig_phy(struct e1000_hw *hw);
+static int32_t e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t bank);
+static int32_t e1000_get_auto_rd_done(struct e1000_hw *hw);
+static int32_t e1000_get_cable_length(struct e1000_hw *hw, uint16_t *min_length, uint16_t *max_length);
+static int32_t e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw);
+static int32_t e1000_get_phy_cfg_done(struct e1000_hw *hw);
+static int32_t e1000_get_software_flag(struct e1000_hw *hw);
+static int32_t e1000_ich8_cycle_init(struct e1000_hw *hw);
+static int32_t e1000_ich8_flash_cycle(struct e1000_hw *hw, uint32_t timeout);
+static int32_t e1000_id_led_init(struct e1000_hw *hw);
+static int32_t e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, uint32_t cnf_base_addr, uint32_t cnf_size);
+static int32_t e1000_init_lcd_from_nvm(struct e1000_hw *hw);
+static void e1000_init_rx_addrs(struct e1000_hw *hw);
+static void e1000_initialize_hardware_bits(struct e1000_hw *hw);
+static boolean_t e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw);
+static int32_t e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw);
+static int32_t e1000_mng_enable_host_if(struct e1000_hw *hw);
+static int32_t e1000_mng_host_if_write(struct e1000_hw *hw, uint8_t *buffer, uint16_t length, uint16_t offset, uint8_t *sum);
+static int32_t e1000_mng_write_cmd_header(struct e1000_hw* hw, struct e1000_host_mng_command_header* hdr);
+static int32_t e1000_mng_write_commit(struct e1000_hw *hw);
+static int32_t e1000_phy_ife_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info);
+static int32_t e1000_phy_igp_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info);
+static int32_t e1000_read_eeprom_eerd(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data);
+static int32_t e1000_write_eeprom_eewr(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data);
+static int32_t e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd);
+static int32_t e1000_phy_m88_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info);
+static void e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw);
+static int32_t e1000_read_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t *data);
+static int32_t e1000_verify_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t byte);
+static int32_t e1000_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t byte);
+static int32_t e1000_read_ich8_word(struct e1000_hw *hw, uint32_t index, uint16_t *data);
+static int32_t e1000_read_ich8_data(struct e1000_hw *hw, uint32_t index, uint32_t size, uint16_t *data);
+static int32_t e1000_write_ich8_data(struct e1000_hw *hw, uint32_t index, uint32_t size, uint16_t data);
+static int32_t e1000_read_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data);
+static int32_t e1000_write_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data);
+static void e1000_release_software_flag(struct e1000_hw *hw);
+static int32_t e1000_set_d3_lplu_state(struct e1000_hw *hw, boolean_t active);
+static int32_t e1000_set_d0_lplu_state(struct e1000_hw *hw, boolean_t active);
+static int32_t e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, uint32_t no_snoop);
+static void e1000_set_pci_express_master_disable(struct e1000_hw *hw);
+static int32_t e1000_wait_autoneg(struct e1000_hw *hw);
+static void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset, uint32_t value);
+static int32_t e1000_set_phy_type(struct e1000_hw *hw);
+static void e1000_phy_init_script(struct e1000_hw *hw);
+static int32_t e1000_setup_copper_link(struct e1000_hw *hw);
+static int32_t e1000_setup_fiber_serdes_link(struct e1000_hw *hw);
+static int32_t e1000_adjust_serdes_amplitude(struct e1000_hw *hw);
+static int32_t e1000_phy_force_speed_duplex(struct e1000_hw *hw);
+static int32_t e1000_config_mac_to_phy(struct e1000_hw *hw);
+static void e1000_raise_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl);
+static void e1000_lower_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl);
+static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, uint32_t data,
+ uint16_t count);
+static uint16_t e1000_shift_in_mdi_bits(struct e1000_hw *hw);
+static int32_t e1000_phy_reset_dsp(struct e1000_hw *hw);
+static int32_t e1000_write_eeprom_spi(struct e1000_hw *hw, uint16_t offset,
+ uint16_t words, uint16_t *data);
+static int32_t e1000_write_eeprom_microwire(struct e1000_hw *hw,
+ uint16_t offset, uint16_t words,
+ uint16_t *data);
+static int32_t e1000_spi_eeprom_ready(struct e1000_hw *hw);
+static void e1000_raise_ee_clk(struct e1000_hw *hw, uint32_t *eecd);
+static void e1000_lower_ee_clk(struct e1000_hw *hw, uint32_t *eecd);
+static void e1000_shift_out_ee_bits(struct e1000_hw *hw, uint16_t data,
+ uint16_t count);
+static int32_t e1000_write_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr,
+ uint16_t phy_data);
+static int32_t e1000_read_phy_reg_ex(struct e1000_hw *hw,uint32_t reg_addr,
+ uint16_t *phy_data);
+static uint16_t e1000_shift_in_ee_bits(struct e1000_hw *hw, uint16_t count);
+static int32_t e1000_acquire_eeprom(struct e1000_hw *hw);
+static void e1000_release_eeprom(struct e1000_hw *hw);
+static void e1000_standby_eeprom(struct e1000_hw *hw);
+static int32_t e1000_set_vco_speed(struct e1000_hw *hw);
+static int32_t e1000_polarity_reversal_workaround(struct e1000_hw *hw);
+static int32_t e1000_set_phy_mode(struct e1000_hw *hw);
+static int32_t e1000_host_if_read_cookie(struct e1000_hw *hw, uint8_t *buffer);
+static uint8_t e1000_calculate_mng_checksum(char *buffer, uint32_t length);
+static int32_t e1000_configure_kmrn_for_10_100(struct e1000_hw *hw,
+ uint16_t duplex);
+static int32_t e1000_configure_kmrn_for_1000(struct e1000_hw *hw);
+
+/* IGP cable length table */
+static const
+uint16_t e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] =
+ { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 10, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 25, 25, 25,
+ 25, 25, 25, 25, 30, 30, 30, 30, 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 50, 50, 50, 50, 50, 50, 50, 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 70, 70, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120};
+
+static const
+uint16_t e1000_igp_2_cable_length_table[IGP02E1000_AGC_LENGTH_TABLE_SIZE] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21,
+ 0, 0, 0, 3, 6, 10, 13, 16, 19, 23, 26, 29, 32, 35, 38, 41,
+ 6, 10, 14, 18, 22, 26, 30, 33, 37, 41, 44, 48, 51, 54, 58, 61,
+ 21, 26, 31, 35, 40, 44, 49, 53, 57, 61, 65, 68, 72, 75, 79, 82,
+ 40, 45, 51, 56, 61, 66, 70, 75, 79, 83, 87, 91, 94, 98, 101, 104,
+ 60, 66, 72, 77, 82, 87, 92, 96, 100, 104, 108, 111, 114, 117, 119, 121,
+ 83, 89, 95, 100, 105, 109, 113, 116, 119, 122, 124,
+ 104, 109, 114, 118, 121, 124};
+
+/******************************************************************************
+ * Set the phy type member in the hw struct.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int32_t
+e1000_set_phy_type(struct e1000_hw *hw)
+{
+ DEBUGFUNC("e1000_set_phy_type");
+
+ if (hw->mac_type == e1000_undefined)
+ return -E1000_ERR_PHY_TYPE;
+
+ switch (hw->phy_id) {
+ case M88E1000_E_PHY_ID:
+ case M88E1000_I_PHY_ID:
+ case M88E1011_I_PHY_ID:
+ case M88E1111_I_PHY_ID:
+ hw->phy_type = e1000_phy_m88;
+ break;
+ case IGP01E1000_I_PHY_ID:
+ if (hw->mac_type == e1000_82541 ||
+ hw->mac_type == e1000_82541_rev_2 ||
+ hw->mac_type == e1000_82547 ||
+ hw->mac_type == e1000_82547_rev_2) {
+ hw->phy_type = e1000_phy_igp;
+ break;
+ }
+ case IGP03E1000_E_PHY_ID:
+ hw->phy_type = e1000_phy_igp_3;
+ break;
+ case IFE_E_PHY_ID:
+ case IFE_PLUS_E_PHY_ID:
+ case IFE_C_E_PHY_ID:
+ hw->phy_type = e1000_phy_ife;
+ break;
+ case GG82563_E_PHY_ID:
+ if (hw->mac_type == e1000_80003es2lan) {
+ hw->phy_type = e1000_phy_gg82563;
+ break;
+ }
+ /* Fall Through */
+ default:
+ /* Should never have loaded on this device */
+ hw->phy_type = e1000_phy_undefined;
+ return -E1000_ERR_PHY_TYPE;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * IGP phy init script - initializes the GbE PHY
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_phy_init_script(struct e1000_hw *hw)
+{
+ uint32_t ret_val;
+ uint16_t phy_saved_data;
+
+ DEBUGFUNC("e1000_phy_init_script");
+
+ if (hw->phy_init_script) {
+ msleep(20);
+
+ /* Save off the current value of register 0x2F5B to be restored at
+ * the end of this routine. */
+ ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data);
+
+ /* Disabled the PHY transmitter */
+ e1000_write_phy_reg(hw, 0x2F5B, 0x0003);
+
+ msleep(20);
+
+ e1000_write_phy_reg(hw,0x0000,0x0140);
+
+ msleep(5);
+
+ switch (hw->mac_type) {
+ case e1000_82541:
+ case e1000_82547:
+ e1000_write_phy_reg(hw, 0x1F95, 0x0001);
+
+ e1000_write_phy_reg(hw, 0x1F71, 0xBD21);
+
+ e1000_write_phy_reg(hw, 0x1F79, 0x0018);
+
+ e1000_write_phy_reg(hw, 0x1F30, 0x1600);
+
+ e1000_write_phy_reg(hw, 0x1F31, 0x0014);
+
+ e1000_write_phy_reg(hw, 0x1F32, 0x161C);
+
+ e1000_write_phy_reg(hw, 0x1F94, 0x0003);
+
+ e1000_write_phy_reg(hw, 0x1F96, 0x003F);
+
+ e1000_write_phy_reg(hw, 0x2010, 0x0008);
+ break;
+
+ case e1000_82541_rev_2:
+ case e1000_82547_rev_2:
+ e1000_write_phy_reg(hw, 0x1F73, 0x0099);
+ break;
+ default:
+ break;
+ }
+
+ e1000_write_phy_reg(hw, 0x0000, 0x3300);
+
+ msleep(20);
+
+ /* Now enable the transmitter */
+ e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data);
+
+ if (hw->mac_type == e1000_82547) {
+ uint16_t fused, fine, coarse;
+
+ /* Move to analog registers page */
+ e1000_read_phy_reg(hw, IGP01E1000_ANALOG_SPARE_FUSE_STATUS, &fused);
+
+ if (!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) {
+ e1000_read_phy_reg(hw, IGP01E1000_ANALOG_FUSE_STATUS, &fused);
+
+ fine = fused & IGP01E1000_ANALOG_FUSE_FINE_MASK;
+ coarse = fused & IGP01E1000_ANALOG_FUSE_COARSE_MASK;
+
+ if (coarse > IGP01E1000_ANALOG_FUSE_COARSE_THRESH) {
+ coarse -= IGP01E1000_ANALOG_FUSE_COARSE_10;
+ fine -= IGP01E1000_ANALOG_FUSE_FINE_1;
+ } else if (coarse == IGP01E1000_ANALOG_FUSE_COARSE_THRESH)
+ fine -= IGP01E1000_ANALOG_FUSE_FINE_10;
+
+ fused = (fused & IGP01E1000_ANALOG_FUSE_POLY_MASK) |
+ (fine & IGP01E1000_ANALOG_FUSE_FINE_MASK) |
+ (coarse & IGP01E1000_ANALOG_FUSE_COARSE_MASK);
+
+ e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_CONTROL, fused);
+ e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_BYPASS,
+ IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL);
+ }
+ }
+ }
+}
+
+/******************************************************************************
+ * Set the mac type member in the hw struct.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+int32_t
+e1000_set_mac_type(struct e1000_hw *hw)
+{
+ DEBUGFUNC("e1000_set_mac_type");
+
+ switch (hw->device_id) {
+ case E1000_DEV_ID_82542:
+ switch (hw->revision_id) {
+ case E1000_82542_2_0_REV_ID:
+ hw->mac_type = e1000_82542_rev2_0;
+ break;
+ case E1000_82542_2_1_REV_ID:
+ hw->mac_type = e1000_82542_rev2_1;
+ break;
+ default:
+ /* Invalid 82542 revision ID */
+ return -E1000_ERR_MAC_TYPE;
+ }
+ break;
+ case E1000_DEV_ID_82543GC_FIBER:
+ case E1000_DEV_ID_82543GC_COPPER:
+ hw->mac_type = e1000_82543;
+ break;
+ case E1000_DEV_ID_82544EI_COPPER:
+ case E1000_DEV_ID_82544EI_FIBER:
+ case E1000_DEV_ID_82544GC_COPPER:
+ case E1000_DEV_ID_82544GC_LOM:
+ hw->mac_type = e1000_82544;
+ break;
+ case E1000_DEV_ID_82540EM:
+ case E1000_DEV_ID_82540EM_LOM:
+ case E1000_DEV_ID_82540EP:
+ case E1000_DEV_ID_82540EP_LOM:
+ case E1000_DEV_ID_82540EP_LP:
+ hw->mac_type = e1000_82540;
+ break;
+ case E1000_DEV_ID_82545EM_COPPER:
+ case E1000_DEV_ID_82545EM_FIBER:
+ hw->mac_type = e1000_82545;
+ break;
+ case E1000_DEV_ID_82545GM_COPPER:
+ case E1000_DEV_ID_82545GM_FIBER:
+ case E1000_DEV_ID_82545GM_SERDES:
+ hw->mac_type = e1000_82545_rev_3;
+ break;
+ case E1000_DEV_ID_82546EB_COPPER:
+ case E1000_DEV_ID_82546EB_FIBER:
+ case E1000_DEV_ID_82546EB_QUAD_COPPER:
+ hw->mac_type = e1000_82546;
+ break;
+ case E1000_DEV_ID_82546GB_COPPER:
+ case E1000_DEV_ID_82546GB_FIBER:
+ case E1000_DEV_ID_82546GB_SERDES:
+ case E1000_DEV_ID_82546GB_PCIE:
+ case E1000_DEV_ID_82546GB_QUAD_COPPER:
+ case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3:
+ hw->mac_type = e1000_82546_rev_3;
+ break;
+ case E1000_DEV_ID_82541EI:
+ case E1000_DEV_ID_82541EI_MOBILE:
+ case E1000_DEV_ID_82541ER_LOM:
+ hw->mac_type = e1000_82541;
+ break;
+ case E1000_DEV_ID_82541ER:
+ case E1000_DEV_ID_82541GI:
+ case E1000_DEV_ID_82541GI_LF:
+ case E1000_DEV_ID_82541GI_MOBILE:
+ hw->mac_type = e1000_82541_rev_2;
+ break;
+ case E1000_DEV_ID_82547EI:
+ case E1000_DEV_ID_82547EI_MOBILE:
+ hw->mac_type = e1000_82547;
+ break;
+ case E1000_DEV_ID_82547GI:
+ hw->mac_type = e1000_82547_rev_2;
+ break;
+ case E1000_DEV_ID_82571EB_COPPER:
+ case E1000_DEV_ID_82571EB_FIBER:
+ case E1000_DEV_ID_82571EB_SERDES:
+ case E1000_DEV_ID_82571EB_SERDES_DUAL:
+ case E1000_DEV_ID_82571EB_SERDES_QUAD:
+ case E1000_DEV_ID_82571EB_QUAD_COPPER:
+ case E1000_DEV_ID_82571EB_QUAD_FIBER:
+ case E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE:
+ hw->mac_type = e1000_82571;
+ break;
+ case E1000_DEV_ID_82572EI_COPPER:
+ case E1000_DEV_ID_82572EI_FIBER:
+ case E1000_DEV_ID_82572EI_SERDES:
+ case E1000_DEV_ID_82572EI:
+ hw->mac_type = e1000_82572;
+ break;
+ case E1000_DEV_ID_82573E:
+ case E1000_DEV_ID_82573E_IAMT:
+ case E1000_DEV_ID_82573L:
+ hw->mac_type = e1000_82573;
+ break;
+ case E1000_DEV_ID_80003ES2LAN_COPPER_SPT:
+ case E1000_DEV_ID_80003ES2LAN_SERDES_SPT:
+ case E1000_DEV_ID_80003ES2LAN_COPPER_DPT:
+ case E1000_DEV_ID_80003ES2LAN_SERDES_DPT:
+ hw->mac_type = e1000_80003es2lan;
+ break;
+ case E1000_DEV_ID_ICH8_IGP_M_AMT:
+ case E1000_DEV_ID_ICH8_IGP_AMT:
+ case E1000_DEV_ID_ICH8_IGP_C:
+ case E1000_DEV_ID_ICH8_IFE:
+ case E1000_DEV_ID_ICH8_IFE_GT:
+ case E1000_DEV_ID_ICH8_IFE_G:
+ case E1000_DEV_ID_ICH8_IGP_M:
+ hw->mac_type = e1000_ich8lan;
+ break;
+ default:
+ /* Should never have loaded on this device */
+ return -E1000_ERR_MAC_TYPE;
+ }
+
+ switch (hw->mac_type) {
+ case e1000_ich8lan:
+ hw->swfwhw_semaphore_present = TRUE;
+ hw->asf_firmware_present = TRUE;
+ break;
+ case e1000_80003es2lan:
+ hw->swfw_sync_present = TRUE;
+ /* fall through */
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_82573:
+ hw->eeprom_semaphore_present = TRUE;
+ /* fall through */
+ case e1000_82541:
+ case e1000_82547:
+ case e1000_82541_rev_2:
+ case e1000_82547_rev_2:
+ hw->asf_firmware_present = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ /* The 82543 chip does not count tx_carrier_errors properly in
+ * FD mode
+ */
+ if (hw->mac_type == e1000_82543)
+ hw->bad_tx_carr_stats_fd = TRUE;
+
+ /* capable of receiving management packets to the host */
+ if (hw->mac_type >= e1000_82571)
+ hw->has_manc2h = TRUE;
+
+ /* In rare occasions, ESB2 systems would end up started without
+ * the RX unit being turned on.
+ */
+ if (hw->mac_type == e1000_80003es2lan)
+ hw->rx_needs_kicking = TRUE;
+
+ if (hw->mac_type > e1000_82544)
+ hw->has_smbus = TRUE;
+
+ return E1000_SUCCESS;
+}
+
+/*****************************************************************************
+ * Set media type and TBI compatibility.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * **************************************************************************/
+void
+e1000_set_media_type(struct e1000_hw *hw)
+{
+ uint32_t status;
+
+ DEBUGFUNC("e1000_set_media_type");
+
+ if (hw->mac_type != e1000_82543) {
+ /* tbi_compatibility is only valid on 82543 */
+ hw->tbi_compatibility_en = FALSE;
+ }
+
+ switch (hw->device_id) {
+ case E1000_DEV_ID_82545GM_SERDES:
+ case E1000_DEV_ID_82546GB_SERDES:
+ case E1000_DEV_ID_82571EB_SERDES:
+ case E1000_DEV_ID_82571EB_SERDES_DUAL:
+ case E1000_DEV_ID_82571EB_SERDES_QUAD:
+ case E1000_DEV_ID_82572EI_SERDES:
+ case E1000_DEV_ID_80003ES2LAN_SERDES_DPT:
+ hw->media_type = e1000_media_type_internal_serdes;
+ break;
+ default:
+ switch (hw->mac_type) {
+ case e1000_82542_rev2_0:
+ case e1000_82542_rev2_1:
+ hw->media_type = e1000_media_type_fiber;
+ break;
+ case e1000_ich8lan:
+ case e1000_82573:
+ /* The STATUS_TBIMODE bit is reserved or reused for the this
+ * device.
+ */
+ hw->media_type = e1000_media_type_copper;
+ break;
+ default:
+ status = E1000_READ_REG(hw, STATUS);
+ if (status & E1000_STATUS_TBIMODE) {
+ hw->media_type = e1000_media_type_fiber;
+ /* tbi_compatibility not valid on fiber */
+ hw->tbi_compatibility_en = FALSE;
+ } else {
+ hw->media_type = e1000_media_type_copper;
+ }
+ break;
+ }
+ }
+}
+
+/******************************************************************************
+ * Reset the transmit and receive units; mask and clear all interrupts.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+int32_t
+e1000_reset_hw(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ uint32_t ctrl_ext;
+ uint32_t icr;
+ uint32_t manc;
+ uint32_t led_ctrl;
+ uint32_t timeout;
+ uint32_t extcnf_ctrl;
+ int32_t ret_val;
+
+ DEBUGFUNC("e1000_reset_hw");
+
+ /* For 82542 (rev 2.0), disable MWI before issuing a device reset */
+ if (hw->mac_type == e1000_82542_rev2_0) {
+ DEBUGOUT("Disabling MWI on 82542 rev 2.0\n");
+ e1000_pci_clear_mwi(hw);
+ }
+
+ if (hw->bus_type == e1000_bus_type_pci_express) {
+ /* Prevent the PCI-E bus from sticking if there is no TLP connection
+ * on the last TLP read/write transaction when MAC is reset.
+ */
+ if (e1000_disable_pciex_master(hw) != E1000_SUCCESS) {
+ DEBUGOUT("PCI-E Master disable polling has failed.\n");
+ }
+ }
+
+ /* Clear interrupt mask to stop board from generating interrupts */
+ DEBUGOUT("Masking off all interrupts\n");
+ E1000_WRITE_REG(hw, IMC, 0xffffffff);
+
+ /* Disable the Transmit and Receive units. Then delay to allow
+ * any pending transactions to complete before we hit the MAC with
+ * the global reset.
+ */
+ E1000_WRITE_REG(hw, RCTL, 0);
+ E1000_WRITE_REG(hw, TCTL, E1000_TCTL_PSP);
+ E1000_WRITE_FLUSH(hw);
+
+ /* The tbi_compatibility_on Flag must be cleared when Rctl is cleared. */
+ hw->tbi_compatibility_on = FALSE;
+
+ /* Delay to allow any outstanding PCI transactions to complete before
+ * resetting the device
+ */
+ msleep(10);
+
+ ctrl = E1000_READ_REG(hw, CTRL);
+
+ /* Must reset the PHY before resetting the MAC */
+ if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+ E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_PHY_RST));
+ msleep(5);
+ }
+
+ /* Must acquire the MDIO ownership before MAC reset.
+ * Ownership defaults to firmware after a reset. */
+ if (hw->mac_type == e1000_82573) {
+ timeout = 10;
+
+ extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL);
+ extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP;
+
+ do {
+ E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl);
+ extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL);
+
+ if (extcnf_ctrl & E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP)
+ break;
+ else
+ extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP;
+
+ msleep(2);
+ timeout--;
+ } while (timeout);
+ }
+
+ /* Workaround for ICH8 bit corruption issue in FIFO memory */
+ if (hw->mac_type == e1000_ich8lan) {
+ /* Set Tx and Rx buffer allocation to 8k apiece. */
+ E1000_WRITE_REG(hw, PBA, E1000_PBA_8K);
+ /* Set Packet Buffer Size to 16k. */
+ E1000_WRITE_REG(hw, PBS, E1000_PBS_16K);
+ }
+
+ /* Issue a global reset to the MAC. This will reset the chip's
+ * transmit, receive, DMA, and link units. It will not effect
+ * the current PCI configuration. The global reset bit is self-
+ * clearing, and should clear within a microsecond.
+ */
+ DEBUGOUT("Issuing a global reset to MAC\n");
+
+ switch (hw->mac_type) {
+ case e1000_82544:
+ case e1000_82540:
+ case e1000_82545:
+ case e1000_82546:
+ case e1000_82541:
+ case e1000_82541_rev_2:
+ /* These controllers can't ack the 64-bit write when issuing the
+ * reset, so use IO-mapping as a workaround to issue the reset */
+ E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_RST));
+ break;
+ case e1000_82545_rev_3:
+ case e1000_82546_rev_3:
+ /* Reset is performed on a shadow of the control register */
+ E1000_WRITE_REG(hw, CTRL_DUP, (ctrl | E1000_CTRL_RST));
+ break;
+ case e1000_ich8lan:
+ if (!hw->phy_reset_disable &&
+ e1000_check_phy_reset_block(hw) == E1000_SUCCESS) {
+ /* e1000_ich8lan PHY HW reset requires MAC CORE reset
+ * at the same time to make sure the interface between
+ * MAC and the external PHY is reset.
+ */
+ ctrl |= E1000_CTRL_PHY_RST;
+ }
+
+ e1000_get_software_flag(hw);
+ E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST));
+ msleep(5);
+ break;
+ default:
+ E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST));
+ break;
+ }
+
+ /* After MAC reset, force reload of EEPROM to restore power-on settings to
+ * device. Later controllers reload the EEPROM automatically, so just wait
+ * for reload to complete.
+ */
+ switch (hw->mac_type) {
+ case e1000_82542_rev2_0:
+ case e1000_82542_rev2_1:
+ case e1000_82543:
+ case e1000_82544:
+ /* Wait for reset to complete */
+ udelay(10);
+ ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ ctrl_ext |= E1000_CTRL_EXT_EE_RST;
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ E1000_WRITE_FLUSH(hw);
+ /* Wait for EEPROM reload */
+ msleep(2);
+ break;
+ case e1000_82541:
+ case e1000_82541_rev_2:
+ case e1000_82547:
+ case e1000_82547_rev_2:
+ /* Wait for EEPROM reload */
+ msleep(20);
+ break;
+ case e1000_82573:
+ if (e1000_is_onboard_nvm_eeprom(hw) == FALSE) {
+ udelay(10);
+ ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ ctrl_ext |= E1000_CTRL_EXT_EE_RST;
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ E1000_WRITE_FLUSH(hw);
+ }
+ /* fall through */
+ default:
+ /* Auto read done will delay 5ms or poll based on mac type */
+ ret_val = e1000_get_auto_rd_done(hw);
+ if (ret_val)
+ return ret_val;
+ break;
+ }
+
+ /* Disable HW ARPs on ASF enabled adapters */
+ if (hw->mac_type >= e1000_82540 && hw->mac_type <= e1000_82547_rev_2) {
+ manc = E1000_READ_REG(hw, MANC);
+ manc &= ~(E1000_MANC_ARP_EN);
+ E1000_WRITE_REG(hw, MANC, manc);
+ }
+
+ if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+ e1000_phy_init_script(hw);
+
+ /* Configure activity LED after PHY reset */
+ led_ctrl = E1000_READ_REG(hw, LEDCTL);
+ led_ctrl &= IGP_ACTIVITY_LED_MASK;
+ led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
+ E1000_WRITE_REG(hw, LEDCTL, led_ctrl);
+ }
+
+ /* Clear interrupt mask to stop board from generating interrupts */
+ DEBUGOUT("Masking off all interrupts\n");
+ E1000_WRITE_REG(hw, IMC, 0xffffffff);
+
+ /* Clear any pending interrupt events. */
+ icr = E1000_READ_REG(hw, ICR);
+
+ /* If MWI was previously enabled, reenable it. */
+ if (hw->mac_type == e1000_82542_rev2_0) {
+ if (hw->pci_cmd_word & PCI_COMMAND_INVALIDATE)
+ e1000_pci_set_mwi(hw);
+ }
+
+ if (hw->mac_type == e1000_ich8lan) {
+ uint32_t kab = E1000_READ_REG(hw, KABGTXD);
+ kab |= E1000_KABGTXD_BGSQLBIAS;
+ E1000_WRITE_REG(hw, KABGTXD, kab);
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ *
+ * Initialize a number of hardware-dependent bits
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * This function contains hardware limitation workarounds for PCI-E adapters
+ *
+ *****************************************************************************/
+static void
+e1000_initialize_hardware_bits(struct e1000_hw *hw)
+{
+ if ((hw->mac_type >= e1000_82571) && (!hw->initialize_hw_bits_disable)) {
+ /* Settings common to all PCI-express silicon */
+ uint32_t reg_ctrl, reg_ctrl_ext;
+ uint32_t reg_tarc0, reg_tarc1;
+ uint32_t reg_tctl;
+ uint32_t reg_txdctl, reg_txdctl1;
+
+ /* link autonegotiation/sync workarounds */
+ reg_tarc0 = E1000_READ_REG(hw, TARC0);
+ reg_tarc0 &= ~((1 << 30)|(1 << 29)|(1 << 28)|(1 << 27));
+
+ /* Enable not-done TX descriptor counting */
+ reg_txdctl = E1000_READ_REG(hw, TXDCTL);
+ reg_txdctl |= E1000_TXDCTL_COUNT_DESC;
+ E1000_WRITE_REG(hw, TXDCTL, reg_txdctl);
+ reg_txdctl1 = E1000_READ_REG(hw, TXDCTL1);
+ reg_txdctl1 |= E1000_TXDCTL_COUNT_DESC;
+ E1000_WRITE_REG(hw, TXDCTL1, reg_txdctl1);
+
+ switch (hw->mac_type) {
+ case e1000_82571:
+ case e1000_82572:
+ /* Clear PHY TX compatible mode bits */
+ reg_tarc1 = E1000_READ_REG(hw, TARC1);
+ reg_tarc1 &= ~((1 << 30)|(1 << 29));
+
+ /* link autonegotiation/sync workarounds */
+ reg_tarc0 |= ((1 << 26)|(1 << 25)|(1 << 24)|(1 << 23));
+
+ /* TX ring control fixes */
+ reg_tarc1 |= ((1 << 26)|(1 << 25)|(1 << 24));
+
+ /* Multiple read bit is reversed polarity */
+ reg_tctl = E1000_READ_REG(hw, TCTL);
+ if (reg_tctl & E1000_TCTL_MULR)
+ reg_tarc1 &= ~(1 << 28);
+ else
+ reg_tarc1 |= (1 << 28);
+
+ E1000_WRITE_REG(hw, TARC1, reg_tarc1);
+ break;
+ case e1000_82573:
+ reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ reg_ctrl_ext &= ~(1 << 23);
+ reg_ctrl_ext |= (1 << 22);
+
+ /* TX byte count fix */
+ reg_ctrl = E1000_READ_REG(hw, CTRL);
+ reg_ctrl &= ~(1 << 29);
+
+ E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext);
+ E1000_WRITE_REG(hw, CTRL, reg_ctrl);
+ break;
+ case e1000_80003es2lan:
+ /* improve small packet performace for fiber/serdes */
+ if ((hw->media_type == e1000_media_type_fiber) ||
+ (hw->media_type == e1000_media_type_internal_serdes)) {
+ reg_tarc0 &= ~(1 << 20);
+ }
+
+ /* Multiple read bit is reversed polarity */
+ reg_tctl = E1000_READ_REG(hw, TCTL);
+ reg_tarc1 = E1000_READ_REG(hw, TARC1);
+ if (reg_tctl & E1000_TCTL_MULR)
+ reg_tarc1 &= ~(1 << 28);
+ else
+ reg_tarc1 |= (1 << 28);
+
+ E1000_WRITE_REG(hw, TARC1, reg_tarc1);
+ break;
+ case e1000_ich8lan:
+ /* Reduce concurrent DMA requests to 3 from 4 */
+ if ((hw->revision_id < 3) ||
+ ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) &&
+ (hw->device_id != E1000_DEV_ID_ICH8_IGP_M)))
+ reg_tarc0 |= ((1 << 29)|(1 << 28));
+
+ reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ reg_ctrl_ext |= (1 << 22);
+ E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext);
+
+ /* workaround TX hang with TSO=on */
+ reg_tarc0 |= ((1 << 27)|(1 << 26)|(1 << 24)|(1 << 23));
+
+ /* Multiple read bit is reversed polarity */
+ reg_tctl = E1000_READ_REG(hw, TCTL);
+ reg_tarc1 = E1000_READ_REG(hw, TARC1);
+ if (reg_tctl & E1000_TCTL_MULR)
+ reg_tarc1 &= ~(1 << 28);
+ else
+ reg_tarc1 |= (1 << 28);
+
+ /* workaround TX hang with TSO=on */
+ reg_tarc1 |= ((1 << 30)|(1 << 26)|(1 << 24));
+
+ E1000_WRITE_REG(hw, TARC1, reg_tarc1);
+ break;
+ default:
+ break;
+ }
+
+ E1000_WRITE_REG(hw, TARC0, reg_tarc0);
+ }
+}
+
+/******************************************************************************
+ * Performs basic configuration of the adapter.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Assumes that the controller has previously been reset and is in a
+ * post-reset uninitialized state. Initializes the receive address registers,
+ * multicast table, and VLAN filter table. Calls routines to setup link
+ * configuration and flow control settings. Clears all on-chip counters. Leaves
+ * the transmit and receive units disabled and uninitialized.
+ *****************************************************************************/
+int32_t
+e1000_init_hw(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ uint32_t i;
+ int32_t ret_val;
+ uint16_t pcix_cmd_word;
+ uint16_t pcix_stat_hi_word;
+ uint16_t cmd_mmrbc;
+ uint16_t stat_mmrbc;
+ uint32_t mta_size;
+ uint32_t reg_data;
+ uint32_t ctrl_ext;
+
+ DEBUGFUNC("e1000_init_hw");
+
+ /* force full DMA clock frequency for 10/100 on ICH8 A0-B0 */
+ if ((hw->mac_type == e1000_ich8lan) &&
+ ((hw->revision_id < 3) ||
+ ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) &&
+ (hw->device_id != E1000_DEV_ID_ICH8_IGP_M)))) {
+ reg_data = E1000_READ_REG(hw, STATUS);
+ reg_data &= ~0x80000000;
+ E1000_WRITE_REG(hw, STATUS, reg_data);
+ }
+
+ /* Initialize Identification LED */
+ ret_val = e1000_id_led_init(hw);
+ if (ret_val) {
+ DEBUGOUT("Error Initializing Identification LED\n");
+ return ret_val;
+ }
+
+ /* Set the media type and TBI compatibility */
+ e1000_set_media_type(hw);
+
+ /* Must be called after e1000_set_media_type because media_type is used */
+ e1000_initialize_hardware_bits(hw);
+
+ /* Disabling VLAN filtering. */
+ DEBUGOUT("Initializing the IEEE VLAN\n");
+ /* VET hardcoded to standard value and VFTA removed in ICH8 LAN */
+ if (hw->mac_type != e1000_ich8lan) {
+ if (hw->mac_type < e1000_82545_rev_3)
+ E1000_WRITE_REG(hw, VET, 0);
+ e1000_clear_vfta(hw);
+ }
+
+ /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */
+ if (hw->mac_type == e1000_82542_rev2_0) {
+ DEBUGOUT("Disabling MWI on 82542 rev 2.0\n");
+ e1000_pci_clear_mwi(hw);
+ E1000_WRITE_REG(hw, RCTL, E1000_RCTL_RST);
+ E1000_WRITE_FLUSH(hw);
+ msleep(5);
+ }
+
+ /* Setup the receive address. This involves initializing all of the Receive
+ * Address Registers (RARs 0 - 15).
+ */
+ e1000_init_rx_addrs(hw);
+
+ /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */
+ if (hw->mac_type == e1000_82542_rev2_0) {
+ E1000_WRITE_REG(hw, RCTL, 0);
+ E1000_WRITE_FLUSH(hw);
+ msleep(1);
+ if (hw->pci_cmd_word & PCI_COMMAND_INVALIDATE)
+ e1000_pci_set_mwi(hw);
+ }
+
+ /* Zero out the Multicast HASH table */
+ DEBUGOUT("Zeroing the MTA\n");
+ mta_size = E1000_MC_TBL_SIZE;
+ if (hw->mac_type == e1000_ich8lan)
+ mta_size = E1000_MC_TBL_SIZE_ICH8LAN;
+ for (i = 0; i < mta_size; i++) {
+ E1000_WRITE_REG_ARRAY(hw, MTA, i, 0);
+ /* use write flush to prevent Memory Write Block (MWB) from
+ * occuring when accessing our register space */
+ E1000_WRITE_FLUSH(hw);
+ }
+
+ /* Set the PCI priority bit correctly in the CTRL register. This
+ * determines if the adapter gives priority to receives, or if it
+ * gives equal priority to transmits and receives. Valid only on
+ * 82542 and 82543 silicon.
+ */
+ if (hw->dma_fairness && hw->mac_type <= e1000_82543) {
+ ctrl = E1000_READ_REG(hw, CTRL);
+ E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PRIOR);
+ }
+
+ switch (hw->mac_type) {
+ case e1000_82545_rev_3:
+ case e1000_82546_rev_3:
+ break;
+ default:
+ /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */
+ if (hw->bus_type == e1000_bus_type_pcix) {
+ e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
+ e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI,
+ &pcix_stat_hi_word);
+ cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >>
+ PCIX_COMMAND_MMRBC_SHIFT;
+ stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >>
+ PCIX_STATUS_HI_MMRBC_SHIFT;
+ if (stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K)
+ stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K;
+ if (cmd_mmrbc > stat_mmrbc) {
+ pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK;
+ pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT;
+ e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER,
+ &pcix_cmd_word);
+ }
+ }
+ break;
+ }
+
+ /* More time needed for PHY to initialize */
+ if (hw->mac_type == e1000_ich8lan)
+ msleep(15);
+
+ /* Call a subroutine to configure the link and setup flow control. */
+ ret_val = e1000_setup_link(hw);
+
+ /* Set the transmit descriptor write-back policy */
+ if (hw->mac_type > e1000_82544) {
+ ctrl = E1000_READ_REG(hw, TXDCTL);
+ ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB;
+ E1000_WRITE_REG(hw, TXDCTL, ctrl);
+ }
+
+ if (hw->mac_type == e1000_82573) {
+ e1000_enable_tx_pkt_filtering(hw);
+ }
+
+ switch (hw->mac_type) {
+ default:
+ break;
+ case e1000_80003es2lan:
+ /* Enable retransmit on late collisions */
+ reg_data = E1000_READ_REG(hw, TCTL);
+ reg_data |= E1000_TCTL_RTLC;
+ E1000_WRITE_REG(hw, TCTL, reg_data);
+
+ /* Configure Gigabit Carry Extend Padding */
+ reg_data = E1000_READ_REG(hw, TCTL_EXT);
+ reg_data &= ~E1000_TCTL_EXT_GCEX_MASK;
+ reg_data |= DEFAULT_80003ES2LAN_TCTL_EXT_GCEX;
+ E1000_WRITE_REG(hw, TCTL_EXT, reg_data);
+
+ /* Configure Transmit Inter-Packet Gap */
+ reg_data = E1000_READ_REG(hw, TIPG);
+ reg_data &= ~E1000_TIPG_IPGT_MASK;
+ reg_data |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000;
+ E1000_WRITE_REG(hw, TIPG, reg_data);
+
+ reg_data = E1000_READ_REG_ARRAY(hw, FFLT, 0x0001);
+ reg_data &= ~0x00100000;
+ E1000_WRITE_REG_ARRAY(hw, FFLT, 0x0001, reg_data);
+ /* Fall through */
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_ich8lan:
+ ctrl = E1000_READ_REG(hw, TXDCTL1);
+ ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB;
+ E1000_WRITE_REG(hw, TXDCTL1, ctrl);
+ break;
+ }
+
+
+ if (hw->mac_type == e1000_82573) {
+ uint32_t gcr = E1000_READ_REG(hw, GCR);
+ gcr |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX;
+ E1000_WRITE_REG(hw, GCR, gcr);
+ }
+
+ /* Clear all of the statistics registers (clear on read). It is
+ * important that we do this after we have tried to establish link
+ * because the symbol error count will increment wildly if there
+ * is no link.
+ */
+ e1000_clear_hw_cntrs(hw);
+
+ /* ICH8 No-snoop bits are opposite polarity.
+ * Set to snoop by default after reset. */
+ if (hw->mac_type == e1000_ich8lan)
+ e1000_set_pci_ex_no_snoop(hw, PCI_EX_82566_SNOOP_ALL);
+
+ if (hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER ||
+ hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3) {
+ ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ /* Relaxed ordering must be disabled to avoid a parity
+ * error crash in a PCI slot. */
+ ctrl_ext |= E1000_CTRL_EXT_RO_DIS;
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ }
+
+ return ret_val;
+}
+
+/******************************************************************************
+ * Adjust SERDES output amplitude based on EEPROM setting.
+ *
+ * hw - Struct containing variables accessed by shared code.
+ *****************************************************************************/
+static int32_t
+e1000_adjust_serdes_amplitude(struct e1000_hw *hw)
+{
+ uint16_t eeprom_data;
+ int32_t ret_val;
+
+ DEBUGFUNC("e1000_adjust_serdes_amplitude");
+
+ if (hw->media_type != e1000_media_type_internal_serdes)
+ return E1000_SUCCESS;
+
+ switch (hw->mac_type) {
+ case e1000_82545_rev_3:
+ case e1000_82546_rev_3:
+ break;
+ default:
+ return E1000_SUCCESS;
+ }
+
+ ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1, &eeprom_data);
+ if (ret_val) {
+ return ret_val;
+ }
+
+ if (eeprom_data != EEPROM_RESERVED_WORD) {
+ /* Adjust SERDES output amplitude only. */
+ eeprom_data &= EEPROM_SERDES_AMPLITUDE_MASK;
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_EXT_CTRL, eeprom_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Configures flow control and link settings.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Determines which flow control settings to use. Calls the apropriate media-
+ * specific link configuration function. Configures the flow control settings.
+ * Assuming the adapter has a valid link partner, a valid link should be
+ * established. Assumes the hardware has previously been reset and the
+ * transmitter and receiver are not enabled.
+ *****************************************************************************/
+int32_t
+e1000_setup_link(struct e1000_hw *hw)
+{
+ uint32_t ctrl_ext;
+ int32_t ret_val;
+ uint16_t eeprom_data;
+
+ DEBUGFUNC("e1000_setup_link");
+
+ /* In the case of the phy reset being blocked, we already have a link.
+ * We do not have to set it up again. */
+ if (e1000_check_phy_reset_block(hw))
+ return E1000_SUCCESS;
+
+ /* Read and store word 0x0F of the EEPROM. This word contains bits
+ * that determine the hardware's default PAUSE (flow control) mode,
+ * a bit that determines whether the HW defaults to enabling or
+ * disabling auto-negotiation, and the direction of the
+ * SW defined pins. If there is no SW over-ride of the flow
+ * control setting, then the variable hw->fc will
+ * be initialized based on a value in the EEPROM.
+ */
+ if (hw->fc == E1000_FC_DEFAULT) {
+ switch (hw->mac_type) {
+ case e1000_ich8lan:
+ case e1000_82573:
+ hw->fc = E1000_FC_FULL;
+ break;
+ default:
+ ret_val = e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG,
+ 1, &eeprom_data);
+ if (ret_val) {
+ DEBUGOUT("EEPROM Read Error\n");
+ return -E1000_ERR_EEPROM;
+ }
+ if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == 0)
+ hw->fc = E1000_FC_NONE;
+ else if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) ==
+ EEPROM_WORD0F_ASM_DIR)
+ hw->fc = E1000_FC_TX_PAUSE;
+ else
+ hw->fc = E1000_FC_FULL;
+ break;
+ }
+ }
+
+ /* We want to save off the original Flow Control configuration just
+ * in case we get disconnected and then reconnected into a different
+ * hub or switch with different Flow Control capabilities.
+ */
+ if (hw->mac_type == e1000_82542_rev2_0)
+ hw->fc &= (~E1000_FC_TX_PAUSE);
+
+ if ((hw->mac_type < e1000_82543) && (hw->report_tx_early == 1))
+ hw->fc &= (~E1000_FC_RX_PAUSE);
+
+ hw->original_fc = hw->fc;
+
+ DEBUGOUT1("After fix-ups FlowControl is now = %x\n", hw->fc);
+
+ /* Take the 4 bits from EEPROM word 0x0F that determine the initial
+ * polarity value for the SW controlled pins, and setup the
+ * Extended Device Control reg with that info.
+ * This is needed because one of the SW controlled pins is used for
+ * signal detection. So this should be done before e1000_setup_pcs_link()
+ * or e1000_phy_setup() is called.
+ */
+ if (hw->mac_type == e1000_82543) {
+ ret_val = e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG,
+ 1, &eeprom_data);
+ if (ret_val) {
+ DEBUGOUT("EEPROM Read Error\n");
+ return -E1000_ERR_EEPROM;
+ }
+ ctrl_ext = ((eeprom_data & EEPROM_WORD0F_SWPDIO_EXT) <<
+ SWDPIO__EXT_SHIFT);
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ }
+
+ /* Call the necessary subroutine to configure the link. */
+ ret_val = (hw->media_type == e1000_media_type_copper) ?
+ e1000_setup_copper_link(hw) :
+ e1000_setup_fiber_serdes_link(hw);
+
+ /* Initialize the flow control address, type, and PAUSE timer
+ * registers to their default values. This is done even if flow
+ * control is disabled, because it does not hurt anything to
+ * initialize these registers.
+ */
+ DEBUGOUT("Initializing the Flow Control address, type and timer regs\n");
+
+ /* FCAL/H and FCT are hardcoded to standard values in e1000_ich8lan. */
+ if (hw->mac_type != e1000_ich8lan) {
+ E1000_WRITE_REG(hw, FCT, FLOW_CONTROL_TYPE);
+ E1000_WRITE_REG(hw, FCAH, FLOW_CONTROL_ADDRESS_HIGH);
+ E1000_WRITE_REG(hw, FCAL, FLOW_CONTROL_ADDRESS_LOW);
+ }
+
+ E1000_WRITE_REG(hw, FCTTV, hw->fc_pause_time);
+
+ /* Set the flow control receive threshold registers. Normally,
+ * these registers will be set to a default threshold that may be
+ * adjusted later by the driver's runtime code. However, if the
+ * ability to transmit pause frames in not enabled, then these
+ * registers will be set to 0.
+ */
+ if (!(hw->fc & E1000_FC_TX_PAUSE)) {
+ E1000_WRITE_REG(hw, FCRTL, 0);
+ E1000_WRITE_REG(hw, FCRTH, 0);
+ } else {
+ /* We need to set up the Receive Threshold high and low water marks
+ * as well as (optionally) enabling the transmission of XON frames.
+ */
+ if (hw->fc_send_xon) {
+ E1000_WRITE_REG(hw, FCRTL, (hw->fc_low_water | E1000_FCRTL_XONE));
+ E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water);
+ } else {
+ E1000_WRITE_REG(hw, FCRTL, hw->fc_low_water);
+ E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water);
+ }
+ }
+ return ret_val;
+}
+
+/******************************************************************************
+ * Sets up link for a fiber based or serdes based adapter
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Manipulates Physical Coding Sublayer functions in order to configure
+ * link. Assumes the hardware has been previously reset and the transmitter
+ * and receiver are not enabled.
+ *****************************************************************************/
+static int32_t
+e1000_setup_fiber_serdes_link(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ uint32_t status;
+ uint32_t txcw = 0;
+ uint32_t i;
+ uint32_t signal = 0;
+ int32_t ret_val;
+
+ DEBUGFUNC("e1000_setup_fiber_serdes_link");
+
+ /* On 82571 and 82572 Fiber connections, SerDes loopback mode persists
+ * until explicitly turned off or a power cycle is performed. A read to
+ * the register does not indicate its status. Therefore, we ensure
+ * loopback mode is disabled during initialization.
+ */
+ if (hw->mac_type == e1000_82571 || hw->mac_type == e1000_82572)
+ E1000_WRITE_REG(hw, SCTL, E1000_DISABLE_SERDES_LOOPBACK);
+
+ /* On adapters with a MAC newer than 82544, SWDP 1 will be
+ * set when the optics detect a signal. On older adapters, it will be
+ * cleared when there is a signal. This applies to fiber media only.
+ * If we're on serdes media, adjust the output amplitude to value
+ * set in the EEPROM.
+ */
+ ctrl = E1000_READ_REG(hw, CTRL);
+ if (hw->media_type == e1000_media_type_fiber)
+ signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0;
+
+ ret_val = e1000_adjust_serdes_amplitude(hw);
+ if (ret_val)
+ return ret_val;
+
+ /* Take the link out of reset */
+ ctrl &= ~(E1000_CTRL_LRST);
+
+ /* Adjust VCO speed to improve BER performance */
+ ret_val = e1000_set_vco_speed(hw);
+ if (ret_val)
+ return ret_val;
+
+ e1000_config_collision_dist(hw);
+
+ /* Check for a software override of the flow control settings, and setup
+ * the device accordingly. If auto-negotiation is enabled, then software
+ * will have to set the "PAUSE" bits to the correct value in the Tranmsit
+ * Config Word Register (TXCW) and re-start auto-negotiation. However, if
+ * auto-negotiation is disabled, then software will have to manually
+ * configure the two flow control enable bits in the CTRL register.
+ *
+ * The possible values of the "fc" parameter are:
+ * 0: Flow control is completely disabled
+ * 1: Rx flow control is enabled (we can receive pause frames, but
+ * not send pause frames).
+ * 2: Tx flow control is enabled (we can send pause frames but we do
+ * not support receiving pause frames).
+ * 3: Both Rx and TX flow control (symmetric) are enabled.
+ */
+ switch (hw->fc) {
+ case E1000_FC_NONE:
+ /* Flow control is completely disabled by a software over-ride. */
+ txcw = (E1000_TXCW_ANE | E1000_TXCW_FD);
+ break;
+ case E1000_FC_RX_PAUSE:
+ /* RX Flow control is enabled and TX Flow control is disabled by a
+ * software over-ride. Since there really isn't a way to advertise
+ * that we are capable of RX Pause ONLY, we will advertise that we
+ * support both symmetric and asymmetric RX PAUSE. Later, we will
+ * disable the adapter's ability to send PAUSE frames.
+ */
+ txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK);
+ break;
+ case E1000_FC_TX_PAUSE:
+ /* TX Flow control is enabled, and RX Flow control is disabled, by a
+ * software over-ride.
+ */
+ txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR);
+ break;
+ case E1000_FC_FULL:
+ /* Flow control (both RX and TX) is enabled by a software over-ride. */
+ txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK);
+ break;
+ default:
+ DEBUGOUT("Flow control param set incorrectly\n");
+ return -E1000_ERR_CONFIG;
+ break;
+ }
+
+ /* Since auto-negotiation is enabled, take the link out of reset (the link
+ * will be in reset, because we previously reset the chip). This will
+ * restart auto-negotiation. If auto-neogtiation is successful then the
+ * link-up status bit will be set and the flow control enable bits (RFCE
+ * and TFCE) will be set according to their negotiated value.
+ */
+ DEBUGOUT("Auto-negotiation enabled\n");
+
+ E1000_WRITE_REG(hw, TXCW, txcw);
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ E1000_WRITE_FLUSH(hw);
+
+ hw->txcw = txcw;
+ msleep(1);
+
+ /* If we have a signal (the cable is plugged in) then poll for a "Link-Up"
+ * indication in the Device Status Register. Time-out if a link isn't
+ * seen in 500 milliseconds seconds (Auto-negotiation should complete in
+ * less than 500 milliseconds even if the other end is doing it in SW).
+ * For internal serdes, we just assume a signal is present, then poll.
+ */
+ if (hw->media_type == e1000_media_type_internal_serdes ||
+ (E1000_READ_REG(hw, CTRL) & E1000_CTRL_SWDPIN1) == signal) {
+ DEBUGOUT("Looking for Link\n");
+ for (i = 0; i < (LINK_UP_TIMEOUT / 10); i++) {
+ msleep(10);
+ status = E1000_READ_REG(hw, STATUS);
+ if (status & E1000_STATUS_LU) break;
+ }
+ if (i == (LINK_UP_TIMEOUT / 10)) {
+ DEBUGOUT("Never got a valid link from auto-neg!!!\n");
+ hw->autoneg_failed = 1;
+ /* AutoNeg failed to achieve a link, so we'll call
+ * e1000_check_for_link. This routine will force the link up if
+ * we detect a signal. This will allow us to communicate with
+ * non-autonegotiating link partners.
+ */
+ ret_val = e1000_check_for_link(hw);
+ if (ret_val) {
+ DEBUGOUT("Error while checking for link\n");
+ return ret_val;
+ }
+ hw->autoneg_failed = 0;
+ } else {
+ hw->autoneg_failed = 0;
+ DEBUGOUT("Valid Link Found\n");
+ }
+ } else {
+ DEBUGOUT("No Signal Detected\n");
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Make sure we have a valid PHY and change PHY mode before link setup.
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int32_t
+e1000_copper_link_preconfig(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_copper_link_preconfig");
+
+ ctrl = E1000_READ_REG(hw, CTRL);
+ /* With 82543, we need to force speed and duplex on the MAC equal to what
+ * the PHY speed and duplex configuration is. In addition, we need to
+ * perform a hardware reset on the PHY to take it out of reset.
+ */
+ if (hw->mac_type > e1000_82543) {
+ ctrl |= E1000_CTRL_SLU;
+ ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ } else {
+ ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX | E1000_CTRL_SLU);
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ ret_val = e1000_phy_hw_reset(hw);
+ if (ret_val)
+ return ret_val;
+ }
+
+ /* Make sure we have a valid PHY */
+ ret_val = e1000_detect_gig_phy(hw);
+ if (ret_val) {
+ DEBUGOUT("Error, did not detect valid phy.\n");
+ return ret_val;
+ }
+ DEBUGOUT1("Phy ID = %#08lx \n", hw->phy_id);
+
+ /* Set PHY to class A mode (if necessary) */
+ ret_val = e1000_set_phy_mode(hw);
+ if (ret_val)
+ return ret_val;
+
+ if ((hw->mac_type == e1000_82545_rev_3) ||
+ (hw->mac_type == e1000_82546_rev_3)) {
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
+ phy_data |= 0x00000008;
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
+ }
+
+ if (hw->mac_type <= e1000_82543 ||
+ hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 ||
+ hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2)
+ hw->phy_reset_disable = FALSE;
+
+ return E1000_SUCCESS;
+}
+
+
+/********************************************************************
+* Copper link setup for e1000_phy_igp series.
+*
+* hw - Struct containing variables accessed by shared code
+*********************************************************************/
+static int32_t
+e1000_copper_link_igp_setup(struct e1000_hw *hw)
+{
+ uint32_t led_ctrl;
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_copper_link_igp_setup");
+
+ if (hw->phy_reset_disable)
+ return E1000_SUCCESS;
+
+ ret_val = e1000_phy_reset(hw);
+ if (ret_val) {
+ DEBUGOUT("Error Resetting the PHY\n");
+ return ret_val;
+ }
+
+ /* Wait 15ms for MAC to configure PHY from eeprom settings */
+ msleep(15);
+ if (hw->mac_type != e1000_ich8lan) {
+ /* Configure activity LED after PHY reset */
+ led_ctrl = E1000_READ_REG(hw, LEDCTL);
+ led_ctrl &= IGP_ACTIVITY_LED_MASK;
+ led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
+ E1000_WRITE_REG(hw, LEDCTL, led_ctrl);
+ }
+
+ /* The NVM settings will configure LPLU in D3 for IGP2 and IGP3 PHYs */
+ if (hw->phy_type == e1000_phy_igp) {
+ /* disable lplu d3 during driver init */
+ ret_val = e1000_set_d3_lplu_state(hw, FALSE);
+ if (ret_val) {
+ DEBUGOUT("Error Disabling LPLU D3\n");
+ return ret_val;
+ }
+ }
+
+ /* disable lplu d0 during driver init */
+ ret_val = e1000_set_d0_lplu_state(hw, FALSE);
+ if (ret_val) {
+ DEBUGOUT("Error Disabling LPLU D0\n");
+ return ret_val;
+ }
+ /* Configure mdi-mdix settings */
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+ hw->dsp_config_state = e1000_dsp_config_disabled;
+ /* Force MDI for earlier revs of the IGP PHY */
+ phy_data &= ~(IGP01E1000_PSCR_AUTO_MDIX | IGP01E1000_PSCR_FORCE_MDI_MDIX);
+ hw->mdix = 1;
+
+ } else {
+ hw->dsp_config_state = e1000_dsp_config_enabled;
+ phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX;
+
+ switch (hw->mdix) {
+ case 1:
+ phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX;
+ break;
+ case 2:
+ phy_data |= IGP01E1000_PSCR_FORCE_MDI_MDIX;
+ break;
+ case 0:
+ default:
+ phy_data |= IGP01E1000_PSCR_AUTO_MDIX;
+ break;
+ }
+ }
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* set auto-master slave resolution settings */
+ if (hw->autoneg) {
+ e1000_ms_type phy_ms_setting = hw->master_slave;
+
+ if (hw->ffe_config_state == e1000_ffe_config_active)
+ hw->ffe_config_state = e1000_ffe_config_enabled;
+
+ if (hw->dsp_config_state == e1000_dsp_config_activated)
+ hw->dsp_config_state = e1000_dsp_config_enabled;
+
+ /* when autonegotiation advertisment is only 1000Mbps then we
+ * should disable SmartSpeed and enable Auto MasterSlave
+ * resolution as hardware default. */
+ if (hw->autoneg_advertised == ADVERTISE_1000_FULL) {
+ /* Disable SmartSpeed */
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+ phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+ phy_data);
+ if (ret_val)
+ return ret_val;
+ /* Set auto Master/Slave resolution process */
+ ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+ phy_data &= ~CR_1000T_MS_ENABLE;
+ ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+ ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* load defaults for future use */
+ hw->original_master_slave = (phy_data & CR_1000T_MS_ENABLE) ?
+ ((phy_data & CR_1000T_MS_VALUE) ?
+ e1000_ms_force_master :
+ e1000_ms_force_slave) :
+ e1000_ms_auto;
+
+ switch (phy_ms_setting) {
+ case e1000_ms_force_master:
+ phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE);
+ break;
+ case e1000_ms_force_slave:
+ phy_data |= CR_1000T_MS_ENABLE;
+ phy_data &= ~(CR_1000T_MS_VALUE);
+ break;
+ case e1000_ms_auto:
+ phy_data &= ~CR_1000T_MS_ENABLE;
+ default:
+ break;
+ }
+ ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/********************************************************************
+* Copper link setup for e1000_phy_gg82563 series.
+*
+* hw - Struct containing variables accessed by shared code
+*********************************************************************/
+static int32_t
+e1000_copper_link_ggp_setup(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t phy_data;
+ uint32_t reg_data;
+
+ DEBUGFUNC("e1000_copper_link_ggp_setup");
+
+ if (!hw->phy_reset_disable) {
+
+ /* Enable CRS on TX for half-duplex operation. */
+ ret_val = e1000_read_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
+ /* Use 25MHz for both link down and 1000BASE-T for Tx clock */
+ phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ;
+
+ ret_val = e1000_write_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL,
+ phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* Options:
+ * MDI/MDI-X = 0 (default)
+ * 0 - Auto for all speeds
+ * 1 - MDI mode
+ * 2 - MDI-X mode
+ * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
+ */
+ ret_val = e1000_read_phy_reg(hw, GG82563_PHY_SPEC_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK;
+
+ switch (hw->mdix) {
+ case 1:
+ phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDI;
+ break;
+ case 2:
+ phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDIX;
+ break;
+ case 0:
+ default:
+ phy_data |= GG82563_PSCR_CROSSOVER_MODE_AUTO;
+ break;
+ }
+
+ /* Options:
+ * disable_polarity_correction = 0 (default)
+ * Automatic Correction for Reversed Cable Polarity
+ * 0 - Disabled
+ * 1 - Enabled
+ */
+ phy_data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE;
+ if (hw->disable_polarity_correction == 1)
+ phy_data |= GG82563_PSCR_POLARITY_REVERSAL_DISABLE;
+ ret_val = e1000_write_phy_reg(hw, GG82563_PHY_SPEC_CTRL, phy_data);
+
+ if (ret_val)
+ return ret_val;
+
+ /* SW Reset the PHY so all changes take effect */
+ ret_val = e1000_phy_reset(hw);
+ if (ret_val) {
+ DEBUGOUT("Error Resetting the PHY\n");
+ return ret_val;
+ }
+ } /* phy_reset_disable */
+
+ if (hw->mac_type == e1000_80003es2lan) {
+ /* Bypass RX and TX FIFO's */
+ ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_FIFO_CTRL,
+ E1000_KUMCTRLSTA_FIFO_CTRL_RX_BYPASS |
+ E1000_KUMCTRLSTA_FIFO_CTRL_TX_BYPASS);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_read_phy_reg(hw, GG82563_PHY_SPEC_CTRL_2, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG;
+ ret_val = e1000_write_phy_reg(hw, GG82563_PHY_SPEC_CTRL_2, phy_data);
+
+ if (ret_val)
+ return ret_val;
+
+ reg_data = E1000_READ_REG(hw, CTRL_EXT);
+ reg_data &= ~(E1000_CTRL_EXT_LINK_MODE_MASK);
+ E1000_WRITE_REG(hw, CTRL_EXT, reg_data);
+
+ ret_val = e1000_read_phy_reg(hw, GG82563_PHY_PWR_MGMT_CTRL,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* Do not init these registers when the HW is in IAMT mode, since the
+ * firmware will have already initialized them. We only initialize
+ * them if the HW is not in IAMT mode.
+ */
+ if (e1000_check_mng_mode(hw) == FALSE) {
+ /* Enable Electrical Idle on the PHY */
+ phy_data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE;
+ ret_val = e1000_write_phy_reg(hw, GG82563_PHY_PWR_MGMT_CTRL,
+ phy_data);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
+ ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
+ phy_data);
+
+ if (ret_val)
+ return ret_val;
+ }
+
+ /* Workaround: Disable padding in Kumeran interface in the MAC
+ * and in the PHY to avoid CRC errors.
+ */
+ ret_val = e1000_read_phy_reg(hw, GG82563_PHY_INBAND_CTRL,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+ phy_data |= GG82563_ICR_DIS_PADDING;
+ ret_val = e1000_write_phy_reg(hw, GG82563_PHY_INBAND_CTRL,
+ phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/********************************************************************
+* Copper link setup for e1000_phy_m88 series.
+*
+* hw - Struct containing variables accessed by shared code
+*********************************************************************/
+static int32_t
+e1000_copper_link_mgp_setup(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_copper_link_mgp_setup");
+
+ if (hw->phy_reset_disable)
+ return E1000_SUCCESS;
+
+ /* Enable CRS on TX. This must be set for half-duplex operation. */
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
+
+ /* Options:
+ * MDI/MDI-X = 0 (default)
+ * 0 - Auto for all speeds
+ * 1 - MDI mode
+ * 2 - MDI-X mode
+ * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
+ */
+ phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
+
+ switch (hw->mdix) {
+ case 1:
+ phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE;
+ break;
+ case 2:
+ phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE;
+ break;
+ case 3:
+ phy_data |= M88E1000_PSCR_AUTO_X_1000T;
+ break;
+ case 0:
+ default:
+ phy_data |= M88E1000_PSCR_AUTO_X_MODE;
+ break;
+ }
+
+ /* Options:
+ * disable_polarity_correction = 0 (default)
+ * Automatic Correction for Reversed Cable Polarity
+ * 0 - Disabled
+ * 1 - Enabled
+ */
+ phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL;
+ if (hw->disable_polarity_correction == 1)
+ phy_data |= M88E1000_PSCR_POLARITY_REVERSAL;
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ if (hw->phy_revision < M88E1011_I_REV_4) {
+ /* Force TX_CLK in the Extended PHY Specific Control Register
+ * to 25MHz clock.
+ */
+ ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data |= M88E1000_EPSCR_TX_CLK_25;
+
+ if ((hw->phy_revision == E1000_REVISION_2) &&
+ (hw->phy_id == M88E1111_I_PHY_ID)) {
+ /* Vidalia Phy, set the downshift counter to 5x */
+ phy_data &= ~(M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK);
+ phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X;
+ ret_val = e1000_write_phy_reg(hw,
+ M88E1000_EXT_PHY_SPEC_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+ } else {
+ /* Configure Master and Slave downshift values */
+ phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK |
+ M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK);
+ phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X |
+ M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X);
+ ret_val = e1000_write_phy_reg(hw,
+ M88E1000_EXT_PHY_SPEC_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+ }
+
+ /* SW Reset the PHY so all changes take effect */
+ ret_val = e1000_phy_reset(hw);
+ if (ret_val) {
+ DEBUGOUT("Error Resetting the PHY\n");
+ return ret_val;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/********************************************************************
+* Setup auto-negotiation and flow control advertisements,
+* and then perform auto-negotiation.
+*
+* hw - Struct containing variables accessed by shared code
+*********************************************************************/
+static int32_t
+e1000_copper_link_autoneg(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_copper_link_autoneg");
+
+ /* Perform some bounds checking on the hw->autoneg_advertised
+ * parameter. If this variable is zero, then set it to the default.
+ */
+ hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT;
+
+ /* If autoneg_advertised is zero, we assume it was not defaulted
+ * by the calling code so we set to advertise full capability.
+ */
+ if (hw->autoneg_advertised == 0)
+ hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+
+ /* IFE phy only supports 10/100 */
+ if (hw->phy_type == e1000_phy_ife)
+ hw->autoneg_advertised &= AUTONEG_ADVERTISE_10_100_ALL;
+
+ DEBUGOUT("Reconfiguring auto-neg advertisement params\n");
+ ret_val = e1000_phy_setup_autoneg(hw);
+ if (ret_val) {
+ DEBUGOUT("Error Setting up Auto-Negotiation\n");
+ return ret_val;
+ }
+ DEBUGOUT("Restarting Auto-Neg\n");
+
+ /* Restart auto-negotiation by setting the Auto Neg Enable bit and
+ * the Auto Neg Restart bit in the PHY control register.
+ */
+ ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
+ ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* Does the user want to wait for Auto-Neg to complete here, or
+ * check at a later time (for example, callback routine).
+ */
+ if (hw->wait_autoneg_complete) {
+ ret_val = e1000_wait_autoneg(hw);
+ if (ret_val) {
+ DEBUGOUT("Error while waiting for autoneg to complete\n");
+ return ret_val;
+ }
+ }
+
+ hw->get_link_status = TRUE;
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Config the MAC and the PHY after link is up.
+* 1) Set up the MAC to the current PHY speed/duplex
+* if we are on 82543. If we
+* are on newer silicon, we only need to configure
+* collision distance in the Transmit Control Register.
+* 2) Set up flow control on the MAC to that established with
+* the link partner.
+* 3) Config DSP to improve Gigabit link quality for some PHY revisions.
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int32_t
+e1000_copper_link_postconfig(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ DEBUGFUNC("e1000_copper_link_postconfig");
+
+ if (hw->mac_type >= e1000_82544) {
+ e1000_config_collision_dist(hw);
+ } else {
+ ret_val = e1000_config_mac_to_phy(hw);
+ if (ret_val) {
+ DEBUGOUT("Error configuring MAC to PHY settings\n");
+ return ret_val;
+ }
+ }
+ ret_val = e1000_config_fc_after_link_up(hw);
+ if (ret_val) {
+ DEBUGOUT("Error Configuring Flow Control\n");
+ return ret_val;
+ }
+
+ /* Config DSP to improve Giga link quality */
+ if (hw->phy_type == e1000_phy_igp) {
+ ret_val = e1000_config_dsp_after_link_change(hw, TRUE);
+ if (ret_val) {
+ DEBUGOUT("Error Configuring DSP after link up\n");
+ return ret_val;
+ }
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Detects which PHY is present and setup the speed and duplex
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int32_t
+e1000_setup_copper_link(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t i;
+ uint16_t phy_data;
+ uint16_t reg_data;
+
+ DEBUGFUNC("e1000_setup_copper_link");
+
+ switch (hw->mac_type) {
+ case e1000_80003es2lan:
+ case e1000_ich8lan:
+ /* Set the mac to wait the maximum time between each
+ * iteration and increase the max iterations when
+ * polling the phy; this fixes erroneous timeouts at 10Mbps. */
+ ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 4), 0xFFFF);
+ if (ret_val)
+ return ret_val;
+ ret_val = e1000_read_kmrn_reg(hw, GG82563_REG(0x34, 9), &reg_data);
+ if (ret_val)
+ return ret_val;
+ reg_data |= 0x3F;
+ ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 9), reg_data);
+ if (ret_val)
+ return ret_val;
+ default:
+ break;
+ }
+
+ /* Check if it is a valid PHY and set PHY mode if necessary. */
+ ret_val = e1000_copper_link_preconfig(hw);
+ if (ret_val)
+ return ret_val;
+
+ switch (hw->mac_type) {
+ case e1000_80003es2lan:
+ /* Kumeran registers are written-only */
+ reg_data = E1000_KUMCTRLSTA_INB_CTRL_LINK_STATUS_TX_TIMEOUT_DEFAULT;
+ reg_data |= E1000_KUMCTRLSTA_INB_CTRL_DIS_PADDING;
+ ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_INB_CTRL,
+ reg_data);
+ if (ret_val)
+ return ret_val;
+ break;
+ default:
+ break;
+ }
+
+ if (hw->phy_type == e1000_phy_igp ||
+ hw->phy_type == e1000_phy_igp_3 ||
+ hw->phy_type == e1000_phy_igp_2) {
+ ret_val = e1000_copper_link_igp_setup(hw);
+ if (ret_val)
+ return ret_val;
+ } else if (hw->phy_type == e1000_phy_m88) {
+ ret_val = e1000_copper_link_mgp_setup(hw);
+ if (ret_val)
+ return ret_val;
+ } else if (hw->phy_type == e1000_phy_gg82563) {
+ ret_val = e1000_copper_link_ggp_setup(hw);
+ if (ret_val)
+ return ret_val;
+ }
+
+ if (hw->autoneg) {
+ /* Setup autoneg and flow control advertisement
+ * and perform autonegotiation */
+ ret_val = e1000_copper_link_autoneg(hw);
+ if (ret_val)
+ return ret_val;
+ } else {
+ /* PHY will be set to 10H, 10F, 100H,or 100F
+ * depending on value from forced_speed_duplex. */
+ DEBUGOUT("Forcing speed and duplex\n");
+ ret_val = e1000_phy_force_speed_duplex(hw);
+ if (ret_val) {
+ DEBUGOUT("Error Forcing Speed and Duplex\n");
+ return ret_val;
+ }
+ }
+
+ /* Check link status. Wait up to 100 microseconds for link to become
+ * valid.
+ */
+ for (i = 0; i < 10; i++) {
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ if (phy_data & MII_SR_LINK_STATUS) {
+ /* Config the MAC and PHY after link is up */
+ ret_val = e1000_copper_link_postconfig(hw);
+ if (ret_val)
+ return ret_val;
+
+ DEBUGOUT("Valid link established!!!\n");
+ return E1000_SUCCESS;
+ }
+ udelay(10);
+ }
+
+ DEBUGOUT("Unable to establish link!!!\n");
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Configure the MAC-to-PHY interface for 10/100Mbps
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int32_t
+e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, uint16_t duplex)
+{
+ int32_t ret_val = E1000_SUCCESS;
+ uint32_t tipg;
+ uint16_t reg_data;
+
+ DEBUGFUNC("e1000_configure_kmrn_for_10_100");
+
+ reg_data = E1000_KUMCTRLSTA_HD_CTRL_10_100_DEFAULT;
+ ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_HD_CTRL,
+ reg_data);
+ if (ret_val)
+ return ret_val;
+
+ /* Configure Transmit Inter-Packet Gap */
+ tipg = E1000_READ_REG(hw, TIPG);
+ tipg &= ~E1000_TIPG_IPGT_MASK;
+ tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_10_100;
+ E1000_WRITE_REG(hw, TIPG, tipg);
+
+ ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, &reg_data);
+
+ if (ret_val)
+ return ret_val;
+
+ if (duplex == HALF_DUPLEX)
+ reg_data |= GG82563_KMCR_PASS_FALSE_CARRIER;
+ else
+ reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
+
+ ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data);
+
+ return ret_val;
+}
+
+static int32_t
+e1000_configure_kmrn_for_1000(struct e1000_hw *hw)
+{
+ int32_t ret_val = E1000_SUCCESS;
+ uint16_t reg_data;
+ uint32_t tipg;
+
+ DEBUGFUNC("e1000_configure_kmrn_for_1000");
+
+ reg_data = E1000_KUMCTRLSTA_HD_CTRL_1000_DEFAULT;
+ ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_HD_CTRL,
+ reg_data);
+ if (ret_val)
+ return ret_val;
+
+ /* Configure Transmit Inter-Packet Gap */
+ tipg = E1000_READ_REG(hw, TIPG);
+ tipg &= ~E1000_TIPG_IPGT_MASK;
+ tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000;
+ E1000_WRITE_REG(hw, TIPG, tipg);
+
+ ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, &reg_data);
+
+ if (ret_val)
+ return ret_val;
+
+ reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
+ ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data);
+
+ return ret_val;
+}
+
+/******************************************************************************
+* Configures PHY autoneg and flow control advertisement settings
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+int32_t
+e1000_phy_setup_autoneg(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t mii_autoneg_adv_reg;
+ uint16_t mii_1000t_ctrl_reg;
+
+ DEBUGFUNC("e1000_phy_setup_autoneg");
+
+ /* Read the MII Auto-Neg Advertisement Register (Address 4). */
+ ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg);
+ if (ret_val)
+ return ret_val;
+
+ if (hw->phy_type != e1000_phy_ife) {
+ /* Read the MII 1000Base-T Control Register (Address 9). */
+ ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg);
+ if (ret_val)
+ return ret_val;
+ } else
+ mii_1000t_ctrl_reg=0;
+
+ /* Need to parse both autoneg_advertised and fc and set up
+ * the appropriate PHY registers. First we will parse for
+ * autoneg_advertised software override. Since we can advertise
+ * a plethora of combinations, we need to check each bit
+ * individually.
+ */
+
+ /* First we clear all the 10/100 mb speed bits in the Auto-Neg
+ * Advertisement Register (Address 4) and the 1000 mb speed bits in
+ * the 1000Base-T Control Register (Address 9).
+ */
+ mii_autoneg_adv_reg &= ~REG4_SPEED_MASK;
+ mii_1000t_ctrl_reg &= ~REG9_SPEED_MASK;
+
+ DEBUGOUT1("autoneg_advertised %x\n", hw->autoneg_advertised);
+
+ /* Do we want to advertise 10 Mb Half Duplex? */
+ if (hw->autoneg_advertised & ADVERTISE_10_HALF) {
+ DEBUGOUT("Advertise 10mb Half duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS;
+ }
+
+ /* Do we want to advertise 10 Mb Full Duplex? */
+ if (hw->autoneg_advertised & ADVERTISE_10_FULL) {
+ DEBUGOUT("Advertise 10mb Full duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS;
+ }
+
+ /* Do we want to advertise 100 Mb Half Duplex? */
+ if (hw->autoneg_advertised & ADVERTISE_100_HALF) {
+ DEBUGOUT("Advertise 100mb Half duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS;
+ }
+
+ /* Do we want to advertise 100 Mb Full Duplex? */
+ if (hw->autoneg_advertised & ADVERTISE_100_FULL) {
+ DEBUGOUT("Advertise 100mb Full duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS;
+ }
+
+ /* We do not allow the Phy to advertise 1000 Mb Half Duplex */
+ if (hw->autoneg_advertised & ADVERTISE_1000_HALF) {
+ DEBUGOUT("Advertise 1000mb Half duplex requested, request denied!\n");
+ }
+
+ /* Do we want to advertise 1000 Mb Full Duplex? */
+ if (hw->autoneg_advertised & ADVERTISE_1000_FULL) {
+ DEBUGOUT("Advertise 1000mb Full duplex\n");
+ mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS;
+ if (hw->phy_type == e1000_phy_ife) {
+ DEBUGOUT("e1000_phy_ife is a 10/100 PHY. Gigabit speed is not supported.\n");
+ }
+ }
+
+ /* Check for a software override of the flow control settings, and
+ * setup the PHY advertisement registers accordingly. If
+ * auto-negotiation is enabled, then software will have to set the
+ * "PAUSE" bits to the correct value in the Auto-Negotiation
+ * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-negotiation.
+ *
+ * The possible values of the "fc" parameter are:
+ * 0: Flow control is completely disabled
+ * 1: Rx flow control is enabled (we can receive pause frames
+ * but not send pause frames).
+ * 2: Tx flow control is enabled (we can send pause frames
+ * but we do not support receiving pause frames).
+ * 3: Both Rx and TX flow control (symmetric) are enabled.
+ * other: No software override. The flow control configuration
+ * in the EEPROM is used.
+ */
+ switch (hw->fc) {
+ case E1000_FC_NONE: /* 0 */
+ /* Flow control (RX & TX) is completely disabled by a
+ * software over-ride.
+ */
+ mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+ break;
+ case E1000_FC_RX_PAUSE: /* 1 */
+ /* RX Flow control is enabled, and TX Flow control is
+ * disabled, by a software over-ride.
+ */
+ /* Since there really isn't a way to advertise that we are
+ * capable of RX Pause ONLY, we will advertise that we
+ * support both symmetric and asymmetric RX PAUSE. Later
+ * (in e1000_config_fc_after_link_up) we will disable the
+ *hw's ability to send PAUSE frames.
+ */
+ mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+ break;
+ case E1000_FC_TX_PAUSE: /* 2 */
+ /* TX Flow control is enabled, and RX Flow control is
+ * disabled, by a software over-ride.
+ */
+ mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR;
+ mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE;
+ break;
+ case E1000_FC_FULL: /* 3 */
+ /* Flow control (both RX and TX) is enabled by a software
+ * over-ride.
+ */
+ mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+ break;
+ default:
+ DEBUGOUT("Flow control param set incorrectly\n");
+ return -E1000_ERR_CONFIG;
+ }
+
+ ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg);
+ if (ret_val)
+ return ret_val;
+
+ DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg);
+
+ if (hw->phy_type != e1000_phy_ife) {
+ ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg);
+ if (ret_val)
+ return ret_val;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Force PHY speed and duplex settings to hw->forced_speed_duplex
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int32_t
+e1000_phy_force_speed_duplex(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ int32_t ret_val;
+ uint16_t mii_ctrl_reg;
+ uint16_t mii_status_reg;
+ uint16_t phy_data;
+ uint16_t i;
+
+ DEBUGFUNC("e1000_phy_force_speed_duplex");
+
+ /* Turn off Flow control if we are forcing speed and duplex. */
+ hw->fc = E1000_FC_NONE;
+
+ DEBUGOUT1("hw->fc = %d\n", hw->fc);
+
+ /* Read the Device Control Register. */
+ ctrl = E1000_READ_REG(hw, CTRL);
+
+ /* Set the bits to Force Speed and Duplex in the Device Ctrl Reg. */
+ ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
+ ctrl &= ~(DEVICE_SPEED_MASK);
+
+ /* Clear the Auto Speed Detect Enable bit. */
+ ctrl &= ~E1000_CTRL_ASDE;
+
+ /* Read the MII Control Register. */
+ ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &mii_ctrl_reg);
+ if (ret_val)
+ return ret_val;
+
+ /* We need to disable autoneg in order to force link and duplex. */
+
+ mii_ctrl_reg &= ~MII_CR_AUTO_NEG_EN;
+
+ /* Are we forcing Full or Half Duplex? */
+ if (hw->forced_speed_duplex == e1000_100_full ||
+ hw->forced_speed_duplex == e1000_10_full) {
+ /* We want to force full duplex so we SET the full duplex bits in the
+ * Device and MII Control Registers.
+ */
+ ctrl |= E1000_CTRL_FD;
+ mii_ctrl_reg |= MII_CR_FULL_DUPLEX;
+ DEBUGOUT("Full Duplex\n");
+ } else {
+ /* We want to force half duplex so we CLEAR the full duplex bits in
+ * the Device and MII Control Registers.
+ */
+ ctrl &= ~E1000_CTRL_FD;
+ mii_ctrl_reg &= ~MII_CR_FULL_DUPLEX;
+ DEBUGOUT("Half Duplex\n");
+ }
+
+ /* Are we forcing 100Mbps??? */
+ if (hw->forced_speed_duplex == e1000_100_full ||
+ hw->forced_speed_duplex == e1000_100_half) {
+ /* Set the 100Mb bit and turn off the 1000Mb and 10Mb bits. */
+ ctrl |= E1000_CTRL_SPD_100;
+ mii_ctrl_reg |= MII_CR_SPEED_100;
+ mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10);
+ DEBUGOUT("Forcing 100mb ");
+ } else {
+ /* Set the 10Mb bit and turn off the 1000Mb and 100Mb bits. */
+ ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100);
+ mii_ctrl_reg |= MII_CR_SPEED_10;
+ mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100);
+ DEBUGOUT("Forcing 10mb ");
+ }
+
+ e1000_config_collision_dist(hw);
+
+ /* Write the configured values back to the Device Control Reg. */
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+
+ if ((hw->phy_type == e1000_phy_m88) ||
+ (hw->phy_type == e1000_phy_gg82563)) {
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI
+ * forced whenever speed are duplex are forced.
+ */
+ phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ DEBUGOUT1("M88E1000 PSCR: %x \n", phy_data);
+
+ /* Need to reset the PHY or these changes will be ignored */
+ mii_ctrl_reg |= MII_CR_RESET;
+
+ /* Disable MDI-X support for 10/100 */
+ } else if (hw->phy_type == e1000_phy_ife) {
+ ret_val = e1000_read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~IFE_PMC_AUTO_MDIX;
+ phy_data &= ~IFE_PMC_FORCE_MDIX;
+
+ ret_val = e1000_write_phy_reg(hw, IFE_PHY_MDIX_CONTROL, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ } else {
+ /* Clear Auto-Crossover to force MDI manually. IGP requires MDI
+ * forced whenever speed or duplex are forced.
+ */
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX;
+ phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX;
+
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+ /* Write back the modified PHY MII control register. */
+ ret_val = e1000_write_phy_reg(hw, PHY_CTRL, mii_ctrl_reg);
+ if (ret_val)
+ return ret_val;
+
+ udelay(1);
+
+ /* The wait_autoneg_complete flag may be a little misleading here.
+ * Since we are forcing speed and duplex, Auto-Neg is not enabled.
+ * But we do want to delay for a period while forcing only so we
+ * don't generate false No Link messages. So we will wait here
+ * only if the user has set wait_autoneg_complete to 1, which is
+ * the default.
+ */
+ if (hw->wait_autoneg_complete) {
+ /* We will wait for autoneg to complete. */
+ DEBUGOUT("Waiting for forced speed/duplex link.\n");
+ mii_status_reg = 0;
+
+ /* We will wait for autoneg to complete or 4.5 seconds to expire. */
+ for (i = PHY_FORCE_TIME; i > 0; i--) {
+ /* Read the MII Status Register and wait for Auto-Neg Complete bit
+ * to be set.
+ */
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+
+ if (mii_status_reg & MII_SR_LINK_STATUS) break;
+ msleep(100);
+ }
+ if ((i == 0) &&
+ ((hw->phy_type == e1000_phy_m88) ||
+ (hw->phy_type == e1000_phy_gg82563))) {
+ /* We didn't get link. Reset the DSP and wait again for link. */
+ ret_val = e1000_phy_reset_dsp(hw);
+ if (ret_val) {
+ DEBUGOUT("Error Resetting PHY DSP\n");
+ return ret_val;
+ }
+ }
+ /* This loop will early-out if the link condition has been met. */
+ for (i = PHY_FORCE_TIME; i > 0; i--) {
+ if (mii_status_reg & MII_SR_LINK_STATUS) break;
+ msleep(100);
+ /* Read the MII Status Register and wait for Auto-Neg Complete bit
+ * to be set.
+ */
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+ }
+ }
+
+ if (hw->phy_type == e1000_phy_m88) {
+ /* Because we reset the PHY above, we need to re-force TX_CLK in the
+ * Extended PHY Specific Control Register to 25MHz clock. This value
+ * defaults back to a 2.5MHz clock when the PHY is reset.
+ */
+ ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data |= M88E1000_EPSCR_TX_CLK_25;
+ ret_val = e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* In addition, because of the s/w reset above, we need to enable CRS on
+ * TX. This must be set for both full and half duplex operation.
+ */
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ if ((hw->mac_type == e1000_82544 || hw->mac_type == e1000_82543) &&
+ (!hw->autoneg) && (hw->forced_speed_duplex == e1000_10_full ||
+ hw->forced_speed_duplex == e1000_10_half)) {
+ ret_val = e1000_polarity_reversal_workaround(hw);
+ if (ret_val)
+ return ret_val;
+ }
+ } else if (hw->phy_type == e1000_phy_gg82563) {
+ /* The TX_CLK of the Extended PHY Specific Control Register defaults
+ * to 2.5MHz on a reset. We need to re-force it back to 25MHz, if
+ * we're not in a forced 10/duplex configuration. */
+ ret_val = e1000_read_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~GG82563_MSCR_TX_CLK_MASK;
+ if ((hw->forced_speed_duplex == e1000_10_full) ||
+ (hw->forced_speed_duplex == e1000_10_half))
+ phy_data |= GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ;
+ else
+ phy_data |= GG82563_MSCR_TX_CLK_100MBPS_25MHZ;
+
+ /* Also due to the reset, we need to enable CRS on Tx. */
+ phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
+
+ ret_val = e1000_write_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Sets the collision distance in the Transmit Control register
+*
+* hw - Struct containing variables accessed by shared code
+*
+* Link should have been established previously. Reads the speed and duplex
+* information from the Device Status register.
+******************************************************************************/
+void
+e1000_config_collision_dist(struct e1000_hw *hw)
+{
+ uint32_t tctl, coll_dist;
+
+ DEBUGFUNC("e1000_config_collision_dist");
+
+ if (hw->mac_type < e1000_82543)
+ coll_dist = E1000_COLLISION_DISTANCE_82542;
+ else
+ coll_dist = E1000_COLLISION_DISTANCE;
+
+ tctl = E1000_READ_REG(hw, TCTL);
+
+ tctl &= ~E1000_TCTL_COLD;
+ tctl |= coll_dist << E1000_COLD_SHIFT;
+
+ E1000_WRITE_REG(hw, TCTL, tctl);
+ E1000_WRITE_FLUSH(hw);
+}
+
+/******************************************************************************
+* Sets MAC speed and duplex settings to reflect the those in the PHY
+*
+* hw - Struct containing variables accessed by shared code
+* mii_reg - data to write to the MII control register
+*
+* The contents of the PHY register containing the needed information need to
+* be passed in.
+******************************************************************************/
+static int32_t
+e1000_config_mac_to_phy(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_config_mac_to_phy");
+
+ /* 82544 or newer MAC, Auto Speed Detection takes care of
+ * MAC speed/duplex configuration.*/
+ if (hw->mac_type >= e1000_82544)
+ return E1000_SUCCESS;
+
+ /* Read the Device Control Register and set the bits to Force Speed
+ * and Duplex.
+ */
+ ctrl = E1000_READ_REG(hw, CTRL);
+ ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
+ ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS);
+
+ /* Set up duplex in the Device Control and Transmit Control
+ * registers depending on negotiated values.
+ */
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ if (phy_data & M88E1000_PSSR_DPLX)
+ ctrl |= E1000_CTRL_FD;
+ else
+ ctrl &= ~E1000_CTRL_FD;
+
+ e1000_config_collision_dist(hw);
+
+ /* Set up speed in the Device Control register depending on
+ * negotiated values.
+ */
+ if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS)
+ ctrl |= E1000_CTRL_SPD_1000;
+ else if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS)
+ ctrl |= E1000_CTRL_SPD_100;
+
+ /* Write the configured values back to the Device Control Reg. */
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Forces the MAC's flow control settings.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Sets the TFCE and RFCE bits in the device control register to reflect
+ * the adapter settings. TFCE and RFCE need to be explicitly set by
+ * software when a Copper PHY is used because autonegotiation is managed
+ * by the PHY rather than the MAC. Software must also configure these
+ * bits when link is forced on a fiber connection.
+ *****************************************************************************/
+int32_t
+e1000_force_mac_fc(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+
+ DEBUGFUNC("e1000_force_mac_fc");
+
+ /* Get the current configuration of the Device Control Register */
+ ctrl = E1000_READ_REG(hw, CTRL);
+
+ /* Because we didn't get link via the internal auto-negotiation
+ * mechanism (we either forced link or we got link via PHY
+ * auto-neg), we have to manually enable/disable transmit an
+ * receive flow control.
+ *
+ * The "Case" statement below enables/disable flow control
+ * according to the "hw->fc" parameter.
+ *
+ * The possible values of the "fc" parameter are:
+ * 0: Flow control is completely disabled
+ * 1: Rx flow control is enabled (we can receive pause
+ * frames but not send pause frames).
+ * 2: Tx flow control is enabled (we can send pause frames
+ * frames but we do not receive pause frames).
+ * 3: Both Rx and TX flow control (symmetric) is enabled.
+ * other: No other values should be possible at this point.
+ */
+
+ switch (hw->fc) {
+ case E1000_FC_NONE:
+ ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE));
+ break;
+ case E1000_FC_RX_PAUSE:
+ ctrl &= (~E1000_CTRL_TFCE);
+ ctrl |= E1000_CTRL_RFCE;
+ break;
+ case E1000_FC_TX_PAUSE:
+ ctrl &= (~E1000_CTRL_RFCE);
+ ctrl |= E1000_CTRL_TFCE;
+ break;
+ case E1000_FC_FULL:
+ ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE);
+ break;
+ default:
+ DEBUGOUT("Flow control param set incorrectly\n");
+ return -E1000_ERR_CONFIG;
+ }
+
+ /* Disable TX Flow Control for 82542 (rev 2.0) */
+ if (hw->mac_type == e1000_82542_rev2_0)
+ ctrl &= (~E1000_CTRL_TFCE);
+
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Configures flow control settings after link is established
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Should be called immediately after a valid link has been established.
+ * Forces MAC flow control settings if link was forced. When in MII/GMII mode
+ * and autonegotiation is enabled, the MAC flow control settings will be set
+ * based on the flow control negotiated by the PHY. In TBI mode, the TFCE
+ * and RFCE bits will be automaticaly set to the negotiated flow control mode.
+ *****************************************************************************/
+static int32_t
+e1000_config_fc_after_link_up(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t mii_status_reg;
+ uint16_t mii_nway_adv_reg;
+ uint16_t mii_nway_lp_ability_reg;
+ uint16_t speed;
+ uint16_t duplex;
+
+ DEBUGFUNC("e1000_config_fc_after_link_up");
+
+ /* Check for the case where we have fiber media and auto-neg failed
+ * so we had to force link. In this case, we need to force the
+ * configuration of the MAC to match the "fc" parameter.
+ */
+ if (((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) ||
+ ((hw->media_type == e1000_media_type_internal_serdes) &&
+ (hw->autoneg_failed)) ||
+ ((hw->media_type == e1000_media_type_copper) && (!hw->autoneg))) {
+ ret_val = e1000_force_mac_fc(hw);
+ if (ret_val) {
+ DEBUGOUT("Error forcing flow control settings\n");
+ return ret_val;
+ }
+ }
+
+ /* Check for the case where we have copper media and auto-neg is
+ * enabled. In this case, we need to check and see if Auto-Neg
+ * has completed, and if so, how the PHY and link partner has
+ * flow control configured.
+ */
+ if ((hw->media_type == e1000_media_type_copper) && hw->autoneg) {
+ /* Read the MII Status Register and check to see if AutoNeg
+ * has completed. We read this twice because this reg has
+ * some "sticky" (latched) bits.
+ */
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+
+ if (mii_status_reg & MII_SR_AUTONEG_COMPLETE) {
+ /* The AutoNeg process has completed, so we now need to
+ * read both the Auto Negotiation Advertisement Register
+ * (Address 4) and the Auto_Negotiation Base Page Ability
+ * Register (Address 5) to determine how flow control was
+ * negotiated.
+ */
+ ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV,
+ &mii_nway_adv_reg);
+ if (ret_val)
+ return ret_val;
+ ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY,
+ &mii_nway_lp_ability_reg);
+ if (ret_val)
+ return ret_val;
+
+ /* Two bits in the Auto Negotiation Advertisement Register
+ * (Address 4) and two bits in the Auto Negotiation Base
+ * Page Ability Register (Address 5) determine flow control
+ * for both the PHY and the link partner. The following
+ * table, taken out of the IEEE 802.3ab/D6.0 dated March 25,
+ * 1999, describes these PAUSE resolution bits and how flow
+ * control is determined based upon these settings.
+ * NOTE: DC = Don't Care
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution
+ *-------|---------|-------|---------|--------------------
+ * 0 | 0 | DC | DC | E1000_FC_NONE
+ * 0 | 1 | 0 | DC | E1000_FC_NONE
+ * 0 | 1 | 1 | 0 | E1000_FC_NONE
+ * 0 | 1 | 1 | 1 | E1000_FC_TX_PAUSE
+ * 1 | 0 | 0 | DC | E1000_FC_NONE
+ * 1 | DC | 1 | DC | E1000_FC_FULL
+ * 1 | 1 | 0 | 0 | E1000_FC_NONE
+ * 1 | 1 | 0 | 1 | E1000_FC_RX_PAUSE
+ *
+ */
+ /* Are both PAUSE bits set to 1? If so, this implies
+ * Symmetric Flow Control is enabled at both ends. The
+ * ASM_DIR bits are irrelevant per the spec.
+ *
+ * For Symmetric Flow Control:
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+ *-------|---------|-------|---------|--------------------
+ * 1 | DC | 1 | DC | E1000_FC_FULL
+ *
+ */
+ if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) {
+ /* Now we need to check if the user selected RX ONLY
+ * of pause frames. In this case, we had to advertise
+ * FULL flow control because we could not advertise RX
+ * ONLY. Hence, we must now check to see if we need to
+ * turn OFF the TRANSMISSION of PAUSE frames.
+ */
+ if (hw->original_fc == E1000_FC_FULL) {
+ hw->fc = E1000_FC_FULL;
+ DEBUGOUT("Flow Control = FULL.\n");
+ } else {
+ hw->fc = E1000_FC_RX_PAUSE;
+ DEBUGOUT("Flow Control = RX PAUSE frames only.\n");
+ }
+ }
+ /* For receiving PAUSE frames ONLY.
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+ *-------|---------|-------|---------|--------------------
+ * 0 | 1 | 1 | 1 | E1000_FC_TX_PAUSE
+ *
+ */
+ else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+ (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
+ hw->fc = E1000_FC_TX_PAUSE;
+ DEBUGOUT("Flow Control = TX PAUSE frames only.\n");
+ }
+ /* For transmitting PAUSE frames ONLY.
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+ *-------|---------|-------|---------|--------------------
+ * 1 | 1 | 0 | 1 | E1000_FC_RX_PAUSE
+ *
+ */
+ else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+ (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
+ !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
+ hw->fc = E1000_FC_RX_PAUSE;
+ DEBUGOUT("Flow Control = RX PAUSE frames only.\n");
+ }
+ /* Per the IEEE spec, at this point flow control should be
+ * disabled. However, we want to consider that we could
+ * be connected to a legacy switch that doesn't advertise
+ * desired flow control, but can be forced on the link
+ * partner. So if we advertised no flow control, that is
+ * what we will resolve to. If we advertised some kind of
+ * receive capability (Rx Pause Only or Full Flow Control)
+ * and the link partner advertised none, we will configure
+ * ourselves to enable Rx Flow Control only. We can do
+ * this safely for two reasons: If the link partner really
+ * didn't want flow control enabled, and we enable Rx, no
+ * harm done since we won't be receiving any PAUSE frames
+ * anyway. If the intent on the link partner was to have
+ * flow control enabled, then by us enabling RX only, we
+ * can at least receive pause frames and process them.
+ * This is a good idea because in most cases, since we are
+ * predominantly a server NIC, more times than not we will
+ * be asked to delay transmission of packets than asking
+ * our link partner to pause transmission of frames.
+ */
+ else if ((hw->original_fc == E1000_FC_NONE ||
+ hw->original_fc == E1000_FC_TX_PAUSE) ||
+ hw->fc_strict_ieee) {
+ hw->fc = E1000_FC_NONE;
+ DEBUGOUT("Flow Control = NONE.\n");
+ } else {
+ hw->fc = E1000_FC_RX_PAUSE;
+ DEBUGOUT("Flow Control = RX PAUSE frames only.\n");
+ }
+
+ /* Now we need to do one last check... If we auto-
+ * negotiated to HALF DUPLEX, flow control should not be
+ * enabled per IEEE 802.3 spec.
+ */
+ ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex);
+ if (ret_val) {
+ DEBUGOUT("Error getting link speed and duplex\n");
+ return ret_val;
+ }
+
+ if (duplex == HALF_DUPLEX)
+ hw->fc = E1000_FC_NONE;
+
+ /* Now we call a subroutine to actually force the MAC
+ * controller to use the correct flow control settings.
+ */
+ ret_val = e1000_force_mac_fc(hw);
+ if (ret_val) {
+ DEBUGOUT("Error forcing flow control settings\n");
+ return ret_val;
+ }
+ } else {
+ DEBUGOUT("Copper PHY and Auto Neg has not completed.\n");
+ }
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Checks to see if the link status of the hardware has changed.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Called by any function that needs to check the link status of the adapter.
+ *****************************************************************************/
+int32_t
+e1000_check_for_link(struct e1000_hw *hw)
+{
+ uint32_t rxcw = 0;
+ uint32_t ctrl;
+ uint32_t status;
+ uint32_t rctl;
+ uint32_t icr;
+ uint32_t signal = 0;
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_check_for_link");
+
+ ctrl = E1000_READ_REG(hw, CTRL);
+ status = E1000_READ_REG(hw, STATUS);
+
+ /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be
+ * set when the optics detect a signal. On older adapters, it will be
+ * cleared when there is a signal. This applies to fiber media only.
+ */
+ if ((hw->media_type == e1000_media_type_fiber) ||
+ (hw->media_type == e1000_media_type_internal_serdes)) {
+ rxcw = E1000_READ_REG(hw, RXCW);
+
+ if (hw->media_type == e1000_media_type_fiber) {
+ signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0;
+ if (status & E1000_STATUS_LU)
+ hw->get_link_status = FALSE;
+ }
+ }
+
+ /* If we have a copper PHY then we only want to go out to the PHY
+ * registers to see if Auto-Neg has completed and/or if our link
+ * status has changed. The get_link_status flag will be set if we
+ * receive a Link Status Change interrupt or we have Rx Sequence
+ * Errors.
+ */
+ if ((hw->media_type == e1000_media_type_copper) && hw->get_link_status) {
+ /* First we want to see if the MII Status Register reports
+ * link. If so, then we want to get the current speed/duplex
+ * of the PHY.
+ * Read the register twice since the link bit is sticky.
+ */
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ if (phy_data & MII_SR_LINK_STATUS) {
+ hw->get_link_status = FALSE;
+ /* Check if there was DownShift, must be checked immediately after
+ * link-up */
+ e1000_check_downshift(hw);
+
+ /* If we are on 82544 or 82543 silicon and speed/duplex
+ * are forced to 10H or 10F, then we will implement the polarity
+ * reversal workaround. We disable interrupts first, and upon
+ * returning, place the devices interrupt state to its previous
+ * value except for the link status change interrupt which will
+ * happen due to the execution of this workaround.
+ */
+
+ if ((hw->mac_type == e1000_82544 || hw->mac_type == e1000_82543) &&
+ (!hw->autoneg) &&
+ (hw->forced_speed_duplex == e1000_10_full ||
+ hw->forced_speed_duplex == e1000_10_half)) {
+ E1000_WRITE_REG(hw, IMC, 0xffffffff);
+ ret_val = e1000_polarity_reversal_workaround(hw);
+ icr = E1000_READ_REG(hw, ICR);
+ E1000_WRITE_REG(hw, ICS, (icr & ~E1000_ICS_LSC));
+ E1000_WRITE_REG(hw, IMS, IMS_ENABLE_MASK);
+ }
+
+ } else {
+ /* No link detected */
+ e1000_config_dsp_after_link_change(hw, FALSE);
+ return 0;
+ }
+
+ /* If we are forcing speed/duplex, then we simply return since
+ * we have already determined whether we have link or not.
+ */
+ if (!hw->autoneg) return -E1000_ERR_CONFIG;
+
+ /* optimize the dsp settings for the igp phy */
+ e1000_config_dsp_after_link_change(hw, TRUE);
+
+ /* We have a M88E1000 PHY and Auto-Neg is enabled. If we
+ * have Si on board that is 82544 or newer, Auto
+ * Speed Detection takes care of MAC speed/duplex
+ * configuration. So we only need to configure Collision
+ * Distance in the MAC. Otherwise, we need to force
+ * speed/duplex on the MAC to the current PHY speed/duplex
+ * settings.
+ */
+ if (hw->mac_type >= e1000_82544)
+ e1000_config_collision_dist(hw);
+ else {
+ ret_val = e1000_config_mac_to_phy(hw);
+ if (ret_val) {
+ DEBUGOUT("Error configuring MAC to PHY settings\n");
+ return ret_val;
+ }
+ }
+
+ /* Configure Flow Control now that Auto-Neg has completed. First, we
+ * need to restore the desired flow control settings because we may
+ * have had to re-autoneg with a different link partner.
+ */
+ ret_val = e1000_config_fc_after_link_up(hw);
+ if (ret_val) {
+ DEBUGOUT("Error configuring flow control\n");
+ return ret_val;
+ }
+
+ /* At this point we know that we are on copper and we have
+ * auto-negotiated link. These are conditions for checking the link
+ * partner capability register. We use the link speed to determine if
+ * TBI compatibility needs to be turned on or off. If the link is not
+ * at gigabit speed, then TBI compatibility is not needed. If we are
+ * at gigabit speed, we turn on TBI compatibility.
+ */
+ if (hw->tbi_compatibility_en) {
+ uint16_t speed, duplex;
+ ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex);
+ if (ret_val) {
+ DEBUGOUT("Error getting link speed and duplex\n");
+ return ret_val;
+ }
+ if (speed != SPEED_1000) {
+ /* If link speed is not set to gigabit speed, we do not need
+ * to enable TBI compatibility.
+ */
+ if (hw->tbi_compatibility_on) {
+ /* If we previously were in the mode, turn it off. */
+ rctl = E1000_READ_REG(hw, RCTL);
+ rctl &= ~E1000_RCTL_SBP;
+ E1000_WRITE_REG(hw, RCTL, rctl);
+ hw->tbi_compatibility_on = FALSE;
+ }
+ } else {
+ /* If TBI compatibility is was previously off, turn it on. For
+ * compatibility with a TBI link partner, we will store bad
+ * packets. Some frames have an additional byte on the end and
+ * will look like CRC errors to to the hardware.
+ */
+ if (!hw->tbi_compatibility_on) {
+ hw->tbi_compatibility_on = TRUE;
+ rctl = E1000_READ_REG(hw, RCTL);
+ rctl |= E1000_RCTL_SBP;
+ E1000_WRITE_REG(hw, RCTL, rctl);
+ }
+ }
+ }
+ }
+ /* If we don't have link (auto-negotiation failed or link partner cannot
+ * auto-negotiate), the cable is plugged in (we have signal), and our
+ * link partner is not trying to auto-negotiate with us (we are receiving
+ * idles or data), we need to force link up. We also need to give
+ * auto-negotiation time to complete, in case the cable was just plugged
+ * in. The autoneg_failed flag does this.
+ */
+ else if ((((hw->media_type == e1000_media_type_fiber) &&
+ ((ctrl & E1000_CTRL_SWDPIN1) == signal)) ||
+ (hw->media_type == e1000_media_type_internal_serdes)) &&
+ (!(status & E1000_STATUS_LU)) &&
+ (!(rxcw & E1000_RXCW_C))) {
+ if (hw->autoneg_failed == 0) {
+ hw->autoneg_failed = 1;
+ return 0;
+ }
+ DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\n");
+
+ /* Disable auto-negotiation in the TXCW register */
+ E1000_WRITE_REG(hw, TXCW, (hw->txcw & ~E1000_TXCW_ANE));
+
+ /* Force link-up and also force full-duplex. */
+ ctrl = E1000_READ_REG(hw, CTRL);
+ ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD);
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+
+ /* Configure Flow Control after forcing link up. */
+ ret_val = e1000_config_fc_after_link_up(hw);
+ if (ret_val) {
+ DEBUGOUT("Error configuring flow control\n");
+ return ret_val;
+ }
+ }
+ /* If we are forcing link and we are receiving /C/ ordered sets, re-enable
+ * auto-negotiation in the TXCW register and disable forced link in the
+ * Device Control register in an attempt to auto-negotiate with our link
+ * partner.
+ */
+ else if (((hw->media_type == e1000_media_type_fiber) ||
+ (hw->media_type == e1000_media_type_internal_serdes)) &&
+ (ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) {
+ DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\n");
+ E1000_WRITE_REG(hw, TXCW, hw->txcw);
+ E1000_WRITE_REG(hw, CTRL, (ctrl & ~E1000_CTRL_SLU));
+
+ hw->serdes_link_down = FALSE;
+ }
+ /* If we force link for non-auto-negotiation switch, check link status
+ * based on MAC synchronization for internal serdes media type.
+ */
+ else if ((hw->media_type == e1000_media_type_internal_serdes) &&
+ !(E1000_TXCW_ANE & E1000_READ_REG(hw, TXCW))) {
+ /* SYNCH bit and IV bit are sticky. */
+ udelay(10);
+ if (E1000_RXCW_SYNCH & E1000_READ_REG(hw, RXCW)) {
+ if (!(rxcw & E1000_RXCW_IV)) {
+ hw->serdes_link_down = FALSE;
+ DEBUGOUT("SERDES: Link is up.\n");
+ }
+ } else {
+ hw->serdes_link_down = TRUE;
+ DEBUGOUT("SERDES: Link is down.\n");
+ }
+ }
+ if ((hw->media_type == e1000_media_type_internal_serdes) &&
+ (E1000_TXCW_ANE & E1000_READ_REG(hw, TXCW))) {
+ hw->serdes_link_down = !(E1000_STATUS_LU & E1000_READ_REG(hw, STATUS));
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Detects the current speed and duplex settings of the hardware.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * speed - Speed of the connection
+ * duplex - Duplex setting of the connection
+ *****************************************************************************/
+int32_t
+e1000_get_speed_and_duplex(struct e1000_hw *hw,
+ uint16_t *speed,
+ uint16_t *duplex)
+{
+ uint32_t status;
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_get_speed_and_duplex");
+
+ if (hw->mac_type >= e1000_82543) {
+ status = E1000_READ_REG(hw, STATUS);
+ if (status & E1000_STATUS_SPEED_1000) {
+ *speed = SPEED_1000;
+ DEBUGOUT("1000 Mbs, ");
+ } else if (status & E1000_STATUS_SPEED_100) {
+ *speed = SPEED_100;
+ DEBUGOUT("100 Mbs, ");
+ } else {
+ *speed = SPEED_10;
+ DEBUGOUT("10 Mbs, ");
+ }
+
+ if (status & E1000_STATUS_FD) {
+ *duplex = FULL_DUPLEX;
+ DEBUGOUT("Full Duplex\n");
+ } else {
+ *duplex = HALF_DUPLEX;
+ DEBUGOUT(" Half Duplex\n");
+ }
+ } else {
+ DEBUGOUT("1000 Mbs, Full Duplex\n");
+ *speed = SPEED_1000;
+ *duplex = FULL_DUPLEX;
+ }
+
+ /* IGP01 PHY may advertise full duplex operation after speed downgrade even
+ * if it is operating at half duplex. Here we set the duplex settings to
+ * match the duplex in the link partner's capabilities.
+ */
+ if (hw->phy_type == e1000_phy_igp && hw->speed_downgraded) {
+ ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ if (!(phy_data & NWAY_ER_LP_NWAY_CAPS))
+ *duplex = HALF_DUPLEX;
+ else {
+ ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data);
+ if (ret_val)
+ return ret_val;
+ if ((*speed == SPEED_100 && !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) ||
+ (*speed == SPEED_10 && !(phy_data & NWAY_LPAR_10T_FD_CAPS)))
+ *duplex = HALF_DUPLEX;
+ }
+ }
+
+ if ((hw->mac_type == e1000_80003es2lan) &&
+ (hw->media_type == e1000_media_type_copper)) {
+ if (*speed == SPEED_1000)
+ ret_val = e1000_configure_kmrn_for_1000(hw);
+ else
+ ret_val = e1000_configure_kmrn_for_10_100(hw, *duplex);
+ if (ret_val)
+ return ret_val;
+ }
+
+ if ((hw->phy_type == e1000_phy_igp_3) && (*speed == SPEED_1000)) {
+ ret_val = e1000_kumeran_lock_loss_workaround(hw);
+ if (ret_val)
+ return ret_val;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Blocks until autoneg completes or times out (~4.5 seconds)
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int32_t
+e1000_wait_autoneg(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t i;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_wait_autoneg");
+ DEBUGOUT("Waiting for Auto-Neg to complete.\n");
+
+ /* We will wait for autoneg to complete or 4.5 seconds to expire. */
+ for (i = PHY_AUTO_NEG_TIME; i > 0; i--) {
+ /* Read the MII Status Register and wait for Auto-Neg
+ * Complete bit to be set.
+ */
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+ if (phy_data & MII_SR_AUTONEG_COMPLETE) {
+ return E1000_SUCCESS;
+ }
+ msleep(100);
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Raises the Management Data Clock
+*
+* hw - Struct containing variables accessed by shared code
+* ctrl - Device control register's current value
+******************************************************************************/
+static void
+e1000_raise_mdi_clk(struct e1000_hw *hw,
+ uint32_t *ctrl)
+{
+ /* Raise the clock input to the Management Data Clock (by setting the MDC
+ * bit), and then delay 10 microseconds.
+ */
+ E1000_WRITE_REG(hw, CTRL, (*ctrl | E1000_CTRL_MDC));
+ E1000_WRITE_FLUSH(hw);
+ udelay(10);
+}
+
+/******************************************************************************
+* Lowers the Management Data Clock
+*
+* hw - Struct containing variables accessed by shared code
+* ctrl - Device control register's current value
+******************************************************************************/
+static void
+e1000_lower_mdi_clk(struct e1000_hw *hw,
+ uint32_t *ctrl)
+{
+ /* Lower the clock input to the Management Data Clock (by clearing the MDC
+ * bit), and then delay 10 microseconds.
+ */
+ E1000_WRITE_REG(hw, CTRL, (*ctrl & ~E1000_CTRL_MDC));
+ E1000_WRITE_FLUSH(hw);
+ udelay(10);
+}
+
+/******************************************************************************
+* Shifts data bits out to the PHY
+*
+* hw - Struct containing variables accessed by shared code
+* data - Data to send out to the PHY
+* count - Number of bits to shift out
+*
+* Bits are shifted out in MSB to LSB order.
+******************************************************************************/
+static void
+e1000_shift_out_mdi_bits(struct e1000_hw *hw,
+ uint32_t data,
+ uint16_t count)
+{
+ uint32_t ctrl;
+ uint32_t mask;
+
+ /* We need to shift "count" number of bits out to the PHY. So, the value
+ * in the "data" parameter will be shifted out to the PHY one bit at a
+ * time. In order to do this, "data" must be broken down into bits.
+ */
+ mask = 0x01;
+ mask <<= (count - 1);
+
+ ctrl = E1000_READ_REG(hw, CTRL);
+
+ /* Set MDIO_DIR and MDC_DIR direction bits to be used as output pins. */
+ ctrl |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR);
+
+ while (mask) {
+ /* A "1" is shifted out to the PHY by setting the MDIO bit to "1" and
+ * then raising and lowering the Management Data Clock. A "0" is
+ * shifted out to the PHY by setting the MDIO bit to "0" and then
+ * raising and lowering the clock.
+ */
+ if (data & mask)
+ ctrl |= E1000_CTRL_MDIO;
+ else
+ ctrl &= ~E1000_CTRL_MDIO;
+
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ E1000_WRITE_FLUSH(hw);
+
+ udelay(10);
+
+ e1000_raise_mdi_clk(hw, &ctrl);
+ e1000_lower_mdi_clk(hw, &ctrl);
+
+ mask = mask >> 1;
+ }
+}
+
+/******************************************************************************
+* Shifts data bits in from the PHY
+*
+* hw - Struct containing variables accessed by shared code
+*
+* Bits are shifted in in MSB to LSB order.
+******************************************************************************/
+static uint16_t
+e1000_shift_in_mdi_bits(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ uint16_t data = 0;
+ uint8_t i;
+
+ /* In order to read a register from the PHY, we need to shift in a total
+ * of 18 bits from the PHY. The first two bit (turnaround) times are used
+ * to avoid contention on the MDIO pin when a read operation is performed.
+ * These two bits are ignored by us and thrown away. Bits are "shifted in"
+ * by raising the input to the Management Data Clock (setting the MDC bit),
+ * and then reading the value of the MDIO bit.
+ */
+ ctrl = E1000_READ_REG(hw, CTRL);
+
+ /* Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as input. */
+ ctrl &= ~E1000_CTRL_MDIO_DIR;
+ ctrl &= ~E1000_CTRL_MDIO;
+
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ E1000_WRITE_FLUSH(hw);
+
+ /* Raise and Lower the clock before reading in the data. This accounts for
+ * the turnaround bits. The first clock occurred when we clocked out the
+ * last bit of the Register Address.
+ */
+ e1000_raise_mdi_clk(hw, &ctrl);
+ e1000_lower_mdi_clk(hw, &ctrl);
+
+ for (data = 0, i = 0; i < 16; i++) {
+ data = data << 1;
+ e1000_raise_mdi_clk(hw, &ctrl);
+ ctrl = E1000_READ_REG(hw, CTRL);
+ /* Check to see if we shifted in a "1". */
+ if (ctrl & E1000_CTRL_MDIO)
+ data |= 1;
+ e1000_lower_mdi_clk(hw, &ctrl);
+ }
+
+ e1000_raise_mdi_clk(hw, &ctrl);
+ e1000_lower_mdi_clk(hw, &ctrl);
+
+ return data;
+}
+
+static int32_t
+e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask)
+{
+ uint32_t swfw_sync = 0;
+ uint32_t swmask = mask;
+ uint32_t fwmask = mask << 16;
+ int32_t timeout = 200;
+
+ DEBUGFUNC("e1000_swfw_sync_acquire");
+
+ if (hw->swfwhw_semaphore_present)
+ return e1000_get_software_flag(hw);
+
+ if (!hw->swfw_sync_present)
+ return e1000_get_hw_eeprom_semaphore(hw);
+
+ while (timeout) {
+ if (e1000_get_hw_eeprom_semaphore(hw))
+ return -E1000_ERR_SWFW_SYNC;
+
+ swfw_sync = E1000_READ_REG(hw, SW_FW_SYNC);
+ if (!(swfw_sync & (fwmask | swmask))) {
+ break;
+ }
+
+ /* firmware currently using resource (fwmask) */
+ /* or other software thread currently using resource (swmask) */
+ e1000_put_hw_eeprom_semaphore(hw);
+ mdelay(5);
+ timeout--;
+ }
+
+ if (!timeout) {
+ DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n");
+ return -E1000_ERR_SWFW_SYNC;
+ }
+
+ swfw_sync |= swmask;
+ E1000_WRITE_REG(hw, SW_FW_SYNC, swfw_sync);
+
+ e1000_put_hw_eeprom_semaphore(hw);
+ return E1000_SUCCESS;
+}
+
+static void
+e1000_swfw_sync_release(struct e1000_hw *hw, uint16_t mask)
+{
+ uint32_t swfw_sync;
+ uint32_t swmask = mask;
+
+ DEBUGFUNC("e1000_swfw_sync_release");
+
+ if (hw->swfwhw_semaphore_present) {
+ e1000_release_software_flag(hw);
+ return;
+ }
+
+ if (!hw->swfw_sync_present) {
+ e1000_put_hw_eeprom_semaphore(hw);
+ return;
+ }
+
+ /* if (e1000_get_hw_eeprom_semaphore(hw))
+ * return -E1000_ERR_SWFW_SYNC; */
+ while (e1000_get_hw_eeprom_semaphore(hw) != E1000_SUCCESS);
+ /* empty */
+
+ swfw_sync = E1000_READ_REG(hw, SW_FW_SYNC);
+ swfw_sync &= ~swmask;
+ E1000_WRITE_REG(hw, SW_FW_SYNC, swfw_sync);
+
+ e1000_put_hw_eeprom_semaphore(hw);
+}
+
+/*****************************************************************************
+* Reads the value from a PHY register, if the value is on a specific non zero
+* page, sets the page first.
+* hw - Struct containing variables accessed by shared code
+* reg_addr - address of the PHY register to read
+******************************************************************************/
+int32_t
+e1000_read_phy_reg(struct e1000_hw *hw,
+ uint32_t reg_addr,
+ uint16_t *phy_data)
+{
+ uint32_t ret_val;
+ uint16_t swfw;
+
+ DEBUGFUNC("e1000_read_phy_reg");
+
+ if ((hw->mac_type == e1000_80003es2lan) &&
+ (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
+ swfw = E1000_SWFW_PHY1_SM;
+ } else {
+ swfw = E1000_SWFW_PHY0_SM;
+ }
+ if (e1000_swfw_sync_acquire(hw, swfw))
+ return -E1000_ERR_SWFW_SYNC;
+
+ if ((hw->phy_type == e1000_phy_igp ||
+ hw->phy_type == e1000_phy_igp_3 ||
+ hw->phy_type == e1000_phy_igp_2) &&
+ (reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
+ ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
+ (uint16_t)reg_addr);
+ if (ret_val) {
+ e1000_swfw_sync_release(hw, swfw);
+ return ret_val;
+ }
+ } else if (hw->phy_type == e1000_phy_gg82563) {
+ if (((reg_addr & MAX_PHY_REG_ADDRESS) > MAX_PHY_MULTI_PAGE_REG) ||
+ (hw->mac_type == e1000_80003es2lan)) {
+ /* Select Configuration Page */
+ if ((reg_addr & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) {
+ ret_val = e1000_write_phy_reg_ex(hw, GG82563_PHY_PAGE_SELECT,
+ (uint16_t)((uint16_t)reg_addr >> GG82563_PAGE_SHIFT));
+ } else {
+ /* Use Alternative Page Select register to access
+ * registers 30 and 31
+ */
+ ret_val = e1000_write_phy_reg_ex(hw,
+ GG82563_PHY_PAGE_SELECT_ALT,
+ (uint16_t)((uint16_t)reg_addr >> GG82563_PAGE_SHIFT));
+ }
+
+ if (ret_val) {
+ e1000_swfw_sync_release(hw, swfw);
+ return ret_val;
+ }
+ }
+ }
+
+ ret_val = e1000_read_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr,
+ phy_data);
+
+ e1000_swfw_sync_release(hw, swfw);
+ return ret_val;
+}
+
+static int32_t
+e1000_read_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr,
+ uint16_t *phy_data)
+{
+ uint32_t i;
+ uint32_t mdic = 0;
+ const uint32_t phy_addr = 1;
+
+ DEBUGFUNC("e1000_read_phy_reg_ex");
+
+ if (reg_addr > MAX_PHY_REG_ADDRESS) {
+ DEBUGOUT1("PHY Address %ld is out of range\n", reg_addr);
+ return -E1000_ERR_PARAM;
+ }
+
+ if (hw->mac_type > e1000_82543) {
+ /* Set up Op-code, Phy Address, and register address in the MDI
+ * Control register. The MAC will take care of interfacing with the
+ * PHY to retrieve the desired data.
+ */
+ mdic = ((reg_addr << E1000_MDIC_REG_SHIFT) |
+ (phy_addr << E1000_MDIC_PHY_SHIFT) |
+ (E1000_MDIC_OP_READ));
+
+ E1000_WRITE_REG(hw, MDIC, mdic);
+
+ /* Poll the ready bit to see if the MDI read completed */
+ for (i = 0; i < 64; i++) {
+ udelay(50);
+ mdic = E1000_READ_REG(hw, MDIC);
+ if (mdic & E1000_MDIC_READY) break;
+ }
+ if (!(mdic & E1000_MDIC_READY)) {
+ DEBUGOUT("MDI Read did not complete\n");
+ return -E1000_ERR_PHY;
+ }
+ if (mdic & E1000_MDIC_ERROR) {
+ DEBUGOUT("MDI Error\n");
+ return -E1000_ERR_PHY;
+ }
+ *phy_data = (uint16_t) mdic;
+ } else {
+ /* We must first send a preamble through the MDIO pin to signal the
+ * beginning of an MII instruction. This is done by sending 32
+ * consecutive "1" bits.
+ */
+ e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE);
+
+ /* Now combine the next few fields that are required for a read
+ * operation. We use this method instead of calling the
+ * e1000_shift_out_mdi_bits routine five different times. The format of
+ * a MII read instruction consists of a shift out of 14 bits and is
+ * defined as follows:
+ * <Preamble><SOF><Op Code><Phy Addr><Reg Addr>
+ * followed by a shift in of 18 bits. This first two bits shifted in
+ * are TurnAround bits used to avoid contention on the MDIO pin when a
+ * READ operation is performed. These two bits are thrown away
+ * followed by a shift in of 16 bits which contains the desired data.
+ */
+ mdic = ((reg_addr) | (phy_addr << 5) |
+ (PHY_OP_READ << 10) | (PHY_SOF << 12));
+
+ e1000_shift_out_mdi_bits(hw, mdic, 14);
+
+ /* Now that we've shifted out the read command to the MII, we need to
+ * "shift in" the 16-bit value (18 total bits) of the requested PHY
+ * register address.
+ */
+ *phy_data = e1000_shift_in_mdi_bits(hw);
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Writes a value to a PHY register
+*
+* hw - Struct containing variables accessed by shared code
+* reg_addr - address of the PHY register to write
+* data - data to write to the PHY
+******************************************************************************/
+int32_t
+e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr,
+ uint16_t phy_data)
+{
+ uint32_t ret_val;
+ uint16_t swfw;
+
+ DEBUGFUNC("e1000_write_phy_reg");
+
+ if ((hw->mac_type == e1000_80003es2lan) &&
+ (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
+ swfw = E1000_SWFW_PHY1_SM;
+ } else {
+ swfw = E1000_SWFW_PHY0_SM;
+ }
+ if (e1000_swfw_sync_acquire(hw, swfw))
+ return -E1000_ERR_SWFW_SYNC;
+
+ if ((hw->phy_type == e1000_phy_igp ||
+ hw->phy_type == e1000_phy_igp_3 ||
+ hw->phy_type == e1000_phy_igp_2) &&
+ (reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
+ ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
+ (uint16_t)reg_addr);
+ if (ret_val) {
+ e1000_swfw_sync_release(hw, swfw);
+ return ret_val;
+ }
+ } else if (hw->phy_type == e1000_phy_gg82563) {
+ if (((reg_addr & MAX_PHY_REG_ADDRESS) > MAX_PHY_MULTI_PAGE_REG) ||
+ (hw->mac_type == e1000_80003es2lan)) {
+ /* Select Configuration Page */
+ if ((reg_addr & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) {
+ ret_val = e1000_write_phy_reg_ex(hw, GG82563_PHY_PAGE_SELECT,
+ (uint16_t)((uint16_t)reg_addr >> GG82563_PAGE_SHIFT));
+ } else {
+ /* Use Alternative Page Select register to access
+ * registers 30 and 31
+ */
+ ret_val = e1000_write_phy_reg_ex(hw,
+ GG82563_PHY_PAGE_SELECT_ALT,
+ (uint16_t)((uint16_t)reg_addr >> GG82563_PAGE_SHIFT));
+ }
+
+ if (ret_val) {
+ e1000_swfw_sync_release(hw, swfw);
+ return ret_val;
+ }
+ }
+ }
+
+ ret_val = e1000_write_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr,
+ phy_data);
+
+ e1000_swfw_sync_release(hw, swfw);
+ return ret_val;
+}
+
+static int32_t
+e1000_write_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr,
+ uint16_t phy_data)
+{
+ uint32_t i;
+ uint32_t mdic = 0;
+ const uint32_t phy_addr = 1;
+
+ DEBUGFUNC("e1000_write_phy_reg_ex");
+
+ if (reg_addr > MAX_PHY_REG_ADDRESS) {
+ DEBUGOUT1("PHY Address %ld is out of range\n", reg_addr);
+ return -E1000_ERR_PARAM;
+ }
+
+ if (hw->mac_type > e1000_82543) {
+ /* Set up Op-code, Phy Address, register address, and data intended
+ * for the PHY register in the MDI Control register. The MAC will take
+ * care of interfacing with the PHY to send the desired data.
+ */
+ mdic = (((uint32_t) phy_data) |
+ (reg_addr << E1000_MDIC_REG_SHIFT) |
+ (phy_addr << E1000_MDIC_PHY_SHIFT) |
+ (E1000_MDIC_OP_WRITE));
+
+ E1000_WRITE_REG(hw, MDIC, mdic);
+
+ /* Poll the ready bit to see if the MDI read completed */
+ for (i = 0; i < 641; i++) {
+ udelay(5);
+ mdic = E1000_READ_REG(hw, MDIC);
+ if (mdic & E1000_MDIC_READY) break;
+ }
+ if (!(mdic & E1000_MDIC_READY)) {
+ DEBUGOUT("MDI Write did not complete\n");
+ return -E1000_ERR_PHY;
+ }
+ } else {
+ /* We'll need to use the SW defined pins to shift the write command
+ * out to the PHY. We first send a preamble to the PHY to signal the
+ * beginning of the MII instruction. This is done by sending 32
+ * consecutive "1" bits.
+ */
+ e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE);
+
+ /* Now combine the remaining required fields that will indicate a
+ * write operation. We use this method instead of calling the
+ * e1000_shift_out_mdi_bits routine for each field in the command. The
+ * format of a MII write instruction is as follows:
+ * <Preamble><SOF><Op Code><Phy Addr><Reg Addr><Turnaround><Data>.
+ */
+ mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) |
+ (PHY_OP_WRITE << 12) | (PHY_SOF << 14));
+ mdic <<= 16;
+ mdic |= (uint32_t) phy_data;
+
+ e1000_shift_out_mdi_bits(hw, mdic, 32);
+ }
+
+ return E1000_SUCCESS;
+}
+
+static int32_t
+e1000_read_kmrn_reg(struct e1000_hw *hw,
+ uint32_t reg_addr,
+ uint16_t *data)
+{
+ uint32_t reg_val;
+ uint16_t swfw;
+ DEBUGFUNC("e1000_read_kmrn_reg");
+
+ if ((hw->mac_type == e1000_80003es2lan) &&
+ (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
+ swfw = E1000_SWFW_PHY1_SM;
+ } else {
+ swfw = E1000_SWFW_PHY0_SM;
+ }
+ if (e1000_swfw_sync_acquire(hw, swfw))
+ return -E1000_ERR_SWFW_SYNC;
+
+ /* Write register address */
+ reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) &
+ E1000_KUMCTRLSTA_OFFSET) |
+ E1000_KUMCTRLSTA_REN;
+ E1000_WRITE_REG(hw, KUMCTRLSTA, reg_val);
+ udelay(2);
+
+ /* Read the data returned */
+ reg_val = E1000_READ_REG(hw, KUMCTRLSTA);
+ *data = (uint16_t)reg_val;
+
+ e1000_swfw_sync_release(hw, swfw);
+ return E1000_SUCCESS;
+}
+
+static int32_t
+e1000_write_kmrn_reg(struct e1000_hw *hw,
+ uint32_t reg_addr,
+ uint16_t data)
+{
+ uint32_t reg_val;
+ uint16_t swfw;
+ DEBUGFUNC("e1000_write_kmrn_reg");
+
+ if ((hw->mac_type == e1000_80003es2lan) &&
+ (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
+ swfw = E1000_SWFW_PHY1_SM;
+ } else {
+ swfw = E1000_SWFW_PHY0_SM;
+ }
+ if (e1000_swfw_sync_acquire(hw, swfw))
+ return -E1000_ERR_SWFW_SYNC;
+
+ reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) &
+ E1000_KUMCTRLSTA_OFFSET) | data;
+ E1000_WRITE_REG(hw, KUMCTRLSTA, reg_val);
+ udelay(2);
+
+ e1000_swfw_sync_release(hw, swfw);
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Returns the PHY to the power-on reset state
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+int32_t
+e1000_phy_hw_reset(struct e1000_hw *hw)
+{
+ uint32_t ctrl, ctrl_ext;
+ uint32_t led_ctrl;
+ int32_t ret_val;
+ uint16_t swfw;
+
+ DEBUGFUNC("e1000_phy_hw_reset");
+
+ /* In the case of the phy reset being blocked, it's not an error, we
+ * simply return success without performing the reset. */
+ ret_val = e1000_check_phy_reset_block(hw);
+ if (ret_val)
+ return E1000_SUCCESS;
+
+ DEBUGOUT("Resetting Phy...\n");
+
+ if (hw->mac_type > e1000_82543) {
+ if ((hw->mac_type == e1000_80003es2lan) &&
+ (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) {
+ swfw = E1000_SWFW_PHY1_SM;
+ } else {
+ swfw = E1000_SWFW_PHY0_SM;
+ }
+ if (e1000_swfw_sync_acquire(hw, swfw)) {
+ DEBUGOUT("Unable to acquire swfw sync\n");
+ return -E1000_ERR_SWFW_SYNC;
+ }
+ /* Read the device control register and assert the E1000_CTRL_PHY_RST
+ * bit. Then, take it out of reset.
+ * For pre-e1000_82571 hardware, we delay for 10ms between the assert
+ * and deassert. For e1000_82571 hardware and later, we instead delay
+ * for 50us between and 10ms after the deassertion.
+ */
+ ctrl = E1000_READ_REG(hw, CTRL);
+ E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PHY_RST);
+ E1000_WRITE_FLUSH(hw);
+
+ if (hw->mac_type < e1000_82571)
+ msleep(10);
+ else
+ udelay(100);
+
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ E1000_WRITE_FLUSH(hw);
+
+ if (hw->mac_type >= e1000_82571)
+ mdelay(10);
+
+ e1000_swfw_sync_release(hw, swfw);
+ } else {
+ /* Read the Extended Device Control Register, assert the PHY_RESET_DIR
+ * bit to put the PHY into reset. Then, take it out of reset.
+ */
+ ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ ctrl_ext |= E1000_CTRL_EXT_SDP4_DIR;
+ ctrl_ext &= ~E1000_CTRL_EXT_SDP4_DATA;
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ E1000_WRITE_FLUSH(hw);
+ msleep(10);
+ ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA;
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ E1000_WRITE_FLUSH(hw);
+ }
+ udelay(150);
+
+ if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+ /* Configure activity LED after PHY reset */
+ led_ctrl = E1000_READ_REG(hw, LEDCTL);
+ led_ctrl &= IGP_ACTIVITY_LED_MASK;
+ led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
+ E1000_WRITE_REG(hw, LEDCTL, led_ctrl);
+ }
+
+ /* Wait for FW to finish PHY configuration. */
+ ret_val = e1000_get_phy_cfg_done(hw);
+ if (ret_val != E1000_SUCCESS)
+ return ret_val;
+ e1000_release_software_semaphore(hw);
+
+ if ((hw->mac_type == e1000_ich8lan) && (hw->phy_type == e1000_phy_igp_3))
+ ret_val = e1000_init_lcd_from_nvm(hw);
+
+ return ret_val;
+}
+
+/******************************************************************************
+* Resets the PHY
+*
+* hw - Struct containing variables accessed by shared code
+*
+* Sets bit 15 of the MII Control register
+******************************************************************************/
+int32_t
+e1000_phy_reset(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_phy_reset");
+
+ /* In the case of the phy reset being blocked, it's not an error, we
+ * simply return success without performing the reset. */
+ ret_val = e1000_check_phy_reset_block(hw);
+ if (ret_val)
+ return E1000_SUCCESS;
+
+ switch (hw->phy_type) {
+ case e1000_phy_igp:
+ case e1000_phy_igp_2:
+ case e1000_phy_igp_3:
+ case e1000_phy_ife:
+ ret_val = e1000_phy_hw_reset(hw);
+ if (ret_val)
+ return ret_val;
+ break;
+ default:
+ ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data |= MII_CR_RESET;
+ ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ udelay(1);
+ break;
+ }
+
+ if (hw->phy_type == e1000_phy_igp || hw->phy_type == e1000_phy_igp_2)
+ e1000_phy_init_script(hw);
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Work-around for 82566 power-down: on D3 entry-
+* 1) disable gigabit link
+* 2) write VR power-down enable
+* 3) read it back
+* if successful continue, else issue LCD reset and repeat
+*
+* hw - struct containing variables accessed by shared code
+******************************************************************************/
+void
+e1000_phy_powerdown_workaround(struct e1000_hw *hw)
+{
+ int32_t reg;
+ uint16_t phy_data;
+ int32_t retry = 0;
+
+ DEBUGFUNC("e1000_phy_powerdown_workaround");
+
+ if (hw->phy_type != e1000_phy_igp_3)
+ return;
+
+ do {
+ /* Disable link */
+ reg = E1000_READ_REG(hw, PHY_CTRL);
+ E1000_WRITE_REG(hw, PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE |
+ E1000_PHY_CTRL_NOND0A_GBE_DISABLE);
+
+ /* Write VR power-down enable - bits 9:8 should be 10b */
+ e1000_read_phy_reg(hw, IGP3_VR_CTRL, &phy_data);
+ phy_data |= (1 << 9);
+ phy_data &= ~(1 << 8);
+ e1000_write_phy_reg(hw, IGP3_VR_CTRL, phy_data);
+
+ /* Read it back and test */
+ e1000_read_phy_reg(hw, IGP3_VR_CTRL, &phy_data);
+ if (((phy_data & IGP3_VR_CTRL_MODE_MASK) == IGP3_VR_CTRL_MODE_SHUT) || retry)
+ break;
+
+ /* Issue PHY reset and repeat at most one more time */
+ reg = E1000_READ_REG(hw, CTRL);
+ E1000_WRITE_REG(hw, CTRL, reg | E1000_CTRL_PHY_RST);
+ retry++;
+ } while (retry);
+
+ return;
+
+}
+
+/******************************************************************************
+* Work-around for 82566 Kumeran PCS lock loss:
+* On link status change (i.e. PCI reset, speed change) and link is up and
+* speed is gigabit-
+* 0) if workaround is optionally disabled do nothing
+* 1) wait 1ms for Kumeran link to come up
+* 2) check Kumeran Diagnostic register PCS lock loss bit
+* 3) if not set the link is locked (all is good), otherwise...
+* 4) reset the PHY
+* 5) repeat up to 10 times
+* Note: this is only called for IGP3 copper when speed is 1gb.
+*
+* hw - struct containing variables accessed by shared code
+******************************************************************************/
+static int32_t
+e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ int32_t reg;
+ int32_t cnt;
+ uint16_t phy_data;
+
+ if (hw->kmrn_lock_loss_workaround_disabled)
+ return E1000_SUCCESS;
+
+ /* Make sure link is up before proceeding. If not just return.
+ * Attempting this while link is negotiating fouled up link
+ * stability */
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
+
+ if (phy_data & MII_SR_LINK_STATUS) {
+ for (cnt = 0; cnt < 10; cnt++) {
+ /* read once to clear */
+ ret_val = e1000_read_phy_reg(hw, IGP3_KMRN_DIAG, &phy_data);
+ if (ret_val)
+ return ret_val;
+ /* and again to get new status */
+ ret_val = e1000_read_phy_reg(hw, IGP3_KMRN_DIAG, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* check for PCS lock */
+ if (!(phy_data & IGP3_KMRN_DIAG_PCS_LOCK_LOSS))
+ return E1000_SUCCESS;
+
+ /* Issue PHY reset */
+ e1000_phy_hw_reset(hw);
+ mdelay(5);
+ }
+ /* Disable GigE link negotiation */
+ reg = E1000_READ_REG(hw, PHY_CTRL);
+ E1000_WRITE_REG(hw, PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE |
+ E1000_PHY_CTRL_NOND0A_GBE_DISABLE);
+
+ /* unable to acquire PCS lock */
+ return E1000_ERR_PHY;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Probes the expected PHY address for known PHY IDs
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int32_t
+e1000_detect_gig_phy(struct e1000_hw *hw)
+{
+ int32_t phy_init_status, ret_val;
+ uint16_t phy_id_high, phy_id_low;
+ boolean_t match = FALSE;
+
+ DEBUGFUNC("e1000_detect_gig_phy");
+
+ if (hw->phy_id != 0)
+ return E1000_SUCCESS;
+
+ /* The 82571 firmware may still be configuring the PHY. In this
+ * case, we cannot access the PHY until the configuration is done. So
+ * we explicitly set the PHY values. */
+ if (hw->mac_type == e1000_82571 ||
+ hw->mac_type == e1000_82572) {
+ hw->phy_id = IGP01E1000_I_PHY_ID;
+ hw->phy_type = e1000_phy_igp_2;
+ return E1000_SUCCESS;
+ }
+
+ /* ESB-2 PHY reads require e1000_phy_gg82563 to be set because of a work-
+ * around that forces PHY page 0 to be set or the reads fail. The rest of
+ * the code in this routine uses e1000_read_phy_reg to read the PHY ID.
+ * So for ESB-2 we need to have this set so our reads won't fail. If the
+ * attached PHY is not a e1000_phy_gg82563, the routines below will figure
+ * this out as well. */
+ if (hw->mac_type == e1000_80003es2lan)
+ hw->phy_type = e1000_phy_gg82563;
+
+ /* Read the PHY ID Registers to identify which PHY is onboard. */
+ ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high);
+ if (ret_val)
+ return ret_val;
+
+ hw->phy_id = (uint32_t) (phy_id_high << 16);
+ udelay(20);
+ ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low);
+ if (ret_val)
+ return ret_val;
+
+ hw->phy_id |= (uint32_t) (phy_id_low & PHY_REVISION_MASK);
+ hw->phy_revision = (uint32_t) phy_id_low & ~PHY_REVISION_MASK;
+
+ switch (hw->mac_type) {
+ case e1000_82543:
+ if (hw->phy_id == M88E1000_E_PHY_ID) match = TRUE;
+ break;
+ case e1000_82544:
+ if (hw->phy_id == M88E1000_I_PHY_ID) match = TRUE;
+ break;
+ case e1000_82540:
+ case e1000_82545:
+ case e1000_82545_rev_3:
+ case e1000_82546:
+ case e1000_82546_rev_3:
+ if (hw->phy_id == M88E1011_I_PHY_ID) match = TRUE;
+ break;
+ case e1000_82541:
+ case e1000_82541_rev_2:
+ case e1000_82547:
+ case e1000_82547_rev_2:
+ if (hw->phy_id == IGP01E1000_I_PHY_ID) match = TRUE;
+ break;
+ case e1000_82573:
+ if (hw->phy_id == M88E1111_I_PHY_ID) match = TRUE;
+ break;
+ case e1000_80003es2lan:
+ if (hw->phy_id == GG82563_E_PHY_ID) match = TRUE;
+ break;
+ case e1000_ich8lan:
+ if (hw->phy_id == IGP03E1000_E_PHY_ID) match = TRUE;
+ if (hw->phy_id == IFE_E_PHY_ID) match = TRUE;
+ if (hw->phy_id == IFE_PLUS_E_PHY_ID) match = TRUE;
+ if (hw->phy_id == IFE_C_E_PHY_ID) match = TRUE;
+ break;
+ default:
+ DEBUGOUT1("Invalid MAC type %d\n", hw->mac_type);
+ return -E1000_ERR_CONFIG;
+ }
+ phy_init_status = e1000_set_phy_type(hw);
+
+ if ((match) && (phy_init_status == E1000_SUCCESS)) {
+ DEBUGOUT1("PHY ID %#08lx detected\n", hw->phy_id);
+ return E1000_SUCCESS;
+ }
+ DEBUGOUT1("Invalid PHY ID %#08lx\n", hw->phy_id);
+ return -E1000_ERR_PHY;
+}
+
+/******************************************************************************
+* Resets the PHY's DSP
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int32_t
+e1000_phy_reset_dsp(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ DEBUGFUNC("e1000_phy_reset_dsp");
+
+ do {
+ if (hw->phy_type != e1000_phy_gg82563) {
+ ret_val = e1000_write_phy_reg(hw, 29, 0x001d);
+ if (ret_val) break;
+ }
+ ret_val = e1000_write_phy_reg(hw, 30, 0x00c1);
+ if (ret_val) break;
+ ret_val = e1000_write_phy_reg(hw, 30, 0x0000);
+ if (ret_val) break;
+ ret_val = E1000_SUCCESS;
+ } while (0);
+
+ return ret_val;
+}
+
+/******************************************************************************
+* Get PHY information from various PHY registers for igp PHY only.
+*
+* hw - Struct containing variables accessed by shared code
+* phy_info - PHY information structure
+******************************************************************************/
+static int32_t
+e1000_phy_igp_get_info(struct e1000_hw *hw,
+ struct e1000_phy_info *phy_info)
+{
+ int32_t ret_val;
+ uint16_t phy_data, min_length, max_length, average;
+ e1000_rev_polarity polarity;
+
+ DEBUGFUNC("e1000_phy_igp_get_info");
+
+ /* The downshift status is checked only once, after link is established,
+ * and it stored in the hw->speed_downgraded parameter. */
+ phy_info->downshift = (e1000_downshift)hw->speed_downgraded;
+
+ /* IGP01E1000 does not need to support it. */
+ phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_normal;
+
+ /* IGP01E1000 always correct polarity reversal */
+ phy_info->polarity_correction = e1000_polarity_reversal_enabled;
+
+ /* Check polarity status */
+ ret_val = e1000_check_polarity(hw, &polarity);
+ if (ret_val)
+ return ret_val;
+
+ phy_info->cable_polarity = polarity;
+
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_info->mdix_mode = (e1000_auto_x_mode)((phy_data & IGP01E1000_PSSR_MDIX) >>
+ IGP01E1000_PSSR_MDIX_SHIFT);
+
+ if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
+ IGP01E1000_PSSR_SPEED_1000MBPS) {
+ /* Local/Remote Receiver Information are only valid at 1000 Mbps */
+ ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_info->local_rx = ((phy_data & SR_1000T_LOCAL_RX_STATUS) >>
+ SR_1000T_LOCAL_RX_STATUS_SHIFT) ?
+ e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
+ phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >>
+ SR_1000T_REMOTE_RX_STATUS_SHIFT) ?
+ e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
+
+ /* Get cable length */
+ ret_val = e1000_get_cable_length(hw, &min_length, &max_length);
+ if (ret_val)
+ return ret_val;
+
+ /* Translate to old method */
+ average = (max_length + min_length) / 2;
+
+ if (average <= e1000_igp_cable_length_50)
+ phy_info->cable_length = e1000_cable_length_50;
+ else if (average <= e1000_igp_cable_length_80)
+ phy_info->cable_length = e1000_cable_length_50_80;
+ else if (average <= e1000_igp_cable_length_110)
+ phy_info->cable_length = e1000_cable_length_80_110;
+ else if (average <= e1000_igp_cable_length_140)
+ phy_info->cable_length = e1000_cable_length_110_140;
+ else
+ phy_info->cable_length = e1000_cable_length_140;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Get PHY information from various PHY registers for ife PHY only.
+*
+* hw - Struct containing variables accessed by shared code
+* phy_info - PHY information structure
+******************************************************************************/
+static int32_t
+e1000_phy_ife_get_info(struct e1000_hw *hw,
+ struct e1000_phy_info *phy_info)
+{
+ int32_t ret_val;
+ uint16_t phy_data;
+ e1000_rev_polarity polarity;
+
+ DEBUGFUNC("e1000_phy_ife_get_info");
+
+ phy_info->downshift = (e1000_downshift)hw->speed_downgraded;
+ phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_normal;
+
+ ret_val = e1000_read_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, &phy_data);
+ if (ret_val)
+ return ret_val;
+ phy_info->polarity_correction =
+ ((phy_data & IFE_PSC_AUTO_POLARITY_DISABLE) >>
+ IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT) ?
+ e1000_polarity_reversal_disabled : e1000_polarity_reversal_enabled;
+
+ if (phy_info->polarity_correction == e1000_polarity_reversal_enabled) {
+ ret_val = e1000_check_polarity(hw, &polarity);
+ if (ret_val)
+ return ret_val;
+ } else {
+ /* Polarity is forced. */
+ polarity = ((phy_data & IFE_PSC_FORCE_POLARITY) >>
+ IFE_PSC_FORCE_POLARITY_SHIFT) ?
+ e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
+ }
+ phy_info->cable_polarity = polarity;
+
+ ret_val = e1000_read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_info->mdix_mode = (e1000_auto_x_mode)
+ ((phy_data & (IFE_PMC_AUTO_MDIX | IFE_PMC_FORCE_MDIX)) >>
+ IFE_PMC_MDIX_MODE_SHIFT);
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Get PHY information from various PHY registers fot m88 PHY only.
+*
+* hw - Struct containing variables accessed by shared code
+* phy_info - PHY information structure
+******************************************************************************/
+static int32_t
+e1000_phy_m88_get_info(struct e1000_hw *hw,
+ struct e1000_phy_info *phy_info)
+{
+ int32_t ret_val;
+ uint16_t phy_data;
+ e1000_rev_polarity polarity;
+
+ DEBUGFUNC("e1000_phy_m88_get_info");
+
+ /* The downshift status is checked only once, after link is established,
+ * and it stored in the hw->speed_downgraded parameter. */
+ phy_info->downshift = (e1000_downshift)hw->speed_downgraded;
+
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_info->extended_10bt_distance =
+ ((phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >>
+ M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT) ?
+ e1000_10bt_ext_dist_enable_lower : e1000_10bt_ext_dist_enable_normal;
+
+ phy_info->polarity_correction =
+ ((phy_data & M88E1000_PSCR_POLARITY_REVERSAL) >>
+ M88E1000_PSCR_POLARITY_REVERSAL_SHIFT) ?
+ e1000_polarity_reversal_disabled : e1000_polarity_reversal_enabled;
+
+ /* Check polarity status */
+ ret_val = e1000_check_polarity(hw, &polarity);
+ if (ret_val)
+ return ret_val;
+ phy_info->cable_polarity = polarity;
+
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_info->mdix_mode = (e1000_auto_x_mode)((phy_data & M88E1000_PSSR_MDIX) >>
+ M88E1000_PSSR_MDIX_SHIFT);
+
+ if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) {
+ /* Cable Length Estimation and Local/Remote Receiver Information
+ * are only valid at 1000 Mbps.
+ */
+ if (hw->phy_type != e1000_phy_gg82563) {
+ phy_info->cable_length = (e1000_cable_length)((phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
+ M88E1000_PSSR_CABLE_LENGTH_SHIFT);
+ } else {
+ ret_val = e1000_read_phy_reg(hw, GG82563_PHY_DSP_DISTANCE,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_info->cable_length = (e1000_cable_length)(phy_data & GG82563_DSPD_CABLE_LENGTH);
+ }
+
+ ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_info->local_rx = ((phy_data & SR_1000T_LOCAL_RX_STATUS) >>
+ SR_1000T_LOCAL_RX_STATUS_SHIFT) ?
+ e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
+ phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >>
+ SR_1000T_REMOTE_RX_STATUS_SHIFT) ?
+ e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
+
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Get PHY information from various PHY registers
+*
+* hw - Struct containing variables accessed by shared code
+* phy_info - PHY information structure
+******************************************************************************/
+int32_t
+e1000_phy_get_info(struct e1000_hw *hw,
+ struct e1000_phy_info *phy_info)
+{
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_phy_get_info");
+
+ phy_info->cable_length = e1000_cable_length_undefined;
+ phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_undefined;
+ phy_info->cable_polarity = e1000_rev_polarity_undefined;
+ phy_info->downshift = e1000_downshift_undefined;
+ phy_info->polarity_correction = e1000_polarity_reversal_undefined;
+ phy_info->mdix_mode = e1000_auto_x_mode_undefined;
+ phy_info->local_rx = e1000_1000t_rx_status_undefined;
+ phy_info->remote_rx = e1000_1000t_rx_status_undefined;
+
+ if (hw->media_type != e1000_media_type_copper) {
+ DEBUGOUT("PHY info is only valid for copper media\n");
+ return -E1000_ERR_CONFIG;
+ }
+
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ if ((phy_data & MII_SR_LINK_STATUS) != MII_SR_LINK_STATUS) {
+ DEBUGOUT("PHY info is only valid if link is up\n");
+ return -E1000_ERR_CONFIG;
+ }
+
+ if (hw->phy_type == e1000_phy_igp ||
+ hw->phy_type == e1000_phy_igp_3 ||
+ hw->phy_type == e1000_phy_igp_2)
+ return e1000_phy_igp_get_info(hw, phy_info);
+ else if (hw->phy_type == e1000_phy_ife)
+ return e1000_phy_ife_get_info(hw, phy_info);
+ else
+ return e1000_phy_m88_get_info(hw, phy_info);
+}
+
+int32_t
+e1000_validate_mdi_setting(struct e1000_hw *hw)
+{
+ DEBUGFUNC("e1000_validate_mdi_settings");
+
+ if (!hw->autoneg && (hw->mdix == 0 || hw->mdix == 3)) {
+ DEBUGOUT("Invalid MDI setting detected\n");
+ hw->mdix = 1;
+ return -E1000_ERR_CONFIG;
+ }
+ return E1000_SUCCESS;
+}
+
+
+/******************************************************************************
+ * Sets up eeprom variables in the hw struct. Must be called after mac_type
+ * is configured. Additionally, if this is ICH8, the flash controller GbE
+ * registers must be mapped, or this will crash.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+int32_t
+e1000_init_eeprom_params(struct e1000_hw *hw)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd = E1000_READ_REG(hw, EECD);
+ int32_t ret_val = E1000_SUCCESS;
+ uint16_t eeprom_size;
+
+ DEBUGFUNC("e1000_init_eeprom_params");
+
+ switch (hw->mac_type) {
+ case e1000_82542_rev2_0:
+ case e1000_82542_rev2_1:
+ case e1000_82543:
+ case e1000_82544:
+ eeprom->type = e1000_eeprom_microwire;
+ eeprom->word_size = 64;
+ eeprom->opcode_bits = 3;
+ eeprom->address_bits = 6;
+ eeprom->delay_usec = 50;
+ eeprom->use_eerd = FALSE;
+ eeprom->use_eewr = FALSE;
+ break;
+ case e1000_82540:
+ case e1000_82545:
+ case e1000_82545_rev_3:
+ case e1000_82546:
+ case e1000_82546_rev_3:
+ eeprom->type = e1000_eeprom_microwire;
+ eeprom->opcode_bits = 3;
+ eeprom->delay_usec = 50;
+ if (eecd & E1000_EECD_SIZE) {
+ eeprom->word_size = 256;
+ eeprom->address_bits = 8;
+ } else {
+ eeprom->word_size = 64;
+ eeprom->address_bits = 6;
+ }
+ eeprom->use_eerd = FALSE;
+ eeprom->use_eewr = FALSE;
+ break;
+ case e1000_82541:
+ case e1000_82541_rev_2:
+ case e1000_82547:
+ case e1000_82547_rev_2:
+ if (eecd & E1000_EECD_TYPE) {
+ eeprom->type = e1000_eeprom_spi;
+ eeprom->opcode_bits = 8;
+ eeprom->delay_usec = 1;
+ if (eecd & E1000_EECD_ADDR_BITS) {
+ eeprom->page_size = 32;
+ eeprom->address_bits = 16;
+ } else {
+ eeprom->page_size = 8;
+ eeprom->address_bits = 8;
+ }
+ } else {
+ eeprom->type = e1000_eeprom_microwire;
+ eeprom->opcode_bits = 3;
+ eeprom->delay_usec = 50;
+ if (eecd & E1000_EECD_ADDR_BITS) {
+ eeprom->word_size = 256;
+ eeprom->address_bits = 8;
+ } else {
+ eeprom->word_size = 64;
+ eeprom->address_bits = 6;
+ }
+ }
+ eeprom->use_eerd = FALSE;
+ eeprom->use_eewr = FALSE;
+ break;
+ case e1000_82571:
+ case e1000_82572:
+ eeprom->type = e1000_eeprom_spi;
+ eeprom->opcode_bits = 8;
+ eeprom->delay_usec = 1;
+ if (eecd & E1000_EECD_ADDR_BITS) {
+ eeprom->page_size = 32;
+ eeprom->address_bits = 16;
+ } else {
+ eeprom->page_size = 8;
+ eeprom->address_bits = 8;
+ }
+ eeprom->use_eerd = FALSE;
+ eeprom->use_eewr = FALSE;
+ break;
+ case e1000_82573:
+ eeprom->type = e1000_eeprom_spi;
+ eeprom->opcode_bits = 8;
+ eeprom->delay_usec = 1;
+ if (eecd & E1000_EECD_ADDR_BITS) {
+ eeprom->page_size = 32;
+ eeprom->address_bits = 16;
+ } else {
+ eeprom->page_size = 8;
+ eeprom->address_bits = 8;
+ }
+ eeprom->use_eerd = TRUE;
+ eeprom->use_eewr = TRUE;
+ if (e1000_is_onboard_nvm_eeprom(hw) == FALSE) {
+ eeprom->type = e1000_eeprom_flash;
+ eeprom->word_size = 2048;
+
+ /* Ensure that the Autonomous FLASH update bit is cleared due to
+ * Flash update issue on parts which use a FLASH for NVM. */
+ eecd &= ~E1000_EECD_AUPDEN;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ }
+ break;
+ case e1000_80003es2lan:
+ eeprom->type = e1000_eeprom_spi;
+ eeprom->opcode_bits = 8;
+ eeprom->delay_usec = 1;
+ if (eecd & E1000_EECD_ADDR_BITS) {
+ eeprom->page_size = 32;
+ eeprom->address_bits = 16;
+ } else {
+ eeprom->page_size = 8;
+ eeprom->address_bits = 8;
+ }
+ eeprom->use_eerd = TRUE;
+ eeprom->use_eewr = FALSE;
+ break;
+ case e1000_ich8lan:
+ {
+ int32_t i = 0;
+ uint32_t flash_size = E1000_READ_ICH_FLASH_REG(hw, ICH_FLASH_GFPREG);
+
+ eeprom->type = e1000_eeprom_ich8;
+ eeprom->use_eerd = FALSE;
+ eeprom->use_eewr = FALSE;
+ eeprom->word_size = E1000_SHADOW_RAM_WORDS;
+
+ /* Zero the shadow RAM structure. But don't load it from NVM
+ * so as to save time for driver init */
+ if (hw->eeprom_shadow_ram != NULL) {
+ for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) {
+ hw->eeprom_shadow_ram[i].modified = FALSE;
+ hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF;
+ }
+ }
+
+ hw->flash_base_addr = (flash_size & ICH_GFPREG_BASE_MASK) *
+ ICH_FLASH_SECTOR_SIZE;
+
+ hw->flash_bank_size = ((flash_size >> 16) & ICH_GFPREG_BASE_MASK) + 1;
+ hw->flash_bank_size -= (flash_size & ICH_GFPREG_BASE_MASK);
+
+ hw->flash_bank_size *= ICH_FLASH_SECTOR_SIZE;
+
+ hw->flash_bank_size /= 2 * sizeof(uint16_t);
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (eeprom->type == e1000_eeprom_spi) {
+ /* eeprom_size will be an enum [0..8] that maps to eeprom sizes 128B to
+ * 32KB (incremented by powers of 2).
+ */
+ if (hw->mac_type <= e1000_82547_rev_2) {
+ /* Set to default value for initial eeprom read. */
+ eeprom->word_size = 64;
+ ret_val = e1000_read_eeprom(hw, EEPROM_CFG, 1, &eeprom_size);
+ if (ret_val)
+ return ret_val;
+ eeprom_size = (eeprom_size & EEPROM_SIZE_MASK) >> EEPROM_SIZE_SHIFT;
+ /* 256B eeprom size was not supported in earlier hardware, so we
+ * bump eeprom_size up one to ensure that "1" (which maps to 256B)
+ * is never the result used in the shifting logic below. */
+ if (eeprom_size)
+ eeprom_size++;
+ } else {
+ eeprom_size = (uint16_t)((eecd & E1000_EECD_SIZE_EX_MASK) >>
+ E1000_EECD_SIZE_EX_SHIFT);
+ }
+
+ eeprom->word_size = 1 << (eeprom_size + EEPROM_WORD_SIZE_SHIFT);
+ }
+ return ret_val;
+}
+
+/******************************************************************************
+ * Raises the EEPROM's clock input.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * eecd - EECD's current value
+ *****************************************************************************/
+static void
+e1000_raise_ee_clk(struct e1000_hw *hw,
+ uint32_t *eecd)
+{
+ /* Raise the clock input to the EEPROM (by setting the SK bit), and then
+ * wait <delay> microseconds.
+ */
+ *eecd = *eecd | E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, *eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+}
+
+/******************************************************************************
+ * Lowers the EEPROM's clock input.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * eecd - EECD's current value
+ *****************************************************************************/
+static void
+e1000_lower_ee_clk(struct e1000_hw *hw,
+ uint32_t *eecd)
+{
+ /* Lower the clock input to the EEPROM (by clearing the SK bit), and then
+ * wait 50 microseconds.
+ */
+ *eecd = *eecd & ~E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, *eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+}
+
+/******************************************************************************
+ * Shift data bits out to the EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * data - data to send to the EEPROM
+ * count - number of bits to shift out
+ *****************************************************************************/
+static void
+e1000_shift_out_ee_bits(struct e1000_hw *hw,
+ uint16_t data,
+ uint16_t count)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd;
+ uint32_t mask;
+
+ /* We need to shift "count" bits out to the EEPROM. So, value in the
+ * "data" parameter will be shifted out to the EEPROM one bit at a time.
+ * In order to do this, "data" must be broken down into bits.
+ */
+ mask = 0x01 << (count - 1);
+ eecd = E1000_READ_REG(hw, EECD);
+ if (eeprom->type == e1000_eeprom_microwire) {
+ eecd &= ~E1000_EECD_DO;
+ } else if (eeprom->type == e1000_eeprom_spi) {
+ eecd |= E1000_EECD_DO;
+ }
+ do {
+ /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1",
+ * and then raising and then lowering the clock (the SK bit controls
+ * the clock input to the EEPROM). A "0" is shifted out to the EEPROM
+ * by setting "DI" to "0" and then raising and then lowering the clock.
+ */
+ eecd &= ~E1000_EECD_DI;
+
+ if (data & mask)
+ eecd |= E1000_EECD_DI;
+
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+
+ udelay(eeprom->delay_usec);
+
+ e1000_raise_ee_clk(hw, &eecd);
+ e1000_lower_ee_clk(hw, &eecd);
+
+ mask = mask >> 1;
+
+ } while (mask);
+
+ /* We leave the "DI" bit set to "0" when we leave this routine. */
+ eecd &= ~E1000_EECD_DI;
+ E1000_WRITE_REG(hw, EECD, eecd);
+}
+
+/******************************************************************************
+ * Shift data bits in from the EEPROM
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static uint16_t
+e1000_shift_in_ee_bits(struct e1000_hw *hw,
+ uint16_t count)
+{
+ uint32_t eecd;
+ uint32_t i;
+ uint16_t data;
+
+ /* In order to read a register from the EEPROM, we need to shift 'count'
+ * bits in from the EEPROM. Bits are "shifted in" by raising the clock
+ * input to the EEPROM (setting the SK bit), and then reading the value of
+ * the "DO" bit. During this "shifting in" process the "DI" bit should
+ * always be clear.
+ */
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ eecd &= ~(E1000_EECD_DO | E1000_EECD_DI);
+ data = 0;
+
+ for (i = 0; i < count; i++) {
+ data = data << 1;
+ e1000_raise_ee_clk(hw, &eecd);
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ eecd &= ~(E1000_EECD_DI);
+ if (eecd & E1000_EECD_DO)
+ data |= 1;
+
+ e1000_lower_ee_clk(hw, &eecd);
+ }
+
+ return data;
+}
+
+/******************************************************************************
+ * Prepares EEPROM for access
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This
+ * function should be called before issuing a command to the EEPROM.
+ *****************************************************************************/
+static int32_t
+e1000_acquire_eeprom(struct e1000_hw *hw)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd, i=0;
+
+ DEBUGFUNC("e1000_acquire_eeprom");
+
+ if (e1000_swfw_sync_acquire(hw, E1000_SWFW_EEP_SM))
+ return -E1000_ERR_SWFW_SYNC;
+ eecd = E1000_READ_REG(hw, EECD);
+
+ if (hw->mac_type != e1000_82573) {
+ /* Request EEPROM Access */
+ if (hw->mac_type > e1000_82544) {
+ eecd |= E1000_EECD_REQ;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ eecd = E1000_READ_REG(hw, EECD);
+ while ((!(eecd & E1000_EECD_GNT)) &&
+ (i < E1000_EEPROM_GRANT_ATTEMPTS)) {
+ i++;
+ udelay(5);
+ eecd = E1000_READ_REG(hw, EECD);
+ }
+ if (!(eecd & E1000_EECD_GNT)) {
+ eecd &= ~E1000_EECD_REQ;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ DEBUGOUT("Could not acquire EEPROM grant\n");
+ e1000_swfw_sync_release(hw, E1000_SWFW_EEP_SM);
+ return -E1000_ERR_EEPROM;
+ }
+ }
+ }
+
+ /* Setup EEPROM for Read/Write */
+
+ if (eeprom->type == e1000_eeprom_microwire) {
+ /* Clear SK and DI */
+ eecd &= ~(E1000_EECD_DI | E1000_EECD_SK);
+ E1000_WRITE_REG(hw, EECD, eecd);
+
+ /* Set CS */
+ eecd |= E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ } else if (eeprom->type == e1000_eeprom_spi) {
+ /* Clear SK and CS */
+ eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
+ E1000_WRITE_REG(hw, EECD, eecd);
+ udelay(1);
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Returns EEPROM to a "standby" state
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_standby_eeprom(struct e1000_hw *hw)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd;
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ if (eeprom->type == e1000_eeprom_microwire) {
+ eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+
+ /* Clock high */
+ eecd |= E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+
+ /* Select EEPROM */
+ eecd |= E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+
+ /* Clock low */
+ eecd &= ~E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+ } else if (eeprom->type == e1000_eeprom_spi) {
+ /* Toggle CS to flush commands */
+ eecd |= E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+ eecd &= ~E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+ }
+}
+
+/******************************************************************************
+ * Terminates a command by inverting the EEPROM's chip select pin
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_release_eeprom(struct e1000_hw *hw)
+{
+ uint32_t eecd;
+
+ DEBUGFUNC("e1000_release_eeprom");
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ if (hw->eeprom.type == e1000_eeprom_spi) {
+ eecd |= E1000_EECD_CS; /* Pull CS high */
+ eecd &= ~E1000_EECD_SK; /* Lower SCK */
+
+ E1000_WRITE_REG(hw, EECD, eecd);
+
+ udelay(hw->eeprom.delay_usec);
+ } else if (hw->eeprom.type == e1000_eeprom_microwire) {
+ /* cleanup eeprom */
+
+ /* CS on Microwire is active-high */
+ eecd &= ~(E1000_EECD_CS | E1000_EECD_DI);
+
+ E1000_WRITE_REG(hw, EECD, eecd);
+
+ /* Rising edge of clock */
+ eecd |= E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+
+ /* Falling edge of clock */
+ eecd &= ~E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+ }
+
+ /* Stop requesting EEPROM access */
+ if (hw->mac_type > e1000_82544) {
+ eecd &= ~E1000_EECD_REQ;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ }
+
+ e1000_swfw_sync_release(hw, E1000_SWFW_EEP_SM);
+}
+
+/******************************************************************************
+ * Reads a 16 bit word from the EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int32_t
+e1000_spi_eeprom_ready(struct e1000_hw *hw)
+{
+ uint16_t retry_count = 0;
+ uint8_t spi_stat_reg;
+
+ DEBUGFUNC("e1000_spi_eeprom_ready");
+
+ /* Read "Status Register" repeatedly until the LSB is cleared. The
+ * EEPROM will signal that the command has been completed by clearing
+ * bit 0 of the internal status register. If it's not cleared within
+ * 5 milliseconds, then error out.
+ */
+ retry_count = 0;
+ do {
+ e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI,
+ hw->eeprom.opcode_bits);
+ spi_stat_reg = (uint8_t)e1000_shift_in_ee_bits(hw, 8);
+ if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI))
+ break;
+
+ udelay(5);
+ retry_count += 5;
+
+ e1000_standby_eeprom(hw);
+ } while (retry_count < EEPROM_MAX_RETRY_SPI);
+
+ /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and
+ * only 0-5mSec on 5V devices)
+ */
+ if (retry_count >= EEPROM_MAX_RETRY_SPI) {
+ DEBUGOUT("SPI EEPROM Status error\n");
+ return -E1000_ERR_EEPROM;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Reads a 16 bit word from the EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset of word in the EEPROM to read
+ * data - word read from the EEPROM
+ * words - number of words to read
+ *****************************************************************************/
+int32_t
+e1000_read_eeprom(struct e1000_hw *hw,
+ uint16_t offset,
+ uint16_t words,
+ uint16_t *data)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t i = 0;
+
+ DEBUGFUNC("e1000_read_eeprom");
+
+ /* If eeprom is not yet detected, do so now */
+ if (eeprom->word_size == 0)
+ e1000_init_eeprom_params(hw);
+
+ /* A check for invalid values: offset too large, too many words, and not
+ * enough words.
+ */
+ if ((offset >= eeprom->word_size) || (words > eeprom->word_size - offset) ||
+ (words == 0)) {
+ DEBUGOUT2("\"words\" parameter out of bounds. Words = %d, size = %d\n", offset, eeprom->word_size);
+ return -E1000_ERR_EEPROM;
+ }
+
+ /* EEPROM's that don't use EERD to read require us to bit-bang the SPI
+ * directly. In this case, we need to acquire the EEPROM so that
+ * FW or other port software does not interrupt.
+ */
+ if (e1000_is_onboard_nvm_eeprom(hw) == TRUE &&
+ hw->eeprom.use_eerd == FALSE) {
+ /* Prepare the EEPROM for bit-bang reading */
+ if (e1000_acquire_eeprom(hw) != E1000_SUCCESS)
+ return -E1000_ERR_EEPROM;
+ }
+
+ /* Eerd register EEPROM access requires no eeprom aquire/release */
+ if (eeprom->use_eerd == TRUE)
+ return e1000_read_eeprom_eerd(hw, offset, words, data);
+
+ /* ICH EEPROM access is done via the ICH flash controller */
+ if (eeprom->type == e1000_eeprom_ich8)
+ return e1000_read_eeprom_ich8(hw, offset, words, data);
+
+ /* Set up the SPI or Microwire EEPROM for bit-bang reading. We have
+ * acquired the EEPROM at this point, so any returns should relase it */
+ if (eeprom->type == e1000_eeprom_spi) {
+ uint16_t word_in;
+ uint8_t read_opcode = EEPROM_READ_OPCODE_SPI;
+
+ if (e1000_spi_eeprom_ready(hw)) {
+ e1000_release_eeprom(hw);
+ return -E1000_ERR_EEPROM;
+ }
+
+ e1000_standby_eeprom(hw);
+
+ /* Some SPI eeproms use the 8th address bit embedded in the opcode */
+ if ((eeprom->address_bits == 8) && (offset >= 128))
+ read_opcode |= EEPROM_A8_OPCODE_SPI;
+
+ /* Send the READ command (opcode + addr) */
+ e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits);
+ e1000_shift_out_ee_bits(hw, (uint16_t)(offset*2), eeprom->address_bits);
+
+ /* Read the data. The address of the eeprom internally increments with
+ * each byte (spi) being read, saving on the overhead of eeprom setup
+ * and tear-down. The address counter will roll over if reading beyond
+ * the size of the eeprom, thus allowing the entire memory to be read
+ * starting from any offset. */
+ for (i = 0; i < words; i++) {
+ word_in = e1000_shift_in_ee_bits(hw, 16);
+ data[i] = (word_in >> 8) | (word_in << 8);
+ }
+ } else if (eeprom->type == e1000_eeprom_microwire) {
+ for (i = 0; i < words; i++) {
+ /* Send the READ command (opcode + addr) */
+ e1000_shift_out_ee_bits(hw, EEPROM_READ_OPCODE_MICROWIRE,
+ eeprom->opcode_bits);
+ e1000_shift_out_ee_bits(hw, (uint16_t)(offset + i),
+ eeprom->address_bits);
+
+ /* Read the data. For microwire, each word requires the overhead
+ * of eeprom setup and tear-down. */
+ data[i] = e1000_shift_in_ee_bits(hw, 16);
+ e1000_standby_eeprom(hw);
+ }
+ }
+
+ /* End this read operation */
+ e1000_release_eeprom(hw);
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Reads a 16 bit word from the EEPROM using the EERD register.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset of word in the EEPROM to read
+ * data - word read from the EEPROM
+ * words - number of words to read
+ *****************************************************************************/
+static int32_t
+e1000_read_eeprom_eerd(struct e1000_hw *hw,
+ uint16_t offset,
+ uint16_t words,
+ uint16_t *data)
+{
+ uint32_t i, eerd = 0;
+ int32_t error = 0;
+
+ for (i = 0; i < words; i++) {
+ eerd = ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) +
+ E1000_EEPROM_RW_REG_START;
+
+ E1000_WRITE_REG(hw, EERD, eerd);
+ error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_READ);
+
+ if (error) {
+ break;
+ }
+ data[i] = (E1000_READ_REG(hw, EERD) >> E1000_EEPROM_RW_REG_DATA);
+
+ }
+
+ return error;
+}
+
+/******************************************************************************
+ * Writes a 16 bit word from the EEPROM using the EEWR register.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset of word in the EEPROM to read
+ * data - word read from the EEPROM
+ * words - number of words to read
+ *****************************************************************************/
+static int32_t
+e1000_write_eeprom_eewr(struct e1000_hw *hw,
+ uint16_t offset,
+ uint16_t words,
+ uint16_t *data)
+{
+ uint32_t register_value = 0;
+ uint32_t i = 0;
+ int32_t error = 0;
+
+ if (e1000_swfw_sync_acquire(hw, E1000_SWFW_EEP_SM))
+ return -E1000_ERR_SWFW_SYNC;
+
+ for (i = 0; i < words; i++) {
+ register_value = (data[i] << E1000_EEPROM_RW_REG_DATA) |
+ ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) |
+ E1000_EEPROM_RW_REG_START;
+
+ error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_WRITE);
+ if (error) {
+ break;
+ }
+
+ E1000_WRITE_REG(hw, EEWR, register_value);
+
+ error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_WRITE);
+
+ if (error) {
+ break;
+ }
+ }
+
+ e1000_swfw_sync_release(hw, E1000_SWFW_EEP_SM);
+ return error;
+}
+
+/******************************************************************************
+ * Polls the status bit (bit 1) of the EERD to determine when the read is done.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int32_t
+e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd)
+{
+ uint32_t attempts = 100000;
+ uint32_t i, reg = 0;
+ int32_t done = E1000_ERR_EEPROM;
+
+ for (i = 0; i < attempts; i++) {
+ if (eerd == E1000_EEPROM_POLL_READ)
+ reg = E1000_READ_REG(hw, EERD);
+ else
+ reg = E1000_READ_REG(hw, EEWR);
+
+ if (reg & E1000_EEPROM_RW_REG_DONE) {
+ done = E1000_SUCCESS;
+ break;
+ }
+ udelay(5);
+ }
+
+ return done;
+}
+
+/***************************************************************************
+* Description: Determines if the onboard NVM is FLASH or EEPROM.
+*
+* hw - Struct containing variables accessed by shared code
+****************************************************************************/
+static boolean_t
+e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw)
+{
+ uint32_t eecd = 0;
+
+ DEBUGFUNC("e1000_is_onboard_nvm_eeprom");
+
+ if (hw->mac_type == e1000_ich8lan)
+ return FALSE;
+
+ if (hw->mac_type == e1000_82573) {
+ eecd = E1000_READ_REG(hw, EECD);
+
+ /* Isolate bits 15 & 16 */
+ eecd = ((eecd >> 15) & 0x03);
+
+ /* If both bits are set, device is Flash type */
+ if (eecd == 0x03) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/******************************************************************************
+ * Verifies that the EEPROM has a valid checksum
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Reads the first 64 16 bit words of the EEPROM and sums the values read.
+ * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is
+ * valid.
+ *****************************************************************************/
+int32_t
+e1000_validate_eeprom_checksum(struct e1000_hw *hw)
+{
+ uint16_t checksum = 0;
+ uint16_t i, eeprom_data;
+
+ DEBUGFUNC("e1000_validate_eeprom_checksum");
+
+ if ((hw->mac_type == e1000_82573) &&
+ (e1000_is_onboard_nvm_eeprom(hw) == FALSE)) {
+ /* Check bit 4 of word 10h. If it is 0, firmware is done updating
+ * 10h-12h. Checksum may need to be fixed. */
+ e1000_read_eeprom(hw, 0x10, 1, &eeprom_data);
+ if ((eeprom_data & 0x10) == 0) {
+ /* Read 0x23 and check bit 15. This bit is a 1 when the checksum
+ * has already been fixed. If the checksum is still wrong and this
+ * bit is a 1, we need to return bad checksum. Otherwise, we need
+ * to set this bit to a 1 and update the checksum. */
+ e1000_read_eeprom(hw, 0x23, 1, &eeprom_data);
+ if ((eeprom_data & 0x8000) == 0) {
+ eeprom_data |= 0x8000;
+ e1000_write_eeprom(hw, 0x23, 1, &eeprom_data);
+ e1000_update_eeprom_checksum(hw);
+ }
+ }
+ }
+
+ if (hw->mac_type == e1000_ich8lan) {
+ /* Drivers must allocate the shadow ram structure for the
+ * EEPROM checksum to be updated. Otherwise, this bit as well
+ * as the checksum must both be set correctly for this
+ * validation to pass.
+ */
+ e1000_read_eeprom(hw, 0x19, 1, &eeprom_data);
+ if ((eeprom_data & 0x40) == 0) {
+ eeprom_data |= 0x40;
+ e1000_write_eeprom(hw, 0x19, 1, &eeprom_data);
+ e1000_update_eeprom_checksum(hw);
+ }
+ }
+
+ for (i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) {
+ if (e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) {
+ DEBUGOUT("EEPROM Read Error\n");
+ return -E1000_ERR_EEPROM;
+ }
+ checksum += eeprom_data;
+ }
+
+ if (checksum == (uint16_t) EEPROM_SUM)
+ return E1000_SUCCESS;
+ else {
+ DEBUGOUT("EEPROM Checksum Invalid\n");
+ return -E1000_ERR_EEPROM;
+ }
+}
+
+/******************************************************************************
+ * Calculates the EEPROM checksum and writes it to the EEPROM
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Sums the first 63 16 bit words of the EEPROM. Subtracts the sum from 0xBABA.
+ * Writes the difference to word offset 63 of the EEPROM.
+ *****************************************************************************/
+int32_t
+e1000_update_eeprom_checksum(struct e1000_hw *hw)
+{
+ uint32_t ctrl_ext;
+ uint16_t checksum = 0;
+ uint16_t i, eeprom_data;
+
+ DEBUGFUNC("e1000_update_eeprom_checksum");
+
+ for (i = 0; i < EEPROM_CHECKSUM_REG; i++) {
+ if (e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) {
+ DEBUGOUT("EEPROM Read Error\n");
+ return -E1000_ERR_EEPROM;
+ }
+ checksum += eeprom_data;
+ }
+ checksum = (uint16_t) EEPROM_SUM - checksum;
+ if (e1000_write_eeprom(hw, EEPROM_CHECKSUM_REG, 1, &checksum) < 0) {
+ DEBUGOUT("EEPROM Write Error\n");
+ return -E1000_ERR_EEPROM;
+ } else if (hw->eeprom.type == e1000_eeprom_flash) {
+ e1000_commit_shadow_ram(hw);
+ } else if (hw->eeprom.type == e1000_eeprom_ich8) {
+ e1000_commit_shadow_ram(hw);
+ /* Reload the EEPROM, or else modifications will not appear
+ * until after next adapter reset. */
+ ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ ctrl_ext |= E1000_CTRL_EXT_EE_RST;
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ msleep(10);
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Parent function for writing words to the different EEPROM types.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset within the EEPROM to be written to
+ * words - number of words to write
+ * data - 16 bit word to be written to the EEPROM
+ *
+ * If e1000_update_eeprom_checksum is not called after this function, the
+ * EEPROM will most likely contain an invalid checksum.
+ *****************************************************************************/
+int32_t
+e1000_write_eeprom(struct e1000_hw *hw,
+ uint16_t offset,
+ uint16_t words,
+ uint16_t *data)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ int32_t status = 0;
+
+ DEBUGFUNC("e1000_write_eeprom");
+
+ /* If eeprom is not yet detected, do so now */
+ if (eeprom->word_size == 0)
+ e1000_init_eeprom_params(hw);
+
+ /* A check for invalid values: offset too large, too many words, and not
+ * enough words.
+ */
+ if ((offset >= eeprom->word_size) || (words > eeprom->word_size - offset) ||
+ (words == 0)) {
+ DEBUGOUT("\"words\" parameter out of bounds\n");
+ return -E1000_ERR_EEPROM;
+ }
+
+ /* 82573 writes only through eewr */
+ if (eeprom->use_eewr == TRUE)
+ return e1000_write_eeprom_eewr(hw, offset, words, data);
+
+ if (eeprom->type == e1000_eeprom_ich8)
+ return e1000_write_eeprom_ich8(hw, offset, words, data);
+
+ /* Prepare the EEPROM for writing */
+ if (e1000_acquire_eeprom(hw) != E1000_SUCCESS)
+ return -E1000_ERR_EEPROM;
+
+ if (eeprom->type == e1000_eeprom_microwire) {
+ status = e1000_write_eeprom_microwire(hw, offset, words, data);
+ } else {
+ status = e1000_write_eeprom_spi(hw, offset, words, data);
+ msleep(10);
+ }
+
+ /* Done with writing */
+ e1000_release_eeprom(hw);
+
+ return status;
+}
+
+/******************************************************************************
+ * Writes a 16 bit word to a given offset in an SPI EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset within the EEPROM to be written to
+ * words - number of words to write
+ * data - pointer to array of 8 bit words to be written to the EEPROM
+ *
+ *****************************************************************************/
+static int32_t
+e1000_write_eeprom_spi(struct e1000_hw *hw,
+ uint16_t offset,
+ uint16_t words,
+ uint16_t *data)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint16_t widx = 0;
+
+ DEBUGFUNC("e1000_write_eeprom_spi");
+
+ while (widx < words) {
+ uint8_t write_opcode = EEPROM_WRITE_OPCODE_SPI;
+
+ if (e1000_spi_eeprom_ready(hw)) return -E1000_ERR_EEPROM;
+
+ e1000_standby_eeprom(hw);
+
+ /* Send the WRITE ENABLE command (8 bit opcode ) */
+ e1000_shift_out_ee_bits(hw, EEPROM_WREN_OPCODE_SPI,
+ eeprom->opcode_bits);
+
+ e1000_standby_eeprom(hw);
+
+ /* Some SPI eeproms use the 8th address bit embedded in the opcode */
+ if ((eeprom->address_bits == 8) && (offset >= 128))
+ write_opcode |= EEPROM_A8_OPCODE_SPI;
+
+ /* Send the Write command (8-bit opcode + addr) */
+ e1000_shift_out_ee_bits(hw, write_opcode, eeprom->opcode_bits);
+
+ e1000_shift_out_ee_bits(hw, (uint16_t)((offset + widx)*2),
+ eeprom->address_bits);
+
+ /* Send the data */
+
+ /* Loop to allow for up to whole page write (32 bytes) of eeprom */
+ while (widx < words) {
+ uint16_t word_out = data[widx];
+ word_out = (word_out >> 8) | (word_out << 8);
+ e1000_shift_out_ee_bits(hw, word_out, 16);
+ widx++;
+
+ /* Some larger eeprom sizes are capable of a 32-byte PAGE WRITE
+ * operation, while the smaller eeproms are capable of an 8-byte
+ * PAGE WRITE operation. Break the inner loop to pass new address
+ */
+ if ((((offset + widx)*2) % eeprom->page_size) == 0) {
+ e1000_standby_eeprom(hw);
+ break;
+ }
+ }
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Writes a 16 bit word to a given offset in a Microwire EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset within the EEPROM to be written to
+ * words - number of words to write
+ * data - pointer to array of 16 bit words to be written to the EEPROM
+ *
+ *****************************************************************************/
+static int32_t
+e1000_write_eeprom_microwire(struct e1000_hw *hw,
+ uint16_t offset,
+ uint16_t words,
+ uint16_t *data)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd;
+ uint16_t words_written = 0;
+ uint16_t i = 0;
+
+ DEBUGFUNC("e1000_write_eeprom_microwire");
+
+ /* Send the write enable command to the EEPROM (3-bit opcode plus
+ * 6/8-bit dummy address beginning with 11). It's less work to include
+ * the 11 of the dummy address as part of the opcode than it is to shift
+ * it over the correct number of bits for the address. This puts the
+ * EEPROM into write/erase mode.
+ */
+ e1000_shift_out_ee_bits(hw, EEPROM_EWEN_OPCODE_MICROWIRE,
+ (uint16_t)(eeprom->opcode_bits + 2));
+
+ e1000_shift_out_ee_bits(hw, 0, (uint16_t)(eeprom->address_bits - 2));
+
+ /* Prepare the EEPROM */
+ e1000_standby_eeprom(hw);
+
+ while (words_written < words) {
+ /* Send the Write command (3-bit opcode + addr) */
+ e1000_shift_out_ee_bits(hw, EEPROM_WRITE_OPCODE_MICROWIRE,
+ eeprom->opcode_bits);
+
+ e1000_shift_out_ee_bits(hw, (uint16_t)(offset + words_written),
+ eeprom->address_bits);
+
+ /* Send the data */
+ e1000_shift_out_ee_bits(hw, data[words_written], 16);
+
+ /* Toggle the CS line. This in effect tells the EEPROM to execute
+ * the previous command.
+ */
+ e1000_standby_eeprom(hw);
+
+ /* Read DO repeatedly until it is high (equal to '1'). The EEPROM will
+ * signal that the command has been completed by raising the DO signal.
+ * If DO does not go high in 10 milliseconds, then error out.
+ */
+ for (i = 0; i < 200; i++) {
+ eecd = E1000_READ_REG(hw, EECD);
+ if (eecd & E1000_EECD_DO) break;
+ udelay(50);
+ }
+ if (i == 200) {
+ DEBUGOUT("EEPROM Write did not complete\n");
+ return -E1000_ERR_EEPROM;
+ }
+
+ /* Recover from write */
+ e1000_standby_eeprom(hw);
+
+ words_written++;
+ }
+
+ /* Send the write disable command to the EEPROM (3-bit opcode plus
+ * 6/8-bit dummy address beginning with 10). It's less work to include
+ * the 10 of the dummy address as part of the opcode than it is to shift
+ * it over the correct number of bits for the address. This takes the
+ * EEPROM out of write/erase mode.
+ */
+ e1000_shift_out_ee_bits(hw, EEPROM_EWDS_OPCODE_MICROWIRE,
+ (uint16_t)(eeprom->opcode_bits + 2));
+
+ e1000_shift_out_ee_bits(hw, 0, (uint16_t)(eeprom->address_bits - 2));
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Flushes the cached eeprom to NVM. This is done by saving the modified values
+ * in the eeprom cache and the non modified values in the currently active bank
+ * to the new bank.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset of word in the EEPROM to read
+ * data - word read from the EEPROM
+ * words - number of words to read
+ *****************************************************************************/
+static int32_t
+e1000_commit_shadow_ram(struct e1000_hw *hw)
+{
+ uint32_t attempts = 100000;
+ uint32_t eecd = 0;
+ uint32_t flop = 0;
+ uint32_t i = 0;
+ int32_t error = E1000_SUCCESS;
+ uint32_t old_bank_offset = 0;
+ uint32_t new_bank_offset = 0;
+ uint8_t low_byte = 0;
+ uint8_t high_byte = 0;
+ boolean_t sector_write_failed = FALSE;
+
+ if (hw->mac_type == e1000_82573) {
+ /* The flop register will be used to determine if flash type is STM */
+ flop = E1000_READ_REG(hw, FLOP);
+ for (i=0; i < attempts; i++) {
+ eecd = E1000_READ_REG(hw, EECD);
+ if ((eecd & E1000_EECD_FLUPD) == 0) {
+ break;
+ }
+ udelay(5);
+ }
+
+ if (i == attempts) {
+ return -E1000_ERR_EEPROM;
+ }
+
+ /* If STM opcode located in bits 15:8 of flop, reset firmware */
+ if ((flop & 0xFF00) == E1000_STM_OPCODE) {
+ E1000_WRITE_REG(hw, HICR, E1000_HICR_FW_RESET);
+ }
+
+ /* Perform the flash update */
+ E1000_WRITE_REG(hw, EECD, eecd | E1000_EECD_FLUPD);
+
+ for (i=0; i < attempts; i++) {
+ eecd = E1000_READ_REG(hw, EECD);
+ if ((eecd & E1000_EECD_FLUPD) == 0) {
+ break;
+ }
+ udelay(5);
+ }
+
+ if (i == attempts) {
+ return -E1000_ERR_EEPROM;
+ }
+ }
+
+ if (hw->mac_type == e1000_ich8lan && hw->eeprom_shadow_ram != NULL) {
+ /* We're writing to the opposite bank so if we're on bank 1,
+ * write to bank 0 etc. We also need to erase the segment that
+ * is going to be written */
+ if (!(E1000_READ_REG(hw, EECD) & E1000_EECD_SEC1VAL)) {
+ new_bank_offset = hw->flash_bank_size * 2;
+ old_bank_offset = 0;
+ e1000_erase_ich8_4k_segment(hw, 1);
+ } else {
+ old_bank_offset = hw->flash_bank_size * 2;
+ new_bank_offset = 0;
+ e1000_erase_ich8_4k_segment(hw, 0);
+ }
+
+ sector_write_failed = FALSE;
+ /* Loop for every byte in the shadow RAM,
+ * which is in units of words. */
+ for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) {
+ /* Determine whether to write the value stored
+ * in the other NVM bank or a modified value stored
+ * in the shadow RAM */
+ if (hw->eeprom_shadow_ram[i].modified == TRUE) {
+ low_byte = (uint8_t)hw->eeprom_shadow_ram[i].eeprom_word;
+ udelay(100);
+ error = e1000_verify_write_ich8_byte(hw,
+ (i << 1) + new_bank_offset, low_byte);
+
+ if (error != E1000_SUCCESS)
+ sector_write_failed = TRUE;
+ else {
+ high_byte =
+ (uint8_t)(hw->eeprom_shadow_ram[i].eeprom_word >> 8);
+ udelay(100);
+ }
+ } else {
+ e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset,
+ &low_byte);
+ udelay(100);
+ error = e1000_verify_write_ich8_byte(hw,
+ (i << 1) + new_bank_offset, low_byte);
+
+ if (error != E1000_SUCCESS)
+ sector_write_failed = TRUE;
+ else {
+ e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset + 1,
+ &high_byte);
+ udelay(100);
+ }
+ }
+
+ /* If the write of the low byte was successful, go ahread and
+ * write the high byte while checking to make sure that if it
+ * is the signature byte, then it is handled properly */
+ if (sector_write_failed == FALSE) {
+ /* If the word is 0x13, then make sure the signature bits
+ * (15:14) are 11b until the commit has completed.
+ * This will allow us to write 10b which indicates the
+ * signature is valid. We want to do this after the write
+ * has completed so that we don't mark the segment valid
+ * while the write is still in progress */
+ if (i == E1000_ICH_NVM_SIG_WORD)
+ high_byte = E1000_ICH_NVM_SIG_MASK | high_byte;
+
+ error = e1000_verify_write_ich8_byte(hw,
+ (i << 1) + new_bank_offset + 1, high_byte);
+ if (error != E1000_SUCCESS)
+ sector_write_failed = TRUE;
+
+ } else {
+ /* If the write failed then break from the loop and
+ * return an error */
+ break;
+ }
+ }
+
+ /* Don't bother writing the segment valid bits if sector
+ * programming failed. */
+ if (sector_write_failed == FALSE) {
+ /* Finally validate the new segment by setting bit 15:14
+ * to 10b in word 0x13 , this can be done without an
+ * erase as well since these bits are 11 to start with
+ * and we need to change bit 14 to 0b */
+ e1000_read_ich8_byte(hw,
+ E1000_ICH_NVM_SIG_WORD * 2 + 1 + new_bank_offset,
+ &high_byte);
+ high_byte &= 0xBF;
+ error = e1000_verify_write_ich8_byte(hw,
+ E1000_ICH_NVM_SIG_WORD * 2 + 1 + new_bank_offset, high_byte);
+ /* And invalidate the previously valid segment by setting
+ * its signature word (0x13) high_byte to 0b. This can be
+ * done without an erase because flash erase sets all bits
+ * to 1's. We can write 1's to 0's without an erase */
+ if (error == E1000_SUCCESS) {
+ error = e1000_verify_write_ich8_byte(hw,
+ E1000_ICH_NVM_SIG_WORD * 2 + 1 + old_bank_offset, 0);
+ }
+
+ /* Clear the now not used entry in the cache */
+ for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) {
+ hw->eeprom_shadow_ram[i].modified = FALSE;
+ hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF;
+ }
+ }
+ }
+
+ return error;
+}
+
+/******************************************************************************
+ * Reads the adapter's MAC address from the EEPROM and inverts the LSB for the
+ * second function of dual function devices
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+int32_t
+e1000_read_mac_addr(struct e1000_hw * hw)
+{
+ uint16_t offset;
+ uint16_t eeprom_data, i;
+
+ DEBUGFUNC("e1000_read_mac_addr");
+
+ for (i = 0; i < NODE_ADDRESS_SIZE; i += 2) {
+ offset = i >> 1;
+ if (e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) {
+ DEBUGOUT("EEPROM Read Error\n");
+ return -E1000_ERR_EEPROM;
+ }
+ hw->perm_mac_addr[i] = (uint8_t) (eeprom_data & 0x00FF);
+ hw->perm_mac_addr[i+1] = (uint8_t) (eeprom_data >> 8);
+ }
+
+ switch (hw->mac_type) {
+ default:
+ break;
+ case e1000_82546:
+ case e1000_82546_rev_3:
+ case e1000_82571:
+ case e1000_80003es2lan:
+ if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)
+ hw->perm_mac_addr[5] ^= 0x01;
+ break;
+ }
+
+ for (i = 0; i < NODE_ADDRESS_SIZE; i++)
+ hw->mac_addr[i] = hw->perm_mac_addr[i];
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Initializes receive address filters.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Places the MAC address in receive address register 0 and clears the rest
+ * of the receive addresss registers. Clears the multicast table. Assumes
+ * the receiver is in reset when the routine is called.
+ *****************************************************************************/
+static void
+e1000_init_rx_addrs(struct e1000_hw *hw)
+{
+ uint32_t i;
+ uint32_t rar_num;
+
+ DEBUGFUNC("e1000_init_rx_addrs");
+
+ /* Setup the receive address. */
+ DEBUGOUT("Programming MAC Address into RAR[0]\n");
+
+ e1000_rar_set(hw, hw->mac_addr, 0);
+
+ rar_num = E1000_RAR_ENTRIES;
+
+ /* Reserve a spot for the Locally Administered Address to work around
+ * an 82571 issue in which a reset on one port will reload the MAC on
+ * the other port. */
+ if ((hw->mac_type == e1000_82571) && (hw->laa_is_present == TRUE))
+ rar_num -= 1;
+ if (hw->mac_type == e1000_ich8lan)
+ rar_num = E1000_RAR_ENTRIES_ICH8LAN;
+
+ /* Zero out the other 15 receive addresses. */
+ DEBUGOUT("Clearing RAR[1-15]\n");
+ for (i = 1; i < rar_num; i++) {
+ E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0);
+ E1000_WRITE_FLUSH(hw);
+ E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0);
+ E1000_WRITE_FLUSH(hw);
+ }
+}
+
+/******************************************************************************
+ * Hashes an address to determine its location in the multicast table
+ *
+ * hw - Struct containing variables accessed by shared code
+ * mc_addr - the multicast address to hash
+ *****************************************************************************/
+uint32_t
+e1000_hash_mc_addr(struct e1000_hw *hw,
+ uint8_t *mc_addr)
+{
+ uint32_t hash_value = 0;
+
+ /* The portion of the address that is used for the hash table is
+ * determined by the mc_filter_type setting.
+ */
+ switch (hw->mc_filter_type) {
+ /* [0] [1] [2] [3] [4] [5]
+ * 01 AA 00 12 34 56
+ * LSB MSB
+ */
+ case 0:
+ if (hw->mac_type == e1000_ich8lan) {
+ /* [47:38] i.e. 0x158 for above example address */
+ hash_value = ((mc_addr[4] >> 6) | (((uint16_t) mc_addr[5]) << 2));
+ } else {
+ /* [47:36] i.e. 0x563 for above example address */
+ hash_value = ((mc_addr[4] >> 4) | (((uint16_t) mc_addr[5]) << 4));
+ }
+ break;
+ case 1:
+ if (hw->mac_type == e1000_ich8lan) {
+ /* [46:37] i.e. 0x2B1 for above example address */
+ hash_value = ((mc_addr[4] >> 5) | (((uint16_t) mc_addr[5]) << 3));
+ } else {
+ /* [46:35] i.e. 0xAC6 for above example address */
+ hash_value = ((mc_addr[4] >> 3) | (((uint16_t) mc_addr[5]) << 5));
+ }
+ break;
+ case 2:
+ if (hw->mac_type == e1000_ich8lan) {
+ /*[45:36] i.e. 0x163 for above example address */
+ hash_value = ((mc_addr[4] >> 4) | (((uint16_t) mc_addr[5]) << 4));
+ } else {
+ /* [45:34] i.e. 0x5D8 for above example address */
+ hash_value = ((mc_addr[4] >> 2) | (((uint16_t) mc_addr[5]) << 6));
+ }
+ break;
+ case 3:
+ if (hw->mac_type == e1000_ich8lan) {
+ /* [43:34] i.e. 0x18D for above example address */
+ hash_value = ((mc_addr[4] >> 2) | (((uint16_t) mc_addr[5]) << 6));
+ } else {
+ /* [43:32] i.e. 0x634 for above example address */
+ hash_value = ((mc_addr[4]) | (((uint16_t) mc_addr[5]) << 8));
+ }
+ break;
+ }
+
+ hash_value &= 0xFFF;
+ if (hw->mac_type == e1000_ich8lan)
+ hash_value &= 0x3FF;
+
+ return hash_value;
+}
+
+/******************************************************************************
+ * Sets the bit in the multicast table corresponding to the hash value.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * hash_value - Multicast address hash value
+ *****************************************************************************/
+void
+e1000_mta_set(struct e1000_hw *hw,
+ uint32_t hash_value)
+{
+ uint32_t hash_bit, hash_reg;
+ uint32_t mta;
+ uint32_t temp;
+
+ /* The MTA is a register array of 128 32-bit registers.
+ * It is treated like an array of 4096 bits. We want to set
+ * bit BitArray[hash_value]. So we figure out what register
+ * the bit is in, read it, OR in the new bit, then write
+ * back the new value. The register is determined by the
+ * upper 7 bits of the hash value and the bit within that
+ * register are determined by the lower 5 bits of the value.
+ */
+ hash_reg = (hash_value >> 5) & 0x7F;
+ if (hw->mac_type == e1000_ich8lan)
+ hash_reg &= 0x1F;
+
+ hash_bit = hash_value & 0x1F;
+
+ mta = E1000_READ_REG_ARRAY(hw, MTA, hash_reg);
+
+ mta |= (1 << hash_bit);
+
+ /* If we are on an 82544 and we are trying to write an odd offset
+ * in the MTA, save off the previous entry before writing and
+ * restore the old value after writing.
+ */
+ if ((hw->mac_type == e1000_82544) && ((hash_reg & 0x1) == 1)) {
+ temp = E1000_READ_REG_ARRAY(hw, MTA, (hash_reg - 1));
+ E1000_WRITE_REG_ARRAY(hw, MTA, hash_reg, mta);
+ E1000_WRITE_FLUSH(hw);
+ E1000_WRITE_REG_ARRAY(hw, MTA, (hash_reg - 1), temp);
+ E1000_WRITE_FLUSH(hw);
+ } else {
+ E1000_WRITE_REG_ARRAY(hw, MTA, hash_reg, mta);
+ E1000_WRITE_FLUSH(hw);
+ }
+}
+
+/******************************************************************************
+ * Puts an ethernet address into a receive address register.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * addr - Address to put into receive address register
+ * index - Receive address register to write
+ *****************************************************************************/
+void
+e1000_rar_set(struct e1000_hw *hw,
+ uint8_t *addr,
+ uint32_t index)
+{
+ uint32_t rar_low, rar_high;
+
+ /* HW expects these in little endian so we reverse the byte order
+ * from network order (big endian) to little endian
+ */
+ rar_low = ((uint32_t) addr[0] |
+ ((uint32_t) addr[1] << 8) |
+ ((uint32_t) addr[2] << 16) | ((uint32_t) addr[3] << 24));
+ rar_high = ((uint32_t) addr[4] | ((uint32_t) addr[5] << 8));
+
+ /* Disable Rx and flush all Rx frames before enabling RSS to avoid Rx
+ * unit hang.
+ *
+ * Description:
+ * If there are any Rx frames queued up or otherwise present in the HW
+ * before RSS is enabled, and then we enable RSS, the HW Rx unit will
+ * hang. To work around this issue, we have to disable receives and
+ * flush out all Rx frames before we enable RSS. To do so, we modify we
+ * redirect all Rx traffic to manageability and then reset the HW.
+ * This flushes away Rx frames, and (since the redirections to
+ * manageability persists across resets) keeps new ones from coming in
+ * while we work. Then, we clear the Address Valid AV bit for all MAC
+ * addresses and undo the re-direction to manageability.
+ * Now, frames are coming in again, but the MAC won't accept them, so
+ * far so good. We now proceed to initialize RSS (if necessary) and
+ * configure the Rx unit. Last, we re-enable the AV bits and continue
+ * on our merry way.
+ */
+ switch (hw->mac_type) {
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_80003es2lan:
+ if (hw->leave_av_bit_off == TRUE)
+ break;
+ default:
+ /* Indicate to hardware the Address is Valid. */
+ rar_high |= E1000_RAH_AV;
+ break;
+ }
+
+ E1000_WRITE_REG_ARRAY(hw, RA, (index << 1), rar_low);
+ E1000_WRITE_FLUSH(hw);
+ E1000_WRITE_REG_ARRAY(hw, RA, ((index << 1) + 1), rar_high);
+ E1000_WRITE_FLUSH(hw);
+}
+
+/******************************************************************************
+ * Writes a value to the specified offset in the VLAN filter table.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - Offset in VLAN filer table to write
+ * value - Value to write into VLAN filter table
+ *****************************************************************************/
+void
+e1000_write_vfta(struct e1000_hw *hw,
+ uint32_t offset,
+ uint32_t value)
+{
+ uint32_t temp;
+
+ if (hw->mac_type == e1000_ich8lan)
+ return;
+
+ if ((hw->mac_type == e1000_82544) && ((offset & 0x1) == 1)) {
+ temp = E1000_READ_REG_ARRAY(hw, VFTA, (offset - 1));
+ E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value);
+ E1000_WRITE_FLUSH(hw);
+ E1000_WRITE_REG_ARRAY(hw, VFTA, (offset - 1), temp);
+ E1000_WRITE_FLUSH(hw);
+ } else {
+ E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value);
+ E1000_WRITE_FLUSH(hw);
+ }
+}
+
+/******************************************************************************
+ * Clears the VLAN filer table
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_clear_vfta(struct e1000_hw *hw)
+{
+ uint32_t offset;
+ uint32_t vfta_value = 0;
+ uint32_t vfta_offset = 0;
+ uint32_t vfta_bit_in_reg = 0;
+
+ if (hw->mac_type == e1000_ich8lan)
+ return;
+
+ if (hw->mac_type == e1000_82573) {
+ if (hw->mng_cookie.vlan_id != 0) {
+ /* The VFTA is a 4096b bit-field, each identifying a single VLAN
+ * ID. The following operations determine which 32b entry
+ * (i.e. offset) into the array we want to set the VLAN ID
+ * (i.e. bit) of the manageability unit. */
+ vfta_offset = (hw->mng_cookie.vlan_id >>
+ E1000_VFTA_ENTRY_SHIFT) &
+ E1000_VFTA_ENTRY_MASK;
+ vfta_bit_in_reg = 1 << (hw->mng_cookie.vlan_id &
+ E1000_VFTA_ENTRY_BIT_SHIFT_MASK);
+ }
+ }
+ for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) {
+ /* If the offset we want to clear is the same offset of the
+ * manageability VLAN ID, then clear all bits except that of the
+ * manageability unit */
+ vfta_value = (offset == vfta_offset) ? vfta_bit_in_reg : 0;
+ E1000_WRITE_REG_ARRAY(hw, VFTA, offset, vfta_value);
+ E1000_WRITE_FLUSH(hw);
+ }
+}
+
+static int32_t
+e1000_id_led_init(struct e1000_hw * hw)
+{
+ uint32_t ledctl;
+ const uint32_t ledctl_mask = 0x000000FF;
+ const uint32_t ledctl_on = E1000_LEDCTL_MODE_LED_ON;
+ const uint32_t ledctl_off = E1000_LEDCTL_MODE_LED_OFF;
+ uint16_t eeprom_data, i, temp;
+ const uint16_t led_mask = 0x0F;
+
+ DEBUGFUNC("e1000_id_led_init");
+
+ if (hw->mac_type < e1000_82540) {
+ /* Nothing to do */
+ return E1000_SUCCESS;
+ }
+
+ ledctl = E1000_READ_REG(hw, LEDCTL);
+ hw->ledctl_default = ledctl;
+ hw->ledctl_mode1 = hw->ledctl_default;
+ hw->ledctl_mode2 = hw->ledctl_default;
+
+ if (e1000_read_eeprom(hw, EEPROM_ID_LED_SETTINGS, 1, &eeprom_data) < 0) {
+ DEBUGOUT("EEPROM Read Error\n");
+ return -E1000_ERR_EEPROM;
+ }
+
+ if ((hw->mac_type == e1000_82573) &&
+ (eeprom_data == ID_LED_RESERVED_82573))
+ eeprom_data = ID_LED_DEFAULT_82573;
+ else if ((eeprom_data == ID_LED_RESERVED_0000) ||
+ (eeprom_data == ID_LED_RESERVED_FFFF)) {
+ if (hw->mac_type == e1000_ich8lan)
+ eeprom_data = ID_LED_DEFAULT_ICH8LAN;
+ else
+ eeprom_data = ID_LED_DEFAULT;
+ }
+
+ for (i = 0; i < 4; i++) {
+ temp = (eeprom_data >> (i << 2)) & led_mask;
+ switch (temp) {
+ case ID_LED_ON1_DEF2:
+ case ID_LED_ON1_ON2:
+ case ID_LED_ON1_OFF2:
+ hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3));
+ hw->ledctl_mode1 |= ledctl_on << (i << 3);
+ break;
+ case ID_LED_OFF1_DEF2:
+ case ID_LED_OFF1_ON2:
+ case ID_LED_OFF1_OFF2:
+ hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3));
+ hw->ledctl_mode1 |= ledctl_off << (i << 3);
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+ switch (temp) {
+ case ID_LED_DEF1_ON2:
+ case ID_LED_ON1_ON2:
+ case ID_LED_OFF1_ON2:
+ hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3));
+ hw->ledctl_mode2 |= ledctl_on << (i << 3);
+ break;
+ case ID_LED_DEF1_OFF2:
+ case ID_LED_ON1_OFF2:
+ case ID_LED_OFF1_OFF2:
+ hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3));
+ hw->ledctl_mode2 |= ledctl_off << (i << 3);
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Prepares SW controlable LED for use and saves the current state of the LED.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+int32_t
+e1000_setup_led(struct e1000_hw *hw)
+{
+ uint32_t ledctl;
+ int32_t ret_val = E1000_SUCCESS;
+
+ DEBUGFUNC("e1000_setup_led");
+
+ switch (hw->mac_type) {
+ case e1000_82542_rev2_0:
+ case e1000_82542_rev2_1:
+ case e1000_82543:
+ case e1000_82544:
+ /* No setup necessary */
+ break;
+ case e1000_82541:
+ case e1000_82547:
+ case e1000_82541_rev_2:
+ case e1000_82547_rev_2:
+ /* Turn off PHY Smart Power Down (if enabled) */
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO,
+ &hw->phy_spd_default);
+ if (ret_val)
+ return ret_val;
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO,
+ (uint16_t)(hw->phy_spd_default &
+ ~IGP01E1000_GMII_SPD));
+ if (ret_val)
+ return ret_val;
+ /* Fall Through */
+ default:
+ if (hw->media_type == e1000_media_type_fiber) {
+ ledctl = E1000_READ_REG(hw, LEDCTL);
+ /* Save current LEDCTL settings */
+ hw->ledctl_default = ledctl;
+ /* Turn off LED0 */
+ ledctl &= ~(E1000_LEDCTL_LED0_IVRT |
+ E1000_LEDCTL_LED0_BLINK |
+ E1000_LEDCTL_LED0_MODE_MASK);
+ ledctl |= (E1000_LEDCTL_MODE_LED_OFF <<
+ E1000_LEDCTL_LED0_MODE_SHIFT);
+ E1000_WRITE_REG(hw, LEDCTL, ledctl);
+ } else if (hw->media_type == e1000_media_type_copper)
+ E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1);
+ break;
+ }
+
+ return E1000_SUCCESS;
+}
+
+
+/******************************************************************************
+ * Used on 82571 and later Si that has LED blink bits.
+ * Callers must use their own timer and should have already called
+ * e1000_id_led_init()
+ * Call e1000_cleanup led() to stop blinking
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+int32_t
+e1000_blink_led_start(struct e1000_hw *hw)
+{
+ int16_t i;
+ uint32_t ledctl_blink = 0;
+
+ DEBUGFUNC("e1000_id_led_blink_on");
+
+ if (hw->mac_type < e1000_82571) {
+ /* Nothing to do */
+ return E1000_SUCCESS;
+ }
+ if (hw->media_type == e1000_media_type_fiber) {
+ /* always blink LED0 for PCI-E fiber */
+ ledctl_blink = E1000_LEDCTL_LED0_BLINK |
+ (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT);
+ } else {
+ /* set the blink bit for each LED that's "on" (0x0E) in ledctl_mode2 */
+ ledctl_blink = hw->ledctl_mode2;
+ for (i=0; i < 4; i++)
+ if (((hw->ledctl_mode2 >> (i * 8)) & 0xFF) ==
+ E1000_LEDCTL_MODE_LED_ON)
+ ledctl_blink |= (E1000_LEDCTL_LED0_BLINK << (i * 8));
+ }
+
+ E1000_WRITE_REG(hw, LEDCTL, ledctl_blink);
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Restores the saved state of the SW controlable LED.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+int32_t
+e1000_cleanup_led(struct e1000_hw *hw)
+{
+ int32_t ret_val = E1000_SUCCESS;
+
+ DEBUGFUNC("e1000_cleanup_led");
+
+ switch (hw->mac_type) {
+ case e1000_82542_rev2_0:
+ case e1000_82542_rev2_1:
+ case e1000_82543:
+ case e1000_82544:
+ /* No cleanup necessary */
+ break;
+ case e1000_82541:
+ case e1000_82547:
+ case e1000_82541_rev_2:
+ case e1000_82547_rev_2:
+ /* Turn on PHY Smart Power Down (if previously enabled) */
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO,
+ hw->phy_spd_default);
+ if (ret_val)
+ return ret_val;
+ /* Fall Through */
+ default:
+ if (hw->phy_type == e1000_phy_ife) {
+ e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, 0);
+ break;
+ }
+ /* Restore LEDCTL settings */
+ E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_default);
+ break;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Turns on the software controllable LED
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+int32_t
+e1000_led_on(struct e1000_hw *hw)
+{
+ uint32_t ctrl = E1000_READ_REG(hw, CTRL);
+
+ DEBUGFUNC("e1000_led_on");
+
+ switch (hw->mac_type) {
+ case e1000_82542_rev2_0:
+ case e1000_82542_rev2_1:
+ case e1000_82543:
+ /* Set SW Defineable Pin 0 to turn on the LED */
+ ctrl |= E1000_CTRL_SWDPIN0;
+ ctrl |= E1000_CTRL_SWDPIO0;
+ break;
+ case e1000_82544:
+ if (hw->media_type == e1000_media_type_fiber) {
+ /* Set SW Defineable Pin 0 to turn on the LED */
+ ctrl |= E1000_CTRL_SWDPIN0;
+ ctrl |= E1000_CTRL_SWDPIO0;
+ } else {
+ /* Clear SW Defineable Pin 0 to turn on the LED */
+ ctrl &= ~E1000_CTRL_SWDPIN0;
+ ctrl |= E1000_CTRL_SWDPIO0;
+ }
+ break;
+ default:
+ if (hw->media_type == e1000_media_type_fiber) {
+ /* Clear SW Defineable Pin 0 to turn on the LED */
+ ctrl &= ~E1000_CTRL_SWDPIN0;
+ ctrl |= E1000_CTRL_SWDPIO0;
+ } else if (hw->phy_type == e1000_phy_ife) {
+ e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED,
+ (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_ON));
+ } else if (hw->media_type == e1000_media_type_copper) {
+ E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode2);
+ return E1000_SUCCESS;
+ }
+ break;
+ }
+
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Turns off the software controllable LED
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+int32_t
+e1000_led_off(struct e1000_hw *hw)
+{
+ uint32_t ctrl = E1000_READ_REG(hw, CTRL);
+
+ DEBUGFUNC("e1000_led_off");
+
+ switch (hw->mac_type) {
+ case e1000_82542_rev2_0:
+ case e1000_82542_rev2_1:
+ case e1000_82543:
+ /* Clear SW Defineable Pin 0 to turn off the LED */
+ ctrl &= ~E1000_CTRL_SWDPIN0;
+ ctrl |= E1000_CTRL_SWDPIO0;
+ break;
+ case e1000_82544:
+ if (hw->media_type == e1000_media_type_fiber) {
+ /* Clear SW Defineable Pin 0 to turn off the LED */
+ ctrl &= ~E1000_CTRL_SWDPIN0;
+ ctrl |= E1000_CTRL_SWDPIO0;
+ } else {
+ /* Set SW Defineable Pin 0 to turn off the LED */
+ ctrl |= E1000_CTRL_SWDPIN0;
+ ctrl |= E1000_CTRL_SWDPIO0;
+ }
+ break;
+ default:
+ if (hw->media_type == e1000_media_type_fiber) {
+ /* Set SW Defineable Pin 0 to turn off the LED */
+ ctrl |= E1000_CTRL_SWDPIN0;
+ ctrl |= E1000_CTRL_SWDPIO0;
+ } else if (hw->phy_type == e1000_phy_ife) {
+ e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED,
+ (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_OFF));
+ } else if (hw->media_type == e1000_media_type_copper) {
+ E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1);
+ return E1000_SUCCESS;
+ }
+ break;
+ }
+
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Clears all hardware statistics counters.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_clear_hw_cntrs(struct e1000_hw *hw)
+{
+ volatile uint32_t temp;
+
+ temp = E1000_READ_REG(hw, CRCERRS);
+ temp = E1000_READ_REG(hw, SYMERRS);
+ temp = E1000_READ_REG(hw, MPC);
+ temp = E1000_READ_REG(hw, SCC);
+ temp = E1000_READ_REG(hw, ECOL);
+ temp = E1000_READ_REG(hw, MCC);
+ temp = E1000_READ_REG(hw, LATECOL);
+ temp = E1000_READ_REG(hw, COLC);
+ temp = E1000_READ_REG(hw, DC);
+ temp = E1000_READ_REG(hw, SEC);
+ temp = E1000_READ_REG(hw, RLEC);
+ temp = E1000_READ_REG(hw, XONRXC);
+ temp = E1000_READ_REG(hw, XONTXC);
+ temp = E1000_READ_REG(hw, XOFFRXC);
+ temp = E1000_READ_REG(hw, XOFFTXC);
+ temp = E1000_READ_REG(hw, FCRUC);
+
+ if (hw->mac_type != e1000_ich8lan) {
+ temp = E1000_READ_REG(hw, PRC64);
+ temp = E1000_READ_REG(hw, PRC127);
+ temp = E1000_READ_REG(hw, PRC255);
+ temp = E1000_READ_REG(hw, PRC511);
+ temp = E1000_READ_REG(hw, PRC1023);
+ temp = E1000_READ_REG(hw, PRC1522);
+ }
+
+ temp = E1000_READ_REG(hw, GPRC);
+ temp = E1000_READ_REG(hw, BPRC);
+ temp = E1000_READ_REG(hw, MPRC);
+ temp = E1000_READ_REG(hw, GPTC);
+ temp = E1000_READ_REG(hw, GORCL);
+ temp = E1000_READ_REG(hw, GORCH);
+ temp = E1000_READ_REG(hw, GOTCL);
+ temp = E1000_READ_REG(hw, GOTCH);
+ temp = E1000_READ_REG(hw, RNBC);
+ temp = E1000_READ_REG(hw, RUC);
+ temp = E1000_READ_REG(hw, RFC);
+ temp = E1000_READ_REG(hw, ROC);
+ temp = E1000_READ_REG(hw, RJC);
+ temp = E1000_READ_REG(hw, TORL);
+ temp = E1000_READ_REG(hw, TORH);
+ temp = E1000_READ_REG(hw, TOTL);
+ temp = E1000_READ_REG(hw, TOTH);
+ temp = E1000_READ_REG(hw, TPR);
+ temp = E1000_READ_REG(hw, TPT);
+
+ if (hw->mac_type != e1000_ich8lan) {
+ temp = E1000_READ_REG(hw, PTC64);
+ temp = E1000_READ_REG(hw, PTC127);
+ temp = E1000_READ_REG(hw, PTC255);
+ temp = E1000_READ_REG(hw, PTC511);
+ temp = E1000_READ_REG(hw, PTC1023);
+ temp = E1000_READ_REG(hw, PTC1522);
+ }
+
+ temp = E1000_READ_REG(hw, MPTC);
+ temp = E1000_READ_REG(hw, BPTC);
+
+ if (hw->mac_type < e1000_82543) return;
+
+ temp = E1000_READ_REG(hw, ALGNERRC);
+ temp = E1000_READ_REG(hw, RXERRC);
+ temp = E1000_READ_REG(hw, TNCRS);
+ temp = E1000_READ_REG(hw, CEXTERR);
+ temp = E1000_READ_REG(hw, TSCTC);
+ temp = E1000_READ_REG(hw, TSCTFC);
+
+ if (hw->mac_type <= e1000_82544) return;
+
+ temp = E1000_READ_REG(hw, MGTPRC);
+ temp = E1000_READ_REG(hw, MGTPDC);
+ temp = E1000_READ_REG(hw, MGTPTC);
+
+ if (hw->mac_type <= e1000_82547_rev_2) return;
+
+ temp = E1000_READ_REG(hw, IAC);
+ temp = E1000_READ_REG(hw, ICRXOC);
+
+ if (hw->mac_type == e1000_ich8lan) return;
+
+ temp = E1000_READ_REG(hw, ICRXPTC);
+ temp = E1000_READ_REG(hw, ICRXATC);
+ temp = E1000_READ_REG(hw, ICTXPTC);
+ temp = E1000_READ_REG(hw, ICTXATC);
+ temp = E1000_READ_REG(hw, ICTXQEC);
+ temp = E1000_READ_REG(hw, ICTXQMTC);
+ temp = E1000_READ_REG(hw, ICRXDMTC);
+}
+
+/******************************************************************************
+ * Resets Adaptive IFS to its default state.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Call this after e1000_init_hw. You may override the IFS defaults by setting
+ * hw->ifs_params_forced to TRUE. However, you must initialize hw->
+ * current_ifs_val, ifs_min_val, ifs_max_val, ifs_step_size, and ifs_ratio
+ * before calling this function.
+ *****************************************************************************/
+void
+e1000_reset_adaptive(struct e1000_hw *hw)
+{
+ DEBUGFUNC("e1000_reset_adaptive");
+
+ if (hw->adaptive_ifs) {
+ if (!hw->ifs_params_forced) {
+ hw->current_ifs_val = 0;
+ hw->ifs_min_val = IFS_MIN;
+ hw->ifs_max_val = IFS_MAX;
+ hw->ifs_step_size = IFS_STEP;
+ hw->ifs_ratio = IFS_RATIO;
+ }
+ hw->in_ifs_mode = FALSE;
+ E1000_WRITE_REG(hw, AIT, 0);
+ } else {
+ DEBUGOUT("Not in Adaptive IFS mode!\n");
+ }
+}
+
+/******************************************************************************
+ * Called during the callback/watchdog routine to update IFS value based on
+ * the ratio of transmits to collisions.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * tx_packets - Number of transmits since last callback
+ * total_collisions - Number of collisions since last callback
+ *****************************************************************************/
+void
+e1000_update_adaptive(struct e1000_hw *hw)
+{
+ DEBUGFUNC("e1000_update_adaptive");
+
+ if (hw->adaptive_ifs) {
+ if ((hw->collision_delta * hw->ifs_ratio) > hw->tx_packet_delta) {
+ if (hw->tx_packet_delta > MIN_NUM_XMITS) {
+ hw->in_ifs_mode = TRUE;
+ if (hw->current_ifs_val < hw->ifs_max_val) {
+ if (hw->current_ifs_val == 0)
+ hw->current_ifs_val = hw->ifs_min_val;
+ else
+ hw->current_ifs_val += hw->ifs_step_size;
+ E1000_WRITE_REG(hw, AIT, hw->current_ifs_val);
+ }
+ }
+ } else {
+ if (hw->in_ifs_mode && (hw->tx_packet_delta <= MIN_NUM_XMITS)) {
+ hw->current_ifs_val = 0;
+ hw->in_ifs_mode = FALSE;
+ E1000_WRITE_REG(hw, AIT, 0);
+ }
+ }
+ } else {
+ DEBUGOUT("Not in Adaptive IFS mode!\n");
+ }
+}
+
+/******************************************************************************
+ * Adjusts the statistic counters when a frame is accepted by TBI_ACCEPT
+ *
+ * hw - Struct containing variables accessed by shared code
+ * frame_len - The length of the frame in question
+ * mac_addr - The Ethernet destination address of the frame in question
+ *****************************************************************************/
+void
+e1000_tbi_adjust_stats(struct e1000_hw *hw,
+ struct e1000_hw_stats *stats,
+ uint32_t frame_len,
+ uint8_t *mac_addr)
+{
+ uint64_t carry_bit;
+
+ /* First adjust the frame length. */
+ frame_len--;
+ /* We need to adjust the statistics counters, since the hardware
+ * counters overcount this packet as a CRC error and undercount
+ * the packet as a good packet
+ */
+ /* This packet should not be counted as a CRC error. */
+ stats->crcerrs--;
+ /* This packet does count as a Good Packet Received. */
+ stats->gprc++;
+
+ /* Adjust the Good Octets received counters */
+ carry_bit = 0x80000000 & stats->gorcl;
+ stats->gorcl += frame_len;
+ /* If the high bit of Gorcl (the low 32 bits of the Good Octets
+ * Received Count) was one before the addition,
+ * AND it is zero after, then we lost the carry out,
+ * need to add one to Gorch (Good Octets Received Count High).
+ * This could be simplified if all environments supported
+ * 64-bit integers.
+ */
+ if (carry_bit && ((stats->gorcl & 0x80000000) == 0))
+ stats->gorch++;
+ /* Is this a broadcast or multicast? Check broadcast first,
+ * since the test for a multicast frame will test positive on
+ * a broadcast frame.
+ */
+ if ((mac_addr[0] == (uint8_t) 0xff) && (mac_addr[1] == (uint8_t) 0xff))
+ /* Broadcast packet */
+ stats->bprc++;
+ else if (*mac_addr & 0x01)
+ /* Multicast packet */
+ stats->mprc++;
+
+ if (frame_len == hw->max_frame_size) {
+ /* In this case, the hardware has overcounted the number of
+ * oversize frames.
+ */
+ if (stats->roc > 0)
+ stats->roc--;
+ }
+
+ /* Adjust the bin counters when the extra byte put the frame in the
+ * wrong bin. Remember that the frame_len was adjusted above.
+ */
+ if (frame_len == 64) {
+ stats->prc64++;
+ stats->prc127--;
+ } else if (frame_len == 127) {
+ stats->prc127++;
+ stats->prc255--;
+ } else if (frame_len == 255) {
+ stats->prc255++;
+ stats->prc511--;
+ } else if (frame_len == 511) {
+ stats->prc511++;
+ stats->prc1023--;
+ } else if (frame_len == 1023) {
+ stats->prc1023++;
+ stats->prc1522--;
+ } else if (frame_len == 1522) {
+ stats->prc1522++;
+ }
+}
+
+/******************************************************************************
+ * Gets the current PCI bus type, speed, and width of the hardware
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+void
+e1000_get_bus_info(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t pci_ex_link_status;
+ uint32_t status;
+
+ switch (hw->mac_type) {
+ case e1000_82542_rev2_0:
+ case e1000_82542_rev2_1:
+ hw->bus_type = e1000_bus_type_pci;
+ hw->bus_speed = e1000_bus_speed_unknown;
+ hw->bus_width = e1000_bus_width_unknown;
+ break;
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_82573:
+ case e1000_80003es2lan:
+ hw->bus_type = e1000_bus_type_pci_express;
+ hw->bus_speed = e1000_bus_speed_2500;
+ ret_val = e1000_read_pcie_cap_reg(hw,
+ PCI_EX_LINK_STATUS,
+ &pci_ex_link_status);
+ if (ret_val)
+ hw->bus_width = e1000_bus_width_unknown;
+ else
+ hw->bus_width = (pci_ex_link_status & PCI_EX_LINK_WIDTH_MASK) >>
+ PCI_EX_LINK_WIDTH_SHIFT;
+ break;
+ case e1000_ich8lan:
+ hw->bus_type = e1000_bus_type_pci_express;
+ hw->bus_speed = e1000_bus_speed_2500;
+ hw->bus_width = e1000_bus_width_pciex_1;
+ break;
+ default:
+ status = E1000_READ_REG(hw, STATUS);
+ hw->bus_type = (status & E1000_STATUS_PCIX_MODE) ?
+ e1000_bus_type_pcix : e1000_bus_type_pci;
+
+ if (hw->device_id == E1000_DEV_ID_82546EB_QUAD_COPPER) {
+ hw->bus_speed = (hw->bus_type == e1000_bus_type_pci) ?
+ e1000_bus_speed_66 : e1000_bus_speed_120;
+ } else if (hw->bus_type == e1000_bus_type_pci) {
+ hw->bus_speed = (status & E1000_STATUS_PCI66) ?
+ e1000_bus_speed_66 : e1000_bus_speed_33;
+ } else {
+ switch (status & E1000_STATUS_PCIX_SPEED) {
+ case E1000_STATUS_PCIX_SPEED_66:
+ hw->bus_speed = e1000_bus_speed_66;
+ break;
+ case E1000_STATUS_PCIX_SPEED_100:
+ hw->bus_speed = e1000_bus_speed_100;
+ break;
+ case E1000_STATUS_PCIX_SPEED_133:
+ hw->bus_speed = e1000_bus_speed_133;
+ break;
+ default:
+ hw->bus_speed = e1000_bus_speed_reserved;
+ break;
+ }
+ }
+ hw->bus_width = (status & E1000_STATUS_BUS64) ?
+ e1000_bus_width_64 : e1000_bus_width_32;
+ break;
+ }
+}
+
+/******************************************************************************
+ * Writes a value to one of the devices registers using port I/O (as opposed to
+ * memory mapped I/O). Only 82544 and newer devices support port I/O.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset to write to
+ * value - value to write
+ *****************************************************************************/
+static void
+e1000_write_reg_io(struct e1000_hw *hw,
+ uint32_t offset,
+ uint32_t value)
+{
+ unsigned long io_addr = hw->io_base;
+ unsigned long io_data = hw->io_base + 4;
+
+ e1000_io_write(hw, io_addr, offset);
+ e1000_io_write(hw, io_data, value);
+}
+
+/******************************************************************************
+ * Estimates the cable length.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * min_length - The estimated minimum length
+ * max_length - The estimated maximum length
+ *
+ * returns: - E1000_ERR_XXX
+ * E1000_SUCCESS
+ *
+ * This function always returns a ranged length (minimum & maximum).
+ * So for M88 phy's, this function interprets the one value returned from the
+ * register to the minimum and maximum range.
+ * For IGP phy's, the function calculates the range by the AGC registers.
+ *****************************************************************************/
+static int32_t
+e1000_get_cable_length(struct e1000_hw *hw,
+ uint16_t *min_length,
+ uint16_t *max_length)
+{
+ int32_t ret_val;
+ uint16_t agc_value = 0;
+ uint16_t i, phy_data;
+ uint16_t cable_length;
+
+ DEBUGFUNC("e1000_get_cable_length");
+
+ *min_length = *max_length = 0;
+
+ /* Use old method for Phy older than IGP */
+ if (hw->phy_type == e1000_phy_m88) {
+
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+ cable_length = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
+ M88E1000_PSSR_CABLE_LENGTH_SHIFT;
+
+ /* Convert the enum value to ranged values */
+ switch (cable_length) {
+ case e1000_cable_length_50:
+ *min_length = 0;
+ *max_length = e1000_igp_cable_length_50;
+ break;
+ case e1000_cable_length_50_80:
+ *min_length = e1000_igp_cable_length_50;
+ *max_length = e1000_igp_cable_length_80;
+ break;
+ case e1000_cable_length_80_110:
+ *min_length = e1000_igp_cable_length_80;
+ *max_length = e1000_igp_cable_length_110;
+ break;
+ case e1000_cable_length_110_140:
+ *min_length = e1000_igp_cable_length_110;
+ *max_length = e1000_igp_cable_length_140;
+ break;
+ case e1000_cable_length_140:
+ *min_length = e1000_igp_cable_length_140;
+ *max_length = e1000_igp_cable_length_170;
+ break;
+ default:
+ return -E1000_ERR_PHY;
+ break;
+ }
+ } else if (hw->phy_type == e1000_phy_gg82563) {
+ ret_val = e1000_read_phy_reg(hw, GG82563_PHY_DSP_DISTANCE,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+ cable_length = phy_data & GG82563_DSPD_CABLE_LENGTH;
+
+ switch (cable_length) {
+ case e1000_gg_cable_length_60:
+ *min_length = 0;
+ *max_length = e1000_igp_cable_length_60;
+ break;
+ case e1000_gg_cable_length_60_115:
+ *min_length = e1000_igp_cable_length_60;
+ *max_length = e1000_igp_cable_length_115;
+ break;
+ case e1000_gg_cable_length_115_150:
+ *min_length = e1000_igp_cable_length_115;
+ *max_length = e1000_igp_cable_length_150;
+ break;
+ case e1000_gg_cable_length_150:
+ *min_length = e1000_igp_cable_length_150;
+ *max_length = e1000_igp_cable_length_180;
+ break;
+ default:
+ return -E1000_ERR_PHY;
+ break;
+ }
+ } else if (hw->phy_type == e1000_phy_igp) { /* For IGP PHY */
+ uint16_t cur_agc_value;
+ uint16_t min_agc_value = IGP01E1000_AGC_LENGTH_TABLE_SIZE;
+ uint16_t agc_reg_array[IGP01E1000_PHY_CHANNEL_NUM] =
+ {IGP01E1000_PHY_AGC_A,
+ IGP01E1000_PHY_AGC_B,
+ IGP01E1000_PHY_AGC_C,
+ IGP01E1000_PHY_AGC_D};
+ /* Read the AGC registers for all channels */
+ for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+
+ ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ cur_agc_value = phy_data >> IGP01E1000_AGC_LENGTH_SHIFT;
+
+ /* Value bound check. */
+ if ((cur_agc_value >= IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) ||
+ (cur_agc_value == 0))
+ return -E1000_ERR_PHY;
+
+ agc_value += cur_agc_value;
+
+ /* Update minimal AGC value. */
+ if (min_agc_value > cur_agc_value)
+ min_agc_value = cur_agc_value;
+ }
+
+ /* Remove the minimal AGC result for length < 50m */
+ if (agc_value < IGP01E1000_PHY_CHANNEL_NUM * e1000_igp_cable_length_50) {
+ agc_value -= min_agc_value;
+
+ /* Get the average length of the remaining 3 channels */
+ agc_value /= (IGP01E1000_PHY_CHANNEL_NUM - 1);
+ } else {
+ /* Get the average length of all the 4 channels. */
+ agc_value /= IGP01E1000_PHY_CHANNEL_NUM;
+ }
+
+ /* Set the range of the calculated length. */
+ *min_length = ((e1000_igp_cable_length_table[agc_value] -
+ IGP01E1000_AGC_RANGE) > 0) ?
+ (e1000_igp_cable_length_table[agc_value] -
+ IGP01E1000_AGC_RANGE) : 0;
+ *max_length = e1000_igp_cable_length_table[agc_value] +
+ IGP01E1000_AGC_RANGE;
+ } else if (hw->phy_type == e1000_phy_igp_2 ||
+ hw->phy_type == e1000_phy_igp_3) {
+ uint16_t cur_agc_index, max_agc_index = 0;
+ uint16_t min_agc_index = IGP02E1000_AGC_LENGTH_TABLE_SIZE - 1;
+ uint16_t agc_reg_array[IGP02E1000_PHY_CHANNEL_NUM] =
+ {IGP02E1000_PHY_AGC_A,
+ IGP02E1000_PHY_AGC_B,
+ IGP02E1000_PHY_AGC_C,
+ IGP02E1000_PHY_AGC_D};
+ /* Read the AGC registers for all channels */
+ for (i = 0; i < IGP02E1000_PHY_CHANNEL_NUM; i++) {
+ ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* Getting bits 15:9, which represent the combination of course and
+ * fine gain values. The result is a number that can be put into
+ * the lookup table to obtain the approximate cable length. */
+ cur_agc_index = (phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) &
+ IGP02E1000_AGC_LENGTH_MASK;
+
+ /* Array index bound check. */
+ if ((cur_agc_index >= IGP02E1000_AGC_LENGTH_TABLE_SIZE) ||
+ (cur_agc_index == 0))
+ return -E1000_ERR_PHY;
+
+ /* Remove min & max AGC values from calculation. */
+ if (e1000_igp_2_cable_length_table[min_agc_index] >
+ e1000_igp_2_cable_length_table[cur_agc_index])
+ min_agc_index = cur_agc_index;
+ if (e1000_igp_2_cable_length_table[max_agc_index] <
+ e1000_igp_2_cable_length_table[cur_agc_index])
+ max_agc_index = cur_agc_index;
+
+ agc_value += e1000_igp_2_cable_length_table[cur_agc_index];
+ }
+
+ agc_value -= (e1000_igp_2_cable_length_table[min_agc_index] +
+ e1000_igp_2_cable_length_table[max_agc_index]);
+ agc_value /= (IGP02E1000_PHY_CHANNEL_NUM - 2);
+
+ /* Calculate cable length with the error range of +/- 10 meters. */
+ *min_length = ((agc_value - IGP02E1000_AGC_RANGE) > 0) ?
+ (agc_value - IGP02E1000_AGC_RANGE) : 0;
+ *max_length = agc_value + IGP02E1000_AGC_RANGE;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Check the cable polarity
+ *
+ * hw - Struct containing variables accessed by shared code
+ * polarity - output parameter : 0 - Polarity is not reversed
+ * 1 - Polarity is reversed.
+ *
+ * returns: - E1000_ERR_XXX
+ * E1000_SUCCESS
+ *
+ * For phy's older then IGP, this function simply reads the polarity bit in the
+ * Phy Status register. For IGP phy's, this bit is valid only if link speed is
+ * 10 Mbps. If the link speed is 100 Mbps there is no polarity so this bit will
+ * return 0. If the link speed is 1000 Mbps the polarity status is in the
+ * IGP01E1000_PHY_PCS_INIT_REG.
+ *****************************************************************************/
+static int32_t
+e1000_check_polarity(struct e1000_hw *hw,
+ e1000_rev_polarity *polarity)
+{
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_check_polarity");
+
+ if ((hw->phy_type == e1000_phy_m88) ||
+ (hw->phy_type == e1000_phy_gg82563)) {
+ /* return the Polarity bit in the Status register. */
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+ *polarity = ((phy_data & M88E1000_PSSR_REV_POLARITY) >>
+ M88E1000_PSSR_REV_POLARITY_SHIFT) ?
+ e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
+
+ } else if (hw->phy_type == e1000_phy_igp ||
+ hw->phy_type == e1000_phy_igp_3 ||
+ hw->phy_type == e1000_phy_igp_2) {
+ /* Read the Status register to check the speed */
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* If speed is 1000 Mbps, must read the IGP01E1000_PHY_PCS_INIT_REG to
+ * find the polarity status */
+ if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
+ IGP01E1000_PSSR_SPEED_1000MBPS) {
+
+ /* Read the GIG initialization PCS register (0x00B4) */
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* Check the polarity bits */
+ *polarity = (phy_data & IGP01E1000_PHY_POLARITY_MASK) ?
+ e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
+ } else {
+ /* For 10 Mbps, read the polarity bit in the status register. (for
+ * 100 Mbps this bit is always 0) */
+ *polarity = (phy_data & IGP01E1000_PSSR_POLARITY_REVERSED) ?
+ e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
+ }
+ } else if (hw->phy_type == e1000_phy_ife) {
+ ret_val = e1000_read_phy_reg(hw, IFE_PHY_EXTENDED_STATUS_CONTROL,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+ *polarity = ((phy_data & IFE_PESC_POLARITY_REVERSED) >>
+ IFE_PESC_POLARITY_REVERSED_SHIFT) ?
+ e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Check if Downshift occured
+ *
+ * hw - Struct containing variables accessed by shared code
+ * downshift - output parameter : 0 - No Downshift ocured.
+ * 1 - Downshift ocured.
+ *
+ * returns: - E1000_ERR_XXX
+ * E1000_SUCCESS
+ *
+ * For phy's older then IGP, this function reads the Downshift bit in the Phy
+ * Specific Status register. For IGP phy's, it reads the Downgrade bit in the
+ * Link Health register. In IGP this bit is latched high, so the driver must
+ * read it immediately after link is established.
+ *****************************************************************************/
+static int32_t
+e1000_check_downshift(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_check_downshift");
+
+ if (hw->phy_type == e1000_phy_igp ||
+ hw->phy_type == e1000_phy_igp_3 ||
+ hw->phy_type == e1000_phy_igp_2) {
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_LINK_HEALTH,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ hw->speed_downgraded = (phy_data & IGP01E1000_PLHR_SS_DOWNGRADE) ? 1 : 0;
+ } else if ((hw->phy_type == e1000_phy_m88) ||
+ (hw->phy_type == e1000_phy_gg82563)) {
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ hw->speed_downgraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) >>
+ M88E1000_PSSR_DOWNSHIFT_SHIFT;
+ } else if (hw->phy_type == e1000_phy_ife) {
+ /* e1000_phy_ife supports 10/100 speed only */
+ hw->speed_downgraded = FALSE;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/*****************************************************************************
+ *
+ * 82541_rev_2 & 82547_rev_2 have the capability to configure the DSP when a
+ * gigabit link is achieved to improve link quality.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * returns: - E1000_ERR_PHY if fail to read/write the PHY
+ * E1000_SUCCESS at any other case.
+ *
+ ****************************************************************************/
+
+static int32_t
+e1000_config_dsp_after_link_change(struct e1000_hw *hw,
+ boolean_t link_up)
+{
+ int32_t ret_val;
+ uint16_t phy_data, phy_saved_data, speed, duplex, i;
+ uint16_t dsp_reg_array[IGP01E1000_PHY_CHANNEL_NUM] =
+ {IGP01E1000_PHY_AGC_PARAM_A,
+ IGP01E1000_PHY_AGC_PARAM_B,
+ IGP01E1000_PHY_AGC_PARAM_C,
+ IGP01E1000_PHY_AGC_PARAM_D};
+ uint16_t min_length, max_length;
+
+ DEBUGFUNC("e1000_config_dsp_after_link_change");
+
+ if (hw->phy_type != e1000_phy_igp)
+ return E1000_SUCCESS;
+
+ if (link_up) {
+ ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex);
+ if (ret_val) {
+ DEBUGOUT("Error getting link speed and duplex\n");
+ return ret_val;
+ }
+
+ if (speed == SPEED_1000) {
+
+ ret_val = e1000_get_cable_length(hw, &min_length, &max_length);
+ if (ret_val)
+ return ret_val;
+
+ if ((hw->dsp_config_state == e1000_dsp_config_enabled) &&
+ min_length >= e1000_igp_cable_length_50) {
+
+ for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+ ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i],
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX;
+
+ ret_val = e1000_write_phy_reg(hw, dsp_reg_array[i],
+ phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+ hw->dsp_config_state = e1000_dsp_config_activated;
+ }
+
+ if ((hw->ffe_config_state == e1000_ffe_config_enabled) &&
+ (min_length < e1000_igp_cable_length_50)) {
+
+ uint16_t ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_20;
+ uint32_t idle_errs = 0;
+
+ /* clear previous idle error counts */
+ ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ for (i = 0; i < ffe_idle_err_timeout; i++) {
+ udelay(1000);
+ ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ idle_errs += (phy_data & SR_1000T_IDLE_ERROR_CNT);
+ if (idle_errs > SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT) {
+ hw->ffe_config_state = e1000_ffe_config_active;
+
+ ret_val = e1000_write_phy_reg(hw,
+ IGP01E1000_PHY_DSP_FFE,
+ IGP01E1000_PHY_DSP_FFE_CM_CP);
+ if (ret_val)
+ return ret_val;
+ break;
+ }
+
+ if (idle_errs)
+ ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_100;
+ }
+ }
+ }
+ } else {
+ if (hw->dsp_config_state == e1000_dsp_config_activated) {
+ /* Save off the current value of register 0x2F5B to be restored at
+ * the end of the routines. */
+ ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data);
+
+ if (ret_val)
+ return ret_val;
+
+ /* Disable the PHY transmitter */
+ ret_val = e1000_write_phy_reg(hw, 0x2F5B, 0x0003);
+
+ if (ret_val)
+ return ret_val;
+
+ mdelay(20);
+
+ ret_val = e1000_write_phy_reg(hw, 0x0000,
+ IGP01E1000_IEEE_FORCE_GIGA);
+ if (ret_val)
+ return ret_val;
+ for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+ ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i], &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX;
+ phy_data |= IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS;
+
+ ret_val = e1000_write_phy_reg(hw,dsp_reg_array[i], phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+ ret_val = e1000_write_phy_reg(hw, 0x0000,
+ IGP01E1000_IEEE_RESTART_AUTONEG);
+ if (ret_val)
+ return ret_val;
+
+ mdelay(20);
+
+ /* Now enable the transmitter */
+ ret_val = e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data);
+
+ if (ret_val)
+ return ret_val;
+
+ hw->dsp_config_state = e1000_dsp_config_enabled;
+ }
+
+ if (hw->ffe_config_state == e1000_ffe_config_active) {
+ /* Save off the current value of register 0x2F5B to be restored at
+ * the end of the routines. */
+ ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data);
+
+ if (ret_val)
+ return ret_val;
+
+ /* Disable the PHY transmitter */
+ ret_val = e1000_write_phy_reg(hw, 0x2F5B, 0x0003);
+
+ if (ret_val)
+ return ret_val;
+
+ mdelay(20);
+
+ ret_val = e1000_write_phy_reg(hw, 0x0000,
+ IGP01E1000_IEEE_FORCE_GIGA);
+ if (ret_val)
+ return ret_val;
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_DSP_FFE,
+ IGP01E1000_PHY_DSP_FFE_DEFAULT);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_write_phy_reg(hw, 0x0000,
+ IGP01E1000_IEEE_RESTART_AUTONEG);
+ if (ret_val)
+ return ret_val;
+
+ mdelay(20);
+
+ /* Now enable the transmitter */
+ ret_val = e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data);
+
+ if (ret_val)
+ return ret_val;
+
+ hw->ffe_config_state = e1000_ffe_config_enabled;
+ }
+ }
+ return E1000_SUCCESS;
+}
+
+/*****************************************************************************
+ * Set PHY to class A mode
+ * Assumes the following operations will follow to enable the new class mode.
+ * 1. Do a PHY soft reset
+ * 2. Restart auto-negotiation or force link.
+ *
+ * hw - Struct containing variables accessed by shared code
+ ****************************************************************************/
+static int32_t
+e1000_set_phy_mode(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t eeprom_data;
+
+ DEBUGFUNC("e1000_set_phy_mode");
+
+ if ((hw->mac_type == e1000_82545_rev_3) &&
+ (hw->media_type == e1000_media_type_copper)) {
+ ret_val = e1000_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, 1, &eeprom_data);
+ if (ret_val) {
+ return ret_val;
+ }
+
+ if ((eeprom_data != EEPROM_RESERVED_WORD) &&
+ (eeprom_data & EEPROM_PHY_CLASS_A)) {
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x000B);
+ if (ret_val)
+ return ret_val;
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x8104);
+ if (ret_val)
+ return ret_val;
+
+ hw->phy_reset_disable = FALSE;
+ }
+ }
+
+ return E1000_SUCCESS;
+}
+
+/*****************************************************************************
+ *
+ * This function sets the lplu state according to the active flag. When
+ * activating lplu this function also disables smart speed and vise versa.
+ * lplu will not be activated unless the device autonegotiation advertisment
+ * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes.
+ * hw: Struct containing variables accessed by shared code
+ * active - true to enable lplu false to disable lplu.
+ *
+ * returns: - E1000_ERR_PHY if fail to read/write the PHY
+ * E1000_SUCCESS at any other case.
+ *
+ ****************************************************************************/
+
+static int32_t
+e1000_set_d3_lplu_state(struct e1000_hw *hw,
+ boolean_t active)
+{
+ uint32_t phy_ctrl = 0;
+ int32_t ret_val;
+ uint16_t phy_data;
+ DEBUGFUNC("e1000_set_d3_lplu_state");
+
+ if (hw->phy_type != e1000_phy_igp && hw->phy_type != e1000_phy_igp_2
+ && hw->phy_type != e1000_phy_igp_3)
+ return E1000_SUCCESS;
+
+ /* During driver activity LPLU should not be used or it will attain link
+ * from the lowest speeds starting from 10Mbps. The capability is used for
+ * Dx transitions and states */
+ if (hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) {
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data);
+ if (ret_val)
+ return ret_val;
+ } else if (hw->mac_type == e1000_ich8lan) {
+ /* MAC writes into PHY register based on the state transition
+ * and start auto-negotiation. SW driver can overwrite the settings
+ * in CSR PHY power control E1000_PHY_CTRL register. */
+ phy_ctrl = E1000_READ_REG(hw, PHY_CTRL);
+ } else {
+ ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+ if (!active) {
+ if (hw->mac_type == e1000_82541_rev_2 ||
+ hw->mac_type == e1000_82547_rev_2) {
+ phy_data &= ~IGP01E1000_GMII_FLEX_SPD;
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data);
+ if (ret_val)
+ return ret_val;
+ } else {
+ if (hw->mac_type == e1000_ich8lan) {
+ phy_ctrl &= ~E1000_PHY_CTRL_NOND0A_LPLU;
+ E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl);
+ } else {
+ phy_data &= ~IGP02E1000_PM_D3_LPLU;
+ ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT,
+ phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+ }
+
+ /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during
+ * Dx states where the power conservation is most important. During
+ * driver activity we should enable SmartSpeed, so performance is
+ * maintained. */
+ if (hw->smart_speed == e1000_smart_speed_on) {
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data |= IGP01E1000_PSCFR_SMART_SPEED;
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+ phy_data);
+ if (ret_val)
+ return ret_val;
+ } else if (hw->smart_speed == e1000_smart_speed_off) {
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+ phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+ } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) ||
+ (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL ) ||
+ (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) {
+
+ if (hw->mac_type == e1000_82541_rev_2 ||
+ hw->mac_type == e1000_82547_rev_2) {
+ phy_data |= IGP01E1000_GMII_FLEX_SPD;
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data);
+ if (ret_val)
+ return ret_val;
+ } else {
+ if (hw->mac_type == e1000_ich8lan) {
+ phy_ctrl |= E1000_PHY_CTRL_NOND0A_LPLU;
+ E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl);
+ } else {
+ phy_data |= IGP02E1000_PM_D3_LPLU;
+ ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT,
+ phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+ }
+
+ /* When LPLU is enabled we should disable SmartSpeed */
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ }
+ return E1000_SUCCESS;
+}
+
+/*****************************************************************************
+ *
+ * This function sets the lplu d0 state according to the active flag. When
+ * activating lplu this function also disables smart speed and vise versa.
+ * lplu will not be activated unless the device autonegotiation advertisment
+ * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes.
+ * hw: Struct containing variables accessed by shared code
+ * active - true to enable lplu false to disable lplu.
+ *
+ * returns: - E1000_ERR_PHY if fail to read/write the PHY
+ * E1000_SUCCESS at any other case.
+ *
+ ****************************************************************************/
+
+static int32_t
+e1000_set_d0_lplu_state(struct e1000_hw *hw,
+ boolean_t active)
+{
+ uint32_t phy_ctrl = 0;
+ int32_t ret_val;
+ uint16_t phy_data;
+ DEBUGFUNC("e1000_set_d0_lplu_state");
+
+ if (hw->mac_type <= e1000_82547_rev_2)
+ return E1000_SUCCESS;
+
+ if (hw->mac_type == e1000_ich8lan) {
+ phy_ctrl = E1000_READ_REG(hw, PHY_CTRL);
+ } else {
+ ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+ if (!active) {
+ if (hw->mac_type == e1000_ich8lan) {
+ phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU;
+ E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl);
+ } else {
+ phy_data &= ~IGP02E1000_PM_D0_LPLU;
+ ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+ /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during
+ * Dx states where the power conservation is most important. During
+ * driver activity we should enable SmartSpeed, so performance is
+ * maintained. */
+ if (hw->smart_speed == e1000_smart_speed_on) {
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data |= IGP01E1000_PSCFR_SMART_SPEED;
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+ phy_data);
+ if (ret_val)
+ return ret_val;
+ } else if (hw->smart_speed == e1000_smart_speed_off) {
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+ &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+ phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+
+ } else {
+
+ if (hw->mac_type == e1000_ich8lan) {
+ phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU;
+ E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl);
+ } else {
+ phy_data |= IGP02E1000_PM_D0_LPLU;
+ ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data);
+ if (ret_val)
+ return ret_val;
+ }
+
+ /* When LPLU is enabled we should disable SmartSpeed */
+ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+ ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Change VCO speed register to improve Bit Error Rate performance of SERDES.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int32_t
+e1000_set_vco_speed(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t default_page = 0;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_set_vco_speed");
+
+ switch (hw->mac_type) {
+ case e1000_82545_rev_3:
+ case e1000_82546_rev_3:
+ break;
+ default:
+ return E1000_SUCCESS;
+ }
+
+ /* Set PHY register 30, page 5, bit 8 to 0 */
+
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, &default_page);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0005);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data &= ~M88E1000_PHY_VCO_REG_BIT8;
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ /* Set PHY register 30, page 4, bit 11 to 1 */
+
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0004);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ phy_data |= M88E1000_PHY_VCO_REG_BIT11;
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, default_page);
+ if (ret_val)
+ return ret_val;
+
+ return E1000_SUCCESS;
+}
+
+
+/*****************************************************************************
+ * This function reads the cookie from ARC ram.
+ *
+ * returns: - E1000_SUCCESS .
+ ****************************************************************************/
+static int32_t
+e1000_host_if_read_cookie(struct e1000_hw * hw, uint8_t *buffer)
+{
+ uint8_t i;
+ uint32_t offset = E1000_MNG_DHCP_COOKIE_OFFSET;
+ uint8_t length = E1000_MNG_DHCP_COOKIE_LENGTH;
+
+ length = (length >> 2);
+ offset = (offset >> 2);
+
+ for (i = 0; i < length; i++) {
+ *((uint32_t *) buffer + i) =
+ E1000_READ_REG_ARRAY_DWORD(hw, HOST_IF, offset + i);
+ }
+ return E1000_SUCCESS;
+}
+
+
+/*****************************************************************************
+ * This function checks whether the HOST IF is enabled for command operaton
+ * and also checks whether the previous command is completed.
+ * It busy waits in case of previous command is not completed.
+ *
+ * returns: - E1000_ERR_HOST_INTERFACE_COMMAND in case if is not ready or
+ * timeout
+ * - E1000_SUCCESS for success.
+ ****************************************************************************/
+static int32_t
+e1000_mng_enable_host_if(struct e1000_hw * hw)
+{
+ uint32_t hicr;
+ uint8_t i;
+
+ /* Check that the host interface is enabled. */
+ hicr = E1000_READ_REG(hw, HICR);
+ if ((hicr & E1000_HICR_EN) == 0) {
+ DEBUGOUT("E1000_HOST_EN bit disabled.\n");
+ return -E1000_ERR_HOST_INTERFACE_COMMAND;
+ }
+ /* check the previous command is completed */
+ for (i = 0; i < E1000_MNG_DHCP_COMMAND_TIMEOUT; i++) {
+ hicr = E1000_READ_REG(hw, HICR);
+ if (!(hicr & E1000_HICR_C))
+ break;
+ mdelay(1);
+ }
+
+ if (i == E1000_MNG_DHCP_COMMAND_TIMEOUT) {
+ DEBUGOUT("Previous command timeout failed .\n");
+ return -E1000_ERR_HOST_INTERFACE_COMMAND;
+ }
+ return E1000_SUCCESS;
+}
+
+/*****************************************************************************
+ * This function writes the buffer content at the offset given on the host if.
+ * It also does alignment considerations to do the writes in most efficient way.
+ * Also fills up the sum of the buffer in *buffer parameter.
+ *
+ * returns - E1000_SUCCESS for success.
+ ****************************************************************************/
+static int32_t
+e1000_mng_host_if_write(struct e1000_hw * hw, uint8_t *buffer,
+ uint16_t length, uint16_t offset, uint8_t *sum)
+{
+ uint8_t *tmp;
+ uint8_t *bufptr = buffer;
+ uint32_t data = 0;
+ uint16_t remaining, i, j, prev_bytes;
+
+ /* sum = only sum of the data and it is not checksum */
+
+ if (length == 0 || offset + length > E1000_HI_MAX_MNG_DATA_LENGTH) {
+ return -E1000_ERR_PARAM;
+ }
+
+ tmp = (uint8_t *)&data;
+ prev_bytes = offset & 0x3;
+ offset &= 0xFFFC;
+ offset >>= 2;
+
+ if (prev_bytes) {
+ data = E1000_READ_REG_ARRAY_DWORD(hw, HOST_IF, offset);
+ for (j = prev_bytes; j < sizeof(uint32_t); j++) {
+ *(tmp + j) = *bufptr++;
+ *sum += *(tmp + j);
+ }
+ E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset, data);
+ length -= j - prev_bytes;
+ offset++;
+ }
+
+ remaining = length & 0x3;
+ length -= remaining;
+
+ /* Calculate length in DWORDs */
+ length >>= 2;
+
+ /* The device driver writes the relevant command block into the
+ * ram area. */
+ for (i = 0; i < length; i++) {
+ for (j = 0; j < sizeof(uint32_t); j++) {
+ *(tmp + j) = *bufptr++;
+ *sum += *(tmp + j);
+ }
+
+ E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset + i, data);
+ }
+ if (remaining) {
+ for (j = 0; j < sizeof(uint32_t); j++) {
+ if (j < remaining)
+ *(tmp + j) = *bufptr++;
+ else
+ *(tmp + j) = 0;
+
+ *sum += *(tmp + j);
+ }
+ E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset + i, data);
+ }
+
+ return E1000_SUCCESS;
+}
+
+
+/*****************************************************************************
+ * This function writes the command header after does the checksum calculation.
+ *
+ * returns - E1000_SUCCESS for success.
+ ****************************************************************************/
+static int32_t
+e1000_mng_write_cmd_header(struct e1000_hw * hw,
+ struct e1000_host_mng_command_header * hdr)
+{
+ uint16_t i;
+ uint8_t sum;
+ uint8_t *buffer;
+
+ /* Write the whole command header structure which includes sum of
+ * the buffer */
+
+ uint16_t length = sizeof(struct e1000_host_mng_command_header);
+
+ sum = hdr->checksum;
+ hdr->checksum = 0;
+
+ buffer = (uint8_t *) hdr;
+ i = length;
+ while (i--)
+ sum += buffer[i];
+
+ hdr->checksum = 0 - sum;
+
+ length >>= 2;
+ /* The device driver writes the relevant command block into the ram area. */
+ for (i = 0; i < length; i++) {
+ E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, i, *((uint32_t *) hdr + i));
+ E1000_WRITE_FLUSH(hw);
+ }
+
+ return E1000_SUCCESS;
+}
+
+
+/*****************************************************************************
+ * This function indicates to ARC that a new command is pending which completes
+ * one write operation by the driver.
+ *
+ * returns - E1000_SUCCESS for success.
+ ****************************************************************************/
+static int32_t
+e1000_mng_write_commit(struct e1000_hw * hw)
+{
+ uint32_t hicr;
+
+ hicr = E1000_READ_REG(hw, HICR);
+ /* Setting this bit tells the ARC that a new command is pending. */
+ E1000_WRITE_REG(hw, HICR, hicr | E1000_HICR_C);
+
+ return E1000_SUCCESS;
+}
+
+
+/*****************************************************************************
+ * This function checks the mode of the firmware.
+ *
+ * returns - TRUE when the mode is IAMT or FALSE.
+ ****************************************************************************/
+boolean_t
+e1000_check_mng_mode(struct e1000_hw *hw)
+{
+ uint32_t fwsm;
+
+ fwsm = E1000_READ_REG(hw, FWSM);
+
+ if (hw->mac_type == e1000_ich8lan) {
+ if ((fwsm & E1000_FWSM_MODE_MASK) ==
+ (E1000_MNG_ICH_IAMT_MODE << E1000_FWSM_MODE_SHIFT))
+ return TRUE;
+ } else if ((fwsm & E1000_FWSM_MODE_MASK) ==
+ (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT))
+ return TRUE;
+
+ return FALSE;
+}
+
+
+/*****************************************************************************
+ * This function writes the dhcp info .
+ ****************************************************************************/
+int32_t
+e1000_mng_write_dhcp_info(struct e1000_hw * hw, uint8_t *buffer,
+ uint16_t length)
+{
+ int32_t ret_val;
+ struct e1000_host_mng_command_header hdr;
+
+ hdr.command_id = E1000_MNG_DHCP_TX_PAYLOAD_CMD;
+ hdr.command_length = length;
+ hdr.reserved1 = 0;
+ hdr.reserved2 = 0;
+ hdr.checksum = 0;
+
+ ret_val = e1000_mng_enable_host_if(hw);
+ if (ret_val == E1000_SUCCESS) {
+ ret_val = e1000_mng_host_if_write(hw, buffer, length, sizeof(hdr),
+ &(hdr.checksum));
+ if (ret_val == E1000_SUCCESS) {
+ ret_val = e1000_mng_write_cmd_header(hw, &hdr);
+ if (ret_val == E1000_SUCCESS)
+ ret_val = e1000_mng_write_commit(hw);
+ }
+ }
+ return ret_val;
+}
+
+
+/*****************************************************************************
+ * This function calculates the checksum.
+ *
+ * returns - checksum of buffer contents.
+ ****************************************************************************/
+static uint8_t
+e1000_calculate_mng_checksum(char *buffer, uint32_t length)
+{
+ uint8_t sum = 0;
+ uint32_t i;
+
+ if (!buffer)
+ return 0;
+
+ for (i=0; i < length; i++)
+ sum += buffer[i];
+
+ return (uint8_t) (0 - sum);
+}
+
+/*****************************************************************************
+ * This function checks whether tx pkt filtering needs to be enabled or not.
+ *
+ * returns - TRUE for packet filtering or FALSE.
+ ****************************************************************************/
+boolean_t
+e1000_enable_tx_pkt_filtering(struct e1000_hw *hw)
+{
+ /* called in init as well as watchdog timer functions */
+
+ int32_t ret_val, checksum;
+ boolean_t tx_filter = FALSE;
+ struct e1000_host_mng_dhcp_cookie *hdr = &(hw->mng_cookie);
+ uint8_t *buffer = (uint8_t *) &(hw->mng_cookie);
+
+ if (e1000_check_mng_mode(hw)) {
+ ret_val = e1000_mng_enable_host_if(hw);
+ if (ret_val == E1000_SUCCESS) {
+ ret_val = e1000_host_if_read_cookie(hw, buffer);
+ if (ret_val == E1000_SUCCESS) {
+ checksum = hdr->checksum;
+ hdr->checksum = 0;
+ if ((hdr->signature == E1000_IAMT_SIGNATURE) &&
+ checksum == e1000_calculate_mng_checksum((char *)buffer,
+ E1000_MNG_DHCP_COOKIE_LENGTH)) {
+ if (hdr->status &
+ E1000_MNG_DHCP_COOKIE_STATUS_PARSING_SUPPORT)
+ tx_filter = TRUE;
+ } else
+ tx_filter = TRUE;
+ } else
+ tx_filter = TRUE;
+ }
+ }
+
+ hw->tx_pkt_filtering = tx_filter;
+ return tx_filter;
+}
+
+/******************************************************************************
+ * Verifies the hardware needs to allow ARPs to be processed by the host
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * returns: - TRUE/FALSE
+ *
+ *****************************************************************************/
+uint32_t
+e1000_enable_mng_pass_thru(struct e1000_hw *hw)
+{
+ uint32_t manc;
+ uint32_t fwsm, factps;
+
+ if (hw->asf_firmware_present) {
+ manc = E1000_READ_REG(hw, MANC);
+
+ if (!(manc & E1000_MANC_RCV_TCO_EN) ||
+ !(manc & E1000_MANC_EN_MAC_ADDR_FILTER))
+ return FALSE;
+ if (e1000_arc_subsystem_valid(hw) == TRUE) {
+ fwsm = E1000_READ_REG(hw, FWSM);
+ factps = E1000_READ_REG(hw, FACTPS);
+
+ if ((((fwsm & E1000_FWSM_MODE_MASK) >> E1000_FWSM_MODE_SHIFT) ==
+ e1000_mng_mode_pt) && !(factps & E1000_FACTPS_MNGCG))
+ return TRUE;
+ } else
+ if ((manc & E1000_MANC_SMBUS_EN) && !(manc & E1000_MANC_ASF_EN))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int32_t
+e1000_polarity_reversal_workaround(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t mii_status_reg;
+ uint16_t i;
+
+ /* Polarity reversal workaround for forced 10F/10H links. */
+
+ /* Disable the transmitter on the PHY */
+
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019);
+ if (ret_val)
+ return ret_val;
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFFF);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000);
+ if (ret_val)
+ return ret_val;
+
+ /* This loop will early-out if the NO link condition has been met. */
+ for (i = PHY_FORCE_TIME; i > 0; i--) {
+ /* Read the MII Status Register and wait for Link Status bit
+ * to be clear.
+ */
+
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+
+ if ((mii_status_reg & ~MII_SR_LINK_STATUS) == 0) break;
+ mdelay(100);
+ }
+
+ /* Recommended delay time after link has been lost */
+ mdelay(1000);
+
+ /* Now we will re-enable th transmitter on the PHY */
+
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019);
+ if (ret_val)
+ return ret_val;
+ mdelay(50);
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFF0);
+ if (ret_val)
+ return ret_val;
+ mdelay(50);
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFF00);
+ if (ret_val)
+ return ret_val;
+ mdelay(50);
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x0000);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000);
+ if (ret_val)
+ return ret_val;
+
+ /* This loop will early-out if the link condition has been met. */
+ for (i = PHY_FORCE_TIME; i > 0; i--) {
+ /* Read the MII Status Register and wait for Link Status bit
+ * to be set.
+ */
+
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+
+ if (mii_status_reg & MII_SR_LINK_STATUS) break;
+ mdelay(100);
+ }
+ return E1000_SUCCESS;
+}
+
+/***************************************************************************
+ *
+ * Disables PCI-Express master access.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * returns: - none.
+ *
+ ***************************************************************************/
+static void
+e1000_set_pci_express_master_disable(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+
+ DEBUGFUNC("e1000_set_pci_express_master_disable");
+
+ if (hw->bus_type != e1000_bus_type_pci_express)
+ return;
+
+ ctrl = E1000_READ_REG(hw, CTRL);
+ ctrl |= E1000_CTRL_GIO_MASTER_DISABLE;
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+}
+
+/*******************************************************************************
+ *
+ * Disables PCI-Express master access and verifies there are no pending requests
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * returns: - E1000_ERR_MASTER_REQUESTS_PENDING if master disable bit hasn't
+ * caused the master requests to be disabled.
+ * E1000_SUCCESS master requests disabled.
+ *
+ ******************************************************************************/
+int32_t
+e1000_disable_pciex_master(struct e1000_hw *hw)
+{
+ int32_t timeout = MASTER_DISABLE_TIMEOUT; /* 80ms */
+
+ DEBUGFUNC("e1000_disable_pciex_master");
+
+ if (hw->bus_type != e1000_bus_type_pci_express)
+ return E1000_SUCCESS;
+
+ e1000_set_pci_express_master_disable(hw);
+
+ while (timeout) {
+ if (!(E1000_READ_REG(hw, STATUS) & E1000_STATUS_GIO_MASTER_ENABLE))
+ break;
+ else
+ udelay(100);
+ timeout--;
+ }
+
+ if (!timeout) {
+ DEBUGOUT("Master requests are pending.\n");
+ return -E1000_ERR_MASTER_REQUESTS_PENDING;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/*******************************************************************************
+ *
+ * Check for EEPROM Auto Read bit done.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * returns: - E1000_ERR_RESET if fail to reset MAC
+ * E1000_SUCCESS at any other case.
+ *
+ ******************************************************************************/
+static int32_t
+e1000_get_auto_rd_done(struct e1000_hw *hw)
+{
+ int32_t timeout = AUTO_READ_DONE_TIMEOUT;
+
+ DEBUGFUNC("e1000_get_auto_rd_done");
+
+ switch (hw->mac_type) {
+ default:
+ msleep(5);
+ break;
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_82573:
+ case e1000_80003es2lan:
+ case e1000_ich8lan:
+ while (timeout) {
+ if (E1000_READ_REG(hw, EECD) & E1000_EECD_AUTO_RD)
+ break;
+ else msleep(1);
+ timeout--;
+ }
+
+ if (!timeout) {
+ DEBUGOUT("Auto read by HW from EEPROM has not completed.\n");
+ return -E1000_ERR_RESET;
+ }
+ break;
+ }
+
+ /* PHY configuration from NVM just starts after EECD_AUTO_RD sets to high.
+ * Need to wait for PHY configuration completion before accessing NVM
+ * and PHY. */
+ if (hw->mac_type == e1000_82573)
+ msleep(25);
+
+ return E1000_SUCCESS;
+}
+
+/***************************************************************************
+ * Checks if the PHY configuration is done
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * returns: - E1000_ERR_RESET if fail to reset MAC
+ * E1000_SUCCESS at any other case.
+ *
+ ***************************************************************************/
+static int32_t
+e1000_get_phy_cfg_done(struct e1000_hw *hw)
+{
+ int32_t timeout = PHY_CFG_TIMEOUT;
+ uint32_t cfg_mask = E1000_EEPROM_CFG_DONE;
+
+ DEBUGFUNC("e1000_get_phy_cfg_done");
+
+ switch (hw->mac_type) {
+ default:
+ mdelay(10);
+ break;
+ case e1000_80003es2lan:
+ /* Separate *_CFG_DONE_* bit for each port */
+ if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)
+ cfg_mask = E1000_EEPROM_CFG_DONE_PORT_1;
+ /* Fall Through */
+ case e1000_82571:
+ case e1000_82572:
+ while (timeout) {
+ if (E1000_READ_REG(hw, EEMNGCTL) & cfg_mask)
+ break;
+ else
+ msleep(1);
+ timeout--;
+ }
+ if (!timeout) {
+ DEBUGOUT("MNG configuration cycle has not completed.\n");
+ return -E1000_ERR_RESET;
+ }
+ break;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/***************************************************************************
+ *
+ * Using the combination of SMBI and SWESMBI semaphore bits when resetting
+ * adapter or Eeprom access.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * returns: - E1000_ERR_EEPROM if fail to access EEPROM.
+ * E1000_SUCCESS at any other case.
+ *
+ ***************************************************************************/
+static int32_t
+e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw)
+{
+ int32_t timeout;
+ uint32_t swsm;
+
+ DEBUGFUNC("e1000_get_hw_eeprom_semaphore");
+
+ if (!hw->eeprom_semaphore_present)
+ return E1000_SUCCESS;
+
+ if (hw->mac_type == e1000_80003es2lan) {
+ /* Get the SW semaphore. */
+ if (e1000_get_software_semaphore(hw) != E1000_SUCCESS)
+ return -E1000_ERR_EEPROM;
+ }
+
+ /* Get the FW semaphore. */
+ timeout = hw->eeprom.word_size + 1;
+ while (timeout) {
+ swsm = E1000_READ_REG(hw, SWSM);
+ swsm |= E1000_SWSM_SWESMBI;
+ E1000_WRITE_REG(hw, SWSM, swsm);
+ /* if we managed to set the bit we got the semaphore. */
+ swsm = E1000_READ_REG(hw, SWSM);
+ if (swsm & E1000_SWSM_SWESMBI)
+ break;
+
+ udelay(50);
+ timeout--;
+ }
+
+ if (!timeout) {
+ /* Release semaphores */
+ e1000_put_hw_eeprom_semaphore(hw);
+ DEBUGOUT("Driver can't access the Eeprom - SWESMBI bit is set.\n");
+ return -E1000_ERR_EEPROM;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/***************************************************************************
+ * This function clears HW semaphore bits.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * returns: - None.
+ *
+ ***************************************************************************/
+static void
+e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw)
+{
+ uint32_t swsm;
+
+ DEBUGFUNC("e1000_put_hw_eeprom_semaphore");
+
+ if (!hw->eeprom_semaphore_present)
+ return;
+
+ swsm = E1000_READ_REG(hw, SWSM);
+ if (hw->mac_type == e1000_80003es2lan) {
+ /* Release both semaphores. */
+ swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI);
+ } else
+ swsm &= ~(E1000_SWSM_SWESMBI);
+ E1000_WRITE_REG(hw, SWSM, swsm);
+}
+
+/***************************************************************************
+ *
+ * Obtaining software semaphore bit (SMBI) before resetting PHY.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * returns: - E1000_ERR_RESET if fail to obtain semaphore.
+ * E1000_SUCCESS at any other case.
+ *
+ ***************************************************************************/
+static int32_t
+e1000_get_software_semaphore(struct e1000_hw *hw)
+{
+ int32_t timeout = hw->eeprom.word_size + 1;
+ uint32_t swsm;
+
+ DEBUGFUNC("e1000_get_software_semaphore");
+
+ if (hw->mac_type != e1000_80003es2lan) {
+ return E1000_SUCCESS;
+ }
+
+ while (timeout) {
+ swsm = E1000_READ_REG(hw, SWSM);
+ /* If SMBI bit cleared, it is now set and we hold the semaphore */
+ if (!(swsm & E1000_SWSM_SMBI))
+ break;
+ mdelay(1);
+ timeout--;
+ }
+
+ if (!timeout) {
+ DEBUGOUT("Driver can't access device - SMBI bit is set.\n");
+ return -E1000_ERR_RESET;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/***************************************************************************
+ *
+ * Release semaphore bit (SMBI).
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ ***************************************************************************/
+static void
+e1000_release_software_semaphore(struct e1000_hw *hw)
+{
+ uint32_t swsm;
+
+ DEBUGFUNC("e1000_release_software_semaphore");
+
+ if (hw->mac_type != e1000_80003es2lan) {
+ return;
+ }
+
+ swsm = E1000_READ_REG(hw, SWSM);
+ /* Release the SW semaphores.*/
+ swsm &= ~E1000_SWSM_SMBI;
+ E1000_WRITE_REG(hw, SWSM, swsm);
+}
+
+/******************************************************************************
+ * Checks if PHY reset is blocked due to SOL/IDER session, for example.
+ * Returning E1000_BLK_PHY_RESET isn't necessarily an error. But it's up to
+ * the caller to figure out how to deal with it.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * returns: - E1000_BLK_PHY_RESET
+ * E1000_SUCCESS
+ *
+ *****************************************************************************/
+int32_t
+e1000_check_phy_reset_block(struct e1000_hw *hw)
+{
+ uint32_t manc = 0;
+ uint32_t fwsm = 0;
+
+ if (hw->mac_type == e1000_ich8lan) {
+ fwsm = E1000_READ_REG(hw, FWSM);
+ return (fwsm & E1000_FWSM_RSPCIPHY) ? E1000_SUCCESS
+ : E1000_BLK_PHY_RESET;
+ }
+
+ if (hw->mac_type > e1000_82547_rev_2)
+ manc = E1000_READ_REG(hw, MANC);
+ return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ?
+ E1000_BLK_PHY_RESET : E1000_SUCCESS;
+}
+
+static uint8_t
+e1000_arc_subsystem_valid(struct e1000_hw *hw)
+{
+ uint32_t fwsm;
+
+ /* On 8257x silicon, registers in the range of 0x8800 - 0x8FFC
+ * may not be provided a DMA clock when no manageability features are
+ * enabled. We do not want to perform any reads/writes to these registers
+ * if this is the case. We read FWSM to determine the manageability mode.
+ */
+ switch (hw->mac_type) {
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_82573:
+ case e1000_80003es2lan:
+ fwsm = E1000_READ_REG(hw, FWSM);
+ if ((fwsm & E1000_FWSM_MODE_MASK) != 0)
+ return TRUE;
+ break;
+ case e1000_ich8lan:
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+
+/******************************************************************************
+ * Configure PCI-Ex no-snoop
+ *
+ * hw - Struct containing variables accessed by shared code.
+ * no_snoop - Bitmap of no-snoop events.
+ *
+ * returns: E1000_SUCCESS
+ *
+ *****************************************************************************/
+static int32_t
+e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, uint32_t no_snoop)
+{
+ uint32_t gcr_reg = 0;
+
+ DEBUGFUNC("e1000_set_pci_ex_no_snoop");
+
+ if (hw->bus_type == e1000_bus_type_unknown)
+ e1000_get_bus_info(hw);
+
+ if (hw->bus_type != e1000_bus_type_pci_express)
+ return E1000_SUCCESS;
+
+ if (no_snoop) {
+ gcr_reg = E1000_READ_REG(hw, GCR);
+ gcr_reg &= ~(PCI_EX_NO_SNOOP_ALL);
+ gcr_reg |= no_snoop;
+ E1000_WRITE_REG(hw, GCR, gcr_reg);
+ }
+ if (hw->mac_type == e1000_ich8lan) {
+ uint32_t ctrl_ext;
+
+ E1000_WRITE_REG(hw, GCR, PCI_EX_82566_SNOOP_ALL);
+
+ ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ ctrl_ext |= E1000_CTRL_EXT_RO_DIS;
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ }
+
+ return E1000_SUCCESS;
+}
+
+/***************************************************************************
+ *
+ * Get software semaphore FLAG bit (SWFLAG).
+ * SWFLAG is used to synchronize the access to all shared resource between
+ * SW, FW and HW.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ ***************************************************************************/
+static int32_t
+e1000_get_software_flag(struct e1000_hw *hw)
+{
+ int32_t timeout = PHY_CFG_TIMEOUT;
+ uint32_t extcnf_ctrl;
+
+ DEBUGFUNC("e1000_get_software_flag");
+
+ if (hw->mac_type == e1000_ich8lan) {
+ while (timeout) {
+ extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL);
+ extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
+ E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl);
+
+ extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL);
+ if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)
+ break;
+ mdelay(1);
+ timeout--;
+ }
+
+ if (!timeout) {
+ DEBUGOUT("FW or HW locks the resource too long.\n");
+ return -E1000_ERR_CONFIG;
+ }
+ }
+
+ return E1000_SUCCESS;
+}
+
+/***************************************************************************
+ *
+ * Release software semaphore FLAG bit (SWFLAG).
+ * SWFLAG is used to synchronize the access to all shared resource between
+ * SW, FW and HW.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ ***************************************************************************/
+static void
+e1000_release_software_flag(struct e1000_hw *hw)
+{
+ uint32_t extcnf_ctrl;
+
+ DEBUGFUNC("e1000_release_software_flag");
+
+ if (hw->mac_type == e1000_ich8lan) {
+ extcnf_ctrl= E1000_READ_REG(hw, EXTCNF_CTRL);
+ extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
+ E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl);
+ }
+
+ return;
+}
+
+/******************************************************************************
+ * Reads a 16 bit word or words from the EEPROM using the ICH8's flash access
+ * register.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset of word in the EEPROM to read
+ * data - word read from the EEPROM
+ * words - number of words to read
+ *****************************************************************************/
+static int32_t
+e1000_read_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words,
+ uint16_t *data)
+{
+ int32_t error = E1000_SUCCESS;
+ uint32_t flash_bank = 0;
+ uint32_t act_offset = 0;
+ uint32_t bank_offset = 0;
+ uint16_t word = 0;
+ uint16_t i = 0;
+
+ /* We need to know which is the valid flash bank. In the event
+ * that we didn't allocate eeprom_shadow_ram, we may not be
+ * managing flash_bank. So it cannot be trusted and needs
+ * to be updated with each read.
+ */
+ /* Value of bit 22 corresponds to the flash bank we're on. */
+ flash_bank = (E1000_READ_REG(hw, EECD) & E1000_EECD_SEC1VAL) ? 1 : 0;
+
+ /* Adjust offset appropriately if we're on bank 1 - adjust for word size */
+ bank_offset = flash_bank * (hw->flash_bank_size * 2);
+
+ error = e1000_get_software_flag(hw);
+ if (error != E1000_SUCCESS)
+ return error;
+
+ for (i = 0; i < words; i++) {
+ if (hw->eeprom_shadow_ram != NULL &&
+ hw->eeprom_shadow_ram[offset+i].modified == TRUE) {
+ data[i] = hw->eeprom_shadow_ram[offset+i].eeprom_word;
+ } else {
+ /* The NVM part needs a byte offset, hence * 2 */
+ act_offset = bank_offset + ((offset + i) * 2);
+ error = e1000_read_ich8_word(hw, act_offset, &word);
+ if (error != E1000_SUCCESS)
+ break;
+ data[i] = word;
+ }
+ }
+
+ e1000_release_software_flag(hw);
+
+ return error;
+}
+
+/******************************************************************************
+ * Writes a 16 bit word or words to the EEPROM using the ICH8's flash access
+ * register. Actually, writes are written to the shadow ram cache in the hw
+ * structure hw->e1000_shadow_ram. e1000_commit_shadow_ram flushes this to
+ * the NVM, which occurs when the NVM checksum is updated.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset of word in the EEPROM to write
+ * words - number of words to write
+ * data - words to write to the EEPROM
+ *****************************************************************************/
+static int32_t
+e1000_write_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words,
+ uint16_t *data)
+{
+ uint32_t i = 0;
+ int32_t error = E1000_SUCCESS;
+
+ error = e1000_get_software_flag(hw);
+ if (error != E1000_SUCCESS)
+ return error;
+
+ /* A driver can write to the NVM only if it has eeprom_shadow_ram
+ * allocated. Subsequent reads to the modified words are read from
+ * this cached structure as well. Writes will only go into this
+ * cached structure unless it's followed by a call to
+ * e1000_update_eeprom_checksum() where it will commit the changes
+ * and clear the "modified" field.
+ */
+ if (hw->eeprom_shadow_ram != NULL) {
+ for (i = 0; i < words; i++) {
+ if ((offset + i) < E1000_SHADOW_RAM_WORDS) {
+ hw->eeprom_shadow_ram[offset+i].modified = TRUE;
+ hw->eeprom_shadow_ram[offset+i].eeprom_word = data[i];
+ } else {
+ error = -E1000_ERR_EEPROM;
+ break;
+ }
+ }
+ } else {
+ /* Drivers have the option to not allocate eeprom_shadow_ram as long
+ * as they don't perform any NVM writes. An attempt in doing so
+ * will result in this error.
+ */
+ error = -E1000_ERR_EEPROM;
+ }
+
+ e1000_release_software_flag(hw);
+
+ return error;
+}
+
+/******************************************************************************
+ * This function does initial flash setup so that a new read/write/erase cycle
+ * can be started.
+ *
+ * hw - The pointer to the hw structure
+ ****************************************************************************/
+static int32_t
+e1000_ich8_cycle_init(struct e1000_hw *hw)
+{
+ union ich8_hws_flash_status hsfsts;
+ int32_t error = E1000_ERR_EEPROM;
+ int32_t i = 0;
+
+ DEBUGFUNC("e1000_ich8_cycle_init");
+
+ hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
+
+ /* May be check the Flash Des Valid bit in Hw status */
+ if (hsfsts.hsf_status.fldesvalid == 0) {
+ DEBUGOUT("Flash descriptor invalid. SW Sequencing must be used.");
+ return error;
+ }
+
+ /* Clear FCERR in Hw status by writing 1 */
+ /* Clear DAEL in Hw status by writing a 1 */
+ hsfsts.hsf_status.flcerr = 1;
+ hsfsts.hsf_status.dael = 1;
+
+ E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval);
+
+ /* Either we should have a hardware SPI cycle in progress bit to check
+ * against, in order to start a new cycle or FDONE bit should be changed
+ * in the hardware so that it is 1 after harware reset, which can then be
+ * used as an indication whether a cycle is in progress or has been
+ * completed .. we should also have some software semaphore mechanism to
+ * guard FDONE or the cycle in progress bit so that two threads access to
+ * those bits can be sequentiallized or a way so that 2 threads dont
+ * start the cycle at the same time */
+
+ if (hsfsts.hsf_status.flcinprog == 0) {
+ /* There is no cycle running at present, so we can start a cycle */
+ /* Begin by setting Flash Cycle Done. */
+ hsfsts.hsf_status.flcdone = 1;
+ E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval);
+ error = E1000_SUCCESS;
+ } else {
+ /* otherwise poll for sometime so the current cycle has a chance
+ * to end before giving up. */
+ for (i = 0; i < ICH_FLASH_COMMAND_TIMEOUT; i++) {
+ hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
+ if (hsfsts.hsf_status.flcinprog == 0) {
+ error = E1000_SUCCESS;
+ break;
+ }
+ udelay(1);
+ }
+ if (error == E1000_SUCCESS) {
+ /* Successful in waiting for previous cycle to timeout,
+ * now set the Flash Cycle Done. */
+ hsfsts.hsf_status.flcdone = 1;
+ E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval);
+ } else {
+ DEBUGOUT("Flash controller busy, cannot get access");
+ }
+ }
+ return error;
+}
+
+/******************************************************************************
+ * This function starts a flash cycle and waits for its completion
+ *
+ * hw - The pointer to the hw structure
+ ****************************************************************************/
+static int32_t
+e1000_ich8_flash_cycle(struct e1000_hw *hw, uint32_t timeout)
+{
+ union ich8_hws_flash_ctrl hsflctl;
+ union ich8_hws_flash_status hsfsts;
+ int32_t error = E1000_ERR_EEPROM;
+ uint32_t i = 0;
+
+ /* Start a cycle by writing 1 in Flash Cycle Go in Hw Flash Control */
+ hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL);
+ hsflctl.hsf_ctrl.flcgo = 1;
+ E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval);
+
+ /* wait till FDONE bit is set to 1 */
+ do {
+ hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
+ if (hsfsts.hsf_status.flcdone == 1)
+ break;
+ udelay(1);
+ i++;
+ } while (i < timeout);
+ if (hsfsts.hsf_status.flcdone == 1 && hsfsts.hsf_status.flcerr == 0) {
+ error = E1000_SUCCESS;
+ }
+ return error;
+}
+
+/******************************************************************************
+ * Reads a byte or word from the NVM using the ICH8 flash access registers.
+ *
+ * hw - The pointer to the hw structure
+ * index - The index of the byte or word to read.
+ * size - Size of data to read, 1=byte 2=word
+ * data - Pointer to the word to store the value read.
+ *****************************************************************************/
+static int32_t
+e1000_read_ich8_data(struct e1000_hw *hw, uint32_t index,
+ uint32_t size, uint16_t* data)
+{
+ union ich8_hws_flash_status hsfsts;
+ union ich8_hws_flash_ctrl hsflctl;
+ uint32_t flash_linear_address;
+ uint32_t flash_data = 0;
+ int32_t error = -E1000_ERR_EEPROM;
+ int32_t count = 0;
+
+ DEBUGFUNC("e1000_read_ich8_data");
+
+ if (size < 1 || size > 2 || data == 0x0 ||
+ index > ICH_FLASH_LINEAR_ADDR_MASK)
+ return error;
+
+ flash_linear_address = (ICH_FLASH_LINEAR_ADDR_MASK & index) +
+ hw->flash_base_addr;
+
+ do {
+ udelay(1);
+ /* Steps */
+ error = e1000_ich8_cycle_init(hw);
+ if (error != E1000_SUCCESS)
+ break;
+
+ hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL);
+ /* 0b/1b corresponds to 1 or 2 byte size, respectively. */
+ hsflctl.hsf_ctrl.fldbcount = size - 1;
+ hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_READ;
+ E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval);
+
+ /* Write the last 24 bits of index into Flash Linear address field in
+ * Flash Address */
+ /* TODO: TBD maybe check the index against the size of flash */
+
+ E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_address);
+
+ error = e1000_ich8_flash_cycle(hw, ICH_FLASH_COMMAND_TIMEOUT);
+
+ /* Check if FCERR is set to 1, if set to 1, clear it and try the whole
+ * sequence a few more times, else read in (shift in) the Flash Data0,
+ * the order is least significant byte first msb to lsb */
+ if (error == E1000_SUCCESS) {
+ flash_data = E1000_READ_ICH_FLASH_REG(hw, ICH_FLASH_FDATA0);
+ if (size == 1) {
+ *data = (uint8_t)(flash_data & 0x000000FF);
+ } else if (size == 2) {
+ *data = (uint16_t)(flash_data & 0x0000FFFF);
+ }
+ break;
+ } else {
+ /* If we've gotten here, then things are probably completely hosed,
+ * but if the error condition is detected, it won't hurt to give
+ * it another try...ICH_FLASH_CYCLE_REPEAT_COUNT times.
+ */
+ hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
+ if (hsfsts.hsf_status.flcerr == 1) {
+ /* Repeat for some time before giving up. */
+ continue;
+ } else if (hsfsts.hsf_status.flcdone == 0) {
+ DEBUGOUT("Timeout error - flash cycle did not complete.");
+ break;
+ }
+ }
+ } while (count++ < ICH_FLASH_CYCLE_REPEAT_COUNT);
+
+ return error;
+}
+
+/******************************************************************************
+ * Writes One /two bytes to the NVM using the ICH8 flash access registers.
+ *
+ * hw - The pointer to the hw structure
+ * index - The index of the byte/word to read.
+ * size - Size of data to read, 1=byte 2=word
+ * data - The byte(s) to write to the NVM.
+ *****************************************************************************/
+static int32_t
+e1000_write_ich8_data(struct e1000_hw *hw, uint32_t index, uint32_t size,
+ uint16_t data)
+{
+ union ich8_hws_flash_status hsfsts;
+ union ich8_hws_flash_ctrl hsflctl;
+ uint32_t flash_linear_address;
+ uint32_t flash_data = 0;
+ int32_t error = -E1000_ERR_EEPROM;
+ int32_t count = 0;
+
+ DEBUGFUNC("e1000_write_ich8_data");
+
+ if (size < 1 || size > 2 || data > size * 0xff ||
+ index > ICH_FLASH_LINEAR_ADDR_MASK)
+ return error;
+
+ flash_linear_address = (ICH_FLASH_LINEAR_ADDR_MASK & index) +
+ hw->flash_base_addr;
+
+ do {
+ udelay(1);
+ /* Steps */
+ error = e1000_ich8_cycle_init(hw);
+ if (error != E1000_SUCCESS)
+ break;
+
+ hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL);
+ /* 0b/1b corresponds to 1 or 2 byte size, respectively. */
+ hsflctl.hsf_ctrl.fldbcount = size -1;
+ hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_WRITE;
+ E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval);
+
+ /* Write the last 24 bits of index into Flash Linear address field in
+ * Flash Address */
+ E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_address);
+
+ if (size == 1)
+ flash_data = (uint32_t)data & 0x00FF;
+ else
+ flash_data = (uint32_t)data;
+
+ E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FDATA0, flash_data);
+
+ /* check if FCERR is set to 1 , if set to 1, clear it and try the whole
+ * sequence a few more times else done */
+ error = e1000_ich8_flash_cycle(hw, ICH_FLASH_COMMAND_TIMEOUT);
+ if (error == E1000_SUCCESS) {
+ break;
+ } else {
+ /* If we're here, then things are most likely completely hosed,
+ * but if the error condition is detected, it won't hurt to give
+ * it another try...ICH_FLASH_CYCLE_REPEAT_COUNT times.
+ */
+ hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
+ if (hsfsts.hsf_status.flcerr == 1) {
+ /* Repeat for some time before giving up. */
+ continue;
+ } else if (hsfsts.hsf_status.flcdone == 0) {
+ DEBUGOUT("Timeout error - flash cycle did not complete.");
+ break;
+ }
+ }
+ } while (count++ < ICH_FLASH_CYCLE_REPEAT_COUNT);
+
+ return error;
+}
+
+/******************************************************************************
+ * Reads a single byte from the NVM using the ICH8 flash access registers.
+ *
+ * hw - pointer to e1000_hw structure
+ * index - The index of the byte to read.
+ * data - Pointer to a byte to store the value read.
+ *****************************************************************************/
+static int32_t
+e1000_read_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t* data)
+{
+ int32_t status = E1000_SUCCESS;
+ uint16_t word = 0;
+
+ status = e1000_read_ich8_data(hw, index, 1, &word);
+ if (status == E1000_SUCCESS) {
+ *data = (uint8_t)word;
+ }
+
+ return status;
+}
+
+/******************************************************************************
+ * Writes a single byte to the NVM using the ICH8 flash access registers.
+ * Performs verification by reading back the value and then going through
+ * a retry algorithm before giving up.
+ *
+ * hw - pointer to e1000_hw structure
+ * index - The index of the byte to write.
+ * byte - The byte to write to the NVM.
+ *****************************************************************************/
+static int32_t
+e1000_verify_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t byte)
+{
+ int32_t error = E1000_SUCCESS;
+ int32_t program_retries = 0;
+
+ DEBUGOUT2("Byte := %2.2X Offset := %ld\n", byte, index);
+
+ error = e1000_write_ich8_byte(hw, index, byte);
+
+ if (error != E1000_SUCCESS) {
+ for (program_retries = 0; program_retries < 100; program_retries++) {
+ DEBUGOUT2("Retrying \t Byte := %2.2X Offset := %ld\n", byte, index);
+ error = e1000_write_ich8_byte(hw, index, byte);
+ udelay(100);
+ if (error == E1000_SUCCESS)
+ break;
+ }
+ }
+
+ if (program_retries == 100)
+ error = E1000_ERR_EEPROM;
+
+ return error;
+}
+
+/******************************************************************************
+ * Writes a single byte to the NVM using the ICH8 flash access registers.
+ *
+ * hw - pointer to e1000_hw structure
+ * index - The index of the byte to read.
+ * data - The byte to write to the NVM.
+ *****************************************************************************/
+static int32_t
+e1000_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t data)
+{
+ int32_t status = E1000_SUCCESS;
+ uint16_t word = (uint16_t)data;
+
+ status = e1000_write_ich8_data(hw, index, 1, word);
+
+ return status;
+}
+
+/******************************************************************************
+ * Reads a word from the NVM using the ICH8 flash access registers.
+ *
+ * hw - pointer to e1000_hw structure
+ * index - The starting byte index of the word to read.
+ * data - Pointer to a word to store the value read.
+ *****************************************************************************/
+static int32_t
+e1000_read_ich8_word(struct e1000_hw *hw, uint32_t index, uint16_t *data)
+{
+ int32_t status = E1000_SUCCESS;
+ status = e1000_read_ich8_data(hw, index, 2, data);
+ return status;
+}
+
+/******************************************************************************
+ * Erases the bank specified. Each bank may be a 4, 8 or 64k block. Banks are 0
+ * based.
+ *
+ * hw - pointer to e1000_hw structure
+ * bank - 0 for first bank, 1 for second bank
+ *
+ * Note that this function may actually erase as much as 8 or 64 KBytes. The
+ * amount of NVM used in each bank is a *minimum* of 4 KBytes, but in fact the
+ * bank size may be 4, 8 or 64 KBytes
+ *****************************************************************************/
+int32_t
+e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t bank)
+{
+ union ich8_hws_flash_status hsfsts;
+ union ich8_hws_flash_ctrl hsflctl;
+ uint32_t flash_linear_address;
+ int32_t count = 0;
+ int32_t error = E1000_ERR_EEPROM;
+ int32_t iteration;
+ int32_t sub_sector_size = 0;
+ int32_t bank_size;
+ int32_t j = 0;
+ int32_t error_flag = 0;
+
+ hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
+
+ /* Determine HW Sector size: Read BERASE bits of Hw flash Status register */
+ /* 00: The Hw sector is 256 bytes, hence we need to erase 16
+ * consecutive sectors. The start index for the nth Hw sector can be
+ * calculated as bank * 4096 + n * 256
+ * 01: The Hw sector is 4K bytes, hence we need to erase 1 sector.
+ * The start index for the nth Hw sector can be calculated
+ * as bank * 4096
+ * 10: The HW sector is 8K bytes
+ * 11: The Hw sector size is 64K bytes */
+ if (hsfsts.hsf_status.berasesz == 0x0) {
+ /* Hw sector size 256 */
+ sub_sector_size = ICH_FLASH_SEG_SIZE_256;
+ bank_size = ICH_FLASH_SECTOR_SIZE;
+ iteration = ICH_FLASH_SECTOR_SIZE / ICH_FLASH_SEG_SIZE_256;
+ } else if (hsfsts.hsf_status.berasesz == 0x1) {
+ bank_size = ICH_FLASH_SEG_SIZE_4K;
+ iteration = 1;
+ } else if (hsfsts.hsf_status.berasesz == 0x3) {
+ bank_size = ICH_FLASH_SEG_SIZE_64K;
+ iteration = 1;
+ } else {
+ return error;
+ }
+
+ for (j = 0; j < iteration ; j++) {
+ do {
+ count++;
+ /* Steps */
+ error = e1000_ich8_cycle_init(hw);
+ if (error != E1000_SUCCESS) {
+ error_flag = 1;
+ break;
+ }
+
+ /* Write a value 11 (block Erase) in Flash Cycle field in Hw flash
+ * Control */
+ hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL);
+ hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_ERASE;
+ E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval);
+
+ /* Write the last 24 bits of an index within the block into Flash
+ * Linear address field in Flash Address. This probably needs to
+ * be calculated here based off the on-chip erase sector size and
+ * the software bank size (4, 8 or 64 KBytes) */
+ flash_linear_address = bank * bank_size + j * sub_sector_size;
+ flash_linear_address += hw->flash_base_addr;
+ flash_linear_address &= ICH_FLASH_LINEAR_ADDR_MASK;
+
+ E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_address);
+
+ error = e1000_ich8_flash_cycle(hw, ICH_FLASH_ERASE_TIMEOUT);
+ /* Check if FCERR is set to 1. If 1, clear it and try the whole
+ * sequence a few more times else Done */
+ if (error == E1000_SUCCESS) {
+ break;
+ } else {
+ hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
+ if (hsfsts.hsf_status.flcerr == 1) {
+ /* repeat for some time before giving up */
+ continue;
+ } else if (hsfsts.hsf_status.flcdone == 0) {
+ error_flag = 1;
+ break;
+ }
+ }
+ } while ((count < ICH_FLASH_CYCLE_REPEAT_COUNT) && !error_flag);
+ if (error_flag == 1)
+ break;
+ }
+ if (error_flag != 1)
+ error = E1000_SUCCESS;
+ return error;
+}
+
+static int32_t
+e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw,
+ uint32_t cnf_base_addr, uint32_t cnf_size)
+{
+ uint32_t ret_val = E1000_SUCCESS;
+ uint16_t word_addr, reg_data, reg_addr;
+ uint16_t i;
+
+ /* cnf_base_addr is in DWORD */
+ word_addr = (uint16_t)(cnf_base_addr << 1);
+
+ /* cnf_size is returned in size of dwords */
+ for (i = 0; i < cnf_size; i++) {
+ ret_val = e1000_read_eeprom(hw, (word_addr + i*2), 1, &reg_data);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_read_eeprom(hw, (word_addr + i*2 + 1), 1, &reg_addr);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = e1000_get_software_flag(hw);
+ if (ret_val != E1000_SUCCESS)
+ return ret_val;
+
+ ret_val = e1000_write_phy_reg_ex(hw, (uint32_t)reg_addr, reg_data);
+
+ e1000_release_software_flag(hw);
+ }
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ * This function initializes the PHY from the NVM on ICH8 platforms. This
+ * is needed due to an issue where the NVM configuration is not properly
+ * autoloaded after power transitions. Therefore, after each PHY reset, we
+ * will load the configuration data out of the NVM manually.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int32_t
+e1000_init_lcd_from_nvm(struct e1000_hw *hw)
+{
+ uint32_t reg_data, cnf_base_addr, cnf_size, ret_val, loop;
+
+ if (hw->phy_type != e1000_phy_igp_3)
+ return E1000_SUCCESS;
+
+ /* Check if SW needs configure the PHY */
+ reg_data = E1000_READ_REG(hw, FEXTNVM);
+ if (!(reg_data & FEXTNVM_SW_CONFIG))
+ return E1000_SUCCESS;
+
+ /* Wait for basic configuration completes before proceeding*/
+ loop = 0;
+ do {
+ reg_data = E1000_READ_REG(hw, STATUS) & E1000_STATUS_LAN_INIT_DONE;
+ udelay(100);
+ loop++;
+ } while ((!reg_data) && (loop < 50));
+
+ /* Clear the Init Done bit for the next init event */
+ reg_data = E1000_READ_REG(hw, STATUS);
+ reg_data &= ~E1000_STATUS_LAN_INIT_DONE;
+ E1000_WRITE_REG(hw, STATUS, reg_data);
+
+ /* Make sure HW does not configure LCD from PHY extended configuration
+ before SW configuration */
+ reg_data = E1000_READ_REG(hw, EXTCNF_CTRL);
+ if ((reg_data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE) == 0x0000) {
+ reg_data = E1000_READ_REG(hw, EXTCNF_SIZE);
+ cnf_size = reg_data & E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH;
+ cnf_size >>= 16;
+ if (cnf_size) {
+ reg_data = E1000_READ_REG(hw, EXTCNF_CTRL);
+ cnf_base_addr = reg_data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER;
+ /* cnf_base_addr is in DWORD */
+ cnf_base_addr >>= 16;
+
+ /* Configure LCD from extended configuration region. */
+ ret_val = e1000_init_lcd_from_nvm_config_region(hw, cnf_base_addr,
+ cnf_size);
+ if (ret_val)
+ return ret_val;
+ }
+ }
+
+ return E1000_SUCCESS;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/e1000/e1000_hw.h b/gpxe/src/drivers/net/e1000/e1000_hw.h
new file mode 100644
index 00000000..9e319395
--- /dev/null
+++ b/gpxe/src/drivers/net/e1000/e1000_hw.h
@@ -0,0 +1,3413 @@
+/*******************************************************************************
+
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+/* e1000_hw.h
+ * Structures, enums, and macros for the MAC
+ */
+
+#ifndef _E1000_HW_H_
+#define _E1000_HW_H_
+
+#include "e1000_osdep.h"
+
+
+/* Forward declarations of structures used by the shared code */
+struct e1000_hw;
+struct e1000_hw_stats;
+
+/* Enumerated types specific to the e1000 hardware */
+/* Media Access Controlers */
+typedef enum {
+ e1000_undefined = 0,
+ e1000_82542_rev2_0,
+ e1000_82542_rev2_1,
+ e1000_82543,
+ e1000_82544,
+ e1000_82540,
+ e1000_82545,
+ e1000_82545_rev_3,
+ e1000_82546,
+ e1000_82546_rev_3,
+ e1000_82541,
+ e1000_82541_rev_2,
+ e1000_82547,
+ e1000_82547_rev_2,
+ e1000_82571,
+ e1000_82572,
+ e1000_82573,
+ e1000_80003es2lan,
+ e1000_ich8lan,
+ e1000_num_macs
+} e1000_mac_type;
+
+typedef enum {
+ e1000_eeprom_uninitialized = 0,
+ e1000_eeprom_spi,
+ e1000_eeprom_microwire,
+ e1000_eeprom_flash,
+ e1000_eeprom_ich8,
+ e1000_eeprom_none, /* No NVM support */
+ e1000_num_eeprom_types
+} e1000_eeprom_type;
+
+/* Media Types */
+typedef enum {
+ e1000_media_type_copper = 0,
+ e1000_media_type_fiber = 1,
+ e1000_media_type_internal_serdes = 2,
+ e1000_num_media_types
+} e1000_media_type;
+
+typedef enum {
+ e1000_10_half = 0,
+ e1000_10_full = 1,
+ e1000_100_half = 2,
+ e1000_100_full = 3
+} e1000_speed_duplex_type;
+
+/* Flow Control Settings */
+typedef enum {
+ E1000_FC_NONE = 0,
+ E1000_FC_RX_PAUSE = 1,
+ E1000_FC_TX_PAUSE = 2,
+ E1000_FC_FULL = 3,
+ E1000_FC_DEFAULT = 0xFF
+} e1000_fc_type;
+
+struct e1000_shadow_ram {
+ uint16_t eeprom_word;
+ boolean_t modified;
+};
+
+/* PCI bus types */
+typedef enum {
+ e1000_bus_type_unknown = 0,
+ e1000_bus_type_pci,
+ e1000_bus_type_pcix,
+ e1000_bus_type_pci_express,
+ e1000_bus_type_reserved
+} e1000_bus_type;
+
+/* PCI bus speeds */
+typedef enum {
+ e1000_bus_speed_unknown = 0,
+ e1000_bus_speed_33,
+ e1000_bus_speed_66,
+ e1000_bus_speed_100,
+ e1000_bus_speed_120,
+ e1000_bus_speed_133,
+ e1000_bus_speed_2500,
+ e1000_bus_speed_reserved
+} e1000_bus_speed;
+
+/* PCI bus widths */
+typedef enum {
+ e1000_bus_width_unknown = 0,
+ /* These PCIe values should literally match the possible return values
+ * from config space */
+ e1000_bus_width_pciex_1 = 1,
+ e1000_bus_width_pciex_2 = 2,
+ e1000_bus_width_pciex_4 = 4,
+ e1000_bus_width_32,
+ e1000_bus_width_64,
+ e1000_bus_width_reserved
+} e1000_bus_width;
+
+/* PHY status info structure and supporting enums */
+typedef enum {
+ e1000_cable_length_50 = 0,
+ e1000_cable_length_50_80,
+ e1000_cable_length_80_110,
+ e1000_cable_length_110_140,
+ e1000_cable_length_140,
+ e1000_cable_length_undefined = 0xFF
+} e1000_cable_length;
+
+typedef enum {
+ e1000_gg_cable_length_60 = 0,
+ e1000_gg_cable_length_60_115 = 1,
+ e1000_gg_cable_length_115_150 = 2,
+ e1000_gg_cable_length_150 = 4
+} e1000_gg_cable_length;
+
+typedef enum {
+ e1000_igp_cable_length_10 = 10,
+ e1000_igp_cable_length_20 = 20,
+ e1000_igp_cable_length_30 = 30,
+ e1000_igp_cable_length_40 = 40,
+ e1000_igp_cable_length_50 = 50,
+ e1000_igp_cable_length_60 = 60,
+ e1000_igp_cable_length_70 = 70,
+ e1000_igp_cable_length_80 = 80,
+ e1000_igp_cable_length_90 = 90,
+ e1000_igp_cable_length_100 = 100,
+ e1000_igp_cable_length_110 = 110,
+ e1000_igp_cable_length_115 = 115,
+ e1000_igp_cable_length_120 = 120,
+ e1000_igp_cable_length_130 = 130,
+ e1000_igp_cable_length_140 = 140,
+ e1000_igp_cable_length_150 = 150,
+ e1000_igp_cable_length_160 = 160,
+ e1000_igp_cable_length_170 = 170,
+ e1000_igp_cable_length_180 = 180
+} e1000_igp_cable_length;
+
+typedef enum {
+ e1000_10bt_ext_dist_enable_normal = 0,
+ e1000_10bt_ext_dist_enable_lower,
+ e1000_10bt_ext_dist_enable_undefined = 0xFF
+} e1000_10bt_ext_dist_enable;
+
+typedef enum {
+ e1000_rev_polarity_normal = 0,
+ e1000_rev_polarity_reversed,
+ e1000_rev_polarity_undefined = 0xFF
+} e1000_rev_polarity;
+
+typedef enum {
+ e1000_downshift_normal = 0,
+ e1000_downshift_activated,
+ e1000_downshift_undefined = 0xFF
+} e1000_downshift;
+
+typedef enum {
+ e1000_smart_speed_default = 0,
+ e1000_smart_speed_on,
+ e1000_smart_speed_off
+} e1000_smart_speed;
+
+typedef enum {
+ e1000_polarity_reversal_enabled = 0,
+ e1000_polarity_reversal_disabled,
+ e1000_polarity_reversal_undefined = 0xFF
+} e1000_polarity_reversal;
+
+typedef enum {
+ e1000_auto_x_mode_manual_mdi = 0,
+ e1000_auto_x_mode_manual_mdix,
+ e1000_auto_x_mode_auto1,
+ e1000_auto_x_mode_auto2,
+ e1000_auto_x_mode_undefined = 0xFF
+} e1000_auto_x_mode;
+
+typedef enum {
+ e1000_1000t_rx_status_not_ok = 0,
+ e1000_1000t_rx_status_ok,
+ e1000_1000t_rx_status_undefined = 0xFF
+} e1000_1000t_rx_status;
+
+typedef enum {
+ e1000_phy_m88 = 0,
+ e1000_phy_igp,
+ e1000_phy_igp_2,
+ e1000_phy_gg82563,
+ e1000_phy_igp_3,
+ e1000_phy_ife,
+ e1000_phy_undefined = 0xFF
+} e1000_phy_type;
+
+typedef enum {
+ e1000_ms_hw_default = 0,
+ e1000_ms_force_master,
+ e1000_ms_force_slave,
+ e1000_ms_auto
+} e1000_ms_type;
+
+typedef enum {
+ e1000_ffe_config_enabled = 0,
+ e1000_ffe_config_active,
+ e1000_ffe_config_blocked
+} e1000_ffe_config;
+
+typedef enum {
+ e1000_dsp_config_disabled = 0,
+ e1000_dsp_config_enabled,
+ e1000_dsp_config_activated,
+ e1000_dsp_config_undefined = 0xFF
+} e1000_dsp_config;
+
+struct e1000_phy_info {
+ e1000_cable_length cable_length;
+ e1000_10bt_ext_dist_enable extended_10bt_distance;
+ e1000_rev_polarity cable_polarity;
+ e1000_downshift downshift;
+ e1000_polarity_reversal polarity_correction;
+ e1000_auto_x_mode mdix_mode;
+ e1000_1000t_rx_status local_rx;
+ e1000_1000t_rx_status remote_rx;
+};
+
+struct e1000_phy_stats {
+ uint32_t idle_errors;
+ uint32_t receive_errors;
+};
+
+struct e1000_eeprom_info {
+ e1000_eeprom_type type;
+ uint16_t word_size;
+ uint16_t opcode_bits;
+ uint16_t address_bits;
+ uint16_t delay_usec;
+ uint16_t page_size;
+ boolean_t use_eerd;
+ boolean_t use_eewr;
+};
+
+/* Flex ASF Information */
+#define E1000_HOST_IF_MAX_SIZE 2048
+
+typedef enum {
+ e1000_byte_align = 0,
+ e1000_word_align = 1,
+ e1000_dword_align = 2
+} e1000_align_type;
+
+
+
+/* Error Codes */
+#define E1000_SUCCESS 0
+#define E1000_ERR_EEPROM 1
+#define E1000_ERR_PHY 2
+#define E1000_ERR_CONFIG 3
+#define E1000_ERR_PARAM 4
+#define E1000_ERR_MAC_TYPE 5
+#define E1000_ERR_PHY_TYPE 6
+#define E1000_ERR_RESET 9
+#define E1000_ERR_MASTER_REQUESTS_PENDING 10
+#define E1000_ERR_HOST_INTERFACE_COMMAND 11
+#define E1000_BLK_PHY_RESET 12
+#define E1000_ERR_SWFW_SYNC 13
+
+#define E1000_BYTE_SWAP_WORD(_value) ((((_value) & 0x00ff) << 8) | \
+ (((_value) & 0xff00) >> 8))
+
+/* Function prototypes */
+/* Initialization */
+int32_t e1000_reset_hw(struct e1000_hw *hw);
+int32_t e1000_init_hw(struct e1000_hw *hw);
+int32_t e1000_set_mac_type(struct e1000_hw *hw);
+void e1000_set_media_type(struct e1000_hw *hw);
+
+/* Link Configuration */
+int32_t e1000_setup_link(struct e1000_hw *hw);
+int32_t e1000_phy_setup_autoneg(struct e1000_hw *hw);
+void e1000_config_collision_dist(struct e1000_hw *hw);
+int32_t e1000_check_for_link(struct e1000_hw *hw);
+int32_t e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t *speed, uint16_t *duplex);
+int32_t e1000_force_mac_fc(struct e1000_hw *hw);
+
+/* PHY */
+int32_t e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data);
+int32_t e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t data);
+int32_t e1000_phy_hw_reset(struct e1000_hw *hw);
+int32_t e1000_phy_reset(struct e1000_hw *hw);
+int32_t e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info);
+int32_t e1000_validate_mdi_setting(struct e1000_hw *hw);
+
+void e1000_phy_powerdown_workaround(struct e1000_hw *hw);
+
+/* EEPROM Functions */
+int32_t e1000_init_eeprom_params(struct e1000_hw *hw);
+
+/* MNG HOST IF functions */
+uint32_t e1000_enable_mng_pass_thru(struct e1000_hw *hw);
+
+#define E1000_MNG_DHCP_TX_PAYLOAD_CMD 64
+#define E1000_HI_MAX_MNG_DATA_LENGTH 0x6F8 /* Host Interface data length */
+
+#define E1000_MNG_DHCP_COMMAND_TIMEOUT 10 /* Time in ms to process MNG command */
+#define E1000_MNG_DHCP_COOKIE_OFFSET 0x6F0 /* Cookie offset */
+#define E1000_MNG_DHCP_COOKIE_LENGTH 0x10 /* Cookie length */
+#define E1000_MNG_IAMT_MODE 0x3
+#define E1000_MNG_ICH_IAMT_MODE 0x2
+#define E1000_IAMT_SIGNATURE 0x544D4149 /* Intel(R) Active Management Technology signature */
+
+#define E1000_MNG_DHCP_COOKIE_STATUS_PARSING_SUPPORT 0x1 /* DHCP parsing enabled */
+#define E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT 0x2 /* DHCP parsing enabled */
+#define E1000_VFTA_ENTRY_SHIFT 0x5
+#define E1000_VFTA_ENTRY_MASK 0x7F
+#define E1000_VFTA_ENTRY_BIT_SHIFT_MASK 0x1F
+
+struct e1000_host_mng_command_header {
+ uint8_t command_id;
+ uint8_t checksum;
+ uint16_t reserved1;
+ uint16_t reserved2;
+ uint16_t command_length;
+};
+
+struct e1000_host_mng_command_info {
+ struct e1000_host_mng_command_header command_header; /* Command Head/Command Result Head has 4 bytes */
+ uint8_t command_data[E1000_HI_MAX_MNG_DATA_LENGTH]; /* Command data can length 0..0x658*/
+};
+#ifdef __BIG_ENDIAN
+struct e1000_host_mng_dhcp_cookie{
+ uint32_t signature;
+ uint16_t vlan_id;
+ uint8_t reserved0;
+ uint8_t status;
+ uint32_t reserved1;
+ uint8_t checksum;
+ uint8_t reserved3;
+ uint16_t reserved2;
+};
+#else
+struct e1000_host_mng_dhcp_cookie{
+ uint32_t signature;
+ uint8_t status;
+ uint8_t reserved0;
+ uint16_t vlan_id;
+ uint32_t reserved1;
+ uint16_t reserved2;
+ uint8_t reserved3;
+ uint8_t checksum;
+};
+#endif
+
+int32_t e1000_mng_write_dhcp_info(struct e1000_hw *hw, uint8_t *buffer,
+ uint16_t length);
+boolean_t e1000_check_mng_mode(struct e1000_hw *hw);
+boolean_t e1000_enable_tx_pkt_filtering(struct e1000_hw *hw);
+int32_t e1000_read_eeprom(struct e1000_hw *hw, uint16_t reg, uint16_t words, uint16_t *data);
+int32_t e1000_validate_eeprom_checksum(struct e1000_hw *hw);
+int32_t e1000_update_eeprom_checksum(struct e1000_hw *hw);
+int32_t e1000_write_eeprom(struct e1000_hw *hw, uint16_t reg, uint16_t words, uint16_t *data);
+int32_t e1000_read_mac_addr(struct e1000_hw * hw);
+
+/* Filters (multicast, vlan, receive) */
+uint32_t e1000_hash_mc_addr(struct e1000_hw *hw, uint8_t * mc_addr);
+void e1000_mta_set(struct e1000_hw *hw, uint32_t hash_value);
+void e1000_rar_set(struct e1000_hw *hw, uint8_t * mc_addr, uint32_t rar_index);
+void e1000_write_vfta(struct e1000_hw *hw, uint32_t offset, uint32_t value);
+
+/* LED functions */
+int32_t e1000_setup_led(struct e1000_hw *hw);
+int32_t e1000_cleanup_led(struct e1000_hw *hw);
+int32_t e1000_led_on(struct e1000_hw *hw);
+int32_t e1000_led_off(struct e1000_hw *hw);
+int32_t e1000_blink_led_start(struct e1000_hw *hw);
+
+/* Adaptive IFS Functions */
+
+/* Everything else */
+void e1000_reset_adaptive(struct e1000_hw *hw);
+void e1000_update_adaptive(struct e1000_hw *hw);
+void e1000_tbi_adjust_stats(struct e1000_hw *hw, struct e1000_hw_stats *stats, uint32_t frame_len, uint8_t * mac_addr);
+void e1000_get_bus_info(struct e1000_hw *hw);
+void e1000_pci_set_mwi(struct e1000_hw *hw);
+void e1000_pci_clear_mwi(struct e1000_hw *hw);
+void e1000_read_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value);
+void e1000_write_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value);
+int32_t e1000_read_pcie_cap_reg(struct e1000_hw *hw, uint32_t reg, uint16_t *value);
+/* Port I/O is only supported on 82544 and newer */
+void e1000_io_write(struct e1000_hw *hw, unsigned long port, uint32_t value);
+int32_t e1000_disable_pciex_master(struct e1000_hw *hw);
+int32_t e1000_check_phy_reset_block(struct e1000_hw *hw);
+
+
+#define E1000_READ_REG_IO(a, reg) \
+ e1000_read_reg_io((a), E1000_##reg)
+#define E1000_WRITE_REG_IO(a, reg, val) \
+ e1000_write_reg_io((a), E1000_##reg, val)
+
+/* PCI Device IDs */
+#define E1000_DEV_ID_82542 0x1000
+#define E1000_DEV_ID_82543GC_FIBER 0x1001
+#define E1000_DEV_ID_82543GC_COPPER 0x1004
+#define E1000_DEV_ID_82544EI_COPPER 0x1008
+#define E1000_DEV_ID_82544EI_FIBER 0x1009
+#define E1000_DEV_ID_82544GC_COPPER 0x100C
+#define E1000_DEV_ID_82544GC_LOM 0x100D
+#define E1000_DEV_ID_82540EM 0x100E
+#define E1000_DEV_ID_82540EM_LOM 0x1015
+#define E1000_DEV_ID_82540EP_LOM 0x1016
+#define E1000_DEV_ID_82540EP 0x1017
+#define E1000_DEV_ID_82540EP_LP 0x101E
+#define E1000_DEV_ID_82545EM_COPPER 0x100F
+#define E1000_DEV_ID_82545EM_FIBER 0x1011
+#define E1000_DEV_ID_82545GM_COPPER 0x1026
+#define E1000_DEV_ID_82545GM_FIBER 0x1027
+#define E1000_DEV_ID_82545GM_SERDES 0x1028
+#define E1000_DEV_ID_82546EB_COPPER 0x1010
+#define E1000_DEV_ID_82546EB_FIBER 0x1012
+#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D
+#define E1000_DEV_ID_82541EI 0x1013
+#define E1000_DEV_ID_82541EI_MOBILE 0x1018
+#define E1000_DEV_ID_82541ER_LOM 0x1014
+#define E1000_DEV_ID_82541ER 0x1078
+#define E1000_DEV_ID_82547GI 0x1075
+#define E1000_DEV_ID_82541GI 0x1076
+#define E1000_DEV_ID_82541GI_MOBILE 0x1077
+#define E1000_DEV_ID_82541GI_LF 0x107C
+#define E1000_DEV_ID_82546GB_COPPER 0x1079
+#define E1000_DEV_ID_82546GB_FIBER 0x107A
+#define E1000_DEV_ID_82546GB_SERDES 0x107B
+#define E1000_DEV_ID_82546GB_PCIE 0x108A
+#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099
+#define E1000_DEV_ID_82547EI 0x1019
+#define E1000_DEV_ID_82547EI_MOBILE 0x101A
+#define E1000_DEV_ID_82571EB_COPPER 0x105E
+#define E1000_DEV_ID_82571EB_FIBER 0x105F
+#define E1000_DEV_ID_82571EB_SERDES 0x1060
+#define E1000_DEV_ID_82571EB_QUAD_COPPER 0x10A4
+#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5
+#define E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE 0x10BC
+#define E1000_DEV_ID_82571EB_SERDES_DUAL 0x10D9
+#define E1000_DEV_ID_82571EB_SERDES_QUAD 0x10DA
+#define E1000_DEV_ID_82572EI_COPPER 0x107D
+#define E1000_DEV_ID_82572EI_FIBER 0x107E
+#define E1000_DEV_ID_82572EI_SERDES 0x107F
+#define E1000_DEV_ID_82572EI 0x10B9
+#define E1000_DEV_ID_82573E 0x108B
+#define E1000_DEV_ID_82573E_IAMT 0x108C
+#define E1000_DEV_ID_82573L 0x109A
+#define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5
+#define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096
+#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098
+#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA
+#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB
+
+#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049
+#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A
+#define E1000_DEV_ID_ICH8_IGP_C 0x104B
+#define E1000_DEV_ID_ICH8_IFE 0x104C
+#define E1000_DEV_ID_ICH8_IFE_GT 0x10C4
+#define E1000_DEV_ID_ICH8_IFE_G 0x10C5
+#define E1000_DEV_ID_ICH8_IGP_M 0x104D
+
+
+#define NODE_ADDRESS_SIZE 6
+#define ETH_LENGTH_OF_ADDRESS 6
+
+/* MAC decode size is 128K - This is the size of BAR0 */
+#define MAC_DECODE_SIZE (128 * 1024)
+
+#define E1000_82542_2_0_REV_ID 2
+#define E1000_82542_2_1_REV_ID 3
+#define E1000_REVISION_0 0
+#define E1000_REVISION_1 1
+#define E1000_REVISION_2 2
+#define E1000_REVISION_3 3
+
+#define SPEED_10 10
+#define SPEED_100 100
+#define SPEED_1000 1000
+#define HALF_DUPLEX 1
+#define FULL_DUPLEX 2
+
+/* The sizes (in bytes) of a ethernet packet */
+#define ENET_HEADER_SIZE 14
+#define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* With FCS */
+#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* With FCS */
+#define ETHERNET_FCS_SIZE 4
+#define MAXIMUM_ETHERNET_PACKET_SIZE \
+ (MAXIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE)
+#define MINIMUM_ETHERNET_PACKET_SIZE \
+ (MINIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE)
+#define CRC_LENGTH ETHERNET_FCS_SIZE
+#define MAX_JUMBO_FRAME_SIZE 0x3F00
+
+
+/* 802.1q VLAN Packet Sizes */
+#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMAed) */
+
+/* Ethertype field values */
+#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */
+#define ETHERNET_IP_TYPE 0x0800 /* IP packets */
+#define ETHERNET_ARP_TYPE 0x0806 /* Address Resolution Protocol (ARP) */
+
+/* Packet Header defines */
+#define IP_PROTOCOL_TCP 6
+#define IP_PROTOCOL_UDP 0x11
+
+/* This defines the bits that are set in the Interrupt Mask
+ * Set/Read Register. Each bit is documented below:
+ * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0)
+ * o RXSEQ = Receive Sequence Error
+ */
+#define POLL_IMS_ENABLE_MASK ( \
+ E1000_IMS_RXDMT0 | \
+ E1000_IMS_RXSEQ)
+
+/* This defines the bits that are set in the Interrupt Mask
+ * Set/Read Register. Each bit is documented below:
+ * o RXT0 = Receiver Timer Interrupt (ring 0)
+ * o TXDW = Transmit Descriptor Written Back
+ * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0)
+ * o RXSEQ = Receive Sequence Error
+ * o LSC = Link Status Change
+ */
+#define IMS_ENABLE_MASK ( \
+ E1000_IMS_RXT0 | \
+ E1000_IMS_TXDW | \
+ E1000_IMS_RXDMT0 | \
+ E1000_IMS_RXSEQ | \
+ E1000_IMS_LSC)
+
+/* Additional interrupts need to be handled for e1000_ich8lan:
+ DSW = The FW changed the status of the DISSW bit in FWSM
+ PHYINT = The LAN connected device generates an interrupt
+ EPRST = Manageability reset event */
+#define IMS_ICH8LAN_ENABLE_MASK (\
+ E1000_IMS_DSW | \
+ E1000_IMS_PHYINT | \
+ E1000_IMS_EPRST)
+
+/* Number of high/low register pairs in the RAR. The RAR (Receive Address
+ * Registers) holds the directed and multicast addresses that we monitor. We
+ * reserve one of these spots for our directed address, allowing us room for
+ * E1000_RAR_ENTRIES - 1 multicast addresses.
+ */
+#define E1000_RAR_ENTRIES 15
+
+#define E1000_RAR_ENTRIES_ICH8LAN 6
+
+#define MIN_NUMBER_OF_DESCRIPTORS 8
+#define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8
+
+/* Receive Descriptor */
+struct e1000_rx_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's data buffer */
+ uint16_t length; /* Length of data DMAed into data buffer */
+ uint16_t csum; /* Packet checksum */
+ uint8_t status; /* Descriptor status */
+ uint8_t errors; /* Descriptor Errors */
+ uint16_t special;
+};
+
+/* Receive Descriptor - Extended */
+union e1000_rx_desc_extended {
+ struct {
+ uint64_t buffer_addr;
+ uint64_t reserved;
+ } read;
+ struct {
+ struct {
+ uint32_t mrq; /* Multiple Rx Queues */
+ union {
+ uint32_t rss; /* RSS Hash */
+ struct {
+ uint16_t ip_id; /* IP id */
+ uint16_t csum; /* Packet Checksum */
+ } csum_ip;
+ } hi_dword;
+ } lower;
+ struct {
+ uint32_t status_error; /* ext status/error */
+ uint16_t length;
+ uint16_t vlan; /* VLAN tag */
+ } upper;
+ } wb; /* writeback */
+};
+
+#define MAX_PS_BUFFERS 4
+/* Receive Descriptor - Packet Split */
+union e1000_rx_desc_packet_split {
+ struct {
+ /* one buffer for protocol header(s), three data buffers */
+ uint64_t buffer_addr[MAX_PS_BUFFERS];
+ } read;
+ struct {
+ struct {
+ uint32_t mrq; /* Multiple Rx Queues */
+ union {
+ uint32_t rss; /* RSS Hash */
+ struct {
+ uint16_t ip_id; /* IP id */
+ uint16_t csum; /* Packet Checksum */
+ } csum_ip;
+ } hi_dword;
+ } lower;
+ struct {
+ uint32_t status_error; /* ext status/error */
+ uint16_t length0; /* length of buffer 0 */
+ uint16_t vlan; /* VLAN tag */
+ } middle;
+ struct {
+ uint16_t header_status;
+ uint16_t length[3]; /* length of buffers 1-3 */
+ } upper;
+ uint64_t reserved;
+ } wb; /* writeback */
+};
+
+/* Receive Decriptor bit definitions */
+#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */
+#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */
+#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */
+#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */
+#define E1000_RXD_STAT_UDPCS 0x10 /* UDP xsum caculated */
+#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */
+#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */
+#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */
+#define E1000_RXD_STAT_IPIDV 0x200 /* IP identification valid */
+#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */
+#define E1000_RXD_STAT_ACK 0x8000 /* ACK Packet indication */
+#define E1000_RXD_ERR_CE 0x01 /* CRC Error */
+#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */
+#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */
+#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */
+#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */
+#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */
+#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */
+#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */
+#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */
+#define E1000_RXD_SPC_PRI_SHIFT 13
+#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */
+#define E1000_RXD_SPC_CFI_SHIFT 12
+
+#define E1000_RXDEXT_STATERR_CE 0x01000000
+#define E1000_RXDEXT_STATERR_SE 0x02000000
+#define E1000_RXDEXT_STATERR_SEQ 0x04000000
+#define E1000_RXDEXT_STATERR_CXE 0x10000000
+#define E1000_RXDEXT_STATERR_TCPE 0x20000000
+#define E1000_RXDEXT_STATERR_IPE 0x40000000
+#define E1000_RXDEXT_STATERR_RXE 0x80000000
+
+#define E1000_RXDPS_HDRSTAT_HDRSP 0x00008000
+#define E1000_RXDPS_HDRSTAT_HDRLEN_MASK 0x000003FF
+
+/* mask to determine if packets should be dropped due to frame errors */
+#define E1000_RXD_ERR_FRAME_ERR_MASK ( \
+ E1000_RXD_ERR_CE | \
+ E1000_RXD_ERR_SE | \
+ E1000_RXD_ERR_SEQ | \
+ E1000_RXD_ERR_CXE | \
+ E1000_RXD_ERR_RXE)
+
+
+/* Same mask, but for extended and packet split descriptors */
+#define E1000_RXDEXT_ERR_FRAME_ERR_MASK ( \
+ E1000_RXDEXT_STATERR_CE | \
+ E1000_RXDEXT_STATERR_SE | \
+ E1000_RXDEXT_STATERR_SEQ | \
+ E1000_RXDEXT_STATERR_CXE | \
+ E1000_RXDEXT_STATERR_RXE)
+
+
+/* Transmit Descriptor */
+struct e1000_tx_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's data buffer */
+ union {
+ uint32_t data;
+ struct {
+ uint16_t length; /* Data buffer length */
+ uint8_t cso; /* Checksum offset */
+ uint8_t cmd; /* Descriptor control */
+ } flags;
+ } lower;
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t css; /* Checksum start */
+ uint16_t special;
+ } fields;
+ } upper;
+};
+
+/* Transmit Descriptor bit definitions */
+#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */
+#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */
+#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */
+#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */
+#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */
+#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */
+#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */
+#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */
+#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */
+#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */
+#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */
+#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */
+#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */
+#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */
+#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */
+#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */
+#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */
+#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */
+#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */
+#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */
+
+/* Offload Context Descriptor */
+struct e1000_context_desc {
+ union {
+ uint32_t ip_config;
+ struct {
+ uint8_t ipcss; /* IP checksum start */
+ uint8_t ipcso; /* IP checksum offset */
+ uint16_t ipcse; /* IP checksum end */
+ } ip_fields;
+ } lower_setup;
+ union {
+ uint32_t tcp_config;
+ struct {
+ uint8_t tucss; /* TCP checksum start */
+ uint8_t tucso; /* TCP checksum offset */
+ uint16_t tucse; /* TCP checksum end */
+ } tcp_fields;
+ } upper_setup;
+ uint32_t cmd_and_length; /* */
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t hdr_len; /* Header length */
+ uint16_t mss; /* Maximum segment size */
+ } fields;
+ } tcp_seg_setup;
+};
+
+/* Offload data descriptor */
+struct e1000_data_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's buffer address */
+ union {
+ uint32_t data;
+ struct {
+ uint16_t length; /* Data buffer length */
+ uint8_t typ_len_ext; /* */
+ uint8_t cmd; /* */
+ } flags;
+ } lower;
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t popts; /* Packet Options */
+ uint16_t special; /* */
+ } fields;
+ } upper;
+};
+
+/* Filters */
+#define E1000_NUM_UNICAST 16 /* Unicast filter entries */
+#define E1000_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */
+#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */
+
+#define E1000_NUM_UNICAST_ICH8LAN 7
+#define E1000_MC_TBL_SIZE_ICH8LAN 32
+
+
+/* Receive Address Register */
+struct e1000_rar {
+ volatile uint32_t low; /* receive address low */
+ volatile uint32_t high; /* receive address high */
+};
+
+/* Number of entries in the Multicast Table Array (MTA). */
+#define E1000_NUM_MTA_REGISTERS 128
+#define E1000_NUM_MTA_REGISTERS_ICH8LAN 32
+
+/* IPv4 Address Table Entry */
+struct e1000_ipv4_at_entry {
+ volatile uint32_t ipv4_addr; /* IP Address (RW) */
+ volatile uint32_t reserved;
+};
+
+/* Four wakeup IP addresses are supported */
+#define E1000_WAKEUP_IP_ADDRESS_COUNT_MAX 4
+#define E1000_IP4AT_SIZE E1000_WAKEUP_IP_ADDRESS_COUNT_MAX
+#define E1000_IP4AT_SIZE_ICH8LAN 3
+#define E1000_IP6AT_SIZE 1
+
+/* IPv6 Address Table Entry */
+struct e1000_ipv6_at_entry {
+ volatile uint8_t ipv6_addr[16];
+};
+
+/* Flexible Filter Length Table Entry */
+struct e1000_fflt_entry {
+ volatile uint32_t length; /* Flexible Filter Length (RW) */
+ volatile uint32_t reserved;
+};
+
+/* Flexible Filter Mask Table Entry */
+struct e1000_ffmt_entry {
+ volatile uint32_t mask; /* Flexible Filter Mask (RW) */
+ volatile uint32_t reserved;
+};
+
+/* Flexible Filter Value Table Entry */
+struct e1000_ffvt_entry {
+ volatile uint32_t value; /* Flexible Filter Value (RW) */
+ volatile uint32_t reserved;
+};
+
+/* Four Flexible Filters are supported */
+#define E1000_FLEXIBLE_FILTER_COUNT_MAX 4
+
+/* Each Flexible Filter is at most 128 (0x80) bytes in length */
+#define E1000_FLEXIBLE_FILTER_SIZE_MAX 128
+
+#define E1000_FFLT_SIZE E1000_FLEXIBLE_FILTER_COUNT_MAX
+#define E1000_FFMT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX
+#define E1000_FFVT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX
+
+#define E1000_DISABLE_SERDES_LOOPBACK 0x0400
+
+/* Register Set. (82543, 82544)
+ *
+ * Registers are defined to be 32 bits and should be accessed as 32 bit values.
+ * These registers are physically located on the NIC, but are mapped into the
+ * host memory address space.
+ *
+ * RW - register is both readable and writable
+ * RO - register is read only
+ * WO - register is write only
+ * R/clr - register is read only and is cleared when read
+ * A - register array
+ */
+#define E1000_CTRL 0x00000 /* Device Control - RW */
+#define E1000_CTRL_DUP 0x00004 /* Device Control Duplicate (Shadow) - RW */
+#define E1000_STATUS 0x00008 /* Device Status - RO */
+#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */
+#define E1000_EERD 0x00014 /* EEPROM Read - RW */
+#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */
+#define E1000_FLA 0x0001C /* Flash Access - RW */
+#define E1000_MDIC 0x00020 /* MDI Control - RW */
+#define E1000_SCTL 0x00024 /* SerDes Control - RW */
+#define E1000_FEXTNVM 0x00028 /* Future Extended NVM register */
+#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */
+#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */
+#define E1000_FCT 0x00030 /* Flow Control Type - RW */
+#define E1000_VET 0x00038 /* VLAN Ether Type - RW */
+#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */
+#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */
+#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */
+#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */
+#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */
+#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */
+#define E1000_RCTL 0x00100 /* RX Control - RW */
+#define E1000_RDTR1 0x02820 /* RX Delay Timer (1) - RW */
+#define E1000_RDBAL1 0x02900 /* RX Descriptor Base Address Low (1) - RW */
+#define E1000_RDBAH1 0x02904 /* RX Descriptor Base Address High (1) - RW */
+#define E1000_RDLEN1 0x02908 /* RX Descriptor Length (1) - RW */
+#define E1000_RDH1 0x02910 /* RX Descriptor Head (1) - RW */
+#define E1000_RDT1 0x02918 /* RX Descriptor Tail (1) - RW */
+#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */
+#define E1000_TXCW 0x00178 /* TX Configuration Word - RW */
+#define E1000_RXCW 0x00180 /* RX Configuration Word - RO */
+#define E1000_TCTL 0x00400 /* TX Control - RW */
+#define E1000_TCTL_EXT 0x00404 /* Extended TX Control - RW */
+#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */
+#define E1000_TBT 0x00448 /* TX Burst Timer - RW */
+#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */
+#define E1000_LEDCTL 0x00E00 /* LED Control - RW */
+#define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */
+#define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */
+#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */
+#define FEXTNVM_SW_CONFIG 0x0001
+#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */
+#define E1000_PBS 0x01008 /* Packet Buffer Size */
+#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */
+#define E1000_FLASH_UPDATES 1000
+#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */
+#define E1000_FLASHT 0x01028 /* FLASH Timer Register */
+#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */
+#define E1000_FLSWCTL 0x01030 /* FLASH control register */
+#define E1000_FLSWDATA 0x01034 /* FLASH data register */
+#define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */
+#define E1000_FLOP 0x0103C /* FLASH Opcode Register */
+#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */
+#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */
+#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */
+#define E1000_PSRCTL 0x02170 /* Packet Split Receive Control - RW */
+#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */
+#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */
+#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */
+#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */
+#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */
+#define E1000_RDTR 0x02820 /* RX Delay Timer - RW */
+#define E1000_RDBAL0 E1000_RDBAL /* RX Desc Base Address Low (0) - RW */
+#define E1000_RDBAH0 E1000_RDBAH /* RX Desc Base Address High (0) - RW */
+#define E1000_RDLEN0 E1000_RDLEN /* RX Desc Length (0) - RW */
+#define E1000_RDH0 E1000_RDH /* RX Desc Head (0) - RW */
+#define E1000_RDT0 E1000_RDT /* RX Desc Tail (0) - RW */
+#define E1000_RDTR0 E1000_RDTR /* RX Delay Timer (0) - RW */
+#define E1000_RXDCTL 0x02828 /* RX Descriptor Control queue 0 - RW */
+#define E1000_RXDCTL1 0x02928 /* RX Descriptor Control queue 1 - RW */
+#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */
+#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */
+#define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */
+#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */
+#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */
+#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */
+#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */
+#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */
+#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */
+#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */
+#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */
+#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */
+#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */
+#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */
+#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */
+#define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */
+#define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */
+#define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */
+#define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */
+#define E1000_TARC0 0x03840 /* TX Arbitration Count (0) */
+#define E1000_TDBAL1 0x03900 /* TX Desc Base Address Low (1) - RW */
+#define E1000_TDBAH1 0x03904 /* TX Desc Base Address High (1) - RW */
+#define E1000_TDLEN1 0x03908 /* TX Desc Length (1) - RW */
+#define E1000_TDH1 0x03910 /* TX Desc Head (1) - RW */
+#define E1000_TDT1 0x03918 /* TX Desc Tail (1) - RW */
+#define E1000_TXDCTL1 0x03928 /* TX Descriptor Control (1) - RW */
+#define E1000_TARC1 0x03940 /* TX Arbitration Count (1) */
+#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */
+#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */
+#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */
+#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */
+#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */
+#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */
+#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */
+#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */
+#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */
+#define E1000_COLC 0x04028 /* Collision Count - R/clr */
+#define E1000_DC 0x04030 /* Defer Count - R/clr */
+#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */
+#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */
+#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */
+#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */
+#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */
+#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */
+#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */
+#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */
+#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */
+#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */
+#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */
+#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */
+#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */
+#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */
+#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */
+#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */
+#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */
+#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */
+#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */
+#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */
+#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */
+#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */
+#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */
+#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */
+#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */
+#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */
+#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */
+#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */
+#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */
+#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */
+#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */
+#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */
+#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */
+#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */
+#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */
+#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */
+#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */
+#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */
+#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */
+#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */
+#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */
+#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */
+#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */
+#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */
+#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */
+#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */
+#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */
+#define E1000_IAC 0x04100 /* Interrupt Assertion Count */
+#define E1000_ICRXPTC 0x04104 /* Interrupt Cause Rx Packet Timer Expire Count */
+#define E1000_ICRXATC 0x04108 /* Interrupt Cause Rx Absolute Timer Expire Count */
+#define E1000_ICTXPTC 0x0410C /* Interrupt Cause Tx Packet Timer Expire Count */
+#define E1000_ICTXATC 0x04110 /* Interrupt Cause Tx Absolute Timer Expire Count */
+#define E1000_ICTXQEC 0x04118 /* Interrupt Cause Tx Queue Empty Count */
+#define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Minimum Threshold Count */
+#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Descriptor Minimum Threshold Count */
+#define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */
+#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */
+#define E1000_RFCTL 0x05008 /* Receive Filter Control*/
+#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */
+#define E1000_RA 0x05400 /* Receive Address - RW Array */
+#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */
+#define E1000_WUC 0x05800 /* Wakeup Control - RW */
+#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */
+#define E1000_WUS 0x05810 /* Wakeup Status - RO */
+#define E1000_MANC 0x05820 /* Management Control - RW */
+#define E1000_IPAV 0x05838 /* IP Address Valid - RW */
+#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */
+#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */
+#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */
+#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */
+#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */
+#define E1000_HOST_IF 0x08800 /* Host Interface */
+#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */
+#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */
+
+#define E1000_KUMCTRLSTA 0x00034 /* MAC-PHY interface - RW */
+#define E1000_MDPHYA 0x0003C /* PHY address - RW */
+#define E1000_MANC2H 0x05860 /* Managment Control To Host - RW */
+#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */
+
+#define E1000_GCR 0x05B00 /* PCI-Ex Control */
+#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */
+#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */
+#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */
+#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */
+#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */
+#define E1000_SWSM 0x05B50 /* SW Semaphore */
+#define E1000_FWSM 0x05B54 /* FW Semaphore */
+#define E1000_FFLT_DBG 0x05F04 /* Debug Register */
+#define E1000_HICR 0x08F00 /* Host Inteface Control */
+
+/* RSS registers */
+#define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */
+#define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */
+#define E1000_RETA 0x05C00 /* Redirection Table - RW Array */
+#define E1000_RSSRK 0x05C80 /* RSS Random Key - RW Array */
+#define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */
+#define E1000_RSSIR 0x05868 /* RSS Interrupt Request */
+/* Register Set (82542)
+ *
+ * Some of the 82542 registers are located at different offsets than they are
+ * in more current versions of the 8254x. Despite the difference in location,
+ * the registers function in the same manner.
+ */
+#define E1000_82542_CTRL E1000_CTRL
+#define E1000_82542_CTRL_DUP E1000_CTRL_DUP
+#define E1000_82542_STATUS E1000_STATUS
+#define E1000_82542_EECD E1000_EECD
+#define E1000_82542_EERD E1000_EERD
+#define E1000_82542_CTRL_EXT E1000_CTRL_EXT
+#define E1000_82542_FLA E1000_FLA
+#define E1000_82542_MDIC E1000_MDIC
+#define E1000_82542_SCTL E1000_SCTL
+#define E1000_82542_FEXTNVM E1000_FEXTNVM
+#define E1000_82542_FCAL E1000_FCAL
+#define E1000_82542_FCAH E1000_FCAH
+#define E1000_82542_FCT E1000_FCT
+#define E1000_82542_VET E1000_VET
+#define E1000_82542_RA 0x00040
+#define E1000_82542_ICR E1000_ICR
+#define E1000_82542_ITR E1000_ITR
+#define E1000_82542_ICS E1000_ICS
+#define E1000_82542_IMS E1000_IMS
+#define E1000_82542_IMC E1000_IMC
+#define E1000_82542_RCTL E1000_RCTL
+#define E1000_82542_RDTR 0x00108
+#define E1000_82542_RDBAL 0x00110
+#define E1000_82542_RDBAH 0x00114
+#define E1000_82542_RDLEN 0x00118
+#define E1000_82542_RDH 0x00120
+#define E1000_82542_RDT 0x00128
+#define E1000_82542_RDTR0 E1000_82542_RDTR
+#define E1000_82542_RDBAL0 E1000_82542_RDBAL
+#define E1000_82542_RDBAH0 E1000_82542_RDBAH
+#define E1000_82542_RDLEN0 E1000_82542_RDLEN
+#define E1000_82542_RDH0 E1000_82542_RDH
+#define E1000_82542_RDT0 E1000_82542_RDT
+#define E1000_82542_SRRCTL(_n) (0x280C + ((_n) << 8)) /* Split and Replication
+ * RX Control - RW */
+#define E1000_82542_DCA_RXCTRL(_n) (0x02814 + ((_n) << 8))
+#define E1000_82542_RDBAH3 0x02B04 /* RX Desc Base High Queue 3 - RW */
+#define E1000_82542_RDBAL3 0x02B00 /* RX Desc Low Queue 3 - RW */
+#define E1000_82542_RDLEN3 0x02B08 /* RX Desc Length Queue 3 - RW */
+#define E1000_82542_RDH3 0x02B10 /* RX Desc Head Queue 3 - RW */
+#define E1000_82542_RDT3 0x02B18 /* RX Desc Tail Queue 3 - RW */
+#define E1000_82542_RDBAL2 0x02A00 /* RX Desc Base Low Queue 2 - RW */
+#define E1000_82542_RDBAH2 0x02A04 /* RX Desc Base High Queue 2 - RW */
+#define E1000_82542_RDLEN2 0x02A08 /* RX Desc Length Queue 2 - RW */
+#define E1000_82542_RDH2 0x02A10 /* RX Desc Head Queue 2 - RW */
+#define E1000_82542_RDT2 0x02A18 /* RX Desc Tail Queue 2 - RW */
+#define E1000_82542_RDTR1 0x00130
+#define E1000_82542_RDBAL1 0x00138
+#define E1000_82542_RDBAH1 0x0013C
+#define E1000_82542_RDLEN1 0x00140
+#define E1000_82542_RDH1 0x00148
+#define E1000_82542_RDT1 0x00150
+#define E1000_82542_FCRTH 0x00160
+#define E1000_82542_FCRTL 0x00168
+#define E1000_82542_FCTTV E1000_FCTTV
+#define E1000_82542_TXCW E1000_TXCW
+#define E1000_82542_RXCW E1000_RXCW
+#define E1000_82542_MTA 0x00200
+#define E1000_82542_TCTL E1000_TCTL
+#define E1000_82542_TCTL_EXT E1000_TCTL_EXT
+#define E1000_82542_TIPG E1000_TIPG
+#define E1000_82542_TDBAL 0x00420
+#define E1000_82542_TDBAH 0x00424
+#define E1000_82542_TDLEN 0x00428
+#define E1000_82542_TDH 0x00430
+#define E1000_82542_TDT 0x00438
+#define E1000_82542_TIDV 0x00440
+#define E1000_82542_TBT E1000_TBT
+#define E1000_82542_AIT E1000_AIT
+#define E1000_82542_VFTA 0x00600
+#define E1000_82542_LEDCTL E1000_LEDCTL
+#define E1000_82542_PBA E1000_PBA
+#define E1000_82542_PBS E1000_PBS
+#define E1000_82542_EEMNGCTL E1000_EEMNGCTL
+#define E1000_82542_EEARBC E1000_EEARBC
+#define E1000_82542_FLASHT E1000_FLASHT
+#define E1000_82542_EEWR E1000_EEWR
+#define E1000_82542_FLSWCTL E1000_FLSWCTL
+#define E1000_82542_FLSWDATA E1000_FLSWDATA
+#define E1000_82542_FLSWCNT E1000_FLSWCNT
+#define E1000_82542_FLOP E1000_FLOP
+#define E1000_82542_EXTCNF_CTRL E1000_EXTCNF_CTRL
+#define E1000_82542_EXTCNF_SIZE E1000_EXTCNF_SIZE
+#define E1000_82542_PHY_CTRL E1000_PHY_CTRL
+#define E1000_82542_ERT E1000_ERT
+#define E1000_82542_RXDCTL E1000_RXDCTL
+#define E1000_82542_RXDCTL1 E1000_RXDCTL1
+#define E1000_82542_RADV E1000_RADV
+#define E1000_82542_RSRPD E1000_RSRPD
+#define E1000_82542_TXDMAC E1000_TXDMAC
+#define E1000_82542_KABGTXD E1000_KABGTXD
+#define E1000_82542_TDFHS E1000_TDFHS
+#define E1000_82542_TDFTS E1000_TDFTS
+#define E1000_82542_TDFPC E1000_TDFPC
+#define E1000_82542_TXDCTL E1000_TXDCTL
+#define E1000_82542_TADV E1000_TADV
+#define E1000_82542_TSPMT E1000_TSPMT
+#define E1000_82542_CRCERRS E1000_CRCERRS
+#define E1000_82542_ALGNERRC E1000_ALGNERRC
+#define E1000_82542_SYMERRS E1000_SYMERRS
+#define E1000_82542_RXERRC E1000_RXERRC
+#define E1000_82542_MPC E1000_MPC
+#define E1000_82542_SCC E1000_SCC
+#define E1000_82542_ECOL E1000_ECOL
+#define E1000_82542_MCC E1000_MCC
+#define E1000_82542_LATECOL E1000_LATECOL
+#define E1000_82542_COLC E1000_COLC
+#define E1000_82542_DC E1000_DC
+#define E1000_82542_TNCRS E1000_TNCRS
+#define E1000_82542_SEC E1000_SEC
+#define E1000_82542_CEXTERR E1000_CEXTERR
+#define E1000_82542_RLEC E1000_RLEC
+#define E1000_82542_XONRXC E1000_XONRXC
+#define E1000_82542_XONTXC E1000_XONTXC
+#define E1000_82542_XOFFRXC E1000_XOFFRXC
+#define E1000_82542_XOFFTXC E1000_XOFFTXC
+#define E1000_82542_FCRUC E1000_FCRUC
+#define E1000_82542_PRC64 E1000_PRC64
+#define E1000_82542_PRC127 E1000_PRC127
+#define E1000_82542_PRC255 E1000_PRC255
+#define E1000_82542_PRC511 E1000_PRC511
+#define E1000_82542_PRC1023 E1000_PRC1023
+#define E1000_82542_PRC1522 E1000_PRC1522
+#define E1000_82542_GPRC E1000_GPRC
+#define E1000_82542_BPRC E1000_BPRC
+#define E1000_82542_MPRC E1000_MPRC
+#define E1000_82542_GPTC E1000_GPTC
+#define E1000_82542_GORCL E1000_GORCL
+#define E1000_82542_GORCH E1000_GORCH
+#define E1000_82542_GOTCL E1000_GOTCL
+#define E1000_82542_GOTCH E1000_GOTCH
+#define E1000_82542_RNBC E1000_RNBC
+#define E1000_82542_RUC E1000_RUC
+#define E1000_82542_RFC E1000_RFC
+#define E1000_82542_ROC E1000_ROC
+#define E1000_82542_RJC E1000_RJC
+#define E1000_82542_MGTPRC E1000_MGTPRC
+#define E1000_82542_MGTPDC E1000_MGTPDC
+#define E1000_82542_MGTPTC E1000_MGTPTC
+#define E1000_82542_TORL E1000_TORL
+#define E1000_82542_TORH E1000_TORH
+#define E1000_82542_TOTL E1000_TOTL
+#define E1000_82542_TOTH E1000_TOTH
+#define E1000_82542_TPR E1000_TPR
+#define E1000_82542_TPT E1000_TPT
+#define E1000_82542_PTC64 E1000_PTC64
+#define E1000_82542_PTC127 E1000_PTC127
+#define E1000_82542_PTC255 E1000_PTC255
+#define E1000_82542_PTC511 E1000_PTC511
+#define E1000_82542_PTC1023 E1000_PTC1023
+#define E1000_82542_PTC1522 E1000_PTC1522
+#define E1000_82542_MPTC E1000_MPTC
+#define E1000_82542_BPTC E1000_BPTC
+#define E1000_82542_TSCTC E1000_TSCTC
+#define E1000_82542_TSCTFC E1000_TSCTFC
+#define E1000_82542_RXCSUM E1000_RXCSUM
+#define E1000_82542_WUC E1000_WUC
+#define E1000_82542_WUFC E1000_WUFC
+#define E1000_82542_WUS E1000_WUS
+#define E1000_82542_MANC E1000_MANC
+#define E1000_82542_IPAV E1000_IPAV
+#define E1000_82542_IP4AT E1000_IP4AT
+#define E1000_82542_IP6AT E1000_IP6AT
+#define E1000_82542_WUPL E1000_WUPL
+#define E1000_82542_WUPM E1000_WUPM
+#define E1000_82542_FFLT E1000_FFLT
+#define E1000_82542_TDFH 0x08010
+#define E1000_82542_TDFT 0x08018
+#define E1000_82542_FFMT E1000_FFMT
+#define E1000_82542_FFVT E1000_FFVT
+#define E1000_82542_HOST_IF E1000_HOST_IF
+#define E1000_82542_IAM E1000_IAM
+#define E1000_82542_EEMNGCTL E1000_EEMNGCTL
+#define E1000_82542_PSRCTL E1000_PSRCTL
+#define E1000_82542_RAID E1000_RAID
+#define E1000_82542_TARC0 E1000_TARC0
+#define E1000_82542_TDBAL1 E1000_TDBAL1
+#define E1000_82542_TDBAH1 E1000_TDBAH1
+#define E1000_82542_TDLEN1 E1000_TDLEN1
+#define E1000_82542_TDH1 E1000_TDH1
+#define E1000_82542_TDT1 E1000_TDT1
+#define E1000_82542_TXDCTL1 E1000_TXDCTL1
+#define E1000_82542_TARC1 E1000_TARC1
+#define E1000_82542_RFCTL E1000_RFCTL
+#define E1000_82542_GCR E1000_GCR
+#define E1000_82542_GSCL_1 E1000_GSCL_1
+#define E1000_82542_GSCL_2 E1000_GSCL_2
+#define E1000_82542_GSCL_3 E1000_GSCL_3
+#define E1000_82542_GSCL_4 E1000_GSCL_4
+#define E1000_82542_FACTPS E1000_FACTPS
+#define E1000_82542_SWSM E1000_SWSM
+#define E1000_82542_FWSM E1000_FWSM
+#define E1000_82542_FFLT_DBG E1000_FFLT_DBG
+#define E1000_82542_IAC E1000_IAC
+#define E1000_82542_ICRXPTC E1000_ICRXPTC
+#define E1000_82542_ICRXATC E1000_ICRXATC
+#define E1000_82542_ICTXPTC E1000_ICTXPTC
+#define E1000_82542_ICTXATC E1000_ICTXATC
+#define E1000_82542_ICTXQEC E1000_ICTXQEC
+#define E1000_82542_ICTXQMTC E1000_ICTXQMTC
+#define E1000_82542_ICRXDMTC E1000_ICRXDMTC
+#define E1000_82542_ICRXOC E1000_ICRXOC
+#define E1000_82542_HICR E1000_HICR
+
+#define E1000_82542_CPUVEC E1000_CPUVEC
+#define E1000_82542_MRQC E1000_MRQC
+#define E1000_82542_RETA E1000_RETA
+#define E1000_82542_RSSRK E1000_RSSRK
+#define E1000_82542_RSSIM E1000_RSSIM
+#define E1000_82542_RSSIR E1000_RSSIR
+#define E1000_82542_KUMCTRLSTA E1000_KUMCTRLSTA
+#define E1000_82542_SW_FW_SYNC E1000_SW_FW_SYNC
+#define E1000_82542_MANC2H E1000_MANC2H
+
+/* Statistics counters collected by the MAC */
+struct e1000_hw_stats {
+ uint64_t crcerrs;
+ uint64_t algnerrc;
+ uint64_t symerrs;
+ uint64_t rxerrc;
+ uint64_t txerrc;
+ uint64_t mpc;
+ uint64_t scc;
+ uint64_t ecol;
+ uint64_t mcc;
+ uint64_t latecol;
+ uint64_t colc;
+ uint64_t dc;
+ uint64_t tncrs;
+ uint64_t sec;
+ uint64_t cexterr;
+ uint64_t rlec;
+ uint64_t xonrxc;
+ uint64_t xontxc;
+ uint64_t xoffrxc;
+ uint64_t xofftxc;
+ uint64_t fcruc;
+ uint64_t prc64;
+ uint64_t prc127;
+ uint64_t prc255;
+ uint64_t prc511;
+ uint64_t prc1023;
+ uint64_t prc1522;
+ uint64_t gprc;
+ uint64_t bprc;
+ uint64_t mprc;
+ uint64_t gptc;
+ uint64_t gorcl;
+ uint64_t gorch;
+ uint64_t gotcl;
+ uint64_t gotch;
+ uint64_t rnbc;
+ uint64_t ruc;
+ uint64_t rfc;
+ uint64_t roc;
+ uint64_t rlerrc;
+ uint64_t rjc;
+ uint64_t mgprc;
+ uint64_t mgpdc;
+ uint64_t mgptc;
+ uint64_t torl;
+ uint64_t torh;
+ uint64_t totl;
+ uint64_t toth;
+ uint64_t tpr;
+ uint64_t tpt;
+ uint64_t ptc64;
+ uint64_t ptc127;
+ uint64_t ptc255;
+ uint64_t ptc511;
+ uint64_t ptc1023;
+ uint64_t ptc1522;
+ uint64_t mptc;
+ uint64_t bptc;
+ uint64_t tsctc;
+ uint64_t tsctfc;
+ uint64_t iac;
+ uint64_t icrxptc;
+ uint64_t icrxatc;
+ uint64_t ictxptc;
+ uint64_t ictxatc;
+ uint64_t ictxqec;
+ uint64_t ictxqmtc;
+ uint64_t icrxdmtc;
+ uint64_t icrxoc;
+};
+
+/* Structure containing variables used by the shared code (e1000_hw.c) */
+struct e1000_hw {
+ uint8_t *hw_addr;
+ uint8_t *flash_address;
+ e1000_mac_type mac_type;
+ e1000_phy_type phy_type;
+ uint32_t phy_init_script;
+ e1000_media_type media_type;
+ void *back;
+ struct e1000_shadow_ram *eeprom_shadow_ram;
+ uint32_t flash_bank_size;
+ uint32_t flash_base_addr;
+ e1000_fc_type fc;
+ e1000_bus_speed bus_speed;
+ e1000_bus_width bus_width;
+ e1000_bus_type bus_type;
+ struct e1000_eeprom_info eeprom;
+ e1000_ms_type master_slave;
+ e1000_ms_type original_master_slave;
+ e1000_ffe_config ffe_config_state;
+ uint32_t asf_firmware_present;
+ uint32_t eeprom_semaphore_present;
+ uint32_t swfw_sync_present;
+ uint32_t swfwhw_semaphore_present;
+ unsigned long io_base;
+ uint32_t phy_id;
+ uint32_t phy_revision;
+ uint32_t phy_addr;
+ uint32_t original_fc;
+ uint32_t txcw;
+ uint32_t autoneg_failed;
+ uint32_t max_frame_size;
+ uint32_t min_frame_size;
+ uint32_t mc_filter_type;
+ uint32_t num_mc_addrs;
+ uint32_t collision_delta;
+ uint32_t tx_packet_delta;
+ uint32_t ledctl_default;
+ uint32_t ledctl_mode1;
+ uint32_t ledctl_mode2;
+ boolean_t tx_pkt_filtering;
+ struct e1000_host_mng_dhcp_cookie mng_cookie;
+ uint16_t phy_spd_default;
+ uint16_t autoneg_advertised;
+ uint16_t pci_cmd_word;
+ uint16_t fc_high_water;
+ uint16_t fc_low_water;
+ uint16_t fc_pause_time;
+ uint16_t current_ifs_val;
+ uint16_t ifs_min_val;
+ uint16_t ifs_max_val;
+ uint16_t ifs_step_size;
+ uint16_t ifs_ratio;
+ uint16_t device_id;
+ uint16_t vendor_id;
+ uint16_t subsystem_id;
+ uint16_t subsystem_vendor_id;
+ uint8_t revision_id;
+ uint8_t autoneg;
+ uint8_t mdix;
+ uint8_t forced_speed_duplex;
+ uint8_t wait_autoneg_complete;
+ uint8_t dma_fairness;
+ uint8_t mac_addr[NODE_ADDRESS_SIZE];
+ uint8_t perm_mac_addr[NODE_ADDRESS_SIZE];
+ boolean_t disable_polarity_correction;
+ boolean_t speed_downgraded;
+ e1000_smart_speed smart_speed;
+ e1000_dsp_config dsp_config_state;
+ boolean_t get_link_status;
+ boolean_t serdes_link_down;
+ boolean_t tbi_compatibility_en;
+ boolean_t tbi_compatibility_on;
+ boolean_t laa_is_present;
+ boolean_t phy_reset_disable;
+ boolean_t initialize_hw_bits_disable;
+ boolean_t fc_send_xon;
+ boolean_t fc_strict_ieee;
+ boolean_t report_tx_early;
+ boolean_t adaptive_ifs;
+ boolean_t ifs_params_forced;
+ boolean_t in_ifs_mode;
+ boolean_t mng_reg_access_disabled;
+ boolean_t leave_av_bit_off;
+ boolean_t kmrn_lock_loss_workaround_disabled;
+ boolean_t bad_tx_carr_stats_fd;
+ boolean_t has_manc2h;
+ boolean_t rx_needs_kicking;
+ boolean_t has_smbus;
+};
+
+
+#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */
+#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */
+#define E1000_EEPROM_RW_REG_DATA 16 /* Offset to data in EEPROM read/write registers */
+#define E1000_EEPROM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */
+#define E1000_EEPROM_RW_REG_START 1 /* First bit for telling part to start operation */
+#define E1000_EEPROM_RW_ADDR_SHIFT 2 /* Shift to the address bits */
+#define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write complete */
+#define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */
+/* Register Bit Masks */
+/* Device Control */
+#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */
+#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */
+#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */
+#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master requests */
+#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */
+#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */
+#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */
+#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */
+#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */
+#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */
+#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */
+#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */
+#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */
+#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */
+#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */
+#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */
+#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */
+#define E1000_CTRL_D_UD_EN 0x00002000 /* Dock/Undock enable */
+#define E1000_CTRL_D_UD_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */
+#define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through PHYRST_N pin */
+#define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external LINK_0 and LINK_1 pins */
+#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */
+#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */
+#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */
+#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */
+#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */
+#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */
+#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */
+#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */
+#define E1000_CTRL_RST 0x04000000 /* Global reset */
+#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */
+#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */
+#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */
+#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */
+#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */
+#define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */
+
+/* Device Status */
+#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */
+#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */
+#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */
+#define E1000_STATUS_FUNC_SHIFT 2
+#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */
+#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */
+#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */
+#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */
+#define E1000_STATUS_SPEED_MASK 0x000000C0
+#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */
+#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */
+#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */
+#define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Completion
+ by EEPROM/Flash */
+#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */
+#define E1000_STATUS_DOCK_CI 0x00000800 /* Change in Dock/Undock state. Clear on write '0'. */
+#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */
+#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */
+#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */
+#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */
+#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */
+#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */
+#define E1000_STATUS_BMC_SKU_0 0x00100000 /* BMC USB redirect disabled */
+#define E1000_STATUS_BMC_SKU_1 0x00200000 /* BMC SRAM disabled */
+#define E1000_STATUS_BMC_SKU_2 0x00400000 /* BMC SDRAM disabled */
+#define E1000_STATUS_BMC_CRYPTO 0x00800000 /* BMC crypto disabled */
+#define E1000_STATUS_BMC_LITE 0x01000000 /* BMC external code execution disabled */
+#define E1000_STATUS_RGMII_ENABLE 0x02000000 /* RGMII disabled */
+#define E1000_STATUS_FUSE_8 0x04000000
+#define E1000_STATUS_FUSE_9 0x08000000
+#define E1000_STATUS_SERDES0_DIS 0x10000000 /* SERDES disabled on port 0 */
+#define E1000_STATUS_SERDES1_DIS 0x20000000 /* SERDES disabled on port 1 */
+
+/* Constants used to intrepret the masked PCI-X bus speed. */
+#define E1000_STATUS_PCIX_SPEED_66 0x00000000 /* PCI-X bus speed 50-66 MHz */
+#define E1000_STATUS_PCIX_SPEED_100 0x00004000 /* PCI-X bus speed 66-100 MHz */
+#define E1000_STATUS_PCIX_SPEED_133 0x00008000 /* PCI-X bus speed 100-133 MHz */
+
+/* EEPROM/Flash Control */
+#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */
+#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */
+#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */
+#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */
+#define E1000_EECD_FWE_MASK 0x00000030
+#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */
+#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */
+#define E1000_EECD_FWE_SHIFT 4
+#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */
+#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */
+#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */
+#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */
+#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type
+ * (0-small, 1-large) */
+#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */
+#ifndef E1000_EEPROM_GRANT_ATTEMPTS
+#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */
+#endif
+#define E1000_EECD_AUTO_RD 0x00000200 /* EEPROM Auto Read done */
+#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* EEprom Size */
+#define E1000_EECD_SIZE_EX_SHIFT 11
+#define E1000_EECD_NVADDS 0x00018000 /* NVM Address Size */
+#define E1000_EECD_SELSHAD 0x00020000 /* Select Shadow RAM */
+#define E1000_EECD_INITSRAM 0x00040000 /* Initialize Shadow RAM */
+#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */
+#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */
+#define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */
+#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */
+#define E1000_EECD_SECVAL_SHIFT 22
+#define E1000_STM_OPCODE 0xDB00
+#define E1000_HICR_FW_RESET 0xC0
+
+#define E1000_SHADOW_RAM_WORDS 2048
+#define E1000_ICH_NVM_SIG_WORD 0x13
+#define E1000_ICH_NVM_SIG_MASK 0xC0
+
+/* EEPROM Read */
+#define E1000_EERD_START 0x00000001 /* Start Read */
+#define E1000_EERD_DONE 0x00000010 /* Read Done */
+#define E1000_EERD_ADDR_SHIFT 8
+#define E1000_EERD_ADDR_MASK 0x0000FF00 /* Read Address */
+#define E1000_EERD_DATA_SHIFT 16
+#define E1000_EERD_DATA_MASK 0xFFFF0000 /* Read Data */
+
+/* SPI EEPROM Status Register */
+#define EEPROM_STATUS_RDY_SPI 0x01
+#define EEPROM_STATUS_WEN_SPI 0x02
+#define EEPROM_STATUS_BP0_SPI 0x04
+#define EEPROM_STATUS_BP1_SPI 0x08
+#define EEPROM_STATUS_WPEN_SPI 0x80
+
+/* Extended Device Control */
+#define E1000_CTRL_EXT_GPI0_EN 0x00000001 /* Maps SDP4 to GPI0 */
+#define E1000_CTRL_EXT_GPI1_EN 0x00000002 /* Maps SDP5 to GPI1 */
+#define E1000_CTRL_EXT_PHYINT_EN E1000_CTRL_EXT_GPI1_EN
+#define E1000_CTRL_EXT_GPI2_EN 0x00000004 /* Maps SDP6 to GPI2 */
+#define E1000_CTRL_EXT_GPI3_EN 0x00000008 /* Maps SDP7 to GPI3 */
+#define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* Value of SW Defineable Pin 4 */
+#define E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Defineable Pin 5 */
+#define E1000_CTRL_EXT_PHY_INT E1000_CTRL_EXT_SDP5_DATA
+#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */
+#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */
+#define E1000_CTRL_EXT_SDP4_DIR 0x00000100 /* Direction of SDP4 0=in 1=out */
+#define E1000_CTRL_EXT_SDP5_DIR 0x00000200 /* Direction of SDP5 0=in 1=out */
+#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */
+#define E1000_CTRL_EXT_SDP7_DIR 0x00000800 /* Direction of SDP7 0=in 1=out */
+#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* Initiate an ASD sequence */
+#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */
+#define E1000_CTRL_EXT_IPS 0x00004000 /* Invert Power State */
+#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */
+#define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */
+#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000
+#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000
+#define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000
+#define E1000_CTRL_EXT_LINK_MODE_KMRN 0x00000000
+#define E1000_CTRL_EXT_LINK_MODE_SERDES 0x00C00000
+#define E1000_CTRL_EXT_LINK_MODE_SGMII 0x00800000
+#define E1000_CTRL_EXT_WR_WMARK_MASK 0x03000000
+#define E1000_CTRL_EXT_WR_WMARK_256 0x00000000
+#define E1000_CTRL_EXT_WR_WMARK_320 0x01000000
+#define E1000_CTRL_EXT_WR_WMARK_384 0x02000000
+#define E1000_CTRL_EXT_WR_WMARK_448 0x03000000
+#define E1000_CTRL_EXT_DRV_LOAD 0x10000000 /* Driver loaded bit for FW */
+#define E1000_CTRL_EXT_IAME 0x08000000 /* Interrupt acknowledge Auto-mask */
+#define E1000_CTRL_EXT_INT_TIMER_CLR 0x20000000 /* Clear Interrupt timers after IMS clear */
+#define E1000_CRTL_EXT_PB_PAREN 0x01000000 /* packet buffer parity error detection enabled */
+#define E1000_CTRL_EXT_DF_PAREN 0x02000000 /* descriptor FIFO parity error detection enable */
+#define E1000_CTRL_EXT_GHOST_PAREN 0x40000000
+
+/* MDI Control */
+#define E1000_MDIC_DATA_MASK 0x0000FFFF
+#define E1000_MDIC_REG_MASK 0x001F0000
+#define E1000_MDIC_REG_SHIFT 16
+#define E1000_MDIC_PHY_MASK 0x03E00000
+#define E1000_MDIC_PHY_SHIFT 21
+#define E1000_MDIC_OP_WRITE 0x04000000
+#define E1000_MDIC_OP_READ 0x08000000
+#define E1000_MDIC_READY 0x10000000
+#define E1000_MDIC_INT_EN 0x20000000
+#define E1000_MDIC_ERROR 0x40000000
+
+#define E1000_KUMCTRLSTA_MASK 0x0000FFFF
+#define E1000_KUMCTRLSTA_OFFSET 0x001F0000
+#define E1000_KUMCTRLSTA_OFFSET_SHIFT 16
+#define E1000_KUMCTRLSTA_REN 0x00200000
+
+#define E1000_KUMCTRLSTA_OFFSET_FIFO_CTRL 0x00000000
+#define E1000_KUMCTRLSTA_OFFSET_CTRL 0x00000001
+#define E1000_KUMCTRLSTA_OFFSET_INB_CTRL 0x00000002
+#define E1000_KUMCTRLSTA_OFFSET_DIAG 0x00000003
+#define E1000_KUMCTRLSTA_OFFSET_TIMEOUTS 0x00000004
+#define E1000_KUMCTRLSTA_OFFSET_INB_PARAM 0x00000009
+#define E1000_KUMCTRLSTA_OFFSET_HD_CTRL 0x00000010
+#define E1000_KUMCTRLSTA_OFFSET_M2P_SERDES 0x0000001E
+#define E1000_KUMCTRLSTA_OFFSET_M2P_MODES 0x0000001F
+
+/* FIFO Control */
+#define E1000_KUMCTRLSTA_FIFO_CTRL_RX_BYPASS 0x00000008
+#define E1000_KUMCTRLSTA_FIFO_CTRL_TX_BYPASS 0x00000800
+
+/* In-Band Control */
+#define E1000_KUMCTRLSTA_INB_CTRL_LINK_STATUS_TX_TIMEOUT_DEFAULT 0x00000500
+#define E1000_KUMCTRLSTA_INB_CTRL_DIS_PADDING 0x00000010
+
+/* Half-Duplex Control */
+#define E1000_KUMCTRLSTA_HD_CTRL_10_100_DEFAULT 0x00000004
+#define E1000_KUMCTRLSTA_HD_CTRL_1000_DEFAULT 0x00000000
+
+#define E1000_KUMCTRLSTA_OFFSET_K0S_CTRL 0x0000001E
+
+#define E1000_KUMCTRLSTA_DIAG_FELPBK 0x2000
+#define E1000_KUMCTRLSTA_DIAG_NELPBK 0x1000
+
+#define E1000_KUMCTRLSTA_K0S_100_EN 0x2000
+#define E1000_KUMCTRLSTA_K0S_GBE_EN 0x1000
+#define E1000_KUMCTRLSTA_K0S_ENTRY_LATENCY_MASK 0x0003
+
+#define E1000_KABGTXD_BGSQLBIAS 0x00050000
+
+#define E1000_PHY_CTRL_SPD_EN 0x00000001
+#define E1000_PHY_CTRL_D0A_LPLU 0x00000002
+#define E1000_PHY_CTRL_NOND0A_LPLU 0x00000004
+#define E1000_PHY_CTRL_NOND0A_GBE_DISABLE 0x00000008
+#define E1000_PHY_CTRL_GBE_DISABLE 0x00000040
+#define E1000_PHY_CTRL_B2B_EN 0x00000080
+
+/* LED Control */
+#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F
+#define E1000_LEDCTL_LED0_MODE_SHIFT 0
+#define E1000_LEDCTL_LED0_BLINK_RATE 0x0000020
+#define E1000_LEDCTL_LED0_IVRT 0x00000040
+#define E1000_LEDCTL_LED0_BLINK 0x00000080
+#define E1000_LEDCTL_LED1_MODE_MASK 0x00000F00
+#define E1000_LEDCTL_LED1_MODE_SHIFT 8
+#define E1000_LEDCTL_LED1_BLINK_RATE 0x0002000
+#define E1000_LEDCTL_LED1_IVRT 0x00004000
+#define E1000_LEDCTL_LED1_BLINK 0x00008000
+#define E1000_LEDCTL_LED2_MODE_MASK 0x000F0000
+#define E1000_LEDCTL_LED2_MODE_SHIFT 16
+#define E1000_LEDCTL_LED2_BLINK_RATE 0x00200000
+#define E1000_LEDCTL_LED2_IVRT 0x00400000
+#define E1000_LEDCTL_LED2_BLINK 0x00800000
+#define E1000_LEDCTL_LED3_MODE_MASK 0x0F000000
+#define E1000_LEDCTL_LED3_MODE_SHIFT 24
+#define E1000_LEDCTL_LED3_BLINK_RATE 0x20000000
+#define E1000_LEDCTL_LED3_IVRT 0x40000000
+#define E1000_LEDCTL_LED3_BLINK 0x80000000
+
+#define E1000_LEDCTL_MODE_LINK_10_1000 0x0
+#define E1000_LEDCTL_MODE_LINK_100_1000 0x1
+#define E1000_LEDCTL_MODE_LINK_UP 0x2
+#define E1000_LEDCTL_MODE_ACTIVITY 0x3
+#define E1000_LEDCTL_MODE_LINK_ACTIVITY 0x4
+#define E1000_LEDCTL_MODE_LINK_10 0x5
+#define E1000_LEDCTL_MODE_LINK_100 0x6
+#define E1000_LEDCTL_MODE_LINK_1000 0x7
+#define E1000_LEDCTL_MODE_PCIX_MODE 0x8
+#define E1000_LEDCTL_MODE_FULL_DUPLEX 0x9
+#define E1000_LEDCTL_MODE_COLLISION 0xA
+#define E1000_LEDCTL_MODE_BUS_SPEED 0xB
+#define E1000_LEDCTL_MODE_BUS_SIZE 0xC
+#define E1000_LEDCTL_MODE_PAUSED 0xD
+#define E1000_LEDCTL_MODE_LED_ON 0xE
+#define E1000_LEDCTL_MODE_LED_OFF 0xF
+
+/* Receive Address */
+#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */
+
+/* Interrupt Cause Read */
+#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */
+#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */
+#define E1000_ICR_LSC 0x00000004 /* Link Status Change */
+#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */
+#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */
+#define E1000_ICR_RXO 0x00000040 /* rx overrun */
+#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */
+#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */
+#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */
+#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */
+#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */
+#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */
+#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */
+#define E1000_ICR_TXD_LOW 0x00008000
+#define E1000_ICR_SRPD 0x00010000
+#define E1000_ICR_ACK 0x00020000 /* Receive Ack frame */
+#define E1000_ICR_MNG 0x00040000 /* Manageability event */
+#define E1000_ICR_DOCK 0x00080000 /* Dock/Undock */
+#define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver should claim the interrupt */
+#define E1000_ICR_RXD_FIFO_PAR0 0x00100000 /* queue 0 Rx descriptor FIFO parity error */
+#define E1000_ICR_TXD_FIFO_PAR0 0x00200000 /* queue 0 Tx descriptor FIFO parity error */
+#define E1000_ICR_HOST_ARB_PAR 0x00400000 /* host arb read buffer parity error */
+#define E1000_ICR_PB_PAR 0x00800000 /* packet buffer parity error */
+#define E1000_ICR_RXD_FIFO_PAR1 0x01000000 /* queue 1 Rx descriptor FIFO parity error */
+#define E1000_ICR_TXD_FIFO_PAR1 0x02000000 /* queue 1 Tx descriptor FIFO parity error */
+#define E1000_ICR_ALL_PARITY 0x03F00000 /* all parity error bits */
+#define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */
+#define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */
+#define E1000_ICR_EPRST 0x00100000 /* ME handware reset occurs */
+
+/* Interrupt Cause Set */
+#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */
+#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */
+#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */
+#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */
+#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
+#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */
+#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */
+#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */
+#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */
+#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */
+#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */
+#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */
+#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */
+#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW
+#define E1000_ICS_SRPD E1000_ICR_SRPD
+#define E1000_ICS_ACK E1000_ICR_ACK /* Receive Ack frame */
+#define E1000_ICS_MNG E1000_ICR_MNG /* Manageability event */
+#define E1000_ICS_DOCK E1000_ICR_DOCK /* Dock/Undock */
+#define E1000_ICS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */
+#define E1000_ICS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */
+#define E1000_ICS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */
+#define E1000_ICS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */
+#define E1000_ICS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */
+#define E1000_ICS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */
+#define E1000_ICS_DSW E1000_ICR_DSW
+#define E1000_ICS_PHYINT E1000_ICR_PHYINT
+#define E1000_ICS_EPRST E1000_ICR_EPRST
+
+/* Interrupt Mask Set */
+#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */
+#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */
+#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */
+#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */
+#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
+#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */
+#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */
+#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */
+#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */
+#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */
+#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */
+#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */
+#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */
+#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW
+#define E1000_IMS_SRPD E1000_ICR_SRPD
+#define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */
+#define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */
+#define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */
+#define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */
+#define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */
+#define E1000_IMS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */
+#define E1000_IMS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */
+#define E1000_IMS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */
+#define E1000_IMS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */
+#define E1000_IMS_DSW E1000_ICR_DSW
+#define E1000_IMS_PHYINT E1000_ICR_PHYINT
+#define E1000_IMS_EPRST E1000_ICR_EPRST
+
+/* Interrupt Mask Clear */
+#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */
+#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */
+#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */
+#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */
+#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
+#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */
+#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */
+#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */
+#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */
+#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */
+#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */
+#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */
+#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */
+#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW
+#define E1000_IMC_SRPD E1000_ICR_SRPD
+#define E1000_IMC_ACK E1000_ICR_ACK /* Receive Ack frame */
+#define E1000_IMC_MNG E1000_ICR_MNG /* Manageability event */
+#define E1000_IMC_DOCK E1000_ICR_DOCK /* Dock/Undock */
+#define E1000_IMC_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */
+#define E1000_IMC_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */
+#define E1000_IMC_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */
+#define E1000_IMC_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */
+#define E1000_IMC_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */
+#define E1000_IMC_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */
+#define E1000_IMC_DSW E1000_ICR_DSW
+#define E1000_IMC_PHYINT E1000_ICR_PHYINT
+#define E1000_IMC_EPRST E1000_ICR_EPRST
+
+/* Receive Control */
+#define E1000_RCTL_RST 0x00000001 /* Software reset */
+#define E1000_RCTL_EN 0x00000002 /* enable */
+#define E1000_RCTL_SBP 0x00000004 /* store bad packet */
+#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */
+#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */
+#define E1000_RCTL_LPE 0x00000020 /* long packet enable */
+#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */
+#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */
+#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */
+#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */
+#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */
+#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */
+#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */
+#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */
+#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */
+#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */
+#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */
+#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */
+#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */
+#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */
+/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */
+#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */
+#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */
+#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */
+#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */
+/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */
+#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */
+#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */
+#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */
+#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */
+#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */
+#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */
+#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */
+#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */
+#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */
+#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */
+#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */
+#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */
+
+/* Use byte values for the following shift parameters
+ * Usage:
+ * psrctl |= (((ROUNDUP(value0, 128) >> E1000_PSRCTL_BSIZE0_SHIFT) &
+ * E1000_PSRCTL_BSIZE0_MASK) |
+ * ((ROUNDUP(value1, 1024) >> E1000_PSRCTL_BSIZE1_SHIFT) &
+ * E1000_PSRCTL_BSIZE1_MASK) |
+ * ((ROUNDUP(value2, 1024) << E1000_PSRCTL_BSIZE2_SHIFT) &
+ * E1000_PSRCTL_BSIZE2_MASK) |
+ * ((ROUNDUP(value3, 1024) << E1000_PSRCTL_BSIZE3_SHIFT) |;
+ * E1000_PSRCTL_BSIZE3_MASK))
+ * where value0 = [128..16256], default=256
+ * value1 = [1024..64512], default=4096
+ * value2 = [0..64512], default=4096
+ * value3 = [0..64512], default=0
+ */
+
+#define E1000_PSRCTL_BSIZE0_MASK 0x0000007F
+#define E1000_PSRCTL_BSIZE1_MASK 0x00003F00
+#define E1000_PSRCTL_BSIZE2_MASK 0x003F0000
+#define E1000_PSRCTL_BSIZE3_MASK 0x3F000000
+
+#define E1000_PSRCTL_BSIZE0_SHIFT 7 /* Shift _right_ 7 */
+#define E1000_PSRCTL_BSIZE1_SHIFT 2 /* Shift _right_ 2 */
+#define E1000_PSRCTL_BSIZE2_SHIFT 6 /* Shift _left_ 6 */
+#define E1000_PSRCTL_BSIZE3_SHIFT 14 /* Shift _left_ 14 */
+
+/* SW_W_SYNC definitions */
+#define E1000_SWFW_EEP_SM 0x0001
+#define E1000_SWFW_PHY0_SM 0x0002
+#define E1000_SWFW_PHY1_SM 0x0004
+#define E1000_SWFW_MAC_CSR_SM 0x0008
+
+/* Receive Descriptor */
+#define E1000_RDT_DELAY 0x0000ffff /* Delay timer (1=1024us) */
+#define E1000_RDT_FPDB 0x80000000 /* Flush descriptor block */
+#define E1000_RDLEN_LEN 0x0007ff80 /* descriptor length */
+#define E1000_RDH_RDH 0x0000ffff /* receive descriptor head */
+#define E1000_RDT_RDT 0x0000ffff /* receive descriptor tail */
+
+/* Flow Control */
+#define E1000_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */
+#define E1000_FCRTH_XFCE 0x80000000 /* External Flow Control Enable */
+#define E1000_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */
+#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */
+
+/* Header split receive */
+#define E1000_RFCTL_ISCSI_DIS 0x00000001
+#define E1000_RFCTL_ISCSI_DWC_MASK 0x0000003E
+#define E1000_RFCTL_ISCSI_DWC_SHIFT 1
+#define E1000_RFCTL_NFSW_DIS 0x00000040
+#define E1000_RFCTL_NFSR_DIS 0x00000080
+#define E1000_RFCTL_NFS_VER_MASK 0x00000300
+#define E1000_RFCTL_NFS_VER_SHIFT 8
+#define E1000_RFCTL_IPV6_DIS 0x00000400
+#define E1000_RFCTL_IPV6_XSUM_DIS 0x00000800
+#define E1000_RFCTL_ACK_DIS 0x00001000
+#define E1000_RFCTL_ACKD_DIS 0x00002000
+#define E1000_RFCTL_IPFRSP_DIS 0x00004000
+#define E1000_RFCTL_EXTEN 0x00008000
+#define E1000_RFCTL_IPV6_EX_DIS 0x00010000
+#define E1000_RFCTL_NEW_IPV6_EXT_DIS 0x00020000
+
+/* Receive Descriptor Control */
+#define E1000_RXDCTL_PTHRESH 0x0000003F /* RXDCTL Prefetch Threshold */
+#define E1000_RXDCTL_HTHRESH 0x00003F00 /* RXDCTL Host Threshold */
+#define E1000_RXDCTL_WTHRESH 0x003F0000 /* RXDCTL Writeback Threshold */
+#define E1000_RXDCTL_GRAN 0x01000000 /* RXDCTL Granularity */
+
+/* Transmit Descriptor Control */
+#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */
+#define E1000_TXDCTL_HTHRESH 0x00003F00 /* TXDCTL Host Threshold */
+#define E1000_TXDCTL_WTHRESH 0x003F0000 /* TXDCTL Writeback Threshold */
+#define E1000_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */
+#define E1000_TXDCTL_LWTHRESH 0xFE000000 /* TXDCTL Low Threshold */
+#define E1000_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */
+#define E1000_TXDCTL_COUNT_DESC 0x00400000 /* Enable the counting of desc.
+ still to be processed. */
+/* Transmit Configuration Word */
+#define E1000_TXCW_FD 0x00000020 /* TXCW full duplex */
+#define E1000_TXCW_HD 0x00000040 /* TXCW half duplex */
+#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */
+#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */
+#define E1000_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */
+#define E1000_TXCW_RF 0x00003000 /* TXCW remote fault */
+#define E1000_TXCW_NP 0x00008000 /* TXCW next page */
+#define E1000_TXCW_CW 0x0000ffff /* TxConfigWord mask */
+#define E1000_TXCW_TXC 0x40000000 /* Transmit Config control */
+#define E1000_TXCW_ANE 0x80000000 /* Auto-neg enable */
+
+/* Receive Configuration Word */
+#define E1000_RXCW_CW 0x0000ffff /* RxConfigWord mask */
+#define E1000_RXCW_NC 0x04000000 /* Receive config no carrier */
+#define E1000_RXCW_IV 0x08000000 /* Receive config invalid */
+#define E1000_RXCW_CC 0x10000000 /* Receive config change */
+#define E1000_RXCW_C 0x20000000 /* Receive config */
+#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */
+#define E1000_RXCW_ANC 0x80000000 /* Auto-neg complete */
+
+/* Transmit Control */
+#define E1000_TCTL_RST 0x00000001 /* software reset */
+#define E1000_TCTL_EN 0x00000002 /* enable tx */
+#define E1000_TCTL_BCE 0x00000004 /* busy check enable */
+#define E1000_TCTL_PSP 0x00000008 /* pad short packets */
+#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */
+#define E1000_TCTL_COLD 0x003ff000 /* collision distance */
+#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */
+#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */
+#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */
+#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */
+#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */
+/* Extended Transmit Control */
+#define E1000_TCTL_EXT_BST_MASK 0x000003FF /* Backoff Slot Time */
+#define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */
+
+#define DEFAULT_80003ES2LAN_TCTL_EXT_GCEX 0x00010000
+
+/* Receive Checksum Control */
+#define E1000_RXCSUM_PCSS_MASK 0x000000FF /* Packet Checksum Start */
+#define E1000_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */
+#define E1000_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */
+#define E1000_RXCSUM_IPV6OFL 0x00000400 /* IPv6 checksum offload */
+#define E1000_RXCSUM_IPPCSE 0x00001000 /* IP payload checksum enable */
+#define E1000_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */
+
+/* Multiple Receive Queue Control */
+#define E1000_MRQC_ENABLE_MASK 0x00000003
+#define E1000_MRQC_ENABLE_RSS_2Q 0x00000001
+#define E1000_MRQC_ENABLE_RSS_INT 0x00000004
+#define E1000_MRQC_RSS_FIELD_MASK 0xFFFF0000
+#define E1000_MRQC_RSS_FIELD_IPV4_TCP 0x00010000
+#define E1000_MRQC_RSS_FIELD_IPV4 0x00020000
+#define E1000_MRQC_RSS_FIELD_IPV6_TCP_EX 0x00040000
+#define E1000_MRQC_RSS_FIELD_IPV6_EX 0x00080000
+#define E1000_MRQC_RSS_FIELD_IPV6 0x00100000
+#define E1000_MRQC_RSS_FIELD_IPV6_TCP 0x00200000
+
+/* Definitions for power management and wakeup registers */
+/* Wake Up Control */
+#define E1000_WUC_APME 0x00000001 /* APM Enable */
+#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */
+#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */
+#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */
+#define E1000_WUC_SPM 0x80000000 /* Enable SPM */
+
+/* Wake Up Filter Control */
+#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
+#define E1000_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */
+#define E1000_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */
+#define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */
+#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */
+#define E1000_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */
+#define E1000_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */
+#define E1000_WUFC_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Enable */
+#define E1000_WUFC_IGNORE_TCO 0x00008000 /* Ignore WakeOn TCO packets */
+#define E1000_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */
+#define E1000_WUFC_FLX1 0x00020000 /* Flexible Filter 1 Enable */
+#define E1000_WUFC_FLX2 0x00040000 /* Flexible Filter 2 Enable */
+#define E1000_WUFC_FLX3 0x00080000 /* Flexible Filter 3 Enable */
+#define E1000_WUFC_ALL_FILTERS 0x000F00FF /* Mask for all wakeup filters */
+#define E1000_WUFC_FLX_OFFSET 16 /* Offset to the Flexible Filters bits */
+#define E1000_WUFC_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */
+
+/* Wake Up Status */
+#define E1000_WUS_LNKC 0x00000001 /* Link Status Changed */
+#define E1000_WUS_MAG 0x00000002 /* Magic Packet Received */
+#define E1000_WUS_EX 0x00000004 /* Directed Exact Received */
+#define E1000_WUS_MC 0x00000008 /* Directed Multicast Received */
+#define E1000_WUS_BC 0x00000010 /* Broadcast Received */
+#define E1000_WUS_ARP 0x00000020 /* ARP Request Packet Received */
+#define E1000_WUS_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Received */
+#define E1000_WUS_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Received */
+#define E1000_WUS_FLX0 0x00010000 /* Flexible Filter 0 Match */
+#define E1000_WUS_FLX1 0x00020000 /* Flexible Filter 1 Match */
+#define E1000_WUS_FLX2 0x00040000 /* Flexible Filter 2 Match */
+#define E1000_WUS_FLX3 0x00080000 /* Flexible Filter 3 Match */
+#define E1000_WUS_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */
+
+/* Management Control */
+#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */
+#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */
+#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */
+#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */
+#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */
+#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */
+#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */
+#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */
+#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */
+#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery
+ * Filtering */
+#define E1000_MANC_ARP_RES_EN 0x00008000 /* Enable ARP response Filtering */
+#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */
+#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */
+#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */
+#define E1000_MANC_RCV_ALL 0x00080000 /* Receive All Enabled */
+#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */
+#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 /* Enable MAC address
+ * filtering */
+#define E1000_MANC_EN_MNG2HOST 0x00200000 /* Enable MNG packets to host
+ * memory */
+#define E1000_MANC_EN_IP_ADDR_FILTER 0x00400000 /* Enable IP address
+ * filtering */
+#define E1000_MANC_EN_XSUM_FILTER 0x00800000 /* Enable checksum filtering */
+#define E1000_MANC_BR_EN 0x01000000 /* Enable broadcast filtering */
+#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */
+#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */
+#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */
+#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */
+#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */
+#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */
+
+#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */
+#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */
+
+/* SW Semaphore Register */
+#define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */
+#define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */
+#define E1000_SWSM_WMNG 0x00000004 /* Wake MNG Clock */
+#define E1000_SWSM_DRV_LOAD 0x00000008 /* Driver Loaded Bit */
+
+/* FW Semaphore Register */
+#define E1000_FWSM_MODE_MASK 0x0000000E /* FW mode */
+#define E1000_FWSM_MODE_SHIFT 1
+#define E1000_FWSM_FW_VALID 0x00008000 /* FW established a valid mode */
+
+#define E1000_FWSM_RSPCIPHY 0x00000040 /* Reset PHY on PCI reset */
+#define E1000_FWSM_DISSW 0x10000000 /* FW disable SW Write Access */
+#define E1000_FWSM_SKUSEL_MASK 0x60000000 /* LAN SKU select */
+#define E1000_FWSM_SKUEL_SHIFT 29
+#define E1000_FWSM_SKUSEL_EMB 0x0 /* Embedded SKU */
+#define E1000_FWSM_SKUSEL_CONS 0x1 /* Consumer SKU */
+#define E1000_FWSM_SKUSEL_PERF_100 0x2 /* Perf & Corp 10/100 SKU */
+#define E1000_FWSM_SKUSEL_PERF_GBE 0x3 /* Perf & Copr GbE SKU */
+
+/* FFLT Debug Register */
+#define E1000_FFLT_DBG_INVC 0x00100000 /* Invalid /C/ code handling */
+
+typedef enum {
+ e1000_mng_mode_none = 0,
+ e1000_mng_mode_asf,
+ e1000_mng_mode_pt,
+ e1000_mng_mode_ipmi,
+ e1000_mng_mode_host_interface_only
+} e1000_mng_mode;
+
+/* Host Inteface Control Register */
+#define E1000_HICR_EN 0x00000001 /* Enable Bit - RO */
+#define E1000_HICR_C 0x00000002 /* Driver sets this bit when done
+ * to put command in RAM */
+#define E1000_HICR_SV 0x00000004 /* Status Validity */
+#define E1000_HICR_FWR 0x00000080 /* FW reset. Set by the Host */
+
+/* Host Interface Command Interface - Address range 0x8800-0x8EFF */
+#define E1000_HI_MAX_DATA_LENGTH 252 /* Host Interface data length */
+#define E1000_HI_MAX_BLOCK_BYTE_LENGTH 1792 /* Number of bytes in range */
+#define E1000_HI_MAX_BLOCK_DWORD_LENGTH 448 /* Number of dwords in range */
+#define E1000_HI_COMMAND_TIMEOUT 500 /* Time in ms to process HI command */
+
+struct e1000_host_command_header {
+ uint8_t command_id;
+ uint8_t command_length;
+ uint8_t command_options; /* I/F bits for command, status for return */
+ uint8_t checksum;
+};
+struct e1000_host_command_info {
+ struct e1000_host_command_header command_header; /* Command Head/Command Result Head has 4 bytes */
+ uint8_t command_data[E1000_HI_MAX_DATA_LENGTH]; /* Command data can length 0..252 */
+};
+
+/* Host SMB register #0 */
+#define E1000_HSMC0R_CLKIN 0x00000001 /* SMB Clock in */
+#define E1000_HSMC0R_DATAIN 0x00000002 /* SMB Data in */
+#define E1000_HSMC0R_DATAOUT 0x00000004 /* SMB Data out */
+#define E1000_HSMC0R_CLKOUT 0x00000008 /* SMB Clock out */
+
+/* Host SMB register #1 */
+#define E1000_HSMC1R_CLKIN E1000_HSMC0R_CLKIN
+#define E1000_HSMC1R_DATAIN E1000_HSMC0R_DATAIN
+#define E1000_HSMC1R_DATAOUT E1000_HSMC0R_DATAOUT
+#define E1000_HSMC1R_CLKOUT E1000_HSMC0R_CLKOUT
+
+/* FW Status Register */
+#define E1000_FWSTS_FWS_MASK 0x000000FF /* FW Status */
+
+/* Wake Up Packet Length */
+#define E1000_WUPL_LENGTH_MASK 0x0FFF /* Only the lower 12 bits are valid */
+
+#define E1000_MDALIGN 4096
+
+/* PCI-Ex registers*/
+
+/* PCI-Ex Control Register */
+#define E1000_GCR_RXD_NO_SNOOP 0x00000001
+#define E1000_GCR_RXDSCW_NO_SNOOP 0x00000002
+#define E1000_GCR_RXDSCR_NO_SNOOP 0x00000004
+#define E1000_GCR_TXD_NO_SNOOP 0x00000008
+#define E1000_GCR_TXDSCW_NO_SNOOP 0x00000010
+#define E1000_GCR_TXDSCR_NO_SNOOP 0x00000020
+
+#define PCI_EX_NO_SNOOP_ALL (E1000_GCR_RXD_NO_SNOOP | \
+ E1000_GCR_RXDSCW_NO_SNOOP | \
+ E1000_GCR_RXDSCR_NO_SNOOP | \
+ E1000_GCR_TXD_NO_SNOOP | \
+ E1000_GCR_TXDSCW_NO_SNOOP | \
+ E1000_GCR_TXDSCR_NO_SNOOP)
+
+#define PCI_EX_82566_SNOOP_ALL PCI_EX_NO_SNOOP_ALL
+
+#define E1000_GCR_L1_ACT_WITHOUT_L0S_RX 0x08000000
+/* Function Active and Power State to MNG */
+#define E1000_FACTPS_FUNC0_POWER_STATE_MASK 0x00000003
+#define E1000_FACTPS_LAN0_VALID 0x00000004
+#define E1000_FACTPS_FUNC0_AUX_EN 0x00000008
+#define E1000_FACTPS_FUNC1_POWER_STATE_MASK 0x000000C0
+#define E1000_FACTPS_FUNC1_POWER_STATE_SHIFT 6
+#define E1000_FACTPS_LAN1_VALID 0x00000100
+#define E1000_FACTPS_FUNC1_AUX_EN 0x00000200
+#define E1000_FACTPS_FUNC2_POWER_STATE_MASK 0x00003000
+#define E1000_FACTPS_FUNC2_POWER_STATE_SHIFT 12
+#define E1000_FACTPS_IDE_ENABLE 0x00004000
+#define E1000_FACTPS_FUNC2_AUX_EN 0x00008000
+#define E1000_FACTPS_FUNC3_POWER_STATE_MASK 0x000C0000
+#define E1000_FACTPS_FUNC3_POWER_STATE_SHIFT 18
+#define E1000_FACTPS_SP_ENABLE 0x00100000
+#define E1000_FACTPS_FUNC3_AUX_EN 0x00200000
+#define E1000_FACTPS_FUNC4_POWER_STATE_MASK 0x03000000
+#define E1000_FACTPS_FUNC4_POWER_STATE_SHIFT 24
+#define E1000_FACTPS_IPMI_ENABLE 0x04000000
+#define E1000_FACTPS_FUNC4_AUX_EN 0x08000000
+#define E1000_FACTPS_MNGCG 0x20000000
+#define E1000_FACTPS_LAN_FUNC_SEL 0x40000000
+#define E1000_FACTPS_PM_STATE_CHANGED 0x80000000
+
+/* PCI-Ex Config Space */
+#define PCI_EX_LINK_STATUS 0x12
+#define PCI_EX_LINK_WIDTH_MASK 0x3F0
+#define PCI_EX_LINK_WIDTH_SHIFT 4
+
+/* EEPROM Commands - Microwire */
+#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */
+#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */
+#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */
+#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */
+#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erast/write disable */
+
+/* EEPROM Commands - SPI */
+#define EEPROM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */
+#define EEPROM_READ_OPCODE_SPI 0x03 /* EEPROM read opcode */
+#define EEPROM_WRITE_OPCODE_SPI 0x02 /* EEPROM write opcode */
+#define EEPROM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = address bit-8 */
+#define EEPROM_WREN_OPCODE_SPI 0x06 /* EEPROM set Write Enable latch */
+#define EEPROM_WRDI_OPCODE_SPI 0x04 /* EEPROM reset Write Enable latch */
+#define EEPROM_RDSR_OPCODE_SPI 0x05 /* EEPROM read Status register */
+#define EEPROM_WRSR_OPCODE_SPI 0x01 /* EEPROM write Status register */
+#define EEPROM_ERASE4K_OPCODE_SPI 0x20 /* EEPROM ERASE 4KB */
+#define EEPROM_ERASE64K_OPCODE_SPI 0xD8 /* EEPROM ERASE 64KB */
+#define EEPROM_ERASE256_OPCODE_SPI 0xDB /* EEPROM ERASE 256B */
+
+/* EEPROM Size definitions */
+#define EEPROM_WORD_SIZE_SHIFT 6
+#define EEPROM_SIZE_SHIFT 10
+#define EEPROM_SIZE_MASK 0x1C00
+
+/* EEPROM Word Offsets */
+#define EEPROM_COMPAT 0x0003
+#define EEPROM_ID_LED_SETTINGS 0x0004
+#define EEPROM_VERSION 0x0005
+#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */
+#define EEPROM_PHY_CLASS_WORD 0x0007
+#define EEPROM_INIT_CONTROL1_REG 0x000A
+#define EEPROM_INIT_CONTROL2_REG 0x000F
+#define EEPROM_SWDEF_PINS_CTRL_PORT_1 0x0010
+#define EEPROM_INIT_CONTROL3_PORT_B 0x0014
+#define EEPROM_INIT_3GIO_3 0x001A
+#define EEPROM_SWDEF_PINS_CTRL_PORT_0 0x0020
+#define EEPROM_INIT_CONTROL3_PORT_A 0x0024
+#define EEPROM_CFG 0x0012
+#define EEPROM_FLASH_VERSION 0x0032
+#define EEPROM_CHECKSUM_REG 0x003F
+
+#define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */
+#define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */
+
+/* Word definitions for ID LED Settings */
+#define ID_LED_RESERVED_0000 0x0000
+#define ID_LED_RESERVED_FFFF 0xFFFF
+#define ID_LED_RESERVED_82573 0xF746
+#define ID_LED_DEFAULT_82573 0x1811
+#define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \
+ (ID_LED_OFF1_OFF2 << 8) | \
+ (ID_LED_DEF1_DEF2 << 4) | \
+ (ID_LED_DEF1_DEF2))
+#define ID_LED_DEFAULT_ICH8LAN ((ID_LED_DEF1_DEF2 << 12) | \
+ (ID_LED_DEF1_OFF2 << 8) | \
+ (ID_LED_DEF1_ON2 << 4) | \
+ (ID_LED_DEF1_DEF2))
+#define ID_LED_DEF1_DEF2 0x1
+#define ID_LED_DEF1_ON2 0x2
+#define ID_LED_DEF1_OFF2 0x3
+#define ID_LED_ON1_DEF2 0x4
+#define ID_LED_ON1_ON2 0x5
+#define ID_LED_ON1_OFF2 0x6
+#define ID_LED_OFF1_DEF2 0x7
+#define ID_LED_OFF1_ON2 0x8
+#define ID_LED_OFF1_OFF2 0x9
+
+#define IGP_ACTIVITY_LED_MASK 0xFFFFF0FF
+#define IGP_ACTIVITY_LED_ENABLE 0x0300
+#define IGP_LED3_MODE 0x07000000
+
+
+/* Mask bits for SERDES amplitude adjustment in Word 6 of the EEPROM */
+#define EEPROM_SERDES_AMPLITUDE_MASK 0x000F
+
+/* Mask bit for PHY class in Word 7 of the EEPROM */
+#define EEPROM_PHY_CLASS_A 0x8000
+
+/* Mask bits for fields in Word 0x0a of the EEPROM */
+#define EEPROM_WORD0A_ILOS 0x0010
+#define EEPROM_WORD0A_SWDPIO 0x01E0
+#define EEPROM_WORD0A_LRST 0x0200
+#define EEPROM_WORD0A_FD 0x0400
+#define EEPROM_WORD0A_66MHZ 0x0800
+
+/* Mask bits for fields in Word 0x0f of the EEPROM */
+#define EEPROM_WORD0F_PAUSE_MASK 0x3000
+#define EEPROM_WORD0F_PAUSE 0x1000
+#define EEPROM_WORD0F_ASM_DIR 0x2000
+#define EEPROM_WORD0F_ANE 0x0800
+#define EEPROM_WORD0F_SWPDIO_EXT 0x00F0
+#define EEPROM_WORD0F_LPLU 0x0001
+
+/* Mask bits for fields in Word 0x10/0x20 of the EEPROM */
+#define EEPROM_WORD1020_GIGA_DISABLE 0x0010
+#define EEPROM_WORD1020_GIGA_DISABLE_NON_D0A 0x0008
+
+/* Mask bits for fields in Word 0x1a of the EEPROM */
+#define EEPROM_WORD1A_ASPM_MASK 0x000C
+
+/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */
+#define EEPROM_SUM 0xBABA
+
+/* EEPROM Map defines (WORD OFFSETS)*/
+#define EEPROM_NODE_ADDRESS_BYTE_0 0
+#define EEPROM_PBA_BYTE_1 8
+
+#define EEPROM_RESERVED_WORD 0xFFFF
+
+/* EEPROM Map Sizes (Byte Counts) */
+#define PBA_SIZE 4
+
+/* Collision related configuration parameters */
+#define E1000_COLLISION_THRESHOLD 15
+#define E1000_CT_SHIFT 4
+/* Collision distance is a 0-based value that applies to
+ * half-duplex-capable hardware only. */
+#define E1000_COLLISION_DISTANCE 63
+#define E1000_COLLISION_DISTANCE_82542 64
+#define E1000_FDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE
+#define E1000_HDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE
+#define E1000_COLD_SHIFT 12
+
+/* Number of Transmit and Receive Descriptors must be a multiple of 8 */
+#define REQ_TX_DESCRIPTOR_MULTIPLE 8
+#define REQ_RX_DESCRIPTOR_MULTIPLE 8
+
+/* Default values for the transmit IPG register */
+#define DEFAULT_82542_TIPG_IPGT 10
+#define DEFAULT_82543_TIPG_IPGT_FIBER 9
+#define DEFAULT_82543_TIPG_IPGT_COPPER 8
+
+#define E1000_TIPG_IPGT_MASK 0x000003FF
+#define E1000_TIPG_IPGR1_MASK 0x000FFC00
+#define E1000_TIPG_IPGR2_MASK 0x3FF00000
+
+#define DEFAULT_82542_TIPG_IPGR1 2
+#define DEFAULT_82543_TIPG_IPGR1 8
+#define E1000_TIPG_IPGR1_SHIFT 10
+
+#define DEFAULT_82542_TIPG_IPGR2 10
+#define DEFAULT_82543_TIPG_IPGR2 6
+#define DEFAULT_80003ES2LAN_TIPG_IPGR2 7
+#define E1000_TIPG_IPGR2_SHIFT 20
+
+#define DEFAULT_80003ES2LAN_TIPG_IPGT_10_100 0x00000009
+#define DEFAULT_80003ES2LAN_TIPG_IPGT_1000 0x00000008
+#define E1000_TXDMAC_DPP 0x00000001
+
+/* Adaptive IFS defines */
+#define TX_THRESHOLD_START 8
+#define TX_THRESHOLD_INCREMENT 10
+#define TX_THRESHOLD_DECREMENT 1
+#define TX_THRESHOLD_STOP 190
+#define TX_THRESHOLD_DISABLE 0
+#define TX_THRESHOLD_TIMER_MS 10000
+#define MIN_NUM_XMITS 1000
+#define IFS_MAX 80
+#define IFS_STEP 10
+#define IFS_MIN 40
+#define IFS_RATIO 4
+
+/* Extended Configuration Control and Size */
+#define E1000_EXTCNF_CTRL_PCIE_WRITE_ENABLE 0x00000001
+#define E1000_EXTCNF_CTRL_PHY_WRITE_ENABLE 0x00000002
+#define E1000_EXTCNF_CTRL_D_UD_ENABLE 0x00000004
+#define E1000_EXTCNF_CTRL_D_UD_LATENCY 0x00000008
+#define E1000_EXTCNF_CTRL_D_UD_OWNER 0x00000010
+#define E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP 0x00000020
+#define E1000_EXTCNF_CTRL_MDIO_HW_OWNERSHIP 0x00000040
+#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER 0x0FFF0000
+
+#define E1000_EXTCNF_SIZE_EXT_PHY_LENGTH 0x000000FF
+#define E1000_EXTCNF_SIZE_EXT_DOCK_LENGTH 0x0000FF00
+#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH 0x00FF0000
+#define E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE 0x00000001
+#define E1000_EXTCNF_CTRL_SWFLAG 0x00000020
+
+/* PBA constants */
+#define E1000_PBA_8K 0x0008 /* 8KB, default Rx allocation */
+#define E1000_PBA_12K 0x000C /* 12KB, default Rx allocation */
+#define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */
+#define E1000_PBA_20K 0x0014
+#define E1000_PBA_22K 0x0016
+#define E1000_PBA_24K 0x0018
+#define E1000_PBA_30K 0x001E
+#define E1000_PBA_32K 0x0020
+#define E1000_PBA_34K 0x0022
+#define E1000_PBA_38K 0x0026
+#define E1000_PBA_40K 0x0028
+#define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */
+
+#define E1000_PBS_16K E1000_PBA_16K
+
+/* Flow Control Constants */
+#define FLOW_CONTROL_ADDRESS_LOW 0x00C28001
+#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100
+#define FLOW_CONTROL_TYPE 0x8808
+
+/* The historical defaults for the flow control values are given below. */
+#define FC_DEFAULT_HI_THRESH (0x8000) /* 32KB */
+#define FC_DEFAULT_LO_THRESH (0x4000) /* 16KB */
+#define FC_DEFAULT_TX_TIMER (0x100) /* ~130 us */
+
+/* PCIX Config space */
+#define PCIX_COMMAND_REGISTER 0xE6
+#define PCIX_STATUS_REGISTER_LO 0xE8
+#define PCIX_STATUS_REGISTER_HI 0xEA
+
+#define PCIX_COMMAND_MMRBC_MASK 0x000C
+#define PCIX_COMMAND_MMRBC_SHIFT 0x2
+#define PCIX_STATUS_HI_MMRBC_MASK 0x0060
+#define PCIX_STATUS_HI_MMRBC_SHIFT 0x5
+#define PCIX_STATUS_HI_MMRBC_4K 0x3
+#define PCIX_STATUS_HI_MMRBC_2K 0x2
+
+
+/* Number of bits required to shift right the "pause" bits from the
+ * EEPROM (bits 13:12) to the "pause" (bits 8:7) field in the TXCW register.
+ */
+#define PAUSE_SHIFT 5
+
+/* Number of bits required to shift left the "SWDPIO" bits from the
+ * EEPROM (bits 8:5) to the "SWDPIO" (bits 25:22) field in the CTRL register.
+ */
+#define SWDPIO_SHIFT 17
+
+/* Number of bits required to shift left the "SWDPIO_EXT" bits from the
+ * EEPROM word F (bits 7:4) to the bits 11:8 of The Extended CTRL register.
+ */
+#define SWDPIO__EXT_SHIFT 4
+
+/* Number of bits required to shift left the "ILOS" bit from the EEPROM
+ * (bit 4) to the "ILOS" (bit 7) field in the CTRL register.
+ */
+#define ILOS_SHIFT 3
+
+
+#define RECEIVE_BUFFER_ALIGN_SIZE (256)
+
+/* Number of milliseconds we wait for auto-negotiation to complete */
+#define LINK_UP_TIMEOUT 500
+
+/* Number of 100 microseconds we wait for PCI Express master disable */
+#define MASTER_DISABLE_TIMEOUT 800
+/* Number of milliseconds we wait for Eeprom auto read bit done after MAC reset */
+#define AUTO_READ_DONE_TIMEOUT 10
+/* Number of milliseconds we wait for PHY configuration done after MAC reset */
+#define PHY_CFG_TIMEOUT 100
+
+#define E1000_TX_BUFFER_SIZE ((uint32_t)1514)
+
+/* The carrier extension symbol, as received by the NIC. */
+#define CARRIER_EXTENSION 0x0F
+
+/* TBI_ACCEPT macro definition:
+ *
+ * This macro requires:
+ * adapter = a pointer to struct e1000_hw
+ * status = the 8 bit status field of the RX descriptor with EOP set
+ * error = the 8 bit error field of the RX descriptor with EOP set
+ * length = the sum of all the length fields of the RX descriptors that
+ * make up the current frame
+ * last_byte = the last byte of the frame DMAed by the hardware
+ * max_frame_length = the maximum frame length we want to accept.
+ * min_frame_length = the minimum frame length we want to accept.
+ *
+ * This macro is a conditional that should be used in the interrupt
+ * handler's Rx processing routine when RxErrors have been detected.
+ *
+ * Typical use:
+ * ...
+ * if (TBI_ACCEPT) {
+ * accept_frame = TRUE;
+ * e1000_tbi_adjust_stats(adapter, MacAddress);
+ * frame_length--;
+ * } else {
+ * accept_frame = FALSE;
+ * }
+ * ...
+ */
+
+#define TBI_ACCEPT(adapter, status, errors, length, last_byte) \
+ ((adapter)->tbi_compatibility_on && \
+ (((errors) & E1000_RXD_ERR_FRAME_ERR_MASK) == E1000_RXD_ERR_CE) && \
+ ((last_byte) == CARRIER_EXTENSION) && \
+ (((status) & E1000_RXD_STAT_VP) ? \
+ (((length) > ((adapter)->min_frame_size - VLAN_TAG_SIZE)) && \
+ ((length) <= ((adapter)->max_frame_size + 1))) : \
+ (((length) > (adapter)->min_frame_size) && \
+ ((length) <= ((adapter)->max_frame_size + VLAN_TAG_SIZE + 1)))))
+
+
+/* Structures, enums, and macros for the PHY */
+
+/* Bit definitions for the Management Data IO (MDIO) and Management Data
+ * Clock (MDC) pins in the Device Control Register.
+ */
+#define E1000_CTRL_PHY_RESET_DIR E1000_CTRL_SWDPIO0
+#define E1000_CTRL_PHY_RESET E1000_CTRL_SWDPIN0
+#define E1000_CTRL_MDIO_DIR E1000_CTRL_SWDPIO2
+#define E1000_CTRL_MDIO E1000_CTRL_SWDPIN2
+#define E1000_CTRL_MDC_DIR E1000_CTRL_SWDPIO3
+#define E1000_CTRL_MDC E1000_CTRL_SWDPIN3
+#define E1000_CTRL_PHY_RESET_DIR4 E1000_CTRL_EXT_SDP4_DIR
+#define E1000_CTRL_PHY_RESET4 E1000_CTRL_EXT_SDP4_DATA
+
+/* PHY 1000 MII Register/Bit Definitions */
+/* PHY Registers defined by IEEE */
+#define PHY_CTRL 0x00 /* Control Register */
+#define PHY_STATUS 0x01 /* Status Regiser */
+#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */
+#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */
+#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */
+#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */
+#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */
+#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */
+#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */
+#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */
+#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */
+#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */
+
+#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */
+#define MAX_PHY_MULTI_PAGE_REG 0xF /* Registers equal on all pages */
+
+/* M88E1000 Specific Registers */
+#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */
+#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */
+#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */
+#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */
+#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */
+#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */
+
+#define M88E1000_PHY_EXT_CTRL 0x1A /* PHY extend control register */
+#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for page number setting */
+#define M88E1000_PHY_GEN_CONTROL 0x1E /* Its meaning depends on reg 29 */
+#define M88E1000_PHY_VCO_REG_BIT8 0x100 /* Bits 8 & 11 are adjusted for */
+#define M88E1000_PHY_VCO_REG_BIT11 0x800 /* improved BER performance */
+
+#define IGP01E1000_IEEE_REGS_PAGE 0x0000
+#define IGP01E1000_IEEE_RESTART_AUTONEG 0x3300
+#define IGP01E1000_IEEE_FORCE_GIGA 0x0140
+
+/* IGP01E1000 Specific Registers */
+#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* PHY Specific Port Config Register */
+#define IGP01E1000_PHY_PORT_STATUS 0x11 /* PHY Specific Status Register */
+#define IGP01E1000_PHY_PORT_CTRL 0x12 /* PHY Specific Control Register */
+#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health Register */
+#define IGP01E1000_GMII_FIFO 0x14 /* GMII FIFO Register */
+#define IGP01E1000_PHY_CHANNEL_QUALITY 0x15 /* PHY Channel Quality Register */
+#define IGP02E1000_PHY_POWER_MGMT 0x19
+#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* PHY Page Select Core Register */
+
+/* IGP01E1000 AGC Registers - stores the cable length values*/
+#define IGP01E1000_PHY_AGC_A 0x1172
+#define IGP01E1000_PHY_AGC_B 0x1272
+#define IGP01E1000_PHY_AGC_C 0x1472
+#define IGP01E1000_PHY_AGC_D 0x1872
+
+/* IGP02E1000 AGC Registers for cable length values */
+#define IGP02E1000_PHY_AGC_A 0x11B1
+#define IGP02E1000_PHY_AGC_B 0x12B1
+#define IGP02E1000_PHY_AGC_C 0x14B1
+#define IGP02E1000_PHY_AGC_D 0x18B1
+
+/* IGP01E1000 DSP Reset Register */
+#define IGP01E1000_PHY_DSP_RESET 0x1F33
+#define IGP01E1000_PHY_DSP_SET 0x1F71
+#define IGP01E1000_PHY_DSP_FFE 0x1F35
+
+#define IGP01E1000_PHY_CHANNEL_NUM 4
+#define IGP02E1000_PHY_CHANNEL_NUM 4
+
+#define IGP01E1000_PHY_AGC_PARAM_A 0x1171
+#define IGP01E1000_PHY_AGC_PARAM_B 0x1271
+#define IGP01E1000_PHY_AGC_PARAM_C 0x1471
+#define IGP01E1000_PHY_AGC_PARAM_D 0x1871
+
+#define IGP01E1000_PHY_EDAC_MU_INDEX 0xC000
+#define IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS 0x8000
+
+#define IGP01E1000_PHY_ANALOG_TX_STATE 0x2890
+#define IGP01E1000_PHY_ANALOG_CLASS_A 0x2000
+#define IGP01E1000_PHY_FORCE_ANALOG_ENABLE 0x0004
+#define IGP01E1000_PHY_DSP_FFE_CM_CP 0x0069
+
+#define IGP01E1000_PHY_DSP_FFE_DEFAULT 0x002A
+/* IGP01E1000 PCS Initialization register - stores the polarity status when
+ * speed = 1000 Mbps. */
+#define IGP01E1000_PHY_PCS_INIT_REG 0x00B4
+#define IGP01E1000_PHY_PCS_CTRL_REG 0x00B5
+
+#define IGP01E1000_ANALOG_REGS_PAGE 0x20C0
+
+/* Bits...
+ * 15-5: page
+ * 4-0: register offset
+ */
+#define GG82563_PAGE_SHIFT 5
+#define GG82563_REG(page, reg) \
+ (((page) << GG82563_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS))
+#define GG82563_MIN_ALT_REG 30
+
+/* GG82563 Specific Registers */
+#define GG82563_PHY_SPEC_CTRL \
+ GG82563_REG(0, 16) /* PHY Specific Control */
+#define GG82563_PHY_SPEC_STATUS \
+ GG82563_REG(0, 17) /* PHY Specific Status */
+#define GG82563_PHY_INT_ENABLE \
+ GG82563_REG(0, 18) /* Interrupt Enable */
+#define GG82563_PHY_SPEC_STATUS_2 \
+ GG82563_REG(0, 19) /* PHY Specific Status 2 */
+#define GG82563_PHY_RX_ERR_CNTR \
+ GG82563_REG(0, 21) /* Receive Error Counter */
+#define GG82563_PHY_PAGE_SELECT \
+ GG82563_REG(0, 22) /* Page Select */
+#define GG82563_PHY_SPEC_CTRL_2 \
+ GG82563_REG(0, 26) /* PHY Specific Control 2 */
+#define GG82563_PHY_PAGE_SELECT_ALT \
+ GG82563_REG(0, 29) /* Alternate Page Select */
+#define GG82563_PHY_TEST_CLK_CTRL \
+ GG82563_REG(0, 30) /* Test Clock Control (use reg. 29 to select) */
+
+#define GG82563_PHY_MAC_SPEC_CTRL \
+ GG82563_REG(2, 21) /* MAC Specific Control Register */
+#define GG82563_PHY_MAC_SPEC_CTRL_2 \
+ GG82563_REG(2, 26) /* MAC Specific Control 2 */
+
+#define GG82563_PHY_DSP_DISTANCE \
+ GG82563_REG(5, 26) /* DSP Distance */
+
+/* Page 193 - Port Control Registers */
+#define GG82563_PHY_KMRN_MODE_CTRL \
+ GG82563_REG(193, 16) /* Kumeran Mode Control */
+#define GG82563_PHY_PORT_RESET \
+ GG82563_REG(193, 17) /* Port Reset */
+#define GG82563_PHY_REVISION_ID \
+ GG82563_REG(193, 18) /* Revision ID */
+#define GG82563_PHY_DEVICE_ID \
+ GG82563_REG(193, 19) /* Device ID */
+#define GG82563_PHY_PWR_MGMT_CTRL \
+ GG82563_REG(193, 20) /* Power Management Control */
+#define GG82563_PHY_RATE_ADAPT_CTRL \
+ GG82563_REG(193, 25) /* Rate Adaptation Control */
+
+/* Page 194 - KMRN Registers */
+#define GG82563_PHY_KMRN_FIFO_CTRL_STAT \
+ GG82563_REG(194, 16) /* FIFO's Control/Status */
+#define GG82563_PHY_KMRN_CTRL \
+ GG82563_REG(194, 17) /* Control */
+#define GG82563_PHY_INBAND_CTRL \
+ GG82563_REG(194, 18) /* Inband Control */
+#define GG82563_PHY_KMRN_DIAGNOSTIC \
+ GG82563_REG(194, 19) /* Diagnostic */
+#define GG82563_PHY_ACK_TIMEOUTS \
+ GG82563_REG(194, 20) /* Acknowledge Timeouts */
+#define GG82563_PHY_ADV_ABILITY \
+ GG82563_REG(194, 21) /* Advertised Ability */
+#define GG82563_PHY_LINK_PARTNER_ADV_ABILITY \
+ GG82563_REG(194, 23) /* Link Partner Advertised Ability */
+#define GG82563_PHY_ADV_NEXT_PAGE \
+ GG82563_REG(194, 24) /* Advertised Next Page */
+#define GG82563_PHY_LINK_PARTNER_ADV_NEXT_PAGE \
+ GG82563_REG(194, 25) /* Link Partner Advertised Next page */
+#define GG82563_PHY_KMRN_MISC \
+ GG82563_REG(194, 26) /* Misc. */
+
+/* PHY Control Register */
+#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */
+#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */
+#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
+#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
+#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */
+#define MII_CR_POWER_DOWN 0x0800 /* Power down */
+#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
+#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */
+#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */
+#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */
+
+/* PHY Status Register */
+#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */
+#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */
+#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */
+#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */
+#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */
+#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */
+#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */
+#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */
+#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */
+#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */
+#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */
+#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */
+#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */
+#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */
+#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */
+
+/* Autoneg Advertisement Register */
+#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */
+#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */
+#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */
+#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */
+#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */
+#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */
+#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */
+#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */
+#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */
+#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */
+
+/* Link Partner Ability Register (Base Page) */
+#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */
+#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */
+#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */
+#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */
+#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */
+#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */
+#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */
+#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */
+#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */
+#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */
+#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */
+
+/* Autoneg Expansion Register */
+#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */
+#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */
+#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */
+#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */
+#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */
+
+/* Next Page TX Register */
+#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */
+#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges
+ * of different NP
+ */
+#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg
+ * 0 = cannot comply with msg
+ */
+#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */
+#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow
+ * 0 = sending last NP
+ */
+
+/* Link Partner Next Page Register */
+#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */
+#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges
+ * of different NP
+ */
+#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg
+ * 0 = cannot comply with msg
+ */
+#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */
+#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */
+#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow
+ * 0 = sending last NP
+ */
+
+/* 1000BASE-T Control Register */
+#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */
+#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */
+#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */
+#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */
+ /* 0=DTE device */
+#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */
+ /* 0=Configure PHY as Slave */
+#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */
+ /* 0=Automatic Master/Slave config */
+#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */
+#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */
+#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */
+#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */
+#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */
+
+/* 1000BASE-T Status Register */
+#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */
+#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */
+#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */
+#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */
+#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */
+#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */
+#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */
+#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */
+#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12
+#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13
+#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100
+
+/* Extended Status Register */
+#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */
+#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */
+#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */
+#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */
+
+#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */
+#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */
+
+#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */
+ /* (0=enable, 1=disable) */
+
+/* M88E1000 PHY Specific Control Register */
+#define M88E1000_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */
+#define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */
+#define M88E1000_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */
+#define M88E1000_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low,
+ * 0=CLK125 toggling
+ */
+#define M88E1000_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */
+ /* Manual MDI configuration */
+#define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */
+#define M88E1000_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover,
+ * 100BASE-TX/10BASE-T:
+ * MDI Mode
+ */
+#define M88E1000_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled
+ * all speeds.
+ */
+#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE 0x0080
+ /* 1=Enable Extended 10BASE-T distance
+ * (Lower 10BASE-T RX Threshold)
+ * 0=Normal 10BASE-T RX Threshold */
+#define M88E1000_PSCR_MII_5BIT_ENABLE 0x0100
+ /* 1=5-Bit interface in 100BASE-TX
+ * 0=MII interface in 100BASE-TX */
+#define M88E1000_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */
+#define M88E1000_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */
+#define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */
+
+#define M88E1000_PSCR_POLARITY_REVERSAL_SHIFT 1
+#define M88E1000_PSCR_AUTO_X_MODE_SHIFT 5
+#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7
+
+/* M88E1000 PHY Specific Status Register */
+#define M88E1000_PSSR_JABBER 0x0001 /* 1=Jabber */
+#define M88E1000_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */
+#define M88E1000_PSSR_DOWNSHIFT 0x0020 /* 1=Downshifted */
+#define M88E1000_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */
+#define M88E1000_PSSR_CABLE_LENGTH 0x0380 /* 0=<50M;1=50-80M;2=80-110M;
+ * 3=110-140M;4=>140M */
+#define M88E1000_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */
+#define M88E1000_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */
+#define M88E1000_PSSR_PAGE_RCVD 0x1000 /* 1=Page received */
+#define M88E1000_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */
+#define M88E1000_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */
+#define M88E1000_PSSR_10MBS 0x0000 /* 00=10Mbs */
+#define M88E1000_PSSR_100MBS 0x4000 /* 01=100Mbs */
+#define M88E1000_PSSR_1000MBS 0x8000 /* 10=1000Mbs */
+
+#define M88E1000_PSSR_REV_POLARITY_SHIFT 1
+#define M88E1000_PSSR_DOWNSHIFT_SHIFT 5
+#define M88E1000_PSSR_MDIX_SHIFT 6
+#define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7
+
+/* M88E1000 Extended PHY Specific Control Register */
+#define M88E1000_EPSCR_FIBER_LOOPBACK 0x4000 /* 1=Fiber loopback */
+#define M88E1000_EPSCR_DOWN_NO_IDLE 0x8000 /* 1=Lost lock detect enabled.
+ * Will assert lost lock and bring
+ * link down if idle not seen
+ * within 1ms in 1000BASE-T
+ */
+/* Number of times we will attempt to autonegotiate before downshifting if we
+ * are the master */
+#define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00
+#define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X 0x0000
+#define M88E1000_EPSCR_MASTER_DOWNSHIFT_2X 0x0400
+#define M88E1000_EPSCR_MASTER_DOWNSHIFT_3X 0x0800
+#define M88E1000_EPSCR_MASTER_DOWNSHIFT_4X 0x0C00
+/* Number of times we will attempt to autonegotiate before downshifting if we
+ * are the slave */
+#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK 0x0300
+#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_DIS 0x0000
+#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100
+#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_2X 0x0200
+#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_3X 0x0300
+#define M88E1000_EPSCR_TX_CLK_2_5 0x0060 /* 2.5 MHz TX_CLK */
+#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */
+#define M88E1000_EPSCR_TX_CLK_0 0x0000 /* NO TX_CLK */
+
+/* M88EC018 Rev 2 specific DownShift settings */
+#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00
+#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_1X 0x0000
+#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_2X 0x0200
+#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_3X 0x0400
+#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_4X 0x0600
+#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800
+#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_6X 0x0A00
+#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_7X 0x0C00
+#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_8X 0x0E00
+
+/* IGP01E1000 Specific Port Config Register - R/W */
+#define IGP01E1000_PSCFR_AUTO_MDIX_PAR_DETECT 0x0010
+#define IGP01E1000_PSCFR_PRE_EN 0x0020
+#define IGP01E1000_PSCFR_SMART_SPEED 0x0080
+#define IGP01E1000_PSCFR_DISABLE_TPLOOPBACK 0x0100
+#define IGP01E1000_PSCFR_DISABLE_JABBER 0x0400
+#define IGP01E1000_PSCFR_DISABLE_TRANSMIT 0x2000
+
+/* IGP01E1000 Specific Port Status Register - R/O */
+#define IGP01E1000_PSSR_AUTONEG_FAILED 0x0001 /* RO LH SC */
+#define IGP01E1000_PSSR_POLARITY_REVERSED 0x0002
+#define IGP01E1000_PSSR_CABLE_LENGTH 0x007C
+#define IGP01E1000_PSSR_FULL_DUPLEX 0x0200
+#define IGP01E1000_PSSR_LINK_UP 0x0400
+#define IGP01E1000_PSSR_MDIX 0x0800
+#define IGP01E1000_PSSR_SPEED_MASK 0xC000 /* speed bits mask */
+#define IGP01E1000_PSSR_SPEED_10MBPS 0x4000
+#define IGP01E1000_PSSR_SPEED_100MBPS 0x8000
+#define IGP01E1000_PSSR_SPEED_1000MBPS 0xC000
+#define IGP01E1000_PSSR_CABLE_LENGTH_SHIFT 0x0002 /* shift right 2 */
+#define IGP01E1000_PSSR_MDIX_SHIFT 0x000B /* shift right 11 */
+
+/* IGP01E1000 Specific Port Control Register - R/W */
+#define IGP01E1000_PSCR_TP_LOOPBACK 0x0010
+#define IGP01E1000_PSCR_CORRECT_NC_SCMBLR 0x0200
+#define IGP01E1000_PSCR_TEN_CRS_SELECT 0x0400
+#define IGP01E1000_PSCR_FLIP_CHIP 0x0800
+#define IGP01E1000_PSCR_AUTO_MDIX 0x1000
+#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0-MDI, 1-MDIX */
+
+/* IGP01E1000 Specific Port Link Health Register */
+#define IGP01E1000_PLHR_SS_DOWNGRADE 0x8000
+#define IGP01E1000_PLHR_GIG_SCRAMBLER_ERROR 0x4000
+#define IGP01E1000_PLHR_MASTER_FAULT 0x2000
+#define IGP01E1000_PLHR_MASTER_RESOLUTION 0x1000
+#define IGP01E1000_PLHR_GIG_REM_RCVR_NOK 0x0800 /* LH */
+#define IGP01E1000_PLHR_IDLE_ERROR_CNT_OFLOW 0x0400 /* LH */
+#define IGP01E1000_PLHR_DATA_ERR_1 0x0200 /* LH */
+#define IGP01E1000_PLHR_DATA_ERR_0 0x0100
+#define IGP01E1000_PLHR_AUTONEG_FAULT 0x0040
+#define IGP01E1000_PLHR_AUTONEG_ACTIVE 0x0010
+#define IGP01E1000_PLHR_VALID_CHANNEL_D 0x0008
+#define IGP01E1000_PLHR_VALID_CHANNEL_C 0x0004
+#define IGP01E1000_PLHR_VALID_CHANNEL_B 0x0002
+#define IGP01E1000_PLHR_VALID_CHANNEL_A 0x0001
+
+/* IGP01E1000 Channel Quality Register */
+#define IGP01E1000_MSE_CHANNEL_D 0x000F
+#define IGP01E1000_MSE_CHANNEL_C 0x00F0
+#define IGP01E1000_MSE_CHANNEL_B 0x0F00
+#define IGP01E1000_MSE_CHANNEL_A 0xF000
+
+#define IGP02E1000_PM_SPD 0x0001 /* Smart Power Down */
+#define IGP02E1000_PM_D3_LPLU 0x0004 /* Enable LPLU in non-D0a modes */
+#define IGP02E1000_PM_D0_LPLU 0x0002 /* Enable LPLU in D0a mode */
+
+/* IGP01E1000 DSP reset macros */
+#define DSP_RESET_ENABLE 0x0
+#define DSP_RESET_DISABLE 0x2
+#define E1000_MAX_DSP_RESETS 10
+
+/* IGP01E1000 & IGP02E1000 AGC Registers */
+
+#define IGP01E1000_AGC_LENGTH_SHIFT 7 /* Coarse - 13:11, Fine - 10:7 */
+#define IGP02E1000_AGC_LENGTH_SHIFT 9 /* Coarse - 15:13, Fine - 12:9 */
+
+/* IGP02E1000 AGC Register Length 9-bit mask */
+#define IGP02E1000_AGC_LENGTH_MASK 0x7F
+
+/* 7 bits (3 Coarse + 4 Fine) --> 128 optional values */
+#define IGP01E1000_AGC_LENGTH_TABLE_SIZE 128
+#define IGP02E1000_AGC_LENGTH_TABLE_SIZE 113
+
+/* The precision error of the cable length is +/- 10 meters */
+#define IGP01E1000_AGC_RANGE 10
+#define IGP02E1000_AGC_RANGE 15
+
+/* IGP01E1000 PCS Initialization register */
+/* bits 3:6 in the PCS registers stores the channels polarity */
+#define IGP01E1000_PHY_POLARITY_MASK 0x0078
+
+/* IGP01E1000 GMII FIFO Register */
+#define IGP01E1000_GMII_FLEX_SPD 0x10 /* Enable flexible speed
+ * on Link-Up */
+#define IGP01E1000_GMII_SPD 0x20 /* Enable SPD */
+
+/* IGP01E1000 Analog Register */
+#define IGP01E1000_ANALOG_SPARE_FUSE_STATUS 0x20D1
+#define IGP01E1000_ANALOG_FUSE_STATUS 0x20D0
+#define IGP01E1000_ANALOG_FUSE_CONTROL 0x20DC
+#define IGP01E1000_ANALOG_FUSE_BYPASS 0x20DE
+
+#define IGP01E1000_ANALOG_FUSE_POLY_MASK 0xF000
+#define IGP01E1000_ANALOG_FUSE_FINE_MASK 0x0F80
+#define IGP01E1000_ANALOG_FUSE_COARSE_MASK 0x0070
+#define IGP01E1000_ANALOG_SPARE_FUSE_ENABLED 0x0100
+#define IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL 0x0002
+
+#define IGP01E1000_ANALOG_FUSE_COARSE_THRESH 0x0040
+#define IGP01E1000_ANALOG_FUSE_COARSE_10 0x0010
+#define IGP01E1000_ANALOG_FUSE_FINE_1 0x0080
+#define IGP01E1000_ANALOG_FUSE_FINE_10 0x0500
+
+/* GG82563 PHY Specific Status Register (Page 0, Register 16 */
+#define GG82563_PSCR_DISABLE_JABBER 0x0001 /* 1=Disable Jabber */
+#define GG82563_PSCR_POLARITY_REVERSAL_DISABLE 0x0002 /* 1=Polarity Reversal Disabled */
+#define GG82563_PSCR_POWER_DOWN 0x0004 /* 1=Power Down */
+#define GG82563_PSCR_COPPER_TRANSMITER_DISABLE 0x0008 /* 1=Transmitter Disabled */
+#define GG82563_PSCR_CROSSOVER_MODE_MASK 0x0060
+#define GG82563_PSCR_CROSSOVER_MODE_MDI 0x0000 /* 00=Manual MDI configuration */
+#define GG82563_PSCR_CROSSOVER_MODE_MDIX 0x0020 /* 01=Manual MDIX configuration */
+#define GG82563_PSCR_CROSSOVER_MODE_AUTO 0x0060 /* 11=Automatic crossover */
+#define GG82563_PSCR_ENALBE_EXTENDED_DISTANCE 0x0080 /* 1=Enable Extended Distance */
+#define GG82563_PSCR_ENERGY_DETECT_MASK 0x0300
+#define GG82563_PSCR_ENERGY_DETECT_OFF 0x0000 /* 00,01=Off */
+#define GG82563_PSCR_ENERGY_DETECT_RX 0x0200 /* 10=Sense on Rx only (Energy Detect) */
+#define GG82563_PSCR_ENERGY_DETECT_RX_TM 0x0300 /* 11=Sense and Tx NLP */
+#define GG82563_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force Link Good */
+#define GG82563_PSCR_DOWNSHIFT_ENABLE 0x0800 /* 1=Enable Downshift */
+#define GG82563_PSCR_DOWNSHIFT_COUNTER_MASK 0x7000
+#define GG82563_PSCR_DOWNSHIFT_COUNTER_SHIFT 12
+
+/* PHY Specific Status Register (Page 0, Register 17) */
+#define GG82563_PSSR_JABBER 0x0001 /* 1=Jabber */
+#define GG82563_PSSR_POLARITY 0x0002 /* 1=Polarity Reversed */
+#define GG82563_PSSR_LINK 0x0008 /* 1=Link is Up */
+#define GG82563_PSSR_ENERGY_DETECT 0x0010 /* 1=Sleep, 0=Active */
+#define GG82563_PSSR_DOWNSHIFT 0x0020 /* 1=Downshift */
+#define GG82563_PSSR_CROSSOVER_STATUS 0x0040 /* 1=MDIX, 0=MDI */
+#define GG82563_PSSR_RX_PAUSE_ENABLED 0x0100 /* 1=Receive Pause Enabled */
+#define GG82563_PSSR_TX_PAUSE_ENABLED 0x0200 /* 1=Transmit Pause Enabled */
+#define GG82563_PSSR_LINK_UP 0x0400 /* 1=Link Up */
+#define GG82563_PSSR_SPEED_DUPLEX_RESOLVED 0x0800 /* 1=Resolved */
+#define GG82563_PSSR_PAGE_RECEIVED 0x1000 /* 1=Page Received */
+#define GG82563_PSSR_DUPLEX 0x2000 /* 1-Full-Duplex */
+#define GG82563_PSSR_SPEED_MASK 0xC000
+#define GG82563_PSSR_SPEED_10MBPS 0x0000 /* 00=10Mbps */
+#define GG82563_PSSR_SPEED_100MBPS 0x4000 /* 01=100Mbps */
+#define GG82563_PSSR_SPEED_1000MBPS 0x8000 /* 10=1000Mbps */
+
+/* PHY Specific Status Register 2 (Page 0, Register 19) */
+#define GG82563_PSSR2_JABBER 0x0001 /* 1=Jabber */
+#define GG82563_PSSR2_POLARITY_CHANGED 0x0002 /* 1=Polarity Changed */
+#define GG82563_PSSR2_ENERGY_DETECT_CHANGED 0x0010 /* 1=Energy Detect Changed */
+#define GG82563_PSSR2_DOWNSHIFT_INTERRUPT 0x0020 /* 1=Downshift Detected */
+#define GG82563_PSSR2_MDI_CROSSOVER_CHANGE 0x0040 /* 1=Crossover Changed */
+#define GG82563_PSSR2_FALSE_CARRIER 0x0100 /* 1=False Carrier */
+#define GG82563_PSSR2_SYMBOL_ERROR 0x0200 /* 1=Symbol Error */
+#define GG82563_PSSR2_LINK_STATUS_CHANGED 0x0400 /* 1=Link Status Changed */
+#define GG82563_PSSR2_AUTO_NEG_COMPLETED 0x0800 /* 1=Auto-Neg Completed */
+#define GG82563_PSSR2_PAGE_RECEIVED 0x1000 /* 1=Page Received */
+#define GG82563_PSSR2_DUPLEX_CHANGED 0x2000 /* 1=Duplex Changed */
+#define GG82563_PSSR2_SPEED_CHANGED 0x4000 /* 1=Speed Changed */
+#define GG82563_PSSR2_AUTO_NEG_ERROR 0x8000 /* 1=Auto-Neg Error */
+
+/* PHY Specific Control Register 2 (Page 0, Register 26) */
+#define GG82563_PSCR2_10BT_POLARITY_FORCE 0x0002 /* 1=Force Negative Polarity */
+#define GG82563_PSCR2_1000MB_TEST_SELECT_MASK 0x000C
+#define GG82563_PSCR2_1000MB_TEST_SELECT_NORMAL 0x0000 /* 00,01=Normal Operation */
+#define GG82563_PSCR2_1000MB_TEST_SELECT_112NS 0x0008 /* 10=Select 112ns Sequence */
+#define GG82563_PSCR2_1000MB_TEST_SELECT_16NS 0x000C /* 11=Select 16ns Sequence */
+#define GG82563_PSCR2_REVERSE_AUTO_NEG 0x2000 /* 1=Reverse Auto-Negotiation */
+#define GG82563_PSCR2_1000BT_DISABLE 0x4000 /* 1=Disable 1000BASE-T */
+#define GG82563_PSCR2_TRANSMITER_TYPE_MASK 0x8000
+#define GG82563_PSCR2_TRANSMITTER_TYPE_CLASS_B 0x0000 /* 0=Class B */
+#define GG82563_PSCR2_TRANSMITTER_TYPE_CLASS_A 0x8000 /* 1=Class A */
+
+/* MAC Specific Control Register (Page 2, Register 21) */
+/* Tx clock speed for Link Down and 1000BASE-T for the following speeds */
+#define GG82563_MSCR_TX_CLK_MASK 0x0007
+#define GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ 0x0004
+#define GG82563_MSCR_TX_CLK_100MBPS_25MHZ 0x0005
+#define GG82563_MSCR_TX_CLK_1000MBPS_2_5MHZ 0x0006
+#define GG82563_MSCR_TX_CLK_1000MBPS_25MHZ 0x0007
+
+#define GG82563_MSCR_ASSERT_CRS_ON_TX 0x0010 /* 1=Assert */
+
+/* DSP Distance Register (Page 5, Register 26) */
+#define GG82563_DSPD_CABLE_LENGTH 0x0007 /* 0 = <50M;
+ 1 = 50-80M;
+ 2 = 80-110M;
+ 3 = 110-140M;
+ 4 = >140M */
+
+/* Kumeran Mode Control Register (Page 193, Register 16) */
+#define GG82563_KMCR_PHY_LEDS_EN 0x0020 /* 1=PHY LEDs, 0=Kumeran Inband LEDs */
+#define GG82563_KMCR_FORCE_LINK_UP 0x0040 /* 1=Force Link Up */
+#define GG82563_KMCR_SUPPRESS_SGMII_EPD_EXT 0x0080
+#define GG82563_KMCR_MDIO_BUS_SPEED_SELECT_MASK 0x0400
+#define GG82563_KMCR_MDIO_BUS_SPEED_SELECT 0x0400 /* 1=6.25MHz, 0=0.8MHz */
+#define GG82563_KMCR_PASS_FALSE_CARRIER 0x0800
+
+/* Power Management Control Register (Page 193, Register 20) */
+#define GG82563_PMCR_ENABLE_ELECTRICAL_IDLE 0x0001 /* 1=Enalbe SERDES Electrical Idle */
+#define GG82563_PMCR_DISABLE_PORT 0x0002 /* 1=Disable Port */
+#define GG82563_PMCR_DISABLE_SERDES 0x0004 /* 1=Disable SERDES */
+#define GG82563_PMCR_REVERSE_AUTO_NEG 0x0008 /* 1=Enable Reverse Auto-Negotiation */
+#define GG82563_PMCR_DISABLE_1000_NON_D0 0x0010 /* 1=Disable 1000Mbps Auto-Neg in non D0 */
+#define GG82563_PMCR_DISABLE_1000 0x0020 /* 1=Disable 1000Mbps Auto-Neg Always */
+#define GG82563_PMCR_REVERSE_AUTO_NEG_D0A 0x0040 /* 1=Enable D0a Reverse Auto-Negotiation */
+#define GG82563_PMCR_FORCE_POWER_STATE 0x0080 /* 1=Force Power State */
+#define GG82563_PMCR_PROGRAMMED_POWER_STATE_MASK 0x0300
+#define GG82563_PMCR_PROGRAMMED_POWER_STATE_DR 0x0000 /* 00=Dr */
+#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D0U 0x0100 /* 01=D0u */
+#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D0A 0x0200 /* 10=D0a */
+#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D3 0x0300 /* 11=D3 */
+
+/* In-Band Control Register (Page 194, Register 18) */
+#define GG82563_ICR_DIS_PADDING 0x0010 /* Disable Padding Use */
+
+
+/* Bit definitions for valid PHY IDs. */
+/* I = Integrated
+ * E = External
+ */
+#define M88_VENDOR 0x0141
+#define M88E1000_E_PHY_ID 0x01410C50
+#define M88E1000_I_PHY_ID 0x01410C30
+#define M88E1011_I_PHY_ID 0x01410C20
+#define IGP01E1000_I_PHY_ID 0x02A80380
+#define M88E1000_12_PHY_ID M88E1000_E_PHY_ID
+#define M88E1000_14_PHY_ID M88E1000_E_PHY_ID
+#define M88E1011_I_REV_4 0x04
+#define M88E1111_I_PHY_ID 0x01410CC0
+#define L1LXT971A_PHY_ID 0x001378E0
+#define GG82563_E_PHY_ID 0x01410CA0
+
+
+/* Bits...
+ * 15-5: page
+ * 4-0: register offset
+ */
+#define PHY_PAGE_SHIFT 5
+#define PHY_REG(page, reg) \
+ (((page) << PHY_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS))
+
+#define IGP3_PHY_PORT_CTRL \
+ PHY_REG(769, 17) /* Port General Configuration */
+#define IGP3_PHY_RATE_ADAPT_CTRL \
+ PHY_REG(769, 25) /* Rate Adapter Control Register */
+
+#define IGP3_KMRN_FIFO_CTRL_STATS \
+ PHY_REG(770, 16) /* KMRN FIFO's control/status register */
+#define IGP3_KMRN_POWER_MNG_CTRL \
+ PHY_REG(770, 17) /* KMRN Power Management Control Register */
+#define IGP3_KMRN_INBAND_CTRL \
+ PHY_REG(770, 18) /* KMRN Inband Control Register */
+#define IGP3_KMRN_DIAG \
+ PHY_REG(770, 19) /* KMRN Diagnostic register */
+#define IGP3_KMRN_DIAG_PCS_LOCK_LOSS 0x0002 /* RX PCS is not synced */
+#define IGP3_KMRN_ACK_TIMEOUT \
+ PHY_REG(770, 20) /* KMRN Acknowledge Timeouts register */
+
+#define IGP3_VR_CTRL \
+ PHY_REG(776, 18) /* Voltage regulator control register */
+#define IGP3_VR_CTRL_MODE_SHUT 0x0200 /* Enter powerdown, shutdown VRs */
+#define IGP3_VR_CTRL_MODE_MASK 0x0300 /* Shutdown VR Mask */
+
+#define IGP3_CAPABILITY \
+ PHY_REG(776, 19) /* IGP3 Capability Register */
+
+/* Capabilities for SKU Control */
+#define IGP3_CAP_INITIATE_TEAM 0x0001 /* Able to initiate a team */
+#define IGP3_CAP_WFM 0x0002 /* Support WoL and PXE */
+#define IGP3_CAP_ASF 0x0004 /* Support ASF */
+#define IGP3_CAP_LPLU 0x0008 /* Support Low Power Link Up */
+#define IGP3_CAP_DC_AUTO_SPEED 0x0010 /* Support AC/DC Auto Link Speed */
+#define IGP3_CAP_SPD 0x0020 /* Support Smart Power Down */
+#define IGP3_CAP_MULT_QUEUE 0x0040 /* Support 2 tx & 2 rx queues */
+#define IGP3_CAP_RSS 0x0080 /* Support RSS */
+#define IGP3_CAP_8021PQ 0x0100 /* Support 802.1Q & 802.1p */
+#define IGP3_CAP_AMT_CB 0x0200 /* Support active manageability and circuit breaker */
+
+#define IGP3_PPC_JORDAN_EN 0x0001
+#define IGP3_PPC_JORDAN_GIGA_SPEED 0x0002
+
+#define IGP3_KMRN_PMC_EE_IDLE_LINK_DIS 0x0001
+#define IGP3_KMRN_PMC_K0S_ENTRY_LATENCY_MASK 0x001E
+#define IGP3_KMRN_PMC_K0S_MODE1_EN_GIGA 0x0020
+#define IGP3_KMRN_PMC_K0S_MODE1_EN_100 0x0040
+
+#define IGP3E1000_PHY_MISC_CTRL 0x1B /* Misc. Ctrl register */
+#define IGP3_PHY_MISC_DUPLEX_MANUAL_SET 0x1000 /* Duplex Manual Set */
+
+#define IGP3_KMRN_EXT_CTRL PHY_REG(770, 18)
+#define IGP3_KMRN_EC_DIS_INBAND 0x0080
+
+#define IGP03E1000_E_PHY_ID 0x02A80390
+#define IFE_E_PHY_ID 0x02A80330 /* 10/100 PHY */
+#define IFE_PLUS_E_PHY_ID 0x02A80320
+#define IFE_C_E_PHY_ID 0x02A80310
+
+#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10 /* 100BaseTx Extended Status, Control and Address */
+#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY special control register */
+#define IFE_PHY_RCV_FALSE_CARRIER 0x13 /* 100BaseTx Receive False Carrier Counter */
+#define IFE_PHY_RCV_DISCONNECT 0x14 /* 100BaseTx Receive Disconnet Counter */
+#define IFE_PHY_RCV_ERROT_FRAME 0x15 /* 100BaseTx Receive Error Frame Counter */
+#define IFE_PHY_RCV_SYMBOL_ERR 0x16 /* Receive Symbol Error Counter */
+#define IFE_PHY_PREM_EOF_ERR 0x17 /* 100BaseTx Receive Premature End Of Frame Error Counter */
+#define IFE_PHY_RCV_EOF_ERR 0x18 /* 10BaseT Receive End Of Frame Error Counter */
+#define IFE_PHY_TX_JABBER_DETECT 0x19 /* 10BaseT Transmit Jabber Detect Counter */
+#define IFE_PHY_EQUALIZER 0x1A /* PHY Equalizer Control and Status */
+#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY special control and LED configuration */
+#define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control register */
+#define IFE_PHY_HWI_CONTROL 0x1D /* Hardware Integrity Control (HWI) */
+
+#define IFE_PESC_REDUCED_POWER_DOWN_DISABLE 0x2000 /* Defaut 1 = Disable auto reduced power down */
+#define IFE_PESC_100BTX_POWER_DOWN 0x0400 /* Indicates the power state of 100BASE-TX */
+#define IFE_PESC_10BTX_POWER_DOWN 0x0200 /* Indicates the power state of 10BASE-T */
+#define IFE_PESC_POLARITY_REVERSED 0x0100 /* Indicates 10BASE-T polarity */
+#define IFE_PESC_PHY_ADDR_MASK 0x007C /* Bit 6:2 for sampled PHY address */
+#define IFE_PESC_SPEED 0x0002 /* Auto-negotiation speed result 1=100Mbs, 0=10Mbs */
+#define IFE_PESC_DUPLEX 0x0001 /* Auto-negotiation duplex result 1=Full, 0=Half */
+#define IFE_PESC_POLARITY_REVERSED_SHIFT 8
+
+#define IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN 0x0100 /* 1 = Dyanmic Power Down disabled */
+#define IFE_PSC_FORCE_POLARITY 0x0020 /* 1=Reversed Polarity, 0=Normal */
+#define IFE_PSC_AUTO_POLARITY_DISABLE 0x0010 /* 1=Auto Polarity Disabled, 0=Enabled */
+#define IFE_PSC_JABBER_FUNC_DISABLE 0x0001 /* 1=Jabber Disabled, 0=Normal Jabber Operation */
+#define IFE_PSC_FORCE_POLARITY_SHIFT 5
+#define IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT 4
+
+#define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable MDI/MDI-X feature, default 0=disabled */
+#define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDIX-X, 0=force MDI */
+#define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */
+#define IFE_PMC_AUTO_MDIX_COMPLETE 0x0010 /* Resolution algorithm is completed */
+#define IFE_PMC_MDIX_MODE_SHIFT 6
+#define IFE_PHC_MDIX_RESET_ALL_MASK 0x0000 /* Disable auto MDI-X */
+
+#define IFE_PHC_HWI_ENABLE 0x8000 /* Enable the HWI feature */
+#define IFE_PHC_ABILITY_CHECK 0x4000 /* 1= Test Passed, 0=failed */
+#define IFE_PHC_TEST_EXEC 0x2000 /* PHY launch test pulses on the wire */
+#define IFE_PHC_HIGHZ 0x0200 /* 1 = Open Circuit */
+#define IFE_PHC_LOWZ 0x0400 /* 1 = Short Circuit */
+#define IFE_PHC_LOW_HIGH_Z_MASK 0x0600 /* Mask for indication type of problem on the line */
+#define IFE_PHC_DISTANCE_MASK 0x01FF /* Mask for distance to the cable problem, in 80cm granularity */
+#define IFE_PHC_RESET_ALL_MASK 0x0000 /* Disable HWI */
+#define IFE_PSCL_PROBE_MODE 0x0020 /* LED Probe mode */
+#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 off */
+#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */
+
+#define ICH_FLASH_COMMAND_TIMEOUT 5000 /* 5000 uSecs - adjusted */
+#define ICH_FLASH_ERASE_TIMEOUT 3000000 /* Up to 3 seconds - worst case */
+#define ICH_FLASH_CYCLE_REPEAT_COUNT 10 /* 10 cycles */
+#define ICH_FLASH_SEG_SIZE_256 256
+#define ICH_FLASH_SEG_SIZE_4K 4096
+#define ICH_FLASH_SEG_SIZE_64K 65536
+
+#define ICH_CYCLE_READ 0x0
+#define ICH_CYCLE_RESERVED 0x1
+#define ICH_CYCLE_WRITE 0x2
+#define ICH_CYCLE_ERASE 0x3
+
+#define ICH_FLASH_GFPREG 0x0000
+#define ICH_FLASH_HSFSTS 0x0004
+#define ICH_FLASH_HSFCTL 0x0006
+#define ICH_FLASH_FADDR 0x0008
+#define ICH_FLASH_FDATA0 0x0010
+#define ICH_FLASH_FRACC 0x0050
+#define ICH_FLASH_FREG0 0x0054
+#define ICH_FLASH_FREG1 0x0058
+#define ICH_FLASH_FREG2 0x005C
+#define ICH_FLASH_FREG3 0x0060
+#define ICH_FLASH_FPR0 0x0074
+#define ICH_FLASH_FPR1 0x0078
+#define ICH_FLASH_SSFSTS 0x0090
+#define ICH_FLASH_SSFCTL 0x0092
+#define ICH_FLASH_PREOP 0x0094
+#define ICH_FLASH_OPTYPE 0x0096
+#define ICH_FLASH_OPMENU 0x0098
+
+#define ICH_FLASH_REG_MAPSIZE 0x00A0
+#define ICH_FLASH_SECTOR_SIZE 4096
+#define ICH_GFPREG_BASE_MASK 0x1FFF
+#define ICH_FLASH_LINEAR_ADDR_MASK 0x00FFFFFF
+
+/* ICH8 GbE Flash Hardware Sequencing Flash Status Register bit breakdown */
+/* Offset 04h HSFSTS */
+union ich8_hws_flash_status {
+ struct ich8_hsfsts {
+#ifdef E1000_BIG_ENDIAN
+ uint16_t reserved2 :6;
+ uint16_t fldesvalid :1;
+ uint16_t flockdn :1;
+ uint16_t flcdone :1;
+ uint16_t flcerr :1;
+ uint16_t dael :1;
+ uint16_t berasesz :2;
+ uint16_t flcinprog :1;
+ uint16_t reserved1 :2;
+#else
+ uint16_t flcdone :1; /* bit 0 Flash Cycle Done */
+ uint16_t flcerr :1; /* bit 1 Flash Cycle Error */
+ uint16_t dael :1; /* bit 2 Direct Access error Log */
+ uint16_t berasesz :2; /* bit 4:3 Block/Sector Erase Size */
+ uint16_t flcinprog :1; /* bit 5 flash SPI cycle in Progress */
+ uint16_t reserved1 :2; /* bit 13:6 Reserved */
+ uint16_t reserved2 :6; /* bit 13:6 Reserved */
+ uint16_t fldesvalid :1; /* bit 14 Flash Descriptor Valid */
+ uint16_t flockdn :1; /* bit 15 Flash Configuration Lock-Down */
+#endif
+ } hsf_status;
+ uint16_t regval;
+};
+
+/* ICH8 GbE Flash Hardware Sequencing Flash control Register bit breakdown */
+/* Offset 06h FLCTL */
+union ich8_hws_flash_ctrl {
+ struct ich8_hsflctl {
+#ifdef E1000_BIG_ENDIAN
+ uint16_t fldbcount :2;
+ uint16_t flockdn :6;
+ uint16_t flcgo :1;
+ uint16_t flcycle :2;
+ uint16_t reserved :5;
+#else
+ uint16_t flcgo :1; /* 0 Flash Cycle Go */
+ uint16_t flcycle :2; /* 2:1 Flash Cycle */
+ uint16_t reserved :5; /* 7:3 Reserved */
+ uint16_t fldbcount :2; /* 9:8 Flash Data Byte Count */
+ uint16_t flockdn :6; /* 15:10 Reserved */
+#endif
+ } hsf_ctrl;
+ uint16_t regval;
+};
+
+/* ICH8 Flash Region Access Permissions */
+union ich8_hws_flash_regacc {
+ struct ich8_flracc {
+#ifdef E1000_BIG_ENDIAN
+ uint32_t gmwag :8;
+ uint32_t gmrag :8;
+ uint32_t grwa :8;
+ uint32_t grra :8;
+#else
+ uint32_t grra :8; /* 0:7 GbE region Read Access */
+ uint32_t grwa :8; /* 8:15 GbE region Write Access */
+ uint32_t gmrag :8; /* 23:16 GbE Master Read Access Grant */
+ uint32_t gmwag :8; /* 31:24 GbE Master Write Access Grant */
+#endif
+ } hsf_flregacc;
+ uint16_t regval;
+};
+
+/* Miscellaneous PHY bit definitions. */
+#define PHY_PREAMBLE 0xFFFFFFFF
+#define PHY_SOF 0x01
+#define PHY_OP_READ 0x02
+#define PHY_OP_WRITE 0x01
+#define PHY_TURNAROUND 0x02
+#define PHY_PREAMBLE_SIZE 32
+#define MII_CR_SPEED_1000 0x0040
+#define MII_CR_SPEED_100 0x2000
+#define MII_CR_SPEED_10 0x0000
+#define E1000_PHY_ADDRESS 0x01
+#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */
+#define PHY_FORCE_TIME 20 /* 2.0 Seconds */
+#define PHY_REVISION_MASK 0xFFFFFFF0
+#define DEVICE_SPEED_MASK 0x00000300 /* Device Ctrl Reg Speed Mask */
+#define REG4_SPEED_MASK 0x01E0
+#define REG9_SPEED_MASK 0x0300
+#define ADVERTISE_10_HALF 0x0001
+#define ADVERTISE_10_FULL 0x0002
+#define ADVERTISE_100_HALF 0x0004
+#define ADVERTISE_100_FULL 0x0008
+#define ADVERTISE_1000_HALF 0x0010
+#define ADVERTISE_1000_FULL 0x0020
+#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */
+#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds*/
+#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds*/
+
+#endif /* _E1000_HW_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/e1000/e1000_osdep.h b/gpxe/src/drivers/net/e1000/e1000_osdep.h
new file mode 100644
index 00000000..7df9b5e9
--- /dev/null
+++ b/gpxe/src/drivers/net/e1000/e1000_osdep.h
@@ -0,0 +1,142 @@
+/*******************************************************************************
+
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+
+/* glue for the OS independent part of e1000
+ * includes register access macros
+ */
+
+#ifndef _E1000_OSDEP_H_
+#define _E1000_OSDEP_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <io.h>
+#include <errno.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <gpxe/pci.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+
+typedef enum {
+#undef FALSE
+ FALSE = 0,
+#undef TRUE
+ TRUE = 1
+} boolean_t;
+
+/* Debugging #defines */
+
+#if 1
+#define DEBUGFUNC(F) DBG(F "\n")
+#else
+#define DEBUGFUNC(F)
+#endif
+
+#if 1
+#define DEBUGOUT(S) DBG(S)
+#define DEBUGOUT1(S, A...) DBG(S, A)
+#else
+#define DEBUGOUT(S)
+#define DEBUGOUT1(S, A...)
+#endif
+
+#define DEBUGOUT2 DEBUGOUT1
+#define DEBUGOUT3 DEBUGOUT1
+#define DEBUGOUT7 DEBUGOUT1
+
+#define E1000_WRITE_REG(a, reg, value) ( \
+ writel((value), ((a)->hw_addr + \
+ (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg))))
+
+#define E1000_READ_REG(a, reg) ( \
+ readl((a)->hw_addr + \
+ (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg)))
+
+#define E1000_WRITE_REG_ARRAY(a, reg, offset, value) ( \
+ writel((value), ((a)->hw_addr + \
+ (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \
+ ((offset) << 2))))
+
+#define E1000_READ_REG_ARRAY(a, reg, offset) ( \
+ readl((a)->hw_addr + \
+ (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \
+ ((offset) << 2)))
+
+#define E1000_READ_REG_ARRAY_DWORD E1000_READ_REG_ARRAY
+#define E1000_WRITE_REG_ARRAY_DWORD E1000_WRITE_REG_ARRAY
+
+#define E1000_WRITE_REG_ARRAY_WORD(a, reg, offset, value) ( \
+ writew((value), ((a)->hw_addr + \
+ (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \
+ ((offset) << 1))))
+
+#define E1000_READ_REG_ARRAY_WORD(a, reg, offset) ( \
+ readw((a)->hw_addr + \
+ (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \
+ ((offset) << 1)))
+
+#define E1000_WRITE_REG_ARRAY_BYTE(a, reg, offset, value) ( \
+ writeb((value), ((a)->hw_addr + \
+ (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \
+ (offset))))
+
+#define E1000_READ_REG_ARRAY_BYTE(a, reg, offset) ( \
+ readb((a)->hw_addr + \
+ (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \
+ (offset)))
+
+#define E1000_WRITE_FLUSH(a) E1000_READ_REG(a, STATUS)
+
+#define E1000_WRITE_ICH_FLASH_REG(a, reg, value) ( \
+ writel((value), ((a)->flash_address + reg)))
+
+#define E1000_READ_ICH_FLASH_REG(a, reg) ( \
+ readl((a)->flash_address + reg))
+
+#define E1000_WRITE_ICH_FLASH_REG16(a, reg, value) ( \
+ writew((value), ((a)->flash_address + reg)))
+
+#define E1000_READ_ICH_FLASH_REG16(a, reg) ( \
+ readw((a)->flash_address + reg))
+
+#define msleep(n) mdelay(n)
+
+#endif /* _E1000_OSDEP_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/eepro.c b/gpxe/src/drivers/net/eepro.c
new file mode 100644
index 00000000..2a163d1b
--- /dev/null
+++ b/gpxe/src/drivers/net/eepro.c
@@ -0,0 +1,635 @@
+#ifdef ALLMULTI
+#error multicast support is not yet implemented
+#endif
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Intel EEPRO/10 NIC driver for Etherboot
+Adapted from Linux eepro.c from kernel 2.2.17
+
+This board accepts a 32 pin EEPROM (29C256), however a test with a
+27C010 shows that this EPROM also works in the socket, but it's not clear
+how repeatably. The two top address pins appear to be held low, thus
+the bottom 32kB of the 27C010 is visible in the CPU's address space.
+To be sure you could put 4 copies of the code in the 27C010, then
+it doesn't matter whether the extra lines are held low or high, just
+hopefully not floating as CMOS chips don't like floating inputs.
+
+Be careful with seating the EPROM as the socket on my board actually
+has 34 pins, the top row of 2 are not used.
+***************************************************************************/
+
+/*
+
+ timlegge 2005-05-18 remove the relocation changes cards that
+ write directly to the hardware don't need it
+*/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#include "etherboot.h"
+#include <errno.h>
+#include "nic.h"
+#include <gpxe/isa.h>
+#include <gpxe/ethernet.h>
+
+/* Different 82595 chips */
+#define LAN595 0
+#define LAN595TX 1
+#define LAN595FX 2
+#define LAN595FX_10ISA 3
+
+#define SLOW_DOWN inb(0x80);
+
+/* The station (ethernet) address prefix, used for IDing the board. */
+#define SA_ADDR0 0x00 /* Etherexpress Pro/10 */
+#define SA_ADDR1 0xaa
+#define SA_ADDR2 0x00
+
+#define GetBit(x,y) ((x & (1<<y))>>y)
+
+/* EEPROM Word 0: */
+#define ee_PnP 0 /* Plug 'n Play enable bit */
+#define ee_Word1 1 /* Word 1? */
+#define ee_BusWidth 2 /* 8/16 bit */
+#define ee_FlashAddr 3 /* Flash Address */
+#define ee_FlashMask 0x7 /* Mask */
+#define ee_AutoIO 6 /* */
+#define ee_reserved0 7 /* =0! */
+#define ee_Flash 8 /* Flash there? */
+#define ee_AutoNeg 9 /* Auto Negotiation enabled? */
+#define ee_IO0 10 /* IO Address LSB */
+#define ee_IO0Mask 0x /*...*/
+#define ee_IO1 15 /* IO MSB */
+
+/* EEPROM Word 1: */
+#define ee_IntSel 0 /* Interrupt */
+#define ee_IntMask 0x7
+#define ee_LI 3 /* Link Integrity 0= enabled */
+#define ee_PC 4 /* Polarity Correction 0= enabled */
+#define ee_TPE_AUI 5 /* PortSelection 1=TPE */
+#define ee_Jabber 6 /* Jabber prevention 0= enabled */
+#define ee_AutoPort 7 /* Auto Port Selection 1= Disabled */
+#define ee_SMOUT 8 /* SMout Pin Control 0= Input */
+#define ee_PROM 9 /* Flash EPROM / PROM 0=Flash */
+#define ee_reserved1 10 /* .. 12 =0! */
+#define ee_AltReady 13 /* Alternate Ready, 0=normal */
+#define ee_reserved2 14 /* =0! */
+#define ee_Duplex 15
+
+/* Word2,3,4: */
+#define ee_IA5 0 /*bit start for individual Addr Byte 5 */
+#define ee_IA4 8 /*bit start for individual Addr Byte 5 */
+#define ee_IA3 0 /*bit start for individual Addr Byte 5 */
+#define ee_IA2 8 /*bit start for individual Addr Byte 5 */
+#define ee_IA1 0 /*bit start for individual Addr Byte 5 */
+#define ee_IA0 8 /*bit start for individual Addr Byte 5 */
+
+/* Word 5: */
+#define ee_BNC_TPE 0 /* 0=TPE */
+#define ee_BootType 1 /* 00=None, 01=IPX, 10=ODI, 11=NDIS */
+#define ee_BootTypeMask 0x3
+#define ee_NumConn 3 /* Number of Connections 0= One or Two */
+#define ee_FlashSock 4 /* Presence of Flash Socket 0= Present */
+#define ee_PortTPE 5
+#define ee_PortBNC 6
+#define ee_PortAUI 7
+#define ee_PowerMgt 10 /* 0= disabled */
+#define ee_CP 13 /* Concurrent Processing */
+#define ee_CPMask 0x7
+
+/* Word 6: */
+#define ee_Stepping 0 /* Stepping info */
+#define ee_StepMask 0x0F
+#define ee_BoardID 4 /* Manucaturer Board ID, reserved */
+#define ee_BoardMask 0x0FFF
+
+/* Word 7: */
+#define ee_INT_TO_IRQ 0 /* int to IRQ Mapping = 0x1EB8 for Pro/10+ */
+#define ee_FX_INT2IRQ 0x1EB8 /* the _only_ mapping allowed for FX chips */
+
+/*..*/
+#define ee_SIZE 0x40 /* total EEprom Size */
+#define ee_Checksum 0xBABA /* initial and final value for adding checksum */
+
+
+/* Card identification via EEprom: */
+#define ee_addr_vendor 0x10 /* Word offset for EISA Vendor ID */
+#define ee_addr_id 0x11 /* Word offset for Card ID */
+#define ee_addr_SN 0x12 /* Serial Number */
+#define ee_addr_CRC_8 0x14 /* CRC over last thee Bytes */
+
+
+#define ee_vendor_intel0 0x25 /* Vendor ID Intel */
+#define ee_vendor_intel1 0xD4
+#define ee_id_eepro10p0 0x10 /* ID for eepro/10+ */
+#define ee_id_eepro10p1 0x31
+
+/* now this section could be used by both boards: the oldies and the ee10:
+ * ee10 uses tx buffer before of rx buffer and the oldies the inverse.
+ * (aris)
+ */
+#define RAM_SIZE 0x8000
+
+#define RCV_HEADER 8
+#define RCV_DEFAULT_RAM 0x6000
+#define RCV_RAM rcv_ram
+
+static unsigned rcv_ram = RCV_DEFAULT_RAM;
+
+#define XMT_HEADER 8
+#define XMT_RAM (RAM_SIZE - RCV_RAM)
+
+#define XMT_START ((rcv_start + RCV_RAM) % RAM_SIZE)
+
+#define RCV_LOWER_LIMIT (rcv_start >> 8)
+#define RCV_UPPER_LIMIT (((rcv_start + RCV_RAM) - 2) >> 8)
+#define XMT_LOWER_LIMIT (XMT_START >> 8)
+#define XMT_UPPER_LIMIT (((XMT_START + XMT_RAM) - 2) >> 8)
+
+#define RCV_START_PRO 0x00
+#define RCV_START_10 XMT_RAM
+ /* by default the old driver */
+static unsigned rcv_start = RCV_START_PRO;
+
+#define RCV_DONE 0x0008
+#define RX_OK 0x2000
+#define RX_ERROR 0x0d81
+
+#define TX_DONE_BIT 0x0080
+#define CHAIN_BIT 0x8000
+#define XMT_STATUS 0x02
+#define XMT_CHAIN 0x04
+#define XMT_COUNT 0x06
+
+#define BANK0_SELECT 0x00
+#define BANK1_SELECT 0x40
+#define BANK2_SELECT 0x80
+
+/* Bank 0 registers */
+#define COMMAND_REG 0x00 /* Register 0 */
+#define MC_SETUP 0x03
+#define XMT_CMD 0x04
+#define DIAGNOSE_CMD 0x07
+#define RCV_ENABLE_CMD 0x08
+#define RCV_DISABLE_CMD 0x0a
+#define STOP_RCV_CMD 0x0b
+#define RESET_CMD 0x0e
+#define POWER_DOWN_CMD 0x18
+#define RESUME_XMT_CMD 0x1c
+#define SEL_RESET_CMD 0x1e
+#define STATUS_REG 0x01 /* Register 1 */
+#define RX_INT 0x02
+#define TX_INT 0x04
+#define EXEC_STATUS 0x30
+#define ID_REG 0x02 /* Register 2 */
+#define R_ROBIN_BITS 0xc0 /* round robin counter */
+#define ID_REG_MASK 0x2c
+#define ID_REG_SIG 0x24
+#define AUTO_ENABLE 0x10
+#define INT_MASK_REG 0x03 /* Register 3 */
+#define RX_STOP_MASK 0x01
+#define RX_MASK 0x02
+#define TX_MASK 0x04
+#define EXEC_MASK 0x08
+#define ALL_MASK 0x0f
+#define IO_32_BIT 0x10
+#define RCV_BAR 0x04 /* The following are word (16-bit) registers */
+#define RCV_STOP 0x06
+
+#define XMT_BAR_PRO 0x0a
+#define XMT_BAR_10 0x0b
+static unsigned xmt_bar = XMT_BAR_PRO;
+
+#define HOST_ADDRESS_REG 0x0c
+#define IO_PORT 0x0e
+#define IO_PORT_32_BIT 0x0c
+
+/* Bank 1 registers */
+#define REG1 0x01
+#define WORD_WIDTH 0x02
+#define INT_ENABLE 0x80
+#define INT_NO_REG 0x02
+#define RCV_LOWER_LIMIT_REG 0x08
+#define RCV_UPPER_LIMIT_REG 0x09
+
+#define XMT_LOWER_LIMIT_REG_PRO 0x0a
+#define XMT_UPPER_LIMIT_REG_PRO 0x0b
+#define XMT_LOWER_LIMIT_REG_10 0x0b
+#define XMT_UPPER_LIMIT_REG_10 0x0a
+static unsigned xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_PRO;
+static unsigned xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_PRO;
+
+/* Bank 2 registers */
+#define XMT_Chain_Int 0x20 /* Interrupt at the end of the transmit chain */
+#define XMT_Chain_ErrStop 0x40 /* Interrupt at the end of the chain even if there are errors */
+#define RCV_Discard_BadFrame 0x80 /* Throw bad frames away, and continue to receive others */
+#define REG2 0x02
+#define PRMSC_Mode 0x01
+#define Multi_IA 0x20
+#define REG3 0x03
+#define TPE_BIT 0x04
+#define BNC_BIT 0x20
+#define REG13 0x0d
+#define FDX 0x00
+#define A_N_ENABLE 0x02
+
+#define I_ADD_REG0 0x04
+#define I_ADD_REG1 0x05
+#define I_ADD_REG2 0x06
+#define I_ADD_REG3 0x07
+#define I_ADD_REG4 0x08
+#define I_ADD_REG5 0x09
+
+#define EEPROM_REG_PRO 0x0a
+#define EEPROM_REG_10 0x0b
+static unsigned eeprom_reg = EEPROM_REG_PRO;
+
+#define EESK 0x01
+#define EECS 0x02
+#define EEDI 0x04
+#define EEDO 0x08
+
+/* The horrible routine to read a word from the serial EEPROM. */
+/* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */
+
+/* The delay between EEPROM clock transitions. */
+#define eeprom_delay() { udelay(40); }
+#define EE_READ_CMD (6 << 6)
+
+/* do a full reset; data sheet asks for 250us delay */
+#define eepro_full_reset(ioaddr) outb(RESET_CMD, ioaddr); udelay(255);
+
+/* do a nice reset */
+#define eepro_sel_reset(ioaddr) \
+ do { \
+ outb ( SEL_RESET_CMD, ioaddr ); \
+ (void) SLOW_DOWN; \
+ (void) SLOW_DOWN; \
+ } while (0)
+
+/* clear all interrupts */
+#define eepro_clear_int(ioaddr) outb(ALL_MASK, ioaddr + STATUS_REG)
+
+/* enable rx */
+#define eepro_en_rx(ioaddr) outb(RCV_ENABLE_CMD, ioaddr)
+
+/* disable rx */
+#define eepro_dis_rx(ioaddr) outb(RCV_DISABLE_CMD, ioaddr)
+
+/* switch bank */
+#define eepro_sw2bank0(ioaddr) outb(BANK0_SELECT, ioaddr)
+#define eepro_sw2bank1(ioaddr) outb(BANK1_SELECT, ioaddr)
+#define eepro_sw2bank2(ioaddr) outb(BANK2_SELECT, ioaddr)
+
+static unsigned int rx_start, tx_start;
+static int tx_last;
+static unsigned int tx_end;
+static int eepro = 0;
+static unsigned int mem_start, mem_end = RCV_DEFAULT_RAM / 1024;
+
+/**************************************************************************
+RESET - Reset adapter
+***************************************************************************/
+static void eepro_reset(struct nic *nic)
+{
+ int temp_reg, i;
+
+ /* put the card in its initial state */
+ eepro_sw2bank2(nic->ioaddr); /* be careful, bank2 now */
+ temp_reg = inb(nic->ioaddr + eeprom_reg);
+ DBG("Stepping %d\n", temp_reg >> 5);
+ if (temp_reg & 0x10) /* check the TurnOff Enable bit */
+ outb(temp_reg & 0xEF, nic->ioaddr + eeprom_reg);
+ for (i = 0; i < ETH_ALEN; i++) /* fill the MAC address */
+ outb(nic->node_addr[i], nic->ioaddr + I_ADD_REG0 + i);
+ temp_reg = inb(nic->ioaddr + REG1);
+ /* setup Transmit Chaining and discard bad RCV frames */
+ outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop
+ | RCV_Discard_BadFrame, nic->ioaddr + REG1);
+ temp_reg = inb(nic->ioaddr + REG2); /* match broadcast */
+ outb(temp_reg | 0x14, nic->ioaddr + REG2);
+ temp_reg = inb(nic->ioaddr + REG3);
+ outb(temp_reg & 0x3F, nic->ioaddr + REG3); /* clear test mode */
+ /* set the receiving mode */
+ eepro_sw2bank1(nic->ioaddr); /* be careful, bank1 now */
+ /* initialise the RCV and XMT upper and lower limits */
+ outb(RCV_LOWER_LIMIT, nic->ioaddr + RCV_LOWER_LIMIT_REG);
+ outb(RCV_UPPER_LIMIT, nic->ioaddr + RCV_UPPER_LIMIT_REG);
+ outb(XMT_LOWER_LIMIT, nic->ioaddr + xmt_lower_limit_reg);
+ outb(XMT_UPPER_LIMIT, nic->ioaddr + xmt_upper_limit_reg);
+ eepro_sw2bank0(nic->ioaddr); /* Switch back to bank 0 */
+ eepro_clear_int(nic->ioaddr);
+ /* Initialise RCV */
+ outw(rx_start = (RCV_LOWER_LIMIT << 8), nic->ioaddr + RCV_BAR);
+ outw(((RCV_UPPER_LIMIT << 8) | 0xFE), nic->ioaddr + RCV_STOP);
+ /* Make sure 1st poll won't find a valid packet header */
+ outw((RCV_LOWER_LIMIT << 8), nic->ioaddr + HOST_ADDRESS_REG);
+ outw(0, nic->ioaddr + IO_PORT);
+ /* Intialise XMT */
+ outw((XMT_LOWER_LIMIT << 8), nic->ioaddr + xmt_bar);
+ eepro_sel_reset(nic->ioaddr);
+ tx_start = tx_end = (unsigned int) (XMT_LOWER_LIMIT << 8);
+ tx_last = 0;
+ eepro_en_rx(nic->ioaddr);
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int eepro_poll(struct nic *nic, int retrieve)
+{
+ unsigned int rcv_car = rx_start;
+ unsigned int rcv_event, rcv_status, rcv_next_frame, rcv_size;
+
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+#if 0
+ if ((inb(nic->ioaddr + STATUS_REG) & 0x40) == 0)
+ return (0);
+ outb(0x40, nic->ioaddr + STATUS_REG);
+#endif
+ outw(rcv_car, nic->ioaddr + HOST_ADDRESS_REG);
+ rcv_event = inw(nic->ioaddr + IO_PORT);
+ if (rcv_event != RCV_DONE)
+ return (0);
+
+ /* FIXME: I'm guessing this might not work with this card, since
+ it looks like once a rcv_event is started it must be completed.
+ maybe there's another way. */
+ if ( ! retrieve ) return 1;
+
+ rcv_status = inw(nic->ioaddr + IO_PORT);
+ rcv_next_frame = inw(nic->ioaddr + IO_PORT);
+ rcv_size = inw(nic->ioaddr + IO_PORT);
+#if 0
+ printf("%hX %hX %d %hhX\n", rcv_status, rcv_next_frame, rcv_size,
+ inb(nic->ioaddr + STATUS_REG));
+#endif
+ if ((rcv_status & (RX_OK|RX_ERROR)) != RX_OK) {
+ printf("Receive error %hX\n", rcv_status);
+ return (0);
+ }
+ rcv_size &= 0x3FFF;
+ insw(nic->ioaddr + IO_PORT, nic->packet, ((rcv_size + 3) >> 1));
+#if 0
+{
+ int i;
+ for (i = 0; i < 48; i++) {
+ printf("%hhX", nic->packet[i]);
+ putchar(i % 16 == 15 ? '\n' : ' ');
+ }
+}
+#endif
+ nic->packetlen = rcv_size;
+ rcv_car = (rx_start + RCV_HEADER + rcv_size);
+ rx_start = rcv_next_frame;
+/*
+ hex_dump(rcv_car, nic->packetlen);
+*/
+
+ if (rcv_car == 0)
+ rcv_car = ((RCV_UPPER_LIMIT << 8) | 0xff);
+ outw(rcv_car - 1, nic->ioaddr + RCV_STOP);
+ return (1);
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void eepro_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ unsigned int status, tx_available, last, end, length;
+ unsigned short type;
+ int boguscount = 20;
+
+ length = s + ETH_HLEN;
+ if (tx_end > tx_start)
+ tx_available = XMT_RAM - (tx_end - tx_start);
+ else if (tx_end < tx_start)
+ tx_available = tx_start - tx_end;
+ else
+ tx_available = XMT_RAM;
+ last = tx_end;
+ end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
+ if (end >= (XMT_UPPER_LIMIT << 8)) {
+ last = (XMT_LOWER_LIMIT << 8);
+ end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
+ }
+ outw(last, nic->ioaddr + HOST_ADDRESS_REG);
+ outw(XMT_CMD, nic->ioaddr + IO_PORT);
+ outw(0, nic->ioaddr + IO_PORT);
+ outw(end, nic->ioaddr + IO_PORT);
+ outw(length, nic->ioaddr + IO_PORT);
+ outsw(nic->ioaddr + IO_PORT, d, ETH_ALEN / 2);
+ outsw(nic->ioaddr + IO_PORT, nic->node_addr, ETH_ALEN / 2);
+ type = htons(t);
+ outsw(nic->ioaddr + IO_PORT, &type, sizeof(type) / 2);
+ outsw(nic->ioaddr + IO_PORT, p, (s + 3) >> 1);
+ /* A dummy read to flush the DRAM write pipeline */
+ status = inw(nic->ioaddr + IO_PORT);
+ outw(last, nic->ioaddr + xmt_bar);
+ outb(XMT_CMD, nic->ioaddr);
+ tx_start = last;
+ tx_last = last;
+ tx_end = end;
+#if 0
+ printf("%d %d\n", tx_start, tx_end);
+#endif
+ while (boguscount > 0) {
+ if (((status = inw(nic->ioaddr + IO_PORT)) & TX_DONE_BIT) == 0) {
+ udelay(40);
+ boguscount--;
+ continue;
+ }
+ if ((status & 0x2000) == 0) {
+ DBG("Transmit status %hX\n", status);
+ }
+ }
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void eepro_disable ( struct nic *nic, struct isa_device *isa __unused ) {
+ eepro_sw2bank0(nic->ioaddr); /* Switch to bank 0 */
+ /* Flush the Tx and disable Rx */
+ outb(STOP_RCV_CMD, nic->ioaddr);
+ tx_start = tx_end = (XMT_LOWER_LIMIT << 8);
+ tx_last = 0;
+ /* Reset the 82595 */
+ eepro_full_reset(nic->ioaddr);
+}
+
+/**************************************************************************
+DISABLE - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void eepro_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static int read_eeprom(uint16_t ioaddr, int location)
+{
+ int i;
+ unsigned short retval = 0;
+ int ee_addr = ioaddr + eeprom_reg;
+ int read_cmd = location | EE_READ_CMD;
+ int ctrl_val = EECS;
+
+ if (eepro == LAN595FX_10ISA) {
+ eepro_sw2bank1(ioaddr);
+ outb(0x00, ioaddr + STATUS_REG);
+ }
+ eepro_sw2bank2(ioaddr);
+ outb(ctrl_val, ee_addr);
+ /* shift the read command bits out */
+ for (i = 8; i >= 0; i--) {
+ short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val;
+ outb(outval, ee_addr);
+ outb(outval | EESK, ee_addr); /* EEPROM clock tick */
+ eeprom_delay();
+ outb(outval, ee_addr); /* finish EEPROM clock tick */
+ eeprom_delay();
+ }
+ outb(ctrl_val, ee_addr);
+ for (i = 16; i > 0; i--) {
+ outb(ctrl_val | EESK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0);
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ }
+ /* terminate the EEPROM access */
+ ctrl_val &= ~EECS;
+ outb(ctrl_val | EESK, ee_addr);
+ eeprom_delay();
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ eepro_sw2bank0(ioaddr);
+ return (retval);
+}
+
+static int eepro_probe1 ( isa_probe_addr_t ioaddr ) {
+ int id, counter;
+
+ id = inb(ioaddr + ID_REG);
+ if ((id & ID_REG_MASK) != ID_REG_SIG)
+ return (0);
+ counter = id & R_ROBIN_BITS;
+ if (((id = inb(ioaddr + ID_REG)) & R_ROBIN_BITS) != (counter + 0x40))
+ return (0);
+ /* yes the 82595 has been found */
+ return (1);
+}
+
+static struct nic_operations eepro_operations = {
+ .connect = dummy_connect,
+ .poll = eepro_poll,
+ .transmit = eepro_transmit,
+ .irq = eepro_irq,
+
+};
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int eepro_probe ( struct nic *nic, struct isa_device *isa ) {
+
+ int i, l_eepro = 0;
+ union {
+ unsigned char caddr[ETH_ALEN];
+ unsigned short saddr[ETH_ALEN/2];
+ } station_addr;
+ const char *name;
+
+ nic->irqno = 0;
+ nic->ioaddr = isa->ioaddr;
+
+ station_addr.saddr[2] = read_eeprom(nic->ioaddr,2);
+ if ( ( station_addr.saddr[2] == 0x0000 ) ||
+ ( station_addr.saddr[2] == 0xFFFF ) ) {
+ l_eepro = 3;
+ eepro = LAN595FX_10ISA;
+ eeprom_reg= EEPROM_REG_10;
+ rcv_start = RCV_START_10;
+ xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_10;
+ xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_10;
+ station_addr.saddr[2] = read_eeprom(nic->ioaddr,2);
+ }
+ station_addr.saddr[1] = read_eeprom(nic->ioaddr,3);
+ station_addr.saddr[0] = read_eeprom(nic->ioaddr,4);
+ if (l_eepro)
+ name = "Intel EtherExpress 10 ISA";
+ else if (read_eeprom(nic->ioaddr,7) == ee_FX_INT2IRQ) {
+ name = "Intel EtherExpress Pro/10+ ISA";
+ l_eepro = 2;
+ } else if (station_addr.saddr[0] == SA_ADDR1) {
+ name = "Intel EtherExpress Pro/10 ISA";
+ l_eepro = 1;
+ } else {
+ l_eepro = 0;
+ name = "Intel 82595-based LAN card";
+ }
+ station_addr.saddr[0] = swap16(station_addr.saddr[0]);
+ station_addr.saddr[1] = swap16(station_addr.saddr[1]);
+ station_addr.saddr[2] = swap16(station_addr.saddr[2]);
+ for (i = 0; i < ETH_ALEN; i++) {
+ nic->node_addr[i] = station_addr.caddr[i];
+ }
+
+ DBG ( "%s ioaddr %#hX, addr %s", name, nic->ioaddr, eth_ntoa ( nic->node_addr ) );
+
+ mem_start = RCV_LOWER_LIMIT << 8;
+ if ((mem_end & 0x3F) < 3 || (mem_end & 0x3F) > 29)
+ mem_end = RCV_UPPER_LIMIT << 8;
+ else {
+ mem_end = mem_end * 1024 + (RCV_LOWER_LIMIT << 8);
+ rcv_ram = mem_end - (RCV_LOWER_LIMIT << 8);
+ }
+ printf(", Rx mem %dK, if %s\n", (mem_end - mem_start) >> 10,
+ GetBit(read_eeprom(nic->ioaddr,5), ee_BNC_TPE) ? "BNC" : "TP");
+
+ eepro_reset(nic);
+
+ /* point to NIC specific routines */
+ nic->nic_op = &eepro_operations;
+ return 1;
+}
+
+static isa_probe_addr_t eepro_probe_addrs[] = {
+ 0x300, 0x210, 0x240, 0x280, 0x2C0, 0x200, 0x320, 0x340, 0x360,
+};
+
+ISA_DRIVER ( eepro_driver, eepro_probe_addrs, eepro_probe1,
+ GENERIC_ISAPNP_VENDOR, 0x828a );
+
+DRIVER ( "eepro", nic_driver, isa_driver, eepro_driver,
+ eepro_probe, eepro_disable );
+
+ISA_ROM ( "eepro", "Intel Etherexpress Pro/10" );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/eepro100.c b/gpxe/src/drivers/net/eepro100.c
new file mode 100644
index 00000000..f746976a
--- /dev/null
+++ b/gpxe/src/drivers/net/eepro100.c
@@ -0,0 +1,851 @@
+/*
+ * eepro100.c -- This file implements the eepro100 driver for etherboot.
+ *
+ *
+ * Copyright (C) AW Computer Systems.
+ * written by R.E.Wolff -- R.E.Wolff@BitWizard.nl
+ *
+ *
+ * AW Computer Systems is contributing to the free software community
+ * by paying for this driver and then putting the result under GPL.
+ *
+ * If you need a Linux device driver, please contact BitWizard for a
+ * quote.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * date version by what
+ * Written: May 29 1997 V0.10 REW Initial revision.
+ * changes: May 31 1997 V0.90 REW Works!
+ * Jun 1 1997 V0.91 REW Cleanup
+ * Jun 2 1997 V0.92 REW Add some code documentation
+ * Jul 25 1997 V1.00 REW Tested by AW to work in a PROM
+ * Cleanup for publication
+ * Dez 11 2004 V1.10 Kiszka Add RX ring buffer support
+ *
+ * This is the etherboot intel etherexpress Pro/100B driver.
+ *
+ * It was written from scratch, with Donald Beckers eepro100.c kernel
+ * driver as a guideline. Mostly the 82557 related definitions and the
+ * lower level routines have been cut-and-pasted into this source.
+ *
+ * The driver was finished before Intel got the NDA out of the closet.
+ * I still don't have the docs.
+ *
+ *
+ * Datasheet is now published and available from
+ * ftp://download.intel.com/design/network/manuals/8255X_OpenSDM.pdf
+ * - Michael Brown
+ * */
+
+/* Philosophy of this driver.
+ *
+ * Probing:
+ *
+ * Using the pci.c functions of the Etherboot code, the 82557 chip is detected.
+ * It is verified that the BIOS initialized everything properly and if
+ * something is missing it is done now.
+ *
+ *
+ * Initialization:
+ *
+ *
+ * The chip is then initialized to "know" its ethernet address, and to
+ * start recieving packets. The Linux driver has a whole transmit and
+ * recieve ring of buffers. This is neat if you need high performance:
+ * you can write the buffers asynchronously to the chip reading the
+ * buffers and transmitting them over the network. Performance is NOT
+ * an issue here. We can boot a 400k kernel in about two
+ * seconds. (Theory: 0.4 seconds). Booting a system is going to take
+ * about half a minute anyway, so getting 10 times closer to the
+ * theoretical limit is going to make a difference of a few percent. */
+/* Not totally true: busy networks can cause packet drops due to RX
+ * buffer overflows. Fixed in V1.10 of this driver. [Kiszka] */
+/*
+ *
+ * Transmitting and recieving.
+ *
+ * We have only one transmit descriptor. It has two buffer descriptors:
+ * one for the header, and the other for the data.
+ * We have multiple receive buffers (currently: 4). The chip is told to
+ * receive packets and suspend itself once it ran on the last free buffer.
+ * The recieve (poll) routine simply looks at the current recieve buffer,
+ * picks the packet if any, and releases this buffer again (classic ring
+ * buffer concept). This helps to avoid packet drops on busy networks.
+ *
+ * Caveats:
+ *
+ * The Etherboot framework moves the code to the 48k segment from
+ * 0x94000 to 0xa0000. There is just a little room between the end of
+ * this driver and the 0xa0000 address. If you compile in too many
+ * features, this will overflow.
+ * The number under "hex" in the output of size that scrolls by while
+ * compiling should be less than 8000. Maybe even the stack is up there,
+ * so that you need even more headroom.
+ */
+
+/* The etherboot authors seem to dislike the argument ordering in
+ * outb macros that Linux uses. I disklike the confusion that this
+ * has caused even more.... This file uses the Linux argument ordering. */
+/* Sorry not us. It's inherited code from FreeBSD. [The authors] */
+
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/ethernet.h>
+#include <gpxe/pci.h>
+
+static int ioaddr;
+
+enum speedo_offsets {
+ SCBStatus = 0, SCBCmd = 2, /* Rx/Command Unit command and status. */
+ SCBPointer = 4, /* General purpose pointer. */
+ SCBPort = 8, /* Misc. commands and operands. */
+ SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
+ SCBCtrlMDI = 16, /* MDI interface control. */
+ SCBEarlyRx = 20, /* Early receive byte count. */
+};
+
+enum SCBCmdBits {
+ SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000,
+ SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400,
+ SCBTriggerIntr=0x0200, SCBMaskAll=0x0100,
+ /* The rest are Rx and Tx commands. */
+ CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050,
+ CUCmdBase=0x0060, /* CU Base address (set to zero) . */
+ CUDumpStats=0x0070, /* Dump then reset stats counters. */
+ RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006,
+ RxResumeNoResources=0x0007,
+};
+
+static int do_eeprom_cmd(int cmd, int cmd_len);
+void hd(void *where, int n);
+
+/***********************************************************************/
+/* I82557 related defines */
+/***********************************************************************/
+
+/* Serial EEPROM section.
+ A "bit" grungy, but we work our way through bit-by-bit :->. */
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */
+#define EE_CS 0x02 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+#define EE_WRITE_0 0x4802
+#define EE_WRITE_1 0x4806
+#define EE_ENB (0x4800 | EE_CS)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_READ_CMD 6
+
+/* The SCB accepts the following controls for the Tx and Rx units: */
+#define CU_START 0x0010
+#define CU_RESUME 0x0020
+#define CU_STATSADDR 0x0040
+#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */
+#define CU_CMD_BASE 0x0060 /* Base address to add to add CU commands. */
+#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */
+
+#define RX_START 0x0001
+#define RX_RESUME 0x0002
+#define RX_ABORT 0x0004
+#define RX_ADDR_LOAD 0x0006
+#define RX_RESUMENR 0x0007
+#define INT_MASK 0x0100
+#define DRVR_INT 0x0200 /* Driver generated interrupt. */
+
+enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
+ S80C24, PhyUndefined, DP83840A=10, };
+
+/* Commands that can be put in a command list entry. */
+enum commands {
+ CmdNOp = 0,
+ CmdIASetup = 1,
+ CmdConfigure = 2,
+ CmdMulticastList = 3,
+ CmdTx = 4,
+ CmdTDR = 5,
+ CmdDump = 6,
+ CmdDiagnose = 7,
+
+ /* And some extra flags: */
+ CmdSuspend = 0x4000, /* Suspend after completion. */
+ CmdIntr = 0x2000, /* Interrupt after completion. */
+ CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */
+};
+
+/* How to wait for the command unit to accept a command.
+ Typically this takes 0 ticks. */
+static inline void wait_for_cmd_done(int cmd_ioaddr)
+{
+ int wait = 0;
+ int delayed_cmd;
+
+ do
+ if (inb(cmd_ioaddr) == 0) return;
+ while(++wait <= 100);
+ delayed_cmd = inb(cmd_ioaddr);
+ do
+ if (inb(cmd_ioaddr) == 0) break;
+ while(++wait <= 10000);
+ printf("Command %2.2x was not immediately accepted, %d ticks!\n",
+ delayed_cmd, wait);
+}
+
+/* Elements of the dump_statistics block. This block must be lword aligned. */
+static struct speedo_stats {
+ u32 tx_good_frames;
+ u32 tx_coll16_errs;
+ u32 tx_late_colls;
+ u32 tx_underruns;
+ u32 tx_lost_carrier;
+ u32 tx_deferred;
+ u32 tx_one_colls;
+ u32 tx_multi_colls;
+ u32 tx_total_colls;
+ u32 rx_good_frames;
+ u32 rx_crc_errs;
+ u32 rx_align_errs;
+ u32 rx_resource_errs;
+ u32 rx_overrun_errs;
+ u32 rx_colls_errs;
+ u32 rx_runt_errs;
+ u32 done_marker;
+} lstats;
+
+/* A speedo3 TX buffer descriptor with two buffers... */
+static struct TxFD {
+ volatile s16 status;
+ s16 command;
+ u32 link; /* void * */
+ u32 tx_desc_addr; /* (almost) Always points to the tx_buf_addr element. */
+ s32 count; /* # of TBD (=2), Tx start thresh., etc. */
+ /* This constitutes two "TBD" entries: hdr and data */
+ u32 tx_buf_addr0; /* void *, header of frame to be transmitted. */
+ s32 tx_buf_size0; /* Length of Tx hdr. */
+ u32 tx_buf_addr1; /* void *, data to be transmitted. */
+ s32 tx_buf_size1; /* Length of Tx data. */
+} txfd;
+
+struct RxFD { /* Receive frame descriptor. */
+ volatile s16 status;
+ s16 command;
+ u32 link; /* struct RxFD * */
+ u32 rx_buf_addr; /* void * */
+ u16 count;
+ u16 size;
+ char packet[1518];
+};
+
+static struct nic_operations eepro100_operations;
+
+#define RXFD_COUNT 4
+struct {
+ struct RxFD rxfds[RXFD_COUNT];
+} eepro100_bufs __shared;
+#define rxfds eepro100_bufs.rxfds
+static unsigned int rxfd = 0;
+
+static int congenb = 0; /* Enable congestion control in the DP83840. */
+static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */
+static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */
+static int txdmacount = 0; /* Tx DMA burst length, 0-127, default 0. */
+static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */
+
+/* I don't understand a byte in this structure. It was copied from the
+ * Linux kernel initialization for the eepro100. -- REW */
+static struct ConfCmd {
+ s16 status;
+ s16 command;
+ u32 link;
+ unsigned char data[22];
+} confcmd = {
+ 0, 0, 0, /* filled in later */
+ {22, 0x08, 0, 0, 0, 0x80, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */
+ 0, 0x2E, 0, 0x60, 0,
+ 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */
+ 0x3f, 0x05, }
+};
+
+/***********************************************************************/
+/* Locally used functions */
+/***********************************************************************/
+
+/* Support function: mdio_write
+ *
+ * This probably writes to the "physical media interface chip".
+ * -- REW
+ */
+
+static int mdio_write(int phy_id, int location, int value)
+{
+ int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
+
+ outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
+ ioaddr + SCBCtrlMDI);
+ do {
+ udelay(16);
+
+ val = inl(ioaddr + SCBCtrlMDI);
+ if (--boguscnt < 0) {
+ printf(" mdio_write() timed out with val = %X.\n", val);
+ break;
+ }
+ } while (! (val & 0x10000000));
+ return val & 0xffff;
+}
+
+/* Support function: mdio_read
+ *
+ * This probably reads a register in the "physical media interface chip".
+ * -- REW
+ */
+static int mdio_read(int phy_id, int location)
+{
+ int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
+ outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
+ do {
+ udelay(16);
+
+ val = inl(ioaddr + SCBCtrlMDI);
+
+ if (--boguscnt < 0) {
+ printf( " mdio_read() timed out with val = %X.\n", val);
+ break;
+ }
+ } while (! (val & 0x10000000));
+ return val & 0xffff;
+}
+
+/* The fixes for the code were kindly provided by Dragan Stancevic
+ <visitor@valinux.com> to strictly follow Intel specifications of EEPROM
+ access timing.
+ The publicly available sheet 64486302 (sec. 3.1) specifies 1us access
+ interval for serial EEPROM. However, it looks like that there is an
+ additional requirement dictating larger udelay's in the code below.
+ 2000/05/24 SAW */
+static int do_eeprom_cmd(int cmd, int cmd_len)
+{
+ unsigned retval = 0;
+ long ee_addr = ioaddr + SCBeeprom;
+
+ outw(EE_ENB, ee_addr); udelay(2);
+ outw(EE_ENB | EE_SHIFT_CLK, ee_addr); udelay(2);
+
+ /* Shift the command bits out. */
+ do {
+ short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
+ outw(dataval, ee_addr); udelay(2);
+ outw(dataval | EE_SHIFT_CLK, ee_addr); udelay(2);
+ retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ } while (--cmd_len >= 0);
+ outw(EE_ENB, ee_addr); udelay(2);
+
+ /* Terminate the EEPROM access. */
+ outw(EE_ENB & ~EE_CS, ee_addr);
+ return retval;
+}
+
+#if 0
+static inline void whereami (const char *str)
+{
+ printf ("%s\n", str);
+ sleep (2);
+}
+#else
+#define whereami(s)
+#endif
+
+static void eepro100_irq(struct nic *nic __unused, irq_action_t action)
+{
+ uint16_t enabled_mask = ( SCBMaskCmdDone | SCBMaskCmdIdle |
+ SCBMaskEarlyRx | SCBMaskFlowCtl );
+
+ switch ( action ) {
+ case DISABLE :
+ outw(SCBMaskAll, ioaddr + SCBCmd);
+ break;
+ case ENABLE :
+ outw(enabled_mask, ioaddr + SCBCmd);
+ break;
+ case FORCE :
+ outw(enabled_mask | SCBTriggerIntr, ioaddr + SCBCmd);
+ break;
+ }
+}
+
+/* function: eepro100_transmit
+ * This transmits a packet.
+ *
+ * Arguments: char d[6]: destination ethernet address.
+ * unsigned short t: ethernet protocol type.
+ * unsigned short s: size of the data-part of the packet.
+ * char *p: the data for the packet.
+ * returns: void.
+ */
+
+static void eepro100_transmit(struct nic *nic, const char *d, unsigned int t, unsigned int s, const char *p)
+{
+ struct eth_hdr {
+ unsigned char dst_addr[ETH_ALEN];
+ unsigned char src_addr[ETH_ALEN];
+ unsigned short type;
+ } hdr;
+ unsigned short status;
+ int s1, s2;
+ tick_t ct;
+
+ status = inw(ioaddr + SCBStatus);
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(status & 0xfc00, ioaddr + SCBStatus);
+
+#ifdef DEBUG
+ printf ("transmitting type %hX packet (%d bytes). status = %hX, cmd=%hX\n",
+ t, s, status, inw (ioaddr + SCBCmd));
+#endif
+
+ memcpy (&hdr.dst_addr, d, ETH_ALEN);
+ memcpy (&hdr.src_addr, nic->node_addr, ETH_ALEN);
+
+ hdr.type = htons (t);
+
+ txfd.status = 0;
+ txfd.command = CmdSuspend | CmdTx | CmdTxFlex;
+ txfd.link = virt_to_bus (&txfd);
+ txfd.count = 0x02208000;
+ txfd.tx_desc_addr = virt_to_bus(&txfd.tx_buf_addr0);
+
+ txfd.tx_buf_addr0 = virt_to_bus (&hdr);
+ txfd.tx_buf_size0 = sizeof (hdr);
+
+ txfd.tx_buf_addr1 = virt_to_bus (p);
+ txfd.tx_buf_size1 = s;
+
+#ifdef DEBUG
+ printf ("txfd: \n");
+ hd (&txfd, sizeof (txfd));
+#endif
+
+ outl(virt_to_bus(&txfd), ioaddr + SCBPointer);
+ outb(CU_START, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+
+ s1 = inw (ioaddr + SCBStatus);
+
+ ct = currticks();
+ /* timeout 10 ms for transmit */
+ while (!txfd.status && ct + 10*USECS_IN_MSEC)
+ /* Wait */;
+ s2 = inw (ioaddr + SCBStatus);
+
+#ifdef DEBUG
+ printf ("s1 = %hX, s2 = %hX.\n", s1, s2);
+#endif
+}
+
+/*
+ * Sometimes the receiver stops making progress. This routine knows how to
+ * get it going again, without losing packets or being otherwise nasty like
+ * a chip reset would be. Previously the driver had a whole sequence
+ * of if RxSuspended, if it's no buffers do one thing, if it's no resources,
+ * do another, etc. But those things don't really matter. Separate logic
+ * in the ISR provides for allocating buffers--the other half of operation
+ * is just making sure the receiver is active. speedo_rx_soft_reset does that.
+ * This problem with the old, more involved algorithm is shown up under
+ * ping floods on the order of 60K packets/second on a 100Mbps fdx network.
+ */
+static void
+speedo_rx_soft_reset(void)
+{
+ int i;
+
+
+#ifdef DEBUG
+ printf("reset\n");
+#endif
+ wait_for_cmd_done(ioaddr + SCBCmd);
+ /*
+ * Put the hardware into a known state.
+ */
+ outb(RX_ABORT, ioaddr + SCBCmd);
+
+ for (i = 0; i < RXFD_COUNT; i++) {
+ rxfds[i].status = 0;
+ rxfds[i].rx_buf_addr = 0xffffffff;
+ rxfds[i].count = 0;
+ rxfds[i].size = 1528;
+ }
+
+ wait_for_cmd_done(ioaddr + SCBCmd);
+
+ outl(virt_to_bus(&rxfds[rxfd]), ioaddr + SCBPointer);
+ outb(RX_START, ioaddr + SCBCmd);
+}
+
+/* function: eepro100_poll / eth_poll
+ * This receives a packet from the network.
+ *
+ * Arguments: none
+ *
+ * returns: 1 if a packet was received.
+ * 0 if no packet was received.
+ * side effects:
+ * returns the packet in the array nic->packet.
+ * returns the length of the packet in nic->packetlen.
+ */
+
+static int eepro100_poll(struct nic *nic, int retrieve)
+{
+ if (rxfds[rxfd].status) {
+ if (!retrieve)
+ return 1;
+#ifdef DEBUG
+ printf("Got a packet: Len = %d, rxfd = %d.\n",
+ rxfds[rxfd].count & 0x3fff, rxfd);
+#endif
+ /* First save the data from the rxfd */
+ nic->packetlen = rxfds[rxfd].count & 0x3fff;
+ memcpy(nic->packet, rxfds[rxfd].packet, nic->packetlen);
+
+ rxfds[rxfd].status = 0;
+ rxfds[rxfd].command = 0xc000;
+ rxfds[rxfd].rx_buf_addr = 0xFFFFFFFF;
+ rxfds[rxfd].count = 0;
+ rxfds[rxfd].size = 1528;
+ rxfds[(rxfd-1) % RXFD_COUNT].command = 0x0000;
+ rxfd = (rxfd+1) % RXFD_COUNT;
+
+#ifdef DEBUG
+ hd (nic->packet, 0x30);
+#endif
+
+ /* Acknowledge all conceivable interrupts */
+ outw(0xff00, ioaddr + SCBStatus);
+
+ return 1;
+ }
+
+ /*
+ * The chip may have suspended reception for various reasons.
+ * Check for that, and re-prime it should this be the case.
+ */
+ switch ((inw(ioaddr + SCBStatus) >> 2) & 0xf) {
+ case 0: /* Idle */
+ break;
+ case 1: /* Suspended */
+ case 2: /* No resources (RxFDs) */
+ case 9: /* Suspended with no more RBDs */
+ case 10: /* No resources due to no RBDs */
+ case 12: /* Ready with no RBDs */
+ speedo_rx_soft_reset();
+ break;
+ default:
+ /* reserved values */
+ break;
+ }
+ return 0;
+}
+
+/* function: eepro100_disable
+ * resets the card. This is used to allow Etherboot or Linux
+ * to probe the card again from a "virginal" state....
+ * Arguments: none
+ *
+ * returns: void.
+ */
+static void eepro100_disable ( struct nic *nic __unused ) {
+/* from eepro100_reset */
+ outl(0, ioaddr + SCBPort);
+/* from eepro100_disable */
+ /* See if this PartialReset solves the problem with interfering with
+ kernel operation after Etherboot hands over. - Ken 20001102 */
+ outl(2, ioaddr + SCBPort);
+
+ /* The following is from the Intel e100 driver.
+ * This hopefully solves the problem with hanging hard DOS images. */
+
+ /* wait for the reset to take effect */
+ udelay(20);
+
+ /* Mask off our interrupt line -- it is unmasked after reset */
+ {
+ u16 intr_status;
+ /* Disable interrupts on our PCI board by setting the mask bit */
+ outw(INT_MASK, ioaddr + SCBCmd);
+ intr_status = inw(ioaddr + SCBStatus);
+ /* ack and clear intrs */
+ outw(intr_status, ioaddr + SCBStatus);
+ inw(ioaddr + SCBStatus);
+ }
+}
+
+/* exported function: eepro100_probe / eth_probe
+ * initializes a card
+ *
+ * side effects:
+ * leaves the ioaddress of the 82557 chip in the variable ioaddr.
+ * leaves the 82557 initialized, and ready to recieve packets.
+ */
+
+static int eepro100_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ unsigned short sum = 0;
+ int i;
+ int read_cmd, ee_size;
+ int options;
+ int rx_mode;
+ tick_t ct;
+
+ /* we cache only the first few words of the EEPROM data
+ be careful not to access beyond this array */
+ unsigned short eeprom[16];
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ adjust_pci_device(pci);
+
+ nic->ioaddr = pci->ioaddr;
+ nic->irqno = pci->irq;
+
+ ioaddr = nic->ioaddr;
+
+ if ((do_eeprom_cmd(EE_READ_CMD << 24, 27) & 0xffe0000)
+ == 0xffe0000) {
+ ee_size = 0x100;
+ read_cmd = EE_READ_CMD << 24;
+ } else {
+ ee_size = 0x40;
+ read_cmd = EE_READ_CMD << 22;
+ }
+
+ for (i = 0, sum = 0; i < ee_size; i++) {
+ unsigned short value = do_eeprom_cmd(read_cmd | (i << 16), 27);
+ if (i < (int)(sizeof(eeprom)/sizeof(eeprom[0])))
+ eeprom[i] = value;
+ sum += value;
+ }
+
+ for (i=0;i<ETH_ALEN;i++) {
+ nic->node_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff;
+ }
+
+ DBG ( "Ethernet addr: %s\n", eth_ntoa ( nic->node_addr ) );
+
+ if (sum != 0xBABA)
+ printf("eepro100: Invalid EEPROM checksum %#hX, "
+ "check settings before activating this device!\n", sum);
+ outl(0, ioaddr + SCBPort);
+ udelay (10000);
+ whereami ("Got eeprom.");
+
+ /* Base = 0, disable all interrupts */
+ outl(0, ioaddr + SCBPointer);
+ outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+ whereami ("set rx base addr.");
+
+ outl(virt_to_bus(&lstats), ioaddr + SCBPointer);
+ outb(CU_STATSADDR, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+ whereami ("set stats addr.");
+
+ /* INIT RX stuff. */
+ for (i = 0; i < RXFD_COUNT; i++) {
+ rxfds[i].status = 0x0000;
+ rxfds[i].command = 0x0000;
+ rxfds[i].rx_buf_addr = 0xFFFFFFFF;
+ rxfds[i].count = 0;
+ rxfds[i].size = 1528;
+ rxfds[i].link = virt_to_bus(&rxfds[i+1]);
+ }
+
+ rxfds[RXFD_COUNT-1].status = 0x0000;
+ rxfds[RXFD_COUNT-1].command = 0xC000;
+ rxfds[RXFD_COUNT-1].link = virt_to_bus(&rxfds[0]);
+
+ outl(virt_to_bus(&rxfds[0]), ioaddr + SCBPointer);
+ outb(RX_START, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+
+ whereami ("started RX process.");
+
+ /* INIT TX stuff. */
+
+ /* Base = 0 */
+ outl(0, ioaddr + SCBPointer);
+ outb(CU_CMD_BASE, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+
+ whereami ("set TX base addr.");
+
+ txfd.command = (CmdIASetup);
+ txfd.status = 0x0000;
+ txfd.link = virt_to_bus (&confcmd);
+
+ {
+ char *t = (char *)&txfd.tx_desc_addr;
+
+ for (i=0;i<ETH_ALEN;i++)
+ t[i] = nic->node_addr[i];
+ }
+
+#ifdef DEBUG
+ printf ("Setup_eaddr:\n");
+ hd (&txfd, 0x20);
+#endif
+ /* options = 0x40; */ /* 10mbps half duplex... */
+ options = 0x00; /* Autosense */
+
+#ifdef PROMISC
+ rx_mode = 3;
+#elif ALLMULTI
+ rx_mode = 1;
+#else
+ rx_mode = 0;
+#endif
+
+ if ( ((eeprom[6]>>8) & 0x3f) == DP83840
+ || ((eeprom[6]>>8) & 0x3f) == DP83840A) {
+ int mdi_reg23 = mdio_read(eeprom[6] & 0x1f, 23) | 0x0422;
+ if (congenb)
+ mdi_reg23 |= 0x0100;
+ printf(" DP83840 specific setup, setting register 23 to %hX.\n",
+ mdi_reg23);
+ mdio_write(eeprom[6] & 0x1f, 23, mdi_reg23);
+ }
+ whereami ("Done DP8340 special setup.");
+ if (options != 0) {
+ mdio_write(eeprom[6] & 0x1f, 0,
+ ((options & 0x20) ? 0x2000 : 0) | /* 100mbps? */
+ ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+ whereami ("set mdio_register.");
+ }
+
+ confcmd.command = CmdSuspend | CmdConfigure;
+ confcmd.status = 0x0000;
+ confcmd.link = virt_to_bus (&txfd);
+ confcmd.data[1] = (txfifo << 4) | rxfifo;
+ confcmd.data[4] = rxdmacount;
+ confcmd.data[5] = txdmacount + 0x80;
+ confcmd.data[15] = (rx_mode & 2) ? 0x49: 0x48;
+ confcmd.data[19] = (options & 0x10) ? 0xC0 : 0x80;
+ confcmd.data[21] = (rx_mode & 1) ? 0x0D: 0x05;
+
+ outl(virt_to_bus(&txfd), ioaddr + SCBPointer);
+ outb(CU_START, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+
+ whereami ("started TX thingy (config, iasetup).");
+
+ ct = currticks();
+ while (!txfd.status && ct + 10*USECS_IN_MSEC < currticks())
+ /* Wait */;
+
+ /* Read the status register once to disgard stale data */
+ mdio_read(eeprom[6] & 0x1f, 1);
+ /* Check to see if the network cable is plugged in.
+ * This allows for faster failure if there is nothing
+ * we can do.
+ */
+ if (!(mdio_read(eeprom[6] & 0x1f, 1) & (1 << 2))) {
+ printf("Valid link not established\n");
+ eepro100_disable(nic);
+ return 0;
+ }
+ nic->nic_op = &eepro100_operations;
+ return 1;
+}
+
+/*********************************************************************/
+
+#ifdef DEBUG
+
+/* Hexdump a number of bytes from memory... */
+void hd (void *where, int n)
+{
+ int i;
+
+ while (n > 0) {
+ printf ("%X ", where);
+ for (i=0;i < ( (n>16)?16:n);i++)
+ printf (" %hhX", ((char *)where)[i]);
+ printf ("\n");
+ n -= 16;
+ where += 16;
+ }
+}
+#endif
+
+static struct nic_operations eepro100_operations = {
+ .connect = dummy_connect,
+ .poll = eepro100_poll,
+ .transmit = eepro100_transmit,
+ .irq = eepro100_irq,
+
+};
+
+static struct pci_device_id eepro100_nics[] = {
+PCI_ROM(0x8086, 0x1029, "id1029", "Intel EtherExpressPro100 ID1029"),
+PCI_ROM(0x8086, 0x1030, "id1030", "Intel EtherExpressPro100 ID1030"),
+PCI_ROM(0x8086, 0x1031, "82801cam", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
+PCI_ROM(0x8086, 0x1032, "eepro100-1032", "Intel PRO/100 VE Network Connection"),
+PCI_ROM(0x8086, 0x1033, "eepro100-1033", "Intel PRO/100 VM Network Connection"),
+PCI_ROM(0x8086, 0x1034, "eepro100-1034", "Intel PRO/100 VM Network Connection"),
+PCI_ROM(0x8086, 0x1035, "eepro100-1035", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
+PCI_ROM(0x8086, 0x1036, "eepro100-1036", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
+PCI_ROM(0x8086, 0x1037, "eepro100-1037", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
+PCI_ROM(0x8086, 0x1038, "id1038", "Intel PRO/100 VM Network Connection"),
+PCI_ROM(0x8086, 0x1039, "82562et", "Intel PRO100 VE 82562ET"),
+PCI_ROM(0x8086, 0x103a, "id103a", "Intel Corporation 82559 InBusiness 10/100"),
+PCI_ROM(0x8086, 0x103b, "82562etb", "Intel PRO100 VE 82562ETB"),
+PCI_ROM(0x8086, 0x103c, "eepro100-103c", "Intel PRO/100 VM Network Connection"),
+PCI_ROM(0x8086, 0x103d, "eepro100-103d", "Intel PRO/100 VE Network Connection"),
+PCI_ROM(0x8086, 0x103e, "eepro100-103e", "Intel PRO/100 VM Network Connection"),
+PCI_ROM(0x8086, 0x1051, "prove", "Intel PRO/100 VE Network Connection"),
+PCI_ROM(0x8086, 0x1059, "82551qm", "Intel PRO/100 M Mobile Connection"),
+PCI_ROM(0x8086, 0x1209, "82559er", "Intel EtherExpressPro100 82559ER"),
+PCI_ROM(0x8086, 0x1227, "82865", "Intel 82865 EtherExpress PRO/100A"),
+PCI_ROM(0x8086, 0x1228, "82556", "Intel 82556 EtherExpress PRO/100 Smart"),
+PCI_ROM(0x8086, 0x1229, "eepro100", "Intel EtherExpressPro100"),
+PCI_ROM(0x8086, 0x2449, "82562em", "Intel EtherExpressPro100 82562EM"),
+PCI_ROM(0x8086, 0x2459, "82562-1", "Intel 82562 based Fast Ethernet Connection"),
+PCI_ROM(0x8086, 0x245d, "82562-2", "Intel 82562 based Fast Ethernet Connection"),
+PCI_ROM(0x8086, 0x1050, "82562ez", "Intel 82562EZ Network Connection"),
+PCI_ROM(0x8086, 0x1051, "eepro100-1051", "Intel 82801EB/ER (ICH5/ICH5R) Chipset Ethernet Controller"),
+PCI_ROM(0x8086, 0x1065, "82562-3", "Intel 82562 based Fast Ethernet Connection"),
+PCI_ROM(0x8086, 0x5200, "eepro100-5200", "Intel EtherExpress PRO/100 Intelligent Server"),
+PCI_ROM(0x8086, 0x5201, "eepro100-5201", "Intel EtherExpress PRO/100 Intelligent Server"),
+};
+
+/* Cards with device ids 0x1030 to 0x103F, 0x2449, 0x2459 or 0x245D might need
+ * a workaround for hardware bug on 10 mbit half duplex (see linux driver eepro100.c)
+ * 2003/03/17 gbaum */
+
+
+PCI_DRIVER ( eepro100_driver, eepro100_nics, PCI_NO_CLASS );
+
+DRIVER ( "EEPRO100", nic_driver, pci_driver, eepro100_driver,
+ eepro100_probe, eepro100_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/epic100.c b/gpxe/src/drivers/net/epic100.c
new file mode 100644
index 00000000..67b4f0fb
--- /dev/null
+++ b/gpxe/src/drivers/net/epic100.c
@@ -0,0 +1,537 @@
+
+/* epic100.c: A SMC 83c170 EPIC/100 fast ethernet driver for Etherboot */
+
+/* 05/06/2003 timlegge Fixed relocation and implemented Multicast */
+#define LINUX_OUT_MACROS
+
+#include "etherboot.h"
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+#include "nic.h"
+#include "console.h"
+#include "epic100.h"
+
+/* Condensed operations for readability */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+#define TX_RING_SIZE 2 /* use at least 2 buffers for TX */
+#define RX_RING_SIZE 2
+
+#define PKT_BUF_SZ 1536 /* Size of each temporary Tx/Rx buffer.*/
+
+/*
+#define DEBUG_RX
+#define DEBUG_TX
+#define DEBUG_EEPROM
+*/
+
+#define EPIC_DEBUG 0 /* debug level */
+
+/* The EPIC100 Rx and Tx buffer descriptors. */
+struct epic_rx_desc {
+ unsigned long status;
+ unsigned long bufaddr;
+ unsigned long buflength;
+ unsigned long next;
+};
+/* description of the tx descriptors control bits commonly used */
+#define TD_STDFLAGS TD_LASTDESC
+
+struct epic_tx_desc {
+ unsigned long status;
+ unsigned long bufaddr;
+ unsigned long buflength;
+ unsigned long next;
+};
+
+#define delay(nanosec) do { int _i = 3; while (--_i > 0) \
+ { __SLOW_DOWN_IO; }} while (0)
+
+static void epic100_open(void);
+static void epic100_init_ring(void);
+static void epic100_disable(struct nic *nic);
+static int epic100_poll(struct nic *nic, int retrieve);
+static void epic100_transmit(struct nic *nic, const char *destaddr,
+ unsigned int type, unsigned int len, const char *data);
+#ifdef DEBUG_EEPROM
+static int read_eeprom(int location);
+#endif
+static int mii_read(int phy_id, int location);
+static void epic100_irq(struct nic *nic, irq_action_t action);
+
+static struct nic_operations epic100_operations;
+
+static int ioaddr;
+
+static int command;
+static int intstat;
+static int intmask;
+static int genctl ;
+static int eectl ;
+static int test ;
+static int mmctl ;
+static int mmdata ;
+static int lan0 ;
+static int mc0 ;
+static int rxcon ;
+static int txcon ;
+static int prcdar ;
+static int ptcdar ;
+static int eththr ;
+
+static unsigned int cur_rx, cur_tx; /* The next free ring entry */
+#ifdef DEBUG_EEPROM
+static unsigned short eeprom[64];
+#endif
+static signed char phys[4]; /* MII device addresses. */
+struct {
+ struct epic_rx_desc rx_ring[RX_RING_SIZE]
+ __attribute__ ((aligned(4)));
+ struct epic_tx_desc tx_ring[TX_RING_SIZE]
+ __attribute__ ((aligned(4)));
+ unsigned char rx_packet[PKT_BUF_SZ * RX_RING_SIZE];
+ unsigned char tx_packet[PKT_BUF_SZ * TX_RING_SIZE];
+} epic100_bufs __shared;
+#define rx_ring epic100_bufs.rx_ring
+#define tx_ring epic100_bufs.tx_ring
+#define rx_packet epic100_bufs.rx_packet
+#define tx_packet epic100_bufs.tx_packet
+
+/***********************************************************************/
+/* Externally visible functions */
+/***********************************************************************/
+
+
+static int
+epic100_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ int i;
+ unsigned short* ap;
+ unsigned int phy, phy_idx;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ /* Ideally we would detect all network cards in slot order. That would
+ be best done a central PCI probe dispatch, which wouldn't work
+ well with the current structure. So instead we detect just the
+ Epic cards in slot order. */
+
+ ioaddr = pci->ioaddr;
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ /* compute all used static epic100 registers address */
+ command = ioaddr + COMMAND; /* Control Register */
+ intstat = ioaddr + INTSTAT; /* Interrupt Status */
+ intmask = ioaddr + INTMASK; /* Interrupt Mask */
+ genctl = ioaddr + GENCTL; /* General Control */
+ eectl = ioaddr + EECTL; /* EEPROM Control */
+ test = ioaddr + TEST; /* Test register (clocks) */
+ mmctl = ioaddr + MMCTL; /* MII Management Interface Control */
+ mmdata = ioaddr + MMDATA; /* MII Management Interface Data */
+ lan0 = ioaddr + LAN0; /* MAC address. (0x40-0x48) */
+ mc0 = ioaddr + MC0; /* Multicast Control */
+ rxcon = ioaddr + RXCON; /* Receive Control */
+ txcon = ioaddr + TXCON; /* Transmit Control */
+ prcdar = ioaddr + PRCDAR; /* PCI Receive Current Descr Address */
+ ptcdar = ioaddr + PTCDAR; /* PCI Transmit Current Descr Address */
+ eththr = ioaddr + ETHTHR; /* Early Transmit Threshold */
+
+ /* Reset the chip & bring it out of low-power mode. */
+ outl(GC_SOFT_RESET, genctl);
+
+ /* Disable ALL interrupts by setting the interrupt mask. */
+ outl(INTR_DISABLE, intmask);
+
+ /*
+ * set the internal clocks:
+ * Application Note 7.15 says:
+ * In order to set the CLOCK TEST bit in the TEST register,
+ * perform the following:
+ *
+ * Write 0x0008 to the test register at least sixteen
+ * consecutive times.
+ *
+ * The CLOCK TEST bit is Write-Only. Writing it several times
+ * consecutively insures a successful write to the bit...
+ */
+
+ for (i = 0; i < 16; i++) {
+ outl(0x00000008, test);
+ }
+
+#ifdef DEBUG_EEPROM
+{
+ unsigned short sum = 0;
+ unsigned short value;
+ for (i = 0; i < 64; i++) {
+ value = read_eeprom(i);
+ eeprom[i] = value;
+ sum += value;
+ }
+}
+
+#if (EPIC_DEBUG > 1)
+ printf("EEPROM contents\n");
+ for (i = 0; i < 64; i++) {
+ printf(" %hhX%s", eeprom[i], i % 16 == 15 ? "\n" : "");
+ }
+#endif
+#endif
+
+ /* This could also be read from the EEPROM. */
+ ap = (unsigned short*)nic->node_addr;
+ for (i = 0; i < 3; i++)
+ *ap++ = inw(lan0 + i*4);
+
+ DBG ( " I/O %4.4x %s ", ioaddr, eth_ntoa ( nic->node_addr ) );
+
+ /* Find the connected MII xcvrs. */
+ for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(phys); phy++) {
+ int mii_status = mii_read(phy, 0);
+
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ phys[phy_idx++] = phy;
+#if (EPIC_DEBUG > 1)
+ printf("MII transceiver found at address %d.\n", phy);
+#endif
+ }
+ }
+ if (phy_idx == 0) {
+#if (EPIC_DEBUG > 1)
+ printf("***WARNING***: No MII transceiver found!\n");
+#endif
+ /* Use the known PHY address of the EPII. */
+ phys[0] = 3;
+ }
+
+ epic100_open();
+ nic->nic_op = &epic100_operations;
+
+ return 1;
+}
+
+static void set_rx_mode(void)
+{
+ unsigned char mc_filter[8];
+ int i;
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ outl(0x0C, rxcon);
+ for(i = 0; i < 4; i++)
+ outw(((unsigned short *)mc_filter)[i], mc0 + i*4);
+ return;
+}
+
+ static void
+epic100_open(void)
+{
+ int mii_reg5;
+ int full_duplex = 0;
+ unsigned long tmp;
+
+ epic100_init_ring();
+
+ /* Pull the chip out of low-power mode, and set for PCI read multiple. */
+ outl(GC_RX_FIFO_THR_64 | GC_MRC_READ_MULT | GC_ONE_COPY, genctl);
+
+ outl(TX_FIFO_THRESH, eththr);
+
+ tmp = TC_EARLY_TX_ENABLE | TX_SLOT_TIME;
+
+ mii_reg5 = mii_read(phys[0], 5);
+ if (mii_reg5 != 0xffff && (mii_reg5 & 0x0100)) {
+ full_duplex = 1;
+ printf(" full-duplex mode");
+ tmp |= TC_LM_FULL_DPX;
+ } else
+ tmp |= TC_LM_NORMAL;
+
+ outl(tmp, txcon);
+
+ /* Give adress of RX and TX ring to the chip */
+ outl(virt_to_le32desc(&rx_ring), prcdar);
+ outl(virt_to_le32desc(&tx_ring), ptcdar);
+
+ /* Start the chip's Rx process: receive unicast and broadcast */
+ set_rx_mode();
+ outl(CR_START_RX | CR_QUEUE_RX, command);
+
+ putchar('\n');
+}
+
+/* Initialize the Rx and Tx rings. */
+ static void
+epic100_init_ring(void)
+{
+ int i;
+
+ cur_rx = cur_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_ring[i].status = cpu_to_le32(RRING_OWN); /* Owned by Epic chip */
+ rx_ring[i].buflength = cpu_to_le32(PKT_BUF_SZ);
+ rx_ring[i].bufaddr = virt_to_bus(&rx_packet[i * PKT_BUF_SZ]);
+ rx_ring[i].next = virt_to_le32desc(&rx_ring[i + 1]) ;
+ }
+ /* Mark the last entry as wrapping the ring. */
+ rx_ring[i-1].next = virt_to_le32desc(&rx_ring[0]);
+
+ /*
+ *The Tx buffer descriptor is filled in as needed,
+ * but we do need to clear the ownership bit.
+ */
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ tx_ring[i].status = 0x0000; /* Owned by CPU */
+ tx_ring[i].buflength = 0x0000 | cpu_to_le32(TD_STDFLAGS << 16);
+ tx_ring[i].bufaddr = virt_to_bus(&tx_packet[i * PKT_BUF_SZ]);
+ tx_ring[i].next = virt_to_le32desc(&tx_ring[i + 1]);
+ }
+ tx_ring[i-1].next = virt_to_le32desc(&tx_ring[0]);
+}
+
+/* function: epic100_transmit
+ * This transmits a packet.
+ *
+ * Arguments: char d[6]: destination ethernet address.
+ * unsigned short t: ethernet protocol type.
+ * unsigned short s: size of the data-part of the packet.
+ * char *p: the data for the packet.
+ * returns: void.
+ */
+ static void
+epic100_transmit(struct nic *nic, const char *destaddr, unsigned int type,
+ unsigned int len, const char *data)
+{
+ unsigned short nstype;
+ unsigned char *txp;
+ int entry;
+ tick_t ct;
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = cur_tx % TX_RING_SIZE;
+
+ if ((tx_ring[entry].status & TRING_OWN) == TRING_OWN) {
+ printf("eth_transmit: Unable to transmit. status=%4.4lx. Resetting...\n",
+ tx_ring[entry].status);
+
+ epic100_open();
+ return;
+ }
+
+ txp = tx_packet + (entry * PKT_BUF_SZ);
+
+ memcpy(txp, destaddr, ETH_ALEN);
+ memcpy(txp + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons(type);
+ memcpy(txp + 12, (char*)&nstype, 2);
+ memcpy(txp + ETH_HLEN, data, len);
+
+ len += ETH_HLEN;
+ len &= 0x0FFF;
+ while(len < ETH_ZLEN)
+ txp[len++] = '\0';
+ /*
+ * Caution: the write order is important here,
+ * set the base address with the "ownership"
+ * bits last.
+ */
+
+ tx_ring[entry].buflength |= cpu_to_le32(len);
+ tx_ring[entry].status = cpu_to_le32(len << 16) |
+ cpu_to_le32(TRING_OWN); /* Pass ownership to the chip. */
+
+ cur_tx++;
+
+ /* Trigger an immediate transmit demand. */
+ outl(CR_QUEUE_TX, command);
+
+ ct = currticks();
+ /* timeout 10 ms for transmit */
+ while ((le32_to_cpu(tx_ring[entry].status) & (TRING_OWN)) &&
+ ct + 10*USECS_IN_MSEC < currticks())
+ /* Wait */;
+
+ if ((le32_to_cpu(tx_ring[entry].status) & TRING_OWN) != 0)
+ printf("Oops, transmitter timeout, status=%4.4lX\n",
+ tx_ring[entry].status);
+}
+
+/* function: epic100_poll / eth_poll
+ * This receives a packet from the network.
+ *
+ * Arguments: none
+ *
+ * returns: 1 if a packet was received.
+ * 0 if no pacet was received.
+ * side effects:
+ * returns the packet in the array nic->packet.
+ * returns the length of the packet in nic->packetlen.
+ */
+
+ static int
+epic100_poll(struct nic *nic, int retrieve)
+{
+ int entry;
+ int retcode;
+ int status;
+ entry = cur_rx % RX_RING_SIZE;
+
+ if ((rx_ring[entry].status & cpu_to_le32(RRING_OWN)) == RRING_OWN)
+ return (0);
+
+ if ( ! retrieve ) return 1;
+
+ status = le32_to_cpu(rx_ring[entry].status);
+ /* We own the next entry, it's a new packet. Send it up. */
+
+#if (EPIC_DEBUG > 4)
+ printf("epic_poll: entry %d status %hX\n", entry, status);
+#endif
+
+ cur_rx++;
+ if (status & 0x2000) {
+ printf("epic_poll: Giant packet\n");
+ retcode = 0;
+ } else if (status & 0x0006) {
+ /* Rx Frame errors are counted in hardware. */
+ printf("epic_poll: Frame received with errors\n");
+ retcode = 0;
+ } else {
+ /* Omit the four octet CRC from the length. */
+ nic->packetlen = le32_to_cpu((rx_ring[entry].buflength))- 4;
+ memcpy(nic->packet, &rx_packet[entry * PKT_BUF_SZ], nic->packetlen);
+ retcode = 1;
+ }
+
+ /* Clear all error sources. */
+ outl(status & INTR_CLEARERRS, intstat);
+
+ /* Give the descriptor back to the chip */
+ rx_ring[entry].status = RRING_OWN;
+
+ /* Restart Receiver */
+ outl(CR_START_RX | CR_QUEUE_RX, command);
+
+ return retcode;
+}
+
+
+static void epic100_disable ( struct nic *nic __unused ) {
+ /* Soft reset the chip. */
+ outl(GC_SOFT_RESET, genctl);
+}
+
+static void epic100_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+#ifdef DEBUG_EEPROM
+/* Serial EEPROM section. */
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
+#define EE_CS 0x02 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x08 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x01
+#define EE_WRITE_1 0x09
+#define EE_DATA_READ 0x10 /* EEPROM chip data out. */
+#define EE_ENB (0x0001 | EE_CS)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << 6)
+#define EE_READ_CMD (6 << 6)
+#define EE_ERASE_CMD (7 << 6)
+
+#define eeprom_delay(n) delay(n)
+
+ static int
+read_eeprom(int location)
+{
+ int i;
+ int retval = 0;
+ int read_cmd = location | EE_READ_CMD;
+
+ outl(EE_ENB & ~EE_CS, eectl);
+ outl(EE_ENB, eectl);
+
+ /* Shift the read command bits out. */
+ for (i = 10; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ outl(EE_ENB | dataval, eectl);
+ eeprom_delay(100);
+ outl(EE_ENB | dataval | EE_SHIFT_CLK, eectl);
+ eeprom_delay(150);
+ outl(EE_ENB | dataval, eectl); /* Finish EEPROM a clock tick. */
+ eeprom_delay(250);
+ }
+ outl(EE_ENB, eectl);
+
+ for (i = 16; i > 0; i--) {
+ outl(EE_ENB | EE_SHIFT_CLK, eectl);
+ eeprom_delay(100);
+ retval = (retval << 1) | ((inl(eectl) & EE_DATA_READ) ? 1 : 0);
+ outl(EE_ENB, eectl);
+ eeprom_delay(100);
+ }
+
+ /* Terminate the EEPROM access. */
+ outl(EE_ENB & ~EE_CS, eectl);
+ return retval;
+}
+#endif
+
+
+#define MII_READOP 1
+#define MII_WRITEOP 2
+
+ static int
+mii_read(int phy_id, int location)
+{
+ int i;
+
+ outl((phy_id << 9) | (location << 4) | MII_READOP, mmctl);
+ /* Typical operation takes < 50 ticks. */
+
+ for (i = 4000; i > 0; i--)
+ if ((inl(mmctl) & MII_READOP) == 0)
+ break;
+ return inw(mmdata);
+}
+
+static struct nic_operations epic100_operations = {
+ .connect = dummy_connect,
+ .poll = epic100_poll,
+ .transmit = epic100_transmit,
+ .irq = epic100_irq,
+
+};
+
+static struct pci_device_id epic100_nics[] = {
+PCI_ROM(0x10b8, 0x0005, "epic100", "SMC EtherPowerII"), /* SMC 83c170 EPIC/100 */
+PCI_ROM(0x10b8, 0x0006, "smc-83c175", "SMC EPIC/C 83c175"),
+};
+
+PCI_DRIVER ( epic100_driver, epic100_nics, PCI_NO_CLASS );
+
+DRIVER ( "EPIC100", nic_driver, pci_driver, epic100_driver,
+ epic100_probe, epic100_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/epic100.h b/gpxe/src/drivers/net/epic100.h
new file mode 100644
index 00000000..61bd1d94
--- /dev/null
+++ b/gpxe/src/drivers/net/epic100.h
@@ -0,0 +1,188 @@
+#ifndef _EPIC100_H_
+# define _EPIC100_H_
+
+#ifndef PCI_VENDOR_SMC
+# define PCI_VENDOR_SMC 0x10B8
+#endif
+
+#ifndef PCI_DEVICE_SMC_EPIC100
+# define PCI_DEVICE_SMC_EPIC100 0x0005
+#endif
+
+#define PCI_DEVICE_ID_NONE 0xFFFF
+
+/* Offsets to registers (using SMC names). */
+enum epic100_registers {
+ COMMAND= 0, /* Control Register */
+ INTSTAT= 4, /* Interrupt Status */
+ INTMASK= 8, /* Interrupt Mask */
+ GENCTL = 0x0C, /* General Control */
+ NVCTL = 0x10, /* Non Volatile Control */
+ EECTL = 0x14, /* EEPROM Control */
+ TEST = 0x1C, /* Test register: marked as reserved (see in source code) */
+ CRCCNT = 0x20, /* CRC Error Counter */
+ ALICNT = 0x24, /* Frame Alignment Error Counter */
+ MPCNT = 0x28, /* Missed Packet Counter */
+ MMCTL = 0x30, /* MII Management Interface Control */
+ MMDATA = 0x34, /* MII Management Interface Data */
+ MIICFG = 0x38, /* MII Configuration */
+ IPG = 0x3C, /* InterPacket Gap */
+ LAN0 = 0x40, /* MAC address. (0x40-0x48) */
+ IDCHK = 0x4C, /* BoardID/ Checksum */
+ MC0 = 0x50, /* Multicast filter table. (0x50-0x5c) */
+ RXCON = 0x60, /* Receive Control */
+ TXCON = 0x70, /* Transmit Control */
+ TXSTAT = 0x74, /* Transmit Status */
+ PRCDAR = 0x84, /* PCI Receive Current Descriptor Address */
+ PRSTAT = 0xA4, /* PCI Receive DMA Status */
+ PRCPTHR= 0xB0, /* PCI Receive Copy Threshold */
+ PTCDAR = 0xC4, /* PCI Transmit Current Descriptor Address */
+ ETHTHR = 0xDC /* Early Transmit Threshold */
+};
+
+/* Command register (CR_) bits */
+#define CR_STOP_RX (0x00000001)
+#define CR_START_RX (0x00000002)
+#define CR_QUEUE_TX (0x00000004)
+#define CR_QUEUE_RX (0x00000008)
+#define CR_NEXTFRAME (0x00000010)
+#define CR_STOP_TX_DMA (0x00000020)
+#define CR_STOP_RX_DMA (0x00000040)
+#define CR_TX_UGO (0x00000080)
+
+/* Interrupt register bits. NI means No Interrupt generated */
+
+#define INTR_RX_THR_STA (0x00400000) /* rx copy threshold status NI */
+#define INTR_RX_BUFF_EMPTY (0x00200000) /* rx buffers empty. NI */
+#define INTR_TX_IN_PROG (0x00100000) /* tx copy in progess. NI */
+#define INTR_RX_IN_PROG (0x00080000) /* rx copy in progress. NI */
+#define INTR_TXIDLE (0x00040000) /* tx idle. NI */
+#define INTR_RXIDLE (0x00020000) /* rx idle. NI */
+#define INTR_INTR_ACTIVE (0x00010000) /* Interrupt active. NI */
+#define INTR_RX_STATUS_OK (0x00008000) /* rx status valid. NI */
+#define INTR_PCI_TGT_ABT (0x00004000) /* PCI Target abort */
+#define INTR_PCI_MASTER_ABT (0x00002000) /* PCI Master abort */
+#define INTR_PCI_PARITY_ERR (0x00001000) /* PCI adress parity error */
+#define INTR_PCI_DATA_ERR (0x00000800) /* PCI data parity error */
+#define INTR_RX_THR_CROSSED (0x00000400) /* rx copy threshold crossed */
+#define INTR_CNTFULL (0x00000200) /* Counter overflow */
+#define INTR_TXUNDERRUN (0x00000100) /* tx underrun. */
+#define INTR_TXEMPTY (0x00000080) /* tx queue empty */
+#define INTR_TX_CH_COMPLETE (0x00000040) /* tx chain complete */
+#define INTR_TXDONE (0x00000020) /* tx complete (w or w/o err) */
+#define INTR_RXERROR (0x00000010) /* rx error (CRC) */
+#define INTR_RXOVERFLOW (0x00000008) /* rx buffer overflow */
+#define INTR_RX_QUEUE_EMPTY (0x00000004) /* rx queue empty. */
+#define INTR_RXHEADER (0x00000002) /* header copy complete */
+#define INTR_RXDONE (0x00000001) /* Receive copy complete */
+
+#define INTR_CLEARINTR (0x00007FFF)
+#define INTR_VALIDBITS (0x007FFFFF)
+#define INTR_DISABLE (0x00000000)
+#define INTR_CLEARERRS (0x00007F18)
+#define INTR_ABNINTR (INTR_CNTFULL | INTR_TXUNDERRUN | INTR_RXOVERFLOW)
+
+/* General Control (GC_) bits */
+
+#define GC_SOFT_RESET (0x00000001)
+#define GC_INTR_ENABLE (0x00000002)
+#define GC_SOFT_INTR (0x00000004)
+#define GC_POWER_DOWN (0x00000008)
+#define GC_ONE_COPY (0x00000010)
+#define GC_BIG_ENDIAN (0x00000020)
+#define GC_RX_PREEMPT_TX (0x00000040)
+#define GC_TX_PREEMPT_RX (0x00000080)
+
+/*
+ * Receive FIFO Threshold values
+ * Control the level at which the PCI burst state machine
+ * begins to empty the receive FIFO. Possible values: 0-3
+ *
+ * 0 => 32, 1 => 64, 2 => 96 3 => 128 bytes.
+ */
+#define GC_RX_FIFO_THR_32 (0x00000000)
+#define GC_RX_FIFO_THR_64 (0x00000100)
+#define GC_RX_FIFO_THR_96 (0x00000200)
+#define GC_RX_FIFO_THR_128 (0x00000300)
+
+/* Memory Read Control (MRC_) values */
+#define GC_MRC_MEM_READ (0x00000000)
+#define GC_MRC_READ_MULT (0x00000400)
+#define GC_MRC_READ_LINE (0x00000800)
+
+#define GC_SOFTBIT0 (0x00001000)
+#define GC_SOFTBIT1 (0x00002000)
+#define GC_RESET_PHY (0x00004000)
+
+/* Definitions of the Receive Control (RC_) register bits */
+
+#define RC_SAVE_ERRORED_PKT (0x00000001)
+#define RC_SAVE_RUNT_FRAMES (0x00000002)
+#define RC_RCV_BROADCAST (0x00000004)
+#define RC_RCV_MULTICAST (0x00000008)
+#define RC_RCV_INVERSE_PKT (0x00000010)
+#define RC_PROMISCUOUS_MODE (0x00000020)
+#define RC_MONITOR_MODE (0x00000040)
+#define RC_EARLY_RCV_ENABLE (0x00000080)
+
+/* description of the rx descriptors control bits */
+#define RD_FRAGLIST (0x0001) /* Desc points to a fragment list */
+#define RD_LLFORM (0x0002) /* Frag list format */
+#define RD_HDR_CPY (0x0004) /* Desc used for header copy */
+
+/* Definition of the Transmit CONTROL (TC) register bits */
+
+#define TC_EARLY_TX_ENABLE (0x00000001)
+
+/* Loopback Mode (LM_) Select valuesbits */
+#define TC_LM_NORMAL (0x00000000)
+#define TC_LM_INTERNAL (0x00000002)
+#define TC_LM_EXTERNAL (0x00000004)
+#define TC_LM_FULL_DPX (0x00000006)
+
+#define TX_SLOT_TIME (0x00000078)
+
+/* Bytes transferred to chip before transmission starts. */
+#define TX_FIFO_THRESH 128 /* Rounded down to 4 byte units. */
+
+/* description of rx descriptors status bits */
+#define RRING_PKT_INTACT (0x0001)
+#define RRING_ALIGN_ERR (0x0002)
+#define RRING_CRC_ERR (0x0004)
+#define RRING_MISSED_PKT (0x0008)
+#define RRING_MULTICAST (0x0010)
+#define RRING_BROADCAST (0x0020)
+#define RRING_RECEIVER_DISABLE (0x0040)
+#define RRING_STATUS_VALID (0x1000)
+#define RRING_FRAGLIST_ERR (0x2000)
+#define RRING_HDR_COPIED (0x4000)
+#define RRING_OWN (0x8000)
+
+/* error summary */
+#define RRING_ERROR (RRING_ALIGN_ERR|RRING_CRC_ERR)
+
+/* description of tx descriptors status bits */
+#define TRING_PKT_INTACT (0x0001) /* pkt transmitted. */
+#define TRING_PKT_NONDEFER (0x0002) /* pkt xmitted w/o deferring */
+#define TRING_COLL (0x0004) /* pkt xmitted w collisions */
+#define TRING_CARR (0x0008) /* carrier sense lost */
+#define TRING_UNDERRUN (0x0010) /* DMA underrun */
+#define TRING_HB_COLL (0x0020) /* Collision detect Heartbeat */
+#define TRING_WIN_COLL (0x0040) /* out of window collision */
+#define TRING_DEFERRED (0x0080) /* Deferring */
+#define TRING_COLL_COUNT (0x0F00) /* collision counter (mask) */
+#define TRING_COLL_EXCESS (0x1000) /* tx aborted: excessive colls */
+#define TRING_OWN (0x8000) /* desc ownership bit */
+
+/* error summary */
+#define TRING_ABORT (TRING_COLL_EXCESS|TRING_WIN_COLL|TRING_UNDERRUN)
+#define TRING_ERROR (TRING_DEFERRED|TRING_WIN_COLL|TRING_UNDERRUN|TRING_CARR/*|TRING_COLL*/ )
+
+/* description of the tx descriptors control bits */
+#define TD_FRAGLIST (0x0001) /* Desc points to a fragment list */
+#define TD_LLFORM (0x0002) /* Frag list format */
+#define TD_IAF (0x0004) /* Generate Interrupt after tx */
+#define TD_NOCRC (0x0008) /* No CRC generated */
+#define TD_LASTDESC (0x0010) /* Last desc for this frame */
+
+#endif /* _EPIC100_H_ */
diff --git a/gpxe/src/drivers/net/etherfabric.c b/gpxe/src/drivers/net/etherfabric.c
new file mode 100644
index 00000000..8a6b1a17
--- /dev/null
+++ b/gpxe/src/drivers/net/etherfabric.c
@@ -0,0 +1,3436 @@
+/**************************************************************************
+ *
+ * Etherboot driver for Level 5 Etherfabric network cards
+ *
+ * Written by Michael Brown <mbrown@fensystems.co.uk>
+ *
+ * Copyright Fen Systems Ltd. 2005
+ * Copyright Level 5 Networks Inc. 2005
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License (GPL), incorporated herein by
+ * reference. Drivers based on or derived from this code fall under
+ * the GPL and must retain the authorship, copyright and license
+ * notice.
+ *
+ **************************************************************************
+ */
+
+#include "etherboot.h"
+#include "nic.h"
+#include <errno.h>
+#include <gpxe/pci.h>
+#include <gpxe/bitbash.h>
+#include <gpxe/i2c.h>
+#include <gpxe/spi.h>
+#include <gpxe/nvo.h>
+#define dma_addr_t unsigned long
+#include "etherfabric.h"
+
+/**************************************************************************
+ *
+ * Constants and macros
+ *
+ **************************************************************************
+ */
+
+#define EFAB_ASSERT(x) \
+ do { \
+ if ( ! (x) ) { \
+ DBG ( "ASSERT(%s) failed at %s line %d [%s]\n", #x, \
+ __FILE__, __LINE__, __FUNCTION__ ); \
+ } \
+ } while (0)
+
+#define EFAB_TRACE(...) DBG ( __VA_ARGS__ )
+
+#define EFAB_REGDUMP(...)
+
+#define EFAB_LOG(...) printf ( __VA_ARGS__ )
+#define EFAB_ERR(...) printf ( __VA_ARGS__ )
+
+#define FALCON_USE_IO_BAR 1
+
+/*
+ * EtherFabric constants
+ *
+ */
+
+/* PCI Definitions */
+#define EFAB_VENDID_LEVEL5 0x1924
+#define FALCON_P_DEVID 0x0703 /* Temporary PCI ID */
+#define EF1002_DEVID 0xC101
+
+/**************************************************************************
+ *
+ * Data structures
+ *
+ **************************************************************************
+ */
+
+/*
+ * Buffers used for TX, RX and event queue
+ *
+ */
+#define EFAB_BUF_ALIGN 4096
+#define EFAB_DATA_BUF_SIZE 2048
+#define EFAB_RX_BUFS 16
+#define EFAB_RXD_SIZE 512
+#define EFAB_TXD_SIZE 512
+#define EFAB_EVQ_SIZE 512
+struct efab_buffers {
+ uint8_t eventq[4096];
+ uint8_t rxd[4096];
+ uint8_t txd[4096];
+ uint8_t tx_buf[EFAB_DATA_BUF_SIZE];
+ uint8_t rx_buf[EFAB_RX_BUFS][EFAB_DATA_BUF_SIZE];
+ uint8_t padding[EFAB_BUF_ALIGN-1];
+};
+static struct efab_buffers efab_buffers;
+
+/** An RX buffer */
+struct efab_rx_buf {
+ uint8_t *addr;
+ unsigned int len;
+ int id;
+};
+
+/** A TX buffer */
+struct efab_tx_buf {
+ uint8_t *addr;
+ unsigned int len;
+ int id;
+};
+
+/** Etherfabric event type */
+enum efab_event_type {
+ EFAB_EV_NONE = 0,
+ EFAB_EV_TX,
+ EFAB_EV_RX,
+};
+
+/** Etherfabric event */
+struct efab_event {
+ /** Event type */
+ enum efab_event_type type;
+ /** RX buffer ID */
+ int rx_id;
+ /** RX length */
+ unsigned int rx_len;
+ /** Packet should be dropped */
+ int drop;
+};
+
+/*
+ * Etherfabric abstraction layer
+ *
+ */
+struct efab_nic;
+struct efab_operations {
+ void ( * get_membase ) ( struct efab_nic *efab );
+ int ( * reset ) ( struct efab_nic *efab );
+ int ( * init_nic ) ( struct efab_nic *efab );
+ int ( * read_eeprom ) ( struct efab_nic *efab );
+ void ( * build_rx_desc ) ( struct efab_nic *efab,
+ struct efab_rx_buf *rx_buf );
+ void ( * notify_rx_desc ) ( struct efab_nic *efab );
+ void ( * build_tx_desc ) ( struct efab_nic *efab,
+ struct efab_tx_buf *tx_buf );
+ void ( * notify_tx_desc ) ( struct efab_nic *efab );
+ int ( * fetch_event ) ( struct efab_nic *efab,
+ struct efab_event *event );
+ void ( * mask_irq ) ( struct efab_nic *efab, int enabled );
+ void ( * generate_irq ) ( struct efab_nic *efab );
+ void ( * mdio_write ) ( struct efab_nic *efab, int location,
+ int value );
+ int ( * mdio_read ) ( struct efab_nic *efab, int location );
+};
+
+struct efab_mac_operations {
+ void ( * mac_writel ) ( struct efab_nic *efab, efab_dword_t *value,
+ unsigned int mac_reg );
+ void ( * mac_readl ) ( struct efab_nic *efab, efab_dword_t *value,
+ unsigned int mac_reg );
+ int ( * init ) ( struct efab_nic *efab );
+ int ( * reset ) ( struct efab_nic *efab );
+};
+
+/*
+ * Driver private data structure
+ *
+ */
+struct efab_nic {
+
+ /** PCI device */
+ struct pci_device *pci;
+
+ /** Operations table */
+ struct efab_operations *op;
+
+ /** MAC operations table */
+ struct efab_mac_operations *mac_op;
+
+ /** Memory base */
+ void *membase;
+
+ /** I/O base */
+ unsigned int iobase;
+
+ /** Buffers */
+ uint8_t *eventq; /* Falcon only */
+ uint8_t *txd; /* Falcon only */
+ uint8_t *rxd; /* Falcon only */
+ struct efab_tx_buf tx_buf;
+ struct efab_rx_buf rx_bufs[EFAB_RX_BUFS];
+
+ /** Buffer pointers */
+ unsigned int eventq_read_ptr; /* Falcon only */
+ unsigned int tx_write_ptr;
+ unsigned int rx_write_ptr;
+
+ /** Port 0/1 on the NIC */
+ int port;
+
+ /** MAC address */
+ uint8_t mac_addr[ETH_ALEN];
+ /** GMII link options */
+ unsigned int link_options;
+ /** Link status */
+ int link_up;
+
+ /* Nic type fields */
+ int has_flash : 1;
+ int has_eeprom : 1;
+ int is_10g : 1;
+ int is_dual : 1;
+ int is_asic : 1;
+
+ /** INT_REG_KER for Falcon */
+ efab_oword_t int_ker __attribute__ (( aligned ( 16 ) ));
+
+ /** I2C access */
+ struct i2c_bit_basher ef1002_i2c;
+ unsigned long ef1002_i2c_outputs;
+ struct i2c_device ef1002_eeprom;
+
+ /** SPI access */
+ struct spi_bus spi;
+ struct spi_device falcon_flash;
+ struct spi_device falcon_eeprom;
+
+ /** Non-volatile options */
+ struct nvo_block nvo;
+};
+
+/**************************************************************************
+ *
+ * GMII routines
+ *
+ **************************************************************************
+ */
+
+/* GMII registers */
+#define MII_BMSR 0x01 /* Basic mode status register */
+#define MII_ADVERTISE 0x04 /* Advertisement control register */
+#define MII_LPA 0x05 /* Link partner ability register*/
+#define GMII_GTCR 0x09 /* 1000BASE-T control register */
+#define GMII_GTSR 0x0a /* 1000BASE-T status register */
+#define GMII_PSSR 0x11 /* PHY-specific status register */
+
+/* Basic mode status register. */
+#define BMSR_LSTATUS 0x0004 /* Link status */
+
+/* Link partner ability register. */
+#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */
+#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */
+#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */
+#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */
+#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */
+#define LPA_PAUSE 0x0400 /* Bit 10 - MAC pause */
+
+/* Pseudo extensions to the link partner ability register */
+#define LPA_1000FULL 0x00020000
+#define LPA_1000HALF 0x00010000
+#define LPA_10000FULL 0x00040000
+#define LPA_10000HALF 0x00080000
+
+#define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4)
+#define LPA_1000 ( LPA_1000FULL | LPA_1000HALF )
+#define LPA_10000 ( LPA_10000FULL | LPA_10000HALF )
+#define LPA_DUPLEX ( LPA_10FULL | LPA_100FULL | LPA_1000FULL )
+
+/* Mask of bits not associated with speed or duplexity. */
+#define LPA_OTHER ~( LPA_10FULL | LPA_10HALF | LPA_100FULL | \
+ LPA_100HALF | LPA_1000FULL | LPA_1000HALF )
+
+/* PHY-specific status register */
+#define PSSR_LSTATUS 0x0400 /* Bit 10 - link status */
+
+/**
+ * Retrieve GMII autonegotiation advertised abilities
+ *
+ */
+static unsigned int gmii_autoneg_advertised ( struct efab_nic *efab ) {
+ unsigned int mii_advertise;
+ unsigned int gmii_advertise;
+
+ /* Extended bits are in bits 8 and 9 of GMII_GTCR */
+ mii_advertise = efab->op->mdio_read ( efab, MII_ADVERTISE );
+ gmii_advertise = ( ( efab->op->mdio_read ( efab, GMII_GTCR ) >> 8 )
+ & 0x03 );
+ return ( ( gmii_advertise << 16 ) | mii_advertise );
+}
+
+/**
+ * Retrieve GMII autonegotiation link partner abilities
+ *
+ */
+static unsigned int gmii_autoneg_lpa ( struct efab_nic *efab ) {
+ unsigned int mii_lpa;
+ unsigned int gmii_lpa;
+
+ /* Extended bits are in bits 10 and 11 of GMII_GTSR */
+ mii_lpa = efab->op->mdio_read ( efab, MII_LPA );
+ gmii_lpa = ( efab->op->mdio_read ( efab, GMII_GTSR ) >> 10 ) & 0x03;
+ return ( ( gmii_lpa << 16 ) | mii_lpa );
+}
+
+/**
+ * Calculate GMII autonegotiated link technology
+ *
+ */
+static unsigned int gmii_nway_result ( unsigned int negotiated ) {
+ unsigned int other_bits;
+
+ /* Mask out the speed and duplexity bits */
+ other_bits = negotiated & LPA_OTHER;
+
+ if ( negotiated & LPA_1000FULL )
+ return ( other_bits | LPA_1000FULL );
+ else if ( negotiated & LPA_1000HALF )
+ return ( other_bits | LPA_1000HALF );
+ else if ( negotiated & LPA_100FULL )
+ return ( other_bits | LPA_100FULL );
+ else if ( negotiated & LPA_100BASE4 )
+ return ( other_bits | LPA_100BASE4 );
+ else if ( negotiated & LPA_100HALF )
+ return ( other_bits | LPA_100HALF );
+ else if ( negotiated & LPA_10FULL )
+ return ( other_bits | LPA_10FULL );
+ else return ( other_bits | LPA_10HALF );
+}
+
+/**
+ * Check GMII PHY link status
+ *
+ */
+static int gmii_link_ok ( struct efab_nic *efab ) {
+ int status;
+ int phy_status;
+
+ /* BMSR is latching - it returns "link down" if the link has
+ * been down at any point since the last read. To get a
+ * real-time status, we therefore read the register twice and
+ * use the result of the second read.
+ */
+ efab->op->mdio_read ( efab, MII_BMSR );
+ status = efab->op->mdio_read ( efab, MII_BMSR );
+
+ /* Read the PHY-specific Status Register. This is
+ * non-latching, so we need do only a single read.
+ */
+ phy_status = efab->op->mdio_read ( efab, GMII_PSSR );
+
+ return ( ( status & BMSR_LSTATUS ) && ( phy_status & PSSR_LSTATUS ) );
+}
+
+/**************************************************************************
+ *
+ * Alaska PHY
+ *
+ **************************************************************************
+ */
+
+/**
+ * Initialise Alaska PHY
+ *
+ */
+static void alaska_init ( struct efab_nic *efab ) {
+ unsigned int advertised, lpa;
+
+ /* Read link up status */
+ efab->link_up = gmii_link_ok ( efab );
+
+ if ( ! efab->link_up )
+ return;
+
+ /* Determine link options from PHY. */
+ advertised = gmii_autoneg_advertised ( efab );
+ lpa = gmii_autoneg_lpa ( efab );
+ efab->link_options = gmii_nway_result ( advertised & lpa );
+
+ /* print out the link speed */
+ EFAB_LOG ( "%dMbps %s-duplex (%04x,%04x)\n",
+ ( efab->link_options & LPA_10000 ? 1000 :
+ ( efab->link_options & LPA_1000 ? 1000 :
+ ( efab->link_options & LPA_100 ? 100 : 10 ) ) ),
+ ( efab->link_options & LPA_DUPLEX ? "full" : "half" ),
+ advertised, lpa );
+}
+
+
+/**************************************************************************
+ *
+ * Mentor MAC
+ *
+ **************************************************************************
+ */
+
+/* GMAC configuration register 1 */
+#define GM_CFG1_REG_MAC 0x00
+#define GM_SW_RST_LBN 31
+#define GM_SW_RST_WIDTH 1
+#define GM_RX_FC_EN_LBN 5
+#define GM_RX_FC_EN_WIDTH 1
+#define GM_TX_FC_EN_LBN 4
+#define GM_TX_FC_EN_WIDTH 1
+#define GM_RX_EN_LBN 2
+#define GM_RX_EN_WIDTH 1
+#define GM_TX_EN_LBN 0
+#define GM_TX_EN_WIDTH 1
+
+/* GMAC configuration register 2 */
+#define GM_CFG2_REG_MAC 0x01
+#define GM_PAMBL_LEN_LBN 12
+#define GM_PAMBL_LEN_WIDTH 4
+#define GM_IF_MODE_LBN 8
+#define GM_IF_MODE_WIDTH 2
+#define GM_PAD_CRC_EN_LBN 2
+#define GM_PAD_CRC_EN_WIDTH 1
+#define GM_FD_LBN 0
+#define GM_FD_WIDTH 1
+
+/* GMAC maximum frame length register */
+#define GM_MAX_FLEN_REG_MAC 0x04
+#define GM_MAX_FLEN_LBN 0
+#define GM_MAX_FLEN_WIDTH 16
+
+/* GMAC MII management configuration register */
+#define GM_MII_MGMT_CFG_REG_MAC 0x08
+#define GM_MGMT_CLK_SEL_LBN 0
+#define GM_MGMT_CLK_SEL_WIDTH 3
+
+/* GMAC MII management command register */
+#define GM_MII_MGMT_CMD_REG_MAC 0x09
+#define GM_MGMT_SCAN_CYC_LBN 1
+#define GM_MGMT_SCAN_CYC_WIDTH 1
+#define GM_MGMT_RD_CYC_LBN 0
+#define GM_MGMT_RD_CYC_WIDTH 1
+
+/* GMAC MII management address register */
+#define GM_MII_MGMT_ADR_REG_MAC 0x0a
+#define GM_MGMT_PHY_ADDR_LBN 8
+#define GM_MGMT_PHY_ADDR_WIDTH 5
+#define GM_MGMT_REG_ADDR_LBN 0
+#define GM_MGMT_REG_ADDR_WIDTH 5
+
+/* GMAC MII management control register */
+#define GM_MII_MGMT_CTL_REG_MAC 0x0b
+#define GM_MGMT_CTL_LBN 0
+#define GM_MGMT_CTL_WIDTH 16
+
+/* GMAC MII management status register */
+#define GM_MII_MGMT_STAT_REG_MAC 0x0c
+#define GM_MGMT_STAT_LBN 0
+#define GM_MGMT_STAT_WIDTH 16
+
+/* GMAC MII management indicators register */
+#define GM_MII_MGMT_IND_REG_MAC 0x0d
+#define GM_MGMT_BUSY_LBN 0
+#define GM_MGMT_BUSY_WIDTH 1
+
+/* GMAC station address register 1 */
+#define GM_ADR1_REG_MAC 0x10
+#define GM_HWADDR_5_LBN 24
+#define GM_HWADDR_5_WIDTH 8
+#define GM_HWADDR_4_LBN 16
+#define GM_HWADDR_4_WIDTH 8
+#define GM_HWADDR_3_LBN 8
+#define GM_HWADDR_3_WIDTH 8
+#define GM_HWADDR_2_LBN 0
+#define GM_HWADDR_2_WIDTH 8
+
+/* GMAC station address register 2 */
+#define GM_ADR2_REG_MAC 0x11
+#define GM_HWADDR_1_LBN 24
+#define GM_HWADDR_1_WIDTH 8
+#define GM_HWADDR_0_LBN 16
+#define GM_HWADDR_0_WIDTH 8
+
+/* GMAC FIFO configuration register 0 */
+#define GMF_CFG0_REG_MAC 0x12
+#define GMF_FTFENREQ_LBN 12
+#define GMF_FTFENREQ_WIDTH 1
+#define GMF_STFENREQ_LBN 11
+#define GMF_STFENREQ_WIDTH 1
+#define GMF_FRFENREQ_LBN 10
+#define GMF_FRFENREQ_WIDTH 1
+#define GMF_SRFENREQ_LBN 9
+#define GMF_SRFENREQ_WIDTH 1
+#define GMF_WTMENREQ_LBN 8
+#define GMF_WTMENREQ_WIDTH 1
+
+/* GMAC FIFO configuration register 1 */
+#define GMF_CFG1_REG_MAC 0x13
+#define GMF_CFGFRTH_LBN 16
+#define GMF_CFGFRTH_WIDTH 5
+#define GMF_CFGXOFFRTX_LBN 0
+#define GMF_CFGXOFFRTX_WIDTH 16
+
+/* GMAC FIFO configuration register 2 */
+#define GMF_CFG2_REG_MAC 0x14
+#define GMF_CFGHWM_LBN 16
+#define GMF_CFGHWM_WIDTH 6
+#define GMF_CFGLWM_LBN 0
+#define GMF_CFGLWM_WIDTH 6
+
+/* GMAC FIFO configuration register 3 */
+#define GMF_CFG3_REG_MAC 0x15
+#define GMF_CFGHWMFT_LBN 16
+#define GMF_CFGHWMFT_WIDTH 6
+#define GMF_CFGFTTH_LBN 0
+#define GMF_CFGFTTH_WIDTH 6
+
+/* GMAC FIFO configuration register 4 */
+#define GMF_CFG4_REG_MAC 0x16
+#define GMF_HSTFLTRFRM_PAUSE_LBN 12
+#define GMF_HSTFLTRFRM_PAUSE_WIDTH 12
+
+/* GMAC FIFO configuration register 5 */
+#define GMF_CFG5_REG_MAC 0x17
+#define GMF_CFGHDPLX_LBN 22
+#define GMF_CFGHDPLX_WIDTH 1
+#define GMF_CFGBYTMODE_LBN 19
+#define GMF_CFGBYTMODE_WIDTH 1
+#define GMF_HSTDRPLT64_LBN 18
+#define GMF_HSTDRPLT64_WIDTH 1
+#define GMF_HSTFLTRFRMDC_PAUSE_LBN 12
+#define GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1
+
+struct efab_mentormac_parameters {
+ int gmf_cfgfrth;
+ int gmf_cfgftth;
+ int gmf_cfghwmft;
+ int gmf_cfghwm;
+ int gmf_cfglwm;
+};
+
+/**
+ * Reset Mentor MAC
+ *
+ */
+static void mentormac_reset ( struct efab_nic *efab ) {
+ efab_dword_t reg;
+ int save_port;
+
+ /* Take into reset */
+ EFAB_POPULATE_DWORD_1 ( reg, GM_SW_RST, 1 );
+ efab->mac_op->mac_writel ( efab, &reg, GM_CFG1_REG_MAC );
+ udelay ( 1000 );
+
+ /* Take out of reset */
+ EFAB_POPULATE_DWORD_1 ( reg, GM_SW_RST, 0 );
+ efab->mac_op->mac_writel ( efab, &reg, GM_CFG1_REG_MAC );
+ udelay ( 1000 );
+
+ /* Mentor MAC connects both PHYs to MAC 0 */
+ save_port = efab->port;
+ efab->port = 0;
+ /* Configure GMII interface so PHY is accessible. Note that
+ * GMII interface is connected only to port 0, and that on
+ * Falcon this is a no-op.
+ */
+ EFAB_POPULATE_DWORD_1 ( reg, GM_MGMT_CLK_SEL, 0x4 );
+ efab->mac_op->mac_writel ( efab, &reg, GM_MII_MGMT_CFG_REG_MAC );
+ udelay ( 10 );
+ efab->port = save_port;
+}
+
+/**
+ * Initialise Mentor MAC
+ *
+ */
+static void mentormac_init ( struct efab_nic *efab,
+ struct efab_mentormac_parameters *params ) {
+ int pause, if_mode, full_duplex, bytemode, half_duplex;
+ efab_dword_t reg;
+
+ /* Configuration register 1 */
+ pause = ( efab->link_options & LPA_PAUSE ) ? 1 : 0;
+ if ( ! ( efab->link_options & LPA_DUPLEX ) ) {
+ /* Half-duplex operation requires TX flow control */
+ pause = 1;
+ }
+ EFAB_POPULATE_DWORD_4 ( reg,
+ GM_TX_EN, 1,
+ GM_TX_FC_EN, pause,
+ GM_RX_EN, 1,
+ GM_RX_FC_EN, 1 );
+ efab->mac_op->mac_writel ( efab, &reg, GM_CFG1_REG_MAC );
+ udelay ( 10 );
+
+ /* Configuration register 2 */
+ if_mode = ( efab->link_options & LPA_1000 ) ? 2 : 1;
+ full_duplex = ( efab->link_options & LPA_DUPLEX ) ? 1 : 0;
+ EFAB_POPULATE_DWORD_4 ( reg,
+ GM_IF_MODE, if_mode,
+ GM_PAD_CRC_EN, 1,
+ GM_FD, full_duplex,
+ GM_PAMBL_LEN, 0x7 /* ? */ );
+ efab->mac_op->mac_writel ( efab, &reg, GM_CFG2_REG_MAC );
+ udelay ( 10 );
+
+ /* Max frame len register */
+ EFAB_POPULATE_DWORD_1 ( reg, GM_MAX_FLEN, ETH_FRAME_LEN + 4 /* FCS */);
+ efab->mac_op->mac_writel ( efab, &reg, GM_MAX_FLEN_REG_MAC );
+ udelay ( 10 );
+
+ /* FIFO configuration register 0 */
+ EFAB_POPULATE_DWORD_5 ( reg,
+ GMF_FTFENREQ, 1,
+ GMF_STFENREQ, 1,
+ GMF_FRFENREQ, 1,
+ GMF_SRFENREQ, 1,
+ GMF_WTMENREQ, 1 );
+ efab->mac_op->mac_writel ( efab, &reg, GMF_CFG0_REG_MAC );
+ udelay ( 10 );
+
+ /* FIFO configuration register 1 */
+ EFAB_POPULATE_DWORD_2 ( reg,
+ GMF_CFGFRTH, params->gmf_cfgfrth,
+ GMF_CFGXOFFRTX, 0xffff );
+ efab->mac_op->mac_writel ( efab, &reg, GMF_CFG1_REG_MAC );
+ udelay ( 10 );
+
+ /* FIFO configuration register 2 */
+ EFAB_POPULATE_DWORD_2 ( reg,
+ GMF_CFGHWM, params->gmf_cfghwm,
+ GMF_CFGLWM, params->gmf_cfglwm );
+ efab->mac_op->mac_writel ( efab, &reg, GMF_CFG2_REG_MAC );
+ udelay ( 10 );
+
+ /* FIFO configuration register 3 */
+ EFAB_POPULATE_DWORD_2 ( reg,
+ GMF_CFGHWMFT, params->gmf_cfghwmft,
+ GMF_CFGFTTH, params->gmf_cfgftth );
+ efab->mac_op->mac_writel ( efab, &reg, GMF_CFG3_REG_MAC );
+ udelay ( 10 );
+
+ /* FIFO configuration register 4 */
+ EFAB_POPULATE_DWORD_1 ( reg, GMF_HSTFLTRFRM_PAUSE, 1 );
+ efab->mac_op->mac_writel ( efab, &reg, GMF_CFG4_REG_MAC );
+ udelay ( 10 );
+
+ /* FIFO configuration register 5 */
+ bytemode = ( efab->link_options & LPA_1000 ) ? 1 : 0;
+ half_duplex = ( efab->link_options & LPA_DUPLEX ) ? 0 : 1;
+ efab->mac_op->mac_readl ( efab, &reg, GMF_CFG5_REG_MAC );
+ EFAB_SET_DWORD_FIELD ( reg, GMF_CFGBYTMODE, bytemode );
+ EFAB_SET_DWORD_FIELD ( reg, GMF_CFGHDPLX, half_duplex );
+ EFAB_SET_DWORD_FIELD ( reg, GMF_HSTDRPLT64, half_duplex );
+ EFAB_SET_DWORD_FIELD ( reg, GMF_HSTFLTRFRMDC_PAUSE, 0 );
+ efab->mac_op->mac_writel ( efab, &reg, GMF_CFG5_REG_MAC );
+ udelay ( 10 );
+
+ /* MAC address */
+ EFAB_POPULATE_DWORD_4 ( reg,
+ GM_HWADDR_5, efab->mac_addr[5],
+ GM_HWADDR_4, efab->mac_addr[4],
+ GM_HWADDR_3, efab->mac_addr[3],
+ GM_HWADDR_2, efab->mac_addr[2] );
+ efab->mac_op->mac_writel ( efab, &reg, GM_ADR1_REG_MAC );
+ udelay ( 10 );
+ EFAB_POPULATE_DWORD_2 ( reg,
+ GM_HWADDR_1, efab->mac_addr[1],
+ GM_HWADDR_0, efab->mac_addr[0] );
+ efab->mac_op->mac_writel ( efab, &reg, GM_ADR2_REG_MAC );
+ udelay ( 10 );
+}
+
+/**
+ * Wait for GMII access to complete
+ *
+ */
+static int mentormac_gmii_wait ( struct efab_nic *efab ) {
+ int count;
+ efab_dword_t indicator;
+
+ for ( count = 0 ; count < 1000 ; count++ ) {
+ udelay ( 10 );
+ efab->mac_op->mac_readl ( efab, &indicator,
+ GM_MII_MGMT_IND_REG_MAC );
+ if ( EFAB_DWORD_FIELD ( indicator, GM_MGMT_BUSY ) == 0 )
+ return 1;
+ }
+ EFAB_ERR ( "Timed out waiting for GMII\n" );
+ return 0;
+}
+
+/**
+ * Write a GMII register
+ *
+ */
+static void mentormac_mdio_write ( struct efab_nic *efab, int phy_id,
+ int location, int value ) {
+ efab_dword_t reg;
+ int save_port;
+
+ EFAB_TRACE ( "Writing GMII %d register %02x with %04x\n", phy_id,
+ location, value );
+
+ /* Mentor MAC connects both PHYs to MAC 0 */
+ save_port = efab->port;
+ efab->port = 0;
+
+ /* Check MII not currently being accessed */
+ if ( ! mentormac_gmii_wait ( efab ) )
+ goto out;
+
+ /* Write the address register */
+ EFAB_POPULATE_DWORD_2 ( reg,
+ GM_MGMT_PHY_ADDR, phy_id,
+ GM_MGMT_REG_ADDR, location );
+ efab->mac_op->mac_writel ( efab, &reg, GM_MII_MGMT_ADR_REG_MAC );
+ udelay ( 10 );
+
+ /* Write data */
+ EFAB_POPULATE_DWORD_1 ( reg, GM_MGMT_CTL, value );
+ efab->mac_op->mac_writel ( efab, &reg, GM_MII_MGMT_CTL_REG_MAC );
+
+ /* Wait for data to be written */
+ mentormac_gmii_wait ( efab );
+
+ out:
+ /* Restore efab->port */
+ efab->port = save_port;
+}
+
+/**
+ * Read a GMII register
+ *
+ */
+static int mentormac_mdio_read ( struct efab_nic *efab, int phy_id,
+ int location ) {
+ efab_dword_t reg;
+ int value = 0xffff;
+ int save_port;
+
+ /* Mentor MAC connects both PHYs to MAC 0 */
+ save_port = efab->port;
+ efab->port = 0;
+
+ /* Check MII not currently being accessed */
+ if ( ! mentormac_gmii_wait ( efab ) )
+ goto out;
+
+ /* Write the address register */
+ EFAB_POPULATE_DWORD_2 ( reg,
+ GM_MGMT_PHY_ADDR, phy_id,
+ GM_MGMT_REG_ADDR, location );
+ efab->mac_op->mac_writel ( efab, &reg, GM_MII_MGMT_ADR_REG_MAC );
+ udelay ( 10 );
+
+ /* Request data to be read */
+ EFAB_POPULATE_DWORD_1 ( reg, GM_MGMT_RD_CYC, 1 );
+ efab->mac_op->mac_writel ( efab, &reg, GM_MII_MGMT_CMD_REG_MAC );
+
+ /* Wait for data to be become available */
+ if ( mentormac_gmii_wait ( efab ) ) {
+ /* Read data */
+ efab->mac_op->mac_readl ( efab, &reg, GM_MII_MGMT_STAT_REG_MAC );
+ value = EFAB_DWORD_FIELD ( reg, GM_MGMT_STAT );
+ EFAB_TRACE ( "Read from GMII %d register %02x, got %04x\n",
+ phy_id, location, value );
+ }
+
+ /* Signal completion */
+ EFAB_ZERO_DWORD ( reg );
+ efab->mac_op->mac_writel ( efab, &reg, GM_MII_MGMT_CMD_REG_MAC );
+ udelay ( 10 );
+
+ out:
+ /* Restore efab->port */
+ efab->port = save_port;
+
+ return value;
+}
+
+/**************************************************************************
+ *
+ * EF1002 routines
+ *
+ **************************************************************************
+ */
+
+/** Control and General Status */
+#define EF1_CTR_GEN_STATUS0_REG 0x0
+#define EF1_MASTER_EVENTS_LBN 12
+#define EF1_MASTER_EVENTS_WIDTH 1
+#define EF1_TX_ENGINE_EN_LBN 19
+#define EF1_TX_ENGINE_EN_WIDTH 1
+#define EF1_RX_ENGINE_EN_LBN 18
+#define EF1_RX_ENGINE_EN_WIDTH 1
+#define EF1_TURBO2_LBN 17
+#define EF1_TURBO2_WIDTH 1
+#define EF1_TURBO1_LBN 16
+#define EF1_TURBO1_WIDTH 1
+#define EF1_TURBO3_LBN 14
+#define EF1_TURBO3_WIDTH 1
+#define EF1_LB_RESET_LBN 3
+#define EF1_LB_RESET_WIDTH 1
+#define EF1_MAC_RESET_LBN 2
+#define EF1_MAC_RESET_WIDTH 1
+#define EF1_CAM_ENABLE_LBN 1
+#define EF1_CAM_ENABLE_WIDTH 1
+
+/** IRQ sources */
+#define EF1_IRQ_SRC_REG 0x0008
+
+/** IRQ mask */
+#define EF1_IRQ_MASK_REG 0x000c
+#define EF1_IRQ_PHY1_LBN 11
+#define EF1_IRQ_PHY1_WIDTH 1
+#define EF1_IRQ_PHY0_LBN 10
+#define EF1_IRQ_PHY0_WIDTH 1
+#define EF1_IRQ_SERR_LBN 7
+#define EF1_IRQ_SERR_WIDTH 1
+#define EF1_IRQ_EVQ_LBN 3
+#define EF1_IRQ_EVQ_WIDTH 1
+
+/** Event generation */
+#define EF1_EVT3_REG 0x38
+
+/** EEPROMaccess */
+#define EF1_EEPROM_REG 0x40
+#define EF1_EEPROM_SDA_LBN 31
+#define EF1_EEPROM_SDA_WIDTH 1
+#define EF1_EEPROM_SCL_LBN 30
+#define EF1_EEPROM_SCL_WIDTH 1
+#define EF1_JTAG_DISCONNECT_LBN 17
+#define EF1_JTAG_DISCONNECT_WIDTH 1
+#define EF1_EEPROM_LBN 0
+#define EF1_EEPROM_WIDTH 32
+
+/** Control register 2 */
+#define EF1_CTL2_REG 0x4c
+#define EF1_PLL_TRAP_LBN 31
+#define EF1_PLL_TRAP_WIDTH 1
+#define EF1_MEM_MAP_4MB_LBN 11
+#define EF1_MEM_MAP_4MB_WIDTH 1
+#define EF1_EV_INTR_CLR_WRITE_LBN 6
+#define EF1_EV_INTR_CLR_WRITE_WIDTH 1
+#define EF1_BURST_MERGE_LBN 5
+#define EF1_BURST_MERGE_WIDTH 1
+#define EF1_CLEAR_NULL_PAD_LBN 4
+#define EF1_CLEAR_NULL_PAD_WIDTH 1
+#define EF1_SW_RESET_LBN 2
+#define EF1_SW_RESET_WIDTH 1
+#define EF1_INTR_AFTER_EVENT_LBN 1
+#define EF1_INTR_AFTER_EVENT_WIDTH 1
+
+/** Event FIFO */
+#define EF1_EVENT_FIFO_REG 0x50
+
+/** Event FIFO count */
+#define EF1_EVENT_FIFO_COUNT_REG 0x5c
+#define EF1_EV_COUNT_LBN 0
+#define EF1_EV_COUNT_WIDTH 16
+
+/** TX DMA control and status */
+#define EF1_DMA_TX_CSR_REG 0x80
+#define EF1_DMA_TX_CSR_CHAIN_EN_LBN 8
+#define EF1_DMA_TX_CSR_CHAIN_EN_WIDTH 1
+#define EF1_DMA_TX_CSR_ENABLE_LBN 4
+#define EF1_DMA_TX_CSR_ENABLE_WIDTH 1
+#define EF1_DMA_TX_CSR_INT_EN_LBN 0
+#define EF1_DMA_TX_CSR_INT_EN_WIDTH 1
+
+/** RX DMA control and status */
+#define EF1_DMA_RX_CSR_REG 0xa0
+#define EF1_DMA_RX_ABOVE_1GB_EN_LBN 6
+#define EF1_DMA_RX_ABOVE_1GB_EN_WIDTH 1
+#define EF1_DMA_RX_BELOW_1MB_EN_LBN 5
+#define EF1_DMA_RX_BELOW_1MB_EN_WIDTH 1
+#define EF1_DMA_RX_CSR_ENABLE_LBN 0
+#define EF1_DMA_RX_CSR_ENABLE_WIDTH 1
+
+/** Level 5 watermark register (in MAC space) */
+#define EF1_GMF_L5WM_REG_MAC 0x20
+#define EF1_L5WM_LBN 0
+#define EF1_L5WM_WIDTH 32
+
+/** MAC clock */
+#define EF1_GM_MAC_CLK_REG 0x112000
+#define EF1_GM_PORT0_MAC_CLK_LBN 0
+#define EF1_GM_PORT0_MAC_CLK_WIDTH 1
+#define EF1_GM_PORT1_MAC_CLK_LBN 1
+#define EF1_GM_PORT1_MAC_CLK_WIDTH 1
+
+/** TX descriptor FIFO */
+#define EF1_TX_DESC_FIFO 0x141000
+#define EF1_TX_KER_EVQ_LBN 80
+#define EF1_TX_KER_EVQ_WIDTH 12
+#define EF1_TX_KER_IDX_LBN 64
+#define EF1_TX_KER_IDX_WIDTH 16
+#define EF1_TX_KER_MODE_LBN 63
+#define EF1_TX_KER_MODE_WIDTH 1
+#define EF1_TX_KER_PORT_LBN 60
+#define EF1_TX_KER_PORT_WIDTH 1
+#define EF1_TX_KER_CONT_LBN 56
+#define EF1_TX_KER_CONT_WIDTH 1
+#define EF1_TX_KER_BYTE_CNT_LBN 32
+#define EF1_TX_KER_BYTE_CNT_WIDTH 24
+#define EF1_TX_KER_BUF_ADR_LBN 0
+#define EF1_TX_KER_BUF_ADR_WIDTH 32
+
+/** TX descriptor FIFO flush */
+#define EF1_TX_DESC_FIFO_FLUSH 0x141ffc
+
+/** RX descriptor FIFO */
+#define EF1_RX_DESC_FIFO 0x145000
+#define EF1_RX_KER_EVQ_LBN 48
+#define EF1_RX_KER_EVQ_WIDTH 12
+#define EF1_RX_KER_IDX_LBN 32
+#define EF1_RX_KER_IDX_WIDTH 16
+#define EF1_RX_KER_BUF_ADR_LBN 0
+#define EF1_RX_KER_BUF_ADR_WIDTH 32
+
+/** RX descriptor FIFO flush */
+#define EF1_RX_DESC_FIFO_FLUSH 0x145ffc
+
+/** CAM */
+#define EF1_CAM_BASE 0x1c0000
+#define EF1_CAM_WTF_DOES_THIS_DO_LBN 0
+#define EF1_CAM_WTF_DOES_THIS_DO_WIDTH 32
+
+/** Event queue pointers */
+#define EF1_EVQ_PTR_BASE 0x260000
+#define EF1_EVQ_SIZE_LBN 29
+#define EF1_EVQ_SIZE_WIDTH 2
+#define EF1_EVQ_SIZE_4K 3
+#define EF1_EVQ_SIZE_2K 2
+#define EF1_EVQ_SIZE_1K 1
+#define EF1_EVQ_SIZE_512 0
+#define EF1_EVQ_BUF_BASE_ID_LBN 0
+#define EF1_EVQ_BUF_BASE_ID_WIDTH 29
+
+/* MAC registers */
+#define EF1002_MAC_REGBANK 0x110000
+#define EF1002_MAC_REGBANK_SIZE 0x1000
+#define EF1002_MAC_REG_SIZE 0x08
+
+/** Offset of a MAC register within EF1002 */
+#define EF1002_MAC_REG( efab, mac_reg ) \
+ ( EF1002_MAC_REGBANK + \
+ ( (efab)->port * EF1002_MAC_REGBANK_SIZE ) + \
+ ( (mac_reg) * EF1002_MAC_REG_SIZE ) )
+
+/* Event queue entries */
+#define EF1_EV_CODE_LBN 20
+#define EF1_EV_CODE_WIDTH 8
+#define EF1_RX_EV_DECODE 0x01
+#define EF1_TX_EV_DECODE 0x02
+#define EF1_TIMER_EV_DECODE 0x0b
+#define EF1_DRV_GEN_EV_DECODE 0x0f
+
+/* Receive events */
+#define EF1_RX_EV_LEN_LBN 48
+#define EF1_RX_EV_LEN_WIDTH 16
+#define EF1_RX_EV_PORT_LBN 17
+#define EF1_RX_EV_PORT_WIDTH 3
+#define EF1_RX_EV_OK_LBN 16
+#define EF1_RX_EV_OK_WIDTH 1
+#define EF1_RX_EV_IDX_LBN 0
+#define EF1_RX_EV_IDX_WIDTH 16
+
+/* Transmit events */
+#define EF1_TX_EV_PORT_LBN 17
+#define EF1_TX_EV_PORT_WIDTH 3
+#define EF1_TX_EV_OK_LBN 16
+#define EF1_TX_EV_OK_WIDTH 1
+#define EF1_TX_EV_IDX_LBN 0
+#define EF1_TX_EV_IDX_WIDTH 16
+
+/* forward decleration */
+static struct efab_mac_operations ef1002_mac_operations;
+
+/* I2C ID of the EEPROM */
+#define EF1_EEPROM_I2C_ID 0x50
+
+/* Offset of MAC address within EEPROM */
+#define EF1_EEPROM_HWADDR_OFFSET 0x0
+
+/**
+ * Write dword to EF1002 register
+ *
+ */
+static inline void ef1002_writel ( struct efab_nic *efab, efab_dword_t *value,
+ unsigned int reg ) {
+ EFAB_REGDUMP ( "Writing register %x with " EFAB_DWORD_FMT "\n",
+ reg, EFAB_DWORD_VAL ( *value ) );
+ writel ( value->u32[0], efab->membase + reg );
+}
+
+/**
+ * Read dword from an EF1002 register
+ *
+ */
+static inline void ef1002_readl ( struct efab_nic *efab, efab_dword_t *value,
+ unsigned int reg ) {
+ value->u32[0] = readl ( efab->membase + reg );
+ EFAB_REGDUMP ( "Read from register %x, got " EFAB_DWORD_FMT "\n",
+ reg, EFAB_DWORD_VAL ( *value ) );
+}
+
+/**
+ * Read dword from an EF1002 register, silently
+ *
+ */
+static inline void ef1002_readl_silent ( struct efab_nic *efab,
+ efab_dword_t *value,
+ unsigned int reg ) {
+ value->u32[0] = readl ( efab->membase + reg );
+}
+
+/**
+ * Get memory base
+ *
+ */
+static void ef1002_get_membase ( struct efab_nic *efab ) {
+ unsigned long membase_phys;
+
+ membase_phys = pci_bar_start ( efab->pci, PCI_BASE_ADDRESS_0 );
+ efab->membase = ioremap ( membase_phys, 0x800000 );
+}
+
+/** PCI registers to backup/restore over a device reset */
+static const unsigned int efab_pci_reg_addr[] = {
+ PCI_COMMAND, 0x0c /* PCI_CACHE_LINE_SIZE */,
+ PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
+ PCI_BASE_ADDRESS_3, PCI_ROM_ADDRESS, PCI_INTERRUPT_LINE,
+};
+/** Number of registers in efab_pci_reg_addr */
+#define EFAB_NUM_PCI_REG \
+ ( sizeof ( efab_pci_reg_addr ) / sizeof ( efab_pci_reg_addr[0] ) )
+/** PCI configuration space backup */
+struct efab_pci_reg {
+ uint32_t reg[EFAB_NUM_PCI_REG];
+};
+
+/*
+ * I2C interface and EEPROM
+ *
+ */
+
+static unsigned long ef1002_i2c_bits[] = {
+ [I2C_BIT_SCL] = ( 1 << 30 ),
+ [I2C_BIT_SDA] = ( 1 << 31 ),
+};
+
+static void ef1002_i2c_write_bit ( struct bit_basher *basher,
+ unsigned int bit_id, unsigned long data ) {
+ struct efab_nic *efab = container_of ( basher, struct efab_nic,
+ ef1002_i2c.basher );
+ unsigned long mask;
+ efab_dword_t reg;
+
+ mask = ef1002_i2c_bits[bit_id];
+ efab->ef1002_i2c_outputs &= ~mask;
+ efab->ef1002_i2c_outputs |= ( data & mask );
+ EFAB_POPULATE_DWORD_1 ( reg, EF1_EEPROM, efab->ef1002_i2c_outputs );
+ ef1002_writel ( efab, &reg, EF1_EEPROM_REG );
+}
+
+static int ef1002_i2c_read_bit ( struct bit_basher *basher,
+ unsigned int bit_id ) {
+ struct efab_nic *efab = container_of ( basher, struct efab_nic,
+ ef1002_i2c.basher );
+ unsigned long mask;
+ efab_dword_t reg;
+
+ mask = ef1002_i2c_bits[bit_id];
+ ef1002_readl ( efab, &reg, EF1_EEPROM_REG );
+ return ( EFAB_DWORD_FIELD ( reg, EF1_EEPROM ) & mask );
+}
+
+static struct bit_basher_operations ef1002_basher_ops = {
+ .read = ef1002_i2c_read_bit,
+ .write = ef1002_i2c_write_bit,
+};
+
+static void ef1002_init_eeprom ( struct efab_nic *efab ) {
+ efab->ef1002_i2c.basher.op = &ef1002_basher_ops;
+ init_i2c_bit_basher ( &efab->ef1002_i2c );
+ efab->ef1002_eeprom.address = EF1_EEPROM_I2C_ID;
+}
+
+/**
+ * Reset device
+ *
+ */
+static int ef1002_reset ( struct efab_nic *efab ) {
+ struct efab_pci_reg pci_reg;
+ struct pci_device *pci_dev = efab->pci;
+ efab_dword_t reg;
+ unsigned int i;
+ uint32_t tmp;
+
+ /* Back up PCI configuration registers */
+ for ( i = 0 ; i < EFAB_NUM_PCI_REG ; i++ ) {
+ pci_read_config_dword ( pci_dev, efab_pci_reg_addr[i],
+ &pci_reg.reg[i] );
+ }
+
+ /* Reset the whole device. */
+ EFAB_POPULATE_DWORD_1 ( reg, EF1_SW_RESET, 1 );
+ ef1002_writel ( efab, &reg, EF1_CTL2_REG );
+ mdelay ( 200 );
+
+ /* Restore PCI configuration space */
+ for ( i = 0 ; i < EFAB_NUM_PCI_REG ; i++ ) {
+ pci_write_config_dword ( pci_dev, efab_pci_reg_addr[i],
+ pci_reg.reg[i] );
+ }
+
+ /* Verify PCI configuration space */
+ for ( i = 0 ; i < EFAB_NUM_PCI_REG ; i++ ) {
+ pci_read_config_dword ( pci_dev, efab_pci_reg_addr[i], &tmp );
+ if ( tmp != pci_reg.reg[i] ) {
+ EFAB_LOG ( "PCI restore failed on register %02x "
+ "(is %08lx, should be %08lx); reboot\n",
+ i, tmp, pci_reg.reg[i] );
+ return 0;
+ }
+ }
+
+ /* Verify device reset complete */
+ ef1002_readl ( efab, &reg, EF1_CTR_GEN_STATUS0_REG );
+ if ( EFAB_DWORD_IS_ALL_ONES ( reg ) ) {
+ EFAB_ERR ( "Reset failed\n" );
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Initialise NIC
+ *
+ */
+static int ef1002_init_nic ( struct efab_nic *efab ) {
+ efab_dword_t reg;
+
+ /* patch in the MAC operations */
+ efab->mac_op = &ef1002_mac_operations;
+
+ /* No idea what CAM is, but the 'datasheet' says that we have
+ * to write these values in at start of day
+ */
+ EFAB_POPULATE_DWORD_1 ( reg, EF1_CAM_WTF_DOES_THIS_DO, 0x6 );
+ ef1002_writel ( efab, &reg, EF1_CAM_BASE + 0x20018 );
+ udelay ( 1000 );
+ EFAB_POPULATE_DWORD_1 ( reg, EF1_CAM_WTF_DOES_THIS_DO, 0x01000000 );
+ ef1002_writel ( efab, &reg, EF1_CAM_BASE + 0x00018 );
+ udelay ( 1000 );
+
+ /* General control register 0 */
+ ef1002_readl ( efab, &reg, EF1_CTR_GEN_STATUS0_REG );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_MASTER_EVENTS, 0 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_TX_ENGINE_EN, 0 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_RX_ENGINE_EN, 0 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_TURBO2, 1 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_TURBO1, 1 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_TURBO3, 1 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_CAM_ENABLE, 1 );
+ ef1002_writel ( efab, &reg, EF1_CTR_GEN_STATUS0_REG );
+ udelay ( 1000 );
+
+ /* General control register 2 */
+ ef1002_readl ( efab, &reg, EF1_CTL2_REG );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_PLL_TRAP, 1 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_MEM_MAP_4MB, 0 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_EV_INTR_CLR_WRITE, 0 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_BURST_MERGE, 0 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_CLEAR_NULL_PAD, 1 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_INTR_AFTER_EVENT, 1 );
+ ef1002_writel ( efab, &reg, EF1_CTL2_REG );
+ udelay ( 1000 );
+
+ /* Enable RX DMA */
+ ef1002_readl ( efab, &reg, EF1_DMA_RX_CSR_REG );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_DMA_RX_CSR_ENABLE, 1 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_DMA_RX_BELOW_1MB_EN, 1 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_DMA_RX_ABOVE_1GB_EN, 1 );
+ ef1002_writel ( efab, &reg, EF1_DMA_RX_CSR_REG );
+ udelay ( 1000 );
+
+ /* Enable TX DMA */
+ ef1002_readl ( efab, &reg, EF1_DMA_TX_CSR_REG );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_DMA_TX_CSR_CHAIN_EN, 1 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_DMA_TX_CSR_ENABLE, 0 /* ?? */ );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_DMA_TX_CSR_INT_EN, 0 /* ?? */ );
+ ef1002_writel ( efab, &reg, EF1_DMA_TX_CSR_REG );
+ udelay ( 1000 );
+
+ /* Disconnect the JTAG chain. Read-modify-write is impossible
+ * on the I2C control bits, since reading gives the state of
+ * the line inputs rather than the last written state.
+ */
+ ef1002_readl ( efab, &reg, EF1_EEPROM_REG );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_EEPROM_SDA, 1 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_EEPROM_SCL, 1 );
+ EFAB_SET_DWORD_FIELD ( reg, EF1_JTAG_DISCONNECT, 1 );
+ ef1002_writel ( efab, &reg, EF1_EEPROM_REG );
+ udelay ( 10 );
+
+ /* Flush descriptor queues */
+ EFAB_ZERO_DWORD ( reg );
+ ef1002_writel ( efab, &reg, EF1_RX_DESC_FIFO_FLUSH );
+ ef1002_writel ( efab, &reg, EF1_TX_DESC_FIFO_FLUSH );
+ wmb();
+ udelay ( 10000 );
+
+ /* Reset MAC */
+ efab->mac_op->reset ( efab );
+
+ /* Attach I2C bus */
+ ef1002_init_eeprom ( efab );
+
+ return 1;
+}
+
+/**
+ * Read MAC address from EEPROM
+ *
+ */
+static int ef1002_read_eeprom ( struct efab_nic *efab ) {
+ struct i2c_interface *i2c = &efab->ef1002_i2c.i2c;
+ struct i2c_device *i2cdev = &efab->ef1002_eeprom;
+
+ if ( i2c->read ( i2c, i2cdev, EF1_EEPROM_HWADDR_OFFSET,
+ efab->mac_addr, sizeof ( efab->mac_addr ) ) != 0 )
+ return 0;
+
+ efab->mac_addr[ETH_ALEN-1] += efab->port;
+
+ return 1;
+}
+
+/** RX descriptor */
+typedef efab_qword_t ef1002_rx_desc_t;
+
+/**
+ * Build RX descriptor
+ *
+ */
+static void ef1002_build_rx_desc ( struct efab_nic *efab,
+ struct efab_rx_buf *rx_buf ) {
+ ef1002_rx_desc_t rxd;
+
+ EFAB_POPULATE_QWORD_3 ( rxd,
+ EF1_RX_KER_EVQ, 0,
+ EF1_RX_KER_IDX, rx_buf->id,
+ EF1_RX_KER_BUF_ADR,
+ virt_to_bus ( rx_buf->addr ) );
+ ef1002_writel ( efab, &rxd.dword[0], EF1_RX_DESC_FIFO + 0 );
+ wmb();
+ ef1002_writel ( efab, &rxd.dword[1], EF1_RX_DESC_FIFO + 4 );
+ udelay ( 10 );
+}
+
+/**
+ * Update RX descriptor write pointer
+ *
+ */
+static void ef1002_notify_rx_desc ( struct efab_nic *efab __unused ) {
+ /* Nothing to do */
+}
+
+/** TX descriptor */
+typedef efab_oword_t ef1002_tx_desc_t;
+
+/**
+ * Build TX descriptor
+ *
+ */
+static void ef1002_build_tx_desc ( struct efab_nic *efab,
+ struct efab_tx_buf *tx_buf ) {
+ ef1002_tx_desc_t txd;
+
+ EFAB_POPULATE_OWORD_7 ( txd,
+ EF1_TX_KER_EVQ, 0,
+ EF1_TX_KER_IDX, tx_buf->id,
+ EF1_TX_KER_MODE, 0 /* IP mode */,
+ EF1_TX_KER_PORT, efab->port,
+ EF1_TX_KER_CONT, 0,
+ EF1_TX_KER_BYTE_CNT, tx_buf->len,
+ EF1_TX_KER_BUF_ADR,
+ virt_to_bus ( tx_buf->addr ) );
+
+ ef1002_writel ( efab, &txd.dword[0], EF1_TX_DESC_FIFO + 0 );
+ ef1002_writel ( efab, &txd.dword[1], EF1_TX_DESC_FIFO + 4 );
+ wmb();
+ ef1002_writel ( efab, &txd.dword[2], EF1_TX_DESC_FIFO + 8 );
+ udelay ( 10 );
+}
+
+/**
+ * Update TX descriptor write pointer
+ *
+ */
+static void ef1002_notify_tx_desc ( struct efab_nic *efab __unused ) {
+ /* Nothing to do */
+}
+
+/** An event */
+typedef efab_qword_t ef1002_event_t;
+
+/**
+ * Retrieve event from event queue
+ *
+ */
+static int ef1002_fetch_event ( struct efab_nic *efab,
+ struct efab_event *event ) {
+ efab_dword_t reg;
+ int ev_code;
+ int words;
+
+ /* Check event FIFO depth */
+ ef1002_readl_silent ( efab, &reg, EF1_EVENT_FIFO_COUNT_REG );
+ words = EFAB_DWORD_FIELD ( reg, EF1_EV_COUNT );
+ if ( ! words )
+ return 0;
+
+ /* Read event data */
+ ef1002_readl ( efab, &reg, EF1_EVENT_FIFO_REG );
+ DBG ( "Event is " EFAB_DWORD_FMT "\n", EFAB_DWORD_VAL ( reg ) );
+
+ /* Decode event */
+ ev_code = EFAB_DWORD_FIELD ( reg, EF1_EV_CODE );
+ event->drop = 0;
+ switch ( ev_code ) {
+ case EF1_TX_EV_DECODE:
+ event->type = EFAB_EV_TX;
+ break;
+ case EF1_RX_EV_DECODE:
+ event->type = EFAB_EV_RX;
+ event->rx_id = EFAB_DWORD_FIELD ( reg, EF1_RX_EV_IDX );
+ /* RX len not available via event FIFO */
+ event->rx_len = ETH_FRAME_LEN;
+ break;
+ case EF1_TIMER_EV_DECODE:
+ /* These are safe to ignore. We seem to get some at
+ * start of day, presumably due to the timers starting
+ * up with random contents.
+ */
+ event->type = EFAB_EV_NONE;
+ break;
+ default:
+ EFAB_ERR ( "Unknown event type %d\n", ev_code );
+ event->type = EFAB_EV_NONE;
+ }
+
+ /* Clear any pending interrupts */
+ ef1002_readl ( efab, &reg, EF1_IRQ_SRC_REG );
+
+ return 1;
+}
+
+/**
+ * Enable/disable interrupts
+ *
+ */
+static void ef1002_mask_irq ( struct efab_nic *efab, int enabled ) {
+ efab_dword_t irq_mask;
+
+ EFAB_POPULATE_DWORD_2 ( irq_mask,
+ EF1_IRQ_SERR, enabled,
+ EF1_IRQ_EVQ, enabled );
+ ef1002_writel ( efab, &irq_mask, EF1_IRQ_MASK_REG );
+}
+
+/**
+ * Generate interrupt
+ *
+ */
+static void ef1002_generate_irq ( struct efab_nic *efab ) {
+ ef1002_event_t test_event;
+
+ EFAB_POPULATE_QWORD_1 ( test_event,
+ EF1_EV_CODE, EF1_DRV_GEN_EV_DECODE );
+ ef1002_writel ( efab, &test_event.dword[0], EF1_EVT3_REG );
+}
+
+/**
+ * Write dword to an EF1002 MAC register
+ *
+ */
+static void ef1002_mac_writel ( struct efab_nic *efab,
+ efab_dword_t *value, unsigned int mac_reg ) {
+ ef1002_writel ( efab, value, EF1002_MAC_REG ( efab, mac_reg ) );
+}
+
+/**
+ * Read dword from an EF1002 MAC register
+ *
+ */
+static void ef1002_mac_readl ( struct efab_nic *efab,
+ efab_dword_t *value, unsigned int mac_reg ) {
+ ef1002_readl ( efab, value, EF1002_MAC_REG ( efab, mac_reg ) );
+}
+
+/**
+ * Initialise MAC
+ *
+ */
+static int ef1002_init_mac ( struct efab_nic *efab ) {
+ static struct efab_mentormac_parameters ef1002_mentormac_params = {
+ .gmf_cfgfrth = 0x13,
+ .gmf_cfgftth = 0x10,
+ .gmf_cfghwmft = 0x555,
+ .gmf_cfghwm = 0x2a,
+ .gmf_cfglwm = 0x15,
+ };
+ efab_dword_t reg;
+ unsigned int mac_clk;
+
+ /* Initialise PHY */
+ alaska_init ( efab );
+
+ /* Initialise MAC */
+ mentormac_init ( efab, &ef1002_mentormac_params );
+
+ /* Write Level 5 watermark register */
+ EFAB_POPULATE_DWORD_1 ( reg, EF1_L5WM, 0x10040000 );
+ efab->mac_op->mac_writel ( efab, &reg, EF1_GMF_L5WM_REG_MAC );
+ udelay ( 10 );
+
+ /* Set MAC clock speed */
+ ef1002_readl ( efab, &reg, EF1_GM_MAC_CLK_REG );
+ mac_clk = ( efab->link_options & LPA_1000 ) ? 0 : 1;
+ if ( efab->port == 0 ) {
+ EFAB_SET_DWORD_FIELD ( reg, EF1_GM_PORT0_MAC_CLK, mac_clk );
+ } else {
+ EFAB_SET_DWORD_FIELD ( reg, EF1_GM_PORT1_MAC_CLK, mac_clk );
+ }
+ ef1002_writel ( efab, &reg, EF1_GM_MAC_CLK_REG );
+ udelay ( 10 );
+
+ return 1;
+}
+
+/**
+ * Reset MAC
+ *
+ */
+static int ef1002_reset_mac ( struct efab_nic *efab ) {
+ mentormac_reset ( efab );
+ return 1;
+}
+
+/** MDIO write */
+static void ef1002_mdio_write ( struct efab_nic *efab, int location,
+ int value ) {
+ mentormac_mdio_write ( efab, efab->port + 2, location, value );
+}
+
+/** MDIO read */
+static int ef1002_mdio_read ( struct efab_nic *efab, int location ) {
+ return mentormac_mdio_read ( efab, efab->port + 2, location );
+}
+
+static struct efab_operations ef1002_operations = {
+ .get_membase = ef1002_get_membase,
+ .reset = ef1002_reset,
+ .init_nic = ef1002_init_nic,
+ .read_eeprom = ef1002_read_eeprom,
+ .build_rx_desc = ef1002_build_rx_desc,
+ .notify_rx_desc = ef1002_notify_rx_desc,
+ .build_tx_desc = ef1002_build_tx_desc,
+ .notify_tx_desc = ef1002_notify_tx_desc,
+ .fetch_event = ef1002_fetch_event,
+ .mask_irq = ef1002_mask_irq,
+ .generate_irq = ef1002_generate_irq,
+ .mdio_write = ef1002_mdio_write,
+ .mdio_read = ef1002_mdio_read,
+};
+
+static struct efab_mac_operations ef1002_mac_operations = {
+ .mac_writel = ef1002_mac_writel,
+ .mac_readl = ef1002_mac_readl,
+ .init = ef1002_init_mac,
+ .reset = ef1002_reset_mac,
+};
+
+/**************************************************************************
+ *
+ * Falcon routines
+ *
+ **************************************************************************
+ */
+
+/* I/O BAR address register */
+#define FCN_IOM_IND_ADR_REG 0x0
+
+/* I/O BAR data register */
+#define FCN_IOM_IND_DAT_REG 0x4
+
+/* Interrupt enable register */
+#define FCN_INT_EN_REG_KER 0x0010
+#define FCN_MEM_PERR_INT_EN_KER_LBN 5
+#define FCN_MEM_PERR_INT_EN_KER_WIDTH 1
+#define FCN_KER_INT_CHAR_LBN 4
+#define FCN_KER_INT_CHAR_WIDTH 1
+#define FCN_KER_INT_KER_LBN 3
+#define FCN_KER_INT_KER_WIDTH 1
+#define FCN_ILL_ADR_ERR_INT_EN_KER_LBN 2
+#define FCN_ILL_ADR_ERR_INT_EN_KER_WIDTH 1
+#define FCN_SRM_PERR_INT_EN_KER_LBN 1
+#define FCN_SRM_PERR_INT_EN_KER_WIDTH 1
+#define FCN_DRV_INT_EN_KER_LBN 0
+#define FCN_DRV_INT_EN_KER_WIDTH 1
+
+/* Interrupt status register */
+#define FCN_INT_ADR_REG_KER 0x0030
+#define FCN_INT_ADR_KER_LBN 0
+#define FCN_INT_ADR_KER_WIDTH EFAB_DMA_TYPE_WIDTH ( 64 )
+
+/* Interrupt acknowledge register */
+#define FCN_INT_ACK_KER_REG 0x0050
+
+/* SPI host command register */
+#define FCN_EE_SPI_HCMD_REG_KER 0x0100
+#define FCN_EE_SPI_HCMD_CMD_EN_LBN 31
+#define FCN_EE_SPI_HCMD_CMD_EN_WIDTH 1
+#define FCN_EE_WR_TIMER_ACTIVE_LBN 28
+#define FCN_EE_WR_TIMER_ACTIVE_WIDTH 1
+#define FCN_EE_SPI_HCMD_SF_SEL_LBN 24
+#define FCN_EE_SPI_HCMD_SF_SEL_WIDTH 1
+#define FCN_EE_SPI_EEPROM 0
+#define FCN_EE_SPI_FLASH 1
+#define FCN_EE_SPI_HCMD_DABCNT_LBN 16
+#define FCN_EE_SPI_HCMD_DABCNT_WIDTH 5
+#define FCN_EE_SPI_HCMD_READ_LBN 15
+#define FCN_EE_SPI_HCMD_READ_WIDTH 1
+#define FCN_EE_SPI_READ 1
+#define FCN_EE_SPI_WRITE 0
+#define FCN_EE_SPI_HCMD_DUBCNT_LBN 12
+#define FCN_EE_SPI_HCMD_DUBCNT_WIDTH 2
+#define FCN_EE_SPI_HCMD_ADBCNT_LBN 8
+#define FCN_EE_SPI_HCMD_ADBCNT_WIDTH 2
+#define FCN_EE_SPI_HCMD_ENC_LBN 0
+#define FCN_EE_SPI_HCMD_ENC_WIDTH 8
+
+/* SPI host address register */
+#define FCN_EE_SPI_HADR_REG_KER 0x0110
+#define FCN_EE_SPI_HADR_DUBYTE_LBN 24
+#define FCN_EE_SPI_HADR_DUBYTE_WIDTH 8
+#define FCN_EE_SPI_HADR_ADR_LBN 0
+#define FCN_EE_SPI_HADR_ADR_WIDTH 24
+
+/* SPI host data register */
+#define FCN_EE_SPI_HDATA_REG_KER 0x0120
+#define FCN_EE_SPI_HDATA3_LBN 96
+#define FCN_EE_SPI_HDATA3_WIDTH 32
+#define FCN_EE_SPI_HDATA2_LBN 64
+#define FCN_EE_SPI_HDATA2_WIDTH 32
+#define FCN_EE_SPI_HDATA1_LBN 32
+#define FCN_EE_SPI_HDATA1_WIDTH 32
+#define FCN_EE_SPI_HDATA0_LBN 0
+#define FCN_EE_SPI_HDATA0_WIDTH 32
+
+/* VPI configuration register */
+#define FCN_VPD_CONFIG_REG_KER 0x0140
+#define FCN_VPD_9BIT_LBN 1
+#define FCN_VPD_9BIT_WIDTH 1
+
+/* NIC status register */
+#define FCN_NIC_STAT_REG 0x0200
+#define ONCHIP_SRAM_LBN 16
+#define ONCHIP_SRAM_WIDTH 1
+#define SF_PRST_LBN 9
+#define SF_PRST_WIDTH 1
+#define EE_PRST_LBN 8
+#define EE_PRST_WIDTH 1
+#define EE_STRAP_LBN 7
+#define EE_STRAP_WIDTH 1
+#define PCI_PCIX_MODE_LBN 4
+#define PCI_PCIX_MODE_WIDTH 3
+#define PCI_PCIX_MODE_PCI33_DECODE 0
+#define PCI_PCIX_MODE_PCI66_DECODE 1
+#define PCI_PCIX_MODE_PCIX66_DECODE 5
+#define PCI_PCIX_MODE_PCIX100_DECODE 6
+#define PCI_PCIX_MODE_PCIX133_DECODE 7
+#define STRAP_ISCSI_EN_LBN 3
+#define STRAP_ISCSI_EN_WIDTH 1
+#define STRAP_PINS_LBN 0
+#define STRAP_PINS_WIDTH 3
+/* These bit definitions are extrapolated from the list of numerical
+ * values for STRAP_PINS. If you want a laugh, read the datasheet's
+ * definition for when bits 2:0 are set to 7.
+ */
+#define STRAP_10G_LBN 2
+#define STRAP_10G_WIDTH 1
+#define STRAP_DUAL_PORT_LBN 1
+#define STRAP_DUAL_PORT_WIDTH 1
+#define STRAP_PCIE_LBN 0
+#define STRAP_PCIE_WIDTH 1
+
+/* GPIO control register */
+#define FCN_GPIO_CTL_REG_KER 0x0210
+#define FCN_FLASH_PRESENT_LBN 7
+#define FCN_FLASH_PRESENT_WIDTH 1
+#define FCN_EEPROM_PRESENT_LBN 6
+#define FCN_EEPROM_PRESENT_WIDTH 1
+
+/* Global control register */
+#define FCN_GLB_CTL_REG_KER 0x0220
+#define EXT_PHY_RST_CTL_LBN 63
+#define EXT_PHY_RST_CTL_WIDTH 1
+#define PCIE_SD_RST_CTL_LBN 61
+#define PCIE_SD_RST_CTL_WIDTH 1
+#define PCIX_RST_CTL_LBN 60
+#define PCIX_RST_CTL_WIDTH 1
+#define PCIE_STCK_RST_CTL_LBN 59
+#define PCIE_STCK_RST_CTL_WIDTH 1
+#define PCIE_NSTCK_RST_CTL_LBN 58
+#define PCIE_NSTCK_RST_CTL_WIDTH 1
+#define PCIE_CORE_RST_CTL_LBN 57
+#define PCIE_CORE_RST_CTL_WIDTH 1
+#define EE_RST_CTL_LBN 49
+#define EE_RST_CTL_WIDTH 1
+#define CS_RST_CTL_LBN 48
+#define CS_RST_CTL_WIDTH 1
+#define RST_EXT_PHY_LBN 31
+#define RST_EXT_PHY_WIDTH 1
+#define INT_RST_DUR_LBN 4
+#define INT_RST_DUR_WIDTH 3
+#define EXT_PHY_RST_DUR_LBN 1
+#define EXT_PHY_RST_DUR_WIDTH 3
+#define SWRST_LBN 0
+#define SWRST_WIDTH 1
+#define INCLUDE_IN_RESET 0
+#define EXCLUDE_FROM_RESET 1
+
+/* FPGA build version */
+#define ALTERA_BUILD_REG_KER 0x0300
+#define VER_MAJOR_LBN 24
+#define VER_MAJOR_WIDTH 8
+#define VER_MINOR_LBN 16
+#define VER_MINOR_WIDTH 8
+#define VER_BUILD_LBN 0
+#define VER_BUILD_WIDTH 16
+#define VER_ALL_LBN 0
+#define VER_ALL_WIDTH 32
+
+/* Timer table for kernel access */
+#define FCN_TIMER_CMD_REG_KER 0x420
+#define FCN_TIMER_MODE_LBN 12
+#define FCN_TIMER_MODE_WIDTH 2
+#define FCN_TIMER_MODE_DIS 0
+#define FCN_TIMER_MODE_INT_HLDOFF 1
+#define FCN_TIMER_VAL_LBN 0
+#define FCN_TIMER_VAL_WIDTH 12
+
+/* Receive configuration register */
+#define FCN_RX_CFG_REG_KER 0x800
+#define FCN_RX_XOFF_EN_LBN 0
+#define FCN_RX_XOFF_EN_WIDTH 1
+
+/* SRAM receive descriptor cache configuration register */
+#define FCN_SRM_RX_DC_CFG_REG_KER 0x610
+#define FCN_SRM_RX_DC_BASE_ADR_LBN 0
+#define FCN_SRM_RX_DC_BASE_ADR_WIDTH 21
+
+/* SRAM transmit descriptor cache configuration register */
+#define FCN_SRM_TX_DC_CFG_REG_KER 0x620
+#define FCN_SRM_TX_DC_BASE_ADR_LBN 0
+#define FCN_SRM_TX_DC_BASE_ADR_WIDTH 21
+
+/* Receive filter control register */
+#define FCN_RX_FILTER_CTL_REG_KER 0x810
+#define FCN_NUM_KER_LBN 24
+#define FCN_NUM_KER_WIDTH 2
+
+/* Receive descriptor update register */
+#define FCN_RX_DESC_UPD_REG_KER 0x0830
+#define FCN_RX_DESC_WPTR_LBN 96
+#define FCN_RX_DESC_WPTR_WIDTH 12
+#define FCN_RX_DESC_UPD_REG_KER_DWORD ( FCN_RX_DESC_UPD_REG_KER + 12 )
+#define FCN_RX_DESC_WPTR_DWORD_LBN 0
+#define FCN_RX_DESC_WPTR_DWORD_WIDTH 12
+
+/* Receive descriptor cache configuration register */
+#define FCN_RX_DC_CFG_REG_KER 0x840
+#define FCN_RX_DC_SIZE_LBN 0
+#define FCN_RX_DC_SIZE_WIDTH 2
+
+/* Transmit descriptor update register */
+#define FCN_TX_DESC_UPD_REG_KER 0x0a10
+#define FCN_TX_DESC_WPTR_LBN 96
+#define FCN_TX_DESC_WPTR_WIDTH 12
+#define FCN_TX_DESC_UPD_REG_KER_DWORD ( FCN_TX_DESC_UPD_REG_KER + 12 )
+#define FCN_TX_DESC_WPTR_DWORD_LBN 0
+#define FCN_TX_DESC_WPTR_DWORD_WIDTH 12
+
+/* Transmit descriptor cache configuration register */
+#define FCN_TX_DC_CFG_REG_KER 0xa20
+#define FCN_TX_DC_SIZE_LBN 0
+#define FCN_TX_DC_SIZE_WIDTH 2
+
+/* PHY management transmit data register */
+#define FCN_MD_TXD_REG_KER 0xc00
+#define FCN_MD_TXD_LBN 0
+#define FCN_MD_TXD_WIDTH 16
+
+/* PHY management receive data register */
+#define FCN_MD_RXD_REG_KER 0xc10
+#define FCN_MD_RXD_LBN 0
+#define FCN_MD_RXD_WIDTH 16
+
+/* PHY management configuration & status register */
+#define FCN_MD_CS_REG_KER 0xc20
+#define FCN_MD_GC_LBN 4
+#define FCN_MD_GC_WIDTH 1
+#define FCN_MD_RIC_LBN 2
+#define FCN_MD_RIC_WIDTH 1
+#define FCN_MD_WRC_LBN 0
+#define FCN_MD_WRC_WIDTH 1
+
+/* PHY management PHY address register */
+#define FCN_MD_PHY_ADR_REG_KER 0xc30
+#define FCN_MD_PHY_ADR_LBN 0
+#define FCN_MD_PHY_ADR_WIDTH 16
+
+/* PHY management ID register */
+#define FCN_MD_ID_REG_KER 0xc40
+#define FCN_MD_PRT_ADR_LBN 11
+#define FCN_MD_PRT_ADR_WIDTH 5
+#define FCN_MD_DEV_ADR_LBN 6
+#define FCN_MD_DEV_ADR_WIDTH 5
+
+/* PHY management status & mask register */
+#define FCN_MD_STAT_REG_KER 0xc50
+#define FCN_MD_BSY_LBN 0
+#define FCN_MD_BSY_WIDTH 1
+
+/* Port 0 and 1 MAC control registers */
+#define FCN_MAC0_CTRL_REG_KER 0xc80
+#define FCN_MAC1_CTRL_REG_KER 0xc90
+#define FCN_MAC_XOFF_VAL_LBN 16
+#define FCN_MAC_XOFF_VAL_WIDTH 16
+#define FCN_MAC_BCAD_ACPT_LBN 4
+#define FCN_MAC_BCAD_ACPT_WIDTH 1
+#define FCN_MAC_UC_PROM_LBN 3
+#define FCN_MAC_UC_PROM_WIDTH 1
+#define FCN_MAC_LINK_STATUS_LBN 2
+#define FCN_MAC_LINK_STATUS_WIDTH 1
+#define FCN_MAC_SPEED_LBN 0
+#define FCN_MAC_SPEED_WIDTH 2
+
+/* GMAC registers */
+#define FALCON_GMAC_REGBANK 0xe00
+#define FALCON_GMAC_REGBANK_SIZE 0x200
+#define FALCON_GMAC_REG_SIZE 0x10
+
+/* XGMAC registers */
+#define FALCON_XMAC_REGBANK 0x1200
+#define FALCON_XMAC_REGBANK_SIZE 0x200
+#define FALCON_XMAC_REG_SIZE 0x10
+
+/* XGMAC address register low */
+#define FCN_XM_ADR_LO_REG_MAC 0x00
+#define FCN_XM_ADR_3_LBN 24
+#define FCN_XM_ADR_3_WIDTH 8
+#define FCN_XM_ADR_2_LBN 16
+#define FCN_XM_ADR_2_WIDTH 8
+#define FCN_XM_ADR_1_LBN 8
+#define FCN_XM_ADR_1_WIDTH 8
+#define FCN_XM_ADR_0_LBN 0
+#define FCN_XM_ADR_0_WIDTH 8
+
+/* XGMAC address register high */
+#define FCN_XM_ADR_HI_REG_MAC 0x01
+#define FCN_XM_ADR_5_LBN 8
+#define FCN_XM_ADR_5_WIDTH 8
+#define FCN_XM_ADR_4_LBN 0
+#define FCN_XM_ADR_4_WIDTH 8
+
+/* XGMAC global configuration - port 0*/
+#define FCN_XM_GLB_CFG_REG_MAC 0x02
+#define FCN_XM_RX_STAT_EN_LBN 11
+#define FCN_XM_RX_STAT_EN_WIDTH 1
+#define FCN_XM_TX_STAT_EN_LBN 10
+#define FCN_XM_TX_STAT_EN_WIDTH 1
+#define FCN_XM_RX_JUMBO_MODE_LBN 6
+#define FCN_XM_RX_JUMBO_MODE_WIDTH 1
+#define FCN_XM_CORE_RST_LBN 0
+#define FCN_XM_CORE_RST_WIDTH 1
+
+/* XGMAC transmit configuration - port 0 */
+#define FCN_XM_TX_CFG_REG_MAC 0x03
+#define FCN_XM_IPG_LBN 16
+#define FCN_XM_IPG_WIDTH 4
+#define FCN_XM_FCNTL_LBN 10
+#define FCN_XM_FCNTL_WIDTH 1
+#define FCN_XM_TXCRC_LBN 8
+#define FCN_XM_TXCRC_WIDTH 1
+#define FCN_XM_AUTO_PAD_LBN 5
+#define FCN_XM_AUTO_PAD_WIDTH 1
+#define FCN_XM_TX_PRMBL_LBN 2
+#define FCN_XM_TX_PRMBL_WIDTH 1
+#define FCN_XM_TXEN_LBN 1
+#define FCN_XM_TXEN_WIDTH 1
+
+/* XGMAC receive configuration - port 0 */
+#define FCN_XM_RX_CFG_REG_MAC 0x04
+#define FCN_XM_PASS_CRC_ERR_LBN 25
+#define FCN_XM_PASS_CRC_ERR_WIDTH 1
+#define FCN_XM_AUTO_DEPAD_LBN 8
+#define FCN_XM_AUTO_DEPAD_WIDTH 1
+#define FCN_XM_RXEN_LBN 1
+#define FCN_XM_RXEN_WIDTH 1
+
+/* XGMAC transmit parameter register */
+#define FCN_XM_TX_PARAM_REG_MAC 0x0d
+#define FCN_XM_TX_JUMBO_MODE_LBN 31
+#define FCN_XM_TX_JUMBO_MODE_WIDTH 1
+#define FCN_XM_MAX_TX_FRM_SIZE_LBN 16
+#define FCN_XM_MAX_TX_FRM_SIZE_WIDTH 14
+
+/* XGMAC receive parameter register */
+#define FCN_XM_RX_PARAM_REG_MAC 0x0e
+#define FCN_XM_MAX_RX_FRM_SIZE_LBN 0
+#define FCN_XM_MAX_RX_FRM_SIZE_WIDTH 14
+
+/* XAUI XGXS core status register */
+#define FCN_XX_ALIGN_DONE_LBN 20
+#define FCN_XX_ALIGN_DONE_WIDTH 1
+#define FCN_XX_CORE_STAT_REG_MAC 0x16
+#define FCN_XX_SYNC_STAT_LBN 16
+#define FCN_XX_SYNC_STAT_WIDTH 4
+#define FCN_XX_SYNC_STAT_DECODE_SYNCED 0xf
+#define FCN_XX_COMMA_DET_LBN 12
+#define FCN_XX_COMMA_DET_WIDTH 4
+#define FCN_XX_COMMA_DET_RESET 0xf
+
+
+/* XGXS/XAUI powerdown/reset register */
+#define FCN_XX_PWR_RST_REG_MAC 0x10
+#define FCN_XX_RSTXGXSRX_EN_LBN 2
+#define FCN_XX_RSTXGXSRX_EN_WIDTH 1
+#define FCN_XX_RSTXGXSTX_EN_LBN 1
+#define FCN_XX_RSTXGXSTX_EN_WIDTH 1
+#define FCN_XX_RST_XX_EN_LBN 0
+#define FCN_XX_RST_XX_EN_WIDTH 1
+
+/* Receive descriptor pointer table */
+#define FCN_RX_DESC_PTR_TBL_KER 0x11800
+#define FCN_RX_DESCQ_BUF_BASE_ID_LBN 36
+#define FCN_RX_DESCQ_BUF_BASE_ID_WIDTH 20
+#define FCN_RX_DESCQ_EVQ_ID_LBN 24
+#define FCN_RX_DESCQ_EVQ_ID_WIDTH 12
+#define FCN_RX_DESCQ_OWNER_ID_LBN 10
+#define FCN_RX_DESCQ_OWNER_ID_WIDTH 14
+#define FCN_RX_DESCQ_SIZE_LBN 3
+#define FCN_RX_DESCQ_SIZE_WIDTH 2
+#define FCN_RX_DESCQ_SIZE_4K 3
+#define FCN_RX_DESCQ_SIZE_2K 2
+#define FCN_RX_DESCQ_SIZE_1K 1
+#define FCN_RX_DESCQ_SIZE_512 0
+#define FCN_RX_DESCQ_TYPE_LBN 2
+#define FCN_RX_DESCQ_TYPE_WIDTH 1
+#define FCN_RX_DESCQ_JUMBO_LBN 1
+#define FCN_RX_DESCQ_JUMBO_WIDTH 1
+#define FCN_RX_DESCQ_EN_LBN 0
+#define FCN_RX_DESCQ_EN_WIDTH 1
+
+/* Transmit descriptor pointer table */
+#define FCN_TX_DESC_PTR_TBL_KER 0x11900
+#define FCN_TX_DESCQ_EN_LBN 88
+#define FCN_TX_DESCQ_EN_WIDTH 1
+#define FCN_TX_DESCQ_BUF_BASE_ID_LBN 36
+#define FCN_TX_DESCQ_BUF_BASE_ID_WIDTH 20
+#define FCN_TX_DESCQ_EVQ_ID_LBN 24
+#define FCN_TX_DESCQ_EVQ_ID_WIDTH 12
+#define FCN_TX_DESCQ_OWNER_ID_LBN 10
+#define FCN_TX_DESCQ_OWNER_ID_WIDTH 14
+#define FCN_TX_DESCQ_SIZE_LBN 3
+#define FCN_TX_DESCQ_SIZE_WIDTH 2
+#define FCN_TX_DESCQ_SIZE_4K 3
+#define FCN_TX_DESCQ_SIZE_2K 2
+#define FCN_TX_DESCQ_SIZE_1K 1
+#define FCN_TX_DESCQ_SIZE_512 0
+#define FCN_TX_DESCQ_TYPE_LBN 1
+#define FCN_TX_DESCQ_TYPE_WIDTH 2
+#define FCN_TX_DESCQ_FLUSH_LBN 0
+#define FCN_TX_DESCQ_FLUSH_WIDTH 1
+
+/* Event queue pointer */
+#define FCN_EVQ_PTR_TBL_KER 0x11a00
+#define FCN_EVQ_EN_LBN 23
+#define FCN_EVQ_EN_WIDTH 1
+#define FCN_EVQ_SIZE_LBN 20
+#define FCN_EVQ_SIZE_WIDTH 3
+#define FCN_EVQ_SIZE_32K 6
+#define FCN_EVQ_SIZE_16K 5
+#define FCN_EVQ_SIZE_8K 4
+#define FCN_EVQ_SIZE_4K 3
+#define FCN_EVQ_SIZE_2K 2
+#define FCN_EVQ_SIZE_1K 1
+#define FCN_EVQ_SIZE_512 0
+#define FCN_EVQ_BUF_BASE_ID_LBN 0
+#define FCN_EVQ_BUF_BASE_ID_WIDTH 20
+
+/* Event queue read pointer */
+#define FCN_EVQ_RPTR_REG_KER 0x11b00
+#define FCN_EVQ_RPTR_LBN 0
+#define FCN_EVQ_RPTR_WIDTH 14
+#define FCN_EVQ_RPTR_REG_KER_DWORD ( FCN_EVQ_RPTR_REG_KER + 0 )
+#define FCN_EVQ_RPTR_DWORD_LBN 0
+#define FCN_EVQ_RPTR_DWORD_WIDTH 14
+
+/* Special buffer descriptors */
+#define FCN_BUF_FULL_TBL_KER 0x18000
+#define FCN_IP_DAT_BUF_SIZE_LBN 50
+#define FCN_IP_DAT_BUF_SIZE_WIDTH 1
+#define FCN_IP_DAT_BUF_SIZE_8K 1
+#define FCN_IP_DAT_BUF_SIZE_4K 0
+#define FCN_BUF_ADR_FBUF_LBN 14
+#define FCN_BUF_ADR_FBUF_WIDTH 34
+#define FCN_BUF_OWNER_ID_FBUF_LBN 0
+#define FCN_BUF_OWNER_ID_FBUF_WIDTH 14
+
+/** Offset of a GMAC register within Falcon */
+#define FALCON_GMAC_REG( efab, mac_reg ) \
+ ( FALCON_GMAC_REGBANK + \
+ ( (efab)->port * FALCON_GMAC_REGBANK_SIZE ) + \
+ ( (mac_reg) * FALCON_GMAC_REG_SIZE ) )
+
+/** Offset of an XMAC register within Falcon */
+#define FALCON_XMAC_REG( efab_port, mac_reg ) \
+ ( FALCON_XMAC_REGBANK + \
+ ( (efab_port)->port * FALCON_XMAC_REGBANK_SIZE ) + \
+ ( (mac_reg) * FALCON_XMAC_REG_SIZE ) )
+
+#define FCN_MAC_DATA_LBN 0
+#define FCN_MAC_DATA_WIDTH 32
+
+/* Transmit descriptor */
+#define FCN_TX_KER_PORT_LBN 63
+#define FCN_TX_KER_PORT_WIDTH 1
+#define FCN_TX_KER_BYTE_CNT_LBN 48
+#define FCN_TX_KER_BYTE_CNT_WIDTH 14
+#define FCN_TX_KER_BUF_ADR_LBN 0
+#define FCN_TX_KER_BUF_ADR_WIDTH EFAB_DMA_TYPE_WIDTH ( 46 )
+
+
+/* Receive descriptor */
+#define FCN_RX_KER_BUF_SIZE_LBN 48
+#define FCN_RX_KER_BUF_SIZE_WIDTH 14
+#define FCN_RX_KER_BUF_ADR_LBN 0
+#define FCN_RX_KER_BUF_ADR_WIDTH EFAB_DMA_TYPE_WIDTH ( 46 )
+
+/* Event queue entries */
+#define FCN_EV_CODE_LBN 60
+#define FCN_EV_CODE_WIDTH 4
+#define FCN_RX_IP_EV_DECODE 0
+#define FCN_TX_IP_EV_DECODE 2
+#define FCN_DRIVER_EV_DECODE 5
+
+/* Receive events */
+#define FCN_RX_EV_PKT_OK_LBN 56
+#define FCN_RX_EV_PKT_OK_WIDTH 1
+#define FCN_RX_PORT_LBN 30
+#define FCN_RX_PORT_WIDTH 1
+#define FCN_RX_EV_BYTE_CNT_LBN 16
+#define FCN_RX_EV_BYTE_CNT_WIDTH 14
+#define FCN_RX_EV_DESC_PTR_LBN 0
+#define FCN_RX_EV_DESC_PTR_WIDTH 12
+
+/* Transmit events */
+#define FCN_TX_EV_DESC_PTR_LBN 0
+#define FCN_TX_EV_DESC_PTR_WIDTH 12
+
+/* Fixed special buffer numbers to use */
+#define FALCON_EVQ_ID 0
+#define FALCON_TXD_ID 1
+#define FALCON_RXD_ID 2
+
+#if FALCON_USE_IO_BAR
+
+/* Write dword via the I/O BAR */
+static inline void _falcon_writel ( struct efab_nic *efab, uint32_t value,
+ unsigned int reg ) {
+ outl ( reg, efab->iobase + FCN_IOM_IND_ADR_REG );
+ outl ( value, efab->iobase + FCN_IOM_IND_DAT_REG );
+}
+
+/* Read dword via the I/O BAR */
+static inline uint32_t _falcon_readl ( struct efab_nic *efab,
+ unsigned int reg ) {
+ outl ( reg, efab->iobase + FCN_IOM_IND_ADR_REG );
+ return inl ( efab->iobase + FCN_IOM_IND_DAT_REG );
+}
+
+#else /* FALCON_USE_IO_BAR */
+
+#define _falcon_writel( efab, value, reg ) \
+ writel ( (value), (efab)->membase + (reg) )
+#define _falcon_readl( efab, reg ) readl ( (efab)->membase + (reg) )
+
+#endif /* FALCON_USE_IO_BAR */
+
+/**
+ * Write to a Falcon register
+ *
+ */
+static inline void falcon_write ( struct efab_nic *efab, efab_oword_t *value,
+ unsigned int reg ) {
+
+ EFAB_REGDUMP ( "Writing register %x with " EFAB_OWORD_FMT "\n",
+ reg, EFAB_OWORD_VAL ( *value ) );
+
+ _falcon_writel ( efab, value->u32[0], reg + 0 );
+ _falcon_writel ( efab, value->u32[1], reg + 4 );
+ _falcon_writel ( efab, value->u32[2], reg + 8 );
+ _falcon_writel ( efab, value->u32[3], reg + 12 );
+ wmb();
+}
+
+/**
+ * Write to Falcon SRAM
+ *
+ */
+static inline void falcon_write_sram ( struct efab_nic *efab,
+ efab_qword_t *value,
+ unsigned int index ) {
+ unsigned int reg = ( FCN_BUF_FULL_TBL_KER +
+ ( index * sizeof ( *value ) ) );
+
+ EFAB_REGDUMP ( "Writing SRAM register %x with " EFAB_QWORD_FMT "\n",
+ reg, EFAB_QWORD_VAL ( *value ) );
+
+ _falcon_writel ( efab, value->u32[0], reg + 0 );
+ _falcon_writel ( efab, value->u32[1], reg + 4 );
+ wmb();
+}
+
+/**
+ * Write dword to Falcon register that allows partial writes
+ *
+ */
+static inline void falcon_writel ( struct efab_nic *efab, efab_dword_t *value,
+ unsigned int reg ) {
+ EFAB_REGDUMP ( "Writing partial register %x with " EFAB_DWORD_FMT "\n",
+ reg, EFAB_DWORD_VAL ( *value ) );
+ _falcon_writel ( efab, value->u32[0], reg );
+}
+
+/**
+ * Read from a Falcon register
+ *
+ */
+static inline void falcon_read ( struct efab_nic *efab, efab_oword_t *value,
+ unsigned int reg ) {
+ value->u32[0] = _falcon_readl ( efab, reg + 0 );
+ value->u32[1] = _falcon_readl ( efab, reg + 4 );
+ value->u32[2] = _falcon_readl ( efab, reg + 8 );
+ value->u32[3] = _falcon_readl ( efab, reg + 12 );
+
+ EFAB_REGDUMP ( "Read from register %x, got " EFAB_OWORD_FMT "\n",
+ reg, EFAB_OWORD_VAL ( *value ) );
+}
+
+/**
+ * Read from Falcon SRAM
+ *
+ */
+static inline void falcon_read_sram ( struct efab_nic *efab,
+ efab_qword_t *value,
+ unsigned int index ) {
+ unsigned int reg = ( FCN_BUF_FULL_TBL_KER +
+ ( index * sizeof ( *value ) ) );
+
+ value->u32[0] = _falcon_readl ( efab, reg + 0 );
+ value->u32[1] = _falcon_readl ( efab, reg + 4 );
+ EFAB_REGDUMP ( "Read from SRAM register %x, got " EFAB_QWORD_FMT "\n",
+ reg, EFAB_QWORD_VAL ( *value ) );
+}
+
+/**
+ * Read dword from a portion of a Falcon register
+ *
+ */
+static inline void falcon_readl ( struct efab_nic *efab, efab_dword_t *value,
+ unsigned int reg ) {
+ value->u32[0] = _falcon_readl ( efab, reg );
+ EFAB_REGDUMP ( "Read from register %x, got " EFAB_DWORD_FMT "\n",
+ reg, EFAB_DWORD_VAL ( *value ) );
+}
+
+/**
+ * Verified write to Falcon SRAM
+ *
+ */
+static inline void falcon_write_sram_verify ( struct efab_nic *efab,
+ efab_qword_t *value,
+ unsigned int index ) {
+ efab_qword_t verify;
+
+ falcon_write_sram ( efab, value, index );
+ udelay ( 1000 );
+ falcon_read_sram ( efab, &verify, index );
+ if ( memcmp ( &verify, value, sizeof ( verify ) ) != 0 ) {
+ EFAB_ERR ( "SRAM index %x failure: wrote " EFAB_QWORD_FMT
+ " got " EFAB_QWORD_FMT "\n", index,
+ EFAB_QWORD_VAL ( *value ),
+ EFAB_QWORD_VAL ( verify ) );
+ }
+}
+
+/**
+ * Get memory base
+ *
+ */
+static void falcon_get_membase ( struct efab_nic *efab ) {
+ unsigned long membase_phys;
+
+ membase_phys = pci_bar_start ( efab->pci, PCI_BASE_ADDRESS_2 );
+ efab->membase = ioremap ( membase_phys, 0x20000 );
+}
+
+#define FCN_DUMP_REG( efab, _reg ) do { \
+ efab_oword_t reg; \
+ falcon_read ( efab, &reg, _reg ); \
+ EFAB_LOG ( #_reg " = " EFAB_OWORD_FMT "\n", \
+ EFAB_OWORD_VAL ( reg ) ); \
+ } while ( 0 );
+
+#define FCN_DUMP_MAC_REG( efab, _mac_reg ) do { \
+ efab_dword_t reg; \
+ efab->mac_op->mac_readl ( efab, &reg, _mac_reg ); \
+ EFAB_LOG ( #_mac_reg " = " EFAB_DWORD_FMT "\n", \
+ EFAB_DWORD_VAL ( reg ) ); \
+ } while ( 0 );
+
+/**
+ * Dump register contents (for debugging)
+ *
+ * Marked as static inline so that it will not be compiled in if not
+ * used.
+ */
+static inline void falcon_dump_regs ( struct efab_nic *efab ) {
+ FCN_DUMP_REG ( efab, FCN_INT_EN_REG_KER );
+ FCN_DUMP_REG ( efab, FCN_INT_ADR_REG_KER );
+ FCN_DUMP_REG ( efab, FCN_GLB_CTL_REG_KER );
+ FCN_DUMP_REG ( efab, FCN_TIMER_CMD_REG_KER );
+ FCN_DUMP_REG ( efab, FCN_SRM_RX_DC_CFG_REG_KER );
+ FCN_DUMP_REG ( efab, FCN_SRM_TX_DC_CFG_REG_KER );
+ FCN_DUMP_REG ( efab, FCN_RX_FILTER_CTL_REG_KER );
+ FCN_DUMP_REG ( efab, FCN_RX_DC_CFG_REG_KER );
+ FCN_DUMP_REG ( efab, FCN_TX_DC_CFG_REG_KER );
+ FCN_DUMP_REG ( efab, FCN_MAC0_CTRL_REG_KER );
+ FCN_DUMP_REG ( efab, FCN_MAC1_CTRL_REG_KER );
+ FCN_DUMP_REG ( efab, FCN_RX_DESC_PTR_TBL_KER );
+ FCN_DUMP_REG ( efab, FCN_TX_DESC_PTR_TBL_KER );
+ FCN_DUMP_REG ( efab, FCN_EVQ_PTR_TBL_KER );
+ FCN_DUMP_MAC_REG ( efab, GM_CFG1_REG_MAC );
+ FCN_DUMP_MAC_REG ( efab, GM_CFG2_REG_MAC );
+ FCN_DUMP_MAC_REG ( efab, GM_MAX_FLEN_REG_MAC );
+ FCN_DUMP_MAC_REG ( efab, GM_MII_MGMT_CFG_REG_MAC );
+ FCN_DUMP_MAC_REG ( efab, GM_ADR1_REG_MAC );
+ FCN_DUMP_MAC_REG ( efab, GM_ADR2_REG_MAC );
+ FCN_DUMP_MAC_REG ( efab, GMF_CFG0_REG_MAC );
+ FCN_DUMP_MAC_REG ( efab, GMF_CFG1_REG_MAC );
+ FCN_DUMP_MAC_REG ( efab, GMF_CFG2_REG_MAC );
+ FCN_DUMP_MAC_REG ( efab, GMF_CFG3_REG_MAC );
+ FCN_DUMP_MAC_REG ( efab, GMF_CFG4_REG_MAC );
+ FCN_DUMP_MAC_REG ( efab, GMF_CFG5_REG_MAC );
+}
+
+/**
+ * Create special buffer
+ *
+ */
+static void falcon_create_special_buffer ( struct efab_nic *efab,
+ void *addr, unsigned int index ) {
+ efab_qword_t buf_desc;
+ unsigned long dma_addr;
+
+ memset ( addr, 0, 4096 );
+ dma_addr = virt_to_bus ( addr );
+ EFAB_ASSERT ( ( dma_addr & ( EFAB_BUF_ALIGN - 1 ) ) == 0 );
+ EFAB_POPULATE_QWORD_3 ( buf_desc,
+ FCN_IP_DAT_BUF_SIZE, FCN_IP_DAT_BUF_SIZE_4K,
+ FCN_BUF_ADR_FBUF, ( dma_addr >> 12 ),
+ FCN_BUF_OWNER_ID_FBUF, 0 );
+ falcon_write_sram_verify ( efab, &buf_desc, index );
+}
+
+/**
+ * Update event queue read pointer
+ *
+ */
+static void falcon_eventq_read_ack ( struct efab_nic *efab ) {
+ efab_dword_t reg;
+
+ EFAB_ASSERT ( efab->eventq_read_ptr < EFAB_EVQ_SIZE );
+
+ EFAB_POPULATE_DWORD_1 ( reg, FCN_EVQ_RPTR_DWORD,
+ efab->eventq_read_ptr );
+ falcon_writel ( efab, &reg, FCN_EVQ_RPTR_REG_KER_DWORD );
+}
+
+/**
+ * Reset device
+ *
+ */
+static int falcon_reset ( struct efab_nic *efab ) {
+ efab_oword_t glb_ctl_reg_ker;
+
+ /* Initiate software reset */
+ EFAB_POPULATE_OWORD_7 ( glb_ctl_reg_ker,
+ PCIE_CORE_RST_CTL, EXCLUDE_FROM_RESET,
+ PCIE_NSTCK_RST_CTL, EXCLUDE_FROM_RESET,
+ PCIE_SD_RST_CTL, EXCLUDE_FROM_RESET,
+ EE_RST_CTL, EXCLUDE_FROM_RESET,
+ PCIX_RST_CTL, EXCLUDE_FROM_RESET,
+ EXT_PHY_RST_DUR, 0x7 /* datasheet recommended */,
+ SWRST, 1 );
+
+ falcon_write ( efab, &glb_ctl_reg_ker, FCN_GLB_CTL_REG_KER );
+
+ /* Allow 20ms for reset */
+ mdelay ( 20 );
+
+ /* Check for device reset complete */
+ falcon_read ( efab, &glb_ctl_reg_ker, FCN_GLB_CTL_REG_KER );
+ if ( EFAB_OWORD_FIELD ( glb_ctl_reg_ker, SWRST ) != 0 ) {
+ EFAB_ERR ( "Reset failed\n" );
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Wait for SPI command completion
+ *
+ */
+static int falcon_spi_wait ( struct efab_nic *efab ) {
+ efab_oword_t reg;
+ int count;
+
+ count = 0;
+ do {
+ udelay ( 100 );
+ falcon_read ( efab, &reg, FCN_EE_SPI_HCMD_REG_KER );
+ if ( EFAB_OWORD_FIELD ( reg, FCN_EE_SPI_HCMD_CMD_EN ) == 0 )
+ return 1;
+ } while ( ++count < 1000 );
+ printf ( "Timed out waiting for SPI\n" );
+ return 0;
+}
+
+/**
+ * Perform SPI read/write
+ *
+ */
+static int falcon_spi_rw ( struct spi_bus *bus, struct spi_device *device,
+ unsigned int command, int address,
+ const void *data_out, void *data_in, size_t len ) {
+ struct efab_nic *efab = container_of ( bus, struct efab_nic, spi );
+ efab_oword_t reg;
+
+ /* Program address register */
+ EFAB_POPULATE_OWORD_1 ( reg, FCN_EE_SPI_HADR_ADR, address );
+ falcon_write ( efab, &reg, FCN_EE_SPI_HADR_REG_KER );
+
+ /* Program data register, if applicable */
+ if ( data_out ) {
+ memcpy ( &reg, data_out, len );
+ falcon_write ( efab, &reg, FCN_EE_SPI_HDATA_REG_KER );
+ }
+
+ /* Issue command */
+ EFAB_POPULATE_OWORD_7 ( reg,
+ FCN_EE_SPI_HCMD_CMD_EN, 1,
+ FCN_EE_SPI_HCMD_SF_SEL, device->slave,
+ FCN_EE_SPI_HCMD_DABCNT, len,
+ FCN_EE_SPI_HCMD_READ, ( data_out ?
+ FCN_EE_SPI_WRITE : FCN_EE_SPI_READ ),
+ FCN_EE_SPI_HCMD_DUBCNT, 0,
+ FCN_EE_SPI_HCMD_ADBCNT,
+ ( device->address_len / 8 ),
+ FCN_EE_SPI_HCMD_ENC, command );
+ falcon_write ( efab, &reg, FCN_EE_SPI_HCMD_REG_KER );
+
+ /* Wait for operation to complete */
+ if ( ! falcon_spi_wait ( efab ) )
+ return 0;
+
+ /* Read data, if applicable */
+ if ( data_in ) {
+ falcon_read ( efab, &reg, FCN_EE_SPI_HDATA_REG_KER );
+ memcpy ( data_in, &reg, len );
+ }
+
+ return 0;
+}
+
+/**
+ * Initialise SPI bus and devices
+ *
+ */
+static void falcon_init_spi ( struct efab_nic *efab ) {
+ efab_oword_t reg;
+ int eeprom_9bit;
+
+ /* Initialise SPI bus */
+ efab->spi.rw = falcon_spi_rw;
+ efab->falcon_eeprom.bus = &efab->spi;
+ efab->falcon_eeprom.slave = FCN_EE_SPI_EEPROM;
+ efab->falcon_flash.bus = &efab->spi;
+ efab->falcon_flash.slave = FCN_EE_SPI_FLASH;
+
+ /* Initialise flash if present */
+ if ( efab->has_flash ) {
+ DBG ( "Flash is present\n" );
+ init_at25f1024 ( &efab->falcon_flash );
+ }
+
+ /* Initialise EEPROM if present */
+ if ( efab->has_eeprom ) {
+ if ( efab->is_asic ) {
+ falcon_read ( efab, &reg, FCN_VPD_CONFIG_REG_KER );
+ eeprom_9bit = EFAB_OWORD_FIELD ( reg, FCN_VPD_9BIT );
+ } else {
+ eeprom_9bit = 1;
+ }
+ if ( eeprom_9bit ) {
+ DBG ( "Small EEPROM is present\n" );
+ init_at25040 ( &efab->falcon_eeprom );
+ } else {
+ DBG ( "Large EEPROM is present\n" );
+ init_mc25xx640 ( &efab->falcon_eeprom );
+ /* Falcon's SPI interface cannot support a block
+ size larger than 16, so forcibly reduce it
+ */
+ efab->falcon_eeprom.nvs.block_size = 16;
+ }
+ }
+}
+
+/** Offset of MAC address within EEPROM or Flash */
+#define FALCON_MAC_ADDRESS_OFFSET(port) ( 0x310 + 0x08 * (port) )
+
+static struct nvo_fragment falcon_eeprom_fragments[] = {
+ { 0x100, 0x100 },
+ { 0, 0 }
+};
+
+/**
+ * Read MAC address from EEPROM
+ *
+ */
+static int falcon_read_eeprom ( struct efab_nic *efab ) {
+ struct nvs_device *nvs;
+
+ /* Determine the NVS device containing the MAC address */
+ nvs = ( efab->has_flash ?
+ &efab->falcon_flash.nvs : &efab->falcon_eeprom.nvs );
+
+ return ( nvs_read ( nvs, FALCON_MAC_ADDRESS_OFFSET ( efab->port ),
+ efab->mac_addr, sizeof ( efab->mac_addr ) ) == 0 );
+}
+
+/** RX descriptor */
+typedef efab_qword_t falcon_rx_desc_t;
+
+/**
+ * Build RX descriptor
+ *
+ */
+static void falcon_build_rx_desc ( struct efab_nic *efab,
+ struct efab_rx_buf *rx_buf ) {
+ falcon_rx_desc_t *rxd;
+
+ rxd = ( ( falcon_rx_desc_t * ) efab->rxd ) + rx_buf->id;
+ EFAB_POPULATE_QWORD_2 ( *rxd,
+ FCN_RX_KER_BUF_SIZE, EFAB_DATA_BUF_SIZE,
+ FCN_RX_KER_BUF_ADR,
+ virt_to_bus ( rx_buf->addr ) );
+}
+
+/**
+ * Update RX descriptor write pointer
+ *
+ */
+static void falcon_notify_rx_desc ( struct efab_nic *efab ) {
+ efab_dword_t reg;
+
+ EFAB_POPULATE_DWORD_1 ( reg, FCN_RX_DESC_WPTR_DWORD,
+ efab->rx_write_ptr );
+ falcon_writel ( efab, &reg, FCN_RX_DESC_UPD_REG_KER_DWORD );
+}
+
+/** TX descriptor */
+typedef efab_qword_t falcon_tx_desc_t;
+
+/**
+ * Build TX descriptor
+ *
+ */
+static void falcon_build_tx_desc ( struct efab_nic *efab,
+ struct efab_tx_buf *tx_buf ) {
+ falcon_rx_desc_t *txd;
+
+ txd = ( ( falcon_rx_desc_t * ) efab->txd ) + tx_buf->id;
+ EFAB_POPULATE_QWORD_3 ( *txd,
+ FCN_TX_KER_PORT, efab->port,
+ FCN_TX_KER_BYTE_CNT, tx_buf->len,
+ FCN_TX_KER_BUF_ADR,
+ virt_to_bus ( tx_buf->addr ) );
+}
+
+/**
+ * Update TX descriptor write pointer
+ *
+ */
+static void falcon_notify_tx_desc ( struct efab_nic *efab ) {
+ efab_dword_t reg;
+
+ EFAB_POPULATE_DWORD_1 ( reg, FCN_TX_DESC_WPTR_DWORD,
+ efab->tx_write_ptr );
+ falcon_writel ( efab, &reg, FCN_TX_DESC_UPD_REG_KER_DWORD );
+}
+
+/** An event */
+typedef efab_qword_t falcon_event_t;
+
+/**
+ * See if an event is present
+ *
+ * @v event Falcon event structure
+ * @ret True An event is pending
+ * @ret False No event is pending
+ *
+ * We check both the high and low dword of the event for all ones. We
+ * wrote all ones when we cleared the event, and no valid event can
+ * have all ones in either its high or low dwords. This approach is
+ * robust against reordering.
+ *
+ * Note that using a single 64-bit comparison is incorrect; even
+ * though the CPU read will be atomic, the DMA write may not be.
+ */
+static inline int falcon_event_present ( falcon_event_t* event ) {
+ return ( ! ( EFAB_DWORD_IS_ALL_ONES ( event->dword[0] ) |
+ EFAB_DWORD_IS_ALL_ONES ( event->dword[1] ) ) );
+}
+
+/**
+ * Retrieve event from event queue
+ *
+ */
+static int falcon_fetch_event ( struct efab_nic *efab,
+ struct efab_event *event ) {
+ falcon_event_t *evt;
+ int ev_code;
+ int rx_port;
+
+ /* Check for event */
+ evt = ( ( falcon_event_t * ) efab->eventq ) + efab->eventq_read_ptr;
+ if ( !falcon_event_present ( evt ) ) {
+ /* No event */
+ return 0;
+ }
+
+ DBG ( "Event is " EFAB_QWORD_FMT "\n", EFAB_QWORD_VAL ( *evt ) );
+
+ /* Decode event */
+ ev_code = EFAB_QWORD_FIELD ( *evt, FCN_EV_CODE );
+ event->drop = 0;
+ switch ( ev_code ) {
+ case FCN_TX_IP_EV_DECODE:
+ event->type = EFAB_EV_TX;
+ break;
+ case FCN_RX_IP_EV_DECODE:
+ event->type = EFAB_EV_RX;
+ event->rx_id = EFAB_QWORD_FIELD ( *evt, FCN_RX_EV_DESC_PTR );
+ event->rx_len = EFAB_QWORD_FIELD ( *evt, FCN_RX_EV_BYTE_CNT );
+ event->drop = !EFAB_QWORD_FIELD ( *evt, FCN_RX_EV_PKT_OK );
+ rx_port = EFAB_QWORD_FIELD ( *evt, FCN_RX_PORT );
+ if ( rx_port != efab->port ) {
+ /* Ignore packets on the wrong port. We can't
+ * just set event->type = EFAB_EV_NONE,
+ * because then the descriptor ring won't get
+ * refilled.
+ */
+ event->rx_len = 0;
+ }
+ break;
+ case FCN_DRIVER_EV_DECODE:
+ /* Ignore start-of-day events */
+ event->type = EFAB_EV_NONE;
+ break;
+ default:
+ EFAB_ERR ( "Unknown event type %d data %08lx\n", ev_code,
+ EFAB_DWORD_FIELD ( *evt, EFAB_DWORD_0 ) );
+ event->type = EFAB_EV_NONE;
+ }
+
+ /* Clear event and any pending interrupts */
+ EFAB_SET_QWORD ( *evt );
+ falcon_writel ( efab, 0, FCN_INT_ACK_KER_REG );
+ udelay ( 10 );
+
+ /* Increment and update event queue read pointer */
+ efab->eventq_read_ptr = ( ( efab->eventq_read_ptr + 1 )
+ % EFAB_EVQ_SIZE );
+ falcon_eventq_read_ack ( efab );
+
+ return 1;
+}
+
+/**
+ * Enable/disable/generate interrupt
+ *
+ */
+static inline void falcon_interrupts ( struct efab_nic *efab, int enabled,
+ int force ) {
+ efab_oword_t int_en_reg_ker;
+
+ EFAB_POPULATE_OWORD_2 ( int_en_reg_ker,
+ FCN_KER_INT_KER, force,
+ FCN_DRV_INT_EN_KER, enabled );
+ falcon_write ( efab, &int_en_reg_ker, FCN_INT_EN_REG_KER );
+}
+
+/**
+ * Enable/disable interrupts
+ *
+ */
+static void falcon_mask_irq ( struct efab_nic *efab, int enabled ) {
+ falcon_interrupts ( efab, enabled, 0 );
+ if ( enabled ) {
+ /* Events won't trigger interrupts until we do this */
+ falcon_eventq_read_ack ( efab );
+ }
+}
+
+/**
+ * Generate interrupt
+ *
+ */
+static void falcon_generate_irq ( struct efab_nic *efab ) {
+ falcon_interrupts ( efab, 1, 1 );
+}
+
+
+/**
+ * Reconfigure MAC wrapper
+ *
+ */
+static void falcon_reconfigure_mac_wrapper ( struct efab_nic *efab ) {
+ efab_oword_t reg;
+ int link_speed;
+
+ if ( efab->link_options & LPA_10000 ) {
+ link_speed = 0x3;
+ } else if ( efab->link_options & LPA_1000 ) {
+ link_speed = 0x2;
+ } else if ( efab->link_options & LPA_100 ) {
+ link_speed = 0x1;
+ } else {
+ link_speed = 0x0;
+ }
+ EFAB_POPULATE_OWORD_5 ( reg,
+ FCN_MAC_XOFF_VAL, 0xffff /* datasheet */,
+ FCN_MAC_BCAD_ACPT, 1,
+ FCN_MAC_UC_PROM, 0,
+ FCN_MAC_LINK_STATUS, 1,
+ FCN_MAC_SPEED, link_speed );
+ falcon_write ( efab, &reg,
+ ( efab->port == 0 ?
+ FCN_MAC0_CTRL_REG_KER : FCN_MAC1_CTRL_REG_KER ) );
+
+ /* Disable flow-control (i.e. never generate pause frames) */
+ falcon_read ( efab, &reg, FCN_RX_CFG_REG_KER );
+ EFAB_SET_OWORD_FIELD ( reg, FCN_RX_XOFF_EN, 0 );
+ falcon_write ( efab, &reg, FCN_RX_CFG_REG_KER );
+}
+
+/**
+ * Write dword to a Falcon MAC register
+ *
+ */
+static void falcon_gmac_writel ( struct efab_nic *efab,
+ efab_dword_t *value, unsigned int mac_reg ) {
+ efab_oword_t temp;
+
+ EFAB_POPULATE_OWORD_1 ( temp, FCN_MAC_DATA,
+ EFAB_DWORD_FIELD ( *value, FCN_MAC_DATA ) );
+ falcon_write ( efab, &temp, FALCON_GMAC_REG ( efab, mac_reg ) );
+}
+
+/**
+ * Read dword from a Falcon GMAC register
+ *
+ */
+static void falcon_gmac_readl ( struct efab_nic *efab, efab_dword_t *value,
+ unsigned int mac_reg ) {
+ efab_oword_t temp;
+
+ falcon_read ( efab, &temp, FALCON_GMAC_REG ( efab, mac_reg ) );
+ EFAB_POPULATE_DWORD_1 ( *value, FCN_MAC_DATA,
+ EFAB_OWORD_FIELD ( temp, FCN_MAC_DATA ) );
+}
+
+/**
+ * Write dword to a Falcon XMAC register
+ *
+ */
+static void falcon_xmac_writel ( struct efab_nic *efab,
+ efab_dword_t *value, unsigned int mac_reg ) {
+ efab_oword_t temp;
+
+ EFAB_POPULATE_OWORD_1 ( temp, FCN_MAC_DATA,
+ EFAB_DWORD_FIELD ( *value, FCN_MAC_DATA ) );
+ falcon_write ( efab, &temp,
+ FALCON_XMAC_REG ( efab, mac_reg ) );
+}
+
+/**
+ * Read dword from a Falcon XMAC register
+ *
+ */
+static void falcon_xmac_readl ( struct efab_nic *efab,
+ efab_dword_t *value,
+ unsigned int mac_reg ) {
+ efab_oword_t temp;
+
+ falcon_read ( efab, &temp,
+ FALCON_XMAC_REG ( efab, mac_reg ) );
+ EFAB_POPULATE_DWORD_1 ( *value, FCN_MAC_DATA,
+ EFAB_OWORD_FIELD ( temp, FCN_MAC_DATA ) );
+}
+
+/**
+ * Initialise GMAC
+ *
+ */
+static int falcon_init_gmac ( struct efab_nic *efab ) {
+ static struct efab_mentormac_parameters falcon_mentormac_params = {
+ .gmf_cfgfrth = 0x12,
+ .gmf_cfgftth = 0x08,
+ .gmf_cfghwmft = 0x1c,
+ .gmf_cfghwm = 0x3f,
+ .gmf_cfglwm = 0xa,
+ };
+
+ /* Initialise PHY */
+ alaska_init ( efab );
+
+ /* check the link is up */
+ if ( !efab->link_up )
+ return 0;
+
+ /* Initialise MAC */
+ mentormac_init ( efab, &falcon_mentormac_params );
+
+ /* reconfigure the MAC wrapper */
+ falcon_reconfigure_mac_wrapper ( efab );
+
+ return 1;
+}
+
+/**
+ * Reset GMAC
+ *
+ */
+static int falcon_reset_gmac ( struct efab_nic *efab ) {
+ mentormac_reset ( efab );
+ return 1;
+}
+
+/**
+ * Reset XAUI/XGXS block
+ *
+ */
+static int falcon_reset_xaui ( struct efab_nic *efab )
+{
+ efab_dword_t reg;
+ int count;
+
+ EFAB_POPULATE_DWORD_1 ( reg, FCN_XX_RST_XX_EN, 1 );
+ efab->mac_op->mac_writel ( efab, &reg, FCN_XX_PWR_RST_REG_MAC );
+
+ for ( count = 0 ; count < 1000 ; count++ ) {
+ udelay ( 10 );
+ efab->mac_op->mac_readl ( efab, &reg,
+ FCN_XX_PWR_RST_REG_MAC );
+ if ( EFAB_DWORD_FIELD ( reg, FCN_XX_RST_XX_EN ) == 0 )
+ return 1;
+ }
+
+ /* an error of some kind */
+ return 0;
+}
+
+/**
+ * Reset 10G MAC connected to port
+ *
+ */
+static int falcon_reset_xmac ( struct efab_nic *efab ) {
+ efab_dword_t reg;
+ int count;
+
+ EFAB_POPULATE_DWORD_1 ( reg, FCN_XM_CORE_RST, 1 );
+ efab->mac_op->mac_writel ( efab, &reg, FCN_XM_GLB_CFG_REG_MAC );
+
+ for ( count = 0 ; count < 1000 ; count++ ) {
+ udelay ( 10 );
+ efab->mac_op->mac_readl ( efab, &reg,
+ FCN_XM_GLB_CFG_REG_MAC );
+ if ( EFAB_DWORD_FIELD ( reg, FCN_XM_CORE_RST ) == 0 )
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Get status of 10G link
+ *
+ */
+static int falcon_xaui_link_ok ( struct efab_nic *efab ) {
+ efab_dword_t reg;
+ int align_done;
+ int sync_status;
+ int link_ok = 0;
+
+ /* Read link status */
+ efab->mac_op->mac_readl ( efab, &reg, FCN_XX_CORE_STAT_REG_MAC );
+ align_done = EFAB_DWORD_FIELD ( reg, FCN_XX_ALIGN_DONE );
+ sync_status = EFAB_DWORD_FIELD ( reg, FCN_XX_SYNC_STAT );
+ if ( align_done && ( sync_status == FCN_XX_SYNC_STAT_DECODE_SYNCED ) ) {
+ link_ok = 1;
+ }
+
+ /* Clear link status ready for next read */
+ EFAB_SET_DWORD_FIELD ( reg, FCN_XX_COMMA_DET, FCN_XX_COMMA_DET_RESET );
+ efab->mac_op->mac_writel ( efab, &reg, FCN_XX_CORE_STAT_REG_MAC );
+
+ return link_ok;
+}
+
+/**
+ * Initialise XMAC
+ *
+ */
+static int falcon_init_xmac ( struct efab_nic *efab ) {
+ efab_dword_t reg;
+ int count;
+
+ if ( !falcon_reset_xmac ( efab ) ) {
+ EFAB_ERR ( "failed resetting XMAC\n" );
+ return 0;
+ }
+ if ( !falcon_reset_xaui ( efab ) ) {
+ EFAB_ERR ( "failed resetting XAUI\n");
+ return 0;
+ }
+
+ /* CX4 is always 10000FD only */
+ efab->link_options = LPA_10000FULL;
+
+ /* Configure MAC */
+ EFAB_POPULATE_DWORD_3 ( reg,
+ FCN_XM_RX_JUMBO_MODE, 1,
+ FCN_XM_TX_STAT_EN, 1,
+ FCN_XM_RX_STAT_EN, 1);
+ efab->mac_op->mac_writel ( efab, &reg, FCN_XM_GLB_CFG_REG_MAC );
+
+ /* Configure TX */
+ EFAB_POPULATE_DWORD_6 ( reg,
+ FCN_XM_TXEN, 1,
+ FCN_XM_TX_PRMBL, 1,
+ FCN_XM_AUTO_PAD, 1,
+ FCN_XM_TXCRC, 1,
+ FCN_XM_FCNTL, 1,
+ FCN_XM_IPG, 0x3 );
+ efab->mac_op->mac_writel ( efab, &reg, FCN_XM_TX_CFG_REG_MAC );
+
+ /* Configure RX */
+ EFAB_POPULATE_DWORD_3 ( reg,
+ FCN_XM_RXEN, 1,
+ FCN_XM_AUTO_DEPAD, 1,
+ FCN_XM_PASS_CRC_ERR, 1 );
+ efab->mac_op->mac_writel ( efab, &reg, FCN_XM_RX_CFG_REG_MAC );
+
+ /* Set frame length */
+ EFAB_POPULATE_DWORD_1 ( reg,
+ FCN_XM_MAX_RX_FRM_SIZE, ETH_FRAME_LEN );
+ efab->mac_op->mac_writel ( efab, &reg, FCN_XM_RX_PARAM_REG_MAC );
+ EFAB_POPULATE_DWORD_2 ( reg,
+ FCN_XM_MAX_TX_FRM_SIZE, ETH_FRAME_LEN,
+ FCN_XM_TX_JUMBO_MODE, 1 );
+ efab->mac_op->mac_writel ( efab, &reg, FCN_XM_TX_PARAM_REG_MAC );
+
+ /* Set MAC address */
+ EFAB_POPULATE_DWORD_4 ( reg,
+ FCN_XM_ADR_0, efab->mac_addr[0],
+ FCN_XM_ADR_1, efab->mac_addr[1],
+ FCN_XM_ADR_2, efab->mac_addr[2],
+ FCN_XM_ADR_3, efab->mac_addr[3] );
+ efab->mac_op->mac_writel ( efab, &reg, FCN_XM_ADR_LO_REG_MAC );
+ EFAB_POPULATE_DWORD_2 ( reg,
+ FCN_XM_ADR_4, efab->mac_addr[4],
+ FCN_XM_ADR_5, efab->mac_addr[5] );
+ efab->mac_op->mac_writel ( efab, &reg, FCN_XM_ADR_HI_REG_MAC );
+
+ /* Reconfigure MAC wrapper */
+ falcon_reconfigure_mac_wrapper ( efab );
+
+ /**
+ * Try resetting XAUI on its own waiting for the link
+ * to come up
+ */
+ for(count=0; count<5; count++) {
+ /* Check link status */
+ efab->link_up = falcon_xaui_link_ok ( efab );
+ if ( efab->link_up ) {
+ /**
+ * Print out a speed message since we don't have a PHY
+ */
+ EFAB_LOG ( "%dMbps %s-duplex\n",
+ ( efab->link_options & LPA_10000 ? 1000 :
+ ( efab->link_options & LPA_1000 ? 1000 :
+ ( efab->link_options & LPA_100 ? 100 : 10 ) ) ),
+ ( efab->link_options & LPA_DUPLEX ? "full" : "half" ) );
+ break;
+ }
+
+ if ( !falcon_reset_xaui ( efab ) ) {
+ EFAB_ERR ( "failed resetting xaui\n" );
+ return 0;
+ }
+ udelay(100);
+ }
+
+ return 1;
+}
+
+/**
+ * Wait for GMII access to complete
+ *
+ */
+static int falcon_gmii_wait ( struct efab_nic *efab ) {
+ efab_oword_t md_stat;
+ int count;
+
+ for ( count = 0 ; count < 1000 ; count++ ) {
+ udelay ( 10 );
+ falcon_read ( efab, &md_stat, FCN_MD_STAT_REG_KER );
+ if ( EFAB_OWORD_FIELD ( md_stat, FCN_MD_BSY ) == 0 )
+ return 1;
+ }
+ EFAB_ERR ( "Timed out waiting for GMII\n" );
+ return 0;
+}
+
+
+static struct efab_mac_operations falcon_xmac_operations = {
+ .mac_readl = falcon_xmac_readl,
+ .mac_writel = falcon_xmac_writel,
+ .init = falcon_init_xmac,
+ .reset = falcon_reset_xmac,
+};
+
+static struct efab_mac_operations falcon_gmac_operations = {
+ .mac_readl = falcon_gmac_readl,
+ .mac_writel = falcon_gmac_writel,
+ .init = falcon_init_gmac,
+ .reset = falcon_reset_gmac,
+};
+
+
+/**
+ * Initialise NIC
+ *
+ */
+static int falcon_init_nic ( struct efab_nic *efab ) {
+ efab_oword_t reg;
+ efab_dword_t timer_cmd;
+ int version, minor;
+
+ /* use card in internal SRAM mode */
+ falcon_read ( efab, &reg, FCN_NIC_STAT_REG );
+ EFAB_SET_OWORD_FIELD ( reg, ONCHIP_SRAM, 1 );
+ falcon_write ( efab, &reg, FCN_NIC_STAT_REG );
+ wmb();
+
+ /* identify FPGA/ASIC, and strapping mode */
+ falcon_read ( efab, &reg, ALTERA_BUILD_REG_KER );
+ version = EFAB_OWORD_FIELD ( reg, VER_ALL );
+ efab->is_asic = version ? 0 : 1;
+
+ if ( efab->is_asic ) {
+ falcon_read ( efab, &reg, FCN_NIC_STAT_REG );
+ if ( EFAB_OWORD_FIELD ( reg, STRAP_10G ) ) {
+ efab->is_10g = 1;
+ }
+ if ( EFAB_OWORD_FIELD ( reg, STRAP_DUAL_PORT ) ) {
+ efab->is_dual = 1;
+ }
+ }
+ else {
+ falcon_read ( efab, &reg, ALTERA_BUILD_REG_KER );
+ minor = EFAB_OWORD_FIELD ( reg, VER_MINOR );
+
+ if ( minor == 0x14 ) {
+ efab->is_10g = 1;
+ } else if ( minor == 0x13 ) {
+ efab->is_dual = 1;
+ }
+ }
+
+ DBG ( "NIC type: %s %dx%s\n",
+ efab->is_asic ? "ASIC" : "FPGA",
+ efab->is_dual ? 2 : 1,
+ efab->is_10g ? "10G" : "1G" );
+
+ /* patch in MAC operations */
+ if ( efab->is_10g )
+ efab->mac_op = &falcon_xmac_operations;
+ else
+ efab->mac_op = &falcon_gmac_operations;
+
+ if ( !efab->is_dual && ( efab->port == 1 ) ) {
+ /* device doesn't exist */
+ return 0;
+ }
+
+ /* determine EEPROM / FLASH */
+ if ( efab->is_asic ) {
+ falcon_read ( efab, &reg, FCN_NIC_STAT_REG );
+ efab->has_flash = EFAB_OWORD_FIELD ( reg, SF_PRST );
+ efab->has_eeprom = EFAB_OWORD_FIELD ( reg, EE_PRST );
+ } else {
+ falcon_read ( efab, &reg, FCN_GPIO_CTL_REG_KER );
+ efab->has_flash = EFAB_OWORD_FIELD ( reg, FCN_FLASH_PRESENT );
+ efab->has_eeprom = EFAB_OWORD_FIELD ( reg, FCN_EEPROM_PRESENT);
+ }
+ DBG ( "flash is %s, EEPROM is %s\n",
+ ( efab->has_flash ? "present" : "absent" ),
+ ( efab->has_eeprom ? "present" : "absent" ) );
+ falcon_init_spi ( efab );
+
+ /* Set up TX and RX descriptor caches in SRAM */
+ EFAB_POPULATE_OWORD_1 ( reg, FCN_SRM_TX_DC_BASE_ADR,
+ 0x130000 /* recommended in datasheet */ );
+ falcon_write ( efab, &reg, FCN_SRM_TX_DC_CFG_REG_KER );
+ EFAB_POPULATE_OWORD_1 ( reg, FCN_TX_DC_SIZE, 2 /* 32 descriptors */ );
+ falcon_write ( efab, &reg, FCN_TX_DC_CFG_REG_KER );
+ EFAB_POPULATE_OWORD_1 ( reg, FCN_SRM_RX_DC_BASE_ADR,
+ 0x100000 /* recommended in datasheet */ );
+ falcon_write ( efab, &reg, FCN_SRM_RX_DC_CFG_REG_KER );
+ EFAB_POPULATE_OWORD_1 ( reg, FCN_RX_DC_SIZE, 2 /* 32 descriptors */ );
+ falcon_write ( efab, &reg, FCN_RX_DC_CFG_REG_KER );
+
+ /* Set number of RSS CPUs */
+ EFAB_POPULATE_OWORD_1 ( reg, FCN_NUM_KER, 0 );
+ falcon_write ( efab, &reg, FCN_RX_FILTER_CTL_REG_KER );
+ udelay ( 1000 );
+
+ /* Reset the MAC */
+ mentormac_reset ( efab );
+
+ /* Set up event queue */
+ falcon_create_special_buffer ( efab, efab->eventq, FALCON_EVQ_ID );
+ /* Fill eventq with all ones ( empty events ) */
+ memset(efab->eventq, 0xff, 4096);
+ /* push eventq to card */
+ EFAB_POPULATE_OWORD_3 ( reg,
+ FCN_EVQ_EN, 1,
+ FCN_EVQ_SIZE, FCN_EVQ_SIZE_512,
+ FCN_EVQ_BUF_BASE_ID, FALCON_EVQ_ID );
+ falcon_write ( efab, &reg, FCN_EVQ_PTR_TBL_KER );
+ udelay ( 1000 );
+
+ /* Set timer register */
+ EFAB_POPULATE_DWORD_2 ( timer_cmd,
+ FCN_TIMER_MODE, FCN_TIMER_MODE_DIS,
+ FCN_TIMER_VAL, 0 );
+ falcon_writel ( efab, &timer_cmd, FCN_TIMER_CMD_REG_KER );
+ udelay ( 1000 );
+
+ /* Initialise event queue read pointer */
+ falcon_eventq_read_ack ( efab );
+
+ /* Set up TX descriptor ring */
+ falcon_create_special_buffer ( efab, efab->txd, FALCON_TXD_ID );
+ EFAB_POPULATE_OWORD_5 ( reg,
+ FCN_TX_DESCQ_EN, 1,
+ FCN_TX_DESCQ_BUF_BASE_ID, FALCON_TXD_ID,
+ FCN_TX_DESCQ_EVQ_ID, 0,
+ FCN_TX_DESCQ_SIZE, FCN_TX_DESCQ_SIZE_512,
+ FCN_TX_DESCQ_TYPE, 0 /* kernel queue */ );
+ falcon_write ( efab, &reg, FCN_TX_DESC_PTR_TBL_KER );
+
+ /* Set up RX descriptor ring */
+ falcon_create_special_buffer ( efab, efab->rxd, FALCON_RXD_ID );
+ EFAB_POPULATE_OWORD_6 ( reg,
+ FCN_RX_DESCQ_BUF_BASE_ID, FALCON_RXD_ID,
+ FCN_RX_DESCQ_EVQ_ID, 0,
+ FCN_RX_DESCQ_SIZE, FCN_RX_DESCQ_SIZE_512,
+ FCN_RX_DESCQ_TYPE, 0 /* kernel queue */,
+ FCN_RX_DESCQ_JUMBO, 1,
+ FCN_RX_DESCQ_EN, 1 );
+ falcon_write ( efab, &reg, FCN_RX_DESC_PTR_TBL_KER );
+
+ /* Program INT_ADR_REG_KER */
+ EFAB_POPULATE_OWORD_1 ( reg,
+ FCN_INT_ADR_KER,
+ virt_to_bus ( &efab->int_ker ) );
+ falcon_write ( efab, &reg, FCN_INT_ADR_REG_KER );
+ udelay ( 1000 );
+
+ /* Register non-volatile storage */
+ if ( efab->has_eeprom ) {
+ nvo_init ( &efab->nvo, &efab->falcon_eeprom.nvs,
+ falcon_eeprom_fragments, NULL /* hack */ );
+ if ( register_nvo ( &efab->nvo, NULL /* hack */ ) != 0 )
+ return 0;
+ }
+
+ return 1;
+}
+
+/** MDIO write */
+static void falcon_mdio_write ( struct efab_nic *efab, int location,
+ int value ) {
+ int phy_id = efab->port + 2;
+ efab_oword_t reg;
+
+ EFAB_TRACE ( "Writing GMII %d register %02x with %04x\n",
+ phy_id, location, value );
+
+ /* Check MII not currently being accessed */
+ if ( ! falcon_gmii_wait ( efab ) )
+ return;
+
+ /* Write the address registers */
+ EFAB_POPULATE_OWORD_1 ( reg, FCN_MD_PHY_ADR, 0 /* phy_id ? */ );
+ falcon_write ( efab, &reg, FCN_MD_PHY_ADR_REG_KER );
+ udelay ( 10 );
+ EFAB_POPULATE_OWORD_2 ( reg,
+ FCN_MD_PRT_ADR, phy_id,
+ FCN_MD_DEV_ADR, location );
+ falcon_write ( efab, &reg, FCN_MD_ID_REG_KER );
+ udelay ( 10 );
+
+ /* Write data */
+ EFAB_POPULATE_OWORD_1 ( reg, FCN_MD_TXD, value );
+ falcon_write ( efab, &reg, FCN_MD_TXD_REG_KER );
+ udelay ( 10 );
+ EFAB_POPULATE_OWORD_2 ( reg,
+ FCN_MD_WRC, 1,
+ FCN_MD_GC, 1 );
+ falcon_write ( efab, &reg, FCN_MD_CS_REG_KER );
+ udelay ( 10 );
+
+ /* Wait for data to be written */
+ falcon_gmii_wait ( efab );
+}
+
+/** MDIO read */
+static int falcon_mdio_read ( struct efab_nic *efab, int location ) {
+ int phy_id = efab->port + 2;
+ efab_oword_t reg;
+ int value;
+
+ /* Check MII not currently being accessed */
+ if ( ! falcon_gmii_wait ( efab ) )
+ return 0xffff;
+
+ /* Write the address registers */
+ EFAB_POPULATE_OWORD_1 ( reg, FCN_MD_PHY_ADR, 0 /* phy_id ? */ );
+ falcon_write ( efab, &reg, FCN_MD_PHY_ADR_REG_KER );
+ udelay ( 10 );
+ EFAB_POPULATE_OWORD_2 ( reg,
+ FCN_MD_PRT_ADR, phy_id,
+ FCN_MD_DEV_ADR, location );
+ falcon_write ( efab, &reg, FCN_MD_ID_REG_KER );
+ udelay ( 10 );
+
+ /* Request data to be read */
+ EFAB_POPULATE_OWORD_2 ( reg,
+ FCN_MD_RIC, 1,
+ FCN_MD_GC, 1 );
+ falcon_write ( efab, &reg, FCN_MD_CS_REG_KER );
+ udelay ( 10 );
+
+ /* Wait for data to become available */
+ falcon_gmii_wait ( efab );
+
+ /* Read the data */
+ falcon_read ( efab, &reg, FCN_MD_RXD_REG_KER );
+ value = EFAB_OWORD_FIELD ( reg, FCN_MD_RXD );
+
+ EFAB_TRACE ( "Read from GMII %d register %02x, got %04x\n",
+ phy_id, location, value );
+
+ return value;
+}
+
+static struct efab_operations falcon_operations = {
+ .get_membase = falcon_get_membase,
+ .reset = falcon_reset,
+ .init_nic = falcon_init_nic,
+ .read_eeprom = falcon_read_eeprom,
+ .build_rx_desc = falcon_build_rx_desc,
+ .notify_rx_desc = falcon_notify_rx_desc,
+ .build_tx_desc = falcon_build_tx_desc,
+ .notify_tx_desc = falcon_notify_tx_desc,
+ .fetch_event = falcon_fetch_event,
+ .mask_irq = falcon_mask_irq,
+ .generate_irq = falcon_generate_irq,
+ .mdio_write = falcon_mdio_write,
+ .mdio_read = falcon_mdio_read,
+};
+
+/**************************************************************************
+ *
+ * Etherfabric abstraction layer
+ *
+ **************************************************************************
+ */
+
+/**
+ * Push RX buffer to RXD ring
+ *
+ */
+static inline void efab_push_rx_buffer ( struct efab_nic *efab,
+ struct efab_rx_buf *rx_buf ) {
+ /* Create RX descriptor */
+ rx_buf->id = efab->rx_write_ptr;
+ efab->op->build_rx_desc ( efab, rx_buf );
+
+ /* Update RX write pointer */
+ efab->rx_write_ptr = ( efab->rx_write_ptr + 1 ) % EFAB_RXD_SIZE;
+ efab->op->notify_rx_desc ( efab );
+
+ DBG ( "Added RX id %x\n", rx_buf->id );
+}
+
+/**
+ * Push TX buffer to TXD ring
+ *
+ */
+static inline void efab_push_tx_buffer ( struct efab_nic *efab,
+ struct efab_tx_buf *tx_buf ) {
+ /* Create TX descriptor */
+ tx_buf->id = efab->tx_write_ptr;
+ efab->op->build_tx_desc ( efab, tx_buf );
+
+ /* Update TX write pointer */
+ efab->tx_write_ptr = ( efab->tx_write_ptr + 1 ) % EFAB_TXD_SIZE;
+ efab->op->notify_tx_desc ( efab );
+
+ DBG ( "Added TX id %x\n", tx_buf->id );
+}
+
+/**
+ * Initialise MAC and wait for link up
+ *
+ */
+static int efab_init_mac ( struct efab_nic *efab ) {
+ int count;
+
+ /* This can take several seconds */
+ EFAB_LOG ( "Waiting for link.." );
+ for ( count=0; count<5; count++ ) {
+ putchar ( '.' );
+
+ if ( ! efab->mac_op->init ( efab ) ) {
+ EFAB_ERR ( "Failed reinitialising MAC\n" );
+ return 0;
+ }
+ if ( efab->link_up ) {
+ /* PHY init printed the message for us */
+ return 1;
+ }
+ EFAB_ERR( "link is down" );
+ sleep ( 1 );
+ }
+ EFAB_ERR ( " timed initialising MAC\n " );
+
+ return 0;
+}
+
+/**
+ * Initialise NIC
+ *
+ */
+static int efab_init_nic ( struct efab_nic *efab ) {
+ int i;
+
+ /* Initialise NIC */
+ if ( ! efab->op->init_nic ( efab ) )
+ return 0;
+
+ /* Push RX descriptors */
+ for ( i = 0 ; i < EFAB_RX_BUFS ; i++ ) {
+ efab_push_rx_buffer ( efab, &efab->rx_bufs[i] );
+ }
+
+ /* Read MAC address from EEPROM */
+ if ( ! efab->op->read_eeprom ( efab ) )
+ return 0;
+
+ /* Initialise MAC and wait for link up */
+ if ( ! efab_init_mac ( efab ) )
+ return 0;
+
+ return 1;
+}
+
+/**************************************************************************
+ *
+ * Etherboot interface
+ *
+ **************************************************************************
+ */
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int etherfabric_poll ( struct nic *nic, int retrieve ) {
+ struct efab_nic *efab = nic->priv_data;
+ struct efab_event event;
+ static struct efab_rx_buf *rx_buf = NULL;
+ int i, drop = 0;
+
+ /* Process the event queue until we hit either a packet
+ * received event or an empty event slot.
+ */
+ while ( ( rx_buf == NULL ) &&
+ efab->op->fetch_event ( efab, &event ) ) {
+ drop = event.drop;
+ if ( event.type == EFAB_EV_TX ) {
+ /* TX completed - mark as done */
+ DBG ( "TX id %x complete\n",
+ efab->tx_buf.id );
+ } else if ( event.type == EFAB_EV_RX ) {
+ /* RX - find corresponding buffer */
+ for ( i = 0 ; i < EFAB_RX_BUFS ; i++ ) {
+ if ( efab->rx_bufs[i].id == event.rx_id ) {
+ rx_buf = &efab->rx_bufs[i];
+ rx_buf->len = event.rx_len;
+ DBG ( "RX id %x (len %x) received\n",
+ rx_buf->id, rx_buf->len );
+ break;
+ }
+ }
+ if ( ! rx_buf ) {
+ EFAB_ERR ( "Invalid RX ID %x\n", event.rx_id );
+ }
+ } else if ( event.type == EFAB_EV_NONE ) {
+ DBG ( "Ignorable event\n" );
+ } else {
+ DBG ( "Unknown event\n" );
+ }
+ }
+
+ /* If there is no packet, return 0 */
+ if ( ! rx_buf )
+ return 0;
+
+ /* drop this event if necessary */
+ if ( drop ) {
+ DBG( "discarding RX event\n" );
+ return 0;
+ }
+
+ /* If we don't want to retrieve it just yet, return 1 */
+ if ( ! retrieve )
+ return 1;
+
+ /* There seems to be a hardware race. The event can show up
+ * on the event FIFO before the DMA has completed, so we
+ * insert a tiny delay. If this proves unreliable, we should
+ * switch to using event DMA rather than the event FIFO, since
+ * event DMA ordering is guaranteed.
+ */
+ udelay ( 2 );
+
+ /* Copy packet contents */
+ nic->packetlen = rx_buf->len;
+ memcpy ( nic->packet, rx_buf->addr, nic->packetlen );
+
+ /* Give this buffer back to the NIC */
+ efab_push_rx_buffer ( efab, rx_buf );
+
+ /* Prepare to receive next packet */
+ rx_buf = NULL;
+
+ return 1;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void etherfabric_transmit ( struct nic *nic, const char *dest,
+ unsigned int type, unsigned int size,
+ const char *data ) {
+ struct efab_nic *efab = nic->priv_data;
+ unsigned int nstype = htons ( type );
+
+ /* Fill TX buffer, pad to ETH_ZLEN */
+ memcpy ( efab->tx_buf.addr, dest, ETH_ALEN );
+ memcpy ( efab->tx_buf.addr + ETH_ALEN, nic->node_addr, ETH_ALEN );
+ memcpy ( efab->tx_buf.addr + 2 * ETH_ALEN, &nstype, 2 );
+ memcpy ( efab->tx_buf.addr + ETH_HLEN, data, size );
+ size += ETH_HLEN;
+ while ( size < ETH_ZLEN ) {
+ efab->tx_buf.addr[size++] = '\0';
+ }
+ efab->tx_buf.len = size;
+
+ /* Push TX descriptor */
+ efab_push_tx_buffer ( efab, &efab->tx_buf );
+
+ /* Allow enough time for the packet to be transmitted. This
+ * is a temporary hack until we update to the new driver API.
+ */
+ udelay ( 20 );
+
+ return;
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void etherfabric_disable ( struct nic *nic ) {
+ struct efab_nic *efab = nic->priv_data;
+
+ efab->op->reset ( efab );
+ if ( efab->membase )
+ iounmap ( efab->membase );
+}
+
+/**************************************************************************
+IRQ - handle interrupts
+***************************************************************************/
+static void etherfabric_irq ( struct nic *nic, irq_action_t action ) {
+ struct efab_nic *efab = nic->priv_data;
+
+ switch ( action ) {
+ case DISABLE :
+ efab->op->mask_irq ( efab, 1 );
+ break;
+ case ENABLE :
+ efab->op->mask_irq ( efab, 0 );
+ break;
+ case FORCE :
+ /* Force NIC to generate a receive interrupt */
+ efab->op->generate_irq ( efab );
+ break;
+ }
+
+ return;
+}
+
+static struct nic_operations etherfabric_operations = {
+ .connect = dummy_connect,
+ .poll = etherfabric_poll,
+ .transmit = etherfabric_transmit,
+ .irq = etherfabric_irq,
+};
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int etherfabric_probe ( struct nic *nic, struct pci_device *pci ) {
+ static struct efab_nic efab;
+ static int nic_port = 1;
+ struct efab_buffers *buffers;
+ int i;
+
+ /* Set up our private data structure */
+ nic->priv_data = &efab;
+ memset ( &efab, 0, sizeof ( efab ) );
+ memset ( &efab_buffers, 0, sizeof ( efab_buffers ) );
+
+ /* Hook in appropriate operations table. Do this early. */
+ if ( pci->device == EF1002_DEVID ) {
+ efab.op = &ef1002_operations;
+ } else {
+ efab.op = &falcon_operations;
+ }
+
+ /* Initialise efab data structure */
+ efab.pci = pci;
+ buffers = ( ( struct efab_buffers * )
+ ( ( ( void * ) &efab_buffers ) +
+ ( - virt_to_bus ( &efab_buffers ) ) % EFAB_BUF_ALIGN ) );
+ efab.eventq = buffers->eventq;
+ efab.txd = buffers->txd;
+ efab.rxd = buffers->rxd;
+ efab.tx_buf.addr = buffers->tx_buf;
+ for ( i = 0 ; i < EFAB_RX_BUFS ; i++ ) {
+ efab.rx_bufs[i].addr = buffers->rx_buf[i];
+ }
+
+ /* Enable the PCI device */
+ adjust_pci_device ( pci );
+ nic->ioaddr = pci->ioaddr & ~3;
+ nic->irqno = pci->irq;
+
+ /* Get iobase/membase */
+ efab.iobase = nic->ioaddr;
+ efab.op->get_membase ( &efab );
+
+ /* Switch NIC ports (i.e. try different ports on each probe) */
+ nic_port = 1 - nic_port;
+ efab.port = nic_port;
+
+ /* Initialise hardware */
+ if ( ! efab_init_nic ( &efab ) )
+ return 0;
+ memcpy ( nic->node_addr, efab.mac_addr, ETH_ALEN );
+
+ /* point to NIC specific routines */
+ nic->nic_op = &etherfabric_operations;
+
+ return 1;
+}
+
+static struct pci_device_id etherfabric_nics[] = {
+PCI_ROM(0x1924, 0xC101, "ef1002", "EtherFabric EF1002"),
+PCI_ROM(0x1924, 0x0703, "falcon", "EtherFabric Falcon"),
+};
+
+PCI_DRIVER ( etherfabric_driver, etherfabric_nics, PCI_NO_CLASS );
+
+DRIVER ( "EFAB", nic_driver, pci_driver, etherfabric_driver,
+ etherfabric_probe, etherfabric_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/etherfabric.h b/gpxe/src/drivers/net/etherfabric.h
new file mode 100644
index 00000000..950f8de2
--- /dev/null
+++ b/gpxe/src/drivers/net/etherfabric.h
@@ -0,0 +1,551 @@
+/**************************************************************************
+ *
+ * GPL net driver for Level 5 Etherfabric network cards
+ *
+ * Written by Michael Brown <mbrown@fensystems.co.uk>
+ *
+ * Copyright Fen Systems Ltd. 2005
+ * Copyright Level 5 Networks Inc. 2005
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License (GPL), incorporated herein by
+ * reference. Drivers based on or derived from this code fall under
+ * the GPL and must retain the authorship, copyright and license
+ * notice. This file is not a complete program and may only be used
+ * when the entire operating system is licensed under the GPL.
+ *
+ **************************************************************************
+ */
+
+#ifndef EFAB_BITFIELD_H
+#define EFAB_BITFIELD_H
+
+/** @file
+ *
+ * Etherfabric bitfield access
+ *
+ * Etherfabric NICs make extensive use of bitfields up to 128 bits
+ * wide. Since there is no native 128-bit datatype on most systems,
+ * and since 64-bit datatypes are inefficient on 32-bit systems and
+ * vice versa, we wrap accesses in a way that uses the most efficient
+ * datatype.
+ *
+ * The NICs are PCI devices and therefore little-endian. Since most
+ * of the quantities that we deal with are DMAed to/from host memory,
+ * we define our datatypes (efab_oword_t, efab_qword_t and
+ * efab_dword_t) to be little-endian.
+ *
+ * In the less common case of using PIO for individual register
+ * writes, we construct the little-endian datatype in host memory and
+ * then use non-swapping equivalents of writel/writeq, rather than
+ * constructing a native-endian datatype and relying on the implicit
+ * byte-swapping done by writel/writeq. (We use a similar strategy
+ * for register reads.)
+ */
+
+/** Dummy field low bit number */
+#define EFAB_DUMMY_FIELD_LBN 0
+/** Dummy field width */
+#define EFAB_DUMMY_FIELD_WIDTH 0
+/** Dword 0 low bit number */
+#define EFAB_DWORD_0_LBN 0
+/** Dword 0 width */
+#define EFAB_DWORD_0_WIDTH 32
+/** Dword 1 low bit number */
+#define EFAB_DWORD_1_LBN 32
+/** Dword 1 width */
+#define EFAB_DWORD_1_WIDTH 32
+/** Dword 2 low bit number */
+#define EFAB_DWORD_2_LBN 64
+/** Dword 2 width */
+#define EFAB_DWORD_2_WIDTH 32
+/** Dword 3 low bit number */
+#define EFAB_DWORD_3_LBN 96
+/** Dword 3 width */
+#define EFAB_DWORD_3_WIDTH 32
+
+/** Specified attribute (e.g. LBN) of the specified field */
+#define EFAB_VAL(field,attribute) field ## _ ## attribute
+/** Low bit number of the specified field */
+#define EFAB_LOW_BIT( field ) EFAB_VAL ( field, LBN )
+/** Bit width of the specified field */
+#define EFAB_WIDTH( field ) EFAB_VAL ( field, WIDTH )
+/** High bit number of the specified field */
+#define EFAB_HIGH_BIT(field) ( EFAB_LOW_BIT(field) + EFAB_WIDTH(field) - 1 )
+/** Mask equal in width to the specified field.
+ *
+ * For example, a field with width 5 would have a mask of 0x1f.
+ *
+ * The maximum width mask that can be generated is 64 bits.
+ */
+#define EFAB_MASK64( field ) \
+ ( EFAB_WIDTH(field) == 64 ? ~( ( uint64_t ) 0 ) : \
+ ( ( ( ( ( uint64_t ) 1 ) << EFAB_WIDTH(field) ) ) - 1 ) )
+
+/** Mask equal in width to the specified field.
+ *
+ * For example, a field with width 5 would have a mask of 0x1f.
+ *
+ * The maximum width mask that can be generated is 32 bits. Use
+ * EFAB_MASK64 for higher width fields.
+ */
+#define EFAB_MASK32( field ) \
+ ( EFAB_WIDTH(field) == 32 ? ~( ( uint32_t ) 0 ) : \
+ ( ( ( ( ( uint32_t ) 1 ) << EFAB_WIDTH(field) ) ) - 1 ) )
+
+/** A doubleword (i.e. 4 byte) datatype
+ *
+ * This datatype is defined to be little-endian.
+ */
+typedef union efab_dword {
+ uint32_t u32[1];
+ uint32_t opaque; /* For bitwise operations between two efab_dwords */
+} efab_dword_t;
+
+/** A quadword (i.e. 8 byte) datatype
+ *
+ * This datatype is defined to be little-endian.
+ */
+typedef union efab_qword {
+ uint64_t u64[1];
+ uint32_t u32[2];
+ efab_dword_t dword[2];
+} efab_qword_t;
+
+/**
+ * An octword (eight-word, i.e. 16 byte) datatype
+ *
+ * This datatype is defined to be little-endian.
+ */
+typedef union efab_oword {
+ uint64_t u64[2];
+ efab_qword_t qword[2];
+ uint32_t u32[4];
+ efab_dword_t dword[4];
+} efab_oword_t;
+
+/** Format string for printing an efab_dword_t */
+#define EFAB_DWORD_FMT "%08x"
+
+/** Format string for printing an efab_qword_t */
+#define EFAB_QWORD_FMT "%08x:%08x"
+
+/** Format string for printing an efab_oword_t */
+#define EFAB_OWORD_FMT "%08x:%08x:%08x:%08x"
+
+/** printk parameters for printing an efab_dword_t */
+#define EFAB_DWORD_VAL(dword) \
+ ( ( unsigned int ) le32_to_cpu ( (dword).u32[0] ) )
+
+/** printk parameters for printing an efab_qword_t */
+#define EFAB_QWORD_VAL(qword) \
+ ( ( unsigned int ) le32_to_cpu ( (qword).u32[1] ) ), \
+ ( ( unsigned int ) le32_to_cpu ( (qword).u32[0] ) )
+
+/** printk parameters for printing an efab_oword_t */
+#define EFAB_OWORD_VAL(oword) \
+ ( ( unsigned int ) le32_to_cpu ( (oword).u32[3] ) ), \
+ ( ( unsigned int ) le32_to_cpu ( (oword).u32[2] ) ), \
+ ( ( unsigned int ) le32_to_cpu ( (oword).u32[1] ) ), \
+ ( ( unsigned int ) le32_to_cpu ( (oword).u32[0] ) )
+
+/**
+ * Extract bit field portion [low,high) from the native-endian element
+ * which contains bits [min,max).
+ *
+ * For example, suppose "element" represents the high 32 bits of a
+ * 64-bit value, and we wish to extract the bits belonging to the bit
+ * field occupying bits 28-45 of this 64-bit value.
+ *
+ * Then EFAB_EXTRACT ( element, 32, 63, 28, 45 ) would give
+ *
+ * ( element ) << 4
+ *
+ * The result will contain the relevant bits filled in in the range
+ * [0,high-low), with garbage in bits [high-low+1,...).
+ */
+#define EFAB_EXTRACT_NATIVE( native_element, min ,max ,low ,high ) \
+ ( ( ( low > max ) || ( high < min ) ) ? 0 : \
+ ( ( low > min ) ? \
+ ( (native_element) >> ( low - min ) ) : \
+ ( (native_element) << ( min - low ) ) ) )
+
+/**
+ * Extract bit field portion [low,high) from the 64-bit little-endian
+ * element which contains bits [min,max)
+ */
+#define EFAB_EXTRACT64( element, min, max, low, high ) \
+ EFAB_EXTRACT_NATIVE ( le64_to_cpu(element), min, max, low, high )
+
+/**
+ * Extract bit field portion [low,high) from the 32-bit little-endian
+ * element which contains bits [min,max)
+ */
+#define EFAB_EXTRACT32( element, min, max, low, high ) \
+ EFAB_EXTRACT_NATIVE ( le32_to_cpu(element), min, max, low, high )
+
+#define EFAB_EXTRACT_OWORD64( oword, low, high ) \
+ ( EFAB_EXTRACT64 ( (oword).u64[0], 0, 63, low, high ) | \
+ EFAB_EXTRACT64 ( (oword).u64[1], 64, 127, low, high ) )
+
+#define EFAB_EXTRACT_QWORD64( qword, low, high ) \
+ ( EFAB_EXTRACT64 ( (qword).u64[0], 0, 63, low, high ) )
+
+#define EFAB_EXTRACT_OWORD32( oword, low, high ) \
+ ( EFAB_EXTRACT32 ( (oword).u32[0], 0, 31, low, high ) | \
+ EFAB_EXTRACT32 ( (oword).u32[1], 32, 63, low, high ) | \
+ EFAB_EXTRACT32 ( (oword).u32[2], 64, 95, low, high ) | \
+ EFAB_EXTRACT32 ( (oword).u32[3], 96, 127, low, high ) )
+
+#define EFAB_EXTRACT_QWORD32( qword, low, high ) \
+ ( EFAB_EXTRACT32 ( (qword).u32[0], 0, 31, low, high ) | \
+ EFAB_EXTRACT32 ( (qword).u32[1], 32, 63, low, high ) )
+
+#define EFAB_EXTRACT_DWORD( dword, low, high ) \
+ ( EFAB_EXTRACT32 ( (dword).u32[0], 0, 31, low, high ) )
+
+#define EFAB_OWORD_FIELD64( oword, field ) \
+ ( EFAB_EXTRACT_OWORD64 ( oword, EFAB_LOW_BIT ( field ), \
+ EFAB_HIGH_BIT ( field ) ) & \
+ EFAB_MASK64 ( field ) )
+
+#define EFAB_QWORD_FIELD64( qword, field ) \
+ ( EFAB_EXTRACT_QWORD64 ( qword, EFAB_LOW_BIT ( field ), \
+ EFAB_HIGH_BIT ( field ) ) & \
+ EFAB_MASK64 ( field ) )
+
+#define EFAB_OWORD_FIELD32( oword, field ) \
+ ( EFAB_EXTRACT_OWORD32 ( oword, EFAB_LOW_BIT ( field ), \
+ EFAB_HIGH_BIT ( field ) ) & \
+ EFAB_MASK32 ( field ) )
+
+#define EFAB_QWORD_FIELD32( qword, field ) \
+ ( EFAB_EXTRACT_QWORD32 ( qword, EFAB_LOW_BIT ( field ), \
+ EFAB_HIGH_BIT ( field ) ) & \
+ EFAB_MASK32 ( field ) )
+
+#define EFAB_DWORD_FIELD( dword, field ) \
+ ( EFAB_EXTRACT_DWORD ( dword, EFAB_LOW_BIT ( field ), \
+ EFAB_HIGH_BIT ( field ) ) & \
+ EFAB_MASK32 ( field ) )
+
+#define EFAB_OWORD_IS_ZERO64( oword ) \
+ ( ! ( (oword).u64[0] || (oword).u64[1] ) )
+
+#define EFAB_QWORD_IS_ZERO64( qword ) \
+ ( ! ( (qword).u64[0] ) )
+
+#define EFAB_OWORD_IS_ZERO32( oword ) \
+ ( ! ( (oword).u32[0] || (oword).u32[1] || \
+ (oword).u32[2] || (oword).u32[3] ) )
+
+#define EFAB_QWORD_IS_ZERO32( qword ) \
+ ( ! ( (qword).u32[0] || (qword).u32[1] ) )
+
+#define EFAB_DWORD_IS_ZERO( dword ) \
+ ( ! ( (dword).u32[0] ) )
+
+#define EFAB_OWORD_IS_ALL_ONES64( oword ) \
+ ( ( (oword).u64[0] & (oword).u64[1] ) == ~( ( uint64_t ) 0 ) )
+
+#define EFAB_QWORD_IS_ALL_ONES64( qword ) \
+ ( (qword).u64[0] == ~( ( uint64_t ) 0 ) )
+
+#define EFAB_OWORD_IS_ALL_ONES32( oword ) \
+ ( ( (oword).u32[0] & (oword).u32[1] & \
+ (oword).u32[2] & (oword).u32[3] ) == ~( ( uint32_t ) 0 ) )
+
+#define EFAB_QWORD_IS_ALL_ONES32( qword ) \
+ ( ( (qword).u32[0] & (qword).u32[1] ) == ~( ( uint32_t ) 0 ) )
+
+#define EFAB_DWORD_IS_ALL_ONES( dword ) \
+ ( (dword).u32[0] == ~( ( uint32_t ) 0 ) )
+
+#if ( BITS_PER_LONG == 64 )
+#define EFAB_OWORD_FIELD EFAB_OWORD_FIELD64
+#define EFAB_QWORD_FIELD EFAB_QWORD_FIELD64
+#define EFAB_OWORD_IS_ZERO EFAB_OWORD_IS_ZERO64
+#define EFAB_QWORD_IS_ZERO EFAB_QWORD_IS_ZERO64
+#define EFAB_OWORD_IS_ALL_ONES EFAB_OWORD_IS_ALL_ONES64
+#define EFAB_QWORD_IS_ALL_ONES EFAB_QWORD_IS_ALL_ONES64
+#else
+#define EFAB_OWORD_FIELD EFAB_OWORD_FIELD32
+#define EFAB_QWORD_FIELD EFAB_QWORD_FIELD32
+#define EFAB_OWORD_IS_ZERO EFAB_OWORD_IS_ZERO32
+#define EFAB_QWORD_IS_ZERO EFAB_QWORD_IS_ZERO32
+#define EFAB_OWORD_IS_ALL_ONES EFAB_OWORD_IS_ALL_ONES32
+#define EFAB_QWORD_IS_ALL_ONES EFAB_QWORD_IS_ALL_ONES32
+#endif
+
+/**
+ * Construct bit field portion
+ *
+ * Creates the portion of the bit field [low,high) that lies within
+ * the range [min,max).
+ */
+#define EFAB_INSERT_NATIVE64( min, max, low, high, value ) \
+ ( ( ( low > max ) || ( high < min ) ) ? 0 : \
+ ( ( low > min ) ? \
+ ( ( ( uint64_t ) (value) ) << ( low - min ) ) : \
+ ( ( ( uint64_t ) (value) ) >> ( min - low ) ) ) )
+
+#define EFAB_INSERT_NATIVE32( min, max, low, high, value ) \
+ ( ( ( low > max ) || ( high < min ) ) ? 0 : \
+ ( ( low > min ) ? \
+ ( ( ( uint32_t ) (value) ) << ( low - min ) ) : \
+ ( ( ( uint32_t ) (value) ) >> ( min - low ) ) ) )
+
+#define EFAB_INSERT_NATIVE( min, max, low, high, value ) \
+ ( ( ( ( max - min ) >= 32 ) || \
+ ( ( high - low ) >= 32 ) ) \
+ ? EFAB_INSERT_NATIVE64 ( min, max, low, high, value ) \
+ : EFAB_INSERT_NATIVE32 ( min, max, low, high, value ) )
+
+/**
+ * Construct bit field portion
+ *
+ * Creates the portion of the named bit field that lies within the
+ * range [min,max).
+ */
+#define EFAB_INSERT_FIELD_NATIVE( min, max, field, value ) \
+ EFAB_INSERT_NATIVE ( min, max, EFAB_LOW_BIT ( field ), \
+ EFAB_HIGH_BIT ( field ), value )
+
+/**
+ * Construct bit field
+ *
+ * Creates the portion of the named bit fields that lie within the
+ * range [min,max).
+ */
+#define EFAB_INSERT_FIELDS_NATIVE( min, max, \
+ field1, value1, \
+ field2, value2, \
+ field3, value3, \
+ field4, value4, \
+ field5, value5, \
+ field6, value6, \
+ field7, value7, \
+ field8, value8, \
+ field9, value9, \
+ field10, value10 ) \
+ ( EFAB_INSERT_FIELD_NATIVE ( min, max, field1, value1 ) | \
+ EFAB_INSERT_FIELD_NATIVE ( min, max, field2, value2 ) | \
+ EFAB_INSERT_FIELD_NATIVE ( min, max, field3, value3 ) | \
+ EFAB_INSERT_FIELD_NATIVE ( min, max, field4, value4 ) | \
+ EFAB_INSERT_FIELD_NATIVE ( min, max, field5, value5 ) | \
+ EFAB_INSERT_FIELD_NATIVE ( min, max, field6, value6 ) | \
+ EFAB_INSERT_FIELD_NATIVE ( min, max, field7, value7 ) | \
+ EFAB_INSERT_FIELD_NATIVE ( min, max, field8, value8 ) | \
+ EFAB_INSERT_FIELD_NATIVE ( min, max, field9, value9 ) | \
+ EFAB_INSERT_FIELD_NATIVE ( min, max, field10, value10 ) )
+
+#define EFAB_INSERT_FIELDS64( ... ) \
+ cpu_to_le64 ( EFAB_INSERT_FIELDS_NATIVE ( __VA_ARGS__ ) )
+
+#define EFAB_INSERT_FIELDS32( ... ) \
+ cpu_to_le32 ( EFAB_INSERT_FIELDS_NATIVE ( __VA_ARGS__ ) )
+
+#define EFAB_POPULATE_OWORD64( oword, ... ) do { \
+ (oword).u64[0] = EFAB_INSERT_FIELDS64 ( 0, 63, __VA_ARGS__ );\
+ (oword).u64[1] = EFAB_INSERT_FIELDS64 ( 64, 127, __VA_ARGS__ );\
+ } while ( 0 )
+
+#define EFAB_POPULATE_QWORD64( qword, ... ) do { \
+ (qword).u64[0] = EFAB_INSERT_FIELDS64 ( 0, 63, __VA_ARGS__ );\
+ } while ( 0 )
+
+#define EFAB_POPULATE_OWORD32( oword, ... ) do { \
+ (oword).u32[0] = EFAB_INSERT_FIELDS32 ( 0, 31, __VA_ARGS__ );\
+ (oword).u32[1] = EFAB_INSERT_FIELDS32 ( 32, 63, __VA_ARGS__ );\
+ (oword).u32[2] = EFAB_INSERT_FIELDS32 ( 64, 95, __VA_ARGS__ );\
+ (oword).u32[3] = EFAB_INSERT_FIELDS32 ( 96, 127, __VA_ARGS__ );\
+ } while ( 0 )
+
+#define EFAB_POPULATE_QWORD32( qword, ... ) do { \
+ (qword).u32[0] = EFAB_INSERT_FIELDS32 ( 0, 31, __VA_ARGS__ );\
+ (qword).u32[1] = EFAB_INSERT_FIELDS32 ( 32, 63, __VA_ARGS__ );\
+ } while ( 0 )
+
+#define EFAB_POPULATE_DWORD( dword, ... ) do { \
+ (dword).u32[0] = EFAB_INSERT_FIELDS32 ( 0, 31, __VA_ARGS__ );\
+ } while ( 0 )
+
+#if ( BITS_PER_LONG == 64 )
+#define EFAB_POPULATE_OWORD EFAB_POPULATE_OWORD64
+#define EFAB_POPULATE_QWORD EFAB_POPULATE_QWORD64
+#else
+#define EFAB_POPULATE_OWORD EFAB_POPULATE_OWORD32
+#define EFAB_POPULATE_QWORD EFAB_POPULATE_QWORD32
+#endif
+
+/* Populate an octword field with various numbers of arguments */
+#define EFAB_POPULATE_OWORD_10 EFAB_POPULATE_OWORD
+#define EFAB_POPULATE_OWORD_9( oword, ... ) \
+ EFAB_POPULATE_OWORD_10 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_OWORD_8( oword, ... ) \
+ EFAB_POPULATE_OWORD_9 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_OWORD_7( oword, ... ) \
+ EFAB_POPULATE_OWORD_8 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_OWORD_6( oword, ... ) \
+ EFAB_POPULATE_OWORD_7 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_OWORD_5( oword, ... ) \
+ EFAB_POPULATE_OWORD_6 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_OWORD_4( oword, ... ) \
+ EFAB_POPULATE_OWORD_5 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_OWORD_3( oword, ... ) \
+ EFAB_POPULATE_OWORD_4 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_OWORD_2( oword, ... ) \
+ EFAB_POPULATE_OWORD_3 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_OWORD_1( oword, ... ) \
+ EFAB_POPULATE_OWORD_2 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_ZERO_OWORD( oword ) \
+ EFAB_POPULATE_OWORD_1 ( oword, EFAB_DUMMY_FIELD, 0 )
+#define EFAB_SET_OWORD( oword ) \
+ EFAB_POPULATE_OWORD_4 ( oword, \
+ EFAB_DWORD_0, 0xffffffff, \
+ EFAB_DWORD_1, 0xffffffff, \
+ EFAB_DWORD_2, 0xffffffff, \
+ EFAB_DWORD_3, 0xffffffff )
+
+/* Populate a quadword field with various numbers of arguments */
+#define EFAB_POPULATE_QWORD_10 EFAB_POPULATE_QWORD
+#define EFAB_POPULATE_QWORD_9( qword, ... ) \
+ EFAB_POPULATE_QWORD_10 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_QWORD_8( qword, ... ) \
+ EFAB_POPULATE_QWORD_9 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_QWORD_7( qword, ... ) \
+ EFAB_POPULATE_QWORD_8 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_QWORD_6( qword, ... ) \
+ EFAB_POPULATE_QWORD_7 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_QWORD_5( qword, ... ) \
+ EFAB_POPULATE_QWORD_6 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_QWORD_4( qword, ... ) \
+ EFAB_POPULATE_QWORD_5 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_QWORD_3( qword, ... ) \
+ EFAB_POPULATE_QWORD_4 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_QWORD_2( qword, ... ) \
+ EFAB_POPULATE_QWORD_3 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_QWORD_1( qword, ... ) \
+ EFAB_POPULATE_QWORD_2 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_ZERO_QWORD( qword ) \
+ EFAB_POPULATE_QWORD_1 ( qword, EFAB_DUMMY_FIELD, 0 )
+#define EFAB_SET_QWORD( qword ) \
+ EFAB_POPULATE_QWORD_2 ( qword, \
+ EFAB_DWORD_0, 0xffffffff, \
+ EFAB_DWORD_1, 0xffffffff )
+
+/* Populate a dword field with various numbers of arguments */
+#define EFAB_POPULATE_DWORD_10 EFAB_POPULATE_DWORD
+#define EFAB_POPULATE_DWORD_9( dword, ... ) \
+ EFAB_POPULATE_DWORD_10 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_DWORD_8( dword, ... ) \
+ EFAB_POPULATE_DWORD_9 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_DWORD_7( dword, ... ) \
+ EFAB_POPULATE_DWORD_8 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_DWORD_6( dword, ... ) \
+ EFAB_POPULATE_DWORD_7 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_DWORD_5( dword, ... ) \
+ EFAB_POPULATE_DWORD_6 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_DWORD_4( dword, ... ) \
+ EFAB_POPULATE_DWORD_5 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_DWORD_3( dword, ... ) \
+ EFAB_POPULATE_DWORD_4 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_DWORD_2( dword, ... ) \
+ EFAB_POPULATE_DWORD_3 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_POPULATE_DWORD_1( dword, ... ) \
+ EFAB_POPULATE_DWORD_2 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ )
+#define EFAB_ZERO_DWORD( dword ) \
+ EFAB_POPULATE_DWORD_1 ( dword, EFAB_DUMMY_FIELD, 0 )
+#define EFAB_SET_DWORD( dword ) \
+ EFAB_POPULATE_DWORD_1 ( dword, EFAB_DWORD_0, 0xffffffff )
+
+/*
+ * Modify a named field within an already-populated structure. Used
+ * for read-modify-write operations.
+ *
+ */
+
+#define EFAB_INSERT_FIELD64( ... ) \
+ cpu_to_le64 ( EFAB_INSERT_FIELD_NATIVE ( __VA_ARGS__ ) )
+
+#define EFAB_INSERT_FIELD32( ... ) \
+ cpu_to_le32 ( EFAB_INSERT_FIELD_NATIVE ( __VA_ARGS__ ) )
+
+#define EFAB_INPLACE_MASK64( min, max, field ) \
+ EFAB_INSERT_FIELD64 ( min, max, field, EFAB_MASK64 ( field ) )
+
+#define EFAB_INPLACE_MASK32( min, max, field ) \
+ EFAB_INSERT_FIELD32 ( min, max, field, EFAB_MASK32 ( field ) )
+
+#define EFAB_SET_OWORD_FIELD64( oword, field, value ) do { \
+ (oword).u64[0] = ( ( (oword).u64[0] \
+ & ~EFAB_INPLACE_MASK64 ( 0, 63, field ) ) \
+ | EFAB_INSERT_FIELD64 ( 0, 63, field, value ) ); \
+ (oword).u64[1] = ( ( (oword).u64[1] \
+ & ~EFAB_INPLACE_MASK64 ( 64, 127, field ) ) \
+ | EFAB_INSERT_FIELD64 ( 64, 127, field, value ) ); \
+ } while ( 0 )
+
+#define EFAB_SET_QWORD_FIELD64( qword, field, value ) do { \
+ (qword).u64[0] = ( ( (qword).u64[0] \
+ & ~EFAB_INPLACE_MASK64 ( 0, 63, field ) ) \
+ | EFAB_INSERT_FIELD64 ( 0, 63, field, value ) ); \
+ } while ( 0 )
+
+#define EFAB_SET_OWORD_FIELD32( oword, field, value ) do { \
+ (oword).u32[0] = ( ( (oword).u32[0] \
+ & ~EFAB_INPLACE_MASK32 ( 0, 31, field ) ) \
+ | EFAB_INSERT_FIELD32 ( 0, 31, field, value ) ); \
+ (oword).u32[1] = ( ( (oword).u32[1] \
+ & ~EFAB_INPLACE_MASK32 ( 32, 63, field ) ) \
+ | EFAB_INSERT_FIELD32 ( 32, 63, field, value ) ); \
+ (oword).u32[2] = ( ( (oword).u32[2] \
+ & ~EFAB_INPLACE_MASK32 ( 64, 95, field ) ) \
+ | EFAB_INSERT_FIELD32 ( 64, 95, field, value ) ); \
+ (oword).u32[3] = ( ( (oword).u32[3] \
+ & ~EFAB_INPLACE_MASK32 ( 96, 127, field ) ) \
+ | EFAB_INSERT_FIELD32 ( 96, 127, field, value ) ); \
+ } while ( 0 )
+
+#define EFAB_SET_QWORD_FIELD32( qword, field, value ) do { \
+ (qword).u32[0] = ( ( (qword).u32[0] \
+ & ~EFAB_INPLACE_MASK32 ( 0, 31, field ) ) \
+ | EFAB_INSERT_FIELD32 ( 0, 31, field, value ) ); \
+ (qword).u32[1] = ( ( (qword).u32[1] \
+ & ~EFAB_INPLACE_MASK32 ( 32, 63, field ) ) \
+ | EFAB_INSERT_FIELD32 ( 32, 63, field, value ) ); \
+ } while ( 0 )
+
+#define EFAB_SET_DWORD_FIELD( dword, field, value ) do { \
+ (dword).u32[0] = ( ( (dword).u32[0] \
+ & ~EFAB_INPLACE_MASK32 ( 0, 31, field ) ) \
+ | EFAB_INSERT_FIELD32 ( 0, 31, field, value ) ); \
+ } while ( 0 )
+
+#if ( BITS_PER_LONG == 64 )
+#define EFAB_SET_OWORD_FIELD EFAB_SET_OWORD_FIELD64
+#define EFAB_SET_QWORD_FIELD EFAB_SET_QWORD_FIELD64
+#else
+#define EFAB_SET_OWORD_FIELD EFAB_SET_OWORD_FIELD32
+#define EFAB_SET_QWORD_FIELD EFAB_SET_QWORD_FIELD32
+#endif
+
+/* Used to avoid compiler warnings about shift range exceeding width
+ * of the data types when dma_addr_t is only 32 bits wide.
+ */
+#define DMA_ADDR_T_WIDTH ( 8 * sizeof ( dma_addr_t ) )
+#define EFAB_DMA_TYPE_WIDTH( width ) \
+ ( ( (width) < DMA_ADDR_T_WIDTH ) ? (width) : DMA_ADDR_T_WIDTH )
+#define EFAB_DMA_MAX_MASK ( ( DMA_ADDR_T_WIDTH == 64 ) ? \
+ ~( ( uint64_t ) 0 ) : ~( ( uint32_t ) 0 ) )
+#define EFAB_DMA_MASK(mask) ( (mask) & EFAB_DMA_MAX_MASK )
+
+#endif /* EFAB_BITFIELD_H */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/forcedeth.c b/gpxe/src/drivers/net/forcedeth.c
new file mode 100644
index 00000000..f6195885
--- /dev/null
+++ b/gpxe/src/drivers/net/forcedeth.c
@@ -0,0 +1,1436 @@
+/**************************************************************************
+* forcedeth.c -- Etherboot device driver for the NVIDIA nForce
+* media access controllers.
+*
+* Note: This driver is based on the Linux driver that was based on
+* a cleanroom reimplementation which was based on reverse
+* engineered documentation written by Carl-Daniel Hailfinger
+* and Andrew de Quincey. It's neither supported nor endorsed
+* by NVIDIA Corp. Use at your own risk.
+*
+* Written 2004 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* forcedeth: Ethernet driver for NVIDIA nForce media access controllers:
+*
+* (C) 2003 Manfred Spraul
+* See Linux Driver for full information
+*
+* Linux Driver Version 0.30, 25 Sep 2004
+* Linux Kernel 2.6.10
+*
+*
+* REVISION HISTORY:
+* ================
+* v1.0 01-31-2004 timlegge Initial port of Linux driver
+* v1.1 02-03-2004 timlegge Large Clean up, first release
+* v1.2 05-14-2005 timlegge Add Linux 0.22 to .030 features
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include <gpxe/pci.h>
+/* Include timer support functions */
+#include <gpxe/ethernet.h>
+#include "mii.h"
+
+#define drv_version "v1.2"
+#define drv_date "05-14-2005"
+
+//#define TFTM_DEBUG
+#ifdef TFTM_DEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+#define ETH_DATA_LEN 1500
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+static unsigned long BASE;
+/* NIC specific static variables go here */
+#define PCI_DEVICE_ID_NVIDIA_NVENET_1 0x01c3
+#define PCI_DEVICE_ID_NVIDIA_NVENET_2 0x0066
+#define PCI_DEVICE_ID_NVIDIA_NVENET_4 0x0086
+#define PCI_DEVICE_ID_NVIDIA_NVENET_5 0x008c
+#define PCI_DEVICE_ID_NVIDIA_NVENET_3 0x00d6
+#define PCI_DEVICE_ID_NVIDIA_NVENET_7 0x00df
+#define PCI_DEVICE_ID_NVIDIA_NVENET_6 0x00e6
+#define PCI_DEVICE_ID_NVIDIA_NVENET_8 0x0056
+#define PCI_DEVICE_ID_NVIDIA_NVENET_9 0x0057
+#define PCI_DEVICE_ID_NVIDIA_NVENET_10 0x0037
+#define PCI_DEVICE_ID_NVIDIA_NVENET_11 0x0038
+
+
+/*
+ * Hardware access:
+ */
+
+#define DEV_NEED_LASTPACKET1 0x0001 /* set LASTPACKET1 in tx flags */
+#define DEV_IRQMASK_1 0x0002 /* use NVREG_IRQMASK_WANTED_1 for irq mask */
+#define DEV_IRQMASK_2 0x0004 /* use NVREG_IRQMASK_WANTED_2 for irq mask */
+#define DEV_NEED_TIMERIRQ 0x0008 /* set the timer irq flag in the irq mask */
+#define DEV_NEED_LINKTIMER 0x0010 /* poll link settings. Relies on the timer irq */
+
+enum {
+ NvRegIrqStatus = 0x000,
+#define NVREG_IRQSTAT_MIIEVENT 0040
+#define NVREG_IRQSTAT_MASK 0x1ff
+ NvRegIrqMask = 0x004,
+#define NVREG_IRQ_RX_ERROR 0x0001
+#define NVREG_IRQ_RX 0x0002
+#define NVREG_IRQ_RX_NOBUF 0x0004
+#define NVREG_IRQ_TX_ERR 0x0008
+#define NVREG_IRQ_TX2 0x0010
+#define NVREG_IRQ_TIMER 0x0020
+#define NVREG_IRQ_LINK 0x0040
+#define NVREG_IRQ_TX1 0x0100
+#define NVREG_IRQMASK_WANTED_1 0x005f
+#define NVREG_IRQMASK_WANTED_2 0x0147
+#define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1))
+
+ NvRegUnknownSetupReg6 = 0x008,
+#define NVREG_UNKSETUP6_VAL 3
+
+/*
+ * NVREG_POLL_DEFAULT is the interval length of the timer source on the nic
+ * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
+ */
+ NvRegPollingInterval = 0x00c,
+#define NVREG_POLL_DEFAULT 970
+ NvRegMisc1 = 0x080,
+#define NVREG_MISC1_HD 0x02
+#define NVREG_MISC1_FORCE 0x3b0f3c
+
+ NvRegTransmitterControl = 0x084,
+#define NVREG_XMITCTL_START 0x01
+ NvRegTransmitterStatus = 0x088,
+#define NVREG_XMITSTAT_BUSY 0x01
+
+ NvRegPacketFilterFlags = 0x8c,
+#define NVREG_PFF_ALWAYS 0x7F0008
+#define NVREG_PFF_PROMISC 0x80
+#define NVREG_PFF_MYADDR 0x20
+
+ NvRegOffloadConfig = 0x90,
+#define NVREG_OFFLOAD_HOMEPHY 0x601
+#define NVREG_OFFLOAD_NORMAL RX_NIC_BUFSIZE
+ NvRegReceiverControl = 0x094,
+#define NVREG_RCVCTL_START 0x01
+ NvRegReceiverStatus = 0x98,
+#define NVREG_RCVSTAT_BUSY 0x01
+
+ NvRegRandomSeed = 0x9c,
+#define NVREG_RNDSEED_MASK 0x00ff
+#define NVREG_RNDSEED_FORCE 0x7f00
+#define NVREG_RNDSEED_FORCE2 0x2d00
+#define NVREG_RNDSEED_FORCE3 0x7400
+
+ NvRegUnknownSetupReg1 = 0xA0,
+#define NVREG_UNKSETUP1_VAL 0x16070f
+ NvRegUnknownSetupReg2 = 0xA4,
+#define NVREG_UNKSETUP2_VAL 0x16
+ NvRegMacAddrA = 0xA8,
+ NvRegMacAddrB = 0xAC,
+ NvRegMulticastAddrA = 0xB0,
+#define NVREG_MCASTADDRA_FORCE 0x01
+ NvRegMulticastAddrB = 0xB4,
+ NvRegMulticastMaskA = 0xB8,
+ NvRegMulticastMaskB = 0xBC,
+
+ NvRegPhyInterface = 0xC0,
+#define PHY_RGMII 0x10000000
+
+ NvRegTxRingPhysAddr = 0x100,
+ NvRegRxRingPhysAddr = 0x104,
+ NvRegRingSizes = 0x108,
+#define NVREG_RINGSZ_TXSHIFT 0
+#define NVREG_RINGSZ_RXSHIFT 16
+ NvRegUnknownTransmitterReg = 0x10c,
+ NvRegLinkSpeed = 0x110,
+#define NVREG_LINKSPEED_FORCE 0x10000
+#define NVREG_LINKSPEED_10 1000
+#define NVREG_LINKSPEED_100 100
+#define NVREG_LINKSPEED_1000 50
+ NvRegUnknownSetupReg5 = 0x130,
+#define NVREG_UNKSETUP5_BIT31 (1<<31)
+ NvRegUnknownSetupReg3 = 0x13c,
+#define NVREG_UNKSETUP3_VAL1 0x200010
+ NvRegTxRxControl = 0x144,
+#define NVREG_TXRXCTL_KICK 0x0001
+#define NVREG_TXRXCTL_BIT1 0x0002
+#define NVREG_TXRXCTL_BIT2 0x0004
+#define NVREG_TXRXCTL_IDLE 0x0008
+#define NVREG_TXRXCTL_RESET 0x0010
+#define NVREG_TXRXCTL_RXCHECK 0x0400
+ NvRegMIIStatus = 0x180,
+#define NVREG_MIISTAT_ERROR 0x0001
+#define NVREG_MIISTAT_LINKCHANGE 0x0008
+#define NVREG_MIISTAT_MASK 0x000f
+#define NVREG_MIISTAT_MASK2 0x000f
+ NvRegUnknownSetupReg4 = 0x184,
+#define NVREG_UNKSETUP4_VAL 8
+
+ NvRegAdapterControl = 0x188,
+#define NVREG_ADAPTCTL_START 0x02
+#define NVREG_ADAPTCTL_LINKUP 0x04
+#define NVREG_ADAPTCTL_PHYVALID 0x40000
+#define NVREG_ADAPTCTL_RUNNING 0x100000
+#define NVREG_ADAPTCTL_PHYSHIFT 24
+ NvRegMIISpeed = 0x18c,
+#define NVREG_MIISPEED_BIT8 (1<<8)
+#define NVREG_MIIDELAY 5
+ NvRegMIIControl = 0x190,
+#define NVREG_MIICTL_INUSE 0x08000
+#define NVREG_MIICTL_WRITE 0x00400
+#define NVREG_MIICTL_ADDRSHIFT 5
+ NvRegMIIData = 0x194,
+ NvRegWakeUpFlags = 0x200,
+#define NVREG_WAKEUPFLAGS_VAL 0x7770
+#define NVREG_WAKEUPFLAGS_BUSYSHIFT 24
+#define NVREG_WAKEUPFLAGS_ENABLESHIFT 16
+#define NVREG_WAKEUPFLAGS_D3SHIFT 12
+#define NVREG_WAKEUPFLAGS_D2SHIFT 8
+#define NVREG_WAKEUPFLAGS_D1SHIFT 4
+#define NVREG_WAKEUPFLAGS_D0SHIFT 0
+#define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT 0x01
+#define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT 0x02
+#define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE 0x04
+#define NVREG_WAKEUPFLAGS_ENABLE 0x1111
+
+ NvRegPatternCRC = 0x204,
+ NvRegPatternMask = 0x208,
+ NvRegPowerCap = 0x268,
+#define NVREG_POWERCAP_D3SUPP (1<<30)
+#define NVREG_POWERCAP_D2SUPP (1<<26)
+#define NVREG_POWERCAP_D1SUPP (1<<25)
+ NvRegPowerState = 0x26c,
+#define NVREG_POWERSTATE_POWEREDUP 0x8000
+#define NVREG_POWERSTATE_VALID 0x0100
+#define NVREG_POWERSTATE_MASK 0x0003
+#define NVREG_POWERSTATE_D0 0x0000
+#define NVREG_POWERSTATE_D1 0x0001
+#define NVREG_POWERSTATE_D2 0x0002
+#define NVREG_POWERSTATE_D3 0x0003
+};
+
+#define FLAG_MASK_V1 0xffff0000
+#define FLAG_MASK_V2 0xffffc000
+#define LEN_MASK_V1 (0xffffffff ^ FLAG_MASK_V1)
+#define LEN_MASK_V2 (0xffffffff ^ FLAG_MASK_V2)
+
+#define NV_TX_LASTPACKET (1<<16)
+#define NV_TX_RETRYERROR (1<<19)
+#define NV_TX_LASTPACKET1 (1<<24)
+#define NV_TX_DEFERRED (1<<26)
+#define NV_TX_CARRIERLOST (1<<27)
+#define NV_TX_LATECOLLISION (1<<28)
+#define NV_TX_UNDERFLOW (1<<29)
+#define NV_TX_ERROR (1<<30)
+#define NV_TX_VALID (1<<31)
+
+#define NV_TX2_LASTPACKET (1<<29)
+#define NV_TX2_RETRYERROR (1<<18)
+#define NV_TX2_LASTPACKET1 (1<<23)
+#define NV_TX2_DEFERRED (1<<25)
+#define NV_TX2_CARRIERLOST (1<<26)
+#define NV_TX2_LATECOLLISION (1<<27)
+#define NV_TX2_UNDERFLOW (1<<28)
+/* error and valid are the same for both */
+#define NV_TX2_ERROR (1<<30)
+#define NV_TX2_VALID (1<<31)
+
+#define NV_RX_DESCRIPTORVALID (1<<16)
+#define NV_RX_MISSEDFRAME (1<<17)
+#define NV_RX_SUBSTRACT1 (1<<18)
+#define NV_RX_ERROR1 (1<<23)
+#define NV_RX_ERROR2 (1<<24)
+#define NV_RX_ERROR3 (1<<25)
+#define NV_RX_ERROR4 (1<<26)
+#define NV_RX_CRCERR (1<<27)
+#define NV_RX_OVERFLOW (1<<28)
+#define NV_RX_FRAMINGERR (1<<29)
+#define NV_RX_ERROR (1<<30)
+#define NV_RX_AVAIL (1<<31)
+
+#define NV_RX2_CHECKSUMMASK (0x1C000000)
+#define NV_RX2_CHECKSUMOK1 (0x10000000)
+#define NV_RX2_CHECKSUMOK2 (0x14000000)
+#define NV_RX2_CHECKSUMOK3 (0x18000000)
+#define NV_RX2_DESCRIPTORVALID (1<<29)
+#define NV_RX2_SUBSTRACT1 (1<<25)
+#define NV_RX2_ERROR1 (1<<18)
+#define NV_RX2_ERROR2 (1<<19)
+#define NV_RX2_ERROR3 (1<<20)
+#define NV_RX2_ERROR4 (1<<21)
+#define NV_RX2_CRCERR (1<<22)
+#define NV_RX2_OVERFLOW (1<<23)
+#define NV_RX2_FRAMINGERR (1<<24)
+/* error and avail are the same for both */
+#define NV_RX2_ERROR (1<<30)
+#define NV_RX2_AVAIL (1<<31)
+
+/* Miscelaneous hardware related defines: */
+#define NV_PCI_REGSZ 0x270
+
+/* various timeout delays: all in usec */
+#define NV_TXRX_RESET_DELAY 4
+#define NV_TXSTOP_DELAY1 10
+#define NV_TXSTOP_DELAY1MAX 500000
+#define NV_TXSTOP_DELAY2 100
+#define NV_RXSTOP_DELAY1 10
+#define NV_RXSTOP_DELAY1MAX 500000
+#define NV_RXSTOP_DELAY2 100
+#define NV_SETUP5_DELAY 5
+#define NV_SETUP5_DELAYMAX 50000
+#define NV_POWERUP_DELAY 5
+#define NV_POWERUP_DELAYMAX 5000
+#define NV_MIIBUSY_DELAY 50
+#define NV_MIIPHY_DELAY 10
+#define NV_MIIPHY_DELAYMAX 10000
+
+#define NV_WAKEUPPATTERNS 5
+#define NV_WAKEUPMASKENTRIES 4
+
+/* General driver defaults */
+#define NV_WATCHDOG_TIMEO (5*HZ)
+
+#define RX_RING 4
+#define TX_RING 2
+
+/*
+ * If your nic mysteriously hangs then try to reduce the limits
+ * to 1/0: It might be required to set NV_TX_LASTPACKET in the
+ * last valid ring entry. But this would be impossible to
+ * implement - probably a disassembly error.
+ */
+#define TX_LIMIT_STOP 63
+#define TX_LIMIT_START 62
+
+/* rx/tx mac addr + type + vlan + align + slack*/
+#define RX_NIC_BUFSIZE (ETH_DATA_LEN + 64)
+/* even more slack */
+#define RX_ALLOC_BUFSIZE (ETH_DATA_LEN + 128)
+
+#define OOM_REFILL (1+HZ/20)
+#define POLL_WAIT (1+HZ/100)
+#define LINK_TIMEOUT (3*HZ)
+
+/*
+ * desc_ver values:
+ * This field has two purposes:
+ * - Newer nics uses a different ring layout. The layout is selected by
+ * comparing np->desc_ver with DESC_VER_xy.
+ * - It contains bits that are forced on when writing to NvRegTxRxControl.
+ */
+#define DESC_VER_1 0x0
+#define DESC_VER_2 (0x02100|NVREG_TXRXCTL_RXCHECK)
+
+/* PHY defines */
+#define PHY_OUI_MARVELL 0x5043
+#define PHY_OUI_CICADA 0x03f1
+#define PHYID1_OUI_MASK 0x03ff
+#define PHYID1_OUI_SHFT 6
+#define PHYID2_OUI_MASK 0xfc00
+#define PHYID2_OUI_SHFT 10
+#define PHY_INIT1 0x0f000
+#define PHY_INIT2 0x0e00
+#define PHY_INIT3 0x01000
+#define PHY_INIT4 0x0200
+#define PHY_INIT5 0x0004
+#define PHY_INIT6 0x02000
+#define PHY_GIGABIT 0x0100
+
+#define PHY_TIMEOUT 0x1
+#define PHY_ERROR 0x2
+
+#define PHY_100 0x1
+#define PHY_1000 0x2
+#define PHY_HALF 0x100
+
+/* FIXME: MII defines that should be added to <linux/mii.h> */
+#define MII_1000BT_CR 0x09
+#define MII_1000BT_SR 0x0a
+#define ADVERTISE_1000FULL 0x0200
+#define ADVERTISE_1000HALF 0x0100
+#define LPA_1000FULL 0x0800
+#define LPA_1000HALF 0x0400
+
+/* Big endian: should work, but is untested */
+struct ring_desc {
+ u32 PacketBuffer;
+ u32 FlagLen;
+};
+
+
+/* Define the TX and RX Descriptor and Buffers */
+struct {
+ struct ring_desc tx_ring[TX_RING];
+ unsigned char txb[TX_RING * RX_NIC_BUFSIZE];
+ struct ring_desc rx_ring[RX_RING];
+ unsigned char rxb[RX_RING * RX_NIC_BUFSIZE];
+} forcedeth_bufs __shared;
+#define tx_ring forcedeth_bufs.tx_ring
+#define rx_ring forcedeth_bufs.rx_ring
+#define txb forcedeth_bufs.txb
+#define rxb forcedeth_bufs.rxb
+
+/* Private Storage for the NIC */
+static struct forcedeth_private {
+ /* General data:
+ * Locking: spin_lock(&np->lock); */
+ int in_shutdown;
+ u32 linkspeed;
+ int duplex;
+ int phyaddr;
+ int wolenabled;
+ unsigned int phy_oui;
+ u16 gigabit;
+
+ /* General data: RO fields */
+ u8 *ring_addr;
+ u32 orig_mac[2];
+ u32 irqmask;
+ u32 desc_ver;
+ /* rx specific fields.
+ * Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
+ */
+ unsigned int cur_rx, refill_rx;
+
+ /*
+ * tx specific fields.
+ */
+ unsigned int next_tx, nic_tx;
+ u32 tx_flags;
+} npx;
+
+static struct forcedeth_private *np;
+
+static inline void pci_push(u8 * base)
+{
+ /* force out pending posted writes */
+ readl(base);
+}
+
+static inline u32 nv_descr_getlength(struct ring_desc *prd, u32 v)
+{
+ return le32_to_cpu(prd->FlagLen)
+ & ((v == DESC_VER_1) ? LEN_MASK_V1 : LEN_MASK_V2);
+}
+
+static int reg_delay(int offset, u32 mask,
+ u32 target, int delay, int delaymax, const char *msg)
+{
+ u8 *base = (u8 *) BASE;
+
+ pci_push(base);
+ do {
+ udelay(delay);
+ delaymax -= delay;
+ if (delaymax < 0) {
+ if (msg)
+ printf(msg);
+ return 1;
+ }
+ } while ((readl(base + offset) & mask) != target);
+ return 0;
+}
+
+#define MII_READ (-1)
+#define MII_PHYSID1 0x02 /* PHYS ID 1 */
+#define MII_PHYSID2 0x03 /* PHYS ID 2 */
+#define MII_BMCR 0x00 /* Basic mode control register */
+#define MII_BMSR 0x01 /* Basic mode status register */
+#define MII_ADVERTISE 0x04 /* Advertisement control reg */
+#define MII_LPA 0x05 /* Link partner ability reg */
+
+#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
+
+/* Link partner ability register. */
+#define LPA_SLCT 0x001f /* Same as advertise selector */
+#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */
+#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */
+#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */
+#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */
+#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */
+#define LPA_RESV 0x1c00 /* Unused... */
+#define LPA_RFAULT 0x2000 /* Link partner faulted */
+#define LPA_LPACK 0x4000 /* Link partner acked us */
+#define LPA_NPAGE 0x8000 /* Next page bit */
+
+/* mii_rw: read/write a register on the PHY.
+ *
+ * Caller must guarantee serialization
+ */
+static int mii_rw(struct nic *nic __unused, int addr, int miireg,
+ int value)
+{
+ u8 *base = (u8 *) BASE;
+ u32 reg;
+ int retval;
+
+ writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
+
+ reg = readl(base + NvRegMIIControl);
+ if (reg & NVREG_MIICTL_INUSE) {
+ writel(NVREG_MIICTL_INUSE, base + NvRegMIIControl);
+ udelay(NV_MIIBUSY_DELAY);
+ }
+
+ reg =
+ (addr << NVREG_MIICTL_ADDRSHIFT) | miireg;
+ if (value != MII_READ) {
+ writel(value, base + NvRegMIIData);
+ reg |= NVREG_MIICTL_WRITE;
+ }
+ writel(reg, base + NvRegMIIControl);
+
+ if (reg_delay(NvRegMIIControl, NVREG_MIICTL_INUSE, 0,
+ NV_MIIPHY_DELAY, NV_MIIPHY_DELAYMAX, NULL)) {
+ dprintf(("mii_rw of reg %d at PHY %d timed out.\n",
+ miireg, addr));
+ retval = -1;
+ } else if (value != MII_READ) {
+ /* it was a write operation - fewer failures are detectable */
+ dprintf(("mii_rw wrote 0x%x to reg %d at PHY %d\n",
+ value, miireg, addr));
+ retval = 0;
+ } else if (readl(base + NvRegMIIStatus) & NVREG_MIISTAT_ERROR) {
+ dprintf(("mii_rw of reg %d at PHY %d failed.\n",
+ miireg, addr));
+ retval = -1;
+ } else {
+ retval = readl(base + NvRegMIIData);
+ dprintf(("mii_rw read from reg %d at PHY %d: 0x%x.\n",
+ miireg, addr, retval));
+ }
+ return retval;
+}
+
+static int phy_reset(struct nic *nic)
+{
+
+ u32 miicontrol;
+ unsigned int tries = 0;
+
+ miicontrol = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
+ miicontrol |= BMCR_RESET;
+ if (mii_rw(nic, np->phyaddr, MII_BMCR, miicontrol)) {
+ return -1;
+ }
+
+ /* wait for 500ms */
+ mdelay(500);
+
+ /* must wait till reset is deasserted */
+ while (miicontrol & BMCR_RESET) {
+ mdelay(10);
+ miicontrol = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
+ /* FIXME: 100 tries seem excessive */
+ if (tries++ > 100)
+ return -1;
+ }
+ return 0;
+}
+
+static int phy_init(struct nic *nic)
+{
+ u8 *base = (u8 *) BASE;
+ u32 phyinterface, phy_reserved, mii_status, mii_control,
+ mii_control_1000, reg;
+
+ /* set advertise register */
+ reg = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ);
+ reg |=
+ (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF |
+ ADVERTISE_100FULL | 0x800 | 0x400);
+ if (mii_rw(nic, np->phyaddr, MII_ADVERTISE, reg)) {
+ printf("phy write to advertise failed.\n");
+ return PHY_ERROR;
+ }
+
+ /* get phy interface type */
+ phyinterface = readl(base + NvRegPhyInterface);
+
+ /* see if gigabit phy */
+ mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
+
+ if (mii_status & PHY_GIGABIT) {
+ np->gigabit = PHY_GIGABIT;
+ mii_control_1000 =
+ mii_rw(nic, np->phyaddr, MII_1000BT_CR, MII_READ);
+ mii_control_1000 &= ~ADVERTISE_1000HALF;
+ if (phyinterface & PHY_RGMII)
+ mii_control_1000 |= ADVERTISE_1000FULL;
+ else
+ mii_control_1000 &= ~ADVERTISE_1000FULL;
+
+ if (mii_rw
+ (nic, np->phyaddr, MII_1000BT_CR, mii_control_1000)) {
+ printf("phy init failed.\n");
+ return PHY_ERROR;
+ }
+ } else
+ np->gigabit = 0;
+
+ /* reset the phy */
+ if (phy_reset(nic)) {
+ printf("phy reset failed\n");
+ return PHY_ERROR;
+ }
+
+ /* phy vendor specific configuration */
+ if ((np->phy_oui == PHY_OUI_CICADA) && (phyinterface & PHY_RGMII)) {
+ phy_reserved =
+ mii_rw(nic, np->phyaddr, MII_RESV1, MII_READ);
+ phy_reserved &= ~(PHY_INIT1 | PHY_INIT2);
+ phy_reserved |= (PHY_INIT3 | PHY_INIT4);
+ if (mii_rw(nic, np->phyaddr, MII_RESV1, phy_reserved)) {
+ printf("phy init failed.\n");
+ return PHY_ERROR;
+ }
+ phy_reserved =
+ mii_rw(nic, np->phyaddr, MII_NCONFIG, MII_READ);
+ phy_reserved |= PHY_INIT5;
+ if (mii_rw(nic, np->phyaddr, MII_NCONFIG, phy_reserved)) {
+ printf("phy init failed.\n");
+ return PHY_ERROR;
+ }
+ }
+ if (np->phy_oui == PHY_OUI_CICADA) {
+ phy_reserved =
+ mii_rw(nic, np->phyaddr, MII_SREVISION, MII_READ);
+ phy_reserved |= PHY_INIT6;
+ if (mii_rw(nic, np->phyaddr, MII_SREVISION, phy_reserved)) {
+ printf("phy init failed.\n");
+ return PHY_ERROR;
+ }
+ }
+
+ /* restart auto negotiation */
+ mii_control = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
+ mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE);
+ if (mii_rw(nic, np->phyaddr, MII_BMCR, mii_control)) {
+ return PHY_ERROR;
+ }
+
+ return 0;
+}
+
+static void start_rx(struct nic *nic __unused)
+{
+ u8 *base = (u8 *) BASE;
+
+ dprintf(("start_rx\n"));
+ /* Already running? Stop it. */
+ if (readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) {
+ writel(0, base + NvRegReceiverControl);
+ pci_push(base);
+ }
+ writel(np->linkspeed, base + NvRegLinkSpeed);
+ pci_push(base);
+ writel(NVREG_RCVCTL_START, base + NvRegReceiverControl);
+ pci_push(base);
+}
+
+static void stop_rx(void)
+{
+ u8 *base = (u8 *) BASE;
+
+ dprintf(("stop_rx\n"));
+ writel(0, base + NvRegReceiverControl);
+ reg_delay(NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
+ NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX,
+ "stop_rx: ReceiverStatus remained busy");
+
+ udelay(NV_RXSTOP_DELAY2);
+ writel(0, base + NvRegLinkSpeed);
+}
+
+static void start_tx(struct nic *nic __unused)
+{
+ u8 *base = (u8 *) BASE;
+
+ dprintf(("start_tx\n"));
+ writel(NVREG_XMITCTL_START, base + NvRegTransmitterControl);
+ pci_push(base);
+}
+
+static void stop_tx(void)
+{
+ u8 *base = (u8 *) BASE;
+
+ dprintf(("stop_tx\n"));
+ writel(0, base + NvRegTransmitterControl);
+ reg_delay(NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0,
+ NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX,
+ "stop_tx: TransmitterStatus remained busy");
+
+ udelay(NV_TXSTOP_DELAY2);
+ writel(0, base + NvRegUnknownTransmitterReg);
+}
+
+
+static void txrx_reset(struct nic *nic __unused)
+{
+ u8 *base = (u8 *) BASE;
+
+ dprintf(("txrx_reset\n"));
+ writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->desc_ver,
+ base + NvRegTxRxControl);
+
+ pci_push(base);
+ udelay(NV_TXRX_RESET_DELAY);
+ writel(NVREG_TXRXCTL_BIT2 | np->desc_ver, base + NvRegTxRxControl);
+ pci_push(base);
+}
+
+/*
+ * alloc_rx: fill rx ring entries.
+ * Return 1 if the allocations for the skbs failed and the
+ * rx engine is without Available descriptors
+ */
+static int alloc_rx(struct nic *nic __unused)
+{
+ unsigned int refill_rx = np->refill_rx;
+ int i;
+ //while (np->cur_rx != refill_rx) {
+ for (i = 0; i < RX_RING; i++) {
+ //int nr = refill_rx % RX_RING;
+ rx_ring[i].PacketBuffer =
+ virt_to_le32desc(&rxb[i * RX_NIC_BUFSIZE]);
+ wmb();
+ rx_ring[i].FlagLen =
+ cpu_to_le32(RX_NIC_BUFSIZE | NV_RX_AVAIL);
+ /* printf("alloc_rx: Packet %d marked as Available\n",
+ refill_rx); */
+ refill_rx++;
+ }
+ np->refill_rx = refill_rx;
+ if (np->cur_rx - refill_rx == RX_RING)
+ return 1;
+ return 0;
+}
+
+static int update_linkspeed(struct nic *nic)
+{
+ int adv, lpa;
+ u32 newls;
+ int newdup = np->duplex;
+ u32 mii_status;
+ int retval = 0;
+ u32 control_1000, status_1000, phyreg;
+ u8 *base = (u8 *) BASE;
+ int i;
+
+ /* BMSR_LSTATUS is latched, read it twice:
+ * we want the current value.
+ */
+ mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
+ mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
+
+#if 1
+ //yhlu
+ for(i=0;i<30;i++) {
+ mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
+ if((mii_status & BMSR_LSTATUS) && (mii_status & BMSR_ANEGCOMPLETE)) break;
+ mdelay(100);
+ }
+#endif
+
+ if (!(mii_status & BMSR_LSTATUS)) {
+ printf
+ ("no link detected by phy - falling back to 10HD.\n");
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ retval = 0;
+ goto set_speed;
+ }
+
+ /* check auto negotiation is complete */
+ if (!(mii_status & BMSR_ANEGCOMPLETE)) {
+ /* still in autonegotiation - configure nic for 10 MBit HD and wait. */
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ retval = 0;
+ printf("autoneg not completed - falling back to 10HD.\n");
+ goto set_speed;
+ }
+
+ retval = 1;
+ if (np->gigabit == PHY_GIGABIT) {
+ control_1000 =
+ mii_rw(nic, np->phyaddr, MII_1000BT_CR, MII_READ);
+ status_1000 =
+ mii_rw(nic, np->phyaddr, MII_1000BT_SR, MII_READ);
+
+ if ((control_1000 & ADVERTISE_1000FULL) &&
+ (status_1000 & LPA_1000FULL)) {
+ printf
+ ("update_linkspeed: GBit ethernet detected.\n");
+ newls =
+ NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_1000;
+ newdup = 1;
+ goto set_speed;
+ }
+ }
+
+ adv = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ);
+ lpa = mii_rw(nic, np->phyaddr, MII_LPA, MII_READ);
+ dprintf(("update_linkspeed: PHY advertises 0x%hX, lpa 0x%hX.\n",
+ adv, lpa));
+
+ /* FIXME: handle parallel detection properly, handle gigabit ethernet */
+ lpa = lpa & adv;
+ if (lpa & LPA_100FULL) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
+ newdup = 1;
+ } else if (lpa & LPA_100HALF) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
+ newdup = 0;
+ } else if (lpa & LPA_10FULL) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 1;
+ } else if (lpa & LPA_10HALF) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ } else {
+ printf("bad ability %hX - falling back to 10HD.\n", lpa);
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ }
+
+ set_speed:
+ if (np->duplex == newdup && np->linkspeed == newls)
+ return retval;
+
+ dprintf(("changing link setting from %d/%s to %d/%s.\n",
+ np->linkspeed, np->duplex ? "Full-Duplex": "Half-Duplex", newls, newdup ? "Full-Duplex": "Half-Duplex"));
+
+ np->duplex = newdup;
+ np->linkspeed = newls;
+
+ if (np->gigabit == PHY_GIGABIT) {
+ phyreg = readl(base + NvRegRandomSeed);
+ phyreg &= ~(0x3FF00);
+ if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_10)
+ phyreg |= NVREG_RNDSEED_FORCE3;
+ else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
+ phyreg |= NVREG_RNDSEED_FORCE2;
+ else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
+ phyreg |= NVREG_RNDSEED_FORCE;
+ writel(phyreg, base + NvRegRandomSeed);
+ }
+
+ phyreg = readl(base + NvRegPhyInterface);
+ phyreg &= ~(PHY_HALF | PHY_100 | PHY_1000);
+ if (np->duplex == 0)
+ phyreg |= PHY_HALF;
+ if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
+ phyreg |= PHY_100;
+ else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
+ phyreg |= PHY_1000;
+ writel(phyreg, base + NvRegPhyInterface);
+
+ writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD),
+ base + NvRegMisc1);
+ pci_push(base);
+ writel(np->linkspeed, base + NvRegLinkSpeed);
+ pci_push(base);
+
+ return retval;
+}
+
+#if 0 /* Not used */
+static void nv_linkchange(struct nic *nic)
+{
+ if (update_linkspeed(nic)) {
+// if (netif_carrier_ok(nic)) {
+ stop_rx();
+//= } else {
+ // netif_carrier_on(dev);
+ // printk(KERN_INFO "%s: link up.\n", dev->name);
+ // }
+ start_rx(nic);
+ } else {
+ // if (netif_carrier_ok(dev)) {
+ // netif_carrier_off(dev);
+ // printk(KERN_INFO "%s: link down.\n", dev->name);
+ stop_rx();
+ // }
+ }
+}
+#endif
+
+static int init_ring(struct nic *nic)
+{
+ int i;
+
+ np->next_tx = np->nic_tx = 0;
+ for (i = 0; i < TX_RING; i++)
+ tx_ring[i].FlagLen = 0;
+
+ np->cur_rx = 0;
+ np->refill_rx = 0;
+ for (i = 0; i < RX_RING; i++)
+ rx_ring[i].FlagLen = 0;
+ return alloc_rx(nic);
+}
+
+static void set_multicast(struct nic *nic)
+{
+
+ u8 *base = (u8 *) BASE;
+ u32 addr[2];
+ u32 mask[2];
+ u32 pff;
+ u32 alwaysOff[2];
+ u32 alwaysOn[2];
+
+ memset(addr, 0, sizeof(addr));
+ memset(mask, 0, sizeof(mask));
+
+ pff = NVREG_PFF_MYADDR;
+
+ alwaysOn[0] = alwaysOn[1] = alwaysOff[0] = alwaysOff[1] = 0;
+
+ addr[0] = alwaysOn[0];
+ addr[1] = alwaysOn[1];
+ mask[0] = alwaysOn[0] | alwaysOff[0];
+ mask[1] = alwaysOn[1] | alwaysOff[1];
+
+ addr[0] |= NVREG_MCASTADDRA_FORCE;
+ pff |= NVREG_PFF_ALWAYS;
+ stop_rx();
+ writel(addr[0], base + NvRegMulticastAddrA);
+ writel(addr[1], base + NvRegMulticastAddrB);
+ writel(mask[0], base + NvRegMulticastMaskA);
+ writel(mask[1], base + NvRegMulticastMaskB);
+ writel(pff, base + NvRegPacketFilterFlags);
+ start_rx(nic);
+}
+
+/**************************************************************************
+RESET - Reset the NIC to prepare for use
+***************************************************************************/
+static int forcedeth_reset(struct nic *nic)
+{
+ u8 *base = (u8 *) BASE;
+ int ret, oom, i;
+ ret = 0;
+ dprintf(("forcedeth: open\n"));
+
+ /* 1) erase previous misconfiguration */
+ /* 4.1-1: stop adapter: ignored, 4.3 seems to be overkill */
+ writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
+ writel(0, base + NvRegMulticastAddrB);
+ writel(0, base + NvRegMulticastMaskA);
+ writel(0, base + NvRegMulticastMaskB);
+ writel(0, base + NvRegPacketFilterFlags);
+
+ writel(0, base + NvRegTransmitterControl);
+ writel(0, base + NvRegReceiverControl);
+
+ writel(0, base + NvRegAdapterControl);
+
+ /* 2) initialize descriptor rings */
+ oom = init_ring(nic);
+
+ writel(0, base + NvRegLinkSpeed);
+ writel(0, base + NvRegUnknownTransmitterReg);
+ txrx_reset(nic);
+ writel(0, base + NvRegUnknownSetupReg6);
+
+ np->in_shutdown = 0;
+
+ /* 3) set mac address */
+ {
+ u32 mac[2];
+
+ mac[0] =
+ (nic->node_addr[0] << 0) + (nic->node_addr[1] << 8) +
+ (nic->node_addr[2] << 16) + (nic->node_addr[3] << 24);
+ mac[1] =
+ (nic->node_addr[4] << 0) + (nic->node_addr[5] << 8);
+
+ writel(mac[0], base + NvRegMacAddrA);
+ writel(mac[1], base + NvRegMacAddrB);
+ }
+
+ /* 4) give hw rings */
+ writel((u32) virt_to_le32desc(&rx_ring[0]),
+ base + NvRegRxRingPhysAddr);
+ writel((u32) virt_to_le32desc(&tx_ring[0]),
+ base + NvRegTxRingPhysAddr);
+
+ writel(((RX_RING - 1) << NVREG_RINGSZ_RXSHIFT) +
+ ((TX_RING - 1) << NVREG_RINGSZ_TXSHIFT),
+ base + NvRegRingSizes);
+
+ /* 5) continue setup */
+ np->linkspeed = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ np->duplex = 0;
+ writel(np->linkspeed, base + NvRegLinkSpeed);
+ writel(NVREG_UNKSETUP3_VAL1, base + NvRegUnknownSetupReg3);
+ writel(np->desc_ver, base + NvRegTxRxControl);
+ pci_push(base);
+ writel(NVREG_TXRXCTL_BIT1 | np->desc_ver, base + NvRegTxRxControl);
+ reg_delay(NvRegUnknownSetupReg5, NVREG_UNKSETUP5_BIT31,
+ NVREG_UNKSETUP5_BIT31, NV_SETUP5_DELAY,
+ NV_SETUP5_DELAYMAX,
+ "open: SetupReg5, Bit 31 remained off\n");
+
+ writel(0, base + NvRegUnknownSetupReg4);
+// writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
+ writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
+#if 0
+ printf("%d-Mbs Link, %s-Duplex\n",
+ np->linkspeed & NVREG_LINKSPEED_10 ? 10 : 100,
+ np->duplex ? "Full" : "Half");
+#endif
+
+ /* 6) continue setup */
+ writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1);
+ writel(readl(base + NvRegTransmitterStatus),
+ base + NvRegTransmitterStatus);
+ writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags);
+ writel(NVREG_OFFLOAD_NORMAL, base + NvRegOffloadConfig);
+
+ writel(readl(base + NvRegReceiverStatus),
+ base + NvRegReceiverStatus);
+
+ /* Get a random number */
+ i = random();
+ writel(NVREG_RNDSEED_FORCE | (i & NVREG_RNDSEED_MASK),
+ base + NvRegRandomSeed);
+ writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1);
+ writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2);
+ writel(NVREG_POLL_DEFAULT, base + NvRegPollingInterval);
+ writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
+ writel((np->
+ phyaddr << NVREG_ADAPTCTL_PHYSHIFT) |
+ NVREG_ADAPTCTL_PHYVALID | NVREG_ADAPTCTL_RUNNING,
+ base + NvRegAdapterControl);
+ writel(NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, base + NvRegMIISpeed);
+ writel(NVREG_UNKSETUP4_VAL, base + NvRegUnknownSetupReg4);
+ writel(NVREG_WAKEUPFLAGS_VAL, base + NvRegWakeUpFlags);
+
+ i = readl(base + NvRegPowerState);
+ if ((i & NVREG_POWERSTATE_POWEREDUP) == 0)
+ writel(NVREG_POWERSTATE_POWEREDUP | i,
+ base + NvRegPowerState);
+
+ pci_push(base);
+ udelay(10);
+ writel(readl(base + NvRegPowerState) | NVREG_POWERSTATE_VALID,
+ base + NvRegPowerState);
+
+ writel(0, base + NvRegIrqMask);
+ pci_push(base);
+ writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
+ writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
+ pci_push(base);
+/*
+ writel(np->irqmask, base + NvRegIrqMask);
+*/
+ writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
+ writel(0, base + NvRegMulticastAddrB);
+ writel(0, base + NvRegMulticastMaskA);
+ writel(0, base + NvRegMulticastMaskB);
+ writel(NVREG_PFF_ALWAYS | NVREG_PFF_MYADDR,
+ base + NvRegPacketFilterFlags);
+
+ set_multicast(nic);
+ /* One manual link speed update: Interrupts are enabled, future link
+ * speed changes cause interrupts and are handled by nv_link_irq().
+ */
+ {
+ u32 miistat;
+ miistat = readl(base + NvRegMIIStatus);
+ writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
+ dprintf(("startup: got 0x%hX.\n", miistat));
+ }
+ ret = update_linkspeed(nic);
+
+ //start_rx(nic);
+ start_tx(nic);
+
+ if (ret) {
+ //Start Connection netif_carrier_on(dev);
+ } else {
+ printf("no link during initialization.\n");
+ }
+
+ return ret;
+}
+
+/*
+ * extern void hex_dump(const char *data, const unsigned int len);
+*/
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int forcedeth_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+
+ int len;
+ int i;
+ u32 Flags;
+
+ i = np->cur_rx % RX_RING;
+
+ Flags = le32_to_cpu(rx_ring[i].FlagLen);
+ len = nv_descr_getlength(&rx_ring[i], np->desc_ver);
+
+ if (Flags & NV_RX_AVAIL)
+ return 0; /* still owned by hardware, */
+
+ if (np->desc_ver == DESC_VER_1) {
+ if (!(Flags & NV_RX_DESCRIPTORVALID))
+ return 0;
+ } else {
+ if (!(Flags & NV_RX2_DESCRIPTORVALID))
+ return 0;
+ }
+
+ if (!retrieve)
+ return 1;
+
+ /* got a valid packet - forward it to the network core */
+ nic->packetlen = len;
+ memcpy(nic->packet, rxb + (i * RX_NIC_BUFSIZE), nic->packetlen);
+/*
+ * hex_dump(rxb + (i * RX_NIC_BUFSIZE), len);
+*/
+ wmb();
+ np->cur_rx++;
+ alloc_rx(nic);
+ return 1;
+}
+
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void forcedeth_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ /* send the packet to destination */
+ u8 *ptxb;
+ u16 nstype;
+ u8 *base = (u8 *) BASE;
+ int nr = np->next_tx % TX_RING;
+
+ /* point to the current txb incase multiple tx_rings are used */
+ ptxb = txb + (nr * RX_NIC_BUFSIZE);
+ //np->tx_skbuff[nr] = ptxb;
+
+ /* copy the packet to ring buffer */
+ memcpy(ptxb, d, ETH_ALEN); /* dst */
+ memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+ nstype = htons((u16) t); /* type */
+ memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* type */
+ memcpy(ptxb + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ while (s < ETH_ZLEN) /* pad to min length */
+ ptxb[s++] = '\0';
+
+ tx_ring[nr].PacketBuffer = (u32) virt_to_le32desc(ptxb);
+
+ wmb();
+ tx_ring[nr].FlagLen = cpu_to_le32((s - 1) | np->tx_flags);
+
+ writel(NVREG_TXRXCTL_KICK | np->desc_ver, base + NvRegTxRxControl);
+ pci_push(base);
+ np->next_tx++;
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void forcedeth_disable ( struct nic *nic __unused ) {
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+ u8 *base = (u8 *) BASE;
+ np->in_shutdown = 1;
+ stop_tx();
+ stop_rx();
+
+ /* disable interrupts on the nic or we will lock up */
+ writel(0, base + NvRegIrqMask);
+ pci_push(base);
+ dprintf(("Irqmask is zero again\n"));
+
+ /* specia op:o write back the misordered MAC address - otherwise
+ * the next probe_nic would see a wrong address.
+ */
+ writel(np->orig_mac[0], base + NvRegMacAddrA);
+ writel(np->orig_mac[1], base + NvRegMacAddrB);
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void forcedeth_irq(struct nic *nic __unused,
+ irq_action_t action __unused)
+{
+ switch (action) {
+ case DISABLE:
+ break;
+ case ENABLE:
+ break;
+ case FORCE:
+ break;
+ }
+}
+
+static struct nic_operations forcedeth_operations = {
+ .connect = dummy_connect,
+ .poll = forcedeth_poll,
+ .transmit = forcedeth_transmit,
+ .irq = forcedeth_irq,
+
+};
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+#define IORESOURCE_MEM 0x00000200
+#define board_found 1
+#define valid_link 0
+static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ unsigned long addr;
+ int sz;
+ u8 *base;
+ int i;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ printf("forcedeth.c: Found %s, vendor=0x%hX, device=0x%hX\n",
+ pci->driver_name, pci->vendor, pci->device);
+
+ nic->ioaddr = pci->ioaddr;
+ nic->irqno = 0;
+
+ /* point to private storage */
+ np = &npx;
+
+ adjust_pci_device(pci);
+
+ addr = pci_bar_start(pci, PCI_BASE_ADDRESS_0);
+ sz = pci_bar_size(pci, PCI_BASE_ADDRESS_0);
+
+ /* BASE is used throughout to address the card */
+ BASE = (unsigned long) ioremap(addr, sz);
+ if (!BASE)
+ return 0;
+
+ /* handle different descriptor versions */
+ if (pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_1 ||
+ pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_2 ||
+ pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_3)
+ np->desc_ver = DESC_VER_1;
+ else
+ np->desc_ver = DESC_VER_2;
+
+ //rx_ring[0] = rx_ring;
+ //tx_ring[0] = tx_ring;
+
+ /* read the mac address */
+ base = (u8 *) BASE;
+ np->orig_mac[0] = readl(base + NvRegMacAddrA);
+ np->orig_mac[1] = readl(base + NvRegMacAddrB);
+
+ nic->node_addr[0] = (np->orig_mac[1] >> 8) & 0xff;
+ nic->node_addr[1] = (np->orig_mac[1] >> 0) & 0xff;
+ nic->node_addr[2] = (np->orig_mac[0] >> 24) & 0xff;
+ nic->node_addr[3] = (np->orig_mac[0] >> 16) & 0xff;
+ nic->node_addr[4] = (np->orig_mac[0] >> 8) & 0xff;
+ nic->node_addr[5] = (np->orig_mac[0] >> 0) & 0xff;
+#ifdef LINUX
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ /*
+ * Bad mac address. At least one bios sets the mac address
+ * to 01:23:45:67:89:ab
+ */
+ printk(KERN_ERR
+ "%s: Invalid Mac address detected: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ pci_name(pci_dev), dev->dev_addr[0],
+ dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4],
+ dev->dev_addr[5]);
+ printk(KERN_ERR
+ "Please complain to your hardware vendor. Switching to a random MAC.\n");
+ dev->dev_addr[0] = 0x00;
+ dev->dev_addr[1] = 0x00;
+ dev->dev_addr[2] = 0x6c;
+ get_random_bytes(&dev->dev_addr[3], 3);
+ }
+#endif
+
+ DBG ( "%s: MAC Address %s\n", pci->driver_name, eth_ntoa ( nic->node_addr ) );
+
+ /* disable WOL */
+ writel(0, base + NvRegWakeUpFlags);
+ np->wolenabled = 0;
+
+ if (np->desc_ver == DESC_VER_1) {
+ np->tx_flags = NV_TX_LASTPACKET | NV_TX_VALID;
+ } else {
+ np->tx_flags = NV_TX2_LASTPACKET | NV_TX2_VALID;
+ }
+
+ switch (pci->device) {
+ case 0x01C3: // nforce
+ // DEV_IRQMASK_1|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
+ np->irqmask = NVREG_IRQMASK_WANTED_2 | NVREG_IRQ_TIMER;
+ // np->need_linktimer = 1;
+ // np->link_timeout = jiffies + LINK_TIMEOUT;
+ break;
+ case 0x0066:
+ /* Fall Through */
+ case 0x00D6:
+ // DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER
+ np->irqmask = NVREG_IRQMASK_WANTED_2;
+ np->irqmask |= NVREG_IRQ_TIMER;
+ // np->need_linktimer = 1;
+ // np->link_timeout = jiffies + LINK_TIMEOUT;
+ if (np->desc_ver == DESC_VER_1)
+ np->tx_flags |= NV_TX_LASTPACKET1;
+ else
+ np->tx_flags |= NV_TX2_LASTPACKET1;
+ break;
+ case 0x0086:
+ /* Fall Through */
+ case 0x008c:
+ /* Fall Through */
+ case 0x00e6:
+ /* Fall Through */
+ case 0x00df:
+ /* Fall Through */
+ case 0x0056:
+ /* Fall Through */
+ case 0x0057:
+ /* Fall Through */
+ case 0x0037:
+ /* Fall Through */
+ case 0x0038:
+ //DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ
+ np->irqmask = NVREG_IRQMASK_WANTED_2;
+ np->irqmask |= NVREG_IRQ_TIMER;
+ // np->need_linktimer = 1;
+ // np->link_timeout = jiffies + LINK_TIMEOUT;
+ if (np->desc_ver == DESC_VER_1)
+ np->tx_flags |= NV_TX_LASTPACKET1;
+ else
+ np->tx_flags |= NV_TX2_LASTPACKET1;
+ break;
+ default:
+ printf
+ ("Your card was undefined in this driver. Review driver_data in Linux driver and send a patch\n");
+ }
+
+ /* find a suitable phy */
+ for (i = 1; i < 32; i++) {
+ int id1, id2;
+ id1 = mii_rw(nic, i, MII_PHYSID1, MII_READ);
+ if (id1 < 0 || id1 == 0xffff)
+ continue;
+ id2 = mii_rw(nic, i, MII_PHYSID2, MII_READ);
+ if (id2 < 0 || id2 == 0xffff)
+ continue;
+ id1 = (id1 & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT;
+ id2 = (id2 & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT;
+ dprintf
+ (("%s: open: Found PHY %hX:%hX at address %d.\n",
+ pci->driver_name, id1, id2, i));
+ np->phyaddr = i;
+ np->phy_oui = id1 | id2;
+ break;
+ }
+ if (i == 32) {
+ /* PHY in isolate mode? No phy attached and user wants to
+ * test loopback? Very odd, but can be correct.
+ */
+ printf
+ ("%s: open: Could not find a valid PHY.\n", pci->driver_name);
+ }
+
+ if (i != 32) {
+ /* reset it */
+ phy_init(nic);
+ }
+
+ dprintf(("%s: forcedeth.c: subsystem: %hX:%hX bound to %s\n",
+ pci->driver_name, pci->vendor, pci->dev_id, pci->driver_name));
+ if(!forcedeth_reset(nic)) return 0; // no valid link
+
+ /* point to NIC specific routines */
+ nic->nic_op = &forcedeth_operations;
+ return 1;
+}
+
+static struct pci_device_id forcedeth_nics[] = {
+PCI_ROM(0x10de, 0x01C3, "nforce", "nForce NVENET_1 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0066, "nforce2", "nForce NVENET_2 Ethernet Controller"),
+PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce NVENET_3 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0086, "nforce4", "nForce NVENET_4 Ethernet Controller"),
+PCI_ROM(0x10de, 0x008c, "nforce5", "nForce NVENET_5 Ethernet Controller"),
+PCI_ROM(0x10de, 0x00e6, "nforce6", "nForce NVENET_6 Ethernet Controller"),
+PCI_ROM(0x10de, 0x00df, "nforce7", "nForce NVENET_7 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0056, "nforce8", "nForce NVENET_8 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0057, "nforce9", "nForce NVENET_9 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0037, "nforce10", "nForce NVENET_10 Ethernet Controller"),
+PCI_ROM(0x10de, 0x0038, "nforce11", "nForce NVENET_11 Ethernet Controller"),
+};
+
+PCI_DRIVER ( forcedeth_driver, forcedeth_nics, PCI_NO_CLASS );
+
+DRIVER ( "forcedeth", nic_driver, pci_driver, forcedeth_driver,
+ forcedeth_probe, forcedeth_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/hfa384x.h b/gpxe/src/drivers/net/hfa384x.h
new file mode 100644
index 00000000..afb50693
--- /dev/null
+++ b/gpxe/src/drivers/net/hfa384x.h
@@ -0,0 +1,3067 @@
+/* src/prism2/include/prism2/hfa384x.h
+*
+* Defines the constants and data structures for the hfa384x
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* [Implementation and usage notes]
+*
+* [References]
+* CW10 Programmer's Manual v1.5
+* IEEE 802.11 D10.0
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _HFA384x_H
+#define _HFA384x_H
+
+/*=============================================================*/
+#define HFA384x_FIRMWARE_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
+#define HFA384x_LEVEL_TO_dBm(v) (0x100 + (v) * 100 / 255 - 100)
+
+/*------ Constants --------------------------------------------*/
+/*--- Mins & Maxs -----------------------------------*/
+#define HFA384x_CMD_ALLOC_LEN_MIN ((UINT16)4)
+#define HFA384x_CMD_ALLOC_LEN_MAX ((UINT16)2400)
+#define HFA384x_BAP_DATALEN_MAX ((UINT16)4096)
+#define HFA384x_BAP_OFFSET_MAX ((UINT16)4096)
+#define HFA384x_PORTID_MAX ((UINT16)7)
+#define HFA384x_NUMPORTS_MAX ((UINT16)(HFA384x_PORTID_MAX+1))
+#define HFA384x_PDR_LEN_MAX ((UINT16)512) /* in bytes, from EK */
+#define HFA384x_PDA_RECS_MAX ((UINT16)200) /* a guess */
+#define HFA384x_PDA_LEN_MAX ((UINT16)1024) /* in bytes, from EK */
+#define HFA384x_SCANRESULT_MAX ((UINT16)31)
+#define HFA384x_HSCANRESULT_MAX ((UINT16)31)
+#define HFA384x_CHINFORESULT_MAX ((UINT16)16)
+#define HFA384x_DRVR_FIDSTACKLEN_MAX (10)
+#define HFA384x_DRVR_TXBUF_MAX (sizeof(hfa384x_tx_frame_t) + \
+ WLAN_DATA_MAXLEN - \
+ WLAN_WEP_IV_LEN - \
+ WLAN_WEP_ICV_LEN + 2)
+#define HFA384x_DRVR_MAGIC (0x4a2d)
+#define HFA384x_INFODATA_MAXLEN (sizeof(hfa384x_infodata_t))
+#define HFA384x_INFOFRM_MAXLEN (sizeof(hfa384x_InfFrame_t))
+#define HFA384x_RID_GUESSING_MAXLEN 2048 /* I'm not really sure */
+#define HFA384x_RIDDATA_MAXLEN HFA384x_RID_GUESSING_MAXLEN
+#define HFA384x_USB_RWMEM_MAXLEN 2048
+
+/*--- Support Constants -----------------------------*/
+#define HFA384x_BAP_PROC ((UINT16)0)
+#define HFA384x_BAP_INT ((UINT16)1)
+#define HFA384x_PORTTYPE_IBSS ((UINT16)0)
+#define HFA384x_PORTTYPE_BSS ((UINT16)1)
+#define HFA384x_PORTTYPE_WDS ((UINT16)2)
+#define HFA384x_PORTTYPE_PSUEDOIBSS ((UINT16)3)
+#define HFA384x_PORTTYPE_HOSTAP ((UINT16)6)
+#define HFA384x_WEPFLAGS_PRIVINVOKED ((UINT16)BIT0)
+#define HFA384x_WEPFLAGS_EXCLUDE ((UINT16)BIT1)
+#define HFA384x_WEPFLAGS_DISABLE_TXCRYPT ((UINT16)BIT4)
+#define HFA384x_WEPFLAGS_DISABLE_RXCRYPT ((UINT16)BIT7)
+#define HFA384x_WEPFLAGS_DISALLOW_MIXED ((UINT16)BIT11)
+#define HFA384x_WEPFLAGS_IV_INTERVAL1 ((UINT16)0)
+#define HFA384x_WEPFLAGS_IV_INTERVAL10 ((UINT16)BIT5)
+#define HFA384x_WEPFLAGS_IV_INTERVAL50 ((UINT16)BIT6)
+#define HFA384x_WEPFLAGS_IV_INTERVAL100 ((UINT16)(BIT5 | BIT6))
+#define HFA384x_WEPFLAGS_FIRMWARE_WPA ((UINT16)BIT8)
+#define HFA384x_WEPFLAGS_HOST_MIC ((UINT16)BIT9)
+#define HFA384x_ROAMMODE_FWSCAN_FWROAM ((UINT16)1)
+#define HFA384x_ROAMMODE_FWSCAN_HOSTROAM ((UINT16)2)
+#define HFA384x_ROAMMODE_HOSTSCAN_HOSTROAM ((UINT16)3)
+#define HFA384x_PORTSTATUS_DISABLED ((UINT16)1)
+#define HFA384x_PORTSTATUS_INITSRCH ((UINT16)2)
+#define HFA384x_PORTSTATUS_CONN_IBSS ((UINT16)3)
+#define HFA384x_PORTSTATUS_CONN_ESS ((UINT16)4)
+#define HFA384x_PORTSTATUS_OOR_ESS ((UINT16)5)
+#define HFA384x_PORTSTATUS_CONN_WDS ((UINT16)6)
+#define HFA384x_PORTSTATUS_HOSTAP ((UINT16)8)
+#define HFA384x_RATEBIT_1 ((UINT16)1)
+#define HFA384x_RATEBIT_2 ((UINT16)2)
+#define HFA384x_RATEBIT_5dot5 ((UINT16)4)
+#define HFA384x_RATEBIT_11 ((UINT16)8)
+
+/*--- Just some symbolic names for legibility -------*/
+#define HFA384x_TXCMD_NORECL ((UINT16)0)
+#define HFA384x_TXCMD_RECL ((UINT16)1)
+
+/*--- MAC Internal memory constants and macros ------*/
+/* masks and macros used to manipulate MAC internal memory addresses. */
+/* MAC internal memory addresses are 23 bit quantities. The MAC uses
+ * a paged address space where the upper 16 bits are the page number
+ * and the lower 7 bits are the offset. There are various Host API
+ * elements that require two 16-bit quantities to specify a MAC
+ * internal memory address. Unfortunately, some of the API's use a
+ * page/offset format where the offset value is JUST the lower seven
+ * bits and the page is the remaining 16 bits. Some of the API's
+ * assume that the 23 bit address has been split at the 16th bit. We
+ * refer to these two formats as AUX format and CMD format. The
+ * macros below help handle some of this.
+ */
+
+/* Handy constant */
+#define HFA384x_ADDR_AUX_OFF_MAX ((UINT16)0x007f)
+
+/* Mask bits for discarding unwanted pieces in a flat address */
+#define HFA384x_ADDR_FLAT_AUX_PAGE_MASK (0x007fff80)
+#define HFA384x_ADDR_FLAT_AUX_OFF_MASK (0x0000007f)
+#define HFA384x_ADDR_FLAT_CMD_PAGE_MASK (0xffff0000)
+#define HFA384x_ADDR_FLAT_CMD_OFF_MASK (0x0000ffff)
+
+/* Mask bits for discarding unwanted pieces in AUX format 16-bit address parts */
+#define HFA384x_ADDR_AUX_PAGE_MASK (0xffff)
+#define HFA384x_ADDR_AUX_OFF_MASK (0x007f)
+
+/* Mask bits for discarding unwanted pieces in CMD format 16-bit address parts */
+#define HFA384x_ADDR_CMD_PAGE_MASK (0x007f)
+#define HFA384x_ADDR_CMD_OFF_MASK (0xffff)
+
+/* Make a 32-bit flat address from AUX format 16-bit page and offset */
+#define HFA384x_ADDR_AUX_MKFLAT(p,o) \
+ (((UINT32)(((UINT16)(p))&HFA384x_ADDR_AUX_PAGE_MASK)) <<7) | \
+ ((UINT32)(((UINT16)(o))&HFA384x_ADDR_AUX_OFF_MASK))
+
+/* Make a 32-bit flat address from CMD format 16-bit page and offset */
+#define HFA384x_ADDR_CMD_MKFLAT(p,o) \
+ (((UINT32)(((UINT16)(p))&HFA384x_ADDR_CMD_PAGE_MASK)) <<16) | \
+ ((UINT32)(((UINT16)(o))&HFA384x_ADDR_CMD_OFF_MASK))
+
+/* Make AUX format offset and page from a 32-bit flat address */
+#define HFA384x_ADDR_AUX_MKPAGE(f) \
+ ((UINT16)((((UINT32)(f))&HFA384x_ADDR_FLAT_AUX_PAGE_MASK)>>7))
+#define HFA384x_ADDR_AUX_MKOFF(f) \
+ ((UINT16)(((UINT32)(f))&HFA384x_ADDR_FLAT_AUX_OFF_MASK))
+
+/* Make CMD format offset and page from a 32-bit flat address */
+#define HFA384x_ADDR_CMD_MKPAGE(f) \
+ ((UINT16)((((UINT32)(f))&HFA384x_ADDR_FLAT_CMD_PAGE_MASK)>>16))
+#define HFA384x_ADDR_CMD_MKOFF(f) \
+ ((UINT16)(((UINT32)(f))&HFA384x_ADDR_FLAT_CMD_OFF_MASK))
+
+/*--- Aux register masks/tests ----------------------*/
+/* Some of the upper bits of the AUX offset register are used to */
+/* select address space. */
+#define HFA384x_AUX_CTL_EXTDS (0x00)
+#define HFA384x_AUX_CTL_NV (0x01)
+#define HFA384x_AUX_CTL_PHY (0x02)
+#define HFA384x_AUX_CTL_ICSRAM (0x03)
+
+/* Make AUX register offset and page values from a flat address */
+#define HFA384x_AUX_MKOFF(f, c) \
+ (HFA384x_ADDR_AUX_MKOFF(f) | (((UINT16)(c))<<12))
+#define HFA384x_AUX_MKPAGE(f) HFA384x_ADDR_AUX_MKPAGE(f)
+
+
+/*--- Controller Memory addresses -------------------*/
+#define HFA3842_PDA_BASE (0x007f0000UL)
+#define HFA3841_PDA_BASE (0x003f0000UL)
+#define HFA3841_PDA_BOGUS_BASE (0x00390000UL)
+
+/*--- Driver Download states -----------------------*/
+#define HFA384x_DLSTATE_DISABLED 0
+#define HFA384x_DLSTATE_RAMENABLED 1
+#define HFA384x_DLSTATE_FLASHENABLED 2
+#define HFA384x_DLSTATE_FLASHWRITTEN 3
+#define HFA384x_DLSTATE_FLASHWRITEPENDING 4
+#define HFA384x_DLSTATE_GENESIS 5
+
+/*--- Register I/O offsets --------------------------*/
+#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX))
+
+#define HFA384x_CMD_OFF (0x00)
+#define HFA384x_PARAM0_OFF (0x02)
+#define HFA384x_PARAM1_OFF (0x04)
+#define HFA384x_PARAM2_OFF (0x06)
+#define HFA384x_STATUS_OFF (0x08)
+#define HFA384x_RESP0_OFF (0x0A)
+#define HFA384x_RESP1_OFF (0x0C)
+#define HFA384x_RESP2_OFF (0x0E)
+#define HFA384x_INFOFID_OFF (0x10)
+#define HFA384x_RXFID_OFF (0x20)
+#define HFA384x_ALLOCFID_OFF (0x22)
+#define HFA384x_TXCOMPLFID_OFF (0x24)
+#define HFA384x_SELECT0_OFF (0x18)
+#define HFA384x_OFFSET0_OFF (0x1C)
+#define HFA384x_DATA0_OFF (0x36)
+#define HFA384x_SELECT1_OFF (0x1A)
+#define HFA384x_OFFSET1_OFF (0x1E)
+#define HFA384x_DATA1_OFF (0x38)
+#define HFA384x_EVSTAT_OFF (0x30)
+#define HFA384x_INTEN_OFF (0x32)
+#define HFA384x_EVACK_OFF (0x34)
+#define HFA384x_CONTROL_OFF (0x14)
+#define HFA384x_SWSUPPORT0_OFF (0x28)
+#define HFA384x_SWSUPPORT1_OFF (0x2A)
+#define HFA384x_SWSUPPORT2_OFF (0x2C)
+#define HFA384x_AUXPAGE_OFF (0x3A)
+#define HFA384x_AUXOFFSET_OFF (0x3C)
+#define HFA384x_AUXDATA_OFF (0x3E)
+
+#elif (WLAN_HOSTIF == WLAN_PCI || WLAN_HOSTIF == WLAN_USB)
+
+#define HFA384x_CMD_OFF (0x00)
+#define HFA384x_PARAM0_OFF (0x04)
+#define HFA384x_PARAM1_OFF (0x08)
+#define HFA384x_PARAM2_OFF (0x0c)
+#define HFA384x_STATUS_OFF (0x10)
+#define HFA384x_RESP0_OFF (0x14)
+#define HFA384x_RESP1_OFF (0x18)
+#define HFA384x_RESP2_OFF (0x1c)
+#define HFA384x_INFOFID_OFF (0x20)
+#define HFA384x_RXFID_OFF (0x40)
+#define HFA384x_ALLOCFID_OFF (0x44)
+#define HFA384x_TXCOMPLFID_OFF (0x48)
+#define HFA384x_SELECT0_OFF (0x30)
+#define HFA384x_OFFSET0_OFF (0x38)
+#define HFA384x_DATA0_OFF (0x6c)
+#define HFA384x_SELECT1_OFF (0x34)
+#define HFA384x_OFFSET1_OFF (0x3c)
+#define HFA384x_DATA1_OFF (0x70)
+#define HFA384x_EVSTAT_OFF (0x60)
+#define HFA384x_INTEN_OFF (0x64)
+#define HFA384x_EVACK_OFF (0x68)
+#define HFA384x_CONTROL_OFF (0x28)
+#define HFA384x_SWSUPPORT0_OFF (0x50)
+#define HFA384x_SWSUPPORT1_OFF (0x54)
+#define HFA384x_SWSUPPORT2_OFF (0x58)
+#define HFA384x_AUXPAGE_OFF (0x74)
+#define HFA384x_AUXOFFSET_OFF (0x78)
+#define HFA384x_AUXDATA_OFF (0x7c)
+#define HFA384x_PCICOR_OFF (0x4c)
+#define HFA384x_PCIHCR_OFF (0x5c)
+#define HFA384x_PCI_M0_ADDRH_OFF (0x80)
+#define HFA384x_PCI_M0_ADDRL_OFF (0x84)
+#define HFA384x_PCI_M0_LEN_OFF (0x88)
+#define HFA384x_PCI_M0_CTL_OFF (0x8c)
+#define HFA384x_PCI_STATUS_OFF (0x98)
+#define HFA384x_PCI_M1_ADDRH_OFF (0xa0)
+#define HFA384x_PCI_M1_ADDRL_OFF (0xa4)
+#define HFA384x_PCI_M1_LEN_OFF (0xa8)
+#define HFA384x_PCI_M1_CTL_OFF (0xac)
+
+#endif
+
+/*--- Register Field Masks --------------------------*/
+#define HFA384x_CMD_BUSY ((UINT16)BIT15)
+#define HFA384x_CMD_AINFO ((UINT16)(BIT14 | BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8))
+#define HFA384x_CMD_MACPORT ((UINT16)(BIT10 | BIT9 | BIT8))
+#define HFA384x_CMD_RECL ((UINT16)BIT8)
+#define HFA384x_CMD_WRITE ((UINT16)BIT8)
+#define HFA384x_CMD_PROGMODE ((UINT16)(BIT9 | BIT8))
+#define HFA384x_CMD_CMDCODE ((UINT16)(BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0))
+
+#define HFA384x_STATUS_RESULT ((UINT16)(BIT14 | BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8))
+#define HFA384x_STATUS_CMDCODE ((UINT16)(BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0))
+
+#define HFA384x_OFFSET_BUSY ((UINT16)BIT15)
+#define HFA384x_OFFSET_ERR ((UINT16)BIT14)
+#define HFA384x_OFFSET_DATAOFF ((UINT16)(BIT11 | BIT10 | BIT9 | BIT8 | BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1))
+
+#define HFA384x_EVSTAT_TICK ((UINT16)BIT15)
+#define HFA384x_EVSTAT_WTERR ((UINT16)BIT14)
+#define HFA384x_EVSTAT_INFDROP ((UINT16)BIT13)
+#define HFA384x_EVSTAT_INFO ((UINT16)BIT7)
+#define HFA384x_EVSTAT_DTIM ((UINT16)BIT5)
+#define HFA384x_EVSTAT_CMD ((UINT16)BIT4)
+#define HFA384x_EVSTAT_ALLOC ((UINT16)BIT3)
+#define HFA384x_EVSTAT_TXEXC ((UINT16)BIT2)
+#define HFA384x_EVSTAT_TX ((UINT16)BIT1)
+#define HFA384x_EVSTAT_RX ((UINT16)BIT0)
+
+#define HFA384x_INT_BAP_OP (HFA384x_EVSTAT_INFO|HFA384x_EVSTAT_RX|HFA384x_EVSTAT_TX|HFA384x_EVSTAT_TXEXC)
+
+#define HFA384x_INT_NORMAL (HFA384x_EVSTAT_INFO|HFA384x_EVSTAT_RX|HFA384x_EVSTAT_TX|HFA384x_EVSTAT_TXEXC|HFA384x_EVSTAT_INFDROP|HFA384x_EVSTAT_ALLOC|HFA384x_EVSTAT_DTIM)
+
+#define HFA384x_INTEN_TICK ((UINT16)BIT15)
+#define HFA384x_INTEN_WTERR ((UINT16)BIT14)
+#define HFA384x_INTEN_INFDROP ((UINT16)BIT13)
+#define HFA384x_INTEN_INFO ((UINT16)BIT7)
+#define HFA384x_INTEN_DTIM ((UINT16)BIT5)
+#define HFA384x_INTEN_CMD ((UINT16)BIT4)
+#define HFA384x_INTEN_ALLOC ((UINT16)BIT3)
+#define HFA384x_INTEN_TXEXC ((UINT16)BIT2)
+#define HFA384x_INTEN_TX ((UINT16)BIT1)
+#define HFA384x_INTEN_RX ((UINT16)BIT0)
+
+#define HFA384x_EVACK_TICK ((UINT16)BIT15)
+#define HFA384x_EVACK_WTERR ((UINT16)BIT14)
+#define HFA384x_EVACK_INFDROP ((UINT16)BIT13)
+#define HFA384x_EVACK_INFO ((UINT16)BIT7)
+#define HFA384x_EVACK_DTIM ((UINT16)BIT5)
+#define HFA384x_EVACK_CMD ((UINT16)BIT4)
+#define HFA384x_EVACK_ALLOC ((UINT16)BIT3)
+#define HFA384x_EVACK_TXEXC ((UINT16)BIT2)
+#define HFA384x_EVACK_TX ((UINT16)BIT1)
+#define HFA384x_EVACK_RX ((UINT16)BIT0)
+
+#define HFA384x_CONTROL_AUXEN ((UINT16)(BIT15 | BIT14))
+
+
+/*--- Command Code Constants --------------------------*/
+/*--- Controller Commands --------------------------*/
+#define HFA384x_CMDCODE_INIT ((UINT16)0x00)
+#define HFA384x_CMDCODE_ENABLE ((UINT16)0x01)
+#define HFA384x_CMDCODE_DISABLE ((UINT16)0x02)
+#define HFA384x_CMDCODE_DIAG ((UINT16)0x03)
+
+/*--- Buffer Mgmt Commands --------------------------*/
+#define HFA384x_CMDCODE_ALLOC ((UINT16)0x0A)
+#define HFA384x_CMDCODE_TX ((UINT16)0x0B)
+#define HFA384x_CMDCODE_CLRPRST ((UINT16)0x12)
+
+/*--- Regulate Commands --------------------------*/
+#define HFA384x_CMDCODE_NOTIFY ((UINT16)0x10)
+#define HFA384x_CMDCODE_INQ ((UINT16)0x11)
+
+/*--- Configure Commands --------------------------*/
+#define HFA384x_CMDCODE_ACCESS ((UINT16)0x21)
+#define HFA384x_CMDCODE_DOWNLD ((UINT16)0x22)
+
+/*--- Debugging Commands -----------------------------*/
+#define HFA384x_CMDCODE_MONITOR ((UINT16)(0x38))
+#define HFA384x_MONITOR_ENABLE ((UINT16)(0x0b))
+#define HFA384x_MONITOR_DISABLE ((UINT16)(0x0f))
+
+/*--- Result Codes --------------------------*/
+#define HFA384x_SUCCESS ((UINT16)(0x00))
+#define HFA384x_CARD_FAIL ((UINT16)(0x01))
+#define HFA384x_NO_BUFF ((UINT16)(0x05))
+#define HFA384x_CMD_ERR ((UINT16)(0x7F))
+
+/*--- Programming Modes --------------------------
+ MODE 0: Disable programming
+ MODE 1: Enable volatile memory programming
+ MODE 2: Enable non-volatile memory programming
+ MODE 3: Program non-volatile memory section
+--------------------------------------------------*/
+#define HFA384x_PROGMODE_DISABLE ((UINT16)0x00)
+#define HFA384x_PROGMODE_RAM ((UINT16)0x01)
+#define HFA384x_PROGMODE_NV ((UINT16)0x02)
+#define HFA384x_PROGMODE_NVWRITE ((UINT16)0x03)
+
+/*--- AUX register enable --------------------------*/
+#define HFA384x_AUXPW0 ((UINT16)0xfe01)
+#define HFA384x_AUXPW1 ((UINT16)0xdc23)
+#define HFA384x_AUXPW2 ((UINT16)0xba45)
+
+#define HFA384x_CONTROL_AUX_ISDISABLED ((UINT16)0x0000)
+#define HFA384x_CONTROL_AUX_ISENABLED ((UINT16)0xc000)
+#define HFA384x_CONTROL_AUX_DOENABLE ((UINT16)0x8000)
+#define HFA384x_CONTROL_AUX_DODISABLE ((UINT16)0x4000)
+
+/*--- Record ID Constants --------------------------*/
+/*--------------------------------------------------------------------
+Configuration RIDs: Network Parameters, Static Configuration Entities
+--------------------------------------------------------------------*/
+#define HFA384x_RID_CNFPORTTYPE ((UINT16)0xFC00)
+#define HFA384x_RID_CNFOWNMACADDR ((UINT16)0xFC01)
+#define HFA384x_RID_CNFDESIREDSSID ((UINT16)0xFC02)
+#define HFA384x_RID_CNFOWNCHANNEL ((UINT16)0xFC03)
+#define HFA384x_RID_CNFOWNSSID ((UINT16)0xFC04)
+#define HFA384x_RID_CNFOWNATIMWIN ((UINT16)0xFC05)
+#define HFA384x_RID_CNFSYSSCALE ((UINT16)0xFC06)
+#define HFA384x_RID_CNFMAXDATALEN ((UINT16)0xFC07)
+#define HFA384x_RID_CNFWDSADDR ((UINT16)0xFC08)
+#define HFA384x_RID_CNFPMENABLED ((UINT16)0xFC09)
+#define HFA384x_RID_CNFPMEPS ((UINT16)0xFC0A)
+#define HFA384x_RID_CNFMULTICASTRX ((UINT16)0xFC0B)
+#define HFA384x_RID_CNFMAXSLEEPDUR ((UINT16)0xFC0C)
+#define HFA384x_RID_CNFPMHOLDDUR ((UINT16)0xFC0D)
+#define HFA384x_RID_CNFOWNNAME ((UINT16)0xFC0E)
+#define HFA384x_RID_CNFOWNDTIMPER ((UINT16)0xFC10)
+#define HFA384x_RID_CNFWDSADDR1 ((UINT16)0xFC11)
+#define HFA384x_RID_CNFWDSADDR2 ((UINT16)0xFC12)
+#define HFA384x_RID_CNFWDSADDR3 ((UINT16)0xFC13)
+#define HFA384x_RID_CNFWDSADDR4 ((UINT16)0xFC14)
+#define HFA384x_RID_CNFWDSADDR5 ((UINT16)0xFC15)
+#define HFA384x_RID_CNFWDSADDR6 ((UINT16)0xFC16)
+#define HFA384x_RID_CNFMCASTPMBUFF ((UINT16)0xFC17)
+
+/*--------------------------------------------------------------------
+Configuration RID lengths: Network Params, Static Config Entities
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+/* TODO: fill in the rest of these */
+#define HFA384x_RID_CNFPORTTYPE_LEN ((UINT16)2)
+#define HFA384x_RID_CNFOWNMACADDR_LEN ((UINT16)6)
+#define HFA384x_RID_CNFDESIREDSSID_LEN ((UINT16)34)
+#define HFA384x_RID_CNFOWNCHANNEL_LEN ((UINT16)2)
+#define HFA384x_RID_CNFOWNSSID_LEN ((UINT16)34)
+#define HFA384x_RID_CNFOWNATIMWIN_LEN ((UINT16)2)
+#define HFA384x_RID_CNFSYSSCALE_LEN ((UINT16)0)
+#define HFA384x_RID_CNFMAXDATALEN_LEN ((UINT16)0)
+#define HFA384x_RID_CNFWDSADDR_LEN ((UINT16)6)
+#define HFA384x_RID_CNFPMENABLED_LEN ((UINT16)0)
+#define HFA384x_RID_CNFPMEPS_LEN ((UINT16)0)
+#define HFA384x_RID_CNFMULTICASTRX_LEN ((UINT16)0)
+#define HFA384x_RID_CNFMAXSLEEPDUR_LEN ((UINT16)0)
+#define HFA384x_RID_CNFPMHOLDDUR_LEN ((UINT16)0)
+#define HFA384x_RID_CNFOWNNAME_LEN ((UINT16)34)
+#define HFA384x_RID_CNFOWNDTIMPER_LEN ((UINT16)0)
+#define HFA384x_RID_CNFWDSADDR1_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWDSADDR2_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWDSADDR3_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWDSADDR4_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWDSADDR5_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWDSADDR6_LEN ((UINT16)6)
+#define HFA384x_RID_CNFMCASTPMBUFF_LEN ((UINT16)0)
+#define HFA384x_RID_CNFAUTHENTICATION_LEN ((UINT16)sizeof(UINT16))
+#define HFA384x_RID_CNFMAXSLEEPDUR_LEN ((UINT16)0)
+
+/*--------------------------------------------------------------------
+Configuration RIDs: Network Parameters, Dynamic Configuration Entities
+--------------------------------------------------------------------*/
+#define HFA384x_RID_GROUPADDR ((UINT16)0xFC80)
+#define HFA384x_RID_CREATEIBSS ((UINT16)0xFC81)
+#define HFA384x_RID_FRAGTHRESH ((UINT16)0xFC82)
+#define HFA384x_RID_RTSTHRESH ((UINT16)0xFC83)
+#define HFA384x_RID_TXRATECNTL ((UINT16)0xFC84)
+#define HFA384x_RID_PROMISCMODE ((UINT16)0xFC85)
+#define HFA384x_RID_FRAGTHRESH0 ((UINT16)0xFC90)
+#define HFA384x_RID_FRAGTHRESH1 ((UINT16)0xFC91)
+#define HFA384x_RID_FRAGTHRESH2 ((UINT16)0xFC92)
+#define HFA384x_RID_FRAGTHRESH3 ((UINT16)0xFC93)
+#define HFA384x_RID_FRAGTHRESH4 ((UINT16)0xFC94)
+#define HFA384x_RID_FRAGTHRESH5 ((UINT16)0xFC95)
+#define HFA384x_RID_FRAGTHRESH6 ((UINT16)0xFC96)
+#define HFA384x_RID_RTSTHRESH0 ((UINT16)0xFC97)
+#define HFA384x_RID_RTSTHRESH1 ((UINT16)0xFC98)
+#define HFA384x_RID_RTSTHRESH2 ((UINT16)0xFC99)
+#define HFA384x_RID_RTSTHRESH3 ((UINT16)0xFC9A)
+#define HFA384x_RID_RTSTHRESH4 ((UINT16)0xFC9B)
+#define HFA384x_RID_RTSTHRESH5 ((UINT16)0xFC9C)
+#define HFA384x_RID_RTSTHRESH6 ((UINT16)0xFC9D)
+#define HFA384x_RID_TXRATECNTL0 ((UINT16)0xFC9E)
+#define HFA384x_RID_TXRATECNTL1 ((UINT16)0xFC9F)
+#define HFA384x_RID_TXRATECNTL2 ((UINT16)0xFCA0)
+#define HFA384x_RID_TXRATECNTL3 ((UINT16)0xFCA1)
+#define HFA384x_RID_TXRATECNTL4 ((UINT16)0xFCA2)
+#define HFA384x_RID_TXRATECNTL5 ((UINT16)0xFCA3)
+#define HFA384x_RID_TXRATECNTL6 ((UINT16)0xFCA4)
+
+/*--------------------------------------------------------------------
+Configuration RID Lengths: Network Param, Dynamic Config Entities
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+/* TODO: fill in the rest of these */
+#define HFA384x_RID_GROUPADDR_LEN ((UINT16)16 * WLAN_ADDR_LEN)
+#define HFA384x_RID_CREATEIBSS_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL_LEN ((UINT16)4)
+#define HFA384x_RID_PROMISCMODE_LEN ((UINT16)2)
+#define HFA384x_RID_FRAGTHRESH0_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH1_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH2_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH3_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH4_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH5_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH6_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH0_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH1_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH2_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH3_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH4_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH5_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH6_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL0_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL1_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL2_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL3_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL4_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL5_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL6_LEN ((UINT16)0)
+
+/*--------------------------------------------------------------------
+Configuration RIDs: Behavior Parameters
+--------------------------------------------------------------------*/
+#define HFA384x_RID_ITICKTIME ((UINT16)0xFCE0)
+
+/*--------------------------------------------------------------------
+Configuration RID Lengths: Behavior Parameters
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_ITICKTIME_LEN ((UINT16)2)
+
+/*----------------------------------------------------------------------
+Information RIDs: NIC Information
+--------------------------------------------------------------------*/
+#define HFA384x_RID_MAXLOADTIME ((UINT16)0xFD00)
+#define HFA384x_RID_DOWNLOADBUFFER ((UINT16)0xFD01)
+#define HFA384x_RID_PRIIDENTITY ((UINT16)0xFD02)
+#define HFA384x_RID_PRISUPRANGE ((UINT16)0xFD03)
+#define HFA384x_RID_PRI_CFIACTRANGES ((UINT16)0xFD04)
+#define HFA384x_RID_NICSERIALNUMBER ((UINT16)0xFD0A)
+#define HFA384x_RID_NICIDENTITY ((UINT16)0xFD0B)
+#define HFA384x_RID_MFISUPRANGE ((UINT16)0xFD0C)
+#define HFA384x_RID_CFISUPRANGE ((UINT16)0xFD0D)
+#define HFA384x_RID_CHANNELLIST ((UINT16)0xFD10)
+#define HFA384x_RID_REGULATORYDOMAINS ((UINT16)0xFD11)
+#define HFA384x_RID_TEMPTYPE ((UINT16)0xFD12)
+#define HFA384x_RID_CIS ((UINT16)0xFD13)
+#define HFA384x_RID_STAIDENTITY ((UINT16)0xFD20)
+#define HFA384x_RID_STASUPRANGE ((UINT16)0xFD21)
+#define HFA384x_RID_STA_MFIACTRANGES ((UINT16)0xFD22)
+#define HFA384x_RID_STA_CFIACTRANGES ((UINT16)0xFD23)
+#define HFA384x_RID_BUILDSEQ ((UINT16)0xFFFE)
+#define HFA384x_RID_FWID ((UINT16)0xFFFF)
+
+/*----------------------------------------------------------------------
+Information RID Lengths: NIC Information
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_MAXLOADTIME_LEN ((UINT16)0)
+#define HFA384x_RID_DOWNLOADBUFFER_LEN ((UINT16)sizeof(hfa384x_downloadbuffer_t))
+#define HFA384x_RID_PRIIDENTITY_LEN ((UINT16)8)
+#define HFA384x_RID_PRISUPRANGE_LEN ((UINT16)10)
+#define HFA384x_RID_CFIACTRANGES_LEN ((UINT16)10)
+#define HFA384x_RID_NICSERIALNUMBER_LEN ((UINT16)12)
+#define HFA384x_RID_NICIDENTITY_LEN ((UINT16)8)
+#define HFA384x_RID_MFISUPRANGE_LEN ((UINT16)10)
+#define HFA384x_RID_CFISUPRANGE_LEN ((UINT16)10)
+#define HFA384x_RID_CHANNELLIST_LEN ((UINT16)0)
+#define HFA384x_RID_REGULATORYDOMAINS_LEN ((UINT16)12)
+#define HFA384x_RID_TEMPTYPE_LEN ((UINT16)0)
+#define HFA384x_RID_CIS_LEN ((UINT16)480)
+#define HFA384x_RID_STAIDENTITY_LEN ((UINT16)8)
+#define HFA384x_RID_STASUPRANGE_LEN ((UINT16)10)
+#define HFA384x_RID_MFIACTRANGES_LEN ((UINT16)10)
+#define HFA384x_RID_CFIACTRANGES2_LEN ((UINT16)10)
+#define HFA384x_RID_BUILDSEQ_LEN ((UINT16)sizeof(hfa384x_BuildSeq_t))
+#define HFA384x_RID_FWID_LEN ((UINT16)sizeof(hfa384x_FWID_t))
+
+/*--------------------------------------------------------------------
+Information RIDs: MAC Information
+--------------------------------------------------------------------*/
+#define HFA384x_RID_PORTSTATUS ((UINT16)0xFD40)
+#define HFA384x_RID_CURRENTSSID ((UINT16)0xFD41)
+#define HFA384x_RID_CURRENTBSSID ((UINT16)0xFD42)
+#define HFA384x_RID_COMMSQUALITY ((UINT16)0xFD43)
+#define HFA384x_RID_CURRENTTXRATE ((UINT16)0xFD44)
+#define HFA384x_RID_CURRENTBCNINT ((UINT16)0xFD45)
+#define HFA384x_RID_CURRENTSCALETHRESH ((UINT16)0xFD46)
+#define HFA384x_RID_PROTOCOLRSPTIME ((UINT16)0xFD47)
+#define HFA384x_RID_SHORTRETRYLIMIT ((UINT16)0xFD48)
+#define HFA384x_RID_LONGRETRYLIMIT ((UINT16)0xFD49)
+#define HFA384x_RID_MAXTXLIFETIME ((UINT16)0xFD4A)
+#define HFA384x_RID_MAXRXLIFETIME ((UINT16)0xFD4B)
+#define HFA384x_RID_CFPOLLABLE ((UINT16)0xFD4C)
+#define HFA384x_RID_AUTHALGORITHMS ((UINT16)0xFD4D)
+#define HFA384x_RID_PRIVACYOPTIMP ((UINT16)0xFD4F)
+#define HFA384x_RID_DBMCOMMSQUALITY ((UINT16)0xFD51)
+#define HFA384x_RID_CURRENTTXRATE1 ((UINT16)0xFD80)
+#define HFA384x_RID_CURRENTTXRATE2 ((UINT16)0xFD81)
+#define HFA384x_RID_CURRENTTXRATE3 ((UINT16)0xFD82)
+#define HFA384x_RID_CURRENTTXRATE4 ((UINT16)0xFD83)
+#define HFA384x_RID_CURRENTTXRATE5 ((UINT16)0xFD84)
+#define HFA384x_RID_CURRENTTXRATE6 ((UINT16)0xFD85)
+#define HFA384x_RID_OWNMACADDRESS ((UINT16)0xFD86)
+// #define HFA384x_RID_PCFINFO ((UINT16)0xFD87)
+#define HFA384x_RID_SCANRESULTS ((UINT16)0xFD88) // NEW
+#define HFA384x_RID_HOSTSCANRESULTS ((UINT16)0xFD89) // NEW
+#define HFA384x_RID_AUTHENTICATIONUSED ((UINT16)0xFD8A) // NEW
+#define HFA384x_RID_ASSOCIATEFAILURE ((UINT16)0xFD8D) // 1.8.0
+
+/*--------------------------------------------------------------------
+Information RID Lengths: MAC Information
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_PORTSTATUS_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTSSID_LEN ((UINT16)34)
+#define HFA384x_RID_CURRENTBSSID_LEN ((UINT16)WLAN_BSSID_LEN)
+#define HFA384x_RID_COMMSQUALITY_LEN ((UINT16)sizeof(hfa384x_commsquality_t))
+#define HFA384x_RID_DBMCOMMSQUALITY_LEN ((UINT16)sizeof(hfa384x_dbmcommsquality_t))
+#define HFA384x_RID_CURRENTTXRATE_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTBCNINT_LEN ((UINT16)0)
+#define HFA384x_RID_STACURSCALETHRESH_LEN ((UINT16)12)
+#define HFA384x_RID_APCURSCALETHRESH_LEN ((UINT16)6)
+#define HFA384x_RID_PROTOCOLRSPTIME_LEN ((UINT16)0)
+#define HFA384x_RID_SHORTRETRYLIMIT_LEN ((UINT16)0)
+#define HFA384x_RID_LONGRETRYLIMIT_LEN ((UINT16)0)
+#define HFA384x_RID_MAXTXLIFETIME_LEN ((UINT16)0)
+#define HFA384x_RID_MAXRXLIFETIME_LEN ((UINT16)0)
+#define HFA384x_RID_CFPOLLABLE_LEN ((UINT16)0)
+#define HFA384x_RID_AUTHALGORITHMS_LEN ((UINT16)4)
+#define HFA384x_RID_PRIVACYOPTIMP_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE1_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE2_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE3_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE4_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE5_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE6_LEN ((UINT16)0)
+#define HFA384x_RID_OWNMACADDRESS_LEN ((UINT16)6)
+#define HFA384x_RID_PCFINFO_LEN ((UINT16)6)
+#define HFA384x_RID_CNFAPPCFINFO_LEN ((UINT16)sizeof(hfa384x_PCFInfo_data_t))
+#define HFA384x_RID_SCANREQUEST_LEN ((UINT16)sizeof(hfa384x_ScanRequest_data_t))
+#define HFA384x_RID_JOINREQUEST_LEN ((UINT16)sizeof(hfa384x_JoinRequest_data_t))
+#define HFA384x_RID_AUTHENTICATESTA_LEN ((UINT16)sizeof(hfa384x_authenticateStation_data_t))
+#define HFA384x_RID_CHANNELINFOREQUEST_LEN ((UINT16)sizeof(hfa384x_ChannelInfoRequest_data_t))
+/*--------------------------------------------------------------------
+Information RIDs: Modem Information
+--------------------------------------------------------------------*/
+#define HFA384x_RID_PHYTYPE ((UINT16)0xFDC0)
+#define HFA384x_RID_CURRENTCHANNEL ((UINT16)0xFDC1)
+#define HFA384x_RID_CURRENTPOWERSTATE ((UINT16)0xFDC2)
+#define HFA384x_RID_CCAMODE ((UINT16)0xFDC3)
+#define HFA384x_RID_SUPPORTEDDATARATES ((UINT16)0xFDC6)
+#define HFA384x_RID_LFOSTATUS ((UINT16)0xFDC7) // 1.7.1
+
+/*--------------------------------------------------------------------
+Information RID Lengths: Modem Information
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_PHYTYPE_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTCHANNEL_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTPOWERSTATE_LEN ((UINT16)0)
+#define HFA384x_RID_CCAMODE_LEN ((UINT16)0)
+#define HFA384x_RID_SUPPORTEDDATARATES_LEN ((UINT16)10)
+
+/*--------------------------------------------------------------------
+API ENHANCEMENTS (NOT ALREADY IMPLEMENTED)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_CNFWEPDEFAULTKEYID ((UINT16)0xFC23)
+#define HFA384x_RID_CNFWEPDEFAULTKEY0 ((UINT16)0xFC24)
+#define HFA384x_RID_CNFWEPDEFAULTKEY1 ((UINT16)0xFC25)
+#define HFA384x_RID_CNFWEPDEFAULTKEY2 ((UINT16)0xFC26)
+#define HFA384x_RID_CNFWEPDEFAULTKEY3 ((UINT16)0xFC27)
+#define HFA384x_RID_CNFWEPFLAGS ((UINT16)0xFC28)
+#define HFA384x_RID_CNFWEPKEYMAPTABLE ((UINT16)0xFC29)
+#define HFA384x_RID_CNFAUTHENTICATION ((UINT16)0xFC2A)
+#define HFA384x_RID_CNFMAXASSOCSTATIONS ((UINT16)0xFC2B)
+#define HFA384x_RID_CNFTXCONTROL ((UINT16)0xFC2C)
+#define HFA384x_RID_CNFROAMINGMODE ((UINT16)0xFC2D)
+#define HFA384x_RID_CNFHOSTAUTHASSOC ((UINT16)0xFC2E)
+#define HFA384x_RID_CNFRCVCRCERROR ((UINT16)0xFC30)
+// #define HFA384x_RID_CNFMMLIFE ((UINT16)0xFC31)
+#define HFA384x_RID_CNFALTRETRYCNT ((UINT16)0xFC32)
+#define HFA384x_RID_CNFAPBCNINT ((UINT16)0xFC33)
+#define HFA384x_RID_CNFAPPCFINFO ((UINT16)0xFC34)
+#define HFA384x_RID_CNFSTAPCFINFO ((UINT16)0xFC35)
+#define HFA384x_RID_CNFPRIORITYQUSAGE ((UINT16)0xFC37)
+#define HFA384x_RID_CNFTIMCTRL ((UINT16)0xFC40)
+#define HFA384x_RID_CNFTHIRTY2TALLY ((UINT16)0xFC42)
+#define HFA384x_RID_CNFENHSECURITY ((UINT16)0xFC43)
+#define HFA384x_RID_CNFDBMADJUST ((UINT16)0xFC46) // NEW
+#define HFA384x_RID_CNFWPADATA ((UINT16)0xFC48) // 1.7.0
+#define HFA384x_RID_CNFPROPOGATIONDELAY ((UINT16)0xFC49) // 1.7.6
+#define HFA384x_RID_CNFSHORTPREAMBLE ((UINT16)0xFCB0)
+#define HFA384x_RID_CNFEXCLONGPREAMBLE ((UINT16)0xFCB1)
+#define HFA384x_RID_CNFAUTHRSPTIMEOUT ((UINT16)0xFCB2)
+#define HFA384x_RID_CNFBASICRATES ((UINT16)0xFCB3)
+#define HFA384x_RID_CNFSUPPRATES ((UINT16)0xFCB4)
+#define HFA384x_RID_CNFFALLBACKCTRL ((UINT16)0xFCB5) // NEW
+#define HFA384x_RID_WEPKEYSTATUS ((UINT16)0xFCB6) // NEW
+#define HFA384x_RID_WEPKEYMAPINDEX ((UINT16)0xFCB7) // NEW
+#define HFA384x_RID_BROADCASTKEYID ((UINT16)0xFCB8) // NEW
+#define HFA384x_RID_ENTSECFLAGEYID ((UINT16)0xFCB9) // NEW
+#define HFA384x_RID_CNFPASSIVESCANCTRL ((UINT16)0xFCBA) // NEW STA
+#define HFA384x_RID_CNFWPAHANDLING ((UINT16)0xFCBB) // 1.7.0
+#define HFA384x_RID_MDCCONTROL ((UINT16)0xFCBC) // 1.7.0/1.4.0
+#define HFA384x_RID_MDCCOUNTRY ((UINT16)0xFCBD) // 1.7.0/1.4.0
+#define HFA384x_RID_TXPOWERMAX ((UINT16)0xFCBE) // 1.7.0/1.4.0
+#define HFA384x_RID_CNFLFOENBLED ((UINT16)0xFCBF) // 1.6.3
+#define HFA384x_RID_CAPINFO ((UINT16)0xFCC0) // 1.7.0/1.3.7
+#define HFA384x_RID_LISTENINTERVAL ((UINT16)0xFCC1) // 1.7.0/1.3.7
+#define HFA384x_RID_DIVERSITYENABLED ((UINT16)0xFCC2) // 1.7.0/1.3.7
+#define HFA384x_RID_LED_CONTROL ((UINT16)0xFCC4) // 1.7.6
+#define HFA384x_RID_HFO_DELAY ((UINT16)0xFCC5) // 1.7.6
+#define HFA384x_RID_DISSALOWEDBSSID ((UINT16)0xFCC6) // 1.8.0
+#define HFA384x_RID_SCANREQUEST ((UINT16)0xFCE1)
+#define HFA384x_RID_JOINREQUEST ((UINT16)0xFCE2)
+#define HFA384x_RID_AUTHENTICATESTA ((UINT16)0xFCE3)
+#define HFA384x_RID_CHANNELINFOREQUEST ((UINT16)0xFCE4)
+#define HFA384x_RID_HOSTSCAN ((UINT16)0xFCE5) // NEW STA
+#define HFA384x_RID_ASSOCIATESTA ((UINT16)0xFCE6)
+
+#define HFA384x_RID_CNFWEPDEFAULTKEY_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWEP128DEFAULTKEY_LEN ((UINT16)14)
+#define HFA384x_RID_CNFPRIOQUSAGE_LEN ((UINT16)4)
+/*--------------------------------------------------------------------
+PD Record codes
+--------------------------------------------------------------------*/
+#define HFA384x_PDR_PCB_PARTNUM ((UINT16)0x0001)
+#define HFA384x_PDR_PDAVER ((UINT16)0x0002)
+#define HFA384x_PDR_NIC_SERIAL ((UINT16)0x0003)
+#define HFA384x_PDR_MKK_MEASUREMENTS ((UINT16)0x0004)
+#define HFA384x_PDR_NIC_RAMSIZE ((UINT16)0x0005)
+#define HFA384x_PDR_MFISUPRANGE ((UINT16)0x0006)
+#define HFA384x_PDR_CFISUPRANGE ((UINT16)0x0007)
+#define HFA384x_PDR_NICID ((UINT16)0x0008)
+//#define HFA384x_PDR_REFDAC_MEASUREMENTS ((UINT16)0x0010)
+//#define HFA384x_PDR_VGDAC_MEASUREMENTS ((UINT16)0x0020)
+//#define HFA384x_PDR_LEVEL_COMP_MEASUREMENTS ((UINT16)0x0030)
+//#define HFA384x_PDR_MODEM_TRIMDAC_MEASUREMENTS ((UINT16)0x0040)
+//#define HFA384x_PDR_COREGA_HACK ((UINT16)0x00ff)
+#define HFA384x_PDR_MAC_ADDRESS ((UINT16)0x0101)
+//#define HFA384x_PDR_MKK_CALLNAME ((UINT16)0x0102)
+#define HFA384x_PDR_REGDOMAIN ((UINT16)0x0103)
+#define HFA384x_PDR_ALLOWED_CHANNEL ((UINT16)0x0104)
+#define HFA384x_PDR_DEFAULT_CHANNEL ((UINT16)0x0105)
+//#define HFA384x_PDR_PRIVACY_OPTION ((UINT16)0x0106)
+#define HFA384x_PDR_TEMPTYPE ((UINT16)0x0107)
+//#define HFA384x_PDR_REFDAC_SETUP ((UINT16)0x0110)
+//#define HFA384x_PDR_VGDAC_SETUP ((UINT16)0x0120)
+//#define HFA384x_PDR_LEVEL_COMP_SETUP ((UINT16)0x0130)
+//#define HFA384x_PDR_TRIMDAC_SETUP ((UINT16)0x0140)
+#define HFA384x_PDR_IFR_SETTING ((UINT16)0x0200)
+#define HFA384x_PDR_RFR_SETTING ((UINT16)0x0201)
+#define HFA384x_PDR_HFA3861_BASELINE ((UINT16)0x0202)
+#define HFA384x_PDR_HFA3861_SHADOW ((UINT16)0x0203)
+#define HFA384x_PDR_HFA3861_IFRF ((UINT16)0x0204)
+#define HFA384x_PDR_HFA3861_CHCALSP ((UINT16)0x0300)
+#define HFA384x_PDR_HFA3861_CHCALI ((UINT16)0x0301)
+#define HFA384x_PDR_MAX_TX_POWER ((UINT16)0x0302)
+#define HFA384x_PDR_MASTER_CHAN_LIST ((UINT16)0x0303)
+#define HFA384x_PDR_3842_NIC_CONFIG ((UINT16)0x0400)
+#define HFA384x_PDR_USB_ID ((UINT16)0x0401)
+#define HFA384x_PDR_PCI_ID ((UINT16)0x0402)
+#define HFA384x_PDR_PCI_IFCONF ((UINT16)0x0403)
+#define HFA384x_PDR_PCI_PMCONF ((UINT16)0x0404)
+#define HFA384x_PDR_RFENRGY ((UINT16)0x0406)
+#define HFA384x_PDR_USB_POWER_TYPE ((UINT16)0x0407)
+//#define HFA384x_PDR_UNKNOWN408 ((UINT16)0x0408)
+#define HFA384x_PDR_USB_MAX_POWER ((UINT16)0x0409)
+#define HFA384x_PDR_USB_MANUFACTURER ((UINT16)0x0410)
+#define HFA384x_PDR_USB_PRODUCT ((UINT16)0x0411)
+#define HFA384x_PDR_ANT_DIVERSITY ((UINT16)0x0412)
+#define HFA384x_PDR_HFO_DELAY ((UINT16)0x0413)
+#define HFA384x_PDR_SCALE_THRESH ((UINT16)0x0414)
+
+#define HFA384x_PDR_HFA3861_MANF_TESTSP ((UINT16)0x0900)
+#define HFA384x_PDR_HFA3861_MANF_TESTI ((UINT16)0x0901)
+#define HFA384x_PDR_END_OF_PDA ((UINT16)0x0000)
+
+
+/*=============================================================*/
+/*------ Macros -----------------------------------------------*/
+
+/*--- Register ID macros ------------------------*/
+
+#define HFA384x_CMD HFA384x_CMD_OFF
+#define HFA384x_PARAM0 HFA384x_PARAM0_OFF
+#define HFA384x_PARAM1 HFA384x_PARAM1_OFF
+#define HFA384x_PARAM2 HFA384x_PARAM2_OFF
+#define HFA384x_STATUS HFA384x_STATUS_OFF
+#define HFA384x_RESP0 HFA384x_RESP0_OFF
+#define HFA384x_RESP1 HFA384x_RESP1_OFF
+#define HFA384x_RESP2 HFA384x_RESP2_OFF
+#define HFA384x_INFOFID HFA384x_INFOFID_OFF
+#define HFA384x_RXFID HFA384x_RXFID_OFF
+#define HFA384x_ALLOCFID HFA384x_ALLOCFID_OFF
+#define HFA384x_TXCOMPLFID HFA384x_TXCOMPLFID_OFF
+#define HFA384x_SELECT0 HFA384x_SELECT0_OFF
+#define HFA384x_OFFSET0 HFA384x_OFFSET0_OFF
+#define HFA384x_DATA0 HFA384x_DATA0_OFF
+#define HFA384x_SELECT1 HFA384x_SELECT1_OFF
+#define HFA384x_OFFSET1 HFA384x_OFFSET1_OFF
+#define HFA384x_DATA1 HFA384x_DATA1_OFF
+#define HFA384x_EVSTAT HFA384x_EVSTAT_OFF
+#define HFA384x_INTEN HFA384x_INTEN_OFF
+#define HFA384x_EVACK HFA384x_EVACK_OFF
+#define HFA384x_CONTROL HFA384x_CONTROL_OFF
+#define HFA384x_SWSUPPORT0 HFA384x_SWSUPPORT0_OFF
+#define HFA384x_SWSUPPORT1 HFA384x_SWSUPPORT1_OFF
+#define HFA384x_SWSUPPORT2 HFA384x_SWSUPPORT2_OFF
+#define HFA384x_AUXPAGE HFA384x_AUXPAGE_OFF
+#define HFA384x_AUXOFFSET HFA384x_AUXOFFSET_OFF
+#define HFA384x_AUXDATA HFA384x_AUXDATA_OFF
+#define HFA384x_PCICOR HFA384x_PCICOR_OFF
+#define HFA384x_PCIHCR HFA384x_PCIHCR_OFF
+
+
+/*--- Register Test/Get/Set Field macros ------------------------*/
+
+#define HFA384x_CMD_ISBUSY(value) ((UINT16)(((UINT16)value) & HFA384x_CMD_BUSY))
+#define HFA384x_CMD_AINFO_GET(value) ((UINT16)(((UINT16)(value) & HFA384x_CMD_AINFO) >> 8))
+#define HFA384x_CMD_AINFO_SET(value) ((UINT16)((UINT16)(value) << 8))
+#define HFA384x_CMD_MACPORT_GET(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_MACPORT)))
+#define HFA384x_CMD_MACPORT_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET(value))
+#define HFA384x_CMD_ISRECL(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_RECL)))
+#define HFA384x_CMD_RECL_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET(value))
+#define HFA384x_CMD_QOS_GET(value) ((UINT16((((UINT16)(value))&((UINT16)0x3000)) >> 12))
+#define HFA384x_CMD_QOS_SET(value) ((UINT16)((((UINT16)(value)) << 12) & 0x3000))
+#define HFA384x_CMD_ISWRITE(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_WRITE)))
+#define HFA384x_CMD_WRITE_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET((UINT16)value))
+#define HFA384x_CMD_PROGMODE_GET(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_PROGMODE)))
+#define HFA384x_CMD_PROGMODE_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET((UINT16)value))
+#define HFA384x_CMD_CMDCODE_GET(value) ((UINT16)(((UINT16)(value)) & HFA384x_CMD_CMDCODE))
+#define HFA384x_CMD_CMDCODE_SET(value) ((UINT16)(value))
+
+#define HFA384x_STATUS_RESULT_GET(value) ((UINT16)((((UINT16)(value)) & HFA384x_STATUS_RESULT) >> 8))
+#define HFA384x_STATUS_RESULT_SET(value) (((UINT16)(value)) << 8)
+#define HFA384x_STATUS_CMDCODE_GET(value) (((UINT16)(value)) & HFA384x_STATUS_CMDCODE)
+#define HFA384x_STATUS_CMDCODE_SET(value) ((UINT16)(value))
+
+#define HFA384x_OFFSET_ISBUSY(value) ((UINT16)(((UINT16)(value)) & HFA384x_OFFSET_BUSY))
+#define HFA384x_OFFSET_ISERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_OFFSET_ERR))
+#define HFA384x_OFFSET_DATAOFF_GET(value) ((UINT16)(((UINT16)(value)) & HFA384x_OFFSET_DATAOFF))
+#define HFA384x_OFFSET_DATAOFF_SET(value) ((UINT16)(value))
+
+#define HFA384x_EVSTAT_ISTICK(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_TICK))
+#define HFA384x_EVSTAT_ISWTERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_WTERR))
+#define HFA384x_EVSTAT_ISINFDROP(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_INFDROP))
+#define HFA384x_EVSTAT_ISINFO(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_INFO))
+#define HFA384x_EVSTAT_ISDTIM(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_DTIM))
+#define HFA384x_EVSTAT_ISCMD(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_CMD))
+#define HFA384x_EVSTAT_ISALLOC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_ALLOC))
+#define HFA384x_EVSTAT_ISTXEXC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_TXEXC))
+#define HFA384x_EVSTAT_ISTX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_TX))
+#define HFA384x_EVSTAT_ISRX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_RX))
+
+#define HFA384x_EVSTAT_ISBAP_OP(value) ((UINT16)(((UINT16)(value)) & HFA384x_INT_BAP_OP))
+
+#define HFA384x_INTEN_ISTICK(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_TICK))
+#define HFA384x_INTEN_TICK_SET(value) ((UINT16)(((UINT16)(value)) << 15))
+#define HFA384x_INTEN_ISWTERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_WTERR))
+#define HFA384x_INTEN_WTERR_SET(value) ((UINT16)(((UINT16)(value)) << 14))
+#define HFA384x_INTEN_ISINFDROP(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_INFDROP))
+#define HFA384x_INTEN_INFDROP_SET(value) ((UINT16)(((UINT16)(value)) << 13))
+#define HFA384x_INTEN_ISINFO(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_INFO))
+#define HFA384x_INTEN_INFO_SET(value) ((UINT16)(((UINT16)(value)) << 7))
+#define HFA384x_INTEN_ISDTIM(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_DTIM))
+#define HFA384x_INTEN_DTIM_SET(value) ((UINT16)(((UINT16)(value)) << 5))
+#define HFA384x_INTEN_ISCMD(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_CMD))
+#define HFA384x_INTEN_CMD_SET(value) ((UINT16)(((UINT16)(value)) << 4))
+#define HFA384x_INTEN_ISALLOC(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_ALLOC))
+#define HFA384x_INTEN_ALLOC_SET(value) ((UINT16)(((UINT16)(value)) << 3))
+#define HFA384x_INTEN_ISTXEXC(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_TXEXC))
+#define HFA384x_INTEN_TXEXC_SET(value) ((UINT16)(((UINT16)(value)) << 2))
+#define HFA384x_INTEN_ISTX(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_TX))
+#define HFA384x_INTEN_TX_SET(value) ((UINT16)(((UINT16)(value)) << 1))
+#define HFA384x_INTEN_ISRX(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_RX))
+#define HFA384x_INTEN_RX_SET(value) ((UINT16)(((UINT16)(value)) << 0))
+
+#define HFA384x_EVACK_ISTICK(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_TICK))
+#define HFA384x_EVACK_TICK_SET(value) ((UINT16)(((UINT16)(value)) << 15))
+#define HFA384x_EVACK_ISWTERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_WTERR))
+#define HFA384x_EVACK_WTERR_SET(value) ((UINT16)(((UINT16)(value)) << 14))
+#define HFA384x_EVACK_ISINFDROP(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_INFDROP))
+#define HFA384x_EVACK_INFDROP_SET(value) ((UINT16)(((UINT16)(value)) << 13))
+#define HFA384x_EVACK_ISINFO(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_INFO))
+#define HFA384x_EVACK_INFO_SET(value) ((UINT16)(((UINT16)(value)) << 7))
+#define HFA384x_EVACK_ISDTIM(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_DTIM))
+#define HFA384x_EVACK_DTIM_SET(value) ((UINT16)(((UINT16)(value)) << 5))
+#define HFA384x_EVACK_ISCMD(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_CMD))
+#define HFA384x_EVACK_CMD_SET(value) ((UINT16)(((UINT16)(value)) << 4))
+#define HFA384x_EVACK_ISALLOC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_ALLOC))
+#define HFA384x_EVACK_ALLOC_SET(value) ((UINT16)(((UINT16)(value)) << 3))
+#define HFA384x_EVACK_ISTXEXC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_TXEXC))
+#define HFA384x_EVACK_TXEXC_SET(value) ((UINT16)(((UINT16)(value)) << 2))
+#define HFA384x_EVACK_ISTX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_TX))
+#define HFA384x_EVACK_TX_SET(value) ((UINT16)(((UINT16)(value)) << 1))
+#define HFA384x_EVACK_ISRX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_RX))
+#define HFA384x_EVACK_RX_SET(value) ((UINT16)(((UINT16)(value)) << 0))
+
+#define HFA384x_CONTROL_AUXEN_SET(value) ((UINT16)(((UINT16)(value)) << 14))
+#define HFA384x_CONTROL_AUXEN_GET(value) ((UINT16)(((UINT16)(value)) >> 14))
+
+/* Byte Order */
+#ifdef __KERNEL__
+#define hfa384x2host_16(n) (__le16_to_cpu((UINT16)(n)))
+#define hfa384x2host_32(n) (__le32_to_cpu((UINT32)(n)))
+#define host2hfa384x_16(n) (__cpu_to_le16((UINT16)(n)))
+#define host2hfa384x_32(n) (__cpu_to_le32((UINT32)(n)))
+#endif
+
+/* Host Maintained State Info */
+#define HFA384x_STATE_PREINIT 0
+#define HFA384x_STATE_INIT 1
+#define HFA384x_STATE_RUNNING 2
+
+/*=============================================================*/
+/*------ Types and their related constants --------------------*/
+
+#define HFA384x_HOSTAUTHASSOC_HOSTAUTH BIT0
+#define HFA384x_HOSTAUTHASSOC_HOSTASSOC BIT1
+
+#define HFA384x_WHAHANDLING_DISABLED 0
+#define HFA384x_WHAHANDLING_PASSTHROUGH BIT1
+
+/*-------------------------------------------------------------*/
+/* Commonly used basic types */
+typedef struct hfa384x_bytestr
+{
+ UINT16 len;
+ UINT8 data[0];
+} __WLAN_ATTRIB_PACK__ hfa384x_bytestr_t;
+
+typedef struct hfa384x_bytestr32
+{
+ UINT16 len;
+ UINT8 data[32];
+} __WLAN_ATTRIB_PACK__ hfa384x_bytestr32_t;
+
+/*--------------------------------------------------------------------
+Configuration Record Structures:
+ Network Parameters, Static Configuration Entities
+--------------------------------------------------------------------*/
+/* Prototype structure: all configuration record structures start with
+these members */
+
+typedef struct hfa384x_record
+{
+ UINT16 reclen;
+ UINT16 rid;
+} __WLAN_ATTRIB_PACK__ hfa384x_rec_t;
+
+typedef struct hfa384x_record16
+{
+ UINT16 reclen;
+ UINT16 rid;
+ UINT16 val;
+} __WLAN_ATTRIB_PACK__ hfa384x_rec16_t;
+
+typedef struct hfa384x_record32
+{
+ UINT16 reclen;
+ UINT16 rid;
+ UINT32 val;
+} __WLAN_ATTRIB_PACK__ hfa384x_rec32;
+
+/*-- Hardware/Firmware Component Information ----------*/
+typedef struct hfa384x_compident
+{
+ UINT16 id;
+ UINT16 variant;
+ UINT16 major;
+ UINT16 minor;
+} __WLAN_ATTRIB_PACK__ hfa384x_compident_t;
+
+typedef struct hfa384x_caplevel
+{
+ UINT16 role;
+ UINT16 id;
+ UINT16 variant;
+ UINT16 bottom;
+ UINT16 top;
+} __WLAN_ATTRIB_PACK__ hfa384x_caplevel_t;
+
+/*-- Configuration Record: cnfPortType --*/
+typedef struct hfa384x_cnfPortType
+{
+ UINT16 cnfPortType;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfPortType_t;
+
+/*-- Configuration Record: cnfOwnMACAddress --*/
+typedef struct hfa384x_cnfOwnMACAddress
+{
+ UINT8 cnfOwnMACAddress[6];
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnMACAddress_t;
+
+/*-- Configuration Record: cnfDesiredSSID --*/
+typedef struct hfa384x_cnfDesiredSSID
+{
+ UINT8 cnfDesiredSSID[34];
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfDesiredSSID_t;
+
+/*-- Configuration Record: cnfOwnChannel --*/
+typedef struct hfa384x_cnfOwnChannel
+{
+ UINT16 cnfOwnChannel;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnChannel_t;
+
+/*-- Configuration Record: cnfOwnSSID --*/
+typedef struct hfa384x_cnfOwnSSID
+{
+ UINT8 cnfOwnSSID[34];
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnSSID_t;
+
+/*-- Configuration Record: cnfOwnATIMWindow --*/
+typedef struct hfa384x_cnfOwnATIMWindow
+{
+ UINT16 cnfOwnATIMWindow;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnATIMWindow_t;
+
+/*-- Configuration Record: cnfSystemScale --*/
+typedef struct hfa384x_cnfSystemScale
+{
+ UINT16 cnfSystemScale;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfSystemScale_t;
+
+/*-- Configuration Record: cnfMaxDataLength --*/
+typedef struct hfa384x_cnfMaxDataLength
+{
+ UINT16 cnfMaxDataLength;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfMaxDataLength_t;
+
+/*-- Configuration Record: cnfWDSAddress --*/
+typedef struct hfa384x_cnfWDSAddress
+{
+ UINT8 cnfWDSAddress[6];
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfWDSAddress_t;
+
+/*-- Configuration Record: cnfPMEnabled --*/
+typedef struct hfa384x_cnfPMEnabled
+{
+ UINT16 cnfPMEnabled;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMEnabled_t;
+
+/*-- Configuration Record: cnfPMEPS --*/
+typedef struct hfa384x_cnfPMEPS
+{
+ UINT16 cnfPMEPS;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMEPS_t;
+
+/*-- Configuration Record: cnfMulticastReceive --*/
+typedef struct hfa384x_cnfMulticastReceive
+{
+ UINT16 cnfMulticastReceive;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfMulticastReceive_t;
+
+/*-- Configuration Record: cnfAuthentication --*/
+#define HFA384x_CNFAUTHENTICATION_OPENSYSTEM 0x0001
+#define HFA384x_CNFAUTHENTICATION_SHAREDKEY 0x0002
+#define HFA384x_CNFAUTHENTICATION_LEAP 0x0004
+
+/*-- Configuration Record: cnfMaxSleepDuration --*/
+typedef struct hfa384x_cnfMaxSleepDuration
+{
+ UINT16 cnfMaxSleepDuration;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfMaxSleepDuration_t;
+
+/*-- Configuration Record: cnfPMHoldoverDuration --*/
+typedef struct hfa384x_cnfPMHoldoverDuration
+{
+ UINT16 cnfPMHoldoverDuration;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMHoldoverDuration_t;
+
+/*-- Configuration Record: cnfOwnName --*/
+typedef struct hfa384x_cnfOwnName
+{
+ UINT8 cnfOwnName[34];
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnName_t;
+
+/*-- Configuration Record: cnfOwnDTIMPeriod --*/
+typedef struct hfa384x_cnfOwnDTIMPeriod
+{
+ UINT16 cnfOwnDTIMPeriod;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnDTIMPeriod_t;
+
+/*-- Configuration Record: cnfWDSAddress --*/
+typedef struct hfa384x_cnfWDSAddressN
+{
+ UINT8 cnfWDSAddress[6];
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfWDSAddressN_t;
+
+/*-- Configuration Record: cnfMulticastPMBuffering --*/
+typedef struct hfa384x_cnfMulticastPMBuffering
+{
+ UINT16 cnfMulticastPMBuffering;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfMulticastPMBuffering_t;
+
+/*--------------------------------------------------------------------
+Configuration Record Structures:
+ Network Parameters, Dynamic Configuration Entities
+--------------------------------------------------------------------*/
+
+/*-- Configuration Record: GroupAddresses --*/
+typedef struct hfa384x_GroupAddresses
+{
+ UINT8 MACAddress[16][6];
+} __WLAN_ATTRIB_PACK__ hfa384x_GroupAddresses_t;
+
+/*-- Configuration Record: CreateIBSS --*/
+typedef struct hfa384x_CreateIBSS
+{
+ UINT16 CreateIBSS;
+} __WLAN_ATTRIB_PACK__ hfa384x_CreateIBSS_t;
+
+#define HFA384x_CREATEIBSS_JOINCREATEIBSS 0
+#define HFA384x_CREATEIBSS_JOINESS_JOINCREATEIBSS 1
+#define HFA384x_CREATEIBSS_JOINIBSS 2
+#define HFA384x_CREATEIBSS_JOINESS_JOINIBSS 3
+
+/*-- Configuration Record: FragmentationThreshold --*/
+typedef struct hfa384x_FragmentationThreshold
+{
+ UINT16 FragmentationThreshold;
+} __WLAN_ATTRIB_PACK__ hfa384x_FragmentationThreshold_t;
+
+/*-- Configuration Record: RTSThreshold --*/
+typedef struct hfa384x_RTSThreshold
+{
+ UINT16 RTSThreshold;
+} __WLAN_ATTRIB_PACK__ hfa384x_RTSThreshold_t;
+
+/*-- Configuration Record: TxRateControl --*/
+typedef struct hfa384x_TxRateControl
+{
+ UINT16 TxRateControl;
+} __WLAN_ATTRIB_PACK__ hfa384x_TxRateControl_t;
+
+/*-- Configuration Record: PromiscuousMode --*/
+typedef struct hfa384x_PromiscuousMode
+{
+ UINT16 PromiscuousMode;
+} __WLAN_ATTRIB_PACK__ hfa384x_PromiscuousMode_t;
+
+/*-- Configuration Record: ScanRequest (data portion only) --*/
+typedef struct hfa384x_ScanRequest_data
+{
+ UINT16 channelList;
+ UINT16 txRate;
+} __WLAN_ATTRIB_PACK__ hfa384x_ScanRequest_data_t;
+
+/*-- Configuration Record: HostScanRequest (data portion only) --*/
+typedef struct hfa384x_HostScanRequest_data
+{
+ UINT16 channelList;
+ UINT16 txRate;
+ hfa384x_bytestr32_t ssid;
+} __WLAN_ATTRIB_PACK__ hfa384x_HostScanRequest_data_t;
+
+/*-- Configuration Record: JoinRequest (data portion only) --*/
+typedef struct hfa384x_JoinRequest_data
+{
+ UINT8 bssid[WLAN_BSSID_LEN];
+ UINT16 channel;
+} __WLAN_ATTRIB_PACK__ hfa384x_JoinRequest_data_t;
+
+/*-- Configuration Record: authenticateStation (data portion only) --*/
+typedef struct hfa384x_authenticateStation_data
+{
+ UINT8 address[WLAN_ADDR_LEN];
+ UINT16 status;
+ UINT16 algorithm;
+} __WLAN_ATTRIB_PACK__ hfa384x_authenticateStation_data_t;
+
+/*-- Configuration Record: associateStation (data portion only) --*/
+typedef struct hfa384x_associateStation_data
+{
+ UINT8 address[WLAN_ADDR_LEN];
+ UINT16 status;
+ UINT16 type;
+} __WLAN_ATTRIB_PACK__ hfa384x_associateStation_data_t;
+
+/*-- Configuration Record: ChannelInfoRequest (data portion only) --*/
+typedef struct hfa384x_ChannelInfoRequest_data
+{
+ UINT16 channelList;
+ UINT16 channelDwellTime;
+} __WLAN_ATTRIB_PACK__ hfa384x_ChannelInfoRequest_data_t;
+
+/*-- Configuration Record: WEPKeyMapping (data portion only) --*/
+typedef struct hfa384x_WEPKeyMapping
+{
+ UINT8 address[WLAN_ADDR_LEN];
+ UINT16 key_index;
+ UINT8 key[16];
+ UINT8 mic_transmit_key[4];
+ UINT8 mic_receive_key[4];
+} __WLAN_ATTRIB_PACK__ hfa384x_WEPKeyMapping_t;
+
+/*-- Configuration Record: WPAData (data portion only) --*/
+typedef struct hfa384x_WPAData
+{
+ UINT16 datalen;
+ UINT8 data[0]; // max 80
+} __WLAN_ATTRIB_PACK__ hfa384x_WPAData_t;
+
+/*--------------------------------------------------------------------
+Configuration Record Structures: Behavior Parameters
+--------------------------------------------------------------------*/
+
+/*-- Configuration Record: TickTime --*/
+typedef struct hfa384x_TickTime
+{
+ UINT16 TickTime;
+} __WLAN_ATTRIB_PACK__ hfa384x_TickTime_t;
+
+/*--------------------------------------------------------------------
+Information Record Structures: NIC Information
+--------------------------------------------------------------------*/
+
+/*-- Information Record: MaxLoadTime --*/
+typedef struct hfa384x_MaxLoadTime
+{
+ UINT16 MaxLoadTime;
+} __WLAN_ATTRIB_PACK__ hfa384x_MaxLoadTime_t;
+
+/*-- Information Record: DownLoadBuffer --*/
+/* NOTE: The page and offset are in AUX format */
+typedef struct hfa384x_downloadbuffer
+{
+ UINT16 page;
+ UINT16 offset;
+ UINT16 len;
+} __WLAN_ATTRIB_PACK__ hfa384x_downloadbuffer_t;
+
+/*-- Information Record: PRIIdentity --*/
+typedef struct hfa384x_PRIIdentity
+{
+ UINT16 PRICompID;
+ UINT16 PRIVariant;
+ UINT16 PRIMajorVersion;
+ UINT16 PRIMinorVersion;
+} __WLAN_ATTRIB_PACK__ hfa384x_PRIIdentity_t;
+
+/*-- Information Record: PRISupRange --*/
+typedef struct hfa384x_PRISupRange
+{
+ UINT16 PRIRole;
+ UINT16 PRIID;
+ UINT16 PRIVariant;
+ UINT16 PRIBottom;
+ UINT16 PRITop;
+} __WLAN_ATTRIB_PACK__ hfa384x_PRISupRange_t;
+
+/*-- Information Record: CFIActRanges --*/
+typedef struct hfa384x_CFIActRanges
+{
+ UINT16 CFIRole;
+ UINT16 CFIID;
+ UINT16 CFIVariant;
+ UINT16 CFIBottom;
+ UINT16 CFITop;
+} __WLAN_ATTRIB_PACK__ hfa384x_CFIActRanges_t;
+
+/*-- Information Record: NICSerialNumber --*/
+typedef struct hfa384x_NICSerialNumber
+{
+ UINT8 NICSerialNumber[12];
+} __WLAN_ATTRIB_PACK__ hfa384x_NICSerialNumber_t;
+
+/*-- Information Record: NICIdentity --*/
+typedef struct hfa384x_NICIdentity
+{
+ UINT16 NICCompID;
+ UINT16 NICVariant;
+ UINT16 NICMajorVersion;
+ UINT16 NICMinorVersion;
+} __WLAN_ATTRIB_PACK__ hfa384x_NICIdentity_t;
+
+/*-- Information Record: MFISupRange --*/
+typedef struct hfa384x_MFISupRange
+{
+ UINT16 MFIRole;
+ UINT16 MFIID;
+ UINT16 MFIVariant;
+ UINT16 MFIBottom;
+ UINT16 MFITop;
+} __WLAN_ATTRIB_PACK__ hfa384x_MFISupRange_t;
+
+/*-- Information Record: CFISupRange --*/
+typedef struct hfa384x_CFISupRange
+{
+ UINT16 CFIRole;
+ UINT16 CFIID;
+ UINT16 CFIVariant;
+ UINT16 CFIBottom;
+ UINT16 CFITop;
+} __WLAN_ATTRIB_PACK__ hfa384x_CFISupRange_t;
+
+/*-- Information Record: BUILDSEQ:BuildSeq --*/
+typedef struct hfa384x_BuildSeq {
+ UINT16 primary;
+ UINT16 secondary;
+} __WLAN_ATTRIB_PACK__ hfa384x_BuildSeq_t;
+
+/*-- Information Record: FWID --*/
+#define HFA384x_FWID_LEN 14
+typedef struct hfa384x_FWID {
+ UINT8 primary[HFA384x_FWID_LEN];
+ UINT8 secondary[HFA384x_FWID_LEN];
+} __WLAN_ATTRIB_PACK__ hfa384x_FWID_t;
+
+/*-- Information Record: ChannelList --*/
+typedef struct hfa384x_ChannelList
+{
+ UINT16 ChannelList;
+} __WLAN_ATTRIB_PACK__ hfa384x_ChannelList_t;
+
+/*-- Information Record: RegulatoryDomains --*/
+typedef struct hfa384x_RegulatoryDomains
+{
+ UINT8 RegulatoryDomains[12];
+} __WLAN_ATTRIB_PACK__ hfa384x_RegulatoryDomains_t;
+
+/*-- Information Record: TempType --*/
+typedef struct hfa384x_TempType
+{
+ UINT16 TempType;
+} __WLAN_ATTRIB_PACK__ hfa384x_TempType_t;
+
+/*-- Information Record: CIS --*/
+typedef struct hfa384x_CIS
+{
+ UINT8 CIS[480];
+} __WLAN_ATTRIB_PACK__ hfa384x_CIS_t;
+
+/*-- Information Record: STAIdentity --*/
+typedef struct hfa384x_STAIdentity
+{
+ UINT16 STACompID;
+ UINT16 STAVariant;
+ UINT16 STAMajorVersion;
+ UINT16 STAMinorVersion;
+} __WLAN_ATTRIB_PACK__ hfa384x_STAIdentity_t;
+
+/*-- Information Record: STASupRange --*/
+typedef struct hfa384x_STASupRange
+{
+ UINT16 STARole;
+ UINT16 STAID;
+ UINT16 STAVariant;
+ UINT16 STABottom;
+ UINT16 STATop;
+} __WLAN_ATTRIB_PACK__ hfa384x_STASupRange_t;
+
+/*-- Information Record: MFIActRanges --*/
+typedef struct hfa384x_MFIActRanges
+{
+ UINT16 MFIRole;
+ UINT16 MFIID;
+ UINT16 MFIVariant;
+ UINT16 MFIBottom;
+ UINT16 MFITop;
+} __WLAN_ATTRIB_PACK__ hfa384x_MFIActRanges_t;
+
+/*--------------------------------------------------------------------
+Information Record Structures: NIC Information
+--------------------------------------------------------------------*/
+
+/*-- Information Record: PortStatus --*/
+typedef struct hfa384x_PortStatus
+{
+ UINT16 PortStatus;
+} __WLAN_ATTRIB_PACK__ hfa384x_PortStatus_t;
+
+#define HFA384x_PSTATUS_DISABLED ((UINT16)1)
+#define HFA384x_PSTATUS_SEARCHING ((UINT16)2)
+#define HFA384x_PSTATUS_CONN_IBSS ((UINT16)3)
+#define HFA384x_PSTATUS_CONN_ESS ((UINT16)4)
+#define HFA384x_PSTATUS_OUTOFRANGE ((UINT16)5)
+#define HFA384x_PSTATUS_CONN_WDS ((UINT16)6)
+
+/*-- Information Record: CurrentSSID --*/
+typedef struct hfa384x_CurrentSSID
+{
+ UINT8 CurrentSSID[34];
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentSSID_t;
+
+/*-- Information Record: CurrentBSSID --*/
+typedef struct hfa384x_CurrentBSSID
+{
+ UINT8 CurrentBSSID[6];
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentBSSID_t;
+
+/*-- Information Record: commsquality --*/
+typedef struct hfa384x_commsquality
+{
+ UINT16 CQ_currBSS;
+ UINT16 ASL_currBSS;
+ UINT16 ANL_currFC;
+} __WLAN_ATTRIB_PACK__ hfa384x_commsquality_t;
+
+/*-- Information Record: dmbcommsquality --*/
+typedef struct hfa384x_dbmcommsquality
+{
+ UINT16 CQdbm_currBSS;
+ UINT16 ASLdbm_currBSS;
+ UINT16 ANLdbm_currFC;
+} __WLAN_ATTRIB_PACK__ hfa384x_dbmcommsquality_t;
+
+/*-- Information Record: CurrentTxRate --*/
+typedef struct hfa384x_CurrentTxRate
+{
+ UINT16 CurrentTxRate;
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentTxRate_t;
+
+/*-- Information Record: CurrentBeaconInterval --*/
+typedef struct hfa384x_CurrentBeaconInterval
+{
+ UINT16 CurrentBeaconInterval;
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentBeaconInterval_t;
+
+/*-- Information Record: CurrentScaleThresholds --*/
+typedef struct hfa384x_CurrentScaleThresholds
+{
+ UINT16 EnergyDetectThreshold;
+ UINT16 CarrierDetectThreshold;
+ UINT16 DeferDetectThreshold;
+ UINT16 CellSearchThreshold; /* Stations only */
+ UINT16 DeadSpotThreshold; /* Stations only */
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentScaleThresholds_t;
+
+/*-- Information Record: ProtocolRspTime --*/
+typedef struct hfa384x_ProtocolRspTime
+{
+ UINT16 ProtocolRspTime;
+} __WLAN_ATTRIB_PACK__ hfa384x_ProtocolRspTime_t;
+
+/*-- Information Record: ShortRetryLimit --*/
+typedef struct hfa384x_ShortRetryLimit
+{
+ UINT16 ShortRetryLimit;
+} __WLAN_ATTRIB_PACK__ hfa384x_ShortRetryLimit_t;
+
+/*-- Information Record: LongRetryLimit --*/
+typedef struct hfa384x_LongRetryLimit
+{
+ UINT16 LongRetryLimit;
+} __WLAN_ATTRIB_PACK__ hfa384x_LongRetryLimit_t;
+
+/*-- Information Record: MaxTransmitLifetime --*/
+typedef struct hfa384x_MaxTransmitLifetime
+{
+ UINT16 MaxTransmitLifetime;
+} __WLAN_ATTRIB_PACK__ hfa384x_MaxTransmitLifetime_t;
+
+/*-- Information Record: MaxReceiveLifetime --*/
+typedef struct hfa384x_MaxReceiveLifetime
+{
+ UINT16 MaxReceiveLifetime;
+} __WLAN_ATTRIB_PACK__ hfa384x_MaxReceiveLifetime_t;
+
+/*-- Information Record: CFPollable --*/
+typedef struct hfa384x_CFPollable
+{
+ UINT16 CFPollable;
+} __WLAN_ATTRIB_PACK__ hfa384x_CFPollable_t;
+
+/*-- Information Record: AuthenticationAlgorithms --*/
+typedef struct hfa384x_AuthenticationAlgorithms
+{
+ UINT16 AuthenticationType;
+ UINT16 TypeEnabled;
+} __WLAN_ATTRIB_PACK__ hfa384x_AuthenticationAlgorithms_t;
+
+/*-- Information Record: AuthenticationAlgorithms
+(data only --*/
+typedef struct hfa384x_AuthenticationAlgorithms_data
+{
+ UINT16 AuthenticationType;
+ UINT16 TypeEnabled;
+} __WLAN_ATTRIB_PACK__ hfa384x_AuthenticationAlgorithms_data_t;
+
+/*-- Information Record: PrivacyOptionImplemented --*/
+typedef struct hfa384x_PrivacyOptionImplemented
+{
+ UINT16 PrivacyOptionImplemented;
+} __WLAN_ATTRIB_PACK__ hfa384x_PrivacyOptionImplemented_t;
+
+/*-- Information Record: OwnMACAddress --*/
+typedef struct hfa384x_OwnMACAddress
+{
+ UINT8 OwnMACAddress[6];
+} __WLAN_ATTRIB_PACK__ hfa384x_OwnMACAddress_t;
+
+/*-- Information Record: PCFInfo --*/
+typedef struct hfa384x_PCFInfo
+{
+ UINT16 MediumOccupancyLimit;
+ UINT16 CFPPeriod;
+ UINT16 CFPMaxDuration;
+ UINT16 CFPFlags;
+} __WLAN_ATTRIB_PACK__ hfa384x_PCFInfo_t;
+
+/*-- Information Record: PCFInfo (data portion only) --*/
+typedef struct hfa384x_PCFInfo_data
+{
+ UINT16 MediumOccupancyLimit;
+ UINT16 CFPPeriod;
+ UINT16 CFPMaxDuration;
+ UINT16 CFPFlags;
+} __WLAN_ATTRIB_PACK__ hfa384x_PCFInfo_data_t;
+
+/*--------------------------------------------------------------------
+Information Record Structures: Modem Information Records
+--------------------------------------------------------------------*/
+
+/*-- Information Record: PHYType --*/
+typedef struct hfa384x_PHYType
+{
+ UINT16 PHYType;
+} __WLAN_ATTRIB_PACK__ hfa384x_PHYType_t;
+
+/*-- Information Record: CurrentChannel --*/
+typedef struct hfa384x_CurrentChannel
+{
+ UINT16 CurrentChannel;
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentChannel_t;
+
+/*-- Information Record: CurrentPowerState --*/
+typedef struct hfa384x_CurrentPowerState
+{
+ UINT16 CurrentPowerState;
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentPowerState_t;
+
+/*-- Information Record: CCAMode --*/
+typedef struct hfa384x_CCAMode
+{
+ UINT16 CCAMode;
+} __WLAN_ATTRIB_PACK__ hfa384x_CCAMode_t;
+
+/*-- Information Record: SupportedDataRates --*/
+typedef struct hfa384x_SupportedDataRates
+{
+ UINT8 SupportedDataRates[10];
+} __WLAN_ATTRIB_PACK__ hfa384x_SupportedDataRates_t;
+
+/*-- Information Record: LFOStatus --*/
+typedef struct hfa384x_LFOStatus
+{
+ UINT16 TestResults;
+ UINT16 LFOResult;
+ UINT16 VRHFOResult;
+} __WLAN_ATTRIB_PACK__ hfa384x_LFOStatus_t;
+
+#define HFA384x_TESTRESULT_ALLPASSED BIT0
+#define HFA384x_TESTRESULT_LFO_FAIL BIT1
+#define HFA384x_TESTRESULT_VR_HF0_FAIL BIT2
+#define HFA384x_HOST_FIRM_COORDINATE BIT7
+#define HFA384x_TESTRESULT_COORDINATE BIT15
+
+/*-- Information Record: LEDControl --*/
+typedef struct hfa384x_LEDControl
+{
+ UINT16 searching_on;
+ UINT16 searching_off;
+ UINT16 assoc_on;
+ UINT16 assoc_off;
+ UINT16 activity;
+} __WLAN_ATTRIB_PACK__ hfa384x_LEDControl_t;
+
+/*--------------------------------------------------------------------
+ FRAME DESCRIPTORS AND FRAME STRUCTURES
+
+FRAME DESCRIPTORS: Offsets
+
+----------------------------------------------------------------------
+Control Info (offset 44-51)
+--------------------------------------------------------------------*/
+#define HFA384x_FD_STATUS_OFF ((UINT16)0x44)
+#define HFA384x_FD_TIME_OFF ((UINT16)0x46)
+#define HFA384x_FD_SWSUPPORT_OFF ((UINT16)0x4A)
+#define HFA384x_FD_SILENCE_OFF ((UINT16)0x4A)
+#define HFA384x_FD_SIGNAL_OFF ((UINT16)0x4B)
+#define HFA384x_FD_RATE_OFF ((UINT16)0x4C)
+#define HFA384x_FD_RXFLOW_OFF ((UINT16)0x4D)
+#define HFA384x_FD_RESERVED_OFF ((UINT16)0x4E)
+#define HFA384x_FD_TXCONTROL_OFF ((UINT16)0x50)
+/*--------------------------------------------------------------------
+802.11 Header (offset 52-6B)
+--------------------------------------------------------------------*/
+#define HFA384x_FD_FRAMECONTROL_OFF ((UINT16)0x52)
+#define HFA384x_FD_DURATIONID_OFF ((UINT16)0x54)
+#define HFA384x_FD_ADDRESS1_OFF ((UINT16)0x56)
+#define HFA384x_FD_ADDRESS2_OFF ((UINT16)0x5C)
+#define HFA384x_FD_ADDRESS3_OFF ((UINT16)0x62)
+#define HFA384x_FD_SEQCONTROL_OFF ((UINT16)0x68)
+#define HFA384x_FD_ADDRESS4_OFF ((UINT16)0x6A)
+#define HFA384x_FD_DATALEN_OFF ((UINT16)0x70)
+/*--------------------------------------------------------------------
+802.3 Header (offset 72-7F)
+--------------------------------------------------------------------*/
+#define HFA384x_FD_DESTADDRESS_OFF ((UINT16)0x72)
+#define HFA384x_FD_SRCADDRESS_OFF ((UINT16)0x78)
+#define HFA384x_FD_DATALENGTH_OFF ((UINT16)0x7E)
+
+/*--------------------------------------------------------------------
+FRAME STRUCTURES: Communication Frames
+----------------------------------------------------------------------
+Communication Frames: Transmit Frames
+--------------------------------------------------------------------*/
+/*-- Communication Frame: Transmit Frame Structure --*/
+typedef struct hfa384x_tx_frame
+{
+ UINT16 status;
+ UINT16 reserved1;
+ UINT16 reserved2;
+ UINT32 sw_support;
+ UINT8 tx_retrycount;
+ UINT8 tx_rate;
+ UINT16 tx_control;
+
+ /*-- 802.11 Header Information --*/
+
+ UINT16 frame_control;
+ UINT16 duration_id;
+ UINT8 address1[6];
+ UINT8 address2[6];
+ UINT8 address3[6];
+ UINT16 sequence_control;
+ UINT8 address4[6];
+ UINT16 data_len; /* little endian format */
+
+ /*-- 802.3 Header Information --*/
+
+ UINT8 dest_addr[6];
+ UINT8 src_addr[6];
+ UINT16 data_length; /* big endian format */
+} __WLAN_ATTRIB_PACK__ hfa384x_tx_frame_t;
+/*--------------------------------------------------------------------
+Communication Frames: Field Masks for Transmit Frames
+--------------------------------------------------------------------*/
+/*-- Status Field --*/
+#define HFA384x_TXSTATUS_ACKERR ((UINT16)BIT5)
+#define HFA384x_TXSTATUS_FORMERR ((UINT16)BIT3)
+#define HFA384x_TXSTATUS_DISCON ((UINT16)BIT2)
+#define HFA384x_TXSTATUS_AGEDERR ((UINT16)BIT1)
+#define HFA384x_TXSTATUS_RETRYERR ((UINT16)BIT0)
+/*-- Transmit Control Field --*/
+#define HFA384x_TX_CFPOLL ((UINT16)BIT12)
+#define HFA384x_TX_PRST ((UINT16)BIT11)
+#define HFA384x_TX_MACPORT ((UINT16)(BIT10 | BIT9 | BIT8))
+#define HFA384x_TX_NOENCRYPT ((UINT16)BIT7)
+#define HFA384x_TX_RETRYSTRAT ((UINT16)(BIT6 | BIT5))
+#define HFA384x_TX_STRUCTYPE ((UINT16)(BIT4 | BIT3))
+#define HFA384x_TX_TXEX ((UINT16)BIT2)
+#define HFA384x_TX_TXOK ((UINT16)BIT1)
+/*--------------------------------------------------------------------
+Communication Frames: Test/Get/Set Field Values for Transmit Frames
+--------------------------------------------------------------------*/
+/*-- Status Field --*/
+#define HFA384x_TXSTATUS_ISERROR(v) \
+ (((UINT16)(v))&\
+ (HFA384x_TXSTATUS_ACKERR|HFA384x_TXSTATUS_FORMERR|\
+ HFA384x_TXSTATUS_DISCON|HFA384x_TXSTATUS_AGEDERR|\
+ HFA384x_TXSTATUS_RETRYERR))
+
+#define HFA384x_TXSTATUS_ISACKERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_ACKERR))
+#define HFA384x_TXSTATUS_ISFORMERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_FORMERR))
+#define HFA384x_TXSTATUS_ISDISCON(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_DISCON))
+#define HFA384x_TXSTATUS_ISAGEDERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_AGEDERR))
+#define HFA384x_TXSTATUS_ISRETRYERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_RETRYERR))
+
+#define HFA384x_TX_GET(v,m,s) ((((UINT16)(v))&((UINT16)(m)))>>((UINT16)(s)))
+#define HFA384x_TX_SET(v,m,s) ((((UINT16)(v))<<((UINT16)(s)))&((UINT16)(m)))
+
+#define HFA384x_TX_CFPOLL_GET(v) HFA384x_TX_GET(v, HFA384x_TX_CFPOLL,12)
+#define HFA384x_TX_CFPOLL_SET(v) HFA384x_TX_SET(v, HFA384x_TX_CFPOLL,12)
+#define HFA384x_TX_PRST_GET(v) HFA384x_TX_GET(v, HFA384x_TX_PRST,11)
+#define HFA384x_TX_PRST_SET(v) HFA384x_TX_SET(v, HFA384x_TX_PRST,11)
+#define HFA384x_TX_MACPORT_GET(v) HFA384x_TX_GET(v, HFA384x_TX_MACPORT, 8)
+#define HFA384x_TX_MACPORT_SET(v) HFA384x_TX_SET(v, HFA384x_TX_MACPORT, 8)
+#define HFA384x_TX_NOENCRYPT_GET(v) HFA384x_TX_GET(v, HFA384x_TX_NOENCRYPT, 7)
+#define HFA384x_TX_NOENCRYPT_SET(v) HFA384x_TX_SET(v, HFA384x_TX_NOENCRYPT, 7)
+#define HFA384x_TX_RETRYSTRAT_GET(v) HFA384x_TX_GET(v, HFA384x_TX_RETRYSTRAT, 5)
+#define HFA384x_TX_RETRYSTRAT_SET(v) HFA384x_TX_SET(v, HFA384x_TX_RETRYSTRAT, 5)
+#define HFA384x_TX_STRUCTYPE_GET(v) HFA384x_TX_GET(v, HFA384x_TX_STRUCTYPE, 3)
+#define HFA384x_TX_STRUCTYPE_SET(v) HFA384x_TX_SET(v, HFA384x_TX_STRUCTYPE, 3)
+#define HFA384x_TX_TXEX_GET(v) HFA384x_TX_GET(v, HFA384x_TX_TXEX, 2)
+#define HFA384x_TX_TXEX_SET(v) HFA384x_TX_SET(v, HFA384x_TX_TXEX, 2)
+#define HFA384x_TX_TXOK_GET(v) HFA384x_TX_GET(v, HFA384x_TX_TXOK, 1)
+#define HFA384x_TX_TXOK_SET(v) HFA384x_TX_SET(v, HFA384x_TX_TXOK, 1)
+/*--------------------------------------------------------------------
+Communication Frames: Receive Frames
+--------------------------------------------------------------------*/
+/*-- Communication Frame: Receive Frame Structure --*/
+typedef struct hfa384x_rx_frame
+{
+ /*-- MAC rx descriptor (hfa384x byte order) --*/
+ UINT16 status;
+ UINT32 time;
+ UINT8 silence;
+ UINT8 signal;
+ UINT8 rate;
+ UINT8 rx_flow;
+ UINT16 reserved1;
+ UINT16 reserved2;
+
+ /*-- 802.11 Header Information (802.11 byte order) --*/
+ UINT16 frame_control;
+ UINT16 duration_id;
+ UINT8 address1[6];
+ UINT8 address2[6];
+ UINT8 address3[6];
+ UINT16 sequence_control;
+ UINT8 address4[6];
+ UINT16 data_len; /* hfa384x (little endian) format */
+
+ /*-- 802.3 Header Information --*/
+ UINT8 dest_addr[6];
+ UINT8 src_addr[6];
+ UINT16 data_length; /* IEEE? (big endian) format */
+} __WLAN_ATTRIB_PACK__ hfa384x_rx_frame_t;
+/*--------------------------------------------------------------------
+Communication Frames: Field Masks for Receive Frames
+--------------------------------------------------------------------*/
+/*-- Offsets --------*/
+#define HFA384x_RX_DATA_LEN_OFF ((UINT16)44)
+#define HFA384x_RX_80211HDR_OFF ((UINT16)14)
+#define HFA384x_RX_DATA_OFF ((UINT16)60)
+
+/*-- Status Fields --*/
+#define HFA384x_RXSTATUS_MSGTYPE ((UINT16)(BIT15 | BIT14 | BIT13))
+#define HFA384x_RXSTATUS_MACPORT ((UINT16)(BIT10 | BIT9 | BIT8))
+#define HFA384x_RXSTATUS_UNDECR ((UINT16)BIT1)
+#define HFA384x_RXSTATUS_FCSERR ((UINT16)BIT0)
+/*--------------------------------------------------------------------
+Communication Frames: Test/Get/Set Field Values for Receive Frames
+--------------------------------------------------------------------*/
+#define HFA384x_RXSTATUS_MSGTYPE_GET(value) ((UINT16)((((UINT16)(value)) & HFA384x_RXSTATUS_MSGTYPE) >> 13))
+#define HFA384x_RXSTATUS_MSGTYPE_SET(value) ((UINT16)(((UINT16)(value)) << 13))
+#define HFA384x_RXSTATUS_MACPORT_GET(value) ((UINT16)((((UINT16)(value)) & HFA384x_RXSTATUS_MACPORT) >> 8))
+#define HFA384x_RXSTATUS_MACPORT_SET(value) ((UINT16)(((UINT16)(value)) << 8))
+#define HFA384x_RXSTATUS_ISUNDECR(value) ((UINT16)(((UINT16)(value)) & HFA384x_RXSTATUS_UNDECR))
+#define HFA384x_RXSTATUS_ISFCSERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_RXSTATUS_FCSERR))
+/*--------------------------------------------------------------------
+ FRAME STRUCTURES: Information Types and Information Frame Structures
+----------------------------------------------------------------------
+Information Types
+--------------------------------------------------------------------*/
+#define HFA384x_IT_HANDOVERADDR ((UINT16)0xF000UL)
+#define HFA384x_IT_HANDOVERDEAUTHADDRESS ((UINT16)0xF001UL)//AP 1.3.7
+#define HFA384x_IT_COMMTALLIES ((UINT16)0xF100UL)
+#define HFA384x_IT_SCANRESULTS ((UINT16)0xF101UL)
+#define HFA384x_IT_CHINFORESULTS ((UINT16)0xF102UL)
+#define HFA384x_IT_HOSTSCANRESULTS ((UINT16)0xF103UL)
+#define HFA384x_IT_LINKSTATUS ((UINT16)0xF200UL)
+#define HFA384x_IT_ASSOCSTATUS ((UINT16)0xF201UL)
+#define HFA384x_IT_AUTHREQ ((UINT16)0xF202UL)
+#define HFA384x_IT_PSUSERCNT ((UINT16)0xF203UL)
+#define HFA384x_IT_KEYIDCHANGED ((UINT16)0xF204UL)
+#define HFA384x_IT_ASSOCREQ ((UINT16)0xF205UL)
+#define HFA384x_IT_MICFAILURE ((UINT16)0xF206UL)
+
+/*--------------------------------------------------------------------
+Information Frames Structures
+----------------------------------------------------------------------
+Information Frames: Notification Frame Structures
+--------------------------------------------------------------------*/
+/*-- Notification Frame,MAC Mgmt: Handover Address --*/
+typedef struct hfa384x_HandoverAddr
+{
+ UINT16 framelen;
+ UINT16 infotype;
+ UINT8 handover_addr[WLAN_BSSID_LEN];
+} __WLAN_ATTRIB_PACK__ hfa384x_HandoverAddr_t;
+
+/*-- Inquiry Frame, Diagnose: Communication Tallies --*/
+typedef struct hfa384x_CommTallies16
+{
+ UINT16 txunicastframes;
+ UINT16 txmulticastframes;
+ UINT16 txfragments;
+ UINT16 txunicastoctets;
+ UINT16 txmulticastoctets;
+ UINT16 txdeferredtrans;
+ UINT16 txsingleretryframes;
+ UINT16 txmultipleretryframes;
+ UINT16 txretrylimitexceeded;
+ UINT16 txdiscards;
+ UINT16 rxunicastframes;
+ UINT16 rxmulticastframes;
+ UINT16 rxfragments;
+ UINT16 rxunicastoctets;
+ UINT16 rxmulticastoctets;
+ UINT16 rxfcserrors;
+ UINT16 rxdiscardsnobuffer;
+ UINT16 txdiscardswrongsa;
+ UINT16 rxdiscardswepundecr;
+ UINT16 rxmsginmsgfrag;
+ UINT16 rxmsginbadmsgfrag;
+} __WLAN_ATTRIB_PACK__ hfa384x_CommTallies16_t;
+
+typedef struct hfa384x_CommTallies32
+{
+ UINT32 txunicastframes;
+ UINT32 txmulticastframes;
+ UINT32 txfragments;
+ UINT32 txunicastoctets;
+ UINT32 txmulticastoctets;
+ UINT32 txdeferredtrans;
+ UINT32 txsingleretryframes;
+ UINT32 txmultipleretryframes;
+ UINT32 txretrylimitexceeded;
+ UINT32 txdiscards;
+ UINT32 rxunicastframes;
+ UINT32 rxmulticastframes;
+ UINT32 rxfragments;
+ UINT32 rxunicastoctets;
+ UINT32 rxmulticastoctets;
+ UINT32 rxfcserrors;
+ UINT32 rxdiscardsnobuffer;
+ UINT32 txdiscardswrongsa;
+ UINT32 rxdiscardswepundecr;
+ UINT32 rxmsginmsgfrag;
+ UINT32 rxmsginbadmsgfrag;
+} __WLAN_ATTRIB_PACK__ hfa384x_CommTallies32_t;
+
+/*-- Inquiry Frame, Diagnose: Scan Results & Subfields--*/
+typedef struct hfa384x_ScanResultSub
+{
+ UINT16 chid;
+ UINT16 anl;
+ UINT16 sl;
+ UINT8 bssid[WLAN_BSSID_LEN];
+ UINT16 bcnint;
+ UINT16 capinfo;
+ hfa384x_bytestr32_t ssid;
+ UINT8 supprates[10]; /* 802.11 info element */
+ UINT16 proberesp_rate;
+} __WLAN_ATTRIB_PACK__ hfa384x_ScanResultSub_t;
+
+typedef struct hfa384x_ScanResult
+{
+ UINT16 rsvd;
+ UINT16 scanreason;
+ hfa384x_ScanResultSub_t
+ result[HFA384x_SCANRESULT_MAX];
+} __WLAN_ATTRIB_PACK__ hfa384x_ScanResult_t;
+
+/*-- Inquiry Frame, Diagnose: ChInfo Results & Subfields--*/
+typedef struct hfa384x_ChInfoResultSub
+{
+ UINT16 chid;
+ UINT16 anl;
+ UINT16 pnl;
+ UINT16 active;
+} __WLAN_ATTRIB_PACK__ hfa384x_ChInfoResultSub_t;
+
+#define HFA384x_CHINFORESULT_BSSACTIVE BIT0
+#define HFA384x_CHINFORESULT_PCFACTIVE BIT1
+
+typedef struct hfa384x_ChInfoResult
+{
+ UINT16 scanchannels;
+ hfa384x_ChInfoResultSub_t
+ result[HFA384x_CHINFORESULT_MAX];
+} __WLAN_ATTRIB_PACK__ hfa384x_ChInfoResult_t;
+
+/*-- Inquiry Frame, Diagnose: Host Scan Results & Subfields--*/
+typedef struct hfa384x_HScanResultSub
+{
+ UINT16 chid;
+ UINT16 anl;
+ UINT16 sl;
+ UINT8 bssid[WLAN_BSSID_LEN];
+ UINT16 bcnint;
+ UINT16 capinfo;
+ hfa384x_bytestr32_t ssid;
+ UINT8 supprates[10]; /* 802.11 info element */
+ UINT16 proberesp_rate;
+ UINT16 atim;
+} __WLAN_ATTRIB_PACK__ hfa384x_HScanResultSub_t;
+
+typedef struct hfa384x_HScanResult
+{
+ UINT16 nresult;
+ UINT16 rsvd;
+ hfa384x_HScanResultSub_t
+ result[HFA384x_HSCANRESULT_MAX];
+} __WLAN_ATTRIB_PACK__ hfa384x_HScanResult_t;
+
+/*-- Unsolicited Frame, MAC Mgmt: LinkStatus --*/
+
+#define HFA384x_LINK_NOTCONNECTED ((UINT16)0)
+#define HFA384x_LINK_CONNECTED ((UINT16)1)
+#define HFA384x_LINK_DISCONNECTED ((UINT16)2)
+#define HFA384x_LINK_AP_CHANGE ((UINT16)3)
+#define HFA384x_LINK_AP_OUTOFRANGE ((UINT16)4)
+#define HFA384x_LINK_AP_INRANGE ((UINT16)5)
+#define HFA384x_LINK_ASSOCFAIL ((UINT16)6)
+
+typedef struct hfa384x_LinkStatus
+{
+ UINT16 linkstatus;
+} __WLAN_ATTRIB_PACK__ hfa384x_LinkStatus_t;
+
+
+/*-- Unsolicited Frame, MAC Mgmt: AssociationStatus (--*/
+
+#define HFA384x_ASSOCSTATUS_STAASSOC ((UINT16)1)
+#define HFA384x_ASSOCSTATUS_REASSOC ((UINT16)2)
+#define HFA384x_ASSOCSTATUS_DISASSOC ((UINT16)3)
+#define HFA384x_ASSOCSTATUS_ASSOCFAIL ((UINT16)4)
+#define HFA384x_ASSOCSTATUS_AUTHFAIL ((UINT16)5)
+
+typedef struct hfa384x_AssocStatus
+{
+ UINT16 assocstatus;
+ UINT8 sta_addr[WLAN_ADDR_LEN];
+ /* old_ap_addr is only valid if assocstatus == 2 */
+ UINT8 old_ap_addr[WLAN_ADDR_LEN];
+ UINT16 reason;
+ UINT16 reserved;
+} __WLAN_ATTRIB_PACK__ hfa384x_AssocStatus_t;
+
+/*-- Unsolicited Frame, MAC Mgmt: AuthRequest (AP Only) --*/
+
+typedef struct hfa384x_AuthRequest
+{
+ UINT8 sta_addr[WLAN_ADDR_LEN];
+ UINT16 algorithm;
+} __WLAN_ATTRIB_PACK__ hfa384x_AuthReq_t;
+
+/*-- Unsolicited Frame, MAC Mgmt: AssocRequest (AP Only) --*/
+
+typedef struct hfa384x_AssocRequest
+{
+ UINT8 sta_addr[WLAN_ADDR_LEN];
+ UINT16 type;
+ UINT8 wpa_data[80];
+} __WLAN_ATTRIB_PACK__ hfa384x_AssocReq_t;
+
+
+#define HFA384x_ASSOCREQ_TYPE_ASSOC 0
+#define HFA384x_ASSOCREQ_TYPE_REASSOC 1
+
+/*-- Unsolicited Frame, MAC Mgmt: MIC Failure (AP Only) --*/
+
+typedef struct hfa384x_MicFailure
+{
+ UINT8 sender[WLAN_ADDR_LEN];
+ UINT8 dest[WLAN_ADDR_LEN];
+} __WLAN_ATTRIB_PACK__ hfa384x_MicFailure_t;
+
+/*-- Unsolicited Frame, MAC Mgmt: PSUserCount (AP Only) --*/
+
+typedef struct hfa384x_PSUserCount
+{
+ UINT16 usercnt;
+} __WLAN_ATTRIB_PACK__ hfa384x_PSUserCount_t;
+
+typedef struct hfa384x_KeyIDChanged
+{
+ UINT8 sta_addr[WLAN_ADDR_LEN];
+ UINT16 keyid;
+} __WLAN_ATTRIB_PACK__ hfa384x_KeyIDChanged_t;
+
+/*-- Collection of all Inf frames ---------------*/
+typedef union hfa384x_infodata {
+ hfa384x_CommTallies16_t commtallies16;
+ hfa384x_CommTallies32_t commtallies32;
+ hfa384x_ScanResult_t scanresult;
+ hfa384x_ChInfoResult_t chinforesult;
+ hfa384x_HScanResult_t hscanresult;
+ hfa384x_LinkStatus_t linkstatus;
+ hfa384x_AssocStatus_t assocstatus;
+ hfa384x_AuthReq_t authreq;
+ hfa384x_PSUserCount_t psusercnt;
+ hfa384x_KeyIDChanged_t keyidchanged;
+} __WLAN_ATTRIB_PACK__ hfa384x_infodata_t;
+
+typedef struct hfa384x_InfFrame
+{
+ UINT16 framelen;
+ UINT16 infotype;
+ hfa384x_infodata_t info;
+} __WLAN_ATTRIB_PACK__ hfa384x_InfFrame_t;
+
+#if (WLAN_HOSTIF == WLAN_USB)
+/*--------------------------------------------------------------------
+USB Packet structures and constants.
+--------------------------------------------------------------------*/
+
+/* Should be sent to the ctrlout endpoint */
+#define HFA384x_USB_ENBULKIN 6
+
+/* Should be sent to the bulkout endpoint */
+#define HFA384x_USB_TXFRM 0
+#define HFA384x_USB_CMDREQ 1
+#define HFA384x_USB_WRIDREQ 2
+#define HFA384x_USB_RRIDREQ 3
+#define HFA384x_USB_WMEMREQ 4
+#define HFA384x_USB_RMEMREQ 5
+
+/* Received from the bulkin endpoint */
+#define HFA384x_USB_ISFRM(a) (!((a) & 0x8000))
+#define HFA384x_USB_ISTXFRM(a) (((a) & 0x9000) == 0x1000)
+#define HFA384x_USB_ISRXFRM(a) (!((a) & 0x9000))
+#define HFA384x_USB_INFOFRM 0x8000
+#define HFA384x_USB_CMDRESP 0x8001
+#define HFA384x_USB_WRIDRESP 0x8002
+#define HFA384x_USB_RRIDRESP 0x8003
+#define HFA384x_USB_WMEMRESP 0x8004
+#define HFA384x_USB_RMEMRESP 0x8005
+#define HFA384x_USB_BUFAVAIL 0x8006
+#define HFA384x_USB_ERROR 0x8007
+
+/*------------------------------------*/
+/* Request (bulk OUT) packet contents */
+
+typedef struct hfa384x_usb_txfrm {
+ hfa384x_tx_frame_t desc;
+ UINT8 data[WLAN_DATA_MAXLEN];
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_txfrm_t;
+
+typedef struct hfa384x_usb_cmdreq {
+ UINT16 type;
+ UINT16 cmd;
+ UINT16 parm0;
+ UINT16 parm1;
+ UINT16 parm2;
+ UINT8 pad[54];
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_cmdreq_t;
+
+typedef struct hfa384x_usb_wridreq {
+ UINT16 type;
+ UINT16 frmlen;
+ UINT16 rid;
+ UINT8 data[HFA384x_RIDDATA_MAXLEN];
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_wridreq_t;
+
+typedef struct hfa384x_usb_rridreq {
+ UINT16 type;
+ UINT16 frmlen;
+ UINT16 rid;
+ UINT8 pad[58];
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_rridreq_t;
+
+typedef struct hfa384x_usb_wmemreq {
+ UINT16 type;
+ UINT16 frmlen;
+ UINT16 offset;
+ UINT16 page;
+ UINT8 data[HFA384x_USB_RWMEM_MAXLEN];
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_wmemreq_t;
+
+typedef struct hfa384x_usb_rmemreq {
+ UINT16 type;
+ UINT16 frmlen;
+ UINT16 offset;
+ UINT16 page;
+ UINT8 pad[56];
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_rmemreq_t;
+
+/*------------------------------------*/
+/* Response (bulk IN) packet contents */
+
+typedef struct hfa384x_usb_rxfrm {
+ hfa384x_rx_frame_t desc;
+ UINT8 data[WLAN_DATA_MAXLEN];
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_rxfrm_t;
+
+typedef struct hfa384x_usb_infofrm {
+ UINT16 type;
+ hfa384x_InfFrame_t info;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_infofrm_t;
+
+typedef struct hfa384x_usb_statusresp {
+ UINT16 type;
+ UINT16 status;
+ UINT16 resp0;
+ UINT16 resp1;
+ UINT16 resp2;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_cmdresp_t;
+
+typedef hfa384x_usb_cmdresp_t hfa384x_usb_wridresp_t;
+
+typedef struct hfa384x_usb_rridresp {
+ UINT16 type;
+ UINT16 frmlen;
+ UINT16 rid;
+ UINT8 data[HFA384x_RIDDATA_MAXLEN];
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_rridresp_t;
+
+typedef hfa384x_usb_cmdresp_t hfa384x_usb_wmemresp_t;
+
+typedef struct hfa384x_usb_rmemresp {
+ UINT16 type;
+ UINT16 frmlen;
+ UINT8 data[HFA384x_USB_RWMEM_MAXLEN];
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_rmemresp_t;
+
+typedef struct hfa384x_usb_bufavail {
+ UINT16 type;
+ UINT16 frmlen;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_bufavail_t;
+
+typedef struct hfa384x_usb_error {
+ UINT16 type;
+ UINT16 errortype;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_error_t;
+
+/*----------------------------------------------------------*/
+/* Unions for packaging all the known packet types together */
+
+typedef union hfa384x_usbout {
+ UINT16 type;
+ hfa384x_usb_txfrm_t txfrm;
+ hfa384x_usb_cmdreq_t cmdreq;
+ hfa384x_usb_wridreq_t wridreq;
+ hfa384x_usb_rridreq_t rridreq;
+ hfa384x_usb_wmemreq_t wmemreq;
+ hfa384x_usb_rmemreq_t rmemreq;
+} __WLAN_ATTRIB_PACK__ hfa384x_usbout_t;
+
+typedef union hfa384x_usbin {
+ UINT16 type;
+ hfa384x_usb_rxfrm_t rxfrm;
+ hfa384x_usb_txfrm_t txfrm;
+ hfa384x_usb_infofrm_t infofrm;
+ hfa384x_usb_cmdresp_t cmdresp;
+ hfa384x_usb_wridresp_t wridresp;
+ hfa384x_usb_rridresp_t rridresp;
+ hfa384x_usb_wmemresp_t wmemresp;
+ hfa384x_usb_rmemresp_t rmemresp;
+ hfa384x_usb_bufavail_t bufavail;
+ hfa384x_usb_error_t usberror;
+ UINT8 boguspad[3000];
+} __WLAN_ATTRIB_PACK__ hfa384x_usbin_t;
+
+#endif /* WLAN_USB */
+
+/*--------------------------------------------------------------------
+PD record structures.
+--------------------------------------------------------------------*/
+
+typedef struct hfa384x_pdr_pcb_partnum
+{
+ UINT8 num[8];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_pcb_partnum_t;
+
+typedef struct hfa384x_pdr_pcb_tracenum
+{
+ UINT8 num[8];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_pcb_tracenum_t;
+
+typedef struct hfa384x_pdr_nic_serial
+{
+ UINT8 num[12];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_serial_t;
+
+typedef struct hfa384x_pdr_mkk_measurements
+{
+ double carrier_freq;
+ double occupied_band;
+ double power_density;
+ double tx_spur_f1;
+ double tx_spur_f2;
+ double tx_spur_f3;
+ double tx_spur_f4;
+ double tx_spur_l1;
+ double tx_spur_l2;
+ double tx_spur_l3;
+ double tx_spur_l4;
+ double rx_spur_f1;
+ double rx_spur_f2;
+ double rx_spur_l1;
+ double rx_spur_l2;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mkk_measurements_t;
+
+typedef struct hfa384x_pdr_nic_ramsize
+{
+ UINT8 size[12]; /* units of KB */
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_ramsize_t;
+
+typedef struct hfa384x_pdr_mfisuprange
+{
+ UINT16 id;
+ UINT16 variant;
+ UINT16 bottom;
+ UINT16 top;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mfisuprange_t;
+
+typedef struct hfa384x_pdr_cfisuprange
+{
+ UINT16 id;
+ UINT16 variant;
+ UINT16 bottom;
+ UINT16 top;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_cfisuprange_t;
+
+typedef struct hfa384x_pdr_nicid
+{
+ UINT16 id;
+ UINT16 variant;
+ UINT16 major;
+ UINT16 minor;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nicid_t;
+
+
+typedef struct hfa384x_pdr_refdac_measurements
+{
+ UINT16 value[0];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_refdac_measurements_t;
+
+typedef struct hfa384x_pdr_vgdac_measurements
+{
+ UINT16 value[0];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_vgdac_measurements_t;
+
+typedef struct hfa384x_pdr_level_comp_measurements
+{
+ UINT16 value[0];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_level_compc_measurements_t;
+
+typedef struct hfa384x_pdr_mac_address
+{
+ UINT8 addr[6];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mac_address_t;
+
+typedef struct hfa384x_pdr_mkk_callname
+{
+ UINT8 callname[8];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mkk_callname_t;
+
+typedef struct hfa384x_pdr_regdomain
+{
+ UINT16 numdomains;
+ UINT16 domain[5];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_regdomain_t;
+
+typedef struct hfa384x_pdr_allowed_channel
+{
+ UINT16 ch_bitmap;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_allowed_channel_t;
+
+typedef struct hfa384x_pdr_default_channel
+{
+ UINT16 channel;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_default_channel_t;
+
+typedef struct hfa384x_pdr_privacy_option
+{
+ UINT16 available;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_privacy_option_t;
+
+typedef struct hfa384x_pdr_temptype
+{
+ UINT16 type;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_temptype_t;
+
+typedef struct hfa384x_pdr_refdac_setup
+{
+ UINT16 ch_value[14];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_refdac_setup_t;
+
+typedef struct hfa384x_pdr_vgdac_setup
+{
+ UINT16 ch_value[14];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_vgdac_setup_t;
+
+typedef struct hfa384x_pdr_level_comp_setup
+{
+ UINT16 ch_value[14];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_level_comp_setup_t;
+
+typedef struct hfa384x_pdr_trimdac_setup
+{
+ UINT16 trimidac;
+ UINT16 trimqdac;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_trimdac_setup_t;
+
+typedef struct hfa384x_pdr_ifr_setting
+{
+ UINT16 value[3];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_ifr_setting_t;
+
+typedef struct hfa384x_pdr_rfr_setting
+{
+ UINT16 value[3];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_rfr_setting_t;
+
+typedef struct hfa384x_pdr_hfa3861_baseline
+{
+ UINT16 value[50];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_baseline_t;
+
+typedef struct hfa384x_pdr_hfa3861_shadow
+{
+ UINT32 value[32];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_shadow_t;
+
+typedef struct hfa384x_pdr_hfa3861_ifrf
+{
+ UINT32 value[20];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_ifrf_t;
+
+typedef struct hfa384x_pdr_hfa3861_chcalsp
+{
+ UINT16 value[14];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_chcalsp_t;
+
+typedef struct hfa384x_pdr_hfa3861_chcali
+{
+ UINT16 value[17];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_chcali_t;
+
+typedef struct hfa384x_pdr_hfa3861_nic_config
+{
+ UINT16 config_bitmap;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_config_t;
+
+typedef struct hfa384x_pdr_hfo_delay
+{
+ UINT8 hfo_delay;
+} __WLAN_ATTRIB_PACK__ hfa384x_hfo_delay_t;
+
+typedef struct hfa384x_pdr_hfa3861_manf_testsp
+{
+ UINT16 value[30];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_manf_testsp_t;
+
+typedef struct hfa384x_pdr_hfa3861_manf_testi
+{
+ UINT16 value[30];
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_manf_testi_t;
+
+typedef struct hfa384x_end_of_pda
+{
+ UINT16 crc;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_end_of_pda_t;
+
+typedef struct hfa384x_pdrec
+{
+ UINT16 len; /* in words */
+ UINT16 code;
+ union pdr {
+ hfa384x_pdr_pcb_partnum_t pcb_partnum;
+ hfa384x_pdr_pcb_tracenum_t pcb_tracenum;
+ hfa384x_pdr_nic_serial_t nic_serial;
+ hfa384x_pdr_mkk_measurements_t mkk_measurements;
+ hfa384x_pdr_nic_ramsize_t nic_ramsize;
+ hfa384x_pdr_mfisuprange_t mfisuprange;
+ hfa384x_pdr_cfisuprange_t cfisuprange;
+ hfa384x_pdr_nicid_t nicid;
+ hfa384x_pdr_refdac_measurements_t refdac_measurements;
+ hfa384x_pdr_vgdac_measurements_t vgdac_measurements;
+ hfa384x_pdr_level_compc_measurements_t level_compc_measurements;
+ hfa384x_pdr_mac_address_t mac_address;
+ hfa384x_pdr_mkk_callname_t mkk_callname;
+ hfa384x_pdr_regdomain_t regdomain;
+ hfa384x_pdr_allowed_channel_t allowed_channel;
+ hfa384x_pdr_default_channel_t default_channel;
+ hfa384x_pdr_privacy_option_t privacy_option;
+ hfa384x_pdr_temptype_t temptype;
+ hfa384x_pdr_refdac_setup_t refdac_setup;
+ hfa384x_pdr_vgdac_setup_t vgdac_setup;
+ hfa384x_pdr_level_comp_setup_t level_comp_setup;
+ hfa384x_pdr_trimdac_setup_t trimdac_setup;
+ hfa384x_pdr_ifr_setting_t ifr_setting;
+ hfa384x_pdr_rfr_setting_t rfr_setting;
+ hfa384x_pdr_hfa3861_baseline_t hfa3861_baseline;
+ hfa384x_pdr_hfa3861_shadow_t hfa3861_shadow;
+ hfa384x_pdr_hfa3861_ifrf_t hfa3861_ifrf;
+ hfa384x_pdr_hfa3861_chcalsp_t hfa3861_chcalsp;
+ hfa384x_pdr_hfa3861_chcali_t hfa3861_chcali;
+ hfa384x_pdr_nic_config_t nic_config;
+ hfa384x_hfo_delay_t hfo_delay;
+ hfa384x_pdr_hfa3861_manf_testsp_t hfa3861_manf_testsp;
+ hfa384x_pdr_hfa3861_manf_testi_t hfa3861_manf_testi;
+ hfa384x_pdr_end_of_pda_t end_of_pda;
+
+ } data;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdrec_t;
+
+
+#ifdef __KERNEL__
+/*--------------------------------------------------------------------
+--- MAC state structure, argument to all functions --
+--- Also, a collection of support types --
+--------------------------------------------------------------------*/
+typedef struct hfa384x_statusresult
+{
+ UINT16 status;
+ UINT16 resp0;
+ UINT16 resp1;
+ UINT16 resp2;
+} hfa384x_cmdresult_t;
+
+#if (WLAN_HOSTIF == WLAN_USB)
+
+/* USB Control Exchange (CTLX):
+ * A queue of the structure below is maintained for all of the
+ * Request/Response type USB packets supported by Prism2.
+ */
+/* The following hfa384x_* structures are arguments to
+ * the usercb() for the different CTLX types.
+ */
+typedef hfa384x_cmdresult_t hfa384x_wridresult_t;
+typedef hfa384x_cmdresult_t hfa384x_wmemresult_t;
+
+typedef struct hfa384x_rridresult
+{
+ UINT16 rid;
+ const void *riddata;
+ UINT riddata_len;
+} hfa384x_rridresult_t;
+
+enum ctlx_state {
+ CTLX_START = 0, /* Start state, not queued */
+
+ CTLX_COMPLETE, /* CTLX successfully completed */
+ CTLX_REQ_FAILED, /* OUT URB completed w/ error */
+
+ CTLX_PENDING, /* Queued, data valid */
+ CTLX_REQ_SUBMITTED, /* OUT URB submitted */
+ CTLX_REQ_COMPLETE, /* OUT URB complete */
+ CTLX_RESP_COMPLETE /* IN URB received */
+};
+typedef enum ctlx_state CTLX_STATE;
+
+struct hfa384x_usbctlx;
+struct hfa384x;
+
+typedef void (*ctlx_cmdcb_t)( struct hfa384x*, const struct hfa384x_usbctlx* );
+
+typedef void (*ctlx_usercb_t)(
+ struct hfa384x *hw,
+ void *ctlxresult,
+ void *usercb_data);
+
+typedef struct hfa384x_usbctlx
+{
+ struct list_head list;
+
+ size_t outbufsize;
+ hfa384x_usbout_t outbuf; /* pkt buf for OUT */
+ hfa384x_usbin_t inbuf; /* pkt buf for IN(a copy) */
+
+ CTLX_STATE state; /* Tracks running state */
+
+ struct completion done;
+ volatile int reapable; /* Food for the reaper task */
+
+ ctlx_cmdcb_t cmdcb; /* Async command callback */
+ ctlx_usercb_t usercb; /* Async user callback, */
+ void *usercb_data; /* at CTLX completion */
+
+ int variant; /* Identifies cmd variant */
+} hfa384x_usbctlx_t;
+
+typedef struct hfa384x_usbctlxq
+{
+ spinlock_t lock;
+ struct list_head pending;
+ struct list_head active;
+ struct list_head completing;
+ struct list_head reapable;
+} hfa384x_usbctlxq_t;
+#endif
+
+typedef struct hfa484x_metacmd
+{
+ UINT16 cmd;
+
+ UINT16 parm0;
+ UINT16 parm1;
+ UINT16 parm2;
+
+#if 0 //XXX cmd irq stuff
+ UINT16 bulkid; /* what RID/FID to copy down. */
+ int bulklen; /* how much to copy from BAP */
+ char *bulkdata; /* And to where? */
+#endif
+
+ hfa384x_cmdresult_t result;
+} hfa384x_metacmd_t;
+
+#define MAX_PRISM2_GRP_ADDR 16
+#define MAX_GRP_ADDR 32
+#define WLAN_COMMENT_MAX 80 /* Max. length of user comment string. */
+
+#define MM_SAT_PCF (BIT14)
+#define MM_GCSD_PCF (BIT15)
+#define MM_GCSD_PCF_EB (BIT14 | BIT15)
+
+#define WLAN_STATE_STOPPED 0 /* Network is not active. */
+#define WLAN_STATE_STARTED 1 /* Network has been started. */
+
+#define WLAN_AUTH_MAX 60 /* Max. # of authenticated stations. */
+#define WLAN_ACCESS_MAX 60 /* Max. # of stations in an access list. */
+#define WLAN_ACCESS_NONE 0 /* No stations may be authenticated. */
+#define WLAN_ACCESS_ALL 1 /* All stations may be authenticated. */
+#define WLAN_ACCESS_ALLOW 2 /* Authenticate only "allowed" stations. */
+#define WLAN_ACCESS_DENY 3 /* Do not authenticate "denied" stations. */
+
+/* XXX These are going away ASAP */
+typedef struct prism2sta_authlist
+{
+ UINT cnt;
+ UINT8 addr[WLAN_AUTH_MAX][WLAN_ADDR_LEN];
+ UINT8 assoc[WLAN_AUTH_MAX];
+} prism2sta_authlist_t;
+
+typedef struct prism2sta_accesslist
+{
+ UINT modify;
+ UINT cnt;
+ UINT8 addr[WLAN_ACCESS_MAX][WLAN_ADDR_LEN];
+ UINT cnt1;
+ UINT8 addr1[WLAN_ACCESS_MAX][WLAN_ADDR_LEN];
+} prism2sta_accesslist_t;
+
+typedef struct hfa384x
+{
+#if (WLAN_HOSTIF != WLAN_USB)
+ /* Resource config */
+ UINT32 iobase;
+ char __iomem *membase;
+ UINT32 irq;
+#else
+ /* USB support data */
+ struct usb_device *usb;
+ struct urb rx_urb;
+ struct sk_buff *rx_urb_skb;
+ struct urb tx_urb;
+ struct urb ctlx_urb;
+ hfa384x_usbout_t txbuff;
+ hfa384x_usbctlxq_t ctlxq;
+ struct timer_list reqtimer;
+ struct timer_list resptimer;
+
+ struct timer_list throttle;
+
+ struct tasklet_struct reaper_bh;
+ struct tasklet_struct completion_bh;
+
+ struct work_struct usb_work;
+
+ unsigned long usb_flags;
+#define THROTTLE_RX 0
+#define THROTTLE_TX 1
+#define WORK_RX_HALT 2
+#define WORK_TX_HALT 3
+#define WORK_RX_RESUME 4
+#define WORK_TX_RESUME 5
+
+ unsigned short req_timer_done:1;
+ unsigned short resp_timer_done:1;
+
+ int endp_in;
+ int endp_out;
+#endif /* !USB */
+
+#if (WLAN_HOSTIF == WLAN_PCMCIA)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16)
+ struct pcmcia_device *pdev;
+#else
+ dev_link_t *link;
+#endif
+ dev_node_t node;
+#endif
+
+ int sniff_fcs;
+ int sniff_channel;
+ int sniff_truncate;
+ int sniffhdr;
+
+ wait_queue_head_t cmdq; /* wait queue itself */
+
+ /* Controller state */
+ UINT32 state;
+ UINT32 isap;
+ UINT8 port_enabled[HFA384x_NUMPORTS_MAX];
+#if (WLAN_HOSTIF != WLAN_USB)
+ UINT auxen;
+ UINT isram16;
+#endif /* !USB */
+
+ /* Download support */
+ UINT dlstate;
+ hfa384x_downloadbuffer_t bufinfo;
+ UINT16 dltimeout;
+
+#if (WLAN_HOSTIF != WLAN_USB)
+ spinlock_t cmdlock;
+ volatile int cmdflag; /* wait queue flag */
+ hfa384x_metacmd_t *cmddata; /* for our async callback */
+
+ /* BAP support */
+ spinlock_t baplock;
+ struct tasklet_struct bap_tasklet;
+
+ /* MAC buffer ids */
+ UINT16 txfid_head;
+ UINT16 txfid_tail;
+ UINT txfid_N;
+ UINT16 txfid_queue[HFA384x_DRVR_FIDSTACKLEN_MAX];
+ UINT16 infofid;
+ struct semaphore infofid_sem;
+#endif /* !USB */
+
+ int scanflag; /* to signal scan comlete */
+ int join_ap; /* are we joined to a specific ap */
+ int join_retries; /* number of join retries till we fail */
+ hfa384x_JoinRequest_data_t joinreq; /* join request saved data */
+
+ wlandevice_t *wlandev;
+ /* Timer to allow for the deferred processing of linkstatus messages */
+ struct work_struct link_bh;
+
+ struct work_struct commsqual_bh;
+ hfa384x_commsquality_t qual;
+ struct timer_list commsqual_timer;
+
+ UINT16 link_status;
+ UINT16 link_status_new;
+ struct sk_buff_head authq;
+
+ /* And here we have stuff that used to be in priv */
+
+ /* State variables */
+ UINT presniff_port_type;
+ UINT16 presniff_wepflags;
+ UINT32 dot11_desired_bss_type;
+ int ap; /* AP flag: 0 - Station, 1 - Access Point. */
+
+ int dbmadjust;
+
+ /* Group Addresses - right now, there are up to a total
+ of MAX_GRP_ADDR group addresses */
+ UINT8 dot11_grp_addr[MAX_GRP_ADDR][WLAN_ADDR_LEN];
+ UINT dot11_grpcnt;
+
+ /* Component Identities */
+ hfa384x_compident_t ident_nic;
+ hfa384x_compident_t ident_pri_fw;
+ hfa384x_compident_t ident_sta_fw;
+ hfa384x_compident_t ident_ap_fw;
+ UINT16 mm_mods;
+
+ /* Supplier compatibility ranges */
+ hfa384x_caplevel_t cap_sup_mfi;
+ hfa384x_caplevel_t cap_sup_cfi;
+ hfa384x_caplevel_t cap_sup_pri;
+ hfa384x_caplevel_t cap_sup_sta;
+ hfa384x_caplevel_t cap_sup_ap;
+
+ /* Actor compatibility ranges */
+ hfa384x_caplevel_t cap_act_pri_cfi; /* pri f/w to controller interface */
+ hfa384x_caplevel_t cap_act_sta_cfi; /* sta f/w to controller interface */
+ hfa384x_caplevel_t cap_act_sta_mfi; /* sta f/w to modem interface */
+ hfa384x_caplevel_t cap_act_ap_cfi; /* ap f/w to controller interface */
+ hfa384x_caplevel_t cap_act_ap_mfi; /* ap f/w to modem interface */
+
+ UINT32 psusercount; /* Power save user count. */
+ hfa384x_CommTallies32_t tallies; /* Communication tallies. */
+ UINT8 comment[WLAN_COMMENT_MAX+1]; /* User comment */
+
+ /* Channel Info request results (AP only) */
+ struct {
+ atomic_t done;
+ UINT8 count;
+ hfa384x_ChInfoResult_t results;
+ } channel_info;
+
+ hfa384x_InfFrame_t *scanresults;
+
+
+ prism2sta_authlist_t authlist; /* Authenticated station list. */
+ UINT accessmode; /* Access mode. */
+ prism2sta_accesslist_t allow; /* Allowed station list. */
+ prism2sta_accesslist_t deny; /* Denied station list. */
+
+} hfa384x_t;
+
+/*=============================================================*/
+/*--- Function Declarations -----------------------------------*/
+/*=============================================================*/
+#if (WLAN_HOSTIF == WLAN_USB)
+void
+hfa384x_create(
+ hfa384x_t *hw,
+ struct usb_device *usb);
+#else
+void
+hfa384x_create(
+ hfa384x_t *hw,
+ UINT irq,
+ UINT32 iobase,
+ UINT8 __iomem *membase);
+#endif
+
+void hfa384x_destroy(hfa384x_t *hw);
+
+irqreturn_t
+hfa384x_interrupt(int irq, void *dev_id PT_REGS);
+int
+hfa384x_corereset( hfa384x_t *hw, int holdtime, int settletime, int genesis);
+int
+hfa384x_drvr_chinforesults( hfa384x_t *hw);
+int
+hfa384x_drvr_commtallies( hfa384x_t *hw);
+int
+hfa384x_drvr_disable(hfa384x_t *hw, UINT16 macport);
+int
+hfa384x_drvr_enable(hfa384x_t *hw, UINT16 macport);
+int
+hfa384x_drvr_flashdl_enable(hfa384x_t *hw);
+int
+hfa384x_drvr_flashdl_disable(hfa384x_t *hw);
+int
+hfa384x_drvr_flashdl_write(hfa384x_t *hw, UINT32 daddr, void* buf, UINT32 len);
+int
+hfa384x_drvr_getconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len);
+int
+hfa384x_drvr_handover( hfa384x_t *hw, UINT8 *addr);
+int
+hfa384x_drvr_hostscanresults( hfa384x_t *hw);
+int
+hfa384x_drvr_low_level(hfa384x_t *hw, hfa384x_metacmd_t *cmd);
+int
+hfa384x_drvr_mmi_read(hfa384x_t *hw, UINT32 address, UINT32 *result);
+int
+hfa384x_drvr_mmi_write(hfa384x_t *hw, UINT32 address, UINT32 data);
+int
+hfa384x_drvr_ramdl_enable(hfa384x_t *hw, UINT32 exeaddr);
+int
+hfa384x_drvr_ramdl_disable(hfa384x_t *hw);
+int
+hfa384x_drvr_ramdl_write(hfa384x_t *hw, UINT32 daddr, void* buf, UINT32 len);
+int
+hfa384x_drvr_readpda(hfa384x_t *hw, void *buf, UINT len);
+int
+hfa384x_drvr_scanresults( hfa384x_t *hw);
+
+int
+hfa384x_drvr_setconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len);
+
+static inline int
+hfa384x_drvr_getconfig16(hfa384x_t *hw, UINT16 rid, void *val)
+{
+ int result = 0;
+ result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT16));
+ if ( result == 0 ) {
+ *((UINT16*)val) = hfa384x2host_16(*((UINT16*)val));
+ }
+ return result;
+}
+
+static inline int
+hfa384x_drvr_getconfig32(hfa384x_t *hw, UINT16 rid, void *val)
+{
+ int result = 0;
+
+ result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT32));
+ if ( result == 0 ) {
+ *((UINT32*)val) = hfa384x2host_32(*((UINT32*)val));
+ }
+
+ return result;
+}
+
+static inline int
+hfa384x_drvr_setconfig16(hfa384x_t *hw, UINT16 rid, UINT16 val)
+{
+ UINT16 value = host2hfa384x_16(val);
+ return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(value));
+}
+
+static inline int
+hfa384x_drvr_setconfig32(hfa384x_t *hw, UINT16 rid, UINT32 val)
+{
+ UINT32 value = host2hfa384x_32(val);
+ return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(value));
+}
+
+#if (WLAN_HOSTIF == WLAN_USB)
+int
+hfa384x_drvr_getconfig_async(hfa384x_t *hw,
+ UINT16 rid,
+ ctlx_usercb_t usercb,
+ void *usercb_data);
+
+int
+hfa384x_drvr_setconfig_async(hfa384x_t *hw,
+ UINT16 rid,
+ void *buf,
+ UINT16 len,
+ ctlx_usercb_t usercb,
+ void *usercb_data);
+#else
+static inline int
+hfa384x_drvr_setconfig_async(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len,
+ void *ptr1, void *ptr2)
+{
+ (void)ptr1;
+ (void)ptr2;
+ return hfa384x_drvr_setconfig(hw, rid, buf, len);
+}
+#endif
+
+static inline int
+hfa384x_drvr_setconfig16_async(hfa384x_t *hw, UINT16 rid, UINT16 val)
+{
+ UINT16 value = host2hfa384x_16(val);
+ return hfa384x_drvr_setconfig_async(hw, rid, &value, sizeof(value),
+ NULL , NULL);
+}
+
+static inline int
+hfa384x_drvr_setconfig32_async(hfa384x_t *hw, UINT16 rid, UINT32 val)
+{
+ UINT32 value = host2hfa384x_32(val);
+ return hfa384x_drvr_setconfig_async(hw, rid, &value, sizeof(value),
+ NULL , NULL);
+}
+
+
+int
+hfa384x_drvr_start(hfa384x_t *hw);
+int
+hfa384x_drvr_stop(hfa384x_t *hw);
+int
+hfa384x_drvr_txframe(hfa384x_t *hw, struct sk_buff *skb, p80211_hdr_t *p80211_hdr, p80211_metawep_t *p80211_wep);
+void
+hfa384x_tx_timeout(wlandevice_t *wlandev);
+
+int
+hfa384x_cmd_initialize(hfa384x_t *hw);
+int
+hfa384x_cmd_enable(hfa384x_t *hw, UINT16 macport);
+int
+hfa384x_cmd_disable(hfa384x_t *hw, UINT16 macport);
+int
+hfa384x_cmd_diagnose(hfa384x_t *hw);
+int
+hfa384x_cmd_allocate(hfa384x_t *hw, UINT16 len);
+int
+hfa384x_cmd_transmit(hfa384x_t *hw, UINT16 reclaim, UINT16 qos, UINT16 fid);
+int
+hfa384x_cmd_clearpersist(hfa384x_t *hw, UINT16 fid);
+int
+hfa384x_cmd_notify(hfa384x_t *hw, UINT16 reclaim, UINT16 fid, void *buf, UINT16 len);
+int
+hfa384x_cmd_inquire(hfa384x_t *hw, UINT16 fid);
+int
+hfa384x_cmd_access(hfa384x_t *hw, UINT16 write, UINT16 rid, void *buf, UINT16 len);
+int
+hfa384x_cmd_monitor(hfa384x_t *hw, UINT16 enable);
+int
+hfa384x_cmd_download(
+ hfa384x_t *hw,
+ UINT16 mode,
+ UINT16 lowaddr,
+ UINT16 highaddr,
+ UINT16 codelen);
+int
+hfa384x_cmd_aux_enable(hfa384x_t *hw, int force);
+int
+hfa384x_cmd_aux_disable(hfa384x_t *hw);
+int
+hfa384x_copy_from_bap(
+ hfa384x_t *hw,
+ UINT16 bap,
+ UINT16 id,
+ UINT16 offset,
+ void *buf,
+ UINT len);
+int
+hfa384x_copy_to_bap(
+ hfa384x_t *hw,
+ UINT16 bap,
+ UINT16 id,
+ UINT16 offset,
+ void *buf,
+ UINT len);
+void
+hfa384x_copy_from_aux(
+ hfa384x_t *hw,
+ UINT32 cardaddr,
+ UINT32 auxctl,
+ void *buf,
+ UINT len);
+void
+hfa384x_copy_to_aux(
+ hfa384x_t *hw,
+ UINT32 cardaddr,
+ UINT32 auxctl,
+ void *buf,
+ UINT len);
+
+#if (WLAN_HOSTIF != WLAN_USB)
+
+/*
+ HFA384x is a LITTLE ENDIAN part.
+
+ the get/setreg functions implicitly byte-swap the data to LE.
+ the _noswap variants do not perform a byte-swap on the data.
+*/
+
+static inline UINT16
+__hfa384x_getreg(hfa384x_t *hw, UINT reg);
+
+static inline void
+__hfa384x_setreg(hfa384x_t *hw, UINT16 val, UINT reg);
+
+static inline UINT16
+__hfa384x_getreg_noswap(hfa384x_t *hw, UINT reg);
+
+static inline void
+__hfa384x_setreg_noswap(hfa384x_t *hw, UINT16 val, UINT reg);
+
+#ifdef REVERSE_ENDIAN
+#define hfa384x_getreg __hfa384x_getreg_noswap
+#define hfa384x_setreg __hfa384x_setreg_noswap
+#define hfa384x_getreg_noswap __hfa384x_getreg
+#define hfa384x_setreg_noswap __hfa384x_setreg
+#else
+#define hfa384x_getreg __hfa384x_getreg
+#define hfa384x_setreg __hfa384x_setreg
+#define hfa384x_getreg_noswap __hfa384x_getreg_noswap
+#define hfa384x_setreg_noswap __hfa384x_setreg_noswap
+#endif
+
+/*----------------------------------------------------------------
+* hfa384x_getreg
+*
+* Retrieve the value of one of the MAC registers. Done here
+* because different PRISM2 MAC parts use different buses and such.
+* NOTE: This function returns the value in HOST ORDER!!!!!!
+*
+* Arguments:
+* hw MAC part structure
+* reg Register identifier (offset for I/O based i/f)
+*
+* Returns:
+* Value from the register in HOST ORDER!!!!
+----------------------------------------------------------------*/
+static inline UINT16
+__hfa384x_getreg(hfa384x_t *hw, UINT reg)
+{
+/* printk(KERN_DEBUG "Reading from 0x%0x\n", hw->membase + reg); */
+#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX))
+ return wlan_inw_le16_to_cpu(hw->iobase+reg);
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ return __le16_to_cpu(readw(hw->membase + reg));
+#endif
+}
+
+/*----------------------------------------------------------------
+* hfa384x_setreg
+*
+* Set the value of one of the MAC registers. Done here
+* because different PRISM2 MAC parts use different buses and such.
+* NOTE: This function assumes the value is in HOST ORDER!!!!!!
+*
+* Arguments:
+* hw MAC part structure
+* val Value, in HOST ORDER!!, to put in the register
+* reg Register identifier (offset for I/O based i/f)
+*
+* Returns:
+* Nothing
+----------------------------------------------------------------*/
+static inline void
+__hfa384x_setreg(hfa384x_t *hw, UINT16 val, UINT reg)
+{
+#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX))
+ wlan_outw_cpu_to_le16( val, hw->iobase + reg);
+ return;
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ writew(__cpu_to_le16(val), hw->membase + reg);
+ return;
+#endif
+}
+
+
+/*----------------------------------------------------------------
+* hfa384x_getreg_noswap
+*
+* Retrieve the value of one of the MAC registers. Done here
+* because different PRISM2 MAC parts use different buses and such.
+*
+* Arguments:
+* hw MAC part structure
+* reg Register identifier (offset for I/O based i/f)
+*
+* Returns:
+* Value from the register.
+----------------------------------------------------------------*/
+static inline UINT16
+__hfa384x_getreg_noswap(hfa384x_t *hw, UINT reg)
+{
+#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX))
+ return wlan_inw(hw->iobase+reg);
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ return readw(hw->membase + reg);
+#endif
+}
+
+
+/*----------------------------------------------------------------
+* hfa384x_setreg_noswap
+*
+* Set the value of one of the MAC registers. Done here
+* because different PRISM2 MAC parts use different buses and such.
+*
+* Arguments:
+* hw MAC part structure
+* val Value to put in the register
+* reg Register identifier (offset for I/O based i/f)
+*
+* Returns:
+* Nothing
+----------------------------------------------------------------*/
+static inline void
+__hfa384x_setreg_noswap(hfa384x_t *hw, UINT16 val, UINT reg)
+{
+#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX))
+ wlan_outw( val, hw->iobase + reg);
+ return;
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ writew(val, hw->membase + reg);
+ return;
+#endif
+}
+
+
+static inline void hfa384x_events_all(hfa384x_t *hw)
+{
+ hfa384x_setreg(hw,
+ HFA384x_INT_NORMAL
+#ifdef CMD_IRQ
+ | HFA384x_INTEN_CMD_SET(1)
+#endif
+ ,
+ HFA384x_INTEN);
+
+}
+
+static inline void hfa384x_events_nobap(hfa384x_t *hw)
+{
+ hfa384x_setreg(hw,
+ (HFA384x_INT_NORMAL & ~HFA384x_INT_BAP_OP)
+#ifdef CMD_IRQ
+ | HFA384x_INTEN_CMD_SET(1)
+#endif
+ ,
+ HFA384x_INTEN);
+
+}
+
+#endif /* WLAN_HOSTIF != WLAN_USB */
+#endif /* __KERNEL__ */
+
+#endif /* _HFA384x_H */
diff --git a/gpxe/src/drivers/net/ipoib.c b/gpxe/src/drivers/net/ipoib.c
new file mode 100644
index 00000000..d457b258
--- /dev/null
+++ b/gpxe/src/drivers/net/ipoib.c
@@ -0,0 +1,929 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/if_arp.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/ipoib.h>
+
+/** @file
+ *
+ * IP over Infiniband
+ */
+
+/** IPoIB MTU */
+#define IPOIB_MTU 2048
+
+/** Number of IPoIB data send work queue entries */
+#define IPOIB_DATA_NUM_SEND_WQES 2
+
+/** Number of IPoIB data receive work queue entries */
+#define IPOIB_DATA_NUM_RECV_WQES 4
+
+/** Number of IPoIB data completion entries */
+#define IPOIB_DATA_NUM_CQES 8
+
+/** Number of IPoIB metadata send work queue entries */
+#define IPOIB_META_NUM_SEND_WQES 2
+
+/** Number of IPoIB metadata receive work queue entries */
+#define IPOIB_META_NUM_RECV_WQES 2
+
+/** Number of IPoIB metadata completion entries */
+#define IPOIB_META_NUM_CQES 8
+
+/** An IPoIB queue set */
+struct ipoib_queue_set {
+ /** Completion queue */
+ struct ib_completion_queue *cq;
+ /** Queue pair */
+ struct ib_queue_pair *qp;
+ /** Receive work queue fill level */
+ unsigned int recv_fill;
+ /** Receive work queue maximum fill level */
+ unsigned int recv_max_fill;
+};
+
+/** An IPoIB device */
+struct ipoib_device {
+ /** Network device */
+ struct net_device *netdev;
+ /** Underlying Infiniband device */
+ struct ib_device *ibdev;
+ /** Data queue set */
+ struct ipoib_queue_set data;
+ /** Data queue set */
+ struct ipoib_queue_set meta;
+ /** Broadcast GID */
+ struct ib_gid broadcast_gid;
+ /** Broadcast LID */
+ unsigned int broadcast_lid;
+ /** Joined to broadcast group */
+ int broadcast_joined;
+ /** Data queue key */
+ unsigned long data_qkey;
+};
+
+/**
+ * IPoIB path cache entry
+ *
+ * This serves a similar role to the ARP cache for Ethernet. (ARP
+ * *is* used on IPoIB; we have two caches to maintain.)
+ */
+struct ipoib_cached_path {
+ /** Destination GID */
+ struct ib_gid gid;
+ /** Destination LID */
+ unsigned int dlid;
+ /** Service level */
+ unsigned int sl;
+ /** Rate */
+ unsigned int rate;
+};
+
+/** Number of IPoIB path cache entries */
+#define IPOIB_NUM_CACHED_PATHS 2
+
+/** IPoIB path cache */
+static struct ipoib_cached_path ipoib_path_cache[IPOIB_NUM_CACHED_PATHS];
+
+/** Oldest IPoIB path cache entry index */
+static unsigned int ipoib_path_cache_idx = 0;
+
+/** TID half used to identify get path record replies */
+#define IPOIB_TID_GET_PATH_REC 0x11111111UL
+
+/** TID half used to identify multicast member record replies */
+#define IPOIB_TID_MC_MEMBER_REC 0x22222222UL
+
+/** IPoIB metadata TID */
+static uint32_t ipoib_meta_tid = 0;
+
+/** IPv4 broadcast GID */
+static const struct ib_gid ipv4_broadcast_gid = {
+ { { 0xff, 0x12, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff } }
+};
+
+/** Maximum time we will wait for the broadcast join to succeed */
+#define IPOIB_JOIN_MAX_DELAY_MS 1000
+
+/****************************************************************************
+ *
+ * IPoIB link layer
+ *
+ ****************************************************************************
+ */
+
+/** Broadcast QPN used in IPoIB MAC addresses
+ *
+ * This is a guaranteed invalid real QPN
+ */
+#define IPOIB_BROADCAST_QPN 0xffffffffUL
+
+/** Broadcast IPoIB address */
+static struct ipoib_mac ipoib_broadcast = {
+ .qpn = ntohl ( IPOIB_BROADCAST_QPN ),
+};
+
+/**
+ * Transmit IPoIB packet
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v net_protocol Network-layer protocol
+ * @v ll_dest Link-layer destination address
+ *
+ * Prepends the IPoIB link-layer header and transmits the packet.
+ */
+static int ipoib_tx ( struct io_buffer *iobuf, struct net_device *netdev,
+ struct net_protocol *net_protocol,
+ const void *ll_dest ) {
+ struct ipoib_hdr *ipoib_hdr =
+ iob_push ( iobuf, sizeof ( *ipoib_hdr ) );
+
+ /* Build IPoIB header */
+ memcpy ( &ipoib_hdr->pseudo.peer, ll_dest,
+ sizeof ( ipoib_hdr->pseudo.peer ) );
+ ipoib_hdr->real.proto = net_protocol->net_proto;
+ ipoib_hdr->real.reserved = 0;
+
+ /* Hand off to network device */
+ return netdev_tx ( netdev, iobuf );
+}
+
+/**
+ * Process received IPoIB packet
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ *
+ * Strips off the IPoIB link-layer header and passes up to the
+ * network-layer protocol.
+ */
+static int ipoib_rx ( struct io_buffer *iobuf, struct net_device *netdev ) {
+ struct ipoib_hdr *ipoib_hdr = iobuf->data;
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *ipoib_hdr ) ) {
+ DBG ( "IPoIB packet too short for link-layer header\n" );
+ DBG_HD ( iobuf->data, iob_len ( iobuf ) );
+ free_iob ( iobuf );
+ return -EINVAL;
+ }
+
+ /* Strip off IPoIB header */
+ iob_pull ( iobuf, sizeof ( *ipoib_hdr ) );
+
+ /* Hand off to network-layer protocol */
+ return net_rx ( iobuf, netdev, ipoib_hdr->real.proto,
+ &ipoib_hdr->pseudo.peer );
+}
+
+/**
+ * Transcribe IPoIB address
+ *
+ * @v ll_addr Link-layer address
+ * @ret string Link-layer address in human-readable format
+ */
+const char * ipoib_ntoa ( const void *ll_addr ) {
+ static char buf[45];
+ const struct ipoib_mac *mac = ll_addr;
+
+ snprintf ( buf, sizeof ( buf ), "%08lx:%08lx:%08lx:%08lx:%08lx",
+ htonl ( mac->qpn ), htonl ( mac->gid.u.dwords[0] ),
+ htonl ( mac->gid.u.dwords[1] ),
+ htonl ( mac->gid.u.dwords[2] ),
+ htonl ( mac->gid.u.dwords[3] ) );
+ return buf;
+}
+
+/** IPoIB protocol */
+struct ll_protocol ipoib_protocol __ll_protocol = {
+ .name = "IPoIB",
+ .ll_proto = htons ( ARPHRD_INFINIBAND ),
+ .ll_addr_len = IPOIB_ALEN,
+ .ll_header_len = IPOIB_HLEN,
+ .ll_broadcast = ( uint8_t * ) &ipoib_broadcast,
+ .tx = ipoib_tx,
+ .rx = ipoib_rx,
+ .ntoa = ipoib_ntoa,
+};
+
+/****************************************************************************
+ *
+ * IPoIB network device
+ *
+ ****************************************************************************
+ */
+
+/**
+ * Destroy queue set
+ *
+ * @v ipoib IPoIB device
+ * @v qset Queue set
+ */
+static void ipoib_destroy_qset ( struct ipoib_device *ipoib,
+ struct ipoib_queue_set *qset ) {
+ struct ib_device *ibdev = ipoib->ibdev;
+
+ if ( qset->qp )
+ ib_destroy_qp ( ibdev, qset->qp );
+ if ( qset->cq )
+ ib_destroy_cq ( ibdev, qset->cq );
+ memset ( qset, 0, sizeof ( *qset ) );
+}
+
+/**
+ * Create queue set
+ *
+ * @v ipoib IPoIB device
+ * @v qset Queue set
+ * @ret rc Return status code
+ */
+static int ipoib_create_qset ( struct ipoib_device *ipoib,
+ struct ipoib_queue_set *qset,
+ unsigned int num_cqes,
+ unsigned int num_send_wqes,
+ unsigned int num_recv_wqes,
+ unsigned long qkey ) {
+ struct ib_device *ibdev = ipoib->ibdev;
+ int rc;
+
+ /* Store queue parameters */
+ qset->recv_max_fill = num_recv_wqes;
+
+ /* Allocate completion queue */
+ qset->cq = ib_create_cq ( ibdev, num_cqes );
+ if ( ! qset->cq ) {
+ DBGC ( ipoib, "IPoIB %p could not allocate completion queue\n",
+ ipoib );
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ /* Allocate queue pair */
+ qset->qp = ib_create_qp ( ibdev, num_send_wqes, qset->cq,
+ num_recv_wqes, qset->cq, qkey );
+ if ( ! qset->qp ) {
+ DBGC ( ipoib, "IPoIB %p could not allocate queue pair\n",
+ ipoib );
+ rc = -ENOMEM;
+ goto err;
+ }
+ ib_qp_set_ownerdata ( qset->qp, ipoib->netdev );
+
+ return 0;
+
+ err:
+ ipoib_destroy_qset ( ipoib, qset );
+ return rc;
+}
+
+/**
+ * Find path cache entry by GID
+ *
+ * @v gid GID
+ * @ret entry Path cache entry, or NULL
+ */
+static struct ipoib_cached_path *
+ipoib_find_cached_path ( struct ib_gid *gid ) {
+ struct ipoib_cached_path *path;
+ unsigned int i;
+
+ for ( i = 0 ; i < IPOIB_NUM_CACHED_PATHS ; i++ ) {
+ path = &ipoib_path_cache[i];
+ if ( memcmp ( &path->gid, gid, sizeof ( *gid ) ) == 0 )
+ return path;
+ }
+ DBG ( "IPoIB %08lx:%08lx:%08lx:%08lx cache miss\n",
+ htonl ( gid->u.dwords[0] ), htonl ( gid->u.dwords[1] ),
+ htonl ( gid->u.dwords[2] ), htonl ( gid->u.dwords[3] ) );
+ return NULL;
+}
+
+/**
+ * Transmit path record request
+ *
+ * @v ipoib IPoIB device
+ * @v gid Destination GID
+ * @ret rc Return status code
+ */
+static int ipoib_get_path_record ( struct ipoib_device *ipoib,
+ struct ib_gid *gid ) {
+ struct ib_device *ibdev = ipoib->ibdev;
+ struct io_buffer *iobuf;
+ struct ib_mad_path_record *path_record;
+ struct ib_address_vector av;
+ int rc;
+
+ /* Allocate I/O buffer */
+ iobuf = alloc_iob ( sizeof ( *path_record ) );
+ if ( ! iobuf )
+ return -ENOMEM;
+ iob_put ( iobuf, sizeof ( *path_record ) );
+ path_record = iobuf->data;
+ memset ( path_record, 0, sizeof ( *path_record ) );
+
+ /* Construct path record request */
+ path_record->mad_hdr.base_version = IB_MGMT_BASE_VERSION;
+ path_record->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
+ path_record->mad_hdr.class_version = 2;
+ path_record->mad_hdr.method = IB_MGMT_METHOD_GET;
+ path_record->mad_hdr.attr_id = htons ( IB_SA_ATTR_PATH_REC );
+ path_record->mad_hdr.tid[0] = IPOIB_TID_GET_PATH_REC;
+ path_record->mad_hdr.tid[1] = ipoib_meta_tid++;
+ path_record->sa_hdr.comp_mask[1] =
+ htonl ( IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID );
+ memcpy ( &path_record->dgid, gid, sizeof ( path_record->dgid ) );
+ memcpy ( &path_record->sgid, &ibdev->port_gid,
+ sizeof ( path_record->sgid ) );
+
+ /* Construct address vector */
+ memset ( &av, 0, sizeof ( av ) );
+ av.dlid = ibdev->sm_lid;
+ av.dest_qp = IB_SA_QPN;
+ av.qkey = IB_GLOBAL_QKEY;
+
+ /* Post send request */
+ if ( ( rc = ib_post_send ( ibdev, ipoib->meta.qp, &av,
+ iobuf ) ) != 0 ) {
+ DBGC ( ipoib, "IPoIB %p could not send get path record: %s\n",
+ ipoib, strerror ( rc ) );
+ free_iob ( iobuf );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Transmit multicast group membership request
+ *
+ * @v ipoib IPoIB device
+ * @v gid Multicast GID
+ * @v join Join (rather than leave) group
+ * @ret rc Return status code
+ */
+static int ipoib_mc_member_record ( struct ipoib_device *ipoib,
+ struct ib_gid *gid, int join ) {
+ struct ib_device *ibdev = ipoib->ibdev;
+ struct io_buffer *iobuf;
+ struct ib_mad_mc_member_record *mc_member_record;
+ struct ib_address_vector av;
+ int rc;
+
+ /* Allocate I/O buffer */
+ iobuf = alloc_iob ( sizeof ( *mc_member_record ) );
+ if ( ! iobuf )
+ return -ENOMEM;
+ iob_put ( iobuf, sizeof ( *mc_member_record ) );
+ mc_member_record = iobuf->data;
+ memset ( mc_member_record, 0, sizeof ( *mc_member_record ) );
+
+ /* Construct path record request */
+ mc_member_record->mad_hdr.base_version = IB_MGMT_BASE_VERSION;
+ mc_member_record->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
+ mc_member_record->mad_hdr.class_version = 2;
+ mc_member_record->mad_hdr.method =
+ ( join ? IB_MGMT_METHOD_SET : IB_MGMT_METHOD_DELETE );
+ mc_member_record->mad_hdr.attr_id = htons ( IB_SA_ATTR_MC_MEMBER_REC );
+ mc_member_record->mad_hdr.tid[0] = IPOIB_TID_MC_MEMBER_REC;
+ mc_member_record->mad_hdr.tid[1] = ipoib_meta_tid++;
+ mc_member_record->sa_hdr.comp_mask[1] =
+ htonl ( IB_SA_MCMEMBER_REC_MGID | IB_SA_MCMEMBER_REC_PORT_GID |
+ IB_SA_MCMEMBER_REC_JOIN_STATE );
+ mc_member_record->scope__join_state = 1;
+ memcpy ( &mc_member_record->mgid, gid,
+ sizeof ( mc_member_record->mgid ) );
+ memcpy ( &mc_member_record->port_gid, &ibdev->port_gid,
+ sizeof ( mc_member_record->port_gid ) );
+
+ /* Construct address vector */
+ memset ( &av, 0, sizeof ( av ) );
+ av.dlid = ibdev->sm_lid;
+ av.dest_qp = IB_SA_QPN;
+ av.qkey = IB_GLOBAL_QKEY;
+
+ /* Post send request */
+ if ( ( rc = ib_post_send ( ibdev, ipoib->meta.qp, &av,
+ iobuf ) ) != 0 ) {
+ DBGC ( ipoib, "IPoIB %p could not send get path record: %s\n",
+ ipoib, strerror ( rc ) );
+ free_iob ( iobuf );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Transmit packet via IPoIB network device
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int ipoib_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf ) {
+ struct ipoib_device *ipoib = netdev->priv;
+ struct ib_device *ibdev = ipoib->ibdev;
+ struct ipoib_pseudo_hdr *ipoib_pshdr = iobuf->data;
+ struct ib_address_vector av;
+ struct ib_gid *gid;
+ struct ipoib_cached_path *path;
+ int rc;
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *ipoib_pshdr ) ) {
+ DBGC ( ipoib, "IPoIB %p buffer too short\n", ipoib );
+ return -EINVAL;
+ }
+ iob_pull ( iobuf, ( sizeof ( *ipoib_pshdr ) ) );
+
+ /* Construct address vector */
+ memset ( &av, 0, sizeof ( av ) );
+ av.qkey = IB_GLOBAL_QKEY;
+ av.gid_present = 1;
+ if ( ipoib_pshdr->peer.qpn == htonl ( IPOIB_BROADCAST_QPN ) ) {
+ /* Broadcast address */
+ av.dest_qp = IB_BROADCAST_QPN;
+ av.dlid = ipoib->broadcast_lid;
+ gid = &ipoib->broadcast_gid;
+ } else {
+ /* Unicast - look in path cache */
+ path = ipoib_find_cached_path ( &ipoib_pshdr->peer.gid );
+ if ( ! path ) {
+ /* No path entry - get path record */
+ rc = ipoib_get_path_record ( ipoib,
+ &ipoib_pshdr->peer.gid );
+ netdev_tx_complete ( netdev, iobuf );
+ return rc;
+ }
+ av.dest_qp = ntohl ( ipoib_pshdr->peer.qpn );
+ av.dlid = path->dlid;
+ av.rate = path->rate;
+ av.sl = path->sl;
+ gid = &ipoib_pshdr->peer.gid;
+ }
+ memcpy ( &av.gid, gid, sizeof ( av.gid ) );
+
+ return ib_post_send ( ibdev, ipoib->data.qp, &av, iobuf );
+}
+
+/**
+ * Handle IPoIB data send completion
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v completion Completion
+ * @v iobuf I/O buffer
+ */
+static void ipoib_data_complete_send ( struct ib_device *ibdev __unused,
+ struct ib_queue_pair *qp,
+ struct ib_completion *completion,
+ struct io_buffer *iobuf ) {
+ struct net_device *netdev = ib_qp_get_ownerdata ( qp );
+
+ netdev_tx_complete_err ( netdev, iobuf,
+ ( completion->syndrome ? -EIO : 0 ) );
+}
+
+/**
+ * Handle IPoIB data receive completion
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v completion Completion
+ * @v iobuf I/O buffer
+ */
+static void ipoib_data_complete_recv ( struct ib_device *ibdev __unused,
+ struct ib_queue_pair *qp,
+ struct ib_completion *completion,
+ struct io_buffer *iobuf ) {
+ struct net_device *netdev = ib_qp_get_ownerdata ( qp );
+ struct ipoib_device *ipoib = netdev->priv;
+ struct ipoib_pseudo_hdr *ipoib_pshdr;
+
+ if ( completion->syndrome ) {
+ netdev_rx_err ( netdev, iobuf, -EIO );
+ goto done;
+ }
+
+ iob_put ( iobuf, completion->len );
+ if ( iob_len ( iobuf ) < sizeof ( struct ib_global_route_header ) ) {
+ DBGC ( ipoib, "IPoIB %p received data packet too short to "
+ "contain GRH\n", ipoib );
+ DBGC_HD ( ipoib, iobuf->data, iob_len ( iobuf ) );
+ netdev_rx_err ( netdev, iobuf, -EIO );
+ goto done;
+ }
+ iob_pull ( iobuf, sizeof ( struct ib_global_route_header ) );
+
+ if ( iob_len ( iobuf ) < sizeof ( struct ipoib_real_hdr ) ) {
+ DBGC ( ipoib, "IPoIB %p received data packet too short to "
+ "contain IPoIB header\n", ipoib );
+ DBGC_HD ( ipoib, iobuf->data, iob_len ( iobuf ) );
+ netdev_rx_err ( netdev, iobuf, -EIO );
+ goto done;
+ }
+
+ ipoib_pshdr = iob_push ( iobuf, sizeof ( *ipoib_pshdr ) );
+ /* FIXME: fill in a MAC address for the sake of AoE! */
+
+ netdev_rx ( netdev, iobuf );
+
+ done:
+ ipoib->data.recv_fill--;
+}
+
+/**
+ * Handle IPoIB metadata send completion
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v completion Completion
+ * @v iobuf I/O buffer
+ */
+static void ipoib_meta_complete_send ( struct ib_device *ibdev __unused,
+ struct ib_queue_pair *qp,
+ struct ib_completion *completion,
+ struct io_buffer *iobuf ) {
+ struct net_device *netdev = ib_qp_get_ownerdata ( qp );
+ struct ipoib_device *ipoib = netdev->priv;
+
+ if ( completion->syndrome ) {
+ DBGC ( ipoib, "IPoIB %p metadata TX completion error %x\n",
+ ipoib, completion->syndrome );
+ }
+ free_iob ( iobuf );
+}
+
+/**
+ * Handle received IPoIB path record
+ *
+ * @v ipoib IPoIB device
+ * @v path_record Path record
+ */
+static void ipoib_recv_path_record ( struct ipoib_device *ipoib __unused,
+ struct ib_mad_path_record *path_record ) {
+ struct ipoib_cached_path *path;
+
+ /* Update path cache entry */
+ path = &ipoib_path_cache[ipoib_path_cache_idx];
+ memcpy ( &path->gid, &path_record->dgid, sizeof ( path->gid ) );
+ path->dlid = ntohs ( path_record->dlid );
+ path->sl = ( path_record->reserved__sl & 0x0f );
+ path->rate = ( path_record->rate_selector__rate & 0x3f );
+
+ DBG ( "IPoIB %08lx:%08lx:%08lx:%08lx dlid %x sl %x rate %x\n",
+ htonl ( path->gid.u.dwords[0] ), htonl ( path->gid.u.dwords[1] ),
+ htonl ( path->gid.u.dwords[2] ), htonl ( path->gid.u.dwords[3] ),
+ path->dlid, path->sl, path->rate );
+
+ /* Update path cache index */
+ ipoib_path_cache_idx++;
+ if ( ipoib_path_cache_idx == IPOIB_NUM_CACHED_PATHS )
+ ipoib_path_cache_idx = 0;
+}
+
+/**
+ * Handle received IPoIB multicast membership record
+ *
+ * @v ipoib IPoIB device
+ * @v mc_member_record Multicast membership record
+ */
+static void ipoib_recv_mc_member_record ( struct ipoib_device *ipoib,
+ struct ib_mad_mc_member_record *mc_member_record ) {
+ /* Record parameters */
+ ipoib->broadcast_joined =
+ ( mc_member_record->scope__join_state & 0x0f );
+ ipoib->data_qkey = ntohl ( mc_member_record->qkey );
+ ipoib->broadcast_lid = ntohs ( mc_member_record->mlid );
+ DBGC ( ipoib, "IPoIB %p %s broadcast group: qkey %lx mlid %x\n",
+ ipoib, ( ipoib->broadcast_joined ? "joined" : "left" ),
+ ipoib->data_qkey, ipoib->broadcast_lid );
+}
+
+/**
+ * Handle IPoIB metadata receive completion
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v completion Completion
+ * @v iobuf I/O buffer
+ */
+static void ipoib_meta_complete_recv ( struct ib_device *ibdev __unused,
+ struct ib_queue_pair *qp,
+ struct ib_completion *completion,
+ struct io_buffer *iobuf ) {
+ struct net_device *netdev = ib_qp_get_ownerdata ( qp );
+ struct ipoib_device *ipoib = netdev->priv;
+ union ib_mad *mad;
+
+ if ( completion->syndrome ) {
+ DBGC ( ipoib, "IPoIB %p metadata RX completion error %x\n",
+ ipoib, completion->syndrome );
+ goto done;
+ }
+
+ iob_put ( iobuf, completion->len );
+ if ( iob_len ( iobuf ) < sizeof ( struct ib_global_route_header ) ) {
+ DBGC ( ipoib, "IPoIB %p received metadata packet too short "
+ "to contain GRH\n", ipoib );
+ DBGC_HD ( ipoib, iobuf->data, iob_len ( iobuf ) );
+ goto done;
+ }
+ iob_pull ( iobuf, sizeof ( struct ib_global_route_header ) );
+ if ( iob_len ( iobuf ) < sizeof ( *mad ) ) {
+ DBGC ( ipoib, "IPoIB %p received metadata packet too short "
+ "to contain reply\n", ipoib );
+ DBGC_HD ( ipoib, iobuf->data, iob_len ( iobuf ) );
+ goto done;
+ }
+ mad = iobuf->data;
+
+ if ( mad->mad_hdr.status != 0 ) {
+ DBGC ( ipoib, "IPoIB %p metadata RX err status %04x\n",
+ ipoib, ntohs ( mad->mad_hdr.status ) );
+ goto done;
+ }
+
+ switch ( mad->mad_hdr.tid[0] ) {
+ case IPOIB_TID_GET_PATH_REC:
+ ipoib_recv_path_record ( ipoib, &mad->path_record );
+ break;
+ case IPOIB_TID_MC_MEMBER_REC:
+ ipoib_recv_mc_member_record ( ipoib, &mad->mc_member_record );
+ break;
+ default:
+ DBGC ( ipoib, "IPoIB %p unwanted response:\n",
+ ipoib );
+ DBGC_HD ( ipoib, mad, sizeof ( *mad ) );
+ break;
+ }
+
+ done:
+ ipoib->meta.recv_fill--;
+ free_iob ( iobuf );
+}
+
+/**
+ * Refill IPoIB receive ring
+ *
+ * @v ipoib IPoIB device
+ */
+static void ipoib_refill_recv ( struct ipoib_device *ipoib,
+ struct ipoib_queue_set *qset ) {
+ struct ib_device *ibdev = ipoib->ibdev;
+ struct io_buffer *iobuf;
+ int rc;
+
+ while ( qset->recv_fill < qset->recv_max_fill ) {
+ iobuf = alloc_iob ( IPOIB_MTU );
+ if ( ! iobuf )
+ break;
+ if ( ( rc = ib_post_recv ( ibdev, qset->qp, iobuf ) ) != 0 ) {
+ free_iob ( iobuf );
+ break;
+ }
+ qset->recv_fill++;
+ }
+}
+
+/**
+ * Poll IPoIB network device
+ *
+ * @v netdev Network device
+ */
+static void ipoib_poll ( struct net_device *netdev ) {
+ struct ipoib_device *ipoib = netdev->priv;
+ struct ib_device *ibdev = ipoib->ibdev;
+
+ ib_poll_cq ( ibdev, ipoib->meta.cq, ipoib_meta_complete_send,
+ ipoib_meta_complete_recv );
+ ib_poll_cq ( ibdev, ipoib->data.cq, ipoib_data_complete_send,
+ ipoib_data_complete_recv );
+ ipoib_refill_recv ( ipoib, &ipoib->meta );
+ ipoib_refill_recv ( ipoib, &ipoib->data );
+}
+
+/**
+ * Enable/disable interrupts on IPoIB network device
+ *
+ * @v netdev Network device
+ * @v enable Interrupts should be enabled
+ */
+static void ipoib_irq ( struct net_device *netdev __unused,
+ int enable __unused ) {
+ /* No implementation */
+}
+
+/**
+ * Open IPoIB network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int ipoib_open ( struct net_device *netdev ) {
+ struct ipoib_device *ipoib = netdev->priv;
+ struct ib_device *ibdev = ipoib->ibdev;
+ int rc;
+
+ /* Attach to broadcast multicast GID */
+ if ( ( rc = ib_mcast_attach ( ibdev, ipoib->data.qp,
+ &ipoib->broadcast_gid ) ) != 0 ) {
+ DBG ( "Could not attach to broadcast GID: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Fill receive rings */
+ ipoib_refill_recv ( ipoib, &ipoib->meta );
+ ipoib_refill_recv ( ipoib, &ipoib->data );
+
+ return 0;
+}
+
+/**
+ * Close IPoIB network device
+ *
+ * @v netdev Network device
+ */
+static void ipoib_close ( struct net_device *netdev ) {
+ struct ipoib_device *ipoib = netdev->priv;
+ struct ib_device *ibdev = ipoib->ibdev;
+
+ /* Detach from broadcast multicast GID */
+ ib_mcast_detach ( ibdev, ipoib->data.qp, &ipoib->broadcast_gid );
+
+ /* FIXME: should probably flush the receive ring */
+}
+
+/** IPoIB network device operations */
+static struct net_device_operations ipoib_operations = {
+ .open = ipoib_open,
+ .close = ipoib_close,
+ .transmit = ipoib_transmit,
+ .poll = ipoib_poll,
+ .irq = ipoib_irq,
+};
+
+/**
+ * Join IPoIB broadcast group
+ *
+ * @v ipoib IPoIB device
+ * @ret rc Return status code
+ */
+static int ipoib_join_broadcast_group ( struct ipoib_device *ipoib ) {
+ struct ib_device *ibdev = ipoib->ibdev;
+ unsigned int delay_ms;
+ int rc;
+
+ /* Make sure we have some receive descriptors */
+ ipoib_refill_recv ( ipoib, &ipoib->meta );
+
+ /* Send join request */
+ if ( ( rc = ipoib_mc_member_record ( ipoib, &ipoib->broadcast_gid,
+ 1 ) ) != 0 ) {
+ DBGC ( ipoib, "IPoIB %p could not send broadcast join: %s\n",
+ ipoib, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Wait for join to complete. Ideally we wouldn't delay for
+ * this long, but we need the queue key before we can set up
+ * the data queue pair, which we need before we can know the
+ * MAC address.
+ */
+ for ( delay_ms = IPOIB_JOIN_MAX_DELAY_MS ; delay_ms ; delay_ms-- ) {
+ mdelay ( 1 );
+ ib_poll_cq ( ibdev, ipoib->meta.cq, ipoib_meta_complete_send,
+ ipoib_meta_complete_recv );
+ ipoib_refill_recv ( ipoib, &ipoib->meta );
+ if ( ipoib->broadcast_joined )
+ return 0;
+ }
+ DBGC ( ipoib, "IPoIB %p timed out waiting for broadcast join\n",
+ ipoib );
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * Probe IPoIB device
+ *
+ * @v ibdev Infiniband device
+ * @ret rc Return status code
+ */
+int ipoib_probe ( struct ib_device *ibdev ) {
+ struct net_device *netdev;
+ struct ipoib_device *ipoib;
+ struct ipoib_mac *mac;
+ int rc;
+
+ /* Allocate network device */
+ netdev = alloc_ipoibdev ( sizeof ( *ipoib ) );
+ if ( ! netdev )
+ return -ENOMEM;
+ netdev_init ( netdev, &ipoib_operations );
+ ipoib = netdev->priv;
+ ib_set_ownerdata ( ibdev, netdev );
+ netdev->dev = ibdev->dev;
+ memset ( ipoib, 0, sizeof ( *ipoib ) );
+ ipoib->netdev = netdev;
+ ipoib->ibdev = ibdev;
+
+ /* Calculate broadcast GID */
+ memcpy ( &ipoib->broadcast_gid, &ipv4_broadcast_gid,
+ sizeof ( ipoib->broadcast_gid ) );
+ ipoib->broadcast_gid.u.words[2] = htons ( ibdev->pkey );
+
+ /* Allocate metadata queue set */
+ if ( ( rc = ipoib_create_qset ( ipoib, &ipoib->meta,
+ IPOIB_META_NUM_CQES,
+ IPOIB_META_NUM_SEND_WQES,
+ IPOIB_META_NUM_RECV_WQES,
+ IB_GLOBAL_QKEY ) ) != 0 ) {
+ DBGC ( ipoib, "IPoIB %p could not allocate metadata QP: %s\n",
+ ipoib, strerror ( rc ) );
+ goto err_create_meta_qset;
+ }
+
+ /* Join broadcast group */
+ if ( ( rc = ipoib_join_broadcast_group ( ipoib ) ) != 0 ) {
+ DBGC ( ipoib, "IPoIB %p could not join broadcast group: %s\n",
+ ipoib, strerror ( rc ) );
+ goto err_join_broadcast_group;
+ }
+
+ /* Allocate data queue set */
+ if ( ( rc = ipoib_create_qset ( ipoib, &ipoib->data,
+ IPOIB_DATA_NUM_CQES,
+ IPOIB_DATA_NUM_SEND_WQES,
+ IPOIB_DATA_NUM_RECV_WQES,
+ ipoib->data_qkey ) ) != 0 ) {
+ DBGC ( ipoib, "IPoIB %p could not allocate data QP: %s\n",
+ ipoib, strerror ( rc ) );
+ goto err_create_data_qset;
+ }
+
+ /* Construct MAC address */
+ mac = ( ( struct ipoib_mac * ) netdev->ll_addr );
+ mac->qpn = htonl ( ipoib->data.qp->qpn );
+ memcpy ( &mac->gid, &ibdev->port_gid, sizeof ( mac->gid ) );
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register_netdev;
+
+ return 0;
+
+ err_register_netdev:
+ ipoib_destroy_qset ( ipoib, &ipoib->data );
+ err_join_broadcast_group:
+ err_create_data_qset:
+ ipoib_destroy_qset ( ipoib, &ipoib->meta );
+ err_create_meta_qset:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ return rc;
+}
+
+/**
+ * Remove IPoIB device
+ *
+ * @v ibdev Infiniband device
+ */
+void ipoib_remove ( struct ib_device *ibdev ) {
+ struct net_device *netdev = ib_get_ownerdata ( ibdev );
+ struct ipoib_device *ipoib = netdev->priv;
+
+ unregister_netdev ( netdev );
+ ipoib_destroy_qset ( ipoib, &ipoib->data );
+ ipoib_destroy_qset ( ipoib, &ipoib->meta );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
diff --git a/gpxe/src/drivers/net/legacy.c b/gpxe/src/drivers/net/legacy.c
new file mode 100644
index 00000000..32460adb
--- /dev/null
+++ b/gpxe/src/drivers/net/legacy.c
@@ -0,0 +1,152 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/iobuf.h>
+#include <nic.h>
+
+/*
+ * Quick and dirty compatibility layer
+ *
+ * This should allow old-API PCI drivers to at least function until
+ * they are updated. It will not help non-PCI drivers.
+ *
+ * No drivers should rely on this code. It will be removed asap.
+ *
+ */
+
+struct nic nic;
+
+static int legacy_registered = 0;
+
+static int legacy_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
+ struct nic *nic = netdev->priv;
+ struct ethhdr *ethhdr;
+
+ DBG ( "Transmitting %zd bytes\n", iob_len ( iobuf ) );
+ iob_pad ( iobuf, ETH_ZLEN );
+ ethhdr = iobuf->data;
+ iob_pull ( iobuf, sizeof ( *ethhdr ) );
+ nic->nic_op->transmit ( nic, ( const char * ) ethhdr->h_dest,
+ ntohs ( ethhdr->h_protocol ),
+ iob_len ( iobuf ), iobuf->data );
+ netdev_tx_complete ( netdev, iobuf );
+ return 0;
+}
+
+static void legacy_poll ( struct net_device *netdev ) {
+ struct nic *nic = netdev->priv;
+ struct io_buffer *iobuf;
+
+ iobuf = alloc_iob ( ETH_FRAME_LEN );
+ if ( ! iobuf )
+ return;
+
+ nic->packet = iobuf->data;
+ if ( nic->nic_op->poll ( nic, 1 ) ) {
+ DBG ( "Received %d bytes\n", nic->packetlen );
+ iob_put ( iobuf, nic->packetlen );
+ netdev_rx ( netdev, iobuf );
+ } else {
+ free_iob ( iobuf );
+ }
+}
+
+static int legacy_open ( struct net_device *netdev __unused ) {
+ /* Nothing to do */
+ return 0;
+}
+
+static void legacy_close ( struct net_device *netdev __unused ) {
+ /* Nothing to do */
+}
+
+static void legacy_irq ( struct net_device *netdev __unused, int enable ) {
+ struct nic *nic = netdev->priv;
+
+ nic->nic_op->irq ( nic, ( enable ? ENABLE : DISABLE ) );
+}
+
+static struct net_device_operations legacy_operations = {
+ .open = legacy_open,
+ .close = legacy_close,
+ .transmit = legacy_transmit,
+ .poll = legacy_poll,
+ .irq = legacy_irq,
+};
+
+int legacy_probe ( void *hwdev,
+ void ( * set_drvdata ) ( void *hwdev, void *priv ),
+ struct device *dev,
+ int ( * probe ) ( struct nic *nic, void *hwdev ),
+ void ( * disable ) ( struct nic *nic, void *hwdev ) ) {
+ struct net_device *netdev;
+ int rc;
+
+ if ( legacy_registered )
+ return -EBUSY;
+
+ netdev = alloc_etherdev ( 0 );
+ if ( ! netdev )
+ return -ENOMEM;
+ netdev_init ( netdev, &legacy_operations );
+ netdev->priv = &nic;
+ memset ( &nic, 0, sizeof ( nic ) );
+ set_drvdata ( hwdev, netdev );
+ netdev->dev = dev;
+
+ nic.node_addr = netdev->ll_addr;
+ nic.irqno = dev->desc.irq;
+
+ if ( ! probe ( &nic, hwdev ) ) {
+ rc = -ENODEV;
+ goto err_probe;
+ }
+
+ /* Overwrite the IRQ number. Some legacy devices set
+ * nic->irqno to 0 in the probe routine to indicate that they
+ * don't support interrupts; doing this allows the timer
+ * interrupt to be used instead.
+ */
+ dev->desc.irq = nic.irqno;
+
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register;
+
+ /* Do not remove this message */
+ printf ( "WARNING: Using legacy NIC wrapper on %s\n",
+ ethernet_protocol.ntoa ( nic.node_addr ) );
+
+ legacy_registered = 1;
+ return 0;
+
+ err_register:
+ disable ( &nic, hwdev );
+ err_probe:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ return rc;
+}
+
+void legacy_remove ( void *hwdev,
+ void * ( * get_drvdata ) ( void *hwdev ),
+ void ( * disable ) ( struct nic *nic, void *hwdev ) ) {
+ struct net_device *netdev = get_drvdata ( hwdev );
+ struct nic *nic = netdev->priv;
+
+ unregister_netdev ( netdev );
+ disable ( nic, hwdev );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ legacy_registered = 0;
+}
+
+int dummy_connect ( struct nic *nic __unused ) {
+ return 1;
+}
+
+void dummy_irq ( struct nic *nic __unused, irq_action_t irq_action __unused ) {
+ return;
+}
diff --git a/gpxe/src/drivers/net/mlx_ipoib/MT23108_PRM.h b/gpxe/src/drivers/net/mlx_ipoib/MT23108_PRM.h
new file mode 100644
index 00000000..646e94e4
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/MT23108_PRM.h
@@ -0,0 +1,2800 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+/***
+ *** This file was generated at "Tue Sep 6 09:14:00 2005"
+ *** by:
+ *** % csp_bf -copyright=/mswg/misc/license-header.txt -prefix tavorprm_ -bits -fixnames MT23108_PRM.csp
+ ***/
+
+#ifndef H_prefix_tavorprm_bits_fixnames_MT23108_PRM_csp_H
+#define H_prefix_tavorprm_bits_fixnames_MT23108_PRM_csp_H
+
+#include "bit_ops.h"
+
+/* Send doorbell */
+
+struct tavorprm_send_doorbell_st { /* Little Endian */
+ pseudo_bit_t nopcode[0x00005]; /* Opcode of descriptor to be executed */
+ pseudo_bit_t f[0x00001]; /* Fence bit. If set, descriptor is fenced */
+ pseudo_bit_t nda[0x0001a]; /* Bits 31:6 of descriptors virtual address */
+/* -------------- */
+ pseudo_bit_t nds[0x00006]; /* Next descriptor size (in 16-byte chunks) */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */
+/* -------------- */
+};
+
+/* ACCESS_DDR_inject_errors_input_modifier */
+
+struct tavorprm_access_ddr_inject_errors_input_modifier_st { /* Little Endian */
+ pseudo_bit_t index3[0x00007];
+ pseudo_bit_t q3[0x00001];
+ pseudo_bit_t index2[0x00007];
+ pseudo_bit_t q2[0x00001];
+ pseudo_bit_t index1[0x00007];
+ pseudo_bit_t q1[0x00001];
+ pseudo_bit_t index0[0x00007];
+ pseudo_bit_t q0[0x00001];
+/* -------------- */
+};
+
+/* ACCESS_DDR_inject_errors_input_parameter */
+
+struct tavorprm_access_ddr_inject_errors_input_parameter_st { /* Little Endian */
+ pseudo_bit_t ba[0x00002]; /* Bank Address */
+ pseudo_bit_t da[0x00002]; /* Dimm Address */
+ pseudo_bit_t reserved0[0x0001c];
+/* -------------- */
+ pseudo_bit_t ra[0x00010]; /* Row Address */
+ pseudo_bit_t ca[0x00010]; /* Column Address */
+/* -------------- */
+};
+
+/* Address Path */
+
+struct tavorprm_address_path_st { /* Little Endian */
+ pseudo_bit_t pkey_index[0x00007]; /* PKey table index */
+ pseudo_bit_t reserved0[0x00011];
+ pseudo_bit_t port_number[0x00002]; /* Specific port associated with this QP/EE.
+ 1 - Port 1
+ 2 - Port 2
+ other - reserved */
+ pseudo_bit_t reserved1[0x00006];
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */
+ pseudo_bit_t my_lid_path_bits[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */
+ pseudo_bit_t g[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */
+ pseudo_bit_t reserved2[0x00005];
+ pseudo_bit_t rnr_retry[0x00003]; /* RNR retry count (see C9-132 in IB spec Vol 1)
+ 0-6 - number of retries
+ 7 - infinite */
+/* -------------- */
+ pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */
+ pseudo_bit_t max_stat_rate[0x00003];/* Maximum static rate control.
+ 0 - 4X injection rate
+ 1 - 1X injection rate
+ other - reserved
+ */
+ pseudo_bit_t reserved3[0x00005];
+ pseudo_bit_t mgid_index[0x00006]; /* Index to port GID table */
+ pseudo_bit_t reserved4[0x00005];
+ pseudo_bit_t ack_timeout[0x00005]; /* Local ACK timeout - Transport timer for activation of retransmission mechanism. Refer to IB spec Vol1 9.7.6.1.3 for further details.
+ The transport timer is set to 4.096us*2^ack_timeout, if ack_timeout is 0 then transport timer is disabled. */
+/* -------------- */
+ pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */
+ pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */
+ pseudo_bit_t sl[0x00004]; /* InfiniBand Service Level (SL) */
+/* -------------- */
+ pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */
+/* -------------- */
+ pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */
+/* -------------- */
+ pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */
+/* -------------- */
+ pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] */
+/* -------------- */
+};
+
+/* HCA Command Register (HCR) */
+
+struct tavorprm_hca_command_register_st { /* Little Endian */
+ pseudo_bit_t in_param_h[0x00020]; /* Input Parameter: parameter[63:32] or pointer[63:32] to input mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t in_param_l[0x00020]; /* Input Parameter: parameter[31:0] or pointer[31:0] to input mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t input_modifier[0x00020];/* Input Parameter Modifier */
+/* -------------- */
+ pseudo_bit_t out_param_h[0x00020]; /* Output Parameter: parameter[63:32] or pointer[63:32] to output mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t out_param_l[0x00020]; /* Output Parameter: parameter[31:0] or pointer[31:0] to output mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00010];
+ pseudo_bit_t token[0x00010]; /* Software assigned token to the command, to uniquely identify it. The token is returned to the software in the EQE reported. */
+/* -------------- */
+ pseudo_bit_t opcode[0x0000c]; /* Command opcode */
+ pseudo_bit_t opcode_modifier[0x00004];/* Opcode Modifier, see specific description for each command. */
+ pseudo_bit_t reserved1[0x00006];
+ pseudo_bit_t e[0x00001]; /* Event Request
+ 0 - Don't report event (software will poll the GO bit)
+ 1 - Report event to EQ when the command completes */
+ pseudo_bit_t go[0x00001]; /* Go (0=Software ownership for the HCR, 1=Hardware ownership for the HCR)
+ Software can write to the HCR only if Go bit is cleared.
+ Software must set the Go bit to trigger the HW to execute the command. Software must not write to this register value other than 1 for the Go bit. */
+ pseudo_bit_t status[0x00008]; /* Command execution status report. Valid only if command interface in under SW ownership (Go bit is cleared)
+ 0 - command completed without error. If different than zero, command execution completed with error. Syndrom encoding is depended on command executed and is defined for each command */
+/* -------------- */
+};
+
+/* EQ Doorbell */
+
+struct tavorprm_eq_cmd_doorbell_st { /* Little Endian */
+ pseudo_bit_t eqn[0x00006]; /* EQ accessed */
+ pseudo_bit_t reserved0[0x00012];
+ pseudo_bit_t eq_cmd[0x00008]; /* Command to be executed on EQ
+ 01 - increment Consumer_indx by one
+ 02 - Request notification for next event (Arm EQ)
+ 03 - Disarm CQ (CQ number is specified in EQ_param)
+ 04 - set Consumer_indx to value of EQ_param
+ 05 - move EQ to Always Armed state
+ other - reserved */
+/* -------------- */
+ pseudo_bit_t eq_param[0x00020]; /* parameter to be used by EQ commands 03 and 04. Reserved for other commands. */
+/* -------------- */
+};
+
+/* CQ Doorbell */
+
+struct tavorprm_cq_cmd_doorbell_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number accessed */
+ pseudo_bit_t cq_cmd[0x00008]; /* Command to be executed on CQ
+ 01 - Increment Consumer_indx by cq_param plus 1
+ 02 - Request notification for next Solicited or Unsolicited completion event. CQ_param must contain last succesfully polled consumer index. For newly generated CQs the CQ_param should contain (initial consumer index-1) modulu CQ size. When working with CQs with overrun detection, CQ_param can be set to 0xFFFFFFFF (HW will use the last polled index).
+ 03 - Request notification for next Solicited completion event CQ_param must contain last succesfully polled consumer index. For newly generated CQs the CQ_param should contain (initial consumer index-1) modulu CQ size. When working with CQs with overrun detection, CQ_param can be set to 0xFFFFFFFF (HW will use the last polled index).
+ 04 - Set Consumer_indx to value of CQ_param
+ 05 - Request notification for multiple completions (see Advanced Topics chater)
+ other - reserved */
+/* -------------- */
+ pseudo_bit_t cq_param[0x00020]; /* parameter to be used by CQ command */
+/* -------------- */
+};
+
+/* Receive doorbell */
+
+struct tavorprm_receive_doorbell_st { /* Little Endian */
+ pseudo_bit_t nds[0x00006]; /* Next descriptor size (in 16-byte chunks)
+ Must be zero for SRQ doorbells */
+ pseudo_bit_t nda[0x0001a]; /* Bits 31:6 of descriptors virtual address */
+/* -------------- */
+ pseudo_bit_t credits[0x00008]; /* Amount of credits ((length of the chain) posted with the doorbell on receive queue. Chain of up to 256 descriptors can be linked with single doorbell. Zero value in this field means 256. */
+ pseudo_bit_t qpn[0x00018]; /* QP number or SRQ number this doorbell is rung on */
+/* -------------- */
+};
+
+/* RD-send doorbell */
+
+struct tavorprm_rd_send_doorbell_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t een[0x00018]; /* End-to-end context number (reliable datagram)
+ Must be zero for Nop and Bind operations */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00008];
+ pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */
+/* -------------- */
+ struct tavorprm_send_doorbell_st snd_params;/* Send parameters */
+/* -------------- */
+};
+
+/* Multicast Group Member QP */
+
+struct tavorprm_mgmqp_st { /* Little Endian */
+ pseudo_bit_t qpn_i[0x00018]; /* QPN_i: QP number which is a member in this multicast group. Valid only if Qi bit is set. Length of the QPN_i list is set in INIT_HCA */
+ pseudo_bit_t reserved0[0x00007];
+ pseudo_bit_t qi[0x00001]; /* Qi: QPN_i is valid */
+/* -------------- */
+};
+
+/* vsd */
+
+struct tavorprm_vsd_st { /* Little Endian */
+ pseudo_bit_t vsd_dw0[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw1[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw2[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw3[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw4[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw5[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw6[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw7[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw8[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw9[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw10[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw11[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw12[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw13[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw14[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw15[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw16[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw17[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw18[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw19[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw20[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw21[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw22[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw23[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw24[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw25[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw26[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw27[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw28[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw29[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw30[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw31[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw32[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw33[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw34[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw35[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw36[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw37[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw38[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw39[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw40[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw41[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw42[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw43[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw44[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw45[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw46[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw47[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw48[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw49[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw50[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw51[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw52[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw53[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw54[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw55[0x00020];
+/* -------------- */
+};
+
+/* ACCESS_DDR_inject_errors */
+
+struct tavorprm_access_ddr_inject_errors_st { /* Little Endian */
+ struct tavorprm_access_ddr_inject_errors_input_parameter_st access_ddr_inject_errors_input_parameter;
+/* -------------- */
+ struct tavorprm_access_ddr_inject_errors_input_modifier_st access_ddr_inject_errors_input_modifier;
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* Logical DIMM Information */
+
+struct tavorprm_dimminfo_st { /* Little Endian */
+ pseudo_bit_t dimmsize[0x00010]; /* Size of DIMM in units of 2^20 Bytes. This value is valid only when DIMMStatus is 0. */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t dimmstatus[0x00001]; /* DIMM Status
+ 0 - Enabled
+ 1 - Disabled
+ */
+ pseudo_bit_t dh[0x00001]; /* When set, the DIMM is Hidden and can not be accessed from the PCI bus. */
+ pseudo_bit_t wo[0x00001]; /* When set, the DIMM is write only.
+ If data integrity is configured (other than none), the DIMM must be
+ only targeted by write transactions where the address and size are multiples of 16 bytes. */
+ pseudo_bit_t reserved1[0x00005];
+/* -------------- */
+ pseudo_bit_t spd[0x00001]; /* 0 - DIMM SPD was read from DIMM
+ 1 - DIMM SPD was read from InfiniHost NVMEM */
+ pseudo_bit_t sladr[0x00003]; /* SPD Slave Address 3 LSBits.
+ Valid only if spd bit is 0. */
+ pseudo_bit_t sock_num[0x00002]; /* DIMM socket number (for double sided DIMM one of the two numbers will be reported) */
+ pseudo_bit_t syn[0x00004]; /* Error syndrome (valid regardless of status value)
+ 0 - DIMM has no error
+ 1 - SPD error (e.g. checksum error, no response, error while reading)
+ 2 - DIMM out of bounds (e.g. DIMM rows number is not between 7 and 14, DIMM type is not 2)
+ 3 - DIMM conflict (e.g. mix of registered and unbuffered DIMMs, CAS latency conflict)
+ 5 - DIMM size trimmed due to configuration (size exceeds)
+ other - Error, reserved
+ */
+ pseudo_bit_t reserved2[0x00016];
+/* -------------- */
+ pseudo_bit_t vendor_id_h[0x00020]; /* JDEC Manufacturer ID[63:32] */
+/* -------------- */
+ pseudo_bit_t vendor_id_l[0x00020]; /* JDEC Manufacturer ID[31:0] */
+/* -------------- */
+ pseudo_bit_t dimm_start_adr_h[0x00020];/* DIMM memory start address [63:32]. This value is valid only when DIMMStatus is 0. */
+/* -------------- */
+ pseudo_bit_t dimm_start_adr_l[0x00020];/* DIMM memory start address [31:0]. This value is valid only when DIMMStatus is 0. */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00040];
+/* -------------- */
+};
+
+/* UAR Parameters */
+
+struct tavorprm_uar_params_st { /* Little Endian */
+ pseudo_bit_t uar_base_addr_h[0x00020];/* UAR Base Address [63:32] (QUERY_HCA only) */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00014];
+ pseudo_bit_t uar_base_addr_l[0x0000c];/* UAR Base Address [31:20] (QUERY_HCA only) */
+/* -------------- */
+ pseudo_bit_t uar_page_sz[0x00008]; /* This field defines the size of each UAR page.
+ Size of UAR Page is 4KB*2^UAR_Page_Size */
+ pseudo_bit_t reserved1[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00020];
+/* -------------- */
+ pseudo_bit_t uar_scratch_base_addr_h[0x00020];/* Base address of UAR scratchpad [63:32].
+ Number of entries in table is UAR BAR size divided by UAR Page Size.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t uar_scratch_base_addr_l[0x00020];/* Base address of UAR scratchpad [31:0].
+ Number of entries in table is UAR BAR size divided by UAR Page Size.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00040];
+/* -------------- */
+};
+
+/* Translation and Protection Tables Parameters */
+
+struct tavorprm_tptparams_st { /* Little Endian */
+ pseudo_bit_t mpt_base_adr_h[0x00020];/* MPT - Memory Protection Table base physical address [63:32].
+ Entry size is 64 bytes.
+ Table must be aligned to its size.
+ Address may be set to zero if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t mpt_base_adr_l[0x00020];/* MPT - Memory Protection Table base physical address [31:0].
+ Entry size is 64 bytes.
+ Table must be aligned to its size.
+ Address may be set to zero if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t log_mpt_sz[0x00006]; /* Log (base 2) of the number of region/windows entries in the MPT table. */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t pfto[0x00005]; /* Page Fault RNR Timeout -
+ The field returned in RNR Naks generated when a page fault is detected.
+ It has no effect when on-demand-paging is not used. */
+ pseudo_bit_t reserved1[0x00003];
+ pseudo_bit_t mtt_segment_size[0x00003];/* The size of MTT segment is 64*2^MTT_Segment_Size bytes */
+ pseudo_bit_t reserved2[0x0000d];
+/* -------------- */
+ pseudo_bit_t mtt_version[0x00008]; /* Version of MTT page walk. Must be zero */
+ pseudo_bit_t reserved3[0x00018];
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_h[0x00020];/* MTT - Memory Translation table base physical address [63:32].
+ Table must be aligned to its size.
+ Address may be set to zero if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_l[0x00020];/* MTT - Memory Translation table base physical address [31:0].
+ Table must be aligned to its size.
+ Address may be set to zero if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00040];
+/* -------------- */
+};
+
+/* Multicast Support Parameters */
+
+struct tavorprm_multicastparam_st { /* Little Endian */
+ pseudo_bit_t mc_base_addr_h[0x00020];/* Base Address of the Multicast Table [63:32].
+ The base address must be aligned to the entry size.
+ Address may be set to zero if multicast is not supported. */
+/* -------------- */
+ pseudo_bit_t mc_base_addr_l[0x00020];/* Base Address of the Multicast Table [31:0].
+ The base address must be aligned to the entry size.
+ Address may be set to zero if multicast is not supported. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t log_mc_table_entry_sz[0x00010];/* Log2 of the Size of multicast group member (MGM) entry.
+ Must be greater than 5 (to allow CTRL and GID sections).
+ That implies the number of QPs per MC table entry. */
+ pseudo_bit_t reserved1[0x00010];
+/* -------------- */
+ pseudo_bit_t mc_table_hash_sz[0x00011];/* Number of entries in multicast DGID hash table (must be power of 2)
+ INIT_HCA - the required number of entries
+ QUERY_HCA - the actual number of entries assigned by firmware (will be less than or equal to the amount required in INIT_HCA) */
+ pseudo_bit_t reserved2[0x0000f];
+/* -------------- */
+ pseudo_bit_t log_mc_table_sz[0x00005];/* Log2 of the overall number of MC entries in the MCG table (includes both hash and auxiliary tables) */
+ pseudo_bit_t reserved3[0x00013];
+ pseudo_bit_t mc_hash_fn[0x00003]; /* Multicast hash function
+ 0 - Default hash function
+ other - reserved */
+ pseudo_bit_t reserved4[0x00005];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+};
+
+/* Memory Access Parameters for UD Address Vector Table */
+
+struct tavorprm_udavtable_memory_parameters_st { /* Little Endian */
+ pseudo_bit_t l_key[0x00020]; /* L_Key used to access TPT */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* PD used by TPT for matching against PD of region entry being accessed. */
+ pseudo_bit_t reserved0[0x00005];
+ pseudo_bit_t xlation_en[0x00001]; /* When cleared, address is physical address and no translation will be done. When set, address is virtual. TPT will be accessed in both cases for address decoding purposes. */
+ pseudo_bit_t reserved1[0x00002];
+/* -------------- */
+};
+
+/* QPC/EEC/CQC/EQC/RDB Parameters */
+
+struct tavorprm_qpcbaseaddr_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t qpc_base_addr_h[0x00020];/* QPC Base Address [63:32]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t log_num_of_qp[0x00005];/* Log base 2 of number of supported QPs */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t qpc_base_addr_l[0x00019];/* QPC Base Address [31:7]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t eec_base_addr_h[0x00020];/* EEC Base Address [63:32]
+ Table must be aligned on its size.
+ Address may be set to zero if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t log_num_of_ee[0x00005];/* Log base 2 of number of supported EEs. */
+ pseudo_bit_t reserved3[0x00002];
+ pseudo_bit_t eec_base_addr_l[0x00019];/* EEC Base Address [31:7]
+ Table must be aligned on its size
+ Address may be set to zero if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t srqc_base_addr_h[0x00020];/* SRQ Context Base Address [63:32]
+ Table must be aligned on its size
+ Address may be set to zero if SRQ is not supported. */
+/* -------------- */
+ pseudo_bit_t log_num_of_srq[0x00005];/* Log base 2 of number of supported SRQs. */
+ pseudo_bit_t srqc_base_addr_l[0x0001b];/* SRQ Context Base Address [31:5]
+ Table must be aligned on its size
+ Address may be set to zero if SRQ is not supported. */
+/* -------------- */
+ pseudo_bit_t cqc_base_addr_h[0x00020];/* CQC Base Address [63:32]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t log_num_of_cq[0x00005];/* Log base 2 of number of supported CQs. */
+ pseudo_bit_t reserved4[0x00001];
+ pseudo_bit_t cqc_base_addr_l[0x0001a];/* CQC Base Address [31:6]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00040];
+/* -------------- */
+ pseudo_bit_t eqpc_base_addr_h[0x00020];/* Extended QPC Base Address [63:32]
+ Table has same number of entries as QPC table.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t eqpc_base_addr_l[0x00020];/* Extended QPC Base Address [31:0]
+ Table has same number of entries as QPC table.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00040];
+/* -------------- */
+ pseudo_bit_t eeec_base_addr_h[0x00020];/* Extended EEC Base Address [63:32]
+ Table has same number of entries as EEC table.
+ Table must be aligned to entry size.
+ Address may be set to zero if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t eeec_base_addr_l[0x00020];/* Extended EEC Base Address [31:0]
+ Table has same number of entries as EEC table.
+ Table must be aligned to entry size.
+ Address may be set to zero if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00040];
+/* -------------- */
+ pseudo_bit_t eqc_base_addr_h[0x00020];/* EQC Base Address [63:32]
+ Address may be set to zero if EQs are not supported.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t log_num_eq[0x00004]; /* Log base 2 of number of supported EQs.
+ Must be 6 or less in InfiniHost. */
+ pseudo_bit_t reserved8[0x00002];
+ pseudo_bit_t eqc_base_addr_l[0x0001a];/* EQC Base Address [31:6]
+ Address may be set to zero if EQs are not supported.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00040];
+/* -------------- */
+ pseudo_bit_t rdb_base_addr_h[0x00020];/* Base address of table that holds remote read and remote atomic requests [63:32].
+ Table must be aligned to RDB entry size (32 bytes).
+ Address may be set to zero if remote RDMA reads are not supported.
+ Please refer to QP and EE chapter for further explanation on RDB allocation. */
+/* -------------- */
+ pseudo_bit_t rdb_base_addr_l[0x00020];/* Base address of table that holds remote read and remote atomic requests [31:0].
+ Table must be aligned to RDB entry size (32 bytes).
+ This field must always be zero.
+ Please refer to QP and EE chapter for further explanation on RDB allocation. */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00040];
+/* -------------- */
+};
+
+/* Performance Monitors */
+
+struct tavorprm_performance_monitors_st { /* Little Endian */
+ pseudo_bit_t e0[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t e1[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t e2[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t r0[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t r1[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t r2[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t reserved1[0x00001];
+ pseudo_bit_t i0[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t i1[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t i2[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t f0[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t f1[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t f2[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t reserved3[0x00001];
+ pseudo_bit_t ev_cnt1[0x00005]; /* Specifies event to be counted by Event_counter1 See XXX for events' definition. */
+ pseudo_bit_t reserved4[0x00003];
+ pseudo_bit_t ev_cnt2[0x00005]; /* Specifies event to be counted by Event_counter2 See XXX for events' definition. */
+ pseudo_bit_t reserved5[0x00003];
+/* -------------- */
+ pseudo_bit_t clock_counter[0x00020];
+/* -------------- */
+ pseudo_bit_t event_counter1[0x00020];
+/* -------------- */
+ pseudo_bit_t event_counter2[0x00020];/* Read/write event counter, counting events specified by EvCntl and EvCnt2 fields repsectively. When the event counter reaches is maximum value of 0xFFFFFF, the next event will cause it to roll over to zero, set F1 or F2 bit respectively and generate interrupt by I1 I2 bit respectively. */
+/* -------------- */
+};
+
+/* QP and EE Context Entry */
+
+struct tavorprm_queue_pair_ee_context_entry_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t de[0x00001]; /* Send/Receive Descriptor Event enable - if set, events can be generated upon descriptors' completion on send/receive queue (controlled by E bit in WQE). Invalid in EE context */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t pm_state[0x00002]; /* Path migration state (Migrated, Armed or Rearm)
+ 11-Migrated
+ 00-Armed
+ 01-Rearm
+ 10-Reserved
+ Should be set to 11 for UD QPs and for QPs which do not support APM */
+ pseudo_bit_t reserved2[0x00003];
+ pseudo_bit_t st[0x00003]; /* Service type (invalid in EE context):
+ 000-Reliable Connection
+ 001-Unreliable Connection
+ 010-Reliable Datagram (Not supported for InfiniHost MT23108)
+ 011-Unreliable Datagram
+ 111-MLX transport (raw bits injection). Used for management QPs and RAW */
+ pseudo_bit_t reserved3[0x00009];
+ pseudo_bit_t state[0x00004]; /* QP/EE state:
+ 0 - RST
+ 1 - INIT
+ 2 - RTR
+ 3 - RTS
+ 4 - SQEr
+ 5 - SQD (Send Queue Drained)
+ 6 - ERR
+ 7 - Send Queue Draining
+ 8 - F - RESERVED
+ (Valid for QUERY_QPEE and ERR2RST_QPEE commands only) */
+/* -------------- */
+ pseudo_bit_t sched_queue[0x00004]; /* Schedule queue to be used for WQE scheduling to execution. Determines QOS for this QP. */
+ pseudo_bit_t reserved4[0x0001c];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00018];
+ pseudo_bit_t msg_max[0x00005]; /* Max message size allowed on the QP. Maximum message size is 2^msg_Max.
+ Must be equal to MTU for UD and MLX QPs. */
+ pseudo_bit_t mtu[0x00003]; /* MTU of the QP (Must be the same for both paths: primary and alternative):
+ 0x1 - 256 bytes
+ 0x2 - 512
+ 0x3 - 1024
+ 0x4 - 2048
+ other - reserved
+
+ Should be configured to 0x4 for UD and MLX QPs. */
+/* -------------- */
+ pseudo_bit_t usr_page[0x00018]; /* Index (offset) of user page allocated for this QP (see "non_privileged Access to the HCA Hardware"). Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved6[0x00008];
+/* -------------- */
+ pseudo_bit_t local_qpn_een[0x00018];/* Local QP/EE number Lower bits determine position of this record in QPC table, and - thus - constrained
+ This field is valid for QUERY and ERR2RST commands only. */
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t remote_qpn_een[0x00018];/* Remote QP/EE number */
+ pseudo_bit_t reserved8[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved9[0x00040];
+/* -------------- */
+ struct tavorprm_address_path_st primary_address_path;/* Primary address path for the QP/EE */
+/* -------------- */
+ struct tavorprm_address_path_st alternative_address_path;/* Alternate address path for the QP/EE */
+/* -------------- */
+ pseudo_bit_t rdd[0x00018]; /* Reliable Datagram Domain */
+ pseudo_bit_t reserved10[0x00008];
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* QP protection domain. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved11[0x00008];
+/* -------------- */
+ pseudo_bit_t wqe_base_adr[0x00020]; /* Bits 63:32 of WQE address for both SQ and RQ.
+ Reserved for EE context. */
+/* -------------- */
+ pseudo_bit_t wqe_lkey[0x00020]; /* memory key (L-Key) to be used to access WQEs. Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t reserved12[0x00003];
+ pseudo_bit_t ssc[0x00001]; /* Send Signaled Completion
+ 1 - all send WQEs generate CQEs.
+ 0 - only send WQEs with C bit set generate completion.
+ Not valid (reserved) in EE context. */
+ pseudo_bit_t sic[0x00001]; /* If set - Ignore end to end credits on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t cur_retry_cnt[0x00003];/* Current transport retry counter (QUERY_QPEE only).
+ The current transport retry counter can vary from retry_count down to 1, where 1 means that the last retry attempt is currently executing. */
+ pseudo_bit_t cur_rnr_retry[0x00003];/* Current RNR retry counter (QUERY_QPEE only).
+ The current RNR retry counter can vary from rnr_retry to 1, where 1 means that the last retry attempt is currently executing. */
+ pseudo_bit_t reserved13[0x00002];
+ pseudo_bit_t sae[0x00001]; /* If set - Atomic operations enabled on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t swe[0x00001]; /* If set - RDMA - write enabled on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t sre[0x00001]; /* If set - RDMA - read enabled on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t retry_count[0x00003]; /* Transport timeout Retry count */
+ pseudo_bit_t reserved14[0x00002];
+ pseudo_bit_t sra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed in the send queue. Maximum number is 2^SRA_Max. Must be zero in EE context. */
+ pseudo_bit_t flight_lim[0x00004]; /* Number of outstanding (in-flight) messages on the wire allowed for this send queue.
+ Number of outstanding messages is 2^Flight_Lim.
+ Use 0xF for unlimited number of outstanding messages. */
+ pseudo_bit_t ack_req_freq[0x00004]; /* ACK required frequency. ACK required bit will be set in every 2^AckReqFreq packets at least. Not valid for RD QP. */
+/* -------------- */
+ pseudo_bit_t reserved15[0x00020];
+/* -------------- */
+ pseudo_bit_t next_send_psn[0x00018];/* Next PSN to be sent */
+ pseudo_bit_t reserved16[0x00008];
+/* -------------- */
+ pseudo_bit_t cqn_snd[0x00018]; /* CQ number completions from the send queue to be reported to. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved17[0x00008];
+/* -------------- */
+ pseudo_bit_t next_snd_wqe_0[0x00020];/* Pointer and properties of next WQE on send queue. The format is same as next segment (first 8 bytes) in the WQE. This field is read-only and provided for debug purposes. Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t next_snd_wqe_1[0x00020];/* Pointer and properties of next WQE on send queue. The format is same as next segment (first 8 bytes) in the WQE. This field is read-only and provided for debug purposes. Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t last_acked_psn[0x00018];/* The last acknowledged PSN for the requester (QUERY_QPEE only) */
+ pseudo_bit_t reserved18[0x00008];
+/* -------------- */
+ pseudo_bit_t ssn[0x00018]; /* Requester Send Sequence Number (QUERY_QPEE only) */
+ pseudo_bit_t reserved19[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved20[0x00003];
+ pseudo_bit_t rsc[0x00001]; /* 1 - all receive WQEs generate CQEs.
+ 0 - only receive WQEs with C bit set generate completion.
+ Not valid (reserved) in EE context.
+ */
+ pseudo_bit_t ric[0x00001]; /* Invalid Credits.
+ 1 - place "Invalid Credits" to ACKs sent from this queue.
+ 0 - ACKs report the actual number of end to end credits on the connection.
+ Not valid (reserved) in EE context.
+ Must be set to 1 on QPs which are attached to SRQ. */
+ pseudo_bit_t reserved21[0x00008];
+ pseudo_bit_t rae[0x00001]; /* If set - Atomic operations enabled. on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t rwe[0x00001]; /* If set - RDMA - write enabled on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t rre[0x00001]; /* If set - RDMA - read enabled on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved22[0x00005];
+ pseudo_bit_t rra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed on receive queue is 2^RRA_Max.
+ Must be 0 for EE context. */
+ pseudo_bit_t reserved23[0x00008];
+/* -------------- */
+ pseudo_bit_t next_rcv_psn[0x00018]; /* Next (expected) PSN on receive */
+ pseudo_bit_t min_rnr_nak[0x00005]; /* Minimum RNR NAK timer value (TTTTT field encoding according to the IB spec Vol1 9.7.5.2.8).
+ Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved24[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved25[0x00005];
+ pseudo_bit_t ra_buff_indx[0x0001b]; /* Index to outstanding read/atomic buffer.
+ This field constructs the address to the RDB for maintaining the incoming RDMA read and atomic requests. */
+/* -------------- */
+ pseudo_bit_t cqn_rcv[0x00018]; /* CQ number completions from receive queue to be reported to. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved26[0x00008];
+/* -------------- */
+ pseudo_bit_t next_rcv_wqe_0[0x00020];/* Pointer and properties of next WQE on the receive queue. This format is same as next segment (first 8 bytes) in the WQE.This field is read-only and provided for debug purposes. Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t next_rcv_wqe_1[0x00020];/* Pointer and properties of next WQE on the receive queue. This format is same as next segment (first 8 bytes) in the WQE.This field is read-only and provided for debug purposes. Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t q_key[0x00020]; /* Q_Key to be validated against received datagrams.
+ On send datagrams, if Q_Key[31] specified in the WQE is set, then this Q_Key will be transmitted in the outgoing message.
+ Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t srqn[0x00018]; /* SRQN - Shared Receive Queue Number - specifies the SRQ number from which the QP dequeues receive descriptors.
+ SRQN is valid only if SRQ bit is set. Not valid (reserved) in EE context. */
+ pseudo_bit_t srq[0x00001]; /* SRQ - Shared Receive Queue. If this bit is set, then the QP is associated with a SRQ. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved27[0x00007];
+/* -------------- */
+ pseudo_bit_t rmsn[0x00018]; /* Responder current message sequence number (QUERY_QPEE only) */
+ pseudo_bit_t reserved28[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved29[0x00260];
+/* -------------- */
+};
+
+/* MOD_STAT_CFG */
+
+struct tavorprm_mod_stat_cfg_st { /* Little Endian */
+ pseudo_bit_t log_max_srqs[0x00005]; /* Log (base 2) of the number of SRQs to allocate (0 if no SRQs are required), valid only if srq bit is set. */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t srq[0x00001]; /* When set SRQs are supported */
+ pseudo_bit_t srq_m[0x00001]; /* Modify SRQ parameters */
+ pseudo_bit_t reserved1[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved2[0x007e0];
+/* -------------- */
+};
+
+/* SRQ Context */
+
+struct tavorprm_srq_context_st { /* Little Endian */
+ pseudo_bit_t wqe_addr_h[0x00020]; /* WQE base address for the SRQ [63:32]
+ Must be set at SW2HW_SRQ */
+/* -------------- */
+ pseudo_bit_t ds[0x00006]; /* Descriptor Size on the SRQ in units of 16 bytes */
+ pseudo_bit_t next_wqe_addr_l[0x0001a];/* Next WQE address for the SRQ [31:6]
+ Valid only on QUERY_SRQ and HW2SW_SRQ commands. */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* SRQ PD - used for descriptor fetching on the SRQ and for data scatter on send operations on QPs attached to SRQ.
+ In InfiniHost MT23108 SRQ.PD must be equal to the PD of all QPs which are attached to the SRQ */
+ pseudo_bit_t reserved0[0x00004];
+ pseudo_bit_t state[0x00004]; /* SRQ State:
+ 1111 - SW Ownership
+ 0000 - HW Ownership
+ 0001 - Error
+ Valid only on QUERY_SRQ and HW2SW_SRQ commands. */
+/* -------------- */
+ pseudo_bit_t l_key[0x00020]; /* L_Key for descriptor fetching on the SRQ */
+/* -------------- */
+ pseudo_bit_t uar[0x00018]; /* SRQ User Access Region - Index (offset) of user page allocated for the SRQ (see "Non Privileged Access to the HCA HW"). */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t wqe_cnt[0x00010]; /* WQE count on the SRQ.
+ Valid only on QUERY_SRQ and HW2SW_SRQ commands. */
+ pseudo_bit_t lwm[0x00010]; /* Limit Water Mark - if the LWM is not zero, and the wqe_cnt drops below LWM when a WQE is dequeued from the SRQ, then a SRQ limit event is fired and the LWM is set to zero. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00010];
+ pseudo_bit_t reserved3[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+};
+
+/* InfiniHost Configuration Registers */
+
+struct tavorprm_mt23108_configuration_registers_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x403400];
+/* -------------- */
+ struct tavorprm_hca_command_register_st hca_command_interface_register;/* HCA Command Register */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00320];
+/* -------------- */
+ pseudo_bit_t ecr_h[0x00020]; /* Event Cause Register[63:32]. Each bit in the ECR corresponds to one of the 64 Event Queues in InfiniHost. If bit is set, interrupt was asserted due to event reported on corresponding event queue. This register is read-only; writing to this register will cause undefined results
+ */
+/* -------------- */
+ pseudo_bit_t ecr_l[0x00020]; /* Event Cause Register[31:0]. Each bit in the ECR corresponds to one of the 64 Event Queues in InfiniHost. If bit is set, interrupt was asserted due to event reported on corresponding event queue. This register is read-only; writing to this register will cause undefined results
+ */
+/* -------------- */
+ pseudo_bit_t clr_ecr_h[0x00020]; /* Clear Event Cause Register[63:32].
+ This register is used to clear bits in ECR register. Each set bit in data written to this register clears corresponding bit in the ECR register, Each bit written with zero has no effect. This register is write-only. Reading from this register will cause undefined result
+ */
+/* -------------- */
+ pseudo_bit_t clr_ecr_l[0x00020]; /* Clear Event Cause Register[31:0].
+ This register is used to clear bits in ECR register. Each set bit in data written to this register clears corresponding bit in the ECR register, Each bit written with zero has no effect. This register is write-only. Reading from this register will cause undefined result
+ */
+/* -------------- */
+ pseudo_bit_t reserved2[0x4c780];
+/* -------------- */
+ pseudo_bit_t reserved3[0x01000];
+/* -------------- */
+ pseudo_bit_t reserved4[0x32f6c0];
+/* -------------- */
+ pseudo_bit_t clr_int_h[0x00020]; /* Clear Interrupt [63:32]
+ This register is used to clear (de-assert) interrupt output pins of InfiniHost. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot. This register is write-only. Reading from this register will cause undefined result */
+/* -------------- */
+ pseudo_bit_t clr_int_l[0x00020]; /* Clear Interrupt [31:0]
+ This register is used to clear (de-assert) interrupt output pins of InfiniHost. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot. This register is write-only. Reading from this register will cause undefined result */
+/* -------------- */
+ pseudo_bit_t reserved5[0x7f900];
+/* -------------- */
+};
+
+/* Schedule queues configuration */
+
+struct tavorprm_cfg_schq_st { /* Little Endian */
+ pseudo_bit_t quota[0x00008]; /* Number of WQEs that are executed until preemption of the scheduling queue and switching to the next schedule queue */
+ pseudo_bit_t reserved0[0x00018];
+/* -------------- */
+ pseudo_bit_t rqsq0[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq0[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq1[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq1[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq2[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq2[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq3[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq3[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq4[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq4[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq5[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq5[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq6[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq6[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq7[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq7[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq8[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq8[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq9[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq9[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq10[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq10[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq11[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq11[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq12[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq12[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq13[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq13[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq14[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq14[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq15[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq15[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq16[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq16[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq17[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq17[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq18[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq18[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq19[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq19[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq20[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq20[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq21[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq21[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq22[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq22[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq23[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq23[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq24[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq24[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq25[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq25[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq26[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq26[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq27[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq27[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq28[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq28[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq29[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq29[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t rqsq30[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq30[0x00008]; /* Weight for responder schedule queue */
+ pseudo_bit_t rqsq31[0x00008]; /* Weight for requestor schedule queue */
+ pseudo_bit_t rssq31[0x00008]; /* Weight for responder schedule queue */
+/* -------------- */
+ pseudo_bit_t reserved1[0x005e0];
+/* -------------- */
+};
+
+/* Query BAR */
+
+struct tavorprm_query_bar_st { /* Little Endian */
+ pseudo_bit_t bar_base_h[0x00020]; /* BAR base [63:32] */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00014];
+ pseudo_bit_t bar_base_l[0x0000c]; /* BAR base [31:20] */
+/* -------------- */
+};
+
+/* Performance Counters */
+
+struct tavorprm_performance_counters_st { /* Little Endian */
+ pseudo_bit_t sqpc_access_cnt[0x00020];/* SQPC cache access count */
+/* -------------- */
+ pseudo_bit_t sqpc_miss_cnt[0x00020];/* SQPC cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t rqpc_access_cnt[0x00020];/* RQPC cache access count */
+/* -------------- */
+ pseudo_bit_t rqpc_miss_cnt[0x00020];/* RQPC cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+ pseudo_bit_t cqc_access_cnt[0x00020];/* CQC cache access count */
+/* -------------- */
+ pseudo_bit_t cqc_miss_cnt[0x00020]; /* CQC cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t tpt_access_cnt[0x00020];/* TPT cache access count */
+/* -------------- */
+ pseudo_bit_t mpt_miss_cnt[0x00020]; /* MPT cache miss count */
+/* -------------- */
+ pseudo_bit_t mtt_miss_cnt[0x00020]; /* MTT cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00620];
+/* -------------- */
+};
+
+/* Transport and CI Error Counters */
+
+struct tavorprm_transport_and_ci_error_counters_st { /* Little Endian */
+ pseudo_bit_t rq_num_lle[0x00020]; /* Responder - number of local length errors.
+ Local Length Errors: Inbound "Send" request message exceeded the responders available buffer space. */
+/* -------------- */
+ pseudo_bit_t sq_num_lle[0x00020]; /* Requester - number of local length errors.
+ Length Errors: RDMA READ response message contained too much or too little payload data. */
+/* -------------- */
+ pseudo_bit_t rq_num_lqpoe[0x00020]; /* Responder - number local QP operation error.
+ 1. Malformed WQE: Responder detected a malformed Receive Queue WQE while processing the packet.
+ 2. Local QP Error: Responder detected a local QP related error while executing the request message. The local error prevented the responder from completing the request. */
+/* -------------- */
+ pseudo_bit_t sq_num_lqpoe[0x00020]; /* Requester - number local QP operation error
+ 1. Local Operation Error: (WQE gather, affiliated or unaffiliated): An error occurred in the requesters local channel interface that either cannot be associated with a certain WQE, or occurred when reading a WQE.
+ */
+/* -------------- */
+ pseudo_bit_t rq_num_leeoe[0x00020]; /* Responder - number local EE operation error.
+ RD */
+/* -------------- */
+ pseudo_bit_t sq_num_leeoe[0x00020]; /* Requester - number local EE operation error.
+ RD */
+/* -------------- */
+ pseudo_bit_t rq_num_lpe[0x00020]; /* Responder - number of local protection errors.
+ Local QP (Protection) Error: Responder detected a local access violation error while executing a send request message. The error prevented the responder from completing the request. */
+/* -------------- */
+ pseudo_bit_t sq_num_lpe[0x00020]; /* Requester - number of local protection errors.
+ Local Memory Protection Error: Requester detected a memory translation/protection (TPT) error.
+ */
+/* -------------- */
+ pseudo_bit_t rq_num_wrfe[0x00020]; /* Responder - number of CQEs with error generated. */
+/* -------------- */
+ pseudo_bit_t sq_num_wrfe[0x00020]; /* Requester - number of CQEs with error generated. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_mwbe[0x00020]; /* Requester - number of memory window bind errors. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_bre[0x00020]; /* Requester - number of bad response errors.
+ Bad response: Unexpected opcode for the response packet received at the expected response PSN. */
+/* -------------- */
+ pseudo_bit_t rq_num_lae[0x00020]; /* Responder - number of local access errors.
+ Unused. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t sq_num_rire[0x00020]; /* Requester - number of remote invalid request errors.
+ NAK-Invalid Request on:
+ 1. Unsupported OpCode: Responder detected an unsupported OpCode.
+ 2. Unexpected OpCode: Responder detected an error in the sequence of OpCodes, such as a missing "Last" packet.
+ Note: there is no PSN error, thus this does not indicate a dropped packet. */
+/* -------------- */
+ pseudo_bit_t rq_num_rire[0x00020]; /* Responder - number of remote invalid request errors.
+ NAK may or may not be sent.
+ 1. Unsupported or Reserved OpCode: Inbound request OpCode was either reserved, or was for a function not supported by this QP. (E.G. RDMA or ATOMIC on QP not set up for this). For RC this is "QP Async affiliated".
+ 2. Misaligned ATOMIC: VA does not point to an aligned address on an atomic operation.
+ 3. Too many RDMA READ or ATOMIC Requests: There were more requests received and not ACKed than allowed for the connection.
+ 4. Out of Sequence OpCode, current packet is "first" or "Only": The Responder detected an error in the sequence of OpCodes; a missing "Last" packet
+ 5. Out of Sequence OpCode, current packet is not "first" or "Only": The Responder detected an error in the sequence of OpCodes; a missing "First" packet
+ 6. Local Length Error: Inbound "Send" request message exceeded the responder.s available buffer space.
+ 7. Length error: RDMA WRITE request message contained too much or too little payload data compared to the DMA length advertised in the first or only packet.
+ 8. Length error: Payload length was not consistent with the opcode:
+ a: 0 byte <= "only" <= PMTU bytes
+ b: ("first" or "middle") == PMTU bytes
+ c: 1byte <= "last" <= PMTU bytes
+ 9. Length error: Inbound message exceeded the size supported by the CA port. */
+/* -------------- */
+ pseudo_bit_t sq_num_rae[0x00020]; /* Requester - number of remote access errors.
+ NAK-Remote Access Error on:
+ R_Key Violation: Responder detected an invalid R_Key while executing an RDMA Request. */
+/* -------------- */
+ pseudo_bit_t rq_num_rae[0x00020]; /* Responder - number of remote access errors.
+ R_Key Violation Responder detected an R_Key violation while executing an RDMA request.
+ NAK may or may not be sent. */
+/* -------------- */
+ pseudo_bit_t sq_num_roe[0x00020]; /* Requester - number of remote operation errors.
+ NAK-Remote Operation Error on:
+ Remote Operation Error: Responder encountered an error, (local to the responder), which prevented it from completing the request. */
+/* -------------- */
+ pseudo_bit_t rq_num_roe[0x00020]; /* Responder - number of remote operation errors.
+ NAK-Remote Operation Error on:
+ 1. Malformed WQE: Responder detected a malformed Receive Queue WQE while processing the packet.
+ 2. Remote Operation Error: Responder encountered an error, (local to the responder), which prevented it from completing the request. */
+/* -------------- */
+ pseudo_bit_t sq_num_tree[0x00020]; /* Requester - number of transport retries exceeded errors.
+ 1. Packet sequence error: Retry limit exceeded. Responder detected a PSN larger than it expected. The requestor performed retries, and automatic path migration and additional retries, if applicable, but all attempts failed.
+ 2. Implied NAK sequence error: Retry limit exceeded. Requestor detected an ACK with a PSN larger than the expected PSN for an RDMA READ or atomic response. The requestor performed retries, and automatic path migration and additional retries, if applicable, but all attempts failed.
+ 3. Local Ack Timeout error: Retry limit exceeded. No ACK response within timer interval. The requestor performed retries, and automatic path migration and additional retries, but all attempts failed. */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_rree[0x00020]; /* Requester - number of RNR nak retries exceeded errors.
+ RNR NAK Retry error. Retry limit exceeded. Excessive RNR NAKs returned by the responder: Requestor retried the request "n" times, but received RNR NAK each time. */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_lrdve[0x00020]; /* Requester - number of local RDD violation errors.
+ RD only. */
+/* -------------- */
+ pseudo_bit_t rq_num_rirdre[0x00020];/* Responder - number of remote invalid RD request errors.
+ RD only. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00040];
+/* -------------- */
+ pseudo_bit_t sq_num_rabrte[0x00020];/* Requester - number of remote aborted errors.
+ RD only. */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_ieecne[0x00020];/* Requester - number of invalid EE context number errors.
+ RD only. */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_ieecse[0x00020];/* Requester - invalid EE context state errors.
+ RD only. */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00380];
+/* -------------- */
+ pseudo_bit_t rq_num_oos[0x00020]; /* Responder - number of out of sequence requests received.
+ Out of Sequence Request Packet: Packet PSN of the inbound request is outside the responders valid PSN window.
+ NAK may or may not be sent. */
+/* -------------- */
+ pseudo_bit_t sq_num_oos[0x00020]; /* Requester - number of out of sequence Naks received.
+ NAK-Sequence Error on:
+ 1. Packet sequence error. Retry limit not exceeded: Responder detected a PSN larger than it expected. Requester may retry the request.
+ 2. Packet sequence error. Retry limit exceeded: Responder detected a PSN larger than it expected. The requestor performed retries, and automatic path migration and additional retries, if applicable, but all attempts failed. */
+/* -------------- */
+ pseudo_bit_t rq_num_mce[0x00020]; /* Responder - number of bad multicast packets received.
+ Missing GID or bad GID. */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00020];
+/* -------------- */
+ pseudo_bit_t rq_num_rsync[0x00020]; /* Responder - number of RESYNC operations.
+ RD only. */
+/* -------------- */
+ pseudo_bit_t sq_num_rsync[0x00020]; /* Requester - number of RESYNC operations.
+ RD only. */
+/* -------------- */
+ pseudo_bit_t rq_num_udsdprd[0x00020];/* The number of UD packets silently discarded on the receive queue due to lack of receive descriptor.
+ Resources Not Ready Error: A UD WQE is not currently available. */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00020];
+/* -------------- */
+ pseudo_bit_t rq_num_ucsdprd[0x00020];/* The number of UC packets silently discarded on the receive queue due to lack of receive descriptor.
+ Resources Not Ready Error: A UC WQE is not currently available. */
+/* -------------- */
+ pseudo_bit_t reserved11[0x003e0];
+/* -------------- */
+ pseudo_bit_t num_cqovf[0x00020]; /* Number of CQ overflows.
+ Incremented each time a completion is discarded due CQ overflow. */
+/* -------------- */
+ pseudo_bit_t num_eqovf[0x00020]; /* Number of EQ overflows.
+ Incremented each time EQ enters the overflow state. */
+/* -------------- */
+ pseudo_bit_t num_baddb[0x00020]; /* Number of bad doorbells.
+ Doorbell dropped due to UAR violation or bad resource state. */
+/* -------------- */
+ pseudo_bit_t reserved12[0x002a0];
+/* -------------- */
+};
+
+/* Event_data Field - HCR Completion Event */
+
+struct tavorprm_hcr_completion_event_st { /* Little Endian */
+ pseudo_bit_t token[0x00010]; /* HCR Token */
+ pseudo_bit_t reserved0[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t status[0x00008]; /* HCR Status */
+ pseudo_bit_t reserved2[0x00018];
+/* -------------- */
+ pseudo_bit_t out_param_h[0x00020]; /* HCR Output Parameter [63:32] */
+/* -------------- */
+ pseudo_bit_t out_param_l[0x00020]; /* HCR Output Parameter [31:0] */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+};
+
+/* Completion with Error CQE */
+
+struct tavorprm_completion_with_error_st { /* Little Endian */
+ pseudo_bit_t myqpn[0x00018]; /* Indicates the QP for which completion is being reported */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00060];
+/* -------------- */
+ pseudo_bit_t db_cnt[0x00010]; /* Doorbell count */
+ pseudo_bit_t reserved2[0x00008];
+ pseudo_bit_t syndrome[0x00008]; /* Completion with error syndrome:
+ 0x01 - Local Length Error
+ 0x02 - Local QP Operation Error
+ 0x03 - Local EE Context Operation Error
+ 0x04 - Local Protection Error
+ 0x05 - Work Request Flushed Error
+ 0x06 - Memory Window Bind Error
+ 0x10 - Bad Response Error
+ 0x11 - Local Access Error
+ 0x12 - Remote Invalid Request Error
+ 0x13 - Remote Access Error
+ 0x14 - Remote Operation Error
+ 0x15 - Transport Retry Counter Exceeded
+ 0x16 - RNR Retry Counter Exceeded
+ 0x20 - Local RDD Violation Error
+ 0x21 - Remote Invalid RD Request
+ 0x22 - Remote Aborted Error
+ 0x23 - Invalid EE Context Number
+ 0x24 - Invalid EE Context State
+ other - Reserved
+ Syndrome is defined according to the IB specification volume 1. For detailed explanation of the syndromes, refer to chapters 10-11 of the IB specification rev 1.1. */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t wqe_size[0x00006]; /* Size (in 16-byte chunks) of WQE completion is reported for */
+ pseudo_bit_t wqe_addr[0x0001a]; /* Bits 31:6 of WQE virtual address completion is reported for. The 6 least significant bits are zero. */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00007];
+ pseudo_bit_t owner[0x00001]; /* Owner field. Zero value of this field means SW ownership of CQE. */
+ pseudo_bit_t reserved5[0x00010];
+ pseudo_bit_t opcode[0x00008]; /* The opcode of WQE completion is reported for.
+
+ The following values are reported in case of completion with error:
+ 0xFE - For completion with error on Receive Queues
+ 0xFF - For completion with error on Send Queues */
+/* -------------- */
+};
+
+/* Resize CQ Input Mailbox */
+
+struct tavorprm_resize_cq_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t start_addr_h[0x00020]; /* Start address of CQ[63:32].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t start_addr_l[0x00020]; /* Start address of CQ[31:0].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00018];
+ pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries) */
+ pseudo_bit_t reserved2[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+ pseudo_bit_t l_key[0x00020]; /* Memory key (L_Key) to be used to access CQ */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00100];
+/* -------------- */
+};
+
+/* SYS_EN Output Parameter */
+
+struct tavorprm_sys_en_out_param_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t spd[0x00001]; /* 0 - DIMM SPD was read from DIMM
+ 1 - DIMM SPD was read from InfiniHost NVMEM */
+ pseudo_bit_t sladr[0x00003]; /* SPD Slave Address 3 LSBits.
+ Valid only if spd bit is 0. */
+ pseudo_bit_t sock_num[0x00002]; /* DIMM socket number (for double sided DIMM one of the two numbers will be reported) */
+ pseudo_bit_t syn[0x00004]; /* Error Syndrome
+ 0 - reserved
+ 1 - SPD error (e.g. checksum error, no response, error while reading)
+ 2 - DIMM out of bounds (e.g. DIMM rows number is not between 7 and 14, DIMM type is not 2)
+ 3 - DIMM conflict (e.g. mix of registered and unbuffered DIMMs, CAS latency conflict)
+ 4 - Calibration error
+ 5 - reserved
+ 6- DDR Memory check failed
+ other - Error, reserved */
+ pseudo_bit_t reserved1[0x00016];
+/* -------------- */
+};
+
+/* Query Debug Message */
+
+struct tavorprm_query_debug_msg_st { /* Little Endian */
+ pseudo_bit_t base_addr_h[0x00020]; /* Debug Buffers Base Address [63:32] */
+/* -------------- */
+ pseudo_bit_t base_addr_l[0x00020]; /* Debug Buffers Base Address [31:0] */
+/* -------------- */
+ pseudo_bit_t buf_sz[0x00020]; /* Debug Buffer Size (in bytes) */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t trc_hdr_sz[0x00020]; /* Trace message header size in dwords. */
+/* -------------- */
+ pseudo_bit_t trc_arg_num[0x00020]; /* The number of arguments per trace message. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x000c0];
+/* -------------- */
+ pseudo_bit_t dbg_msk_h[0x00020]; /* Debug messages mask [63:32] */
+/* -------------- */
+ pseudo_bit_t dbg_msk_l[0x00020]; /* Debug messages mask [31:0] */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t fs_base_addr0_h[0x00020];/* Base address for format string for irisc 0 bits[63:32] */
+/* -------------- */
+ pseudo_bit_t fs_base_addr0_l[0x00020];/* Base address for format string for irisc 0 bits[31:0] */
+/* -------------- */
+ pseudo_bit_t fs_base_addr1_h[0x00020];/* Base address for format string for irisc 1 bits[63:32] */
+/* -------------- */
+ pseudo_bit_t fs_base_addr1_l[0x00020];/* Base address for format string for irisc 1 bits[31:0] */
+/* -------------- */
+ pseudo_bit_t fs_base_addr2_h[0x00020];/* Base address for format string for irisc 2 bits[63:32] */
+/* -------------- */
+ pseudo_bit_t fs_base_addr2_l[0x00020];/* Base address for format string for irisc 2 bits[31:0] */
+/* -------------- */
+ pseudo_bit_t fs_base_addr3_h[0x00020];/* Base address for format string for irisc 3 bits[63:32] */
+/* -------------- */
+ pseudo_bit_t fs_base_addr3_l[0x00020];/* Base address for format string for irisc 3 bits[31:0] */
+/* -------------- */
+ pseudo_bit_t fs_base_addr4_h[0x00020];/* Base address for format string for irisc 4 bits[63:32] */
+/* -------------- */
+ pseudo_bit_t fs_base_addr4_l[0x00020];/* Base address for format string for irisc 4 bits[31:0] */
+/* -------------- */
+ pseudo_bit_t fs_base_addr5_h[0x00020];/* Base address for format string for irisc 5 bits[63:32] */
+/* -------------- */
+ pseudo_bit_t fs_base_addr5_l[0x00020];/* Base address for format string for irisc 5 bits[31:0] */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00480];
+/* -------------- */
+};
+
+/* User Access Region */
+
+struct tavorprm_uar_st { /* Little Endian */
+ struct tavorprm_rd_send_doorbell_st rd_send_doorbell;/* Reliable Datagram SQ Doorbell */
+/* -------------- */
+ struct tavorprm_send_doorbell_st send_doorbell;/* SQ Doorbell */
+/* -------------- */
+ struct tavorprm_receive_doorbell_st receive_doorbell;/* RQ Doorbell */
+/* -------------- */
+ struct tavorprm_cq_cmd_doorbell_st cq_command_doorbell;/* CQ Doorbell */
+/* -------------- */
+ struct tavorprm_eq_cmd_doorbell_st eq_command_doorbell;/* EQ Doorbell */
+/* -------------- */
+ pseudo_bit_t reserved0[0x01e80];
+/* -------------- */
+ pseudo_bit_t infini_blast[256][0x00020];/* InfiniBlast buffer (same format as WQE format)
+ Infiniblast is not supported by InfiniHost MT23108 */
+/* -------------- */
+};
+
+/* SET_IB Parameters */
+
+struct tavorprm_set_ib_st { /* Little Endian */
+ pseudo_bit_t rqk[0x00001]; /* Reset QKey Violation Counter */
+ pseudo_bit_t reserved0[0x00011];
+ pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified.
+ system_image_guid and sig must be the same for all ports. */
+ pseudo_bit_t reserved1[0x0000d];
+/* -------------- */
+ pseudo_bit_t capability_mask[0x00020];/* PortInfo Capability Mask */
+/* -------------- */
+ pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00180];
+/* -------------- */
+};
+
+/* Multicast Group Member */
+
+struct tavorprm_mgm_entry_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00006];
+ pseudo_bit_t next_gid_index[0x0001a];/* Index of next Multicast Group Member whose GID maps to same MGID_HASH number.
+ The index is into the Multicast Group Table, which is the comprised the MGHT and AMGM tables.
+ next_gid_index=0 means end of the chain. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00060];
+/* -------------- */
+ pseudo_bit_t mgid_128_96[0x00020]; /* Multicast group GID[128:96] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_95_64[0x00020]; /* Multicast group GID[95:64] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_63_32[0x00020]; /* Multicast group GID[63:32] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_31_0[0x00020]; /* Multicast group GID[31:0] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ struct tavorprm_mgmqp_st mgmqp_0; /* Multicast Group Member QP */
+/* -------------- */
+ struct tavorprm_mgmqp_st mgmqp_1; /* Multicast Group Member QP */
+/* -------------- */
+ struct tavorprm_mgmqp_st mgmqp_2; /* Multicast Group Member QP */
+/* -------------- */
+ struct tavorprm_mgmqp_st mgmqp_3; /* Multicast Group Member QP */
+/* -------------- */
+ struct tavorprm_mgmqp_st mgmqp_4; /* Multicast Group Member QP */
+/* -------------- */
+ struct tavorprm_mgmqp_st mgmqp_5; /* Multicast Group Member QP */
+/* -------------- */
+ struct tavorprm_mgmqp_st mgmqp_6; /* Multicast Group Member QP */
+/* -------------- */
+ struct tavorprm_mgmqp_st mgmqp_7; /* Multicast Group Member QP */
+/* -------------- */
+};
+
+/* INIT_IB Parameters */
+
+struct tavorprm_init_ib_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00004];
+ pseudo_bit_t vl_cap[0x00004]; /* Maximum VLs supported on the port, excluding VL15.
+ Legal values are 1,2,4 and 8. */
+ pseudo_bit_t port_width_cap[0x00004];/* IB Port Width
+ 1 - 1x
+ 3 - 1x, 4x
+ 11 - 1x, 4x or 12x (must not be used in InfiniHost MT23108)
+ else - Reserved */
+ pseudo_bit_t mtu_cap[0x00004]; /* Maximum MTU Supported
+ 0x0 - Reserved
+ 0x1 - 256
+ 0x2 - 512
+ 0x3 - 1024
+ 0x4 - 2048
+ 0x5 - 0xF Reserved */
+ pseudo_bit_t g0[0x00001]; /* Set port GUID0 to GUID0 specified */
+ pseudo_bit_t ng[0x00001]; /* Set node GUID to node_guid specified.
+ node_guid and ng must be the same for all ports. */
+ pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified.
+ system_image_guid and sig must be the same for all ports. */
+ pseudo_bit_t reserved1[0x0000d];
+/* -------------- */
+ pseudo_bit_t max_gid[0x00010]; /* Maximum number of GIDs for the port */
+ pseudo_bit_t reserved2[0x00010];
+/* -------------- */
+ pseudo_bit_t max_pkey[0x00010]; /* Maximum pkeys for the port.
+ Must be the same for both ports. */
+ pseudo_bit_t reserved3[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t guid0_h[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 63:32) */
+/* -------------- */
+ pseudo_bit_t guid0_l[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 31:0) */
+/* -------------- */
+ pseudo_bit_t node_guid_h[0x00020]; /* Node GUID[63:32], takes effect only if the NG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t node_guid_l[0x00020]; /* Node GUID[31:0], takes effect only if the NG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x006c0];
+/* -------------- */
+};
+
+/* Query Device Limitations */
+
+struct tavorprm_query_dev_lim_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t log_max_qp[0x00005]; /* Log2 of the Maximum number of QPs supported */
+ pseudo_bit_t reserved1[0x00003];
+ pseudo_bit_t log2_rsvd_qps[0x00004];/* Log (base 2) of the number of QPs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_qps-1 */
+ pseudo_bit_t reserved2[0x00004];
+ pseudo_bit_t log_max_qp_sz[0x00008];/* The maximum number of WQEs allowed on the RQ or the SQ is 2^log_max_qp_sz-1 */
+ pseudo_bit_t log_max_srq_sz[0x00008];/* The maximum number of WQEs allowed on the SRQ is 2^log_max_srq_sz-1 */
+/* -------------- */
+ pseudo_bit_t log_max_ee[0x00005]; /* Log2 of the Maximum number of EE contexts supported */
+ pseudo_bit_t reserved3[0x00003];
+ pseudo_bit_t log2_rsvd_ees[0x00004];/* Log (base 2) of the number of EECs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_ees-1 */
+ pseudo_bit_t reserved4[0x00004];
+ pseudo_bit_t log_max_srqs[0x00005]; /* Log base 2 of the maximum number of SRQs supported, valid only if SRQ bit is set.
+ */
+ pseudo_bit_t reserved5[0x00007];
+ pseudo_bit_t log2_rsvd_srqs[0x00004];/* Log (base 2) of the number of reserved SRQs for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_srqs-1
+ This parameter is valid only if the SRQ bit is set. */
+/* -------------- */
+ pseudo_bit_t log_max_cq[0x00005]; /* Log2 of the Maximum number of CQs supported */
+ pseudo_bit_t reserved6[0x00003];
+ pseudo_bit_t log2_rsvd_cqs[0x00004];/* Log (base 2) of the number of CQs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsrvd_cqs-1 */
+ pseudo_bit_t reserved7[0x00004];
+ pseudo_bit_t log_max_cq_sz[0x00008];/* Log2 of the Maximum CQEs allowed in a CQ */
+ pseudo_bit_t reserved8[0x00008];
+/* -------------- */
+ pseudo_bit_t log_max_eq[0x00003]; /* Log2 of the Maximum number of EQs */
+ pseudo_bit_t reserved9[0x00005];
+ pseudo_bit_t num_rsvd_eqs[0x00004]; /* The number of EQs reserved for firmware use
+ The reserved resources are numbered from 0 to num_rsvd_eqs-1
+ If 0 - no resources are reserved. */
+ pseudo_bit_t reserved10[0x00004];
+ pseudo_bit_t log_max_mpts[0x00006]; /* Log (base 2) of the maximum number of MPT entries (the number of Regions/Windows) */
+ pseudo_bit_t reserved11[0x0000a];
+/* -------------- */
+ pseudo_bit_t log_max_mtt_seg[0x00006];/* Log2 of the Maximum number of MTT segments */
+ pseudo_bit_t reserved12[0x00002];
+ pseudo_bit_t log2_rsvd_mrws[0x00004];/* Log (base 2) of the number of MPTs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_mrws-1 */
+ pseudo_bit_t reserved13[0x00004];
+ pseudo_bit_t log_max_mrw_sz[0x00008];/* Log2 of the Maximum Size of Memory Region/Window */
+ pseudo_bit_t reserved14[0x00004];
+ pseudo_bit_t log2_rsvd_mtts[0x00004];/* Log (base 2) of the number of MTT segments reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_mtts-1
+ */
+/* -------------- */
+ pseudo_bit_t log_max_av[0x00006]; /* Log2 of the Maximum number of Address Vectors */
+ pseudo_bit_t reserved15[0x0001a];
+/* -------------- */
+ pseudo_bit_t log_max_ra_res_qp[0x00006];/* Log2 of the Maximum number of outstanding RDMA read/Atomic per QP as a responder */
+ pseudo_bit_t reserved16[0x0000a];
+ pseudo_bit_t log_max_ra_req_qp[0x00006];/* Log2 of the maximum number of outstanding RDMA read/Atomic per QP as a requester */
+ pseudo_bit_t reserved17[0x0000a];
+/* -------------- */
+ pseudo_bit_t log_max_ra_res_global[0x00006];/* Log2 of the maximum number of RDMA read/atomic operations the HCA responder can support globally. That implies the RDB table size. */
+ pseudo_bit_t reserved18[0x0001a];
+/* -------------- */
+ pseudo_bit_t reserved19[0x00020];
+/* -------------- */
+ pseudo_bit_t num_ports[0x00004]; /* Number of IB ports. */
+ pseudo_bit_t max_vl[0x00004]; /* Maximum VLs supported on each port, excluding VL15 */
+ pseudo_bit_t max_port_width[0x00004];/* IB Port Width
+ 1 - 1x
+ 3 - 1x, 4x
+ 11 - 1x, 4x or 12x
+ else - Reserved */
+ pseudo_bit_t max_mtu[0x00004]; /* Maximum MTU Supported
+ 0x0 - Reserved
+ 0x1 - 256
+ 0x2 - 512
+ 0x3 - 1024
+ 0x4 - 2048
+ 0x5 - 0xF Reserved */
+ pseudo_bit_t local_ca_ack_delay[0x00005];/* The Local CA ACK Delay. This is the value recommended to be returned in Query HCA verb.
+ The delay value in microseconds is computed using 4.096us * 2^(Local_CA_ACK_Delay). */
+ pseudo_bit_t reserved20[0x0000b];
+/* -------------- */
+ pseudo_bit_t log_max_gid[0x00004]; /* Log2 of the maximum number of GIDs per port */
+ pseudo_bit_t reserved21[0x0001c];
+/* -------------- */
+ pseudo_bit_t log_max_pkey[0x00004]; /* Log2 of the max PKey Table Size (per IB port) */
+ pseudo_bit_t reserved22[0x0001c];
+/* -------------- */
+ pseudo_bit_t reserved23[0x00020];
+/* -------------- */
+ pseudo_bit_t rc[0x00001]; /* RC Transport supported */
+ pseudo_bit_t uc[0x00001]; /* UC Transport Supported */
+ pseudo_bit_t ud[0x00001]; /* UD Transport Supported */
+ pseudo_bit_t rd[0x00001]; /* RD Transport Supported
+ RD is not supported in InfiniHost MT23108 */
+ pseudo_bit_t raw_ipv6[0x00001]; /* Raw IPv6 Transport Supported */
+ pseudo_bit_t raw_ether[0x00001]; /* Raw Ethertype Transport Supported */
+ pseudo_bit_t srq[0x00001]; /* SRQ is supported
+ */
+ pseudo_bit_t reserved24[0x00001];
+ pseudo_bit_t pkv[0x00001]; /* PKey Violation Counter Supported */
+ pseudo_bit_t qkv[0x00001]; /* QKey Violation Coutner Supported */
+ pseudo_bit_t reserved25[0x00006];
+ pseudo_bit_t mw[0x00001]; /* Memory windows supported */
+ pseudo_bit_t apm[0x00001]; /* Automatic Path Migration Supported */
+ pseudo_bit_t atm[0x00001]; /* Atomic operations supported (atomicity is guaranteed between QPs on this HCA) */
+ pseudo_bit_t rm[0x00001]; /* Raw Multicast Supported */
+ pseudo_bit_t avp[0x00001]; /* Address Vector Port checking supported */
+ pseudo_bit_t udm[0x00001]; /* UD Multicast Supported */
+ pseudo_bit_t reserved26[0x00002];
+ pseudo_bit_t pg[0x00001]; /* Paging on demand supported */
+ pseudo_bit_t r[0x00001]; /* Router mode supported */
+ pseudo_bit_t reserved27[0x00006];
+/* -------------- */
+ pseudo_bit_t log_pg_sz[0x00008]; /* Minimum system page size supported (log2) .
+ For proper operation it must be less than or equal the hosting platform (CPU) minimum page size. */
+ pseudo_bit_t reserved28[0x00008];
+ pseudo_bit_t uar_sz[0x00006]; /* UAR Area Size = 1MB * 2^uar_sz */
+ pseudo_bit_t reserved29[0x00006];
+ pseudo_bit_t num_rsvd_uars[0x00004];/* The number of UARs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_uars-1
+ Note that UAR 1 is always for the kernel
+ If 0 - no resources are reserved. */
+/* -------------- */
+ pseudo_bit_t reserved30[0x00020];
+/* -------------- */
+ pseudo_bit_t max_desc_sz[0x00010]; /* Max descriptor size in bytes */
+ pseudo_bit_t max_sg[0x00008]; /* The maximum S/G list elements in a WQE (max_desc_sz/16 - 3) */
+ pseudo_bit_t reserved31[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved32[0x00060];
+/* -------------- */
+ pseudo_bit_t log_max_mcg[0x00008]; /* Log2 of the maximum number of multicast groups */
+ pseudo_bit_t num_rsvd_mcgs[0x00004];/* The number of MGMs reserved for firmware use in the MGHT.
+ The reserved resources are numbered from 0 to num_reserved_mcgs-1
+ If 0 - no resources are reserved. */
+ pseudo_bit_t reserved33[0x00004];
+ pseudo_bit_t log_max_qp_mcg[0x00008];/* Log2 of the maximum number of QPs per multicast group */
+ pseudo_bit_t reserved34[0x00008];
+/* -------------- */
+ pseudo_bit_t log_max_rdds[0x00006]; /* Log2 of the maximum number of RDDs */
+ pseudo_bit_t reserved35[0x00006];
+ pseudo_bit_t num_rsvd_rdds[0x00004];/* The number of RDDs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_rdds-1.
+ If 0 - no resources are reserved. */
+ pseudo_bit_t log_max_pd[0x00006]; /* Log2 of the maximum number of PDs */
+ pseudo_bit_t reserved36[0x00006];
+ pseudo_bit_t num_rsvd_pds[0x00004]; /* The number of PDs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_pds-1
+ If 0 - no resources are reserved. */
+/* -------------- */
+ pseudo_bit_t reserved37[0x000c0];
+/* -------------- */
+ pseudo_bit_t qpc_entry_sz[0x00010]; /* QPC Entry Size for the device
+ For the InfiniHost MT23108 entry size is 256 bytes */
+ pseudo_bit_t eec_entry_sz[0x00010]; /* EEC Entry Size for the device
+ For the InfiniHost MT23108 entry size is 256 bytes */
+/* -------------- */
+ pseudo_bit_t eqpc_entry_sz[0x00010];/* Extended QPC entry size for the device
+ For the InfiniHost MT23108 entry size is 32 bytes */
+ pseudo_bit_t eeec_entry_sz[0x00010];/* Extended EEC entry size for the device
+ For the InfiniHost MT23108 entry size is 32 bytes */
+/* -------------- */
+ pseudo_bit_t cqc_entry_sz[0x00010]; /* CQC entry size for the device
+ For the InfiniHost MT23108 entry size is 64 bytes */
+ pseudo_bit_t eqc_entry_sz[0x00010]; /* EQ context entry size for the device
+ For the InfiniHost MT23108 entry size is 64 bytes */
+/* -------------- */
+ pseudo_bit_t uar_scratch_entry_sz[0x00010];/* UAR Scratchpad Entry Size
+ For the InfiniHost MT23108 entry size is 32 bytes */
+ pseudo_bit_t srq_entry_sz[0x00010]; /* SRQ context entry size for the device
+ For the InfiniHost MT23108 entry size is 32 bytes */
+/* -------------- */
+ pseudo_bit_t reserved38[0x00380];
+/* -------------- */
+};
+
+/* QUERY_ADAPTER Parameters Block */
+
+struct tavorprm_query_adapter_st { /* Little Endian */
+ pseudo_bit_t vendor_id[0x00020]; /* Adapter vendor ID */
+/* -------------- */
+ pseudo_bit_t device_id[0x00020]; /* Adapter Device ID */
+/* -------------- */
+ pseudo_bit_t revision_id[0x00020]; /* Adapter Revision ID */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00018];
+ pseudo_bit_t intapin[0x00008]; /* Interrupt Signal ID of HCA device pin that is connected to the INTA trace in the HCA board.
+ 0..39 and 63 are valid values
+ 255 means INTA trace in board is not connected to the HCA device.
+ All other values are reserved */
+/* -------------- */
+ pseudo_bit_t mode_pci[0x00001]; /* Set when the device is operating in conventional PCI mode (as opposed to PCI-X/PCI-Express). */
+ pseudo_bit_t mode_32bit[0x00001]; /* Set when the device is operating in 32 bit mode (the sampled bus width is 32 bit). */
+ pseudo_bit_t reserved2[0x0001e];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00040];
+/* -------------- */
+ struct tavorprm_vsd_st vsd;
+/* -------------- */
+};
+
+/* QUERY_FW Parameters Block */
+
+struct tavorprm_query_fw_st { /* Little Endian */
+ pseudo_bit_t fw_rev_major[0x00010]; /* Firmware Revision - Major */
+ pseudo_bit_t reserved0[0x00010];
+/* -------------- */
+ pseudo_bit_t fw_rev_minor[0x00010]; /* Firmware Revision - Minor */
+ pseudo_bit_t fw_rev_subminor[0x00010];/* Firmware Sub-minor version (Patch level). */
+/* -------------- */
+ pseudo_bit_t cmd_interface_rev[0x00010];/* Command Interface Interpreter Revision ID */
+ pseudo_bit_t reserved1[0x00010];
+/* -------------- */
+ pseudo_bit_t log_max_outstanding_cmd[0x00008];/* Log2 of the maximum number of commands the HCR can support simultaneously */
+ pseudo_bit_t reserved2[0x00017];
+ pseudo_bit_t dt[0x00001]; /* Debug Trace Support
+ 0 - Debug trace is not supported
+ 1 - Debug trace is supported */
+/* -------------- */
+ pseudo_bit_t cmd_interface_db[0x00001];/* Set if the device accepts commands by means of special doorbells. */
+ pseudo_bit_t reserved3[0x0001f];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00060];
+/* -------------- */
+ pseudo_bit_t fw_base_addr_h[0x00020];/* Physical Address of Firmware Area in DDR Memory [63:32] */
+/* -------------- */
+ pseudo_bit_t fw_base_addr_l[0x00020];/* Physical Address of Firmware Area in DDR Memory [31:0] */
+/* -------------- */
+ pseudo_bit_t fw_end_addr_h[0x00020];/* End of firmware address in DDR memory [63:32] */
+/* -------------- */
+ pseudo_bit_t fw_end_addr_l[0x00020];/* End of firmware address in DDR memory [31:0] */
+/* -------------- */
+ pseudo_bit_t error_buf_start_h[0x00020];/* Read Only buffer for catastrofic error reports. */
+/* -------------- */
+ pseudo_bit_t error_buf_start_l[0x00020];
+/* -------------- */
+ pseudo_bit_t error_buf_size[0x00020];/* Size in words */
+/* -------------- */
+ pseudo_bit_t reserved5[0x000a0];
+/* -------------- */
+ pseudo_bit_t cmd_db_dw1[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 1 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw0[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 0 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_dw3[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 3 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw2[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 2 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_dw5[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 5 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw4[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 4 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_dw7[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 7 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw6[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 6 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_addr_base_h[0x00020];/* High bits of cmd_db_addr_base, which cmd_db_dw offsets refer to. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_addr_base_l[0x00020];/* Low bits of cmd_db_addr_base, which cmd_db_dw offsets refer to. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t reserved6[0x004c0];
+/* -------------- */
+};
+
+/* ACCESS_DDR */
+
+struct tavorprm_access_ddr_st { /* Little Endian */
+ struct tavorprm_access_ddr_inject_errors_st access_ddr_inject_errors;
+/* -------------- */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+};
+
+/* QUERY_DDR Parameters Block */
+
+struct tavorprm_query_ddr_st { /* Little Endian */
+ pseudo_bit_t ddr_start_adr_h[0x00020];/* DDR memory start address [63:32] */
+/* -------------- */
+ pseudo_bit_t ddr_start_adr_l[0x00020];/* DDR memory start address [31:0] */
+/* -------------- */
+ pseudo_bit_t ddr_end_adr_h[0x00020];/* DDR memory end address [63:32] */
+/* -------------- */
+ pseudo_bit_t ddr_end_adr_l[0x00020];/* DDR memory end address [31:0] */
+/* -------------- */
+ pseudo_bit_t di[0x00002]; /* Data Integrity Configuration:
+ 00 - none
+ 01 - Parity
+ 10 - ECC Detection Only
+ 11 - ECC With Correction */
+ pseudo_bit_t ap[0x00002]; /* Auto Precharge Mode
+ 00 - No auto precharge
+ 01 - Auto precharge per transaction
+ 10 - Auto precharge per 64 bytes
+ 11 - reserved */
+ pseudo_bit_t dh[0x00001]; /* When set, DDR is Hidden and can not be accessed from the PCI bus. */
+ pseudo_bit_t reserved0[0x0001b];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00160];
+/* -------------- */
+ struct tavorprm_dimminfo_st dimm0; /* Logical DIMM 0 Parameters */
+/* -------------- */
+ struct tavorprm_dimminfo_st dimm1; /* Logical DIMM 1 Parameters */
+/* -------------- */
+ struct tavorprm_dimminfo_st dimm2; /* Logical DIMM 2 Parameters */
+/* -------------- */
+ struct tavorprm_dimminfo_st dimm3; /* Logical DIMM 3 Parameters */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00200];
+/* -------------- */
+};
+
+/* INIT_HCA & QUERY_HCA Parameters Block */
+
+struct tavorprm_init_hca_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00060];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00018];
+ pseudo_bit_t hca_core_clock[0x00008];/* Internal Clock Period (in units of 1/16 ns) (QUERY_HCA only) */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00008];
+ pseudo_bit_t router_qp[0x00010]; /* Upper 16 bit to be used as a QP number for router mode. Low order 8 bits are taken from the TClass field of the incoming packet.
+ Valid only if RE bit is set */
+ pseudo_bit_t reserved3[0x00007];
+ pseudo_bit_t re[0x00001]; /* Router Mode Enable
+ If this bit is set, entire packet (including all headers and ICRC) will be considered as a data payload and will be scattered to memory as specified in the descriptor that is posted on the QP matching the TClass field of packet. */
+/* -------------- */
+ pseudo_bit_t udp[0x00001]; /* UD Port Check Enable
+ 0 - Port field in Address Vector is ignored
+ 1 - HCA will check the port field in AV entry (fetched for UD descriptor) against the Port of the UD QP executing the descriptor. */
+ pseudo_bit_t he[0x00001]; /* Host Endianess - Used for Atomic Operations
+ 0 - Host is Little Endian
+ 1 - Host is Big endian
+ */
+ pseudo_bit_t ud[0x00001]; /* Force UD address vector protection check. If this bit is set, Passing address vector as immediate data in WQE is suppressed and privileged memory key will be used by hardware to access UD address vector table. */
+ pseudo_bit_t reserved4[0x00005];
+ pseudo_bit_t responder_exu[0x00004];/* How many execution engines are dedicated to the responder. Legal values are 0x0-0xF. 0 is "auto" */
+ pseudo_bit_t reserved5[0x00004];
+ pseudo_bit_t wqe_quota[0x0000f]; /* Maximum number of WQEs that are executed prior to preemption of execution unit. 0 - reserved. */
+ pseudo_bit_t wqe_quota_en[0x00001]; /* If set - wqe_quota field is used. If cleared - WQE quota is set to "auto" value */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00040];
+/* -------------- */
+ struct tavorprm_qpcbaseaddr_st qpc_eec_cqc_eqc_rdb_parameters;
+/* -------------- */
+ pseudo_bit_t reserved7[0x00080];
+/* -------------- */
+ struct tavorprm_udavtable_memory_parameters_st udavtable_memory_parameters;/* Memory Access Parameters for UD Address Vector Table. Used for QPs/EEc that are configured to use protected Address Vectors. */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00040];
+/* -------------- */
+ struct tavorprm_multicastparam_st multicast_parameters;
+/* -------------- */
+ pseudo_bit_t reserved9[0x00080];
+/* -------------- */
+ struct tavorprm_tptparams_st tpt_parameters;
+/* -------------- */
+ pseudo_bit_t reserved10[0x00080];
+/* -------------- */
+ struct tavorprm_uar_params_st uar_parameters;/* UAR Parameters */
+/* -------------- */
+ pseudo_bit_t reserved11[0x00600];
+/* -------------- */
+};
+
+/* Event Queue Context Table Entry */
+
+struct tavorprm_eqc_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t st[0x00002]; /* Event delivery state machine
+ 01 - Armed
+ 10 - Fired
+ 11 - Always_Armed (auto-rearm)
+ 00 - Reserved */
+ pseudo_bit_t reserved1[0x00007];
+ pseudo_bit_t oi[0x00001]; /* Ignore overrun on this EQ if this bit is set */
+ pseudo_bit_t tr[0x00001]; /* Translation Required. If set - EQ access undergo address translation. */
+ pseudo_bit_t reserved2[0x00005];
+ pseudo_bit_t owner[0x00004]; /* 0 - SW ownership
+ 1 - HW ownership
+ Valid for the QUERY_EQ and HW2SW_EQ commands only */
+ pseudo_bit_t status[0x00004]; /* EQ status:
+ 0000 - OK
+ 1001 - EQ overflow
+ 1010 - EQ write failure
+ Valid for the QUERY_EQ and HW2SW_EQ commands only */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start Address of Event Queue[63:32].
+ Must be aligned on 32-byte boundary */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start Address of Event Queue[31:0].
+ Must be aligned on 32-byte boundary */
+/* -------------- */
+ pseudo_bit_t usr_page[0x00018];
+ pseudo_bit_t log_eq_size[0x00005]; /* Amount of entries in this EQ is 2^log_eq_size.
+ Log_eq_size must be bigger than 1 */
+ pseudo_bit_t reserved3[0x00003];
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* PD to be used to access EQ */
+ pseudo_bit_t reserved4[0x00008];
+/* -------------- */
+ pseudo_bit_t intr[0x00008]; /* Interrupt (message) to be generated to report event to INT layer.
+ 00iiiiii - specifies GPIO pin to be asserted (according to INTA given in QUERY_ADAPTER)
+ 10jjjjjj - specificies type of interrupt message to be generated (total 64 different messages supported).
+
+ If interrupt generation is not required one of the two following options should be set:
+ 1. ST must be set on creation to Fired state and not EQ arming doorbell should be performed. In this case hardware will not generate any interrupt.
+ 2. intr should be set to 60 decimal
+ */
+ pseudo_bit_t reserved5[0x00018];
+/* -------------- */
+ pseudo_bit_t lost_count[0x00020]; /* Number of events lost due to EQ overrun */
+/* -------------- */
+ pseudo_bit_t lkey[0x00020]; /* Memory key (L-Key) to be used to access EQ */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00040];
+/* -------------- */
+ pseudo_bit_t consumer_indx[0x00020];/* Contains next entry to be read upon polling the event queue.
+ Must be initalized to '0 while opening EQ */
+/* -------------- */
+ pseudo_bit_t producer_indx[0x00020];/* Contains next entry in EQ to be written by the HCA.
+ Must be initalized to '0 while opening EQ. */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00080];
+/* -------------- */
+};
+
+/* Memory Translation Table (MTT) Entry */
+
+struct tavorprm_mtt_st { /* Little Endian */
+ pseudo_bit_t ptag_h[0x00020]; /* High-order bits of physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */
+/* -------------- */
+ pseudo_bit_t p[0x00001]; /* Present bit. If set, page entry is valid. If cleared, access to this page will generate 'non-present page access fault'. */
+ pseudo_bit_t reserved0[0x0000b];
+ pseudo_bit_t ptag_l[0x00014]; /* Low-order bits of Physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */
+/* -------------- */
+};
+
+/* Memory Protection Table (MPT) Entry */
+
+struct tavorprm_mpt_st { /* Little Endian */
+ pseudo_bit_t ver[0x00004]; /* Version. Must be zero for InfiniHost */
+ pseudo_bit_t reserved0[0x00004];
+ pseudo_bit_t r_w[0x00001]; /* Defines whether this entry is Region (1) or Window (0) */
+ pseudo_bit_t pa[0x00001]; /* Physical address. If set, no virtual-to-physical address translation will be performed for this region */
+ pseudo_bit_t lr[0x00001]; /* If set - local read access enabled */
+ pseudo_bit_t lw[0x00001]; /* If set - local write access enabled */
+ pseudo_bit_t rr[0x00001]; /* If set - Remote read access enabled. */
+ pseudo_bit_t rw[0x00001]; /* If set - remote write access enabled */
+ pseudo_bit_t a[0x00001]; /* If set - Remote Atomic access is enabled */
+ pseudo_bit_t eb[0x00001]; /* If set - Bind is enabled. Valid for region entry only. */
+ pseudo_bit_t reserved1[0x00001];
+ pseudo_bit_t m_io[0x00001]; /* Memory / I/O
+ 1 - Memory commands used on the uplink bus
+ 0 - I/O commands used on the uplink bus
+ Must be 1 for the InfiniHost MT23108. */
+ pseudo_bit_t reserved2[0x0000a];
+ pseudo_bit_t status[0x00004]; /* Regios/Window Status
+ 0xF - not valid (SW ownership)
+ else - HW ownership
+ Note that an unbound Window is denoted by the reg_wnd_len field equals zero. */
+/* -------------- */
+ pseudo_bit_t page_size[0x00005]; /* Page size used for the region. Actual size is [4K]*2^Page_size bytes.
+ page_size should be less than 20. */
+ pseudo_bit_t reserved3[0x00002];
+ pseudo_bit_t reserved4[0x00001];
+ pseudo_bit_t reserved5[0x00018];
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* The memory Key. This field is compared to key used to access the region/window. Lower-order bits are restricted (index to the table). */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* Protection Domain */
+ pseudo_bit_t reserved6[0x00001];
+ pseudo_bit_t reserved7[0x00001];
+ pseudo_bit_t reserved8[0x00001];
+ pseudo_bit_t reserved9[0x00001];
+ pseudo_bit_t reserved10[0x00001];
+ pseudo_bit_t reserved11[0x00003];
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start Address[63:32] - Virtual Address where this region/window starts */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start Address[31:0] - Virtual Address where this region/window starts */
+/* -------------- */
+ pseudo_bit_t reg_wnd_len_h[0x00020];/* Region/Window Length[63:32] */
+/* -------------- */
+ pseudo_bit_t reg_wnd_len_l[0x00020];/* Region/Window Length[31:0] */
+/* -------------- */
+ pseudo_bit_t lkey[0x00020]; /* Must be 0 for SW2HW_MPT.
+ On QUERY_MPT and HW2SW_MPT commands for Memory Window it reflects the LKey of the Region that the Window is bound to. */
+/* -------------- */
+ pseudo_bit_t win_cnt[0x00020]; /* Number of windows bound to this region. Valid for regions only.
+ The field is valid only for the QUERY_MPT and HW2SW_MPT commands. */
+/* -------------- */
+ pseudo_bit_t win_cnt_limit[0x00020];/* The number of windows (limit) that can be bound to this region. If a bind operation is attempted when WIN_CNT == WIN_CNT_LIMIT, the operation will be aborted, a CQE with error will be generated, and the QP will be moved into the error state.
+ Zero means no limit.
+ Note that for best hardware performance, win_cnt_limit should be set to zero. */
+/* -------------- */
+ pseudo_bit_t mtt_seg_adr_h[0x00020];/* Base (first) address of the MTT segment, aligned on segment_size boundary (bits 63:31). */
+/* -------------- */
+ pseudo_bit_t reserved12[0x00006];
+ pseudo_bit_t mtt_seg_adr_l[0x0001a];/* Base (first) address of the MTT segment, aligned on segment_size boundary (bits 31:6). */
+/* -------------- */
+ pseudo_bit_t reserved13[0x00060];
+/* -------------- */
+};
+
+/* Completion Queue Context Table Entry */
+
+struct tavorprm_completion_queue_context_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t st[0x00004]; /* Event delivery state machine
+ 0x0 - DISARMED
+ 0x1 - ARMED (Request for Notification)
+ 0x4 - ARMED SOLICITED (Request Solicited Notification)
+ 0xA - FIRED
+ other - reserved */
+ pseudo_bit_t reserved1[0x00005];
+ pseudo_bit_t oi[0x00001]; /* Ignore overrun of this CQ if this bit is set */
+ pseudo_bit_t tr[0x00001]; /* Translation Required
+ 1 - accesses to CQ will undergo address translation
+ 0 - accesses to CQ will not undergo address translation */
+ pseudo_bit_t reserved2[0x00009];
+ pseudo_bit_t status[0x00004]; /* CQ status
+ 0000 - OK
+ 1001 - CQ overflow
+ 1010 - CQ write failure
+ Valid for the QUERY_CQ and HW2SW_CQ commands only */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start address of CQ[63:32].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start address of CQ[31:0].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t usr_page[0x00018]; /* UAR page this CQ can be accessed through (ringinig CQ doorbells) */
+ pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries).
+ Maximum CQ size is 128K CQEs (max log_cq_size is 17) */
+ pseudo_bit_t reserved3[0x00003];
+/* -------------- */
+ pseudo_bit_t e_eqn[0x00008]; /* Event Queue this CQ reports errors to (e.g. CQ overflow)
+ Valid values are 0 to 63
+ If configured to value other than 0-63, error events will not be reported on the CQ. */
+ pseudo_bit_t reserved4[0x00018];
+/* -------------- */
+ pseudo_bit_t c_eqn[0x00008]; /* Event Queue this CQ reports completion events to.
+ Valid values are 0 to 63
+ If configured to value other than 0-63, completion events will not be reported on the CQ. */
+ pseudo_bit_t reserved5[0x00018];
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* Protection Domain to be used to access CQ.
+ Must be the same PD of the CQ L_Key. */
+ pseudo_bit_t reserved6[0x00008];
+/* -------------- */
+ pseudo_bit_t l_key[0x00020]; /* Memory key (L_Key) to be used to access CQ */
+/* -------------- */
+ pseudo_bit_t last_notified_indx[0x00020];/* Maintained by HW.
+ Valid for QUERY_CQ and HW2SW_CQ commands only. */
+/* -------------- */
+ pseudo_bit_t solicit_producer_indx[0x00020];/* Maintained by HW.
+ Valid for QUERY_CQ and HW2SW_CQ commands only.
+ */
+/* -------------- */
+ pseudo_bit_t consumer_indx[0x00020];/* Contains index to the next entry to be read upon poll for completion. The first completion after passing ownership of CQ from software to hardware will be reported to value passed in this field. Only the low log_cq_size bits may be non-zero. */
+/* -------------- */
+ pseudo_bit_t producer_indx[0x00020];/* Points to the next entry to be written to by Hardware. CQ overrun is reported if Producer_indx + 1 equals to Consumer_indx.
+ Maintained by HW (valid for the QUERY_CQ and HW2SW_CQ commands only) */
+/* -------------- */
+ pseudo_bit_t cqn[0x00018]; /* CQ number. Least significant bits are constrained by the position of this CQ in CQC table
+ Valid for the QUERY_CQ and HW2SW_CQ commands only */
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved8[0x00060];
+/* -------------- */
+};
+
+/* UD Address Vector */
+
+struct tavorprm_ud_address_vector_st { /* Little Endian */
+ pseudo_bit_t pd[0x00018]; /* Protection Domain */
+ pseudo_bit_t port_number[0x00002]; /* Port number
+ 1 - Port 1
+ 2 - Port 2
+ other - reserved */
+ pseudo_bit_t reserved0[0x00006];
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */
+ pseudo_bit_t my_lid_path_bits[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */
+ pseudo_bit_t g[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */
+ pseudo_bit_t max_stat_rate[0x00003];/* Maximum static rate control.
+ 0 - 4X injection rate
+ 1 - 1X injection rate
+ other - reserved
+ */
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t msg[0x00002]; /* Max Message size, size is 256*2^MSG bytes */
+ pseudo_bit_t reserved3[0x00002];
+ pseudo_bit_t mgid_index[0x00006]; /* Index to port GID table
+ mgid_index = (port_number-1) * 2^log_max_gid + gid_index
+ Where:
+ 1. log_max_gid is taken from QUERY_DEV_LIM command
+ 2. gid_index is the index to the GID table */
+ pseudo_bit_t reserved4[0x0000a];
+/* -------------- */
+ pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */
+ pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */
+ pseudo_bit_t sl[0x00004]; /* InfiniBand Service Level (SL) */
+/* -------------- */
+ pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */
+/* -------------- */
+ pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */
+/* -------------- */
+ pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */
+/* -------------- */
+ pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] */
+/* -------------- */
+};
+
+/* GPIO_event_data */
+
+struct tavorprm_gpio_event_data_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00060];
+/* -------------- */
+ pseudo_bit_t gpio_event_hi[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */
+/* -------------- */
+ pseudo_bit_t gpio_event_lo[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+};
+
+/* Event_data Field - QP/EE Events */
+
+struct tavorprm_qp_ee_event_st { /* Little Endian */
+ pseudo_bit_t qpn_een[0x00018]; /* QP/EE/SRQ number event is reported for */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved2[0x0001c];
+ pseudo_bit_t e_q[0x00001]; /* If set - EEN if cleared - QP in the QPN/EEN field
+ Not valid on SRQ events */
+ pseudo_bit_t reserved3[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00060];
+/* -------------- */
+};
+
+/* InfiniHost Type0 Configuration Header */
+
+struct tavorprm_mt23108_type0_st { /* Little Endian */
+ pseudo_bit_t vendor_id[0x00010]; /* Hardwired to 0x15B3 */
+ pseudo_bit_t device_id[0x00010]; /* hardwired to 23108 */
+/* -------------- */
+ pseudo_bit_t command[0x00010]; /* PCI Command Register */
+ pseudo_bit_t status[0x00010]; /* PCI Status Register */
+/* -------------- */
+ pseudo_bit_t revision_id[0x00008];
+ pseudo_bit_t class_code_hca_class_code[0x00018];
+/* -------------- */
+ pseudo_bit_t cache_line_size[0x00008];/* Cache Line Size */
+ pseudo_bit_t latency_timer[0x00008];
+ pseudo_bit_t header_type[0x00008]; /* hardwired to zero */
+ pseudo_bit_t bist[0x00008];
+/* -------------- */
+ pseudo_bit_t bar0_ctrl[0x00004]; /* hard-wired to '0100 */
+ pseudo_bit_t reserved0[0x00010];
+ pseudo_bit_t bar0_l[0x0000c]; /* Lower bits of BAR0 (configuration space) */
+/* -------------- */
+ pseudo_bit_t bar0_h[0x00020]; /* Upper 32 bits of BAR0 (configuration space) */
+/* -------------- */
+ pseudo_bit_t bar1_ctrl[0x00004]; /* Hardwired to '1100 */
+ pseudo_bit_t reserved1[0x00010];
+ pseudo_bit_t bar1_l[0x0000c]; /* Lower bits of BAR1 */
+/* -------------- */
+ pseudo_bit_t bar1_h[0x00020]; /* upper 32 bits of BAR1 (User Access Revion - UAR - space) */
+/* -------------- */
+ pseudo_bit_t bar2_ctrl[0x00004]; /* Hardwired to '1100 */
+ pseudo_bit_t reserved2[0x00010];
+ pseudo_bit_t bar2_l[0x0000c]; /* Lower bits of BAR2 */
+/* -------------- */
+ pseudo_bit_t bar2_h[0x00020]; /* Upper 32 bits of BAR2 - DDR (attached memory) BAR */
+/* -------------- */
+ pseudo_bit_t cardbus_cis_pointer[0x00020];
+/* -------------- */
+ pseudo_bit_t subsystem_vendor_id[0x00010];/* Specified by the device NVMEM configuration */
+ pseudo_bit_t subsystem_id[0x00010]; /* Specified by the device NVMEM configuration */
+/* -------------- */
+ pseudo_bit_t expansion_rom_enable[0x00001];/* Expansion ROM Enable. Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */
+ pseudo_bit_t reserved3[0x0000a];
+ pseudo_bit_t expansion_rom_base_address[0x00015];/* Expansion ROM Base Address (upper 21 bit). Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */
+/* -------------- */
+ pseudo_bit_t capabilities_pointer[0x00008];/* Specified by the device NVMEM configuration */
+ pseudo_bit_t reserved4[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t interrupt_line[0x00008];
+ pseudo_bit_t interrupt_pin[0x00008];
+ pseudo_bit_t min_gnt[0x00008];
+ pseudo_bit_t max_latency[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved6[0x00100];
+/* -------------- */
+ pseudo_bit_t msi_cap_id[0x00008];
+ pseudo_bit_t msi_next_cap_ptr[0x00008];
+ pseudo_bit_t msi_en[0x00001];
+ pseudo_bit_t multiple_msg_cap[0x00003];
+ pseudo_bit_t multiple_msg_en[0x00003];
+ pseudo_bit_t cap_64_bit_addr[0x00001];
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t msg_addr_l[0x00020];
+/* -------------- */
+ pseudo_bit_t msg_addr_h[0x00020];
+/* -------------- */
+ pseudo_bit_t msg_data[0x00010];
+ pseudo_bit_t reserved8[0x00010];
+/* -------------- */
+ pseudo_bit_t pcix_cap_id[0x00008];
+ pseudo_bit_t pcix_next_cap_ptr[0x00008];
+ pseudo_bit_t pcix_command_reg[0x00010];/* PCIX command register */
+/* -------------- */
+ pseudo_bit_t pcix_status_reg[0x00020];/* PCIX Status Register */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00440];
+/* -------------- */
+};
+
+/* NTU QP Map Table Entry */
+
+struct tavorprm_ntu_qpm_st { /* Little Endian */
+ pseudo_bit_t va_h[0x00020]; /* Bits 63:32 of the virtual address to be used in IB request, Number of bits to be actually used depends on the page size (eg. will use all 52 for 4K page, 51 for 8K page etc). */
+/* -------------- */
+ pseudo_bit_t wm[0x00002]; /* Amount of data to fill in to the read response buffer prior to delivering read response to uplink
+ 00 - forward
+ 01 - MTU
+ 10 - full message
+ 11 - Reserved */
+ pseudo_bit_t mtu[0x00002]; /* MTUI of the channel to be used by this page, value is 256*2MU bytes */
+ pseudo_bit_t rd_len[0x00003]; /* Length of speculative prefetch for read, value is 16*2RD_Len bytes */
+ pseudo_bit_t fence[0x00002];
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t err_fence[0x00001]; /* 0,00 - No action in NTU - normal flow
+ 0,01 - Reserved (fence bits value of "01" is not defined)
+ 0,10 - Enter PCU transaction to Error fifo, NO fence trap to consequent transaction
+ 0,11 - Enter PCU transaction to Error fifo, fence trap to consequent transactions
+ 1,xx - Enter PCU transaction to Error fifo, mark QRM indication in error fifo. */
+ pseudo_bit_t va_l[0x00014]; /* Bits 31:12 of the virtual address to be used in IB request, Number of bits to be actually used depends on the page size (eg. will use all 52 for 4K page, 51 for 8K page etc). */
+/* -------------- */
+ pseudo_bit_t rkey[0x00020]; /* RKey to be places for RDMA IB requests message */
+/* -------------- */
+ pseudo_bit_t my_qpn[0x00018]; /* Local QO this page is mapped to */
+ pseudo_bit_t s[0x00001]; /* Force solicit event bit in the descriptor */
+ pseudo_bit_t e[0x00001]; /* Force E-bit in the descriptor */
+ pseudo_bit_t s_r[0x00001]; /* S/R# - generate Send as a result of write hit to this page */
+ pseudo_bit_t b[0x00001]; /* Breakpoint - ptransfer control to firmware for every cycle that hits this page */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t ce[0x00001]; /* Cache Enable - entry can be cached if this bit is set. */
+ pseudo_bit_t v[0x00001]; /* Valid bit - the entry is valid only if this bit is set */
+/* -------------- */
+};
+
+/* Event Data Field - Performance Monitor */
+
+struct tavorprm_performance_monitor_event_st { /* Little Endian */
+ struct tavorprm_performance_monitors_st performance_monitor_snapshot;/* Performance monitor snapshot */
+/* -------------- */
+ pseudo_bit_t monitor_number[0x00008];/* 0x01 - SQPC
+ 0x02 - RQPC
+ 0x03 - CQC
+ 0x04 - Rkey
+ 0x05 - TLB
+ 0x06 - port0
+ 0x07 - port1 */
+ pseudo_bit_t reserved0[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+};
+
+/* Event_data Field - Page Faults */
+
+struct tavorprm_page_fault_event_data_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t s_r[0x00001]; /* Send (1) or Receive (0) queue caused page fault */
+ pseudo_bit_t r_l[0x00001]; /* Remote (1) or local (0) access caused fault */
+ pseudo_bit_t w_d[0x00001]; /* WQE (1) or data (0) access caused fault */
+ pseudo_bit_t wqv[0x00001]; /* Indicates whether message caused fault consumes descriptor (valid for receive queue only). */
+ pseudo_bit_t fault_type[0x00004]; /* 0000-0111 - RESERVED
+ 1000 - Translation page not present
+ 1001 - RESERVED
+ 1010 - Page write access violation
+ 1011 - 1101 - RESERVED
+ 1110 - Unsupported non-present page fault
+ 1111 - unsupported write access fault */
+ pseudo_bit_t reserved1[0x00018];
+/* -------------- */
+ pseudo_bit_t va_h[0x00020]; /* Virtual address that caused access fault[63:32] */
+/* -------------- */
+ pseudo_bit_t va_l[0x00020]; /* Virtual address that caused access fault[31:0] */
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* Memory Key used for address translation */
+/* -------------- */
+};
+
+/* Event_data Field - Port State Change */
+
+struct tavorprm_port_state_change_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t reserved1[0x0001c];
+ pseudo_bit_t p[0x00002]; /* Port number (1 or 2) */
+ pseudo_bit_t reserved2[0x00002];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+};
+
+/* Event_data Field - Completion Queue Error */
+
+struct tavorprm_completion_queue_error_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t syndrome[0x00008]; /* Error syndrome
+ 0x01 - CQ overrun
+ 0x02 - CQ access violation error */
+ pseudo_bit_t reserved2[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+};
+
+/* Event_data Field - Completion Event */
+
+struct tavorprm_completion_event_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x000a0];
+/* -------------- */
+};
+
+/* Event Queue Entry */
+
+struct tavorprm_event_queue_entry_st { /* Little Endian */
+ pseudo_bit_t event_sub_type[0x00008];/* Event Sub Type.
+ Defined for events which have sub types, zero elsewhere. */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t event_type[0x00008]; /* Event Type */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t event_data[6][0x00020];/* Delivers auxilary data to handle event. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00007];
+ pseudo_bit_t owner[0x00001]; /* Owner of the entry
+ 0 SW
+ 1 HW */
+ pseudo_bit_t reserved3[0x00018];
+/* -------------- */
+};
+
+/* QP/EE State Transitions Command Parameters */
+
+struct tavorprm_qp_ee_state_transitions_st { /* Little Endian */
+ pseudo_bit_t opt_param_mask[0x00020];/* This field defines which optional parameters are passed. Each bit specifies whether optional parameter is passed (set) or not (cleared). The optparammask is defined for each QP/EE command. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ struct tavorprm_queue_pair_ee_context_entry_st qpc_eec_data;/* QPC/EEC data */
+/* -------------- */
+ pseudo_bit_t reserved1[0x007c0];
+/* -------------- */
+};
+
+/* Completion Queue Entry Format */
+
+struct tavorprm_completion_queue_entry_st { /* Little Endian */
+ pseudo_bit_t my_qpn[0x00018]; /* Indicates the QP for which completion is being reported */
+ pseudo_bit_t reserved0[0x00004];
+ pseudo_bit_t ver[0x00004]; /* CQE version.
+ 0 for InfiniHost */
+/* -------------- */
+ pseudo_bit_t my_ee[0x00018]; /* EE context (for RD only).
+ Invalid for Bind and Nop operation on RD. */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t rqpn[0x00018]; /* Remote (source) QP number. Valid in Responder CQE only for Datagram QP. */
+ pseudo_bit_t reserved2[0x00008];
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (source) LID of the message. Valid in Responder of UD QP CQE only. */
+ pseudo_bit_t ml_path[0x00007]; /* My (destination) LID path bits - these are the lowemost LMC bits of the DLID in an incoming UD packet, higher bits of this field, that are not part of the LMC bits are zeroed by HW.
+ Valid in responder of UD QP CQE only.
+ Invalid if incoming message DLID is the permissive LID or incoming message is multicast. */
+ pseudo_bit_t g[0x00001]; /* GRH present indicator. Valid in Responder of UD QP CQE only. */
+ pseudo_bit_t reserved3[0x00001];
+ pseudo_bit_t reserved4[0x00003];
+ pseudo_bit_t sl[0x00004]; /* Service Level of the message. Valid in Responder of UD QP CQE only. */
+/* -------------- */
+ pseudo_bit_t immediate_ethertype_pkey_indx_eecredits[0x00020];/* Valid for receive queue completion only.
+ If Opcode field indicates that this was send/write with immediate, this field contains immediate field of the packet.
+ If completion corresponds to RAW receive queue, bits 15:0 contain Ethertype field of the packet.
+ If completion corresponds to GSI receive queue, bits 31:16 contain index in PKey table that matches PKey of the message arrived.
+ For CQE of send queue of the reliable connection service, bits [4:0] of this field contain the encoded EEcredits received in last ACK of the message.
+ */
+/* -------------- */
+ pseudo_bit_t byte_cnt[0x00020]; /* Byte count of data actually transferred (valid for receive queue completions only) */
+/* -------------- */
+ pseudo_bit_t wqe_size[0x00006]; /* Size (in 16-byte chunks) of WQE completion is reported for */
+ pseudo_bit_t wqe_adr[0x0001a]; /* Bits 31:6 of WQE virtual address completion is reported for. The 6 least significant bits are zero. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00007];
+ pseudo_bit_t owner[0x00001]; /* Owner field. Zero value of this field means SW ownership of CQE. */
+ pseudo_bit_t reserved6[0x0000d];
+ pseudo_bit_t reserved7[0x00001];
+ pseudo_bit_t reserved8[0x00001];
+ pseudo_bit_t s[0x00001]; /* If set, completion is reported for Send queue, if cleared - receive queue. */
+ pseudo_bit_t opcode[0x00008]; /* The opcode of WQE completion is reported for.
+ For CQEs corresponding to send completion, NOPCODE field of the WQE is copied to this field.
+ For CQEs corresponding to receive completions, opcode field of last packet in the message copied to this field.
+ For CQEs corresponding to the receive queue of QPs mapped to QP1, the opcode will be SEND with Immediate (messages are guaranteed to be SEND only)
+
+ The following values are reported in case of completion with error:
+ 0xFE - For completion with error on Receive Queues
+ 0xFF - For completion with error on Send Queues */
+/* -------------- */
+};
+
+/* */
+
+struct tavorprm_ecc_detect_event_data_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t cause_lsb[0x00001];
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t cause_msb[0x00001];
+ pseudo_bit_t reserved2[0x00002];
+ pseudo_bit_t err_rmw[0x00001];
+ pseudo_bit_t err_src_id[0x00003];
+ pseudo_bit_t err_da[0x00002];
+ pseudo_bit_t err_ba[0x00002];
+ pseudo_bit_t reserved3[0x00011];
+ pseudo_bit_t overflow[0x00001];
+/* -------------- */
+ pseudo_bit_t err_ra[0x00010];
+ pseudo_bit_t err_ca[0x00010];
+/* -------------- */
+};
+
+/* MAD_IFC Input Mailbox */
+
+struct tavorprm_mad_ifc_st { /* Little Endian */
+ pseudo_bit_t request_mad_packet[64][0x00020];/* Request MAD Packet (256bytes) */
+/* -------------- */
+ pseudo_bit_t my_qpn[0x00018]; /* Destination QP number from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t rqpn[0x00018]; /* Remote (source) QP number from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (source) LID from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t ml_path[0x00007]; /* My (destination) LID path bits from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t g[0x00001]; /* If set, the GRH field in valid.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved2[0x00004];
+ pseudo_bit_t sl[0x00004]; /* Service Level of the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+/* -------------- */
+ pseudo_bit_t pkey_indx[0x00010]; /* Index in PKey table that matches PKey of the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved3[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00180];
+/* -------------- */
+ pseudo_bit_t grh[10][0x00020]; /* The GRH field of the MAD packet that was scattered to the first 40 bytes pointed to by the scatter list.
+ Valid if Mad_extended_info bit (in the input modifier) and g bit are set.
+ Otherwise this field is reserved. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x004c0];
+/* -------------- */
+};
+
+/* Event_data Field - ECC Detection Event */
+
+struct tavorprm_scrubbing_event_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t cause_lsb[0x00001]; /* data integrity error cause:
+ single ECC error in the 64bit lsb data, on the rise edge of the clock */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t cause_msb[0x00001]; /* data integrity error cause:
+ single ECC error in the 64bit msb data, on the fall edge of the clock */
+ pseudo_bit_t reserved2[0x00002];
+ pseudo_bit_t err_rmw[0x00001]; /* transaction type:
+ 0 - read
+ 1 - read/modify/write */
+ pseudo_bit_t err_src_id[0x00003]; /* source of the transaction: 0x4 - PCI, other - internal or IB */
+ pseudo_bit_t err_da[0x00002]; /* Error DIMM address */
+ pseudo_bit_t err_ba[0x00002]; /* Error bank address */
+ pseudo_bit_t reserved3[0x00011];
+ pseudo_bit_t overflow[0x00001]; /* Fatal: ECC error FIFO overflow - ECC errors were detected, which may or may not have been corrected by InfiniHost */
+/* -------------- */
+ pseudo_bit_t err_ra[0x00010]; /* Error row address */
+ pseudo_bit_t err_ca[0x00010]; /* Error column address */
+/* -------------- */
+};
+
+/* PBL */
+
+struct tavorprm_pbl_st { /* Little Endian */
+ pseudo_bit_t mtt_0_h[0x00020]; /* First MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_0_l[0x00020]; /* First MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_1_h[0x00020]; /* Second MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_1_l[0x00020]; /* Second MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_2_h[0x00020]; /* Third MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_2_l[0x00020]; /* Third MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_3_h[0x00020]; /* Fourth MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_3_l[0x00020]; /* Fourth MTT[31:0] */
+/* -------------- */
+};
+
+/* Miscellaneous Counters */
+
+struct tavorprm_misc_counters_st { /* Little Endian */
+ pseudo_bit_t ddr_scan_cnt[0x00020]; /* Number of times whole of DDR was scanned */
+/* -------------- */
+ pseudo_bit_t reserved0[0x007e0];
+/* -------------- */
+};
+
+/* MAD_IFC Opcode Modifier */
+
+struct tavorprm_mad_ifc_opcode_modifier_st { /* Little Endian */
+ pseudo_bit_t mkey[0x00001]; /* Enable MKey validation. */
+ pseudo_bit_t bkey[0x00001]; /* Enable BKey validation. */
+ pseudo_bit_t reserved0[0x0001d];
+ pseudo_bit_t mad_extended_info[0x00001];/* Mad_Extended_Info valid bit.
+ Requeried for for trap generation when BKey check is enabled. */
+/* -------------- */
+};
+
+/* MAD_IFC Input Modifier */
+
+struct tavorprm_mad_ifc_input_modifier_st { /* Little Endian */
+ pseudo_bit_t port_number[0x00008]; /* Port number (1 or 2). */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t rlid[0x00001]; /* Remote (source) LID from the received MAD.
+ This field is required for trap generation upon MKey/BKey validation. */
+ pseudo_bit_t reserved1[0x0000f];
+/* -------------- */
+};
+
+/* Fast_Registration_Segment */
+
+struct tavorprm_fast_registration_segment_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x0001b];
+ pseudo_bit_t lr[0x00001]; /* If set - Local Read access will be enabled */
+ pseudo_bit_t lw[0x00001]; /* If set - Local Write access will be enabled */
+ pseudo_bit_t rr[0x00001]; /* If set - Remote Read access will be enabled */
+ pseudo_bit_t rw[0x00001]; /* If set - Remote Write access will be enabled */
+ pseudo_bit_t a[0x00001]; /* If set - Remote Atomic access will be enabled */
+/* -------------- */
+ pseudo_bit_t pbl_ptr_63_32[0x00020];/* Physical address pointer [63:32] to the physical block list */
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* Memory Key on which the fast registration is executed on. */
+/* -------------- */
+ pseudo_bit_t page_size[0x00005]; /* Page size used for the region. Actual size is [4K]*2^Page_size bytes.
+ page_size should be less than 20. */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t zb[0x00001]; /* Zero Based Region */
+ pseudo_bit_t pbl_ptr_31_8[0x00018]; /* Physical address pointer [31:8] to the physical block list */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start Address[63:32] - Virtual Address where this region starts */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start Address[31:0] - Virtual Address where this region starts */
+/* -------------- */
+ pseudo_bit_t reg_len_h[0x00020]; /* Region Length[63:32] */
+/* -------------- */
+ pseudo_bit_t reg_len_l[0x00020]; /* Region Length[31:0] */
+/* -------------- */
+};
+
+/* 0 */
+
+struct tavorprm_tavor_prm_st { /* Little Endian */
+ struct tavorprm_completion_queue_entry_st completion_queue_entry;/* Completion Queue Entry Format */
+/* -------------- */
+ pseudo_bit_t reserved0[0x7ff00];
+/* -------------- */
+ struct tavorprm_qp_ee_state_transitions_st qp_ee_state_transitions;/* QP/EE State Transitions Command Parameters */
+/* -------------- */
+ pseudo_bit_t reserved1[0x7f000];
+/* -------------- */
+ struct tavorprm_event_queue_entry_st event_queue_entry;/* Event Queue Entry */
+/* -------------- */
+ pseudo_bit_t reserved2[0x7ff00];
+/* -------------- */
+ struct tavorprm_completion_event_st completion_event;/* Event_data Field - Completion Event */
+/* -------------- */
+ pseudo_bit_t reserved3[0x7ff40];
+/* -------------- */
+ struct tavorprm_completion_queue_error_st completion_queue_error;/* Event_data Field - Completion Queue Error */
+/* -------------- */
+ pseudo_bit_t reserved4[0x7ff40];
+/* -------------- */
+ struct tavorprm_port_state_change_st port_state_change;/* Event_data Field - Port State Change */
+/* -------------- */
+ pseudo_bit_t reserved5[0xfff40];
+/* -------------- */
+ struct tavorprm_page_fault_event_data_st page_fault_event_data;/* Event_data Field - Page Faults */
+/* -------------- */
+ pseudo_bit_t reserved6[0x7ff40];
+/* -------------- */
+ struct tavorprm_performance_monitor_event_st performance_monitor_event;/* Event Data Field - Performance Monitor */
+/* -------------- */
+ pseudo_bit_t reserved7[0x7ff20];
+/* -------------- */
+ struct tavorprm_ntu_qpm_st ntu_qpm; /* NTU QP Map Table Entry */
+/* -------------- */
+ pseudo_bit_t reserved8[0x7ff80];
+/* -------------- */
+ struct tavorprm_mt23108_type0_st mt23108_type0;/* InfiniHost Type0 Configuration Header */
+/* -------------- */
+ pseudo_bit_t reserved9[0x7f800];
+/* -------------- */
+ struct tavorprm_qp_ee_event_st qp_ee_event;/* Event_data Field - QP/EE Events */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00040];
+/* -------------- */
+ struct tavorprm_gpio_event_data_st gpio_event_data;
+/* -------------- */
+ pseudo_bit_t reserved11[0x7fe40];
+/* -------------- */
+ struct tavorprm_ud_address_vector_st ud_address_vector;/* UD Address Vector */
+/* -------------- */
+ pseudo_bit_t reserved12[0x7ff00];
+/* -------------- */
+ struct tavorprm_queue_pair_ee_context_entry_st queue_pair_ee_context_entry;/* QP and EE Context Entry */
+/* -------------- */
+ pseudo_bit_t reserved13[0x7f800];
+/* -------------- */
+ struct tavorprm_address_path_st address_path;/* Address Path */
+/* -------------- */
+ pseudo_bit_t reserved14[0x7ff00];
+/* -------------- */
+ struct tavorprm_completion_queue_context_st completion_queue_context;/* Completion Queue Context Table Entry */
+/* -------------- */
+ pseudo_bit_t reserved15[0x7fe00];
+/* -------------- */
+ struct tavorprm_mpt_st mpt; /* Memory Protection Table (MPT) Entry */
+/* -------------- */
+ pseudo_bit_t reserved16[0x7fe00];
+/* -------------- */
+ struct tavorprm_mtt_st mtt; /* Memory Translation Table (MTT) Entry */
+/* -------------- */
+ pseudo_bit_t reserved17[0x7ffc0];
+/* -------------- */
+ struct tavorprm_eqc_st eqc; /* Event Queue Context Table Entry */
+/* -------------- */
+ pseudo_bit_t reserved18[0x7fe00];
+/* -------------- */
+ struct tavorprm_performance_monitors_st performance_monitors;/* Performance Monitors */
+/* -------------- */
+ pseudo_bit_t reserved19[0x7ff80];
+/* -------------- */
+ struct tavorprm_hca_command_register_st hca_command_register;/* HCA Command Register (HCR) */
+/* -------------- */
+ pseudo_bit_t reserved20[0xfff20];
+/* -------------- */
+ struct tavorprm_init_hca_st init_hca;/* INIT_HCA & QUERY_HCA Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved21[0x7f000];
+/* -------------- */
+ struct tavorprm_qpcbaseaddr_st qpcbaseaddr;/* QPC/EEC/CQC/EQC/RDB Parameters */
+/* -------------- */
+ pseudo_bit_t reserved22[0x7fc00];
+/* -------------- */
+ struct tavorprm_udavtable_memory_parameters_st udavtable_memory_parameters;/* Memory Access Parameters for UD Address Vector Table */
+/* -------------- */
+ pseudo_bit_t reserved23[0x7ffc0];
+/* -------------- */
+ struct tavorprm_multicastparam_st multicastparam;/* Multicast Support Parameters */
+/* -------------- */
+ pseudo_bit_t reserved24[0x7ff00];
+/* -------------- */
+ struct tavorprm_tptparams_st tptparams;/* Translation and Protection Tables Parameters */
+/* -------------- */
+ pseudo_bit_t reserved25[0x7ff00];
+/* -------------- */
+ struct tavorprm_query_ddr_st query_ddr;/* QUERY_DDR Parameters Block */
+/* -------------- */
+ struct tavorprm_access_ddr_st access_ddr;
+/* -------------- */
+ pseudo_bit_t reserved26[0x7f700];
+/* -------------- */
+ struct tavorprm_dimminfo_st dimminfo;/* Logical DIMM Information */
+/* -------------- */
+ pseudo_bit_t reserved27[0x7ff00];
+/* -------------- */
+ struct tavorprm_query_fw_st query_fw;/* QUERY_FW Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved28[0x7f800];
+/* -------------- */
+ struct tavorprm_query_adapter_st query_adapter;/* QUERY_ADAPTER Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved29[0x7f800];
+/* -------------- */
+ struct tavorprm_query_dev_lim_st query_dev_lim;/* Query Device Limitations */
+/* -------------- */
+ pseudo_bit_t reserved30[0x7f800];
+/* -------------- */
+ struct tavorprm_uar_params_st uar_params;/* UAR Parameters */
+/* -------------- */
+ pseudo_bit_t reserved31[0x7ff00];
+/* -------------- */
+ struct tavorprm_init_ib_st init_ib; /* INIT_IB Parameters */
+/* -------------- */
+ pseudo_bit_t reserved32[0x7f800];
+/* -------------- */
+ struct tavorprm_mgm_entry_st mgm_entry;/* Multicast Group Member */
+/* -------------- */
+ pseudo_bit_t reserved33[0x7fe00];
+/* -------------- */
+ struct tavorprm_set_ib_st set_ib; /* SET_IB Parameters */
+/* -------------- */
+ pseudo_bit_t reserved34[0x7fe00];
+/* -------------- */
+ struct tavorprm_rd_send_doorbell_st rd_send_doorbell;/* RD-send doorbell */
+/* -------------- */
+ pseudo_bit_t reserved35[0x7ff80];
+/* -------------- */
+ struct tavorprm_send_doorbell_st send_doorbell;/* Send doorbell */
+/* -------------- */
+ pseudo_bit_t reserved36[0x7ffc0];
+/* -------------- */
+ struct tavorprm_receive_doorbell_st receive_doorbell;/* Receive doorbell */
+/* -------------- */
+ pseudo_bit_t reserved37[0x7ffc0];
+/* -------------- */
+ struct tavorprm_cq_cmd_doorbell_st cq_cmd_doorbell;/* CQ Doorbell */
+/* -------------- */
+ pseudo_bit_t reserved38[0x7ffc0];
+/* -------------- */
+ struct tavorprm_eq_cmd_doorbell_st eq_cmd_doorbell;/* EQ Doorbell */
+/* -------------- */
+ pseudo_bit_t reserved39[0x7ffc0];
+/* -------------- */
+ struct tavorprm_uar_st uar; /* User Access Region */
+/* -------------- */
+ pseudo_bit_t reserved40[0x7c000];
+/* -------------- */
+ struct tavorprm_mgmqp_st mgmqp; /* Multicast Group Member QP */
+/* -------------- */
+ pseudo_bit_t reserved41[0x7ffe0];
+/* -------------- */
+ struct tavorprm_query_debug_msg_st query_debug_msg;/* Query Debug Message */
+/* -------------- */
+ pseudo_bit_t reserved42[0x7f800];
+/* -------------- */
+ struct tavorprm_sys_en_out_param_st sys_en_out_param;/* SYS_EN Output Parameter */
+/* -------------- */
+ pseudo_bit_t reserved43[0x7ffc0];
+/* -------------- */
+ struct tavorprm_resize_cq_st resize_cq;/* Resize CQ Input Mailbox */
+/* -------------- */
+ pseudo_bit_t reserved44[0x7fe00];
+/* -------------- */
+ struct tavorprm_completion_with_error_st completion_with_error;/* Completion with Error CQE */
+/* -------------- */
+ pseudo_bit_t reserved45[0x7ff00];
+/* -------------- */
+ struct tavorprm_hcr_completion_event_st hcr_completion_event;/* Event_data Field - HCR Completion Event */
+/* -------------- */
+ pseudo_bit_t reserved46[0x7ff40];
+/* -------------- */
+ struct tavorprm_transport_and_ci_error_counters_st transport_and_ci_error_counters;/* Transport and CI Error Counters */
+/* -------------- */
+ pseudo_bit_t reserved47[0x7f000];
+/* -------------- */
+ struct tavorprm_performance_counters_st performance_counters;/* Performance Counters */
+/* -------------- */
+ pseudo_bit_t reserved48[0x7f800];
+/* -------------- */
+ struct tavorprm_query_bar_st query_bar;/* Query BAR */
+/* -------------- */
+ pseudo_bit_t reserved49[0x7ffc0];
+/* -------------- */
+ struct tavorprm_cfg_schq_st cfg_schq;/* Schedule queues configuration */
+/* -------------- */
+ pseudo_bit_t reserved50[0x7f800];
+/* -------------- */
+ struct tavorprm_mt23108_configuration_registers_st mt23108_configuration_registers;/* InfiniHost Configuration Registers - Used in Mem-Free mode only */
+/* -------------- */
+ pseudo_bit_t reserved51[0x80000];
+/* -------------- */
+ pseudo_bit_t reserved52[0x00100];
+/* -------------- */
+ pseudo_bit_t reserved53[0x7ff00];
+/* -------------- */
+ pseudo_bit_t reserved54[0x00100];
+/* -------------- */
+ pseudo_bit_t reserved55[0x7ff00];
+/* -------------- */
+ struct tavorprm_srq_context_st srq_context;/* SRQ Context */
+/* -------------- */
+ pseudo_bit_t reserved56[0x7ff00];
+/* -------------- */
+ struct tavorprm_mod_stat_cfg_st mod_stat_cfg;/* MOD_STAT_CFG */
+/* -------------- */
+ pseudo_bit_t reserved57[0x00080];
+/* -------------- */
+ pseudo_bit_t reserved58[0x00040];
+/* -------------- */
+ pseudo_bit_t reserved59[0x1bff740];
+/* -------------- */
+};
+
+#include "MT23108_PRM_append.h"
+
+#endif /* H_prefix_tavorprm_bits_fixnames_MT23108_PRM_csp_H */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/MT23108_PRM_append.h b/gpxe/src/drivers/net/mlx_ipoib/MT23108_PRM_append.h
new file mode 100644
index 00000000..e8b6bc5d
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/MT23108_PRM_append.h
@@ -0,0 +1,199 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+/***
+ *** This file was generated at "Tue Nov 16 17:03:53 2004"
+ *** by:
+ *** % csp_bf -copyright=/mswg/misc/license-header.txt -bits MT23108_PRM_append.csp
+ ***/
+
+#ifndef H_bits_MT23108_PRM_append_csp_H
+#define H_bits_MT23108_PRM_append_csp_H
+
+
+/* Gather entry with inline data */
+
+struct wqe_segment_data_inline_st { /* Little Endian */
+ pseudo_bit_t byte_count[0x0000a]; /* Not including padding for 16Byte chunks */
+ pseudo_bit_t reserved0[0x00015];
+ pseudo_bit_t always1[0x00001];
+/* -------------- */
+ pseudo_bit_t data[0x00020]; /* Data may be more this segment size - in 16Byte chunks */
+/* -------------- */
+};
+
+/* Scatter/Gather entry with a pointer */
+
+struct wqe_segment_data_ptr_st { /* Little Endian */
+ pseudo_bit_t byte_count[0x0001f];
+ pseudo_bit_t always0[0x00001];
+/* -------------- */
+ pseudo_bit_t l_key[0x00020];
+/* -------------- */
+ pseudo_bit_t local_address_h[0x00020];
+/* -------------- */
+ pseudo_bit_t local_address_l[0x00020];
+/* -------------- */
+};
+
+/* */
+
+struct wqe_segment_atomic_st { /* Little Endian */
+ pseudo_bit_t swap_add_h[0x00020];
+/* -------------- */
+ pseudo_bit_t swap_add_l[0x00020];
+/* -------------- */
+ pseudo_bit_t compare_h[0x00020];
+/* -------------- */
+ pseudo_bit_t compare_l[0x00020];
+/* -------------- */
+};
+
+/* */
+
+struct wqe_segment_remote_address_st { /* Little Endian */
+ pseudo_bit_t remote_virt_addr_h[0x00020];
+/* -------------- */
+ pseudo_bit_t remote_virt_addr_l[0x00020];
+/* -------------- */
+ pseudo_bit_t rkey[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* Bind memory window segment */
+
+struct wqe_segment_bind_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x0001d];
+ pseudo_bit_t rr[0x00001]; /* Remote read */
+ pseudo_bit_t rw[0x00001]; /* Remote write */
+ pseudo_bit_t a[0x00001]; /* atomic */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t new_rkey[0x00020];
+/* -------------- */
+ pseudo_bit_t region_lkey[0x00020];
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];
+/* -------------- */
+ pseudo_bit_t length_h[0x00020];
+/* -------------- */
+ pseudo_bit_t length_l[0x00020];
+/* -------------- */
+};
+
+/* */
+
+struct wqe_segment_ud_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t l_key[0x00020]; /* memory key for UD AV */
+/* -------------- */
+ pseudo_bit_t av_address_63_32[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00005];
+ pseudo_bit_t av_address_31_5[0x0001b];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00080];
+/* -------------- */
+ pseudo_bit_t destination_qp[0x00018];
+ pseudo_bit_t reserved3[0x00008];
+/* -------------- */
+ pseudo_bit_t q_key[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00040];
+/* -------------- */
+};
+
+/* */
+
+struct wqe_segment_rd_st { /* Little Endian */
+ pseudo_bit_t destination_qp[0x00018];
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t q_key[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+};
+
+/* */
+
+struct wqe_segment_ctrl_recv_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t e[0x00001]; /* WQE event */
+ pseudo_bit_t c[0x00001]; /* Create CQE (for "requested signalling" QP) */
+ pseudo_bit_t reserved1[0x0001c];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00020];
+/* -------------- */
+};
+
+/* */
+
+struct wqe_segment_ctrl_mlx_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t e[0x00001]; /* WQE event */
+ pseudo_bit_t c[0x00001]; /* Create CQE (for "requested signalling" QP) */
+ pseudo_bit_t reserved1[0x00004];
+ pseudo_bit_t sl[0x00004];
+ pseudo_bit_t max_statrate[0x00003];
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t slr[0x00001]; /* 0= take slid from port. 1= take slid from given headers */
+ pseudo_bit_t v15[0x00001]; /* Send packet over VL15 */
+ pseudo_bit_t reserved3[0x0000e];
+/* -------------- */
+ pseudo_bit_t vcrc[0x00010]; /* Packet's VCRC (if not 0 - otherwise computed by HW) */
+ pseudo_bit_t rlid[0x00010]; /* Destination LID (must match given headers) */
+/* -------------- */
+};
+
+/* */
+
+struct wqe_segment_ctrl_send_st { /* Little Endian */
+ pseudo_bit_t always1[0x00001];
+ pseudo_bit_t s[0x00001]; /* Solicited event */
+ pseudo_bit_t e[0x00001]; /* WQE event */
+ pseudo_bit_t c[0x00001]; /* Create CQE (for "requested signalling" QP) */
+ pseudo_bit_t reserved0[0x0001c];
+/* -------------- */
+ pseudo_bit_t immediate[0x00020];
+/* -------------- */
+};
+
+/* */
+
+struct wqe_segment_next_st { /* Little Endian */
+ pseudo_bit_t nopcode[0x00005]; /* next opcode */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t nda_31_6[0x0001a]; /* NDA[31:6] */
+/* -------------- */
+ pseudo_bit_t nds[0x00006];
+ pseudo_bit_t f[0x00001]; /* fence bit */
+ pseudo_bit_t dbd[0x00001]; /* doorbell rung */
+ pseudo_bit_t nee[0x00018]; /* next EE */
+/* -------------- */
+};
+#endif /* H_bits_MT23108_PRM_append_csp_H */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/MT25218_PRM.h b/gpxe/src/drivers/net/mlx_ipoib/MT25218_PRM.h
new file mode 100644
index 00000000..e0eae385
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/MT25218_PRM.h
@@ -0,0 +1,3463 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+/***
+ *** This file was generated at "Tue Nov 22 15:21:23 2005"
+ *** by:
+ *** % csp_bf -copyright=/mswg/misc/license-header.txt -prefix arbelprm_ -bits -fixnames MT25218_PRM.csp
+ ***/
+
+#ifndef H_prefix_arbelprm_bits_fixnames_MT25218_PRM_csp_H
+#define H_prefix_arbelprm_bits_fixnames_MT25218_PRM_csp_H
+
+#include "bit_ops.h"
+
+
+/* UD Address Vector */
+
+struct arbelprm_ud_address_vector_st { /* Little Endian */
+ pseudo_bit_t pd[0x00018]; /* Protection Domain */
+ pseudo_bit_t port_number[0x00002]; /* Port number
+ 1 - Port 1
+ 2 - Port 2
+ other - reserved */
+ pseudo_bit_t reserved0[0x00006];
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */
+ pseudo_bit_t my_lid_path_bits[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */
+ pseudo_bit_t g[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */
+ pseudo_bit_t max_stat_rate[0x00003];/* Maximum static rate control.
+ 0 - 4X injection rate
+ 1 - 1X injection rate
+ other - reserved
+ */
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t msg[0x00002]; /* Max Message size, size is 256*2^MSG bytes */
+ pseudo_bit_t reserved3[0x00002];
+ pseudo_bit_t mgid_index[0x00006]; /* Index to port GID table
+ mgid_index = (port_number-1) * 2^log_max_gid + gid_index
+ Where:
+ 1. log_max_gid is taken from QUERY_DEV_LIM command
+ 2. gid_index is the index to the GID table */
+ pseudo_bit_t reserved4[0x0000a];
+/* -------------- */
+ pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */
+ pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */
+ pseudo_bit_t sl[0x00004]; /* InfiniBand Service Level (SL) */
+/* -------------- */
+ pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */
+/* -------------- */
+ pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */
+/* -------------- */
+ pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */
+/* -------------- */
+ pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] if G bit is set. Must be set to 0x2 if G bit is cleared. */
+/* -------------- */
+};
+
+/* Send doorbell */
+
+struct arbelprm_send_doorbell_st { /* Little Endian */
+ pseudo_bit_t nopcode[0x00005]; /* Opcode of descriptor to be executed */
+ pseudo_bit_t f[0x00001]; /* Fence bit. If set, descriptor is fenced */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t wqe_counter[0x00010]; /* Modulo-64K counter of WQEs posted to the QP since its creation excluding the newly posted WQEs in this doorbell. Should be zero for the first doorbell on the QP */
+ pseudo_bit_t wqe_cnt[0x00008]; /* Number of WQEs posted with this doorbell. Must be grater then zero. */
+/* -------------- */
+ pseudo_bit_t nds[0x00006]; /* Next descriptor size (in 16-byte chunks) */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */
+/* -------------- */
+};
+
+/* ACCESS_LAM_inject_errors_input_modifier */
+
+struct arbelprm_access_lam_inject_errors_input_modifier_st { /* Little Endian */
+ pseudo_bit_t index3[0x00007];
+ pseudo_bit_t q3[0x00001];
+ pseudo_bit_t index2[0x00007];
+ pseudo_bit_t q2[0x00001];
+ pseudo_bit_t index1[0x00007];
+ pseudo_bit_t q1[0x00001];
+ pseudo_bit_t index0[0x00007];
+ pseudo_bit_t q0[0x00001];
+/* -------------- */
+};
+
+/* ACCESS_LAM_inject_errors_input_parameter */
+
+struct arbelprm_access_lam_inject_errors_input_parameter_st { /* Little Endian */
+ pseudo_bit_t ba[0x00002]; /* Bank Address */
+ pseudo_bit_t da[0x00002]; /* Dimm Address */
+ pseudo_bit_t reserved0[0x0001c];
+/* -------------- */
+ pseudo_bit_t ra[0x00010]; /* Row Address */
+ pseudo_bit_t ca[0x00010]; /* Column Address */
+/* -------------- */
+};
+
+/* */
+
+struct arbelprm_recv_wqe_segment_next_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00006];
+ pseudo_bit_t nda_31_6[0x0001a]; /* Next WQE address, low 32 bit. WQE address must be aligned to 64-byte boundary (6 LSB are forced ZERO). */
+/* -------------- */
+ pseudo_bit_t nds[0x00006]; /* Next WQE size in OctoWords (16 bytes).
+ Zero value in NDS field signals end of WQEs? chain.
+ */
+ pseudo_bit_t reserved1[0x0001a];
+/* -------------- */
+};
+
+/* Send wqe segment data inline */
+
+struct arbelprm_wqe_segment_data_inline_st { /* Little Endian */
+ pseudo_bit_t byte_count[0x0000a]; /* Not including padding for 16Byte chunks */
+ pseudo_bit_t reserved0[0x00015];
+ pseudo_bit_t always1[0x00001];
+/* -------------- */
+ pseudo_bit_t data[0x00018]; /* Data may be more this segment size - in 16Byte chunks */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+};
+
+/* Send wqe segment data ptr */
+
+struct arbelprm_wqe_segment_data_ptr_st { /* Little Endian */
+ pseudo_bit_t byte_count[0x0001f];
+ pseudo_bit_t always0[0x00001];
+/* -------------- */
+ pseudo_bit_t l_key[0x00020];
+/* -------------- */
+ pseudo_bit_t local_address_h[0x00020];
+/* -------------- */
+ pseudo_bit_t local_address_l[0x00020];
+/* -------------- */
+};
+
+/* Send wqe segment rd */
+
+struct arbelprm_local_invalidate_segment_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t mem_key[0x00018];
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved2[0x000a0];
+/* -------------- */
+};
+
+/* Fast_Registration_Segment */
+
+struct arbelprm_fast_registration_segment_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x0001b];
+ pseudo_bit_t lr[0x00001]; /* If set - Local Read access will be enabled */
+ pseudo_bit_t lw[0x00001]; /* If set - Local Write access will be enabled */
+ pseudo_bit_t rr[0x00001]; /* If set - Remote Read access will be enabled */
+ pseudo_bit_t rw[0x00001]; /* If set - Remote Write access will be enabled */
+ pseudo_bit_t a[0x00001]; /* If set - Remote Atomic access will be enabled */
+/* -------------- */
+ pseudo_bit_t pbl_ptr_63_32[0x00020];/* Physical address pointer [63:32] to the physical buffer list */
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* Memory Key on which the fast registration is executed on. */
+/* -------------- */
+ pseudo_bit_t page_size[0x00005]; /* Page size used for the region. Actual size is [4K]*2^Page_size bytes.
+ page_size should be less than 20. */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t zb[0x00001]; /* Zero Based Region */
+ pseudo_bit_t pbl_ptr_31_8[0x00018]; /* Physical address pointer [31:8] to the physical buffer list */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start Address[63:32] - Virtual Address where this region starts */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start Address[31:0] - Virtual Address where this region starts */
+/* -------------- */
+ pseudo_bit_t reg_len_h[0x00020]; /* Region Length[63:32] */
+/* -------------- */
+ pseudo_bit_t reg_len_l[0x00020]; /* Region Length[31:0] */
+/* -------------- */
+};
+
+/* Send wqe segment atomic */
+
+struct arbelprm_wqe_segment_atomic_st { /* Little Endian */
+ pseudo_bit_t swap_add_h[0x00020];
+/* -------------- */
+ pseudo_bit_t swap_add_l[0x00020];
+/* -------------- */
+ pseudo_bit_t compare_h[0x00020];
+/* -------------- */
+ pseudo_bit_t compare_l[0x00020];
+/* -------------- */
+};
+
+/* Send wqe segment remote address */
+
+struct arbelprm_wqe_segment_remote_address_st { /* Little Endian */
+ pseudo_bit_t remote_virt_addr_h[0x00020];
+/* -------------- */
+ pseudo_bit_t remote_virt_addr_l[0x00020];
+/* -------------- */
+ pseudo_bit_t rkey[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* end wqe segment bind */
+
+struct arbelprm_wqe_segment_bind_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x0001d];
+ pseudo_bit_t rr[0x00001]; /* If set, Remote Read Enable for bound window. */
+ pseudo_bit_t rw[0x00001]; /* If set, Remote Write Enable for bound window.
+ */
+ pseudo_bit_t a[0x00001]; /* If set, Atomic Enable for bound window. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x0001e];
+ pseudo_bit_t zb[0x00001]; /* If set, Window is Zero Based. */
+ pseudo_bit_t type[0x00001]; /* Window type.
+ 0 - Type one window
+ 1 - Type two window
+ */
+/* -------------- */
+ pseudo_bit_t new_rkey[0x00020]; /* The new RKey of window to bind */
+/* -------------- */
+ pseudo_bit_t region_lkey[0x00020]; /* Local key of region, which window will be bound to */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];
+/* -------------- */
+ pseudo_bit_t length_h[0x00020];
+/* -------------- */
+ pseudo_bit_t length_l[0x00020];
+/* -------------- */
+};
+
+/* Send wqe segment ud */
+
+struct arbelprm_wqe_segment_ud_st { /* Little Endian */
+ struct arbelprm_ud_address_vector_st ud_address_vector;/* UD Address Vector */
+/* -------------- */
+ pseudo_bit_t destination_qp[0x00018];
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t q_key[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+};
+
+/* Send wqe segment rd */
+
+struct arbelprm_wqe_segment_rd_st { /* Little Endian */
+ pseudo_bit_t destination_qp[0x00018];
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t q_key[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+};
+
+/* Send wqe segment ctrl */
+
+struct arbelprm_wqe_segment_ctrl_send_st { /* Little Endian */
+ pseudo_bit_t always1[0x00001];
+ pseudo_bit_t s[0x00001]; /* Solicited Event bit. If set, SE (Solicited Event) bit is set in the (last packet of) message. */
+ pseudo_bit_t e[0x00001]; /* Event bit. If set, event is generated upon WQE?s completion, if QP is allowed to generate an event. Every WQE with E-bit set generates an event. The C bit must be set on unsignalled QPs if the E bit is set. */
+ pseudo_bit_t c[0x00001]; /* Completion Queue bit. Valid for unsignalled QPs only. If set, the CQ is updated upon WQE?s completion */
+ pseudo_bit_t ip[0x00001]; /* When set, InfiniHost III Ex will calculate the IP checksum of the IP header that is present immediately after the IPoverIB encapsulation header. In the case of multiple headers (encapsulation), InfiniHost III Ex will calculate the checksum only for the first IP header following the IPoverIB encapsulation header. Not Valid for IPv6 packets */
+ pseudo_bit_t tcp_udp[0x00001]; /* When set, InfiniHost III Ex will calculate the TCP/UDP checksum of the packet that is present immediately after the IP header. In the case of multiple headers (encapsulation), InfiniHost III Ex will calculate the checksum only for the first TCP header following the IP header. This bit may be set only if the entire TCP/UDP segment is present in one IB packet */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t so[0x00001]; /* Strong Ordering - when set, the WQE will be executed only after all previous WQEs have been executed. Can be set for RC WQEs only. This bit must be set in type two BIND, Fast Registration and Local invalidate operations. */
+ pseudo_bit_t reserved1[0x00018];
+/* -------------- */
+ pseudo_bit_t immediate[0x00020]; /* If the OpCode encodes an operation with Immediate (RDMA-write/SEND), This field will hold the Immediate data to be sent. If the OpCode encodes send and invalidate operations, this field holds the Invalidation key to be inserted into the packet; otherwise, this field is reserved. */
+/* -------------- */
+};
+
+/* Send wqe segment next */
+
+struct arbelprm_wqe_segment_next_st { /* Little Endian */
+ pseudo_bit_t nopcode[0x00005]; /* Next Opcode: OpCode to be used in the next WQE. Encodes the type of operation to be executed on the QP:
+ ?00000? - NOP. WQE with this opcode creates a completion, but does nothing else
+ ?01000? - RDMA-write
+ ?01001? - RDMA-Write with Immediate
+ ?10000? - RDMA-read
+ ?10001? - Atomic Compare & swap
+ ?10010? - Atomic Fetch & Add
+ ?11000? - Bind memory window
+
+ The encoding for the following operations depends on the QP type:
+ For RC, UC and RD QP:
+ ?01010? - SEND
+ ?01011? - SEND with Immediate
+
+ For UD QP:
+ the encoding depends on the values of bit[31] of the Q_key field in the Datagram Segment (see Table 39, ?Unreliable Datagram Segment Format - Pointers,? on page 101) of
+ both the current WQE and the next WQE, as follows:
+
+ If the last WQE Q_Key bit[31] is clear and the next WQE Q_key bit[31] is set :
+ ?01000? - SEND
+ ?01001? - SEND with Immediate
+
+ otherwise (if the next WQE Q_key bit[31] is cleared, or the last WQE Q_Key bit[31] is set):
+ ?01010? - SEND
+ ?01011? - SEND with Immediate
+
+ All other opcode values are RESERVED, and will result in invalid operation execution. */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t nda_31_6[0x0001a]; /* Next WQE address, low 32 bit. WQE address must be aligned to 64-byte boundary (6 LSB are forced ZERO). */
+/* -------------- */
+ pseudo_bit_t nds[0x00006]; /* Next WQE size in OctoWords (16 bytes).
+ Zero value in NDS field signals end of WQEs? chain.
+ */
+ pseudo_bit_t f[0x00001]; /* Fence bit. If set, next WQE will start execution only after all previous Read/Atomic WQEs complete. */
+ pseudo_bit_t always1[0x00001];
+ pseudo_bit_t reserved1[0x00018];
+/* -------------- */
+};
+
+/* Address Path */
+
+struct arbelprm_address_path_st { /* Little Endian */
+ pseudo_bit_t pkey_index[0x00007]; /* PKey table index */
+ pseudo_bit_t reserved0[0x00011];
+ pseudo_bit_t port_number[0x00002]; /* Specific port associated with this QP/EE.
+ 1 - Port 1
+ 2 - Port 2
+ other - reserved */
+ pseudo_bit_t reserved1[0x00006];
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */
+ pseudo_bit_t my_lid_path_bits[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */
+ pseudo_bit_t g[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */
+ pseudo_bit_t reserved2[0x00005];
+ pseudo_bit_t rnr_retry[0x00003]; /* RNR retry count (see C9-132 in IB spec Vol 1)
+ 0-6 - number of retries
+ 7 - infinite */
+/* -------------- */
+ pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */
+ pseudo_bit_t max_stat_rate[0x00003];/* Maximum static rate control.
+ 0 - 100% injection rate
+ 1 - 25% injection rate
+ 2 - 12.5% injection rate
+ 3 - 50% injection rate
+ other - reserved */
+ pseudo_bit_t reserved3[0x00005];
+ pseudo_bit_t mgid_index[0x00006]; /* Index to port GID table */
+ pseudo_bit_t reserved4[0x00005];
+ pseudo_bit_t ack_timeout[0x00005]; /* Local ACK timeout - Transport timer for activation of retransmission mechanism. Refer to IB spec Vol1 9.7.6.1.3 for further details.
+ The transport timer is set to 4.096us*2^ack_timeout, if ack_timeout is 0 then transport timer is disabled. */
+/* -------------- */
+ pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */
+ pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */
+ pseudo_bit_t sl[0x00004]; /* InfiniBand Service Level (SL) */
+/* -------------- */
+ pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */
+/* -------------- */
+ pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */
+/* -------------- */
+ pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */
+/* -------------- */
+ pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] */
+/* -------------- */
+};
+
+/* HCA Command Register (HCR) */
+
+struct arbelprm_hca_command_register_st { /* Little Endian */
+ pseudo_bit_t in_param_h[0x00020]; /* Input Parameter: parameter[63:32] or pointer[63:32] to input mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t in_param_l[0x00020]; /* Input Parameter: parameter[31:0] or pointer[31:0] to input mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t input_modifier[0x00020];/* Input Parameter Modifier */
+/* -------------- */
+ pseudo_bit_t out_param_h[0x00020]; /* Output Parameter: parameter[63:32] or pointer[63:32] to output mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t out_param_l[0x00020]; /* Output Parameter: parameter[31:0] or pointer[31:0] to output mailbox (see command description) */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00010];
+ pseudo_bit_t token[0x00010]; /* Software assigned token to the command, to uniquely identify it. The token is returned to the software in the EQE reported. */
+/* -------------- */
+ pseudo_bit_t opcode[0x0000c]; /* Command opcode */
+ pseudo_bit_t opcode_modifier[0x00004];/* Opcode Modifier, see specific description for each command. */
+ pseudo_bit_t reserved1[0x00006];
+ pseudo_bit_t e[0x00001]; /* Event Request
+ 0 - Don't report event (software will poll the GO bit)
+ 1 - Report event to EQ when the command completes */
+ pseudo_bit_t go[0x00001]; /* Go (0=Software ownership for the HCR, 1=Hardware ownership for the HCR)
+ Software can write to the HCR only if Go bit is cleared.
+ Software must set the Go bit to trigger the HW to execute the command. Software must not write to this register value other than 1 for the Go bit. */
+ pseudo_bit_t status[0x00008]; /* Command execution status report. Valid only if command interface in under SW ownership (Go bit is cleared)
+ 0 - command completed without error. If different than zero, command execution completed with error. Syndrom encoding is depended on command executed and is defined for each command */
+/* -------------- */
+};
+
+/* CQ Doorbell */
+
+struct arbelprm_cq_cmd_doorbell_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number accessed */
+ pseudo_bit_t cmd[0x00003]; /* Command to be executed on CQ
+ 0x0 - Reserved
+ 0x1 - Request notification for next Solicited completion event. CQ_param specifies the current CQ Consumer Counter.
+ 0x2 - Request notification for next Solicited or Unsolicited completion event. CQ_param specifies the current CQ Consumer Counter.
+ 0x3 - Request notification for multiple completions (Arm-N). CQ_param specifies the value of the CQ Counter that when reached by HW (i.e. HW generates a CQE into this Counter) Event will be generated
+ Other - Reserved */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t cmd_sn[0x00002]; /* Command Sequence Number - This field should be incremented upon receiving completion notification of the respective CQ.
+ This transition is done by ringing Request notification for next Solicited, Request notification for next Solicited or Unsolicited
+ completion or Request notification for multiple completions doorbells after receiving completion notification.
+ This field is initialized to Zero */
+ pseudo_bit_t reserved1[0x00002];
+/* -------------- */
+ pseudo_bit_t cq_param[0x00020]; /* parameter to be used by CQ command */
+/* -------------- */
+};
+
+/* RD-send doorbell */
+
+struct arbelprm_rd_send_doorbell_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t een[0x00018]; /* End-to-end context number (reliable datagram)
+ Must be zero for Nop and Bind operations */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00008];
+ pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */
+/* -------------- */
+ struct arbelprm_send_doorbell_st send_doorbell;/* Send Parameters */
+/* -------------- */
+};
+
+/* Multicast Group Member QP */
+
+struct arbelprm_mgmqp_st { /* Little Endian */
+ pseudo_bit_t qpn_i[0x00018]; /* QPN_i: QP number which is a member in this multicast group. Valid only if Qi bit is set. Length of the QPN_i list is set in INIT_HCA */
+ pseudo_bit_t reserved0[0x00007];
+ pseudo_bit_t qi[0x00001]; /* Qi: QPN_i is valid */
+/* -------------- */
+};
+
+/* vsd */
+
+struct arbelprm_vsd_st { /* Little Endian */
+ pseudo_bit_t vsd_dw0[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw1[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw2[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw3[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw4[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw5[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw6[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw7[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw8[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw9[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw10[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw11[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw12[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw13[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw14[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw15[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw16[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw17[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw18[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw19[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw20[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw21[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw22[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw23[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw24[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw25[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw26[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw27[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw28[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw29[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw30[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw31[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw32[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw33[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw34[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw35[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw36[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw37[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw38[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw39[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw40[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw41[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw42[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw43[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw44[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw45[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw46[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw47[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw48[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw49[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw50[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw51[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw52[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw53[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw54[0x00020];
+/* -------------- */
+ pseudo_bit_t vsd_dw55[0x00020];
+/* -------------- */
+};
+
+/* ACCESS_LAM_inject_errors */
+
+struct arbelprm_access_lam_inject_errors_st { /* Little Endian */
+ struct arbelprm_access_lam_inject_errors_input_parameter_st access_lam_inject_errors_input_parameter;
+/* -------------- */
+ struct arbelprm_access_lam_inject_errors_input_modifier_st access_lam_inject_errors_input_modifier;
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* Logical DIMM Information */
+
+struct arbelprm_dimminfo_st { /* Little Endian */
+ pseudo_bit_t dimmsize[0x00010]; /* Size of DIMM in units of 2^20 Bytes. This value is valid only when DIMMStatus is 0. */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t dimmstatus[0x00001]; /* DIMM Status
+ 0 - Enabled
+ 1 - Disabled
+ */
+ pseudo_bit_t dh[0x00001]; /* When set, the DIMM is Hidden and can not be accessed from the PCI bus. */
+ pseudo_bit_t wo[0x00001]; /* When set, the DIMM is write only.
+ If data integrity is configured (other than none), the DIMM must be
+ only targeted by write transactions where the address and size are multiples of 16 bytes. */
+ pseudo_bit_t reserved1[0x00005];
+/* -------------- */
+ pseudo_bit_t spd[0x00001]; /* 0 - DIMM SPD was read from DIMM
+ 1 - DIMM SPD was read from InfiniHost-III-EX NVMEM */
+ pseudo_bit_t sladr[0x00003]; /* SPD Slave Address 3 LSBits.
+ Valid only if spd bit is 0. */
+ pseudo_bit_t sock_num[0x00002]; /* DIMM socket number (for double sided DIMM one of the two numbers will be reported) */
+ pseudo_bit_t syn[0x00004]; /* Error syndrome (valid regardless of status value)
+ 0 - DIMM has no error
+ 1 - SPD error (e.g. checksum error, no response, error while reading)
+ 2 - DIMM out of bounds (e.g. DIMM rows number is not between 7 and 14, DIMM type is not 2)
+ 3 - DIMM conflict (e.g. mix of registered and unbuffered DIMMs, CAS latency conflict)
+ 5 - DIMM size trimmed due to configuration (size exceeds)
+ other - Error, reserved
+ */
+ pseudo_bit_t reserved2[0x00016];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00040];
+/* -------------- */
+ pseudo_bit_t dimm_start_adr_h[0x00020];/* DIMM memory start address [63:32]. This value is valid only when DIMMStatus is 0. */
+/* -------------- */
+ pseudo_bit_t dimm_start_adr_l[0x00020];/* DIMM memory start address [31:0]. This value is valid only when DIMMStatus is 0. */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00040];
+/* -------------- */
+};
+
+/* UAR Parameters */
+
+struct arbelprm_uar_params_st { /* Little Endian */
+ pseudo_bit_t uar_base_addr_h[0x00020];/* UAR Base (pyhsical) Address [63:32] (QUERY_HCA only) */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00014];
+ pseudo_bit_t uar_base_addr_l[0x0000c];/* UAR Base (pyhsical) Address [31:20] (QUERY_HCA only) */
+/* -------------- */
+ pseudo_bit_t uar_page_sz[0x00008]; /* This field defines the size of each UAR page.
+ Size of UAR Page is 4KB*2^UAR_Page_Size */
+ pseudo_bit_t log_max_uars[0x00004]; /* Number of UARs supported is 2^log_max_UARs */
+ pseudo_bit_t reserved1[0x00004];
+ pseudo_bit_t log_uar_entry_sz[0x00006];/* Size of UAR Context entry is 2^log_uar_sz in 4KByte pages */
+ pseudo_bit_t reserved2[0x0000a];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t uar_scratch_base_addr_h[0x00020];/* Base address of UAR scratchpad [63:32].
+ Number of entries in table is 2^log_max_uars.
+ Table must be aligned to its size */
+/* -------------- */
+ pseudo_bit_t uar_scratch_base_addr_l[0x00020];/* Base address of UAR scratchpad [31:0].
+ Number of entries in table is 2^log_max_uars.
+ Table must be aligned to its size. */
+/* -------------- */
+ pseudo_bit_t uar_context_base_addr_h[0x00020];/* Base address of UAR Context [63:32].
+ Number of entries in table is 2^log_max_uars.
+ Table must be aligned to its size. */
+/* -------------- */
+ pseudo_bit_t uar_context_base_addr_l[0x00020];/* Base address of UAR Context [31:0].
+ Number of entries in table is 2^log_max_uars.
+ Table must be aligned to its size. */
+/* -------------- */
+};
+
+/* Translation and Protection Tables Parameters */
+
+struct arbelprm_tptparams_st { /* Little Endian */
+ pseudo_bit_t mpt_base_adr_h[0x00020];/* MPT - Memory Protection Table base physical address [63:32].
+ Entry size is 64 bytes.
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t mpt_base_adr_l[0x00020];/* MPT - Memory Protection Table base physical address [31:0].
+ Entry size is 64 bytes.
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t log_mpt_sz[0x00006]; /* Log (base 2) of the number of region/windows entries in the MPT table. */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t pfto[0x00005]; /* Page Fault RNR Timeout -
+ The field returned in RNR Naks generated when a page fault is detected.
+ It has no effect when on-demand-paging is not used. */
+ pseudo_bit_t reserved1[0x00013];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00020];
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_h[0x00020];/* MTT - Memory Translation table base physical address [63:32].
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t mtt_base_addr_l[0x00020];/* MTT - Memory Translation table base physical address [31:0].
+ Table must be aligned to its size.
+ Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00040];
+/* -------------- */
+};
+
+/* Multicast Support Parameters */
+
+struct arbelprm_multicastparam_st { /* Little Endian */
+ pseudo_bit_t mc_base_addr_h[0x00020];/* Base Address of the Multicast Table [63:32].
+ The base address must be aligned to the entry size.
+ Address may be set to 0xFFFFFFFF if multicast is not supported. */
+/* -------------- */
+ pseudo_bit_t mc_base_addr_l[0x00020];/* Base Address of the Multicast Table [31:0].
+ The base address must be aligned to the entry size.
+ Address may be set to 0xFFFFFFFF if multicast is not supported. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t log_mc_table_entry_sz[0x00010];/* Log2 of the Size of multicast group member (MGM) entry.
+ Must be greater than 5 (to allow CTRL and GID sections).
+ That implies the number of QPs per MC table entry. */
+ pseudo_bit_t reserved1[0x00010];
+/* -------------- */
+ pseudo_bit_t mc_table_hash_sz[0x00011];/* Number of entries in multicast DGID hash table (must be power of 2)
+ INIT_HCA - the required number of entries
+ QUERY_HCA - the actual number of entries assigned by firmware (will be less than or equal to the amount required in INIT_HCA) */
+ pseudo_bit_t reserved2[0x0000f];
+/* -------------- */
+ pseudo_bit_t log_mc_table_sz[0x00005];/* Log2 of the overall number of MC entries in the MCG table (includes both hash and auxiliary tables) */
+ pseudo_bit_t reserved3[0x00013];
+ pseudo_bit_t mc_hash_fn[0x00003]; /* Multicast hash function
+ 0 - Default hash function
+ other - reserved */
+ pseudo_bit_t reserved4[0x00005];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+};
+
+/* QPC/EEC/CQC/EQC/RDB Parameters */
+
+struct arbelprm_qpcbaseaddr_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t qpc_base_addr_h[0x00020];/* QPC Base Address [63:32]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t log_num_of_qp[0x00005];/* Log base 2 of number of supported QPs */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t qpc_base_addr_l[0x00019];/* QPC Base Address [31:7]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t eec_base_addr_h[0x00020];/* EEC Base Address [63:32]
+ Table must be aligned on its size.
+ Address may be set to 0xFFFFFFFF if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t log_num_of_ee[0x00005];/* Log base 2 of number of supported EEs. */
+ pseudo_bit_t reserved3[0x00002];
+ pseudo_bit_t eec_base_addr_l[0x00019];/* EEC Base Address [31:7]
+ Table must be aligned on its size
+ Address may be set to 0xFFFFFFFF if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t srqc_base_addr_h[0x00020];/* SRQ Context Base Address [63:32]
+ Table must be aligned on its size
+ Address may be set to 0xFFFFFFFF if SRQ is not supported. */
+/* -------------- */
+ pseudo_bit_t log_num_of_srq[0x00005];/* Log base 2 of number of supported SRQs. */
+ pseudo_bit_t srqc_base_addr_l[0x0001b];/* SRQ Context Base Address [31:5]
+ Table must be aligned on its size
+ Address may be set to 0xFFFFFFFF if SRQ is not supported. */
+/* -------------- */
+ pseudo_bit_t cqc_base_addr_h[0x00020];/* CQC Base Address [63:32]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t log_num_of_cq[0x00005];/* Log base 2 of number of supported CQs. */
+ pseudo_bit_t reserved4[0x00001];
+ pseudo_bit_t cqc_base_addr_l[0x0001a];/* CQC Base Address [31:6]
+ Table must be aligned on its size */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00040];
+/* -------------- */
+ pseudo_bit_t eqpc_base_addr_h[0x00020];/* Extended QPC Base Address [63:32]
+ Table has same number of entries as QPC table.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t eqpc_base_addr_l[0x00020];/* Extended QPC Base Address [31:0]
+ Table has same number of entries as QPC table.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00040];
+/* -------------- */
+ pseudo_bit_t eeec_base_addr_h[0x00020];/* Extended EEC Base Address [63:32]
+ Table has same number of entries as EEC table.
+ Table must be aligned to entry size.
+ Address may be set to 0xFFFFFFFF if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t eeec_base_addr_l[0x00020];/* Extended EEC Base Address [31:0]
+ Table has same number of entries as EEC table.
+ Table must be aligned to entry size.
+ Address may be set to 0xFFFFFFFF if RD is not supported. */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00040];
+/* -------------- */
+ pseudo_bit_t eqc_base_addr_h[0x00020];/* EQC Base Address [63:32]
+ Address may be set to 0xFFFFFFFF if EQs are not supported.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t log_num_eq[0x00004]; /* Log base 2 of number of supported EQs.
+ Must be 6 or less in InfiniHost-III-EX. */
+ pseudo_bit_t reserved8[0x00002];
+ pseudo_bit_t eqc_base_addr_l[0x0001a];/* EQC Base Address [31:6]
+ Address may be set to 0xFFFFFFFF if EQs are not supported.
+ Table must be aligned to entry size. */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00040];
+/* -------------- */
+ pseudo_bit_t rdb_base_addr_h[0x00020];/* Base address of table that holds remote read and remote atomic requests [63:32].
+ Address may be set to 0xFFFFFFFF if remote RDMA reads are not supported.
+ Please refer to QP and EE chapter for further explanation on RDB allocation. */
+/* -------------- */
+ pseudo_bit_t rdb_base_addr_l[0x00020];/* Base address of table that holds remote read and remote atomic requests [31:0].
+ Table must be aligned to RDB entry size (32 bytes).
+ Address may be set to zero if remote RDMA reads are not supported.
+ Please refer to QP and EE chapter for further explanation on RDB allocation. */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00040];
+/* -------------- */
+};
+
+/* Header_Log_Register */
+
+struct arbelprm_header_log_register_st { /* Little Endian */
+ pseudo_bit_t place_holder[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved0[0x00060];
+/* -------------- */
+};
+
+/* Performance Monitors */
+
+struct arbelprm_performance_monitors_st { /* Little Endian */
+ pseudo_bit_t e0[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t e1[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t e2[0x00001]; /* Enables counting of respective performance counter */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t r0[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t r1[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t r2[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */
+ pseudo_bit_t reserved1[0x00001];
+ pseudo_bit_t i0[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t i1[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t i2[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t f0[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t f1[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t f2[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */
+ pseudo_bit_t reserved3[0x00001];
+ pseudo_bit_t ev_cnt1[0x00005]; /* Specifies event to be counted by Event_counter1 See XXX for events' definition. */
+ pseudo_bit_t reserved4[0x00003];
+ pseudo_bit_t ev_cnt2[0x00005]; /* Specifies event to be counted by Event_counter2 See XXX for events' definition. */
+ pseudo_bit_t reserved5[0x00003];
+/* -------------- */
+ pseudo_bit_t clock_counter[0x00020];
+/* -------------- */
+ pseudo_bit_t event_counter1[0x00020];
+/* -------------- */
+ pseudo_bit_t event_counter2[0x00020];/* Read/write event counter, counting events specified by EvCntl and EvCnt2 fields repsectively. When the event counter reaches is maximum value of 0xFFFFFF, the next event will cause it to roll over to zero, set F1 or F2 bit respectively and generate interrupt by I1 I2 bit respectively. */
+/* -------------- */
+};
+
+/* Receive segment format */
+
+struct arbelprm_wqe_segment_ctrl_recv_st { /* Little Endian */
+ struct arbelprm_recv_wqe_segment_next_st wqe_segment_next;
+/* -------------- */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t reserved1[0x00001];
+ pseudo_bit_t reserved2[0x00001];
+ pseudo_bit_t reserved3[0x0001c];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+};
+
+/* MLX WQE segment format */
+
+struct arbelprm_wqe_segment_ctrl_mlx_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00002];
+ pseudo_bit_t e[0x00001]; /* WQE event */
+ pseudo_bit_t c[0x00001]; /* Create CQE (for "requested signalling" QP) */
+ pseudo_bit_t icrc[0x00002]; /* icrc field detemines what to do with the last dword of the packet: 0 - Calculate ICRC and put it instead of last dword. Last dword must be 0x0. 1,2 - reserved. 3 - Leave last dword as is. Last dword must not be 0x0. */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t sl[0x00004];
+ pseudo_bit_t max_statrate[0x00004];
+ pseudo_bit_t slr[0x00001]; /* 0= take slid from port. 1= take slid from given headers */
+ pseudo_bit_t v15[0x00001]; /* Send packet over VL15 */
+ pseudo_bit_t reserved2[0x0000e];
+/* -------------- */
+ pseudo_bit_t vcrc[0x00010]; /* Packet's VCRC (if not 0 - otherwise computed by HW) */
+ pseudo_bit_t rlid[0x00010]; /* Destination LID (must match given headers) */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00040];
+/* -------------- */
+};
+
+/* Send WQE segment format */
+
+struct arbelprm_send_wqe_segment_st { /* Little Endian */
+ struct arbelprm_wqe_segment_next_st wqe_segment_next;/* Send wqe segment next */
+/* -------------- */
+ struct arbelprm_wqe_segment_ctrl_send_st wqe_segment_ctrl_send;/* Send wqe segment ctrl */
+/* -------------- */
+ struct arbelprm_wqe_segment_rd_st wqe_segment_rd;/* Send wqe segment rd */
+/* -------------- */
+ struct arbelprm_wqe_segment_ud_st wqe_segment_ud;/* Send wqe segment ud */
+/* -------------- */
+ struct arbelprm_wqe_segment_bind_st wqe_segment_bind;/* Send wqe segment bind */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00180];
+/* -------------- */
+ struct arbelprm_wqe_segment_remote_address_st wqe_segment_remote_address;/* Send wqe segment remote address */
+/* -------------- */
+ struct arbelprm_wqe_segment_atomic_st wqe_segment_atomic;/* Send wqe segment atomic */
+/* -------------- */
+ struct arbelprm_fast_registration_segment_st fast_registration_segment;/* Fast Registration Segment */
+/* -------------- */
+ struct arbelprm_local_invalidate_segment_st local_invalidate_segment;/* local invalidate segment */
+/* -------------- */
+ struct arbelprm_wqe_segment_data_ptr_st wqe_segment_data_ptr;/* Send wqe segment data ptr */
+/* -------------- */
+ struct arbelprm_wqe_segment_data_inline_st wqe_segment_data_inline;/* Send wqe segment data inline */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00200];
+/* -------------- */
+};
+
+/* QP and EE Context Entry */
+
+struct arbelprm_queue_pair_ee_context_entry_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t de[0x00001]; /* Send/Receive Descriptor Event enable - if set, events can be generated upon descriptors' completion on send/receive queue (controlled by E bit in WQE). Invalid in EE context */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t pm_state[0x00002]; /* Path migration state (Migrated, Armed or Rearm)
+ 11-Migrated
+ 00-Armed
+ 01-Rearm
+ 10-Reserved
+ Should be set to 11 for UD QPs and for QPs which do not support APM */
+ pseudo_bit_t reserved2[0x00003];
+ pseudo_bit_t st[0x00003]; /* Service type (invalid in EE context):
+ 000-Reliable Connection
+ 001-Unreliable Connection
+ 010-Reliable Datagram
+ 011-Unreliable Datagram
+ 111-MLX transport (raw bits injection). Used for management QPs and RAW */
+ pseudo_bit_t reserved3[0x00009];
+ pseudo_bit_t state[0x00004]; /* QP/EE state:
+ 0 - RST
+ 1 - INIT
+ 2 - RTR
+ 3 - RTS
+ 4 - SQEr
+ 5 - SQD (Send Queue Drained)
+ 6 - ERR
+ 7 - Send Queue Draining
+ 8 - Reserved
+ 9 - Suspended
+ A- F - Reserved
+ (Valid for QUERY_QPEE and ERR2RST_QPEE commands only) */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t sched_queue[0x00004]; /* Schedule queue to be used for WQE scheduling to execution. Determines QOS for this QP. */
+ pseudo_bit_t rlky[0x00001]; /* When set this QP can use the Reserved L_Key */
+ pseudo_bit_t reserved5[0x00003];
+ pseudo_bit_t log_sq_stride[0x00003];/* Stride on the send queue. WQ entry is 16*(2^log_SQ_stride) bytes.
+ Stride must be equal or bigger then 64 bytes (minimum log_RQ_stride value allowed is 2). */
+ pseudo_bit_t log_sq_size[0x00004]; /* Log2 of the Number of WQEs in the Send Queue. */
+ pseudo_bit_t reserved6[0x00001];
+ pseudo_bit_t log_rq_stride[0x00003];/* Stride on the receive queue. WQ entry is 16*(2^log_RQ_stride) bytes.
+ Stride must be equal or bigger then 64 bytes (minimum log_RQ_stride value allowed is 2). */
+ pseudo_bit_t log_rq_size[0x00004]; /* Log2 of the Number of WQEs in the Receive Queue. */
+ pseudo_bit_t reserved7[0x00001];
+ pseudo_bit_t msg_max[0x00005]; /* Max message size allowed on the QP. Maximum message size is 2^msg_Max.
+ Must be equal to MTU for UD and MLX QPs. */
+ pseudo_bit_t mtu[0x00003]; /* MTU of the QP (Must be the same for both paths: primary and alternative):
+ 0x1 - 256 bytes
+ 0x2 - 512
+ 0x3 - 1024
+ 0x4 - 2048
+ other - reserved
+
+ Should be configured to 0x4 for UD and MLX QPs. */
+/* -------------- */
+ pseudo_bit_t usr_page[0x00018]; /* QP (see "non_privileged Access to the HCA Hardware"). Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved8[0x00008];
+/* -------------- */
+ pseudo_bit_t local_qpn_een[0x00018];/* Local QP/EE number Lower bits determine position of this record in QPC table, and - thus - constrained
+ This field is valid for QUERY and ERR2RST commands only. */
+ pseudo_bit_t reserved9[0x00008];
+/* -------------- */
+ pseudo_bit_t remote_qpn_een[0x00018];/* Remote QP/EE number */
+ pseudo_bit_t reserved10[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved11[0x00040];
+/* -------------- */
+ struct arbelprm_address_path_st primary_address_path;/* Primary address path for the QP/EE */
+/* -------------- */
+ struct arbelprm_address_path_st alternative_address_path;/* Alternate address path for the QP/EE */
+/* -------------- */
+ pseudo_bit_t rdd[0x00018]; /* Reliable Datagram Domain */
+ pseudo_bit_t reserved12[0x00008];
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* QP protection domain. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved13[0x00008];
+/* -------------- */
+ pseudo_bit_t wqe_base_adr_h[0x00020];/* Bits 63:32 of WQE address for both SQ and RQ.
+ Reserved for EE context. */
+/* -------------- */
+ pseudo_bit_t wqe_lkey[0x00020]; /* memory key (L-Key) to be used to access WQEs. Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t reserved14[0x00003];
+ pseudo_bit_t ssc[0x00001]; /* Send Signaled Completion
+ 1 - all send WQEs generate CQEs.
+ 0 - only send WQEs with C bit set generate completion.
+ Not valid (reserved) in EE context. */
+ pseudo_bit_t sic[0x00001]; /* If set - Ignore end to end credits on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t cur_retry_cnt[0x00003];/* Current transport retry counter (QUERY_QPEE only).
+ The current transport retry counter can vary from retry_count down to 1, where 1 means that the last retry attempt is currently executing. */
+ pseudo_bit_t cur_rnr_retry[0x00003];/* Current RNR retry counter (QUERY_QPEE only).
+ The current RNR retry counter can vary from rnr_retry to 1, where 1 means that the last retry attempt is currently executing. */
+ pseudo_bit_t fre[0x00001]; /* Fast Registration Work Request Enabled. (Reserved for EE) */
+ pseudo_bit_t reserved15[0x00001];
+ pseudo_bit_t sae[0x00001]; /* If set - Atomic operations enabled on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t swe[0x00001]; /* If set - RDMA - write enabled on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t sre[0x00001]; /* If set - RDMA - read enabled on send queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t retry_count[0x00003]; /* Transport timeout Retry count */
+ pseudo_bit_t reserved16[0x00002];
+ pseudo_bit_t sra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed in the send queue. Maximum number is 2^SRA_Max. Must be zero in EE context. */
+ pseudo_bit_t flight_lim[0x00004]; /* Number of outstanding (in-flight) messages on the wire allowed for this send queue.
+ Number of outstanding messages is 2^Flight_Lim.
+ Use 0xF for unlimited number of outstanding messages. */
+ pseudo_bit_t ack_req_freq[0x00004]; /* ACK required frequency. ACK required bit will be set in every 2^AckReqFreq packets at least. Not valid for RD QP. */
+/* -------------- */
+ pseudo_bit_t reserved17[0x00020];
+/* -------------- */
+ pseudo_bit_t next_send_psn[0x00018];/* Next PSN to be sent */
+ pseudo_bit_t reserved18[0x00008];
+/* -------------- */
+ pseudo_bit_t cqn_snd[0x00018]; /* CQ number completions from the send queue to be reported to. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved19[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved20[0x00006];
+ pseudo_bit_t snd_wqe_base_adr_l[0x0001a];/* While opening (creating) the WQ, this field should contain the address of first descriptor to be posted. Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t snd_db_record_index[0x00020];/* Index in the UAR Context Table Entry.
+ HW uses this index as an offset from the UAR Context Table Entry in order to read this SQ doorbell record.
+ The entry is obtained via the usr_page field.
+ Not valid for EE. */
+/* -------------- */
+ pseudo_bit_t last_acked_psn[0x00018];/* The last acknowledged PSN for the requester (QUERY_QPEE only) */
+ pseudo_bit_t reserved21[0x00008];
+/* -------------- */
+ pseudo_bit_t ssn[0x00018]; /* Requester Send Sequence Number (QUERY_QPEE only) */
+ pseudo_bit_t reserved22[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved23[0x00003];
+ pseudo_bit_t rsc[0x00001]; /* 1 - all receive WQEs generate CQEs.
+ 0 - only receive WQEs with C bit set generate completion.
+ Not valid (reserved) in EE context.
+ */
+ pseudo_bit_t ric[0x00001]; /* Invalid Credits.
+ 1 - place "Invalid Credits" to ACKs sent from this queue.
+ 0 - ACKs report the actual number of end to end credits on the connection.
+ Not valid (reserved) in EE context.
+ Must be set to 1 on QPs which are attached to SRQ. */
+ pseudo_bit_t reserved24[0x00008];
+ pseudo_bit_t rae[0x00001]; /* If set - Atomic operations enabled. on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t rwe[0x00001]; /* If set - RDMA - write enabled on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t rre[0x00001]; /* If set - RDMA - read enabled on receive queue. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved25[0x00005];
+ pseudo_bit_t rra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed on receive queue is 2^RRA_Max.
+ Must be 0 for EE context. */
+ pseudo_bit_t reserved26[0x00008];
+/* -------------- */
+ pseudo_bit_t next_rcv_psn[0x00018]; /* Next (expected) PSN on receive */
+ pseudo_bit_t min_rnr_nak[0x00005]; /* Minimum RNR NAK timer value (TTTTT field encoding according to the IB spec Vol1 9.7.5.2.8).
+ Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved27[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved28[0x00005];
+ pseudo_bit_t ra_buff_indx[0x0001b]; /* Index to outstanding read/atomic buffer.
+ This field constructs the address to the RDB for maintaining the incoming RDMA read and atomic requests. */
+/* -------------- */
+ pseudo_bit_t cqn_rcv[0x00018]; /* CQ number completions from receive queue to be reported to. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved29[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved30[0x00006];
+ pseudo_bit_t rcv_wqe_base_adr_l[0x0001a];/* While opening (creating) the WQ, this field should contain the address of first descriptor to be posted. Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t rcv_db_record_index[0x00020];/* Index in the UAR Context Table Entry containing the doorbell record for the receive queue.
+ HW uses this index as an offset from the UAR Context Table Entry in order to read this RQ doorbell record.
+ The entry is obtained via the usr_page field.
+ Not valid for EE. */
+/* -------------- */
+ pseudo_bit_t q_key[0x00020]; /* Q_Key to be validated against received datagrams.
+ On send datagrams, if Q_Key[31] specified in the WQE is set, then this Q_Key will be transmitted in the outgoing message.
+ Not valid (reserved) in EE context. */
+/* -------------- */
+ pseudo_bit_t srqn[0x00018]; /* SRQN - Shared Receive Queue Number - specifies the SRQ number from which the QP dequeues receive descriptors.
+ SRQN is valid only if SRQ bit is set. Not valid (reserved) in EE context. */
+ pseudo_bit_t srq[0x00001]; /* SRQ - Shared Receive Queue. If this bit is set, then the QP is associated with a SRQ. Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved31[0x00007];
+/* -------------- */
+ pseudo_bit_t rmsn[0x00018]; /* Responder current message sequence number (QUERY_QPEE only) */
+ pseudo_bit_t reserved32[0x00008];
+/* -------------- */
+ pseudo_bit_t sq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the SQ.
+ Must be 0x0 in SQ initialization.
+ (QUERY_QPEE only). */
+ pseudo_bit_t rq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the RQ.
+ Must be 0x0 in RQ initialization.
+ (QUERY_QPEE only). */
+/* -------------- */
+ pseudo_bit_t reserved33[0x00040];
+/* -------------- */
+};
+
+/* Clear Interrupt [63:0] */
+
+struct arbelprm_clr_int_st { /* Little Endian */
+ pseudo_bit_t clr_int_h[0x00020]; /* Clear Interrupt [63:32]
+ Write transactions to this register will clear (de-assert) the virtual interrupt output pins of InfiniHost-III-EX. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot.
+ This register is write-only. Reading from this register will cause undefined result
+ */
+/* -------------- */
+ pseudo_bit_t clr_int_l[0x00020]; /* Clear Interrupt [31:0]
+ Write transactions to this register will clear (de-assert) the virtual interrupt output pins of InfiniHost-III-EX. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot.
+ This register is write-only. Reading from this register will cause undefined result */
+/* -------------- */
+};
+
+/* EQ_Arm_DB_Region */
+
+struct arbelprm_eq_arm_db_region_st { /* Little Endian */
+ pseudo_bit_t eq_x_arm_h[0x00020]; /* EQ[63:32] X state.
+ This register is used to Arm EQs when setting the appropriate bits. */
+/* -------------- */
+ pseudo_bit_t eq_x_arm_l[0x00020]; /* EQ[31:0] X state.
+ This register is used to Arm EQs when setting the appropriate bits. */
+/* -------------- */
+};
+
+/* EQ Set CI DBs Table */
+
+struct arbelprm_eq_set_ci_table_st { /* Little Endian */
+ pseudo_bit_t eq0_set_ci[0x00020]; /* EQ0_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t eq1_set_ci[0x00020]; /* EQ1_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t eq2_set_ci[0x00020]; /* EQ2_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00020];
+/* -------------- */
+ pseudo_bit_t eq3_set_ci[0x00020]; /* EQ3_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t eq4_set_ci[0x00020]; /* EQ4_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t eq5_set_ci[0x00020]; /* EQ5_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t eq6_set_ci[0x00020]; /* EQ6_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00020];
+/* -------------- */
+ pseudo_bit_t eq7_set_ci[0x00020]; /* EQ7_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00020];
+/* -------------- */
+ pseudo_bit_t eq8_set_ci[0x00020]; /* EQ8_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00020];
+/* -------------- */
+ pseudo_bit_t eq9_set_ci[0x00020]; /* EQ9_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00020];
+/* -------------- */
+ pseudo_bit_t eq10_set_ci[0x00020]; /* EQ10_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00020];
+/* -------------- */
+ pseudo_bit_t eq11_set_ci[0x00020]; /* EQ11_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved11[0x00020];
+/* -------------- */
+ pseudo_bit_t eq12_set_ci[0x00020]; /* EQ12_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved12[0x00020];
+/* -------------- */
+ pseudo_bit_t eq13_set_ci[0x00020]; /* EQ13_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved13[0x00020];
+/* -------------- */
+ pseudo_bit_t eq14_set_ci[0x00020]; /* EQ14_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved14[0x00020];
+/* -------------- */
+ pseudo_bit_t eq15_set_ci[0x00020]; /* EQ15_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved15[0x00020];
+/* -------------- */
+ pseudo_bit_t eq16_set_ci[0x00020]; /* EQ16_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved16[0x00020];
+/* -------------- */
+ pseudo_bit_t eq17_set_ci[0x00020]; /* EQ17_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved17[0x00020];
+/* -------------- */
+ pseudo_bit_t eq18_set_ci[0x00020]; /* EQ18_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved18[0x00020];
+/* -------------- */
+ pseudo_bit_t eq19_set_ci[0x00020]; /* EQ19_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved19[0x00020];
+/* -------------- */
+ pseudo_bit_t eq20_set_ci[0x00020]; /* EQ20_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved20[0x00020];
+/* -------------- */
+ pseudo_bit_t eq21_set_ci[0x00020]; /* EQ21_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved21[0x00020];
+/* -------------- */
+ pseudo_bit_t eq22_set_ci[0x00020]; /* EQ22_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved22[0x00020];
+/* -------------- */
+ pseudo_bit_t eq23_set_ci[0x00020]; /* EQ23_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved23[0x00020];
+/* -------------- */
+ pseudo_bit_t eq24_set_ci[0x00020]; /* EQ24_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved24[0x00020];
+/* -------------- */
+ pseudo_bit_t eq25_set_ci[0x00020]; /* EQ25_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved25[0x00020];
+/* -------------- */
+ pseudo_bit_t eq26_set_ci[0x00020]; /* EQ26_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved26[0x00020];
+/* -------------- */
+ pseudo_bit_t eq27_set_ci[0x00020]; /* EQ27_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved27[0x00020];
+/* -------------- */
+ pseudo_bit_t eq28_set_ci[0x00020]; /* EQ28_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved28[0x00020];
+/* -------------- */
+ pseudo_bit_t eq29_set_ci[0x00020]; /* EQ29_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved29[0x00020];
+/* -------------- */
+ pseudo_bit_t eq30_set_ci[0x00020]; /* EQ30_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved30[0x00020];
+/* -------------- */
+ pseudo_bit_t eq31_set_ci[0x00020]; /* EQ31_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved31[0x00020];
+/* -------------- */
+ pseudo_bit_t eq32_set_ci[0x00020]; /* EQ32_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved32[0x00020];
+/* -------------- */
+ pseudo_bit_t eq33_set_ci[0x00020]; /* EQ33_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved33[0x00020];
+/* -------------- */
+ pseudo_bit_t eq34_set_ci[0x00020]; /* EQ34_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved34[0x00020];
+/* -------------- */
+ pseudo_bit_t eq35_set_ci[0x00020]; /* EQ35_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved35[0x00020];
+/* -------------- */
+ pseudo_bit_t eq36_set_ci[0x00020]; /* EQ36_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved36[0x00020];
+/* -------------- */
+ pseudo_bit_t eq37_set_ci[0x00020]; /* EQ37_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved37[0x00020];
+/* -------------- */
+ pseudo_bit_t eq38_set_ci[0x00020]; /* EQ38_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved38[0x00020];
+/* -------------- */
+ pseudo_bit_t eq39_set_ci[0x00020]; /* EQ39_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved39[0x00020];
+/* -------------- */
+ pseudo_bit_t eq40_set_ci[0x00020]; /* EQ40_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved40[0x00020];
+/* -------------- */
+ pseudo_bit_t eq41_set_ci[0x00020]; /* EQ41_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved41[0x00020];
+/* -------------- */
+ pseudo_bit_t eq42_set_ci[0x00020]; /* EQ42_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved42[0x00020];
+/* -------------- */
+ pseudo_bit_t eq43_set_ci[0x00020]; /* EQ43_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved43[0x00020];
+/* -------------- */
+ pseudo_bit_t eq44_set_ci[0x00020]; /* EQ44_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved44[0x00020];
+/* -------------- */
+ pseudo_bit_t eq45_set_ci[0x00020]; /* EQ45_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved45[0x00020];
+/* -------------- */
+ pseudo_bit_t eq46_set_ci[0x00020]; /* EQ46_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved46[0x00020];
+/* -------------- */
+ pseudo_bit_t eq47_set_ci[0x00020]; /* EQ47_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved47[0x00020];
+/* -------------- */
+ pseudo_bit_t eq48_set_ci[0x00020]; /* EQ48_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved48[0x00020];
+/* -------------- */
+ pseudo_bit_t eq49_set_ci[0x00020]; /* EQ49_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved49[0x00020];
+/* -------------- */
+ pseudo_bit_t eq50_set_ci[0x00020]; /* EQ50_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved50[0x00020];
+/* -------------- */
+ pseudo_bit_t eq51_set_ci[0x00020]; /* EQ51_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved51[0x00020];
+/* -------------- */
+ pseudo_bit_t eq52_set_ci[0x00020]; /* EQ52_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved52[0x00020];
+/* -------------- */
+ pseudo_bit_t eq53_set_ci[0x00020]; /* EQ53_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved53[0x00020];
+/* -------------- */
+ pseudo_bit_t eq54_set_ci[0x00020]; /* EQ54_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved54[0x00020];
+/* -------------- */
+ pseudo_bit_t eq55_set_ci[0x00020]; /* EQ55_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved55[0x00020];
+/* -------------- */
+ pseudo_bit_t eq56_set_ci[0x00020]; /* EQ56_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved56[0x00020];
+/* -------------- */
+ pseudo_bit_t eq57_set_ci[0x00020]; /* EQ57_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved57[0x00020];
+/* -------------- */
+ pseudo_bit_t eq58_set_ci[0x00020]; /* EQ58_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved58[0x00020];
+/* -------------- */
+ pseudo_bit_t eq59_set_ci[0x00020]; /* EQ59_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved59[0x00020];
+/* -------------- */
+ pseudo_bit_t eq60_set_ci[0x00020]; /* EQ60_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved60[0x00020];
+/* -------------- */
+ pseudo_bit_t eq61_set_ci[0x00020]; /* EQ61_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved61[0x00020];
+/* -------------- */
+ pseudo_bit_t eq62_set_ci[0x00020]; /* EQ62_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved62[0x00020];
+/* -------------- */
+ pseudo_bit_t eq63_set_ci[0x00020]; /* EQ63_Set_CI */
+/* -------------- */
+ pseudo_bit_t reserved63[0x00020];
+/* -------------- */
+};
+
+/* InfiniHost-III-EX Configuration Registers */
+
+struct arbelprm_configuration_registers_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x403400];
+/* -------------- */
+ struct arbelprm_hca_command_register_st hca_command_interface_register;/* HCA Command Register */
+/* -------------- */
+ pseudo_bit_t reserved1[0x3fcb20];
+/* -------------- */
+};
+
+/* QP_DB_Record */
+
+struct arbelprm_qp_db_record_st { /* Little Endian */
+ pseudo_bit_t counter[0x00010]; /* Modulo-64K counter of WQEs posted to the QP since its creation. Should be initialized to zero. */
+ pseudo_bit_t reserved0[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00005];
+ pseudo_bit_t res[0x00003]; /* 0x3 for SQ
+ 0x4 for RQ
+ 0x5 for SRQ */
+ pseudo_bit_t qp_number[0x00018]; /* QP number */
+/* -------------- */
+};
+
+/* CQ_ARM_DB_Record */
+
+struct arbelprm_cq_arm_db_record_st { /* Little Endian */
+ pseudo_bit_t counter[0x00020]; /* CQ counter for the arming request */
+/* -------------- */
+ pseudo_bit_t cmd[0x00003]; /* 0x0 - No command
+ 0x1 - Request notification for next Solicited completion event. Counter filed specifies the current CQ Consumer Counter.
+ 0x2 - Request notification for next Solicited or Unsolicited completion event. Counter filed specifies the current CQ Consumer counter.
+ 0x3 - Request notification for multiple completions (Arm-N). Counter filed specifies the value of the CQ Index that when reached by HW (i.e. HW generates a CQE into this Index) Event will be generated
+ Other - Reserved */
+ pseudo_bit_t cmd_sn[0x00002]; /* Command Sequence Number - See Table 35, "CQ Doorbell Layout" for definition of this filed */
+ pseudo_bit_t res[0x00003]; /* Must be 0x2 */
+ pseudo_bit_t cq_number[0x00018]; /* CQ number */
+/* -------------- */
+};
+
+/* CQ_CI_DB_Record */
+
+struct arbelprm_cq_ci_db_record_st { /* Little Endian */
+ pseudo_bit_t counter[0x00020]; /* CQ counter */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00005];
+ pseudo_bit_t res[0x00003]; /* Must be 0x1 */
+ pseudo_bit_t cq_number[0x00018]; /* CQ number */
+/* -------------- */
+};
+
+/* Virtual_Physical_Mapping */
+
+struct arbelprm_virtual_physical_mapping_st { /* Little Endian */
+ pseudo_bit_t va_h[0x00020]; /* Virtual Address[63:32]. Valid only for MAP_ICM command. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x0000c];
+ pseudo_bit_t va_l[0x00014]; /* Virtual Address[31:12]. Valid only for MAP_ICM command. */
+/* -------------- */
+ pseudo_bit_t pa_h[0x00020]; /* Physical Address[63:32] */
+/* -------------- */
+ pseudo_bit_t log2size[0x00006]; /* Log2 of the size in 4KB pages of the physical and virtual contiguous memory that starts at PA_L/H and VA_L/H */
+ pseudo_bit_t reserved1[0x00006];
+ pseudo_bit_t pa_l[0x00014]; /* Physical Address[31:12] */
+/* -------------- */
+};
+
+/* MOD_STAT_CFG */
+
+struct arbelprm_mod_stat_cfg_st { /* Little Endian */
+ pseudo_bit_t log_max_srqs[0x00005]; /* Log (base 2) of the number of SRQs to allocate (0 if no SRQs are required), valid only if srq bit is set. */
+ pseudo_bit_t reserved0[0x00001];
+ pseudo_bit_t srq[0x00001]; /* When set SRQs are supported */
+ pseudo_bit_t srq_m[0x00001]; /* Modify SRQ parameters */
+ pseudo_bit_t reserved1[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved2[0x007e0];
+/* -------------- */
+};
+
+/* SRQ Context */
+
+struct arbelprm_srq_context_st { /* Little Endian */
+ pseudo_bit_t srqn[0x00018]; /* SRQ number */
+ pseudo_bit_t log_srq_size[0x00004]; /* Log2 of the Number of WQEs in the Receive Queue.
+ Maximum value is 0x10, i.e. 16M WQEs. */
+ pseudo_bit_t state[0x00004]; /* SRQ State:
+ 1111 - SW Ownership
+ 0000 - HW Ownership
+ 0001 - Error
+ Valid only on QUERY_SRQ and HW2SW_SRQ commands. */
+/* -------------- */
+ pseudo_bit_t l_key[0x00020]; /* memory key (L-Key) to be used to access WQEs. */
+/* -------------- */
+ pseudo_bit_t srq_db_record_index[0x00020];/* Index in the UAR Context Table Entry containing the doorbell record for the receive queue.
+ HW uses this index as an offset from the UAR Context Table Entry in order to read this SRQ doorbell record.
+ The entry is obtained via the usr_page field. */
+/* -------------- */
+ pseudo_bit_t usr_page[0x00018]; /* Index (offset) of user page allocated for this SRQ (see "non_privileged Access to the HCA Hardware"). Not valid (reserved) in EE context. */
+ pseudo_bit_t reserved0[0x00005];
+ pseudo_bit_t log_rq_stride[0x00003];/* Stride (max WQE size) on the receive queue. WQ entry is 16*(2^log_RQ_stride) bytes. */
+/* -------------- */
+ pseudo_bit_t wqe_addr_h[0x00020]; /* Bits 63:32 of WQE address (WQE base address) */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00006];
+ pseudo_bit_t srq_wqe_base_adr_l[0x0001a];/* While opening (creating) the SRQ, this field should contain the address of first descriptor to be posted. */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* SRQ protection domain. */
+ pseudo_bit_t reserved2[0x00008];
+/* -------------- */
+ pseudo_bit_t wqe_cnt[0x00010]; /* WQE count on the SRQ.
+ Valid only on QUERY_SRQ and HW2SW_SRQ commands. */
+ pseudo_bit_t lwm[0x00010]; /* Limit Water Mark - if the LWM is not zero, and the wqe_cnt drops below LWM when a WQE is dequeued from the SRQ, then a SRQ limit event is fired and the LWM is set to zero. */
+/* -------------- */
+ pseudo_bit_t srq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the SQ.
+ Must be 0x0 in SRQ initialization.
+ (QUERY_SRQ only). */
+ pseudo_bit_t reserved3[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00060];
+/* -------------- */
+};
+
+/* PBL */
+
+struct arbelprm_pbl_st { /* Little Endian */
+ pseudo_bit_t mtt_0_h[0x00020]; /* First MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_0_l[0x00020]; /* First MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_1_h[0x00020]; /* Second MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_1_l[0x00020]; /* Second MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_2_h[0x00020]; /* Third MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_2_l[0x00020]; /* Third MTT[31:0] */
+/* -------------- */
+ pseudo_bit_t mtt_3_h[0x00020]; /* Fourth MTT[63:32] */
+/* -------------- */
+ pseudo_bit_t mtt_3_l[0x00020]; /* Fourth MTT[31:0] */
+/* -------------- */
+};
+
+/* Performance Counters */
+
+struct arbelprm_performance_counters_st { /* Little Endian */
+ pseudo_bit_t sqpc_access_cnt[0x00020];/* SQPC cache access count */
+/* -------------- */
+ pseudo_bit_t sqpc_miss_cnt[0x00020];/* SQPC cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t rqpc_access_cnt[0x00020];/* RQPC cache access count */
+/* -------------- */
+ pseudo_bit_t rqpc_miss_cnt[0x00020];/* RQPC cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+ pseudo_bit_t cqc_access_cnt[0x00020];/* CQC cache access count */
+/* -------------- */
+ pseudo_bit_t cqc_miss_cnt[0x00020]; /* CQC cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t tpt_access_cnt[0x00020];/* TPT cache access count */
+/* -------------- */
+ pseudo_bit_t mpt_miss_cnt[0x00020]; /* MPT cache miss count */
+/* -------------- */
+ pseudo_bit_t mtt_miss_cnt[0x00020]; /* MTT cache miss count */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00620];
+/* -------------- */
+};
+
+/* Transport and CI Error Counters */
+
+struct arbelprm_transport_and_ci_error_counters_st { /* Little Endian */
+ pseudo_bit_t rq_num_lle[0x00020]; /* Responder - number of local length errors */
+/* -------------- */
+ pseudo_bit_t sq_num_lle[0x00020]; /* Requester - number of local length errors */
+/* -------------- */
+ pseudo_bit_t rq_num_lqpoe[0x00020]; /* Responder - number local QP operation error */
+/* -------------- */
+ pseudo_bit_t sq_num_lqpoe[0x00020]; /* Requester - number local QP operation error */
+/* -------------- */
+ pseudo_bit_t rq_num_leeoe[0x00020]; /* Responder - number local EE operation error */
+/* -------------- */
+ pseudo_bit_t sq_num_leeoe[0x00020]; /* Requester - number local EE operation error */
+/* -------------- */
+ pseudo_bit_t rq_num_lpe[0x00020]; /* Responder - number of local protection errors */
+/* -------------- */
+ pseudo_bit_t sq_num_lpe[0x00020]; /* Requester - number of local protection errors */
+/* -------------- */
+ pseudo_bit_t rq_num_wrfe[0x00020]; /* Responder - number of CQEs with error.
+ Incremented each time a CQE with error is generated */
+/* -------------- */
+ pseudo_bit_t sq_num_wrfe[0x00020]; /* Requester - number of CQEs with error.
+ Incremented each time a CQE with error is generated */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_mwbe[0x00020]; /* Requester - number of memory window bind errors */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_bre[0x00020]; /* Requester - number of bad response errors */
+/* -------------- */
+ pseudo_bit_t rq_num_lae[0x00020]; /* Responder - number of local access errors */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t sq_num_rire[0x00020]; /* Requester - number of remote invalid request errors
+ NAK-Invalid Request on:
+ 1. Unsupported OpCode: Responder detected an unsupported OpCode.
+ 2. Unexpected OpCode: Responder detected an error in the sequence of OpCodes, such
+ as a missing "Last" packet.
+ Note: there is no PSN error, thus this does not indicate a dropped packet. */
+/* -------------- */
+ pseudo_bit_t rq_num_rire[0x00020]; /* Responder - number of remote invalid request errors.
+ NAK may or may not be sent.
+ 1. QP Async Affiliated Error: Unsupported or Reserved OpCode (RC,RD only):
+ Inbound request OpCode was either reserved, or was for a function not supported by this
+ QP. (E.g. RDMA or ATOMIC on QP not set up for this).
+ 2. Misaligned ATOMIC: VA does not point to an aligned address on an atomic opera-tion.
+ 3. Too many RDMA READ or ATOMIC Requests: There were more requests received
+ and not ACKed than allowed for the connection.
+ 4. Out of Sequence OpCode, current packet is "First" or "Only": The Responder
+ detected an error in the sequence of OpCodes; a missing "Last" packet
+ 5. Out of Sequence OpCode, current packet is not "First" or "Only": The Responder
+ detected an error in the sequence of OpCodes; a missing "First" packet
+ 6. Local Length Error: Inbound "Send" request message exceeded the responder.s avail-able
+ buffer space.
+ 7. Length error: RDMA WRITE request message contained too much or too little pay-load
+ data compared to the DMA length advertised in the first or only packet.
+ 8. Length error: Payload length was not consistent with the opcode:
+ a: 0 byte <= "only" <= PMTU bytes
+ b: ("first" or "middle") == PMTU bytes
+ c: 1byte <= "last" <= PMTU bytes
+ 9. Length error: Inbound message exceeded the size supported by the CA port. */
+/* -------------- */
+ pseudo_bit_t sq_num_rae[0x00020]; /* Requester - number of remote access errors.
+ NAK-Remote Access Error on:
+ R_Key Violation: Responder detected an invalid R_Key while executing an RDMA
+ Request. */
+/* -------------- */
+ pseudo_bit_t rq_num_rae[0x00020]; /* Responder - number of remote access errors.
+ R_Key Violation Responder detected an R_Key violation while executing an RDMA
+ request.
+ NAK may or may not be sent. */
+/* -------------- */
+ pseudo_bit_t sq_num_roe[0x00020]; /* Requester - number of remote operation errors.
+ NAK-Remote Operation Error on:
+ Remote Operation Error: Responder encountered an error, (local to the responder),
+ which prevented it from completing the request. */
+/* -------------- */
+ pseudo_bit_t rq_num_roe[0x00020]; /* Responder - number of remote operation errors.
+ NAK-Remote Operation Error on:
+ 1. Malformed WQE: Responder detected a malformed Receive Queue WQE while pro-cessing
+ the packet.
+ 2. Remote Operation Error: Responder encountered an error, (local to the responder),
+ which prevented it from completing the request. */
+/* -------------- */
+ pseudo_bit_t sq_num_tree[0x00020]; /* Requester - number of transport retries exceeded errors */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_rree[0x00020]; /* Requester - number of RNR nak retries exceeded errors */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_lrdve[0x00020]; /* Requester - number of local RDD violation errors */
+/* -------------- */
+ pseudo_bit_t rq_num_rirdre[0x00020];/* Responder - number of remote invalid RD request errors */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00040];
+/* -------------- */
+ pseudo_bit_t sq_num_rabrte[0x00020];/* Requester - number of remote aborted errors */
+/* -------------- */
+ pseudo_bit_t reserved6[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_ieecne[0x00020];/* Requester - number of invalid EE context number errors */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00020];
+/* -------------- */
+ pseudo_bit_t sq_num_ieecse[0x00020];/* Requester - invalid EE context state errors */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00380];
+/* -------------- */
+ pseudo_bit_t rq_num_oos[0x00020]; /* Responder - number of out of sequence requests received */
+/* -------------- */
+ pseudo_bit_t sq_num_oos[0x00020]; /* Requester - number of out of sequence Naks received */
+/* -------------- */
+ pseudo_bit_t rq_num_mce[0x00020]; /* Responder - number of bad multicast packets received */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00020];
+/* -------------- */
+ pseudo_bit_t rq_num_rsync[0x00020]; /* Responder - number of RESYNC operations */
+/* -------------- */
+ pseudo_bit_t sq_num_rsync[0x00020]; /* Requester - number of RESYNC operations */
+/* -------------- */
+ pseudo_bit_t rq_num_udsdprd[0x00020];/* The number of UD packets silently discarded on the receive queue due to lack of receive descriptor. */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00020];
+/* -------------- */
+ pseudo_bit_t rq_num_ucsdprd[0x00020];/* The number of UC packets silently discarded on the receive queue due to lack of receive descriptor. */
+/* -------------- */
+ pseudo_bit_t reserved11[0x003e0];
+/* -------------- */
+ pseudo_bit_t num_cqovf[0x00020]; /* Number of CQ overflows */
+/* -------------- */
+ pseudo_bit_t num_eqovf[0x00020]; /* Number of EQ overflows */
+/* -------------- */
+ pseudo_bit_t num_baddb[0x00020]; /* Number of bad doorbells */
+/* -------------- */
+ pseudo_bit_t reserved12[0x002a0];
+/* -------------- */
+};
+
+/* Event_data Field - HCR Completion Event */
+
+struct arbelprm_hcr_completion_event_st { /* Little Endian */
+ pseudo_bit_t token[0x00010]; /* HCR Token */
+ pseudo_bit_t reserved0[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t status[0x00008]; /* HCR Status */
+ pseudo_bit_t reserved2[0x00018];
+/* -------------- */
+ pseudo_bit_t out_param_h[0x00020]; /* HCR Output Parameter [63:32] */
+/* -------------- */
+ pseudo_bit_t out_param_l[0x00020]; /* HCR Output Parameter [31:0] */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+};
+
+/* Completion with Error CQE */
+
+struct arbelprm_completion_with_error_st { /* Little Endian */
+ pseudo_bit_t myqpn[0x00018]; /* Indicates the QP for which completion is being reported */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00060];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00010];
+ pseudo_bit_t vendor_code[0x00008];
+ pseudo_bit_t syndrome[0x00008]; /* Completion with error syndrome:
+ 0x01 - Local Length Error
+ 0x02 - Local QP Operation Error
+ 0x03 - Local EE Context Operation Error
+ 0x04 - Local Protection Error
+ 0x05 - Work Request Flushed Error
+ 0x06 - Memory Window Bind Error
+ 0x10 - Bad Response Error
+ 0x11 - Local Access Error
+ 0x12 - Remote Invalid Request Error
+ 0x13 - Remote Access Error
+ 0x14 - Remote Operation Error
+ 0x15 - Transport Retry Counter Exceeded
+ 0x16 - RNR Retry Counter Exceeded
+ 0x20 - Local RDD Violation Error
+ 0x21 - Remote Invalid RD Request
+ 0x22 - Remote Aborted Error
+ 0x23 - Invalid EE Context Number
+ 0x24 - Invalid EE Context State
+ other - Reserved
+ Syndrome is defined according to the IB specification volume 1. For detailed explanation of the syndromes, refer to chapters 10-11 of the IB specification rev 1.1. */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00006];
+ pseudo_bit_t wqe_addr[0x0001a]; /* Bits 31:6 of WQE virtual address completion is reported for. The 6 least significant bits are zero. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00007];
+ pseudo_bit_t owner[0x00001]; /* Owner field. Zero value of this field means SW ownership of CQE. */
+ pseudo_bit_t reserved6[0x00010];
+ pseudo_bit_t opcode[0x00008]; /* The opcode of WQE completion is reported for.
+
+ The following values are reported in case of completion with error:
+ 0xFE - For completion with error on Receive Queues
+ 0xFF - For completion with error on Send Queues */
+/* -------------- */
+};
+
+/* Resize CQ Input Mailbox */
+
+struct arbelprm_resize_cq_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ pseudo_bit_t start_addr_h[0x00020]; /* Start address of CQ[63:32].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t start_addr_l[0x00020]; /* Start address of CQ[31:0].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00018];
+ pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries) */
+ pseudo_bit_t reserved2[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+ pseudo_bit_t l_key[0x00020]; /* Memory key (L_Key) to be used to access CQ */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00100];
+/* -------------- */
+};
+
+/* MAD_IFC Input Modifier */
+
+struct arbelprm_mad_ifc_input_modifier_st { /* Little Endian */
+ pseudo_bit_t port_number[0x00008]; /* The packet reception port number (1 or 2). */
+ pseudo_bit_t mad_extended_info[0x00001];/* Mad_Extended_Info valid bit (MAD_IFC Input Mailbox data from offset 00100h and down). MAD_Extended_Info is read only if this bit is set.
+ Required for trap generation when BKey check is enabled and for global routed packets. */
+ pseudo_bit_t reserved0[0x00007];
+ pseudo_bit_t rlid[0x00010]; /* Remote (source) LID from the received MAD.
+ This field is required for trap generation upon MKey/BKey validation. */
+/* -------------- */
+};
+
+/* MAD_IFC Input Mailbox */
+
+struct arbelprm_mad_ifc_st { /* Little Endian */
+ pseudo_bit_t request_mad_packet[64][0x00020];/* Request MAD Packet (256bytes) */
+/* -------------- */
+ pseudo_bit_t my_qpn[0x00018]; /* Destination QP number from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t rqpn[0x00018]; /* Remote (source) QP number from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (source) LID from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t ml_path[0x00007]; /* My (destination) LID path bits from the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t g[0x00001]; /* If set, the GRH field in valid.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved2[0x00004];
+ pseudo_bit_t sl[0x00004]; /* Service Level of the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+/* -------------- */
+ pseudo_bit_t pkey_indx[0x00010]; /* Index in PKey table that matches PKey of the received MAD.
+ This field is reserved if Mad_extended_info indication in the input modifier is clear. */
+ pseudo_bit_t reserved3[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00180];
+/* -------------- */
+ pseudo_bit_t grh[10][0x00020]; /* The GRH field of the MAD packet that was scattered to the first 40 bytes pointed to by the scatter list.
+ Valid if Mad_extended_info bit (in the input modifier) and g bit are set.
+ Otherwise this field is reserved. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x004c0];
+/* -------------- */
+};
+
+/* Query Debug Message */
+
+struct arbelprm_query_debug_msg_st { /* Little Endian */
+ pseudo_bit_t phy_addr_h[0x00020]; /* Translation of the address in firmware area. High 32 bits. */
+/* -------------- */
+ pseudo_bit_t v[0x00001]; /* Physical translation is valid */
+ pseudo_bit_t reserved0[0x0000b];
+ pseudo_bit_t phy_addr_l[0x00014]; /* Translation of the address in firmware area. Low 32 bits. */
+/* -------------- */
+ pseudo_bit_t fw_area_base[0x00020]; /* Firmware area base address. The format strings and the trace buffers may be located starting from this address. */
+/* -------------- */
+ pseudo_bit_t fw_area_size[0x00020]; /* Firmware area size */
+/* -------------- */
+ pseudo_bit_t trc_hdr_sz[0x00020]; /* Trace message header size in dwords. */
+/* -------------- */
+ pseudo_bit_t trc_arg_num[0x00020]; /* The number of arguments per trace message. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x000c0];
+/* -------------- */
+ pseudo_bit_t dbg_msk_h[0x00020]; /* Debug messages mask [63:32] */
+/* -------------- */
+ pseudo_bit_t dbg_msk_l[0x00020]; /* Debug messages mask [31:0] */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00040];
+/* -------------- */
+ pseudo_bit_t buff0_addr[0x00020]; /* Address in firmware area of Trace Buffer 0 */
+/* -------------- */
+ pseudo_bit_t buff0_size[0x00020]; /* Size of Trace Buffer 0 */
+/* -------------- */
+ pseudo_bit_t buff1_addr[0x00020]; /* Address in firmware area of Trace Buffer 1 */
+/* -------------- */
+ pseudo_bit_t buff1_size[0x00020]; /* Size of Trace Buffer 1 */
+/* -------------- */
+ pseudo_bit_t buff2_addr[0x00020]; /* Address in firmware area of Trace Buffer 2 */
+/* -------------- */
+ pseudo_bit_t buff2_size[0x00020]; /* Size of Trace Buffer 2 */
+/* -------------- */
+ pseudo_bit_t buff3_addr[0x00020]; /* Address in firmware area of Trace Buffer 3 */
+/* -------------- */
+ pseudo_bit_t buff3_size[0x00020]; /* Size of Trace Buffer 3 */
+/* -------------- */
+ pseudo_bit_t buff4_addr[0x00020]; /* Address in firmware area of Trace Buffer 4 */
+/* -------------- */
+ pseudo_bit_t buff4_size[0x00020]; /* Size of Trace Buffer 4 */
+/* -------------- */
+ pseudo_bit_t buff5_addr[0x00020]; /* Address in firmware area of Trace Buffer 5 */
+/* -------------- */
+ pseudo_bit_t buff5_size[0x00020]; /* Size of Trace Buffer 5 */
+/* -------------- */
+ pseudo_bit_t buff6_addr[0x00020]; /* Address in firmware area of Trace Buffer 6 */
+/* -------------- */
+ pseudo_bit_t buff6_size[0x00020]; /* Size of Trace Buffer 6 */
+/* -------------- */
+ pseudo_bit_t buff7_addr[0x00020]; /* Address in firmware area of Trace Buffer 7 */
+/* -------------- */
+ pseudo_bit_t buff7_size[0x00020]; /* Size of Trace Buffer 7 */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00400];
+/* -------------- */
+};
+
+/* User Access Region */
+
+struct arbelprm_uar_st { /* Little Endian */
+ struct arbelprm_rd_send_doorbell_st rd_send_doorbell;/* Reliable Datagram send doorbell */
+/* -------------- */
+ struct arbelprm_send_doorbell_st send_doorbell;/* Send doorbell */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ struct arbelprm_cq_cmd_doorbell_st cq_command_doorbell;/* CQ Doorbell */
+/* -------------- */
+ pseudo_bit_t reserved1[0x03ec0];
+/* -------------- */
+};
+
+/* Receive doorbell */
+
+struct arbelprm_receive_doorbell_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t wqe_counter[0x00010]; /* Modulo-64K counter of WQEs posted on this queue since its creation. Should be zero for the first doorbell on the QP */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved2[0x00005];
+ pseudo_bit_t srq[0x00001]; /* If set, this is a Shared Receive Queue */
+ pseudo_bit_t reserved3[0x00002];
+ pseudo_bit_t qpn[0x00018]; /* QP number or SRQ number this doorbell is rung on */
+/* -------------- */
+};
+
+/* SET_IB Parameters */
+
+struct arbelprm_set_ib_st { /* Little Endian */
+ pseudo_bit_t rqk[0x00001]; /* Reset QKey Violation Counter */
+ pseudo_bit_t reserved0[0x00011];
+ pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified.
+ system_image_guid and sig must be the same for all ports. */
+ pseudo_bit_t reserved1[0x0000d];
+/* -------------- */
+ pseudo_bit_t capability_mask[0x00020];/* PortInfo Capability Mask */
+/* -------------- */
+ pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00180];
+/* -------------- */
+};
+
+/* Multicast Group Member */
+
+struct arbelprm_mgm_entry_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00006];
+ pseudo_bit_t next_gid_index[0x0001a];/* Index of next Multicast Group Member whose GID maps to same MGID_HASH number.
+ The index is into the Multicast Group Table, which is the comprised the MGHT and AMGM tables.
+ next_gid_index=0 means end of the chain. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00060];
+/* -------------- */
+ pseudo_bit_t mgid_128_96[0x00020]; /* Multicast group GID[128:96] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_95_64[0x00020]; /* Multicast group GID[95:64] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_63_32[0x00020]; /* Multicast group GID[63:32] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ pseudo_bit_t mgid_31_0[0x00020]; /* Multicast group GID[31:0] in big endian format.
+ Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_0; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_1; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_2; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_3; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_4; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_5; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_6; /* Multicast Group Member QP */
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp_7; /* Multicast Group Member QP */
+/* -------------- */
+};
+
+/* INIT_IB Parameters */
+
+struct arbelprm_init_ib_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00004];
+ pseudo_bit_t vl_cap[0x00004]; /* Maximum VLs supported on the port, excluding VL15.
+ Legal values are 1,2,4 and 8. */
+ pseudo_bit_t port_width_cap[0x00004];/* IB Port Width
+ 1 - 1x
+ 3 - 1x, 4x
+ 11 - 1x, 4x or 12x (must not be used in InfiniHost-III-EX MT25208)
+ else - Reserved */
+ pseudo_bit_t mtu_cap[0x00004]; /* Maximum MTU Supported
+ 0x0 - Reserved
+ 0x1 - 256
+ 0x2 - 512
+ 0x3 - 1024
+ 0x4 - 2048
+ 0x5 - 0xF Reserved */
+ pseudo_bit_t g0[0x00001]; /* Set port GUID0 to GUID0 specified */
+ pseudo_bit_t ng[0x00001]; /* Set node GUID to node_guid specified.
+ node_guid and ng must be the same for all ports. */
+ pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified.
+ system_image_guid and sig must be the same for all ports. */
+ pseudo_bit_t reserved1[0x0000d];
+/* -------------- */
+ pseudo_bit_t max_gid[0x00010]; /* Maximum number of GIDs for the port */
+ pseudo_bit_t reserved2[0x00010];
+/* -------------- */
+ pseudo_bit_t max_pkey[0x00010]; /* Maximum pkeys for the port.
+ Must be the same for both ports. */
+ pseudo_bit_t reserved3[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t guid0_h[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 63:32) */
+/* -------------- */
+ pseudo_bit_t guid0_l[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 31:0) */
+/* -------------- */
+ pseudo_bit_t node_guid_h[0x00020]; /* Node GUID[63:32], takes effect only if the NG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t node_guid_l[0x00020]; /* Node GUID[31:0], takes effect only if the NG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set
+ Must be the same for both ports. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x006c0];
+/* -------------- */
+};
+
+/* Query Device Limitations */
+
+struct arbelprm_query_dev_lim_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t log_max_qp[0x00005]; /* Log2 of the Maximum number of QPs supported */
+ pseudo_bit_t reserved1[0x00003];
+ pseudo_bit_t log2_rsvd_qps[0x00004];/* Log (base 2) of the number of QPs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_qps-1 */
+ pseudo_bit_t reserved2[0x00004];
+ pseudo_bit_t log_max_qp_sz[0x00008];/* The maximum number of WQEs allowed on the RQ or the SQ is 2^log_max_qp_sz-1 */
+ pseudo_bit_t log_max_srq_sz[0x00008];/* The maximum number of WQEs allowed on the SRQ is 2^log_max_srq_sz-1 */
+/* -------------- */
+ pseudo_bit_t log_max_ee[0x00005]; /* Log2 of the Maximum number of EE contexts supported */
+ pseudo_bit_t reserved3[0x00003];
+ pseudo_bit_t log2_rsvd_ees[0x00004];/* Log (base 2) of the number of EECs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_ees-1 */
+ pseudo_bit_t reserved4[0x00004];
+ pseudo_bit_t log_max_srqs[0x00005]; /* Log base 2 of the maximum number of SRQs supported, valid only if SRQ bit is set.
+ */
+ pseudo_bit_t reserved5[0x00007];
+ pseudo_bit_t log2_rsvd_srqs[0x00004];/* Log (base 2) of the number of reserved SRQs for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_srqs-1
+ This parameter is valid only if the SRQ bit is set. */
+/* -------------- */
+ pseudo_bit_t log_max_cq[0x00005]; /* Log2 of the Maximum number of CQs supported */
+ pseudo_bit_t reserved6[0x00003];
+ pseudo_bit_t log2_rsvd_cqs[0x00004];/* Log (base 2) of the number of CQs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsrvd_cqs-1 */
+ pseudo_bit_t reserved7[0x00004];
+ pseudo_bit_t log_max_cq_sz[0x00008];/* Log2 of the Maximum CQEs allowed in a CQ */
+ pseudo_bit_t reserved8[0x00008];
+/* -------------- */
+ pseudo_bit_t log_max_eq[0x00003]; /* Log2 of the Maximum number of EQs */
+ pseudo_bit_t reserved9[0x00005];
+ pseudo_bit_t num_rsvd_eqs[0x00004]; /* The number of EQs reserved for firmware use
+ The reserved resources are numbered from 0 to num_rsvd_eqs-1
+ If 0 - no resources are reserved. */
+ pseudo_bit_t reserved10[0x00004];
+ pseudo_bit_t log_max_mpts[0x00006]; /* Log (base 2) of the maximum number of MPT entries (the number of Regions/Windows) */
+ pseudo_bit_t reserved11[0x00002];
+ pseudo_bit_t log_max_eq_sz[0x00008];/* Log2 of the Maximum EQEs allowed in a EQ */
+/* -------------- */
+ pseudo_bit_t log_max_mtts[0x00006]; /* Log2 of the Maximum number of MTT entries */
+ pseudo_bit_t reserved12[0x00002];
+ pseudo_bit_t log2_rsvd_mrws[0x00004];/* Log (base 2) of the number of MPTs reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_mrws-1 */
+ pseudo_bit_t reserved13[0x00004];
+ pseudo_bit_t log_max_mrw_sz[0x00008];/* Log2 of the Maximum Size of Memory Region/Window */
+ pseudo_bit_t reserved14[0x00004];
+ pseudo_bit_t log2_rsvd_mtts[0x00004];/* Log (base 2) of the number of MTT entries reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_mtts-1
+ */
+/* -------------- */
+ pseudo_bit_t reserved15[0x00020];
+/* -------------- */
+ pseudo_bit_t log_max_ra_res_qp[0x00006];/* Log2 of the Maximum number of outstanding RDMA read/Atomic per QP as a responder */
+ pseudo_bit_t reserved16[0x0000a];
+ pseudo_bit_t log_max_ra_req_qp[0x00006];/* Log2 of the maximum number of outstanding RDMA read/Atomic per QP as a requester */
+ pseudo_bit_t reserved17[0x0000a];
+/* -------------- */
+ pseudo_bit_t log_max_ra_res_global[0x00006];/* Log2 of the maximum number of RDMA read/atomic operations the HCA responder can support globally. That implies the RDB table size. */
+ pseudo_bit_t reserved18[0x00016];
+ pseudo_bit_t log2_rsvd_rdbs[0x00004];/* Log (base 2) of the number of RDB entries reserved for firmware use
+ The reserved resources are numbered from 0 to 2^log2_rsvd_rdbs-1 */
+/* -------------- */
+ pseudo_bit_t rsz_srq[0x00001]; /* Ability to modify the maximum number of WRs per SRQ. */
+ pseudo_bit_t reserved19[0x0001f];
+/* -------------- */
+ pseudo_bit_t num_ports[0x00004]; /* Number of IB ports. */
+ pseudo_bit_t max_vl[0x00004]; /* Maximum VLs supported on each port, excluding VL15 */
+ pseudo_bit_t max_port_width[0x00004];/* IB Port Width
+ 1 - 1x
+ 3 - 1x, 4x
+ 11 - 1x, 4x or 12x
+ else - Reserved */
+ pseudo_bit_t max_mtu[0x00004]; /* Maximum MTU Supported
+ 0x0 - Reserved
+ 0x1 - 256
+ 0x2 - 512
+ 0x3 - 1024
+ 0x4 - 2048
+ 0x5 - 0xF Reserved */
+ pseudo_bit_t local_ca_ack_delay[0x00005];/* The Local CA ACK Delay. This is the value recommended to be returned in Query HCA verb.
+ The delay value in microseconds is computed using 4.096us * 2^(local_ca_ack_delay). */
+ pseudo_bit_t reserved20[0x0000b];
+/* -------------- */
+ pseudo_bit_t log_max_gid[0x00004]; /* Log2 of the maximum number of GIDs per port */
+ pseudo_bit_t reserved21[0x0001c];
+/* -------------- */
+ pseudo_bit_t log_max_pkey[0x00004]; /* Log2 of the max PKey Table Size (per IB port) */
+ pseudo_bit_t reserved22[0x0000c];
+ pseudo_bit_t stat_rate_support[0x00010];/* bit mask of stat rate supported
+ bit 0 - full bw
+ bit 1 - 1/4 bw
+ bit 2 - 1/8 bw
+ bit 3 - 1/2 bw; */
+/* -------------- */
+ pseudo_bit_t reserved23[0x00020];
+/* -------------- */
+ pseudo_bit_t rc[0x00001]; /* RC Transport supported */
+ pseudo_bit_t uc[0x00001]; /* UC Transport Supported */
+ pseudo_bit_t ud[0x00001]; /* UD Transport Supported */
+ pseudo_bit_t rd[0x00001]; /* RD Transport Supported */
+ pseudo_bit_t raw_ipv6[0x00001]; /* Raw IPv6 Transport Supported */
+ pseudo_bit_t raw_ether[0x00001]; /* Raw Ethertype Transport Supported */
+ pseudo_bit_t srq[0x00001]; /* SRQ is supported
+ */
+ pseudo_bit_t ipo_ib_checksum[0x00001];/* IP over IB checksum is supported */
+ pseudo_bit_t pkv[0x00001]; /* PKey Violation Counter Supported */
+ pseudo_bit_t qkv[0x00001]; /* QKey Violation Coutner Supported */
+ pseudo_bit_t reserved24[0x00006];
+ pseudo_bit_t mw[0x00001]; /* Memory windows supported */
+ pseudo_bit_t apm[0x00001]; /* Automatic Path Migration Supported */
+ pseudo_bit_t atm[0x00001]; /* Atomic operations supported (atomicity is guaranteed between QPs on this HCA) */
+ pseudo_bit_t rm[0x00001]; /* Raw Multicast Supported */
+ pseudo_bit_t avp[0x00001]; /* Address Vector Port checking supported */
+ pseudo_bit_t udm[0x00001]; /* UD Multicast Supported */
+ pseudo_bit_t reserved25[0x00002];
+ pseudo_bit_t pg[0x00001]; /* Paging on demand supported */
+ pseudo_bit_t r[0x00001]; /* Router mode supported */
+ pseudo_bit_t reserved26[0x00006];
+/* -------------- */
+ pseudo_bit_t log_pg_sz[0x00008]; /* Minimum system page size supported (log2).
+ For proper operation it must be less than or equal the hosting platform (CPU) minimum page size. */
+ pseudo_bit_t reserved27[0x00008];
+ pseudo_bit_t uar_sz[0x00006]; /* UAR Area Size = 1MB * 2^uar_sz */
+ pseudo_bit_t reserved28[0x00006];
+ pseudo_bit_t num_rsvd_uars[0x00004];/* The number of UARs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_uars-1
+ Note that UAR number num_reserved_uars is always for the kernel. */
+/* -------------- */
+ pseudo_bit_t reserved29[0x00020];
+/* -------------- */
+ pseudo_bit_t max_desc_sz_sq[0x00010];/* Max descriptor size in bytes for the send queue */
+ pseudo_bit_t max_sg_sq[0x00008]; /* The maximum S/G list elements in a SQ WQE (max_desc_sz/16 - 3) */
+ pseudo_bit_t reserved30[0x00008];
+/* -------------- */
+ pseudo_bit_t max_desc_sz_rq[0x00010];/* Max descriptor size in bytes for the receive queue */
+ pseudo_bit_t max_sg_rq[0x00008]; /* The maximum S/G list elements in a RQ WQE (max_desc_sz/16 - 3) */
+ pseudo_bit_t reserved31[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved32[0x00040];
+/* -------------- */
+ pseudo_bit_t log_max_mcg[0x00008]; /* Log2 of the maximum number of multicast groups */
+ pseudo_bit_t num_rsvd_mcgs[0x00004];/* The number of MGMs reserved for firmware use in the MGHT.
+ The reserved resources are numbered from 0 to num_reserved_mcgs-1
+ If 0 - no resources are reserved. */
+ pseudo_bit_t reserved33[0x00004];
+ pseudo_bit_t log_max_qp_mcg[0x00008];/* Log2 of the maximum number of QPs per multicast group */
+ pseudo_bit_t reserved34[0x00008];
+/* -------------- */
+ pseudo_bit_t log_max_rdds[0x00006]; /* Log2 of the maximum number of RDDs */
+ pseudo_bit_t reserved35[0x00006];
+ pseudo_bit_t num_rsvd_rdds[0x00004];/* The number of RDDs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_rdds-1.
+ If 0 - no resources are reserved. */
+ pseudo_bit_t log_max_pd[0x00006]; /* Log2 of the maximum number of PDs */
+ pseudo_bit_t reserved36[0x00006];
+ pseudo_bit_t num_rsvd_pds[0x00004]; /* The number of PDs reserved for firmware use
+ The reserved resources are numbered from 0 to num_reserved_pds-1
+ If 0 - no resources are reserved. */
+/* -------------- */
+ pseudo_bit_t reserved37[0x000c0];
+/* -------------- */
+ pseudo_bit_t qpc_entry_sz[0x00010]; /* QPC Entry Size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 256 bytes */
+ pseudo_bit_t eec_entry_sz[0x00010]; /* EEC Entry Size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 256 bytes */
+/* -------------- */
+ pseudo_bit_t eqpc_entry_sz[0x00010];/* Extended QPC entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 32 bytes */
+ pseudo_bit_t eeec_entry_sz[0x00010];/* Extended EEC entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 32 bytes */
+/* -------------- */
+ pseudo_bit_t cqc_entry_sz[0x00010]; /* CQC entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 64 bytes */
+ pseudo_bit_t eqc_entry_sz[0x00010]; /* EQ context entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 64 bytes */
+/* -------------- */
+ pseudo_bit_t uar_scratch_entry_sz[0x00010];/* UAR Scratchpad Entry Size
+ For the InfiniHost-III-EX MT25208 entry size is 32 bytes */
+ pseudo_bit_t srq_entry_sz[0x00010]; /* SRQ context entry size for the device
+ For the InfiniHost-III-EX MT25208 entry size is 32 bytes */
+/* -------------- */
+ pseudo_bit_t mpt_entry_sz[0x00010]; /* MPT entry size in Bytes for the device.
+ For the InfiniHost-III-EX MT25208 entry size is 64 bytes */
+ pseudo_bit_t mtt_entry_sz[0x00010]; /* MTT entry size in Bytes for the device.
+ For the InfiniHost-III-EX MT25208 entry size is 8 bytes */
+/* -------------- */
+ pseudo_bit_t bmme[0x00001]; /* Base Memory Management Extension Support */
+ pseudo_bit_t win_type[0x00001]; /* Bound Type 2 Memory Window Association mechanism:
+ 0 - Type 2A - QP Number Association; or
+ 1 - Type 2B - QP Number and PD Association. */
+ pseudo_bit_t mps[0x00001]; /* Ability of this HCA to support multiple page sizes per Memory Region. */
+ pseudo_bit_t bl[0x00001]; /* Ability of this HCA to support Block List Physical Buffer Lists. (The device does not supports Block List) */
+ pseudo_bit_t zb[0x00001]; /* Zero Based region/windows supported */
+ pseudo_bit_t lif[0x00001]; /* Ability of this HCA to support Local Invalidate Fencing. */
+ pseudo_bit_t reserved38[0x00002];
+ pseudo_bit_t log_pbl_sz[0x00006]; /* Log2 of the Maximum Physical Buffer List size in Bytes supported by this HCA when invoking the Allocate L_Key verb.
+ */
+ pseudo_bit_t reserved39[0x00012];
+/* -------------- */
+ pseudo_bit_t resd_lkey[0x00020]; /* The value of the reserved Lkey for Base Memory Management Extension */
+/* -------------- */
+ pseudo_bit_t lamr[0x00001]; /* When set the device requires local attached memory in order to operate.
+ When set, ICM pages, Firmware Area and ICM auxiliary pages must be allocated in the local attached memory. */
+ pseudo_bit_t reserved40[0x0001f];
+/* -------------- */
+ pseudo_bit_t max_icm_size_h[0x00020];/* Bits [63:32] of maximum ICM size InfiniHost III Ex support in bytes. */
+/* -------------- */
+ pseudo_bit_t max_icm_size_l[0x00020];/* Bits [31:0] of maximum ICM size InfiniHost III Ex support in bytes. */
+/* -------------- */
+ pseudo_bit_t reserved41[0x002c0];
+/* -------------- */
+};
+
+/* QUERY_ADAPTER Parameters Block */
+
+struct arbelprm_query_adapter_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00018];
+ pseudo_bit_t intapin[0x00008]; /* Driver should set this field to INTR value in the event queue in order to get Express interrupt messages. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00060];
+/* -------------- */
+ struct arbelprm_vsd_st vsd;
+/* -------------- */
+};
+
+/* QUERY_FW Parameters Block */
+
+struct arbelprm_query_fw_st { /* Little Endian */
+ pseudo_bit_t fw_rev_major[0x00010]; /* Firmware Revision - Major */
+ pseudo_bit_t fw_pages[0x00010]; /* Amount of physical memory to be allocated for FW usage is in 4KByte pages. */
+/* -------------- */
+ pseudo_bit_t fw_rev_minor[0x00010]; /* Firmware Revision - Minor */
+ pseudo_bit_t fw_rev_subminor[0x00010];/* Firmware Sub-minor version (Patch level). */
+/* -------------- */
+ pseudo_bit_t cmd_interface_rev[0x00010];/* Command Interface Interpreter Revision ID */
+ pseudo_bit_t reserved0[0x0000e];
+ pseudo_bit_t wqe_h_mode[0x00001]; /* Hermon mode. If '1', then WQE and AV format is the advanced format */
+ pseudo_bit_t zb_wq_cq[0x00001]; /* If '1', then ZB mode of WQ and CQ are enabled (i.e. real Memfree PRM is supported) */
+/* -------------- */
+ pseudo_bit_t log_max_outstanding_cmd[0x00008];/* Log2 of the maximum number of commands the HCR can support simultaneously */
+ pseudo_bit_t reserved1[0x00017];
+ pseudo_bit_t dt[0x00001]; /* Debug Trace Support
+ 0 - Debug trace is not supported
+ 1 - Debug trace is supported */
+/* -------------- */
+ pseudo_bit_t cmd_interface_db[0x00001];/* Set if the device accepts commands by means of special doorbells */
+ pseudo_bit_t reserved2[0x0001f];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+ pseudo_bit_t clr_int_base_addr_h[0x00020];/* Bits [63:32] of Clear interrupt register physical address.
+ Points to 64 bit register. */
+/* -------------- */
+ pseudo_bit_t clr_int_base_addr_l[0x00020];/* Bits [31:0] of Clear interrupt register physical address.
+ Points to 64 bit register. */
+/* -------------- */
+ pseudo_bit_t reserved4[0x00040];
+/* -------------- */
+ pseudo_bit_t error_buf_start_h[0x00020];/* Read Only buffer for catastrophic error reports (physical address) */
+/* -------------- */
+ pseudo_bit_t error_buf_start_l[0x00020];/* Read Only buffer for catastrophic error reports (physical address) */
+/* -------------- */
+ pseudo_bit_t error_buf_size[0x00020];/* Size in words */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t eq_arm_base_addr_h[0x00020];/* Bits [63:32] of EQ Arm DBs physical address.
+ Points to 64 bit register.
+ Setting bit x in the offset, arms EQ number x.
+ */
+/* -------------- */
+ pseudo_bit_t eq_arm_base_addr_l[0x00020];/* Bits [31:0] of EQ Arm DBs physical address.
+ Points to 64 bit register.
+ Setting bit x in the offset, arms EQ number x. */
+/* -------------- */
+ pseudo_bit_t eq_set_ci_base_addr_h[0x00020];/* Bits [63:32] of EQ Set CI DBs Table physical address.
+ Points to a the EQ Set CI DBs Table base address. */
+/* -------------- */
+ pseudo_bit_t eq_set_ci_base_addr_l[0x00020];/* Bits [31:0] of EQ Set CI DBs Table physical address.
+ Points to a the EQ Set CI DBs Table base address. */
+/* -------------- */
+ pseudo_bit_t cmd_db_dw1[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 1 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw0[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 0 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_dw3[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 3 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw2[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 2 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_dw5[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 5 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw4[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 4 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_dw7[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 7 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+ pseudo_bit_t cmd_db_dw6[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 6 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_addr_base_h[0x00020];/* High bits of cmd_db_addr_base, which cmd_db_dw offsets refer to. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t cmd_db_addr_base_l[0x00020];/* Low bits of cmd_db_addr_base, which cmd_db_dw offsets refer to. Valid only if CmdInterfaceDb bit is '1' */
+/* -------------- */
+ pseudo_bit_t reserved6[0x004c0];
+/* -------------- */
+};
+
+/* ACCESS_LAM */
+
+struct arbelprm_access_lam_st { /* Little Endian */
+ struct arbelprm_access_lam_inject_errors_st access_lam_inject_errors;
+/* -------------- */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+};
+
+/* ENABLE_LAM Parameters Block */
+
+struct arbelprm_enable_lam_st { /* Little Endian */
+ pseudo_bit_t lam_start_adr_h[0x00020];/* LAM start address [63:32] */
+/* -------------- */
+ pseudo_bit_t lam_start_adr_l[0x00020];/* LAM start address [31:0] */
+/* -------------- */
+ pseudo_bit_t lam_end_adr_h[0x00020];/* LAM end address [63:32] */
+/* -------------- */
+ pseudo_bit_t lam_end_adr_l[0x00020];/* LAM end address [31:0] */
+/* -------------- */
+ pseudo_bit_t di[0x00002]; /* Data Integrity Configuration:
+ 00 - none
+ 01 - Parity
+ 10 - ECC Detection Only
+ 11 - ECC With Correction */
+ pseudo_bit_t ap[0x00002]; /* Auto Precharge Mode
+ 00 - No auto precharge
+ 01 - Auto precharge per transaction
+ 10 - Auto precharge per 64 bytes
+ 11 - reserved */
+ pseudo_bit_t dh[0x00001]; /* When set, LAM is Hidden and can not be accessed directly from the PCI bus. */
+ pseudo_bit_t reserved0[0x0001b];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00160];
+/* -------------- */
+ struct arbelprm_dimminfo_st dimm0; /* Logical DIMM 0 Parameters */
+/* -------------- */
+ struct arbelprm_dimminfo_st dimm1; /* Logical DIMM 1 Parameters */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00400];
+/* -------------- */
+};
+
+/* Memory Access Parameters for UD Address Vector Table */
+
+struct arbelprm_udavtable_memory_parameters_st { /* Little Endian */
+ pseudo_bit_t l_key[0x00020]; /* L_Key used to access TPT */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* PD used by TPT for matching against PD of region entry being accessed. */
+ pseudo_bit_t reserved0[0x00005];
+ pseudo_bit_t xlation_en[0x00001]; /* When cleared, address is physical address and no translation will be done. When set, address is virtual. */
+ pseudo_bit_t reserved1[0x00002];
+/* -------------- */
+};
+
+/* INIT_HCA & QUERY_HCA Parameters Block */
+
+struct arbelprm_init_hca_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00060];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00010];
+ pseudo_bit_t time_stamp_granularity[0x00008];/* This field controls the granularity in which CQE Timestamp counter is incremented.
+ The TimeStampGranularity units is 1/4 of a microseconds. (e.g is TimeStampGranularity is configured to 0x2, CQE Timestamp will be incremented every one microsecond)
+ When sets to Zero, timestamp reporting in the CQE is disabled.
+ This feature is currently not supported.
+ */
+ pseudo_bit_t hca_core_clock[0x00008];/* Internal Clock Period (in units of 1/16 ns) (QUERY_HCA only) */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00008];
+ pseudo_bit_t router_qp[0x00010]; /* Upper 16 bit to be used as a QP number for router mode. Low order 8 bits are taken from the TClass field of the incoming packet.
+ Valid only if RE bit is set */
+ pseudo_bit_t reserved3[0x00007];
+ pseudo_bit_t re[0x00001]; /* Router Mode Enable
+ If this bit is set, entire packet (including all headers and ICRC) will be considered as a data payload and will be scattered to memory as specified in the descriptor that is posted on the QP matching the TClass field of packet. */
+/* -------------- */
+ pseudo_bit_t udp[0x00001]; /* UD Port Check Enable
+ 0 - Port field in Address Vector is ignored
+ 1 - HCA will check the port field in AV entry (fetched for UD descriptor) against the Port of the UD QP executing the descriptor. */
+ pseudo_bit_t he[0x00001]; /* Host Endianess - Used for Atomic Operations
+ 0 - Host is Little Endian
+ 1 - Host is Big endian
+ */
+ pseudo_bit_t reserved4[0x00001];
+ pseudo_bit_t ce[0x00001]; /* Checksum Enabled - when Set IPoverIB checksum generation & checking is enabled */
+ pseudo_bit_t sph[0x00001]; /* 0 - SW calculates TCP/UDP Pseudo-Header checksum and inserts it into the TCP/UDP checksum field when sending a packet
+ 1 - HW calculates TCP/UDP Pseudo-Header checksum when sending a packet
+ */
+ pseudo_bit_t rph[0x00001]; /* 0 - Not HW calculation of TCP/UDP Pseudo-Header checksum are done when receiving a packet
+ 1 - HW calculates TCP/UDP Pseudo-Header checksum when receiving a packet
+ */
+ pseudo_bit_t reserved5[0x00002];
+ pseudo_bit_t responder_exu[0x00004];/* Indicate the relation between the execution enegines allocation dedicated for responder versus the engines dedicated for reqvester .
+ responder_exu/16 = (number of responder exu engines)/(total number of engines)
+ Legal values are 0x0-0xF. 0 is "auto".
+
+ */
+ pseudo_bit_t reserved6[0x00004];
+ pseudo_bit_t wqe_quota[0x0000f]; /* Maximum number of WQEs that are executed prior to preemption of execution unit. 0 - reserved. */
+ pseudo_bit_t wqe_quota_en[0x00001]; /* If set - wqe_quota field is used. If cleared - WQE quota is set to "auto" value */
+/* -------------- */
+ pseudo_bit_t reserved7[0x00040];
+/* -------------- */
+ struct arbelprm_qpcbaseaddr_st qpc_eec_cqc_eqc_rdb_parameters;
+/* -------------- */
+ pseudo_bit_t reserved8[0x00100];
+/* -------------- */
+ struct arbelprm_multicastparam_st multicast_parameters;
+/* -------------- */
+ pseudo_bit_t reserved9[0x00080];
+/* -------------- */
+ struct arbelprm_tptparams_st tpt_parameters;
+/* -------------- */
+ pseudo_bit_t reserved10[0x00080];
+/* -------------- */
+ struct arbelprm_uar_params_st uar_parameters;/* UAR Parameters */
+/* -------------- */
+ pseudo_bit_t reserved11[0x00600];
+/* -------------- */
+};
+
+/* Event Queue Context Table Entry */
+
+struct arbelprm_eqc_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t st[0x00004]; /* Event delivery state machine
+ 0x9 - Armed
+ 0xA - Fired
+ 0xB - Always_Armed (auto-rearm)
+ other - reserved */
+ pseudo_bit_t reserved1[0x00005];
+ pseudo_bit_t oi[0x00001]; /* Oerrun ignore.
+ If set, HW will not check EQ full condition when writing new EQEs. */
+ pseudo_bit_t tr[0x00001]; /* Translation Required. If set - EQ access undergo address translation. */
+ pseudo_bit_t reserved2[0x00005];
+ pseudo_bit_t owner[0x00004]; /* 0 - SW ownership
+ 1 - HW ownership
+ Valid for the QUERY_EQ and HW2SW_EQ commands only */
+ pseudo_bit_t status[0x00004]; /* EQ status:
+ 0000 - OK
+ 1010 - EQ write failure
+ Valid for the QUERY_EQ and HW2SW_EQ commands only */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start Address of Event Queue[63:32]. */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start Address of Event Queue[31:0].
+ Must be aligned on 32-byte boundary */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00018];
+ pseudo_bit_t log_eq_size[0x00005]; /* Amount of entries in this EQ is 2^log_eq_size.
+ Log_eq_size must be bigger than 1.
+ Maximum EQ size is 2^17 EQEs (max Log_eq_size is 17). */
+ pseudo_bit_t reserved4[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t intr[0x00008]; /* Interrupt (message) to be generated to report event to INT layer.
+ 00iiiiii - set to INTA given in QUERY_ADAPTER in order to generate INTA messages on Express.
+ 10jjjjjj - specificies type of interrupt message to be generated (total 64 different messages supported).
+ All other values are reserved and should not be used.
+
+ If interrupt generation is not required, ST field must be set upon creation to Fired state. No EQ arming doorbell should be performed. In this case hardware will not generate any interrupt. */
+ pseudo_bit_t reserved6[0x00018];
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* PD to be used to access EQ */
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t lkey[0x00020]; /* Memory key (L-Key) to be used to access EQ */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00040];
+/* -------------- */
+ pseudo_bit_t consumer_indx[0x00020];/* Contains next entry to be read upon polling the event queue.
+ Must be initalized to zero while opening EQ */
+/* -------------- */
+ pseudo_bit_t producer_indx[0x00020];/* Contains next entry in EQ to be written by the HCA.
+ Must be initalized to zero while opening EQ. */
+/* -------------- */
+ pseudo_bit_t reserved9[0x00080];
+/* -------------- */
+};
+
+/* Memory Translation Table (MTT) Entry */
+
+struct arbelprm_mtt_st { /* Little Endian */
+ pseudo_bit_t ptag_h[0x00020]; /* High-order bits of physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */
+/* -------------- */
+ pseudo_bit_t p[0x00001]; /* Present bit. If set, page entry is valid. If cleared, access to this page will generate non-present page access fault. */
+ pseudo_bit_t reserved0[0x0000b];
+ pseudo_bit_t ptag_l[0x00014]; /* Low-order bits of Physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */
+/* -------------- */
+};
+
+/* Memory Protection Table (MPT) Entry */
+
+struct arbelprm_mpt_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t r_w[0x00001]; /* Defines whether this entry is Region (1) or Window (0) */
+ pseudo_bit_t pa[0x00001]; /* Physical address. If set, no virtual-to-physical address translation will be performed for this region */
+ pseudo_bit_t lr[0x00001]; /* If set - local read access enabled */
+ pseudo_bit_t lw[0x00001]; /* If set - local write access enabled */
+ pseudo_bit_t rr[0x00001]; /* If set - remote read access enabled. */
+ pseudo_bit_t rw[0x00001]; /* If set - remote write access enabled */
+ pseudo_bit_t a[0x00001]; /* If set - remote Atomic access is enabled */
+ pseudo_bit_t eb[0x00001]; /* If set - Bind is enabled. Valid for region entry only. */
+ pseudo_bit_t reserved1[0x0000c];
+ pseudo_bit_t status[0x00004]; /* Region/Window Status
+ 0xF - not valid (SW ownership)
+ 0x3 - FREE state
+ else - HW ownership
+ Unbound Type I windows are doneted reg_wnd_len field equals zero.
+ Unbound Type II windows are donated by Status=FREE. */
+/* -------------- */
+ pseudo_bit_t page_size[0x00005]; /* Page size used for the region. Actual size is [4K]*2^Page_size bytes.
+ page_size should be less than 20. */
+ pseudo_bit_t reserved2[0x00002];
+ pseudo_bit_t type[0x00001]; /* Applicable for windows only, must be zero for regions
+ 0 - Type one window
+ 1 - Type two window */
+ pseudo_bit_t qpn[0x00018]; /* QP number this MW is attached to. Valid for type2 memory windows and on QUERY_MPT only */
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* The memory Key. The field holds the mem_key field in the following semantics: {key[7:0],key[31:8]}.
+ */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* Protection Domain */
+ pseudo_bit_t reserved3[0x00001];
+ pseudo_bit_t ei[0x00001]; /* Enable Invalidation - When set, Local/Remote invalidation can be executed on this window/region.
+ Must be set for type2 windows and non-shared physical memory regions.
+ Must be clear for regions that are used to access Work Queues, Completion Queues and Event Queues */
+ pseudo_bit_t zb[0x00001]; /* When set, this region is Zero Based Region */
+ pseudo_bit_t fre[0x00001]; /* When set, Fast Registration Operations can be executed on this region */
+ pseudo_bit_t rae[0x00001]; /* When set, remote access can be enabled on this region.
+ Used when executing Fast Registration Work Request to validate that remote access rights can be granted to this MPT.
+ If the bit is cleared, Fast Registration Work Request requesting remote access rights will fail.
+ */
+ pseudo_bit_t reserved4[0x00003];
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start Address[63:32] - Virtual Address where this region/window starts */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start Address[31:0] - Virtual Address where this region/window starts */
+/* -------------- */
+ pseudo_bit_t reg_wnd_len_h[0x00020];/* Region/Window Length[63:32] */
+/* -------------- */
+ pseudo_bit_t reg_wnd_len_l[0x00020];/* Region/Window Length[31:0] */
+/* -------------- */
+ pseudo_bit_t lkey[0x00020]; /* Must be 0 for SW2HW_MPT.
+ On QUERY_MPT and HW2SW_MPT commands for Memory Window it reflects the LKey of the Region that the Window is bound to.
+ The field holds the lkey field in the following semantics: {key[7:0],key[31:8]}. */
+/* -------------- */
+ pseudo_bit_t win_cnt[0x00020]; /* Number of windows bound to this region. Valid for regions only.
+ The field is valid only for the QUERY_MPT and HW2SW_MPT commands. */
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t mtt_adr_h[0x00006]; /* Base (first) address of the MTT relative to MTT base in the ICM */
+ pseudo_bit_t reserved6[0x0001a];
+/* -------------- */
+ pseudo_bit_t reserved7[0x00003];
+ pseudo_bit_t mtt_adr_l[0x0001d]; /* Base (first) address of the MTT relative to MTT base address in the ICM. Must be aligned on 8 bytes. */
+/* -------------- */
+ pseudo_bit_t mtt_sz[0x00020]; /* Number of MTT entries allocated for this MR.
+ When Fast Registration Operations can not be executed on this region (FRE bit is zero) this field is reserved.
+ When Fast Registration Operation is enabled (FRE bit is set) this field indicates the number of MTTs allocated for this MR. If mtt_sz value is zero, there is no limit for the numbers of MTTs and the HCA does not check this field when executing fast register WQE. */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00040];
+/* -------------- */
+};
+
+/* Completion Queue Context Table Entry */
+
+struct arbelprm_completion_queue_context_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t st[0x00004]; /* Event delivery state machine
+ 0x0 - reserved
+ 0x9 - ARMED (Request for Notification)
+ 0x6 - ARMED SOLICITED (Request Solicited Notification)
+ 0xA - FIRED
+ other - reserved
+
+ Must be 0x0 in CQ initialization.
+ Valid for the QUERY_CQ and HW2SW_CQ commands only. */
+ pseudo_bit_t reserved1[0x00005];
+ pseudo_bit_t oi[0x00001]; /* When set, overrun ignore is enabled.
+ When set, Updates of CQ consumer counter (poll for completion) or Request completion notifications (Arm CQ) doorbells should not be rang on that CQ. */
+ pseudo_bit_t reserved2[0x0000a];
+ pseudo_bit_t status[0x00004]; /* CQ status
+ 0000 - OK
+ 1001 - CQ overflow
+ 1010 - CQ write failure
+ Valid for the QUERY_CQ and HW2SW_CQ commands only */
+/* -------------- */
+ pseudo_bit_t start_address_h[0x00020];/* Start address of CQ[63:32].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t start_address_l[0x00020];/* Start address of CQ[31:0].
+ Must be aligned on CQE size (32 bytes) */
+/* -------------- */
+ pseudo_bit_t usr_page[0x00018]; /* UAR page this CQ can be accessed through (ringinig CQ doorbells) */
+ pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries).
+ Maximum CQ size is 2^17 CQEs (max log_cq_size is 17) */
+ pseudo_bit_t reserved3[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00020];
+/* -------------- */
+ pseudo_bit_t c_eqn[0x00008]; /* Event Queue this CQ reports completion events to.
+ Valid values are 0 to 63
+ If configured to value other than 0-63, completion events will not be reported on the CQ. */
+ pseudo_bit_t reserved5[0x00018];
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* Protection Domain to be used to access CQ.
+ Must be the same PD of the CQ L_Key. */
+ pseudo_bit_t reserved6[0x00008];
+/* -------------- */
+ pseudo_bit_t l_key[0x00020]; /* Memory key (L_Key) to be used to access CQ */
+/* -------------- */
+ pseudo_bit_t last_notified_indx[0x00020];/* Maintained by HW.
+ Valid for QUERY_CQ and HW2SW_CQ commands only. */
+/* -------------- */
+ pseudo_bit_t solicit_producer_indx[0x00020];/* Maintained by HW.
+ Valid for QUERY_CQ and HW2SW_CQ commands only.
+ */
+/* -------------- */
+ pseudo_bit_t consumer_counter[0x00020];/* Consumer counter is a 32bits counter that is incremented for each CQE pooled from the CQ.
+ Must be 0x0 in CQ initialization.
+ Valid for the QUERY_CQ and HW2SW_CQ commands only. */
+/* -------------- */
+ pseudo_bit_t producer_counter[0x00020];/* Producer counter is a 32bits counter that is incremented for each CQE that is written by the HW to the CQ.
+ CQ overrun is reported if Producer_counter + 1 equals to Consumer_counter and a CQE needs to be added..
+ Maintained by HW (valid for the QUERY_CQ and HW2SW_CQ commands only) */
+/* -------------- */
+ pseudo_bit_t cqn[0x00018]; /* CQ number. Least significant bits are constrained by the position of this CQ in CQC table
+ Valid for the QUERY_CQ and HW2SW_CQ commands only */
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t cq_ci_db_record[0x00020];/* Index in the UAR Context Table Entry.
+ HW uses this index as an offset from the UAR Context Table Entry in order to read this CQ Consumer Counter doorbell record.
+ This value can be retrieved from the HW in the QUERY_CQ command. */
+/* -------------- */
+ pseudo_bit_t cq_state_db_record[0x00020];/* Index in the UAR Context Table Entry.
+ HW uses this index as an offset from the UAR Context Table Entry in order to read this CQ state doorbell record.
+ This value can be retrieved from the HW in the QUERY_CQ command. */
+/* -------------- */
+ pseudo_bit_t reserved8[0x00020];
+/* -------------- */
+};
+
+/* GPIO_event_data */
+
+struct arbelprm_gpio_event_data_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00060];
+/* -------------- */
+ pseudo_bit_t gpio_event_hi[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */
+/* -------------- */
+ pseudo_bit_t gpio_event_lo[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+};
+
+/* Event_data Field - QP/EE Events */
+
+struct arbelprm_qp_ee_event_st { /* Little Endian */
+ pseudo_bit_t qpn_een[0x00018]; /* QP/EE/SRQ number event is reported for */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved2[0x0001c];
+ pseudo_bit_t e_q[0x00001]; /* If set - EEN if cleared - QP in the QPN/EEN field
+ Not valid on SRQ events */
+ pseudo_bit_t reserved3[0x00003];
+/* -------------- */
+ pseudo_bit_t reserved4[0x00060];
+/* -------------- */
+};
+
+/* InfiniHost-III-EX Type0 Configuration Header */
+
+struct arbelprm_mt25208_type0_st { /* Little Endian */
+ pseudo_bit_t vendor_id[0x00010]; /* Hardwired to 0x15B3 */
+ pseudo_bit_t device_id[0x00010]; /* 25208 (decimal) - InfiniHost-III compatible mode
+ 25218 (decimal) - InfiniHost-III EX mode (the mode described in this manual)
+ 25209 (decimal) - Flash burner mode - see Flash burning application note for further details on this mode
+ */
+/* -------------- */
+ pseudo_bit_t command[0x00010]; /* PCI Command Register */
+ pseudo_bit_t status[0x00010]; /* PCI Status Register */
+/* -------------- */
+ pseudo_bit_t revision_id[0x00008];
+ pseudo_bit_t class_code_hca_class_code[0x00018];
+/* -------------- */
+ pseudo_bit_t cache_line_size[0x00008];/* Cache Line Size */
+ pseudo_bit_t latency_timer[0x00008];
+ pseudo_bit_t header_type[0x00008]; /* hardwired to zero */
+ pseudo_bit_t bist[0x00008];
+/* -------------- */
+ pseudo_bit_t bar0_ctrl[0x00004]; /* hard-wired to 0100 */
+ pseudo_bit_t reserved0[0x00010];
+ pseudo_bit_t bar0_l[0x0000c]; /* Lower bits of BAR0 (Device Configuration Space) */
+/* -------------- */
+ pseudo_bit_t bar0_h[0x00020]; /* Upper 32 bits of BAR0 (Device Configuration Space) */
+/* -------------- */
+ pseudo_bit_t bar1_ctrl[0x00004]; /* Hardwired to 1100 */
+ pseudo_bit_t reserved1[0x00010];
+ pseudo_bit_t bar1_l[0x0000c]; /* Lower bits of BAR1 (User Access Region - UAR - space) */
+/* -------------- */
+ pseudo_bit_t bar1_h[0x00020]; /* upper 32 bits of BAR1 (User Access Region - UAR - space) */
+/* -------------- */
+ pseudo_bit_t bar2_ctrl[0x00004]; /* Hardwired to 1100 */
+ pseudo_bit_t reserved2[0x00010];
+ pseudo_bit_t bar2_l[0x0000c]; /* Lower bits of BAR2 - Local Attached Memory if present and enabled. Else zeroed. */
+/* -------------- */
+ pseudo_bit_t bar2_h[0x00020]; /* Upper 32 bits of BAR2 - Local Attached Memory if present and enabled. Else zeroed. */
+/* -------------- */
+ pseudo_bit_t cardbus_cis_pointer[0x00020];
+/* -------------- */
+ pseudo_bit_t subsystem_vendor_id[0x00010];/* Specified by the device NVMEM configuration */
+ pseudo_bit_t subsystem_id[0x00010]; /* Specified by the device NVMEM configuration */
+/* -------------- */
+ pseudo_bit_t expansion_rom_enable[0x00001];/* Expansion ROM Enable. Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */
+ pseudo_bit_t reserved3[0x0000a];
+ pseudo_bit_t expansion_rom_base_address[0x00015];/* Expansion ROM Base Address (upper 21 bit). Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */
+/* -------------- */
+ pseudo_bit_t capabilities_pointer[0x00008];/* Specified by the device NVMEM configuration */
+ pseudo_bit_t reserved4[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved5[0x00020];
+/* -------------- */
+ pseudo_bit_t interrupt_line[0x00008];
+ pseudo_bit_t interrupt_pin[0x00008];
+ pseudo_bit_t min_gnt[0x00008];
+ pseudo_bit_t max_latency[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved6[0x00100];
+/* -------------- */
+ pseudo_bit_t msi_cap_id[0x00008];
+ pseudo_bit_t msi_next_cap_ptr[0x00008];
+ pseudo_bit_t msi_en[0x00001];
+ pseudo_bit_t multiple_msg_cap[0x00003];
+ pseudo_bit_t multiple_msg_en[0x00003];
+ pseudo_bit_t cap_64_bit_addr[0x00001];
+ pseudo_bit_t reserved7[0x00008];
+/* -------------- */
+ pseudo_bit_t msg_addr_l[0x00020];
+/* -------------- */
+ pseudo_bit_t msg_addr_h[0x00020];
+/* -------------- */
+ pseudo_bit_t msg_data[0x00010];
+ pseudo_bit_t reserved8[0x00010];
+/* -------------- */
+ pseudo_bit_t reserved9[0x00080];
+/* -------------- */
+ pseudo_bit_t pm_cap_id[0x00008]; /* Power management capability ID - 01h */
+ pseudo_bit_t pm_next_cap_ptr[0x00008];
+ pseudo_bit_t pm_cap[0x00010]; /* [2:0] Version - 02h
+ [3] PME clock - 0h
+ [4] RsvP
+ [5] Device specific initialization - 0h
+ [8:6] AUX current - 0h
+ [9] D1 support - 0h
+ [10] D2 support - 0h
+ [15:11] PME support - 0h */
+/* -------------- */
+ pseudo_bit_t pm_status_control[0x00010];/* [14:13] - Data scale - 0h */
+ pseudo_bit_t pm_control_status_brdg_ext[0x00008];
+ pseudo_bit_t data[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved10[0x00040];
+/* -------------- */
+ pseudo_bit_t vpd_cap_id[0x00008]; /* 03h */
+ pseudo_bit_t vpd_next_cap_id[0x00008];
+ pseudo_bit_t vpd_address[0x0000f];
+ pseudo_bit_t f[0x00001];
+/* -------------- */
+ pseudo_bit_t vpd_data[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved11[0x00040];
+/* -------------- */
+ pseudo_bit_t pciex_cap_id[0x00008]; /* PCI-Express capability ID - 10h */
+ pseudo_bit_t pciex_next_cap_ptr[0x00008];
+ pseudo_bit_t pciex_cap[0x00010]; /* [3:0] Capability version - 1h
+ [7:4] Device/Port Type - 0h
+ [8] Slot implemented - 0h
+ [13:9] Interrupt message number
+ */
+/* -------------- */
+ pseudo_bit_t device_cap[0x00020]; /* [2:0] Max_Payload_Size supported - 2h
+ [4:3] Phantom Function supported - 0h
+ [5] Extended Tag Filed supported - 0h
+ [8:6] Endpoint L0s Acceptable Latency - TBD
+ [11:9] Endpoint L1 Acceptable Latency - TBD
+ [12] Attention Button Present - configured through InfiniBurn
+ [13] Attention Indicator Present - configured through InfiniBurn
+ [14] Power Indicator Present - configured through InfiniBurn
+ [25:18] Captured Slot Power Limit Value
+ [27:26] Captured Slot Power Limit Scale */
+/* -------------- */
+ pseudo_bit_t device_control[0x00010];
+ pseudo_bit_t device_status[0x00010];
+/* -------------- */
+ pseudo_bit_t link_cap[0x00020]; /* [3:0] Maximum Link Speed - 1h
+ [9:4] Maximum Link Width - 8h
+ [11:10] Active State Power Management Support - 3h
+ [14:12] L0s Exit Latency - TBD
+ [17:15] L1 Exit Latency - TBD
+ [31:24] Port Number - 0h */
+/* -------------- */
+ pseudo_bit_t link_control[0x00010];
+ pseudo_bit_t link_status[0x00010]; /* [3:0] Link Speed - 1h
+ [9:4] Negotiated Link Width
+ [12] Slot clock configuration - 1h */
+/* -------------- */
+ pseudo_bit_t reserved12[0x00260];
+/* -------------- */
+ pseudo_bit_t advanced_error_reporting_cap_id[0x00010];/* 0001h. */
+ pseudo_bit_t capability_version[0x00004];/* 1h */
+ pseudo_bit_t next_capability_offset[0x0000c];/* 0h */
+/* -------------- */
+ pseudo_bit_t uncorrectable_error_status_register[0x00020];/* 0 Training Error Status
+ 4 Data Link Protocol Error Status
+ 12 Poisoned TLP Status
+ 13 Flow Control Protocol Error Status
+ 14 Completion Timeout Status
+ 15 Completer Abort Status
+ 16 Unexpected Completion Status
+ 17 Receiver Overflow Status
+ 18 Malformed TLP Status
+ 19 ECRC Error Status
+ 20 Unsupported Request Error Status */
+/* -------------- */
+ pseudo_bit_t uncorrectable_error_mask_register[0x00020];/* 0 Training Error Mask
+ 4 Data Link Protocol Error Mask
+ 12 Poisoned TLP Mask
+ 13 Flow Control Protocol Error Mask
+ 14 Completion Timeout Mask
+ 15 Completer Abort Mask
+ 16 Unexpected Completion Mask
+ 17 Receiver Overflow Mask
+ 18 Malformed TLP Mask
+ 19 ECRC Error Mask
+ 20 Unsupported Request Error Mask */
+/* -------------- */
+ pseudo_bit_t uncorrectable_severity_mask_register[0x00020];/* 0 Training Error Severity
+ 4 Data Link Protocol Error Severity
+ 12 Poisoned TLP Severity
+ 13 Flow Control Protocol Error Severity
+ 14 Completion Timeout Severity
+ 15 Completer Abort Severity
+ 16 Unexpected Completion Severity
+ 17 Receiver Overflow Severity
+ 18 Malformed TLP Severity
+ 19 ECRC Error Severity
+ 20 Unsupported Request Error Severity */
+/* -------------- */
+ pseudo_bit_t correctable_error_status_register[0x00020];/* 0 Receiver Error Status
+ 6 Bad TLP Status
+ 7 Bad DLLP Status
+ 8 REPLAY_NUM Rollover Status
+ 12 Replay Timer Timeout Status */
+/* -------------- */
+ pseudo_bit_t correctable_error_mask_register[0x00020];/* 0 Receiver Error Mask
+ 6 Bad TLP Mask
+ 7 Bad DLLP Mask
+ 8 REPLAY_NUM Rollover Mask
+ 12 Replay Timer Timeout Mask */
+/* -------------- */
+ pseudo_bit_t advance_error_capabilities_and_control_register[0x00020];
+/* -------------- */
+ struct arbelprm_header_log_register_st header_log_register;
+/* -------------- */
+ pseudo_bit_t reserved13[0x006a0];
+/* -------------- */
+};
+
+/* Event Data Field - Performance Monitor */
+
+struct arbelprm_performance_monitor_event_st { /* Little Endian */
+ struct arbelprm_performance_monitors_st performance_monitor_snapshot;/* Performance monitor snapshot */
+/* -------------- */
+ pseudo_bit_t monitor_number[0x00008];/* 0x01 - SQPC
+ 0x02 - RQPC
+ 0x03 - CQC
+ 0x04 - Rkey
+ 0x05 - TLB
+ 0x06 - port0
+ 0x07 - port1 */
+ pseudo_bit_t reserved0[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00040];
+/* -------------- */
+};
+
+/* Event_data Field - Page Faults */
+
+struct arbelprm_page_fault_event_data_st { /* Little Endian */
+ pseudo_bit_t va_h[0x00020]; /* Virtual Address[63:32] this page fault is reported on */
+/* -------------- */
+ pseudo_bit_t va_l[0x00020]; /* Virtual Address[63:32] this page fault is reported on */
+/* -------------- */
+ pseudo_bit_t mem_key[0x00020]; /* Memory Key this page fault is reported on */
+/* -------------- */
+ pseudo_bit_t qp[0x00018]; /* QP this page fault is reported on */
+ pseudo_bit_t reserved0[0x00003];
+ pseudo_bit_t a[0x00001]; /* If set the memory access that caused the page fault was atomic */
+ pseudo_bit_t lw[0x00001]; /* If set the memory access that caused the page fault was local write */
+ pseudo_bit_t lr[0x00001]; /* If set the memory access that caused the page fault was local read */
+ pseudo_bit_t rw[0x00001]; /* If set the memory access that caused the page fault was remote write */
+ pseudo_bit_t rr[0x00001]; /* If set the memory access that caused the page fault was remote read */
+/* -------------- */
+ pseudo_bit_t pd[0x00018]; /* PD this page fault is reported on */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t prefetch_len[0x00020]; /* Indicates how many subsequent pages in the same memory region/window will be accessed by the following transaction after this page fault is resolved. measured in bytes. SW can use this information in order to page-in the subsequent pages if they are not present. */
+/* -------------- */
+};
+
+/* WQE segments format */
+
+struct arbelprm_wqe_segment_st { /* Little Endian */
+ struct arbelprm_send_wqe_segment_st send_wqe_segment;/* Send WQE segment format */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00280];
+/* -------------- */
+ struct arbelprm_wqe_segment_ctrl_mlx_st mlx_wqe_segment_ctrl;/* MLX WQE segment format */
+/* -------------- */
+ pseudo_bit_t reserved1[0x00100];
+/* -------------- */
+ struct arbelprm_wqe_segment_ctrl_recv_st recv_wqe_segment_ctrl;/* Receive segment format */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00080];
+/* -------------- */
+};
+
+/* Event_data Field - Port State Change */
+
+struct arbelprm_port_state_change_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+ pseudo_bit_t reserved1[0x0001c];
+ pseudo_bit_t p[0x00002]; /* Port number (1 or 2) */
+ pseudo_bit_t reserved2[0x00002];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+};
+
+/* Event_data Field - Completion Queue Error */
+
+struct arbelprm_completion_queue_error_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x00020];
+/* -------------- */
+ pseudo_bit_t syndrome[0x00008]; /* Error syndrome
+ 0x01 - CQ overrun
+ 0x02 - CQ access violation error */
+ pseudo_bit_t reserved2[0x00018];
+/* -------------- */
+ pseudo_bit_t reserved3[0x00060];
+/* -------------- */
+};
+
+/* Event_data Field - Completion Event */
+
+struct arbelprm_completion_event_st { /* Little Endian */
+ pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */
+ pseudo_bit_t reserved0[0x00008];
+/* -------------- */
+ pseudo_bit_t reserved1[0x000a0];
+/* -------------- */
+};
+
+/* Event Queue Entry */
+
+struct arbelprm_event_queue_entry_st { /* Little Endian */
+ pseudo_bit_t event_sub_type[0x00008];/* Event Sub Type.
+ Defined for events which have sub types, zero elsewhere. */
+ pseudo_bit_t reserved0[0x00008];
+ pseudo_bit_t event_type[0x00008]; /* Event Type */
+ pseudo_bit_t reserved1[0x00008];
+/* -------------- */
+ pseudo_bit_t event_data[6][0x00020];/* Delivers auxilary data to handle event. */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00007];
+ pseudo_bit_t owner[0x00001]; /* Owner of the entry
+ 0 SW
+ 1 HW */
+ pseudo_bit_t reserved3[0x00018];
+/* -------------- */
+};
+
+/* QP/EE State Transitions Command Parameters */
+
+struct arbelprm_qp_ee_state_transitions_st { /* Little Endian */
+ pseudo_bit_t opt_param_mask[0x00020];/* This field defines which optional parameters are passed. Each bit specifies whether optional parameter is passed (set) or not (cleared). The optparammask is defined for each QP/EE command. */
+/* -------------- */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+ struct arbelprm_queue_pair_ee_context_entry_st qpc_eec_data;/* QPC/EEC data */
+/* -------------- */
+ pseudo_bit_t reserved1[0x009c0];
+/* -------------- */
+};
+
+/* Completion Queue Entry Format */
+
+struct arbelprm_completion_queue_entry_st { /* Little Endian */
+ pseudo_bit_t my_qpn[0x00018]; /* Indicates the QP for which completion is being reported */
+ pseudo_bit_t reserved0[0x00004];
+ pseudo_bit_t ver[0x00004]; /* CQE version.
+ 0 for InfiniHost-III-EX */
+/* -------------- */
+ pseudo_bit_t my_ee[0x00018]; /* EE context (for RD only).
+ Invalid for Bind and Nop operation on RD.
+ For non RD services this filed reports the CQE timestamp. The Timestamp is a free running counter that is incremented every TimeStampGranularity tick. The counter rolls-over when it reaches saturation. TimeStampGranularity is configured in the INIT_HCA command. This feature is currently not supported.
+ */
+ pseudo_bit_t checksum_15_8[0x00008];/* Checksum[15:8] - See IPoverIB checksum offloading chapter */
+/* -------------- */
+ pseudo_bit_t rqpn[0x00018]; /* Remote (source) QP number. Valid in Responder CQE only for Datagram QP. */
+ pseudo_bit_t checksum_7_0[0x00008]; /* Checksum[7:0] - See IPoverIB checksum offloading chapter */
+/* -------------- */
+ pseudo_bit_t rlid[0x00010]; /* Remote (source) LID of the message. Valid in Responder of UD QP CQE only. */
+ pseudo_bit_t ml_path[0x00007]; /* My (destination) LID path bits - these are the lowemost LMC bits of the DLID in an incoming UD packet, higher bits of this field, that are not part of the LMC bits are zeroed by HW.
+ Valid in responder of UD QP CQE only.
+ Invalid if incoming message DLID is the permissive LID or incoming message is multicast. */
+ pseudo_bit_t g[0x00001]; /* GRH present indicator. Valid in Responder of UD QP CQE only. */
+ pseudo_bit_t ipok[0x00001]; /* IP OK - See IPoverIB checksum offloading chapter */
+ pseudo_bit_t reserved1[0x00003];
+ pseudo_bit_t sl[0x00004]; /* Service Level of the message. Valid in Responder of UD QP CQE only. */
+/* -------------- */
+ pseudo_bit_t immediate_ethertype_pkey_indx_eecredits[0x00020];/* Valid for receive queue completion only.
+ If Opcode field indicates that this was send/write with immediate, this field contains immediate field of the packet.
+ If completion corresponds to RAW receive queue, bits 15:0 contain Ethertype field of the packet.
+ If completion corresponds to GSI receive queue, bits 31:16 contain index in PKey table that matches PKey of the message arrived.
+ If Opcode field indicates that this was send and invalidate, this field contains the key that was invalidated.
+ For CQE of send queue of the reliable connection service (but send and invalide), bits [4:0] of this field contain the encoded EEcredits received in last ACK of the message. */
+/* -------------- */
+ pseudo_bit_t byte_cnt[0x00020]; /* Byte count of data actually transferred (valid for receive queue completions only) */
+/* -------------- */
+ pseudo_bit_t reserved2[0x00006];
+ pseudo_bit_t wqe_adr[0x0001a]; /* Bits 31:6 of WQE virtual address completion is reported for. The 6 least significant bits are zero. */
+/* -------------- */
+ pseudo_bit_t reserved3[0x00007];
+ pseudo_bit_t owner[0x00001]; /* Owner field. Zero value of this field means SW ownership of CQE. */
+ pseudo_bit_t reserved4[0x0000f];
+ pseudo_bit_t s[0x00001]; /* If set, completion is reported for Send queue, if cleared - receive queue. */
+ pseudo_bit_t opcode[0x00008]; /* The opcode of WQE completion is reported for.
+ For CQEs corresponding to send completion, NOPCODE field of the WQE is copied to this field.
+ For CQEs corresponding to receive completions, opcode field of last packet in the message copied to this field.
+ For CQEs corresponding to the receive queue of QPs mapped to QP1, the opcode will be SEND with Immediate (messages are guaranteed to be SEND only)
+
+ The following values are reported in case of completion with error:
+ 0xFE - For completion with error on Receive Queues
+ 0xFF - For completion with error on Send Queues */
+/* -------------- */
+};
+
+/* */
+
+struct arbelprm_ecc_detect_event_data_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t cause_lsb[0x00001];
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t cause_msb[0x00001];
+ pseudo_bit_t reserved2[0x00002];
+ pseudo_bit_t err_rmw[0x00001];
+ pseudo_bit_t err_src_id[0x00003];
+ pseudo_bit_t err_da[0x00002];
+ pseudo_bit_t err_ba[0x00002];
+ pseudo_bit_t reserved3[0x00011];
+ pseudo_bit_t overflow[0x00001];
+/* -------------- */
+ pseudo_bit_t err_ra[0x00010];
+ pseudo_bit_t err_ca[0x00010];
+/* -------------- */
+};
+
+/* Event_data Field - ECC Detection Event */
+
+struct arbelprm_scrubbing_event_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00080];
+/* -------------- */
+ pseudo_bit_t cause_lsb[0x00001]; /* data integrity error cause:
+ single ECC error in the 64bit lsb data, on the rise edge of the clock */
+ pseudo_bit_t reserved1[0x00002];
+ pseudo_bit_t cause_msb[0x00001]; /* data integrity error cause:
+ single ECC error in the 64bit msb data, on the fall edge of the clock */
+ pseudo_bit_t reserved2[0x00002];
+ pseudo_bit_t err_rmw[0x00001]; /* transaction type:
+ 0 - read
+ 1 - read/modify/write */
+ pseudo_bit_t err_src_id[0x00003]; /* source of the transaction: 0x4 - PCI, other - internal or IB */
+ pseudo_bit_t err_da[0x00002]; /* Error DIMM address */
+ pseudo_bit_t err_ba[0x00002]; /* Error bank address */
+ pseudo_bit_t reserved3[0x00011];
+ pseudo_bit_t overflow[0x00001]; /* Fatal: ECC error FIFO overflow - ECC errors were detected, which may or may not have been corrected by InfiniHost-III-EX */
+/* -------------- */
+ pseudo_bit_t err_ra[0x00010]; /* Error row address */
+ pseudo_bit_t err_ca[0x00010]; /* Error column address */
+/* -------------- */
+};
+
+/* Miscellaneous Counters */
+
+struct arbelprm_misc_counters_st { /* Little Endian */
+ pseudo_bit_t ddr_scan_cnt[0x00020]; /* Number of times whole of LAM was scanned */
+/* -------------- */
+ pseudo_bit_t reserved0[0x007e0];
+/* -------------- */
+};
+
+/* LAM_EN Output Parameter */
+
+struct arbelprm_lam_en_out_param_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00040];
+/* -------------- */
+};
+
+/* Extended_Completion_Queue_Entry */
+
+struct arbelprm_extended_completion_queue_entry_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* */
+
+struct arbelprm_eq_cmd_doorbell_st { /* Little Endian */
+ pseudo_bit_t reserved0[0x00020];
+/* -------------- */
+};
+
+/* 0 */
+
+struct arbelprm_arbel_prm_st { /* Little Endian */
+ struct arbelprm_completion_queue_entry_st completion_queue_entry;/* Completion Queue Entry Format */
+/* -------------- */
+ pseudo_bit_t reserved0[0x7ff00];
+/* -------------- */
+ struct arbelprm_qp_ee_state_transitions_st qp_ee_state_transitions;/* QP/EE State Transitions Command Parameters */
+/* -------------- */
+ pseudo_bit_t reserved1[0x7f000];
+/* -------------- */
+ struct arbelprm_event_queue_entry_st event_queue_entry;/* Event Queue Entry */
+/* -------------- */
+ pseudo_bit_t reserved2[0x7ff00];
+/* -------------- */
+ struct arbelprm_completion_event_st completion_event;/* Event_data Field - Completion Event */
+/* -------------- */
+ pseudo_bit_t reserved3[0x7ff40];
+/* -------------- */
+ struct arbelprm_completion_queue_error_st completion_queue_error;/* Event_data Field - Completion Queue Error */
+/* -------------- */
+ pseudo_bit_t reserved4[0x7ff40];
+/* -------------- */
+ struct arbelprm_port_state_change_st port_state_change;/* Event_data Field - Port State Change */
+/* -------------- */
+ pseudo_bit_t reserved5[0x7ff40];
+/* -------------- */
+ struct arbelprm_wqe_segment_st wqe_segment;/* WQE segments format */
+/* -------------- */
+ pseudo_bit_t reserved6[0x7f000];
+/* -------------- */
+ struct arbelprm_page_fault_event_data_st page_fault_event_data;/* Event_data Field - Page Faults */
+/* -------------- */
+ pseudo_bit_t reserved7[0x7ff40];
+/* -------------- */
+ struct arbelprm_performance_monitor_event_st performance_monitor_event;/* Event Data Field - Performance Monitor */
+/* -------------- */
+ pseudo_bit_t reserved8[0xfff20];
+/* -------------- */
+ struct arbelprm_mt25208_type0_st mt25208_type0;/* InfiniHost-III-EX Type0 Configuration Header */
+/* -------------- */
+ pseudo_bit_t reserved9[0x7f000];
+/* -------------- */
+ struct arbelprm_qp_ee_event_st qp_ee_event;/* Event_data Field - QP/EE Events */
+/* -------------- */
+ pseudo_bit_t reserved10[0x00040];
+/* -------------- */
+ struct arbelprm_gpio_event_data_st gpio_event_data;
+/* -------------- */
+ pseudo_bit_t reserved11[0x7fe40];
+/* -------------- */
+ struct arbelprm_ud_address_vector_st ud_address_vector;/* UD Address Vector */
+/* -------------- */
+ pseudo_bit_t reserved12[0x7ff00];
+/* -------------- */
+ struct arbelprm_queue_pair_ee_context_entry_st queue_pair_ee_context_entry;/* QP and EE Context Entry */
+/* -------------- */
+ pseudo_bit_t reserved13[0x7fa00];
+/* -------------- */
+ struct arbelprm_address_path_st address_path;/* Address Path */
+/* -------------- */
+ pseudo_bit_t reserved14[0x7ff00];
+/* -------------- */
+ struct arbelprm_completion_queue_context_st completion_queue_context;/* Completion Queue Context Table Entry */
+/* -------------- */
+ pseudo_bit_t reserved15[0x7fe00];
+/* -------------- */
+ struct arbelprm_mpt_st mpt; /* Memory Protection Table (MPT) Entry */
+/* -------------- */
+ pseudo_bit_t reserved16[0x7fe00];
+/* -------------- */
+ struct arbelprm_mtt_st mtt; /* Memory Translation Table (MTT) Entry */
+/* -------------- */
+ pseudo_bit_t reserved17[0x7ffc0];
+/* -------------- */
+ struct arbelprm_eqc_st eqc; /* Event Queue Context Table Entry */
+/* -------------- */
+ pseudo_bit_t reserved18[0x7fe00];
+/* -------------- */
+ struct arbelprm_performance_monitors_st performance_monitors;/* Performance Monitors */
+/* -------------- */
+ pseudo_bit_t reserved19[0x7ff80];
+/* -------------- */
+ struct arbelprm_hca_command_register_st hca_command_register;/* HCA Command Register (HCR) */
+/* -------------- */
+ pseudo_bit_t reserved20[0xfff20];
+/* -------------- */
+ struct arbelprm_init_hca_st init_hca;/* INIT_HCA & QUERY_HCA Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved21[0x7f000];
+/* -------------- */
+ struct arbelprm_qpcbaseaddr_st qpcbaseaddr;/* QPC/EEC/CQC/EQC/RDB Parameters */
+/* -------------- */
+ pseudo_bit_t reserved22[0x7fc00];
+/* -------------- */
+ struct arbelprm_udavtable_memory_parameters_st udavtable_memory_parameters;/* Memory Access Parameters for UD Address Vector Table */
+/* -------------- */
+ pseudo_bit_t reserved23[0x7ffc0];
+/* -------------- */
+ struct arbelprm_multicastparam_st multicastparam;/* Multicast Support Parameters */
+/* -------------- */
+ pseudo_bit_t reserved24[0x7ff00];
+/* -------------- */
+ struct arbelprm_tptparams_st tptparams;/* Translation and Protection Tables Parameters */
+/* -------------- */
+ pseudo_bit_t reserved25[0x7ff00];
+/* -------------- */
+ struct arbelprm_enable_lam_st enable_lam;/* ENABLE_LAM Parameters Block */
+/* -------------- */
+ struct arbelprm_access_lam_st access_lam;
+/* -------------- */
+ pseudo_bit_t reserved26[0x7f700];
+/* -------------- */
+ struct arbelprm_dimminfo_st dimminfo;/* Logical DIMM Information */
+/* -------------- */
+ pseudo_bit_t reserved27[0x7ff00];
+/* -------------- */
+ struct arbelprm_query_fw_st query_fw;/* QUERY_FW Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved28[0x7f800];
+/* -------------- */
+ struct arbelprm_query_adapter_st query_adapter;/* QUERY_ADAPTER Parameters Block */
+/* -------------- */
+ pseudo_bit_t reserved29[0x7f800];
+/* -------------- */
+ struct arbelprm_query_dev_lim_st query_dev_lim;/* Query Device Limitations */
+/* -------------- */
+ pseudo_bit_t reserved30[0x7f800];
+/* -------------- */
+ struct arbelprm_uar_params_st uar_params;/* UAR Parameters */
+/* -------------- */
+ pseudo_bit_t reserved31[0x7ff00];
+/* -------------- */
+ struct arbelprm_init_ib_st init_ib; /* INIT_IB Parameters */
+/* -------------- */
+ pseudo_bit_t reserved32[0x7f800];
+/* -------------- */
+ struct arbelprm_mgm_entry_st mgm_entry;/* Multicast Group Member */
+/* -------------- */
+ pseudo_bit_t reserved33[0x7fe00];
+/* -------------- */
+ struct arbelprm_set_ib_st set_ib; /* SET_IB Parameters */
+/* -------------- */
+ pseudo_bit_t reserved34[0x7fe00];
+/* -------------- */
+ struct arbelprm_rd_send_doorbell_st rd_send_doorbell;/* RD-send doorbell */
+/* -------------- */
+ pseudo_bit_t reserved35[0x7ff80];
+/* -------------- */
+ struct arbelprm_send_doorbell_st send_doorbell;/* Send doorbell */
+/* -------------- */
+ pseudo_bit_t reserved36[0x7ffc0];
+/* -------------- */
+ struct arbelprm_receive_doorbell_st receive_doorbell;/* Receive doorbell */
+/* -------------- */
+ pseudo_bit_t reserved37[0x7ffc0];
+/* -------------- */
+ struct arbelprm_cq_cmd_doorbell_st cq_cmd_doorbell;/* CQ Doorbell */
+/* -------------- */
+ pseudo_bit_t reserved38[0xfffc0];
+/* -------------- */
+ struct arbelprm_uar_st uar; /* User Access Region */
+/* -------------- */
+ pseudo_bit_t reserved39[0x7c000];
+/* -------------- */
+ struct arbelprm_mgmqp_st mgmqp; /* Multicast Group Member QP */
+/* -------------- */
+ pseudo_bit_t reserved40[0x7ffe0];
+/* -------------- */
+ struct arbelprm_query_debug_msg_st query_debug_msg;/* Query Debug Message */
+/* -------------- */
+ pseudo_bit_t reserved41[0x7f800];
+/* -------------- */
+ struct arbelprm_mad_ifc_st mad_ifc; /* MAD_IFC Input Mailbox */
+/* -------------- */
+ pseudo_bit_t reserved42[0x00900];
+/* -------------- */
+ struct arbelprm_mad_ifc_input_modifier_st mad_ifc_input_modifier;/* MAD_IFC Input Modifier */
+/* -------------- */
+ pseudo_bit_t reserved43[0x7e6e0];
+/* -------------- */
+ struct arbelprm_resize_cq_st resize_cq;/* Resize CQ Input Mailbox */
+/* -------------- */
+ pseudo_bit_t reserved44[0x7fe00];
+/* -------------- */
+ struct arbelprm_completion_with_error_st completion_with_error;/* Completion with Error CQE */
+/* -------------- */
+ pseudo_bit_t reserved45[0x7ff00];
+/* -------------- */
+ struct arbelprm_hcr_completion_event_st hcr_completion_event;/* Event_data Field - HCR Completion Event */
+/* -------------- */
+ pseudo_bit_t reserved46[0x7ff40];
+/* -------------- */
+ struct arbelprm_transport_and_ci_error_counters_st transport_and_ci_error_counters;/* Transport and CI Error Counters */
+/* -------------- */
+ pseudo_bit_t reserved47[0x7f000];
+/* -------------- */
+ struct arbelprm_performance_counters_st performance_counters;/* Performance Counters */
+/* -------------- */
+ pseudo_bit_t reserved48[0x9ff800];
+/* -------------- */
+ struct arbelprm_fast_registration_segment_st fast_registration_segment;/* Fast Registration Segment */
+/* -------------- */
+ pseudo_bit_t reserved49[0x7ff00];
+/* -------------- */
+ struct arbelprm_pbl_st pbl; /* Physical Buffer List */
+/* -------------- */
+ pseudo_bit_t reserved50[0x7ff00];
+/* -------------- */
+ struct arbelprm_srq_context_st srq_context;/* SRQ Context */
+/* -------------- */
+ pseudo_bit_t reserved51[0x7fe80];
+/* -------------- */
+ struct arbelprm_mod_stat_cfg_st mod_stat_cfg;/* MOD_STAT_CFG */
+/* -------------- */
+ pseudo_bit_t reserved52[0x7f800];
+/* -------------- */
+ struct arbelprm_virtual_physical_mapping_st virtual_physical_mapping;/* Virtual and Physical Mapping */
+/* -------------- */
+ pseudo_bit_t reserved53[0x7ff80];
+/* -------------- */
+ struct arbelprm_cq_ci_db_record_st cq_ci_db_record;/* CQ_CI_DB_Record */
+/* -------------- */
+ pseudo_bit_t reserved54[0x7ffc0];
+/* -------------- */
+ struct arbelprm_cq_arm_db_record_st cq_arm_db_record;/* CQ_ARM_DB_Record */
+/* -------------- */
+ pseudo_bit_t reserved55[0x7ffc0];
+/* -------------- */
+ struct arbelprm_qp_db_record_st qp_db_record;/* QP_DB_Record */
+/* -------------- */
+ pseudo_bit_t reserved56[0x1fffc0];
+/* -------------- */
+ struct arbelprm_configuration_registers_st configuration_registers;/* InfiniHost III EX Configuration Registers */
+/* -------------- */
+ struct arbelprm_eq_set_ci_table_st eq_set_ci_table;/* EQ Set CI DBs Table */
+/* -------------- */
+ pseudo_bit_t reserved57[0x01000];
+/* -------------- */
+ struct arbelprm_eq_arm_db_region_st eq_arm_db_region;/* EQ Arm Doorbell Region */
+/* -------------- */
+ pseudo_bit_t reserved58[0x00fc0];
+/* -------------- */
+ struct arbelprm_clr_int_st clr_int; /* Clear Interrupt Register */
+/* -------------- */
+ pseudo_bit_t reserved59[0xffcfc0];
+/* -------------- */
+};
+#endif /* H_prefix_arbelprm_bits_fixnames_MT25218_PRM_csp_H */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/bit_ops.h b/gpxe/src/drivers/net/mlx_ipoib/bit_ops.h
new file mode 100644
index 00000000..e3fb4331
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/bit_ops.h
@@ -0,0 +1,126 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#ifndef __bit_ops_h__
+#define __bit_ops_h__
+
+typedef unsigned long MT_offset_t;
+typedef unsigned long MT_size_t;
+typedef unsigned char pseudo_bit_t;
+struct addr_64_st {
+ __u32 addr_l;
+ __u32 addr_h;
+};
+
+#define MT_BIT_OFFSET(object_struct,reg_path) \
+ ((MT_offset_t) &( ((struct object_struct *)(0))-> reg_path ))
+
+#define MT_BIT_SIZE(object_struct,reg_path) \
+ ((MT_size_t) sizeof( ((struct object_struct *)(0))-> reg_path ))
+
+#define MT_BIT_OFFSET_SIZE(object_struct,reg_path) \
+ MT_BIT_OFFSET(object_struct,reg_path),MT_BIT_SIZE(object_struct,reg_path)
+
+#define MT_BYTE_OFFSET(object_struct,reg_path) \
+ ((MT_offset_t) (MT_BIT_OFFSET(object_struct,reg_path)/8))
+
+#define MT_BYTE_SIZE(object_struct,reg_path) \
+ ((MT_size_t) MT_BIT_SIZE(object_struct,reg_path)/8)
+
+#define MT_BYTE_OFFSET_SIZE(object_struct,reg_path) \
+ MT_BYTE_OFFSET(object_struct,reg_path),MT_BYTE_SIZE(object_struct,reg_path)
+
+#define MT_STRUCT_SIZE(object_struct) (sizeof(struct object_struct) >> 3)
+
+/*****************************************************************************************
+ * Bit manipulation macros
+ *****************************************************************************************/
+
+/* MASK generate a bit mask S bits width */
+#define MASK32(S) ( ((__u32) ~0L) >> (32-(S)) )
+
+/*
+ * BITS generate a bit mask with bits O+S..O set (assumes 32 bit integer).
+ * numbering bits as following: 31........................76543210
+ */
+#define BITS32(O,S) ( MASK32(S) << (O) )
+
+/*
+ * MT_EXTRACT32 macro extracts S bits from (__u32)W with offset O
+ * and shifts them O places to the right (right justifies the field extracted).
+ */
+#define MT_EXTRACT32(W,O,S) ( ((W)>>(O)) & MASK32(S) )
+
+/*
+ * MT_INSERT32 macro inserts S bits with offset O from field F into word W (__u32)
+ */
+#define MT_INSERT32(W,F,O,S) ((W)= ( ( (W) & (~BITS32(O,S)) ) | (((F) & MASK32(S))<<(O)) ))
+
+/*
+ * MT_EXTRACT_ARRAY32 macro is similar to EXTRACT but works on an array of (__u32),
+ * thus offset may be larger than 32 (but not size).
+ */
+#define MT_EXTRACT_ARRAY32(A,O,S) MT_EXTRACT32(((__u32*)A)[O >> 5],(O & MASK32(5)),S)
+
+/*
+ * MT_INSERT_ARRAY32 macro is similar to INSERT but works on an array of (__u32),
+ * thus offset may be larger than 32 (but not size).
+ */
+#define MT_INSERT_ARRAY32(A,F,O,S) MT_INSERT32(((__u32*)A)[O >> 5],F,(O & MASK32(5)),S)
+
+#define INS_FLD(src, a, st, fld) MT_INSERT_ARRAY32(a, src, MT_BIT_OFFSET(st, fld), MT_BIT_SIZE(st, fld))
+
+#define EX_FLD(a, st, fld) MT_EXTRACT_ARRAY32(a, MT_BIT_OFFSET(st, fld), MT_BIT_SIZE(st, fld))
+
+/* return the address of the dword holding the field
+
+ buf = pointer to buffer where to place the value
+ st = struct describing the buffer
+ fld = field in the struct where to insert the value */
+
+#define FLD_DW_ADDR(buf, st, fld) ((__u32 *)((__u32 *)(buf)+(((__u32)(&(((struct st *)(0))->fld))) >> 5)))
+
+/*
+ val = value to insert
+ buf = pointer to buffer where to place the value
+ st = struct describing the buffer
+ fld = field in the struct where to insert the value */
+
+#define INS_FLD_TO_BE(val, buf, st, fld) \
+ do { \
+ *FLD_DW_ADDR(buf, st, fld) = be32_to_cpu(*FLD_DW_ADDR(buf, st, fld)); \
+ INS_FLD(val, buf, st, fld); \
+ *FLD_DW_ADDR(buf, st, fld) = cpu_to_be32(*FLD_DW_ADDR(buf, st, fld)); \
+ } \
+ while(0)
+
+#define EX_FLD_FROM_BE(buf, st, fld, type) \
+({ \
+ type field; \
+ \
+ *FLD_DW_ADDR(buf, st, fld) = be32_to_cpu(*FLD_DW_ADDR(buf, st, fld)); \
+ field= EX_FLD(buf, st, fld); \
+ *FLD_DW_ADDR(buf, st, fld) = cpu_to_be32(*FLD_DW_ADDR(buf, st, fld)); \
+ \
+ field; \
+})
+
+#endif /* __bit_ops_h__ */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/cmdif.h b/gpxe/src/drivers/net/mlx_ipoib/cmdif.h
new file mode 100644
index 00000000..375a60f2
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/cmdif.h
@@ -0,0 +1,50 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#ifndef __cmdif_h_
+#define __cmdif_h_
+
+#include "ib_mad.h"
+
+static int cmd_init_hca(__u32 * inprm, __u32 in_prm_size);
+static int cmd_close_hca(int panic);
+static int cmd_sw2hw_eq(__u32 inprm_sz);
+static int cmd_hw2sw_eq(__u8 eqn);
+static int cmd_map_eq(__u8 eqn, __u32 mask, int map);
+static int cmd_sw2hw_mpt(__u32 * lkey, __u32 in_key, __u32 * inprm,
+ __u32 inprm_sz);
+static int cmd_hw2sw_mpt(__u32 key);
+static int cmd_init_ib(__u32 port, __u32 * inprm, __u32 inprm_sz);
+static int cmd_close_ib(__u32 port);
+static int cmd_sw2hw_cq(__u32 cqn, __u32 * inprm, __u32 inprm_sz);
+static int cmd_hw2sw_cq(__u32 cqn);
+static int cmd_rst2init_qpee(__u32 qpn, __u32 * inprm, __u32 inprm_sz);
+static int cmd_init2rtr_qpee(__u32 qpn, __u32 * inprm, __u32 inprm_sz);
+static int cmd_rtr2rts_qpee(__u32 qpn, __u32 * inprm, __u32 inprm_sz);
+static int cmd_2rst_qpee(__u32 qpn);
+static int cmd_2err_qpee(__u32 qpn);
+static int cmd_post_doorbell(void *inprm, __u32 offset);
+static int cmd_mad_ifc(void *inprm, struct ib_mad_st *mad, __u8 port);
+static int cmd_write_mgm( /*struct mg_member_layout_st */ void *mg,
+ __u16 index);
+static int cmd_mgid_hash(__u8 * gid, __u16 * mgid_hash_p);
+
+#endif /* __cmdif_h_ */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/cmdif_comm.c b/gpxe/src/drivers/net/mlx_ipoib/cmdif_comm.c
new file mode 100644
index 00000000..d43a1068
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/cmdif_comm.c
@@ -0,0 +1,564 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#include "cmdif.h"
+#include "cmdif_comm.h"
+#include "cmdif_priv.h"
+
+static int cmdif_is_free(int *is_free)
+{
+ int rc;
+ __u32 result;
+
+ rc = gw_read_cr(HCR_OFFSET_GO, &result);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ *is_free = (result & 0x800000) == 0;
+
+ return 0;
+}
+
+static void edit_hcr(command_fields_t * cmd_prms, __u32 * buf)
+{
+ unsigned int i;
+
+ switch (cmd_prms->in_trans) {
+ case TRANS_NA:
+ /* note! since these are zeroes I do not bother to deal with endianess */
+ buf[0] = 0;
+ buf[1] = 0;
+ break;
+
+ case TRANS_IMMEDIATE:
+ buf[0] = cmd_prms->in_param[0];
+ buf[1] = cmd_prms->in_param[1];
+ break;
+
+ case TRANS_MAILBOX:
+ buf[0] = 0;
+ buf[1] = virt_to_bus(cmd_prms->in_param);
+
+ for (i = 0; i < cmd_prms->in_param_size; i += 4)
+ cmd_prms->in_param[i >> 2] =
+ cpu_to_be32(cmd_prms->in_param[i >> 2]);
+ break;
+ }
+
+ buf[2] = cmd_prms->input_modifier;
+
+ switch (cmd_prms->out_trans) {
+ case TRANS_NA:
+ /* note! since these are zeroes I do not bother to deal with endianess */
+ buf[3] = 0;
+ buf[4] = 0;
+ break;
+
+ case TRANS_IMMEDIATE:
+ break;
+ case TRANS_MAILBOX:
+ buf[3] = 0;
+ buf[4] = virt_to_bus(cmd_prms->out_param);
+ break;
+ }
+
+ buf[5] = 0; /* token is always 0 */
+ buf[6] = cmd_prms->opcode | /* opcode */
+ 0x800000 | /* go bit */
+ ((cmd_prms->opcode_modifier & 0xf) << 12); /* opcode modifier
+*/ }
+
+static int wait_cmdif_free(void)
+{
+ int ret, is_free;
+ unsigned int i, relax_time = 1, max_time = 5000;
+
+ /* wait until go bit is free */
+ for (i = 0; i < max_time; i += relax_time) {
+ ret = cmdif_is_free(&is_free);
+ if (ret)
+ return ret;
+ if (is_free)
+ break;
+ mdelay(relax_time);
+ }
+ if (i >= max_time)
+ return -1;
+ return 0;
+}
+
+static XHH_cmd_status_t cmd_invoke(command_fields_t * cmd_prms)
+{
+ int ret, is_free, i;
+ __u32 hcr[7], data;
+ __u8 status;
+
+ /* check if go bit is free */
+ ret = cmdif_is_free(&is_free);
+ if (ret) {
+ eprintf("");
+ return -1;
+ }
+
+ __asm__ __volatile__("":::"memory");
+ /* it must be free */
+ if (!is_free) {
+ eprintf("");
+ return -1;
+ }
+ __asm__ __volatile__("":::"memory");
+ edit_hcr(cmd_prms, hcr);
+ __asm__ __volatile__("":::"memory");
+
+ for (i = 0; i < 7; ++i) {
+ ret = gw_write_cr(HCR_BASE + i * 4, hcr[i]);
+ if (ret) {
+ eprintf("");
+ return -1;
+ }
+ }
+
+ __asm__ __volatile__("":::"memory");
+ /* wait for completion */
+ ret = wait_cmdif_free();
+ if (ret) {
+ eprintf("");
+ return -1;
+ }
+
+ __asm__ __volatile__("":::"memory");
+ ret = gw_read_cr(HCR_OFFSET_STATUS, &data);
+ if (ret) {
+ eprintf("");
+ return -1;
+ }
+
+ status = data >> 24;
+
+ if (status) {
+ tprintf("status=0x%x", status);
+ return status;
+ }
+
+ if (cmd_prms->out_trans == TRANS_MAILBOX)
+ be_to_cpu_buf(cmd_prms->out_param, cmd_prms->out_param_size);
+ else if (cmd_prms->out_trans == TRANS_IMMEDIATE) {
+ if (gw_read_cr(HCR_OFFSET_OUTPRM_H, &cmd_prms->out_param[0]))
+ return -1;
+ if (gw_read_cr(HCR_OFFSET_OUTPRM_L, &cmd_prms->out_param[1]))
+ return -1;
+ }
+
+ return 0;
+}
+
+/*************************************************
+ commands
+*************************************************/
+
+/*
+ * cmd_close_hca
+ */
+static int cmd_close_hca(int panic)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_CLOSE_HCA;
+ cmd_desc.opcode_modifier= panic;
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_init_hca
+ */
+static int cmd_init_hca(__u32 * inprm, __u32 in_prm_size)
+{
+ int rc;
+
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.opcode = XDEV_CMD_INIT_HCA;
+ cmd_desc.in_param = inprm;
+ cmd_desc.in_param_size = in_prm_size;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_sw2hw_eq
+ */
+static int cmd_sw2hw_eq(__u32 inprm_sz)
+{
+ int rc;
+ command_fields_t cmd_desc;
+ void *inprm;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ inprm = get_inprm_buf();
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.opcode = XDEV_CMD_SW2HW_EQ;
+ cmd_desc.in_param = inprm;
+ cmd_desc.in_param_size = inprm_sz;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_hw2sw_eq
+ */
+static int cmd_hw2sw_eq(__u8 eqn)
+{
+ int rc;
+ command_fields_t cmd_desc;
+ void *outprm;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ outprm = get_outprm_buf();
+ cmd_desc.opcode = XDEV_CMD_HW2SW_EQ;
+ cmd_desc.input_modifier = eqn;
+ cmd_desc.out_trans = TRANS_MAILBOX;
+ cmd_desc.out_param = outprm;
+ cmd_desc.out_param_size = 0x40;
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_map_eq
+ */
+static int cmd_map_eq(__u8 eqn, __u32 mask, int map)
+{
+ int rc;
+ command_fields_t cmd_desc;
+ __u32 *inprm;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ inprm = get_inprm_buf();
+
+ inprm[1] = mask;
+ inprm[0] = 0;
+
+ cmd_desc.opcode = XDEV_CMD_MAP_EQ;
+ cmd_desc.in_trans = TRANS_IMMEDIATE;
+ cmd_desc.in_param = inprm;
+ cmd_desc.input_modifier = ((map ? 0 : 1) << 31) | eqn;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_sw2hw_mpt
+ */
+static int cmd_sw2hw_mpt(__u32 * lkey, __u32 in_key, __u32 * inprm,
+ __u32 inprm_sz)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.opcode = XDEV_CMD_SW2HW_MPT;
+ cmd_desc.input_modifier = in_key & MKEY_IDX_MASK; /* only one MR for the whole driver */
+ cmd_desc.in_param = inprm;
+ cmd_desc.in_param_size = inprm_sz;
+
+ rc = cmd_invoke(&cmd_desc);
+ if (!rc)
+ *lkey = in_key;
+
+ return rc;
+}
+
+/*
+ * cmd_hw2sw_mpt
+ */
+static int cmd_hw2sw_mpt(__u32 key)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_HW2SW_MPT;
+ cmd_desc.input_modifier = key & MKEY_IDX_MASK;
+ cmd_desc.opcode_modifier = 1;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_init_ib
+ */
+static int cmd_init_ib(__u32 port, __u32 * inprm, __u32 inprm_sz)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_INIT_IB;
+ cmd_desc.input_modifier = port;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param = inprm;
+ cmd_desc.in_param_size = inprm_sz;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_close_ib
+ */
+static int cmd_close_ib(__u32 port)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_CLOSE_IB;
+ cmd_desc.input_modifier = port;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_sw2hw_cq
+ */
+static int cmd_sw2hw_cq(__u32 cqn, __u32 * inprm, __u32 inprm_sz)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_SW2HW_CQ;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param = inprm;
+ cmd_desc.in_param_size = inprm_sz;
+ cmd_desc.input_modifier = cqn;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_hw2sw_cq
+ */
+static int cmd_hw2sw_cq(__u32 cqn)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_HW2SW_CQ;
+ cmd_desc.input_modifier = cqn;
+ cmd_desc.out_trans = TRANS_MAILBOX;
+ cmd_desc.out_param = get_outprm_buf();
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_rst2init_qpee
+ */
+static int cmd_rst2init_qpee(__u32 qpn, __u32 * inprm, __u32 inprm_sz)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_RST2INIT_QPEE;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param = inprm;
+ cmd_desc.in_param_size = inprm_sz;
+ cmd_desc.input_modifier = qpn;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_init2rtr_qpee
+ */
+static int cmd_init2rtr_qpee(__u32 qpn, __u32 * inprm, __u32 inprm_sz)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_INIT2RTR_QPEE;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param = inprm;
+ cmd_desc.in_param_size = inprm_sz;
+ cmd_desc.input_modifier = qpn;;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_rtr2rts_qpee
+ */
+static int cmd_rtr2rts_qpee(__u32 qpn, __u32 * inprm, __u32 inprm_sz)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_RTR2RTS_QPEE;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param = inprm;
+ cmd_desc.in_param_size = inprm_sz;
+ cmd_desc.input_modifier = qpn;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_2rst_qpee
+ */
+static int cmd_2rst_qpee(__u32 qpn)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_ERR2RST_QPEE;
+ cmd_desc.opcode_modifier = 0;
+ cmd_desc.input_modifier = qpn;
+ cmd_desc.out_trans = TRANS_MAILBOX;
+ cmd_desc.out_param = get_outprm_buf();
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_2err_qpee
+ */
+static int cmd_2err_qpee(__u32 qpn)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_2ERR_QPEE;
+ cmd_desc.input_modifier = qpn;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_post_doorbell
+ */
+static int cmd_post_doorbell(void *inprm, __u32 offset)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_POST_DOORBELL;
+ cmd_desc.in_trans = TRANS_IMMEDIATE;
+ cmd_desc.in_param = inprm;
+ cmd_desc.input_modifier = offset;
+ if (0) {
+ rc = cmd_invoke(&cmd_desc);
+ } else {
+ dev_post_dbell(inprm, offset);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int cmd_mad_ifc(void *inprm, struct ib_mad_st *mad, __u8 port)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = XDEV_CMD_MAD_IFC;
+ cmd_desc.opcode_modifier = 1; /* no mkey/bkey validation */
+ cmd_desc.input_modifier = port;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param_size = 256;
+ cmd_desc.in_param = (__u32 *) inprm;
+ cmd_desc.out_trans = TRANS_MAILBOX;
+ cmd_desc.out_param = (__u32 *) mad;
+ cmd_desc.out_param_size = 256;
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+static int cmd_mgid_hash(__u8 * gid, __u16 * mgid_hash_p)
+{
+ int rc;
+ command_fields_t cmd_desc;
+ __u16 result[2];
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = XDEV_CMD_MGID_HASH;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param = (__u32 *) gid;
+ cmd_desc.in_param_size = 16;
+ cmd_desc.out_trans = TRANS_IMMEDIATE;
+
+ rc = cmd_invoke(&cmd_desc);
+ if (!rc) {
+ rc = gw_read_cr(HCR_BASE + 16, (__u32 *) result);
+ if (!rc) {
+ *mgid_hash_p = result[0];
+ }
+ }
+
+ return rc;
+}
diff --git a/gpxe/src/drivers/net/mlx_ipoib/cmdif_comm.h b/gpxe/src/drivers/net/mlx_ipoib/cmdif_comm.h
new file mode 100644
index 00000000..e0624fa5
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/cmdif_comm.h
@@ -0,0 +1,60 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#ifndef __cmdif_comm_h__
+#define __cmdif_comm_h__
+
+ /* initialization and general commands */
+#define XDEV_CMD_INIT_HCA 0x7
+#define XDEV_CMD_CLOSE_HCA 0x8
+#define XDEV_CMD_INIT_IB 0x9
+#define XDEV_CMD_CLOSE_IB 0xa
+
+ /* TPT commands */
+#define XDEV_CMD_SW2HW_MPT 0xd
+#define XDEV_CMD_HW2SW_MPT 0xf
+
+ /* EQ commands */
+#define XDEV_CMD_MAP_EQ 0x12
+#define XDEV_CMD_SW2HW_EQ 0x13
+#define XDEV_CMD_HW2SW_EQ 0x14
+
+ /* CQ commands */
+#define XDEV_CMD_SW2HW_CQ 0x16
+#define XDEV_CMD_HW2SW_CQ 0x17
+
+ /* QP/EE commands */
+#define XDEV_CMD_RST2INIT_QPEE 0x19
+#define XDEV_CMD_INIT2RTR_QPEE 0x1a
+#define XDEV_CMD_RTR2RTS_QPEE 0x1b
+#define XDEV_CMD_2ERR_QPEE 0x1e
+#define XDEV_CMD_ERR2RST_QPEE 0x21
+
+ /* special QPs and management commands */
+#define XDEV_CMD_MAD_IFC 0x24
+
+ /* multicast commands */
+#define XDEV_CMD_READ_MGM 0x25
+#define XDEV_CMD_MGID_HASH 0x27
+
+#define XDEV_CMD_POST_DOORBELL 0x41
+
+#endif /* __cmdif_comm_h__ */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/cmdif_mt23108.c b/gpxe/src/drivers/net/mlx_ipoib/cmdif_mt23108.c
new file mode 100644
index 00000000..7a8f6d60
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/cmdif_mt23108.c
@@ -0,0 +1,193 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+#include "cmdif.h"
+#include "cmdif_priv.h"
+#include "mt23108.h"
+
+/*
+ * cmd_sys_en
+ */
+static int cmd_sys_en(void)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = TAVOR_CMD_SYS_EN;
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_sys_dis
+ */
+static int cmd_sys_dis(void)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.in_trans = TRANS_NA;
+ cmd_desc.out_trans = TRANS_NA;
+ cmd_desc.opcode = TAVOR_CMD_SYS_DIS;
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_query_dev_lim
+ */
+static int cmd_query_dev_lim(struct dev_lim_st *dev_lim_p)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = TAVOR_CMD_QUERY_DEV_LIM;
+ cmd_desc.out_trans = TRANS_MAILBOX;
+ cmd_desc.out_param = get_outprm_buf();
+ cmd_desc.out_param_size = MT_STRUCT_SIZE(tavorprm_query_dev_lim_st);
+
+ rc = cmd_invoke(&cmd_desc);
+ if (!rc) {
+ dev_lim_p->log2_rsvd_qps =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_dev_lim_st,
+ log2_rsvd_qps);
+ dev_lim_p->qpc_entry_sz =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_dev_lim_st,
+ qpc_entry_sz);
+
+ dev_lim_p->log2_rsvd_srqs =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_dev_lim_st,
+ log2_rsvd_srqs);
+ dev_lim_p->srq_entry_sz =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_dev_lim_st,
+ srq_entry_sz);
+
+ dev_lim_p->log2_rsvd_ees =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_dev_lim_st,
+ log2_rsvd_ees);
+ dev_lim_p->eec_entry_sz =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_dev_lim_st,
+ eec_entry_sz);
+
+ dev_lim_p->log2_rsvd_cqs =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_dev_lim_st,
+ log2_rsvd_cqs);
+ dev_lim_p->cqc_entry_sz =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_dev_lim_st,
+ cqc_entry_sz);
+
+ dev_lim_p->log2_rsvd_mtts =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_dev_lim_st,
+ log2_rsvd_mtts);
+ dev_lim_p->mtt_entry_sz = 64; /* segment size is set to zero in init_hca */
+
+ dev_lim_p->log2_rsvd_mrws =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_dev_lim_st,
+ log2_rsvd_mrws);
+ dev_lim_p->mpt_entry_sz = MT_STRUCT_SIZE(tavorprm_mpt_st);
+
+ dev_lim_p->eqc_entry_sz =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_dev_lim_st,
+ eqc_entry_sz);
+ }
+
+ return rc;
+}
+
+/*
+ * cmd_write_mgm
+ */
+static int cmd_write_mgm(void *mg, __u16 index)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = TAVOR_CMD_WRITE_MGM;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param_size = MT_STRUCT_SIZE(tavorprm_mgm_entry_st);
+ cmd_desc.in_param = (__u32 *) mg;
+ cmd_desc.input_modifier = index;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_mod_stat_cfg
+ */
+static int cmd_mod_stat_cfg(void *cfg)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = TAVOR_CMD_MOD_STAT_CFG;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param_size = MT_STRUCT_SIZE(tavorprm_mod_stat_cfg_st);
+ cmd_desc.in_param = (__u32 *) cfg;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+
+/*
+ * cmd_query_fw
+ */
+static int cmd_query_fw(struct query_fw_st *qfw)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = TAVOR_CMD_QUERY_FW;
+ cmd_desc.out_trans = TRANS_MAILBOX;
+ cmd_desc.out_param = get_outprm_buf();
+ cmd_desc.out_param_size = MT_STRUCT_SIZE(tavorprm_query_fw_st);
+
+ rc = cmd_invoke(&cmd_desc);
+ if (!rc) {
+ qfw->fw_rev_major =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_fw_st, fw_rev_major);
+ qfw->fw_rev_minor =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_fw_st, fw_rev_minor);
+ qfw->fw_rev_subminor =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_fw_st, fw_rev_subminor);
+
+ qfw->error_buf_start_h =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_fw_st, error_buf_start_h);
+ qfw->error_buf_start_l =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_fw_st, error_buf_start_l);
+ qfw->error_buf_size =
+ EX_FLD(cmd_desc.out_param, tavorprm_query_fw_st, error_buf_size);
+ }
+
+ return rc;
+}
diff --git a/gpxe/src/drivers/net/mlx_ipoib/cmdif_mt25218.c b/gpxe/src/drivers/net/mlx_ipoib/cmdif_mt25218.c
new file mode 100644
index 00000000..fb95edbe
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/cmdif_mt25218.c
@@ -0,0 +1,457 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+#include "cmdif.h"
+#include "cmdif_priv.h"
+#include "mt25218.h"
+
+/*
+ * cmd_sys_dis
+ */
+static int cmd_sys_dis(void)
+{
+ return 0;
+}
+
+/*
+ * cmd_write_mgm
+ */
+static int cmd_write_mgm(void *mg, __u16 index)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = MEMFREE_CMD_WRITE_MGM;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param_size = MT_STRUCT_SIZE(arbelprm_mgm_entry_st);
+ cmd_desc.in_param = (__u32 *) mg;
+ cmd_desc.input_modifier = index;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_mod_stat_cfg
+ */
+static int cmd_mod_stat_cfg(void)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+ cmd_desc.opcode = MEMFREE_CMD_MOD_STAT_CFG;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param_size = MT_STRUCT_SIZE(arbelprm_mod_stat_cfg_st);
+ cmd_desc.in_param = get_inprm_buf();
+ memset(cmd_desc.in_param, 0, cmd_desc.in_param_size);
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_query_fw
+ */
+static int cmd_query_fw(struct query_fw_st *qfw)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = MEMFREE_CMD_QUERY_FW;
+ cmd_desc.out_trans = TRANS_MAILBOX;
+ cmd_desc.out_param = get_outprm_buf();
+ cmd_desc.out_param_size = MT_STRUCT_SIZE(arbelprm_query_fw_st);
+
+ rc = cmd_invoke(&cmd_desc);
+ if (!rc) {
+ qfw->fw_rev_major =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_fw_st, fw_rev_major);
+ qfw->fw_rev_minor =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_fw_st, fw_rev_minor);
+ qfw->fw_rev_subminor =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_fw_st, fw_rev_subminor);
+
+ qfw->error_buf_start_h =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_fw_st, error_buf_start_h);
+ qfw->error_buf_start_l =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_fw_st, error_buf_start_l);
+ qfw->error_buf_size =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_fw_st, error_buf_size);
+
+ qfw->fw_pages =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_fw_st, fw_pages);
+ qfw->eq_ci_table.addr_h =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_fw_st,
+ eq_set_ci_base_addr_h);
+ qfw->eq_ci_table.addr_l =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_fw_st,
+ eq_set_ci_base_addr_l);
+ qfw->clear_int_addr.addr_h =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_fw_st,
+ clr_int_base_addr_h);
+ qfw->clear_int_addr.addr_l =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_fw_st,
+ clr_int_base_addr_l);
+ }
+
+ return rc;
+}
+
+/*
+ * cmd_query_adapter
+ */
+static int cmd_query_adapter(struct query_adapter_st *qa)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = MEMFREE_CMD_QUERY_ADAPTER;
+ cmd_desc.out_trans = TRANS_MAILBOX;
+ cmd_desc.out_param = get_outprm_buf();
+ cmd_desc.out_param_size = MT_STRUCT_SIZE(arbelprm_query_adapter_st);
+
+ rc = cmd_invoke(&cmd_desc);
+ if (!rc) {
+ qa->intapin =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_adapter_st,
+ intapin);
+ }
+
+ return rc;
+}
+
+/*
+ * cmd_enable_lam
+ */
+static int cmd_enable_lam(void)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = MEMFREE_CMD_ENABLE_LAM;
+ cmd_desc.opcode_modifier = 1; /* zero locally attached memory */
+ cmd_desc.input_modifier = 0; /* disable fast refresh */
+ cmd_desc.out_trans = TRANS_MAILBOX;
+ cmd_desc.out_param = get_outprm_buf();
+ cmd_desc.out_param_size = MT_STRUCT_SIZE(arbelprm_enable_lam_st);
+
+ rc = cmd_invoke(&cmd_desc);
+ if (rc) {
+ }
+
+ return rc;
+}
+
+/*
+ * cmd_map_fa
+ */
+static int cmd_map_fa(struct map_icm_st *map_fa_p)
+{
+ int rc;
+ command_fields_t cmd_desc;
+ unsigned int in_param_size, i;
+ unsigned long off;
+
+ if (map_fa_p->num_vpm > MAX_VPM_PER_CALL) {
+ return -1;
+ }
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = MEMFREE_CMD_MAP_FA;
+ cmd_desc.input_modifier = map_fa_p->num_vpm;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param = get_inprm_buf();
+ in_param_size =
+ MT_STRUCT_SIZE(arbelprm_virtual_physical_mapping_st) *
+ map_fa_p->num_vpm;
+ cmd_desc.in_param_size = in_param_size;
+ memset(cmd_desc.in_param, 0, in_param_size);
+
+ for (i = 0; i < map_fa_p->num_vpm; ++i) {
+ off = (unsigned long)(cmd_desc.in_param) +
+ MT_STRUCT_SIZE(arbelprm_virtual_physical_mapping_st) * i;
+ INS_FLD(map_fa_p->vpm_arr[i].va_h, off,
+ arbelprm_virtual_physical_mapping_st, va_h);
+ INS_FLD(map_fa_p->vpm_arr[i].va_l >> 12, off,
+ arbelprm_virtual_physical_mapping_st, va_l);
+ INS_FLD(map_fa_p->vpm_arr[i].pa_h, off,
+ arbelprm_virtual_physical_mapping_st, pa_h);
+ INS_FLD(map_fa_p->vpm_arr[i].pa_l >> 12, off,
+ arbelprm_virtual_physical_mapping_st, pa_l);
+ INS_FLD(map_fa_p->vpm_arr[i].log2_size, off,
+ arbelprm_virtual_physical_mapping_st, log2size);
+ }
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_unmap_fa
+ */
+static int cmd_unmap_fa(void)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = MEMFREE_CMD_UNMAP_FA;
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_run_fw
+ */
+static int cmd_run_fw(void)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = MEMFREE_CMD_RUN_FW;
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_set_icm_size
+ */
+static int cmd_set_icm_size(__u32 icm_size, __u32 * aux_pages_p)
+{
+ int rc;
+ command_fields_t cmd_desc;
+ __u32 iprm[2], oprm[2];
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = MEMFREE_CMD_SET_ICM_SIZE;
+
+ iprm[1] = icm_size;
+ iprm[0] = 0;
+ cmd_desc.in_trans = TRANS_IMMEDIATE;
+ cmd_desc.in_param = iprm;
+ cmd_desc.out_trans = TRANS_IMMEDIATE;
+ cmd_desc.out_param = oprm;
+ rc = cmd_invoke(&cmd_desc);
+ if (!rc) {
+ if (oprm[0]) {
+ /* too many pages required */
+ return -1;
+ }
+ *aux_pages_p = oprm[1];
+ }
+
+ return rc;
+}
+
+/*
+ * cmd_map_icm_aux
+ */
+static int cmd_map_icm_aux(struct map_icm_st *map_icm_aux_p)
+{
+ int rc;
+ command_fields_t cmd_desc;
+ unsigned int in_param_size, i;
+ unsigned long off;
+
+ if (map_icm_aux_p->num_vpm > MAX_VPM_PER_CALL) {
+ return -1;
+ }
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = MEMFREE_CMD_MAP_ICM_AUX;
+ cmd_desc.input_modifier = map_icm_aux_p->num_vpm;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param = get_inprm_buf();
+ in_param_size =
+ MT_STRUCT_SIZE(arbelprm_virtual_physical_mapping_st) *
+ map_icm_aux_p->num_vpm;
+ cmd_desc.in_param_size = in_param_size;
+ memset(cmd_desc.in_param, 0, in_param_size);
+
+ for (i = 0; i < map_icm_aux_p->num_vpm; ++i) {
+ off = (unsigned long)(cmd_desc.in_param) +
+ MT_STRUCT_SIZE(arbelprm_virtual_physical_mapping_st) * i;
+ INS_FLD(map_icm_aux_p->vpm_arr[i].va_h, off,
+ arbelprm_virtual_physical_mapping_st, va_h);
+ INS_FLD(map_icm_aux_p->vpm_arr[i].va_l >> 12, off,
+ arbelprm_virtual_physical_mapping_st, va_l);
+ INS_FLD(map_icm_aux_p->vpm_arr[i].pa_h, off,
+ arbelprm_virtual_physical_mapping_st, pa_h);
+ INS_FLD(map_icm_aux_p->vpm_arr[i].pa_l >> 12, off,
+ arbelprm_virtual_physical_mapping_st, pa_l);
+ INS_FLD(map_icm_aux_p->vpm_arr[i].log2_size, off,
+ arbelprm_virtual_physical_mapping_st, log2size);
+ }
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_map_icm
+ */
+static int cmd_map_icm(struct map_icm_st *map_icm_p)
+{
+ int rc;
+ command_fields_t cmd_desc;
+ unsigned int in_param_size, i;
+ unsigned long off;
+
+ if (map_icm_p->num_vpm > MAX_VPM_PER_CALL) {
+ return -1;
+ }
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = MEMFREE_CMD_MAP_ICM;
+ cmd_desc.input_modifier = map_icm_p->num_vpm;
+ cmd_desc.in_trans = TRANS_MAILBOX;
+ cmd_desc.in_param = get_inprm_buf();
+ in_param_size =
+ MT_STRUCT_SIZE(arbelprm_virtual_physical_mapping_st) *
+ map_icm_p->num_vpm;
+ cmd_desc.in_param_size = in_param_size;
+ memset(cmd_desc.in_param, 0, in_param_size);
+
+ for (i = 0; i < map_icm_p->num_vpm; ++i) {
+ off = (unsigned long)(cmd_desc.in_param) +
+ MT_STRUCT_SIZE(arbelprm_virtual_physical_mapping_st) * i;
+ INS_FLD(map_icm_p->vpm_arr[i].va_h, off,
+ arbelprm_virtual_physical_mapping_st, va_h);
+ INS_FLD(map_icm_p->vpm_arr[i].va_l >> 12, off,
+ arbelprm_virtual_physical_mapping_st, va_l);
+ INS_FLD(map_icm_p->vpm_arr[i].pa_h, off,
+ arbelprm_virtual_physical_mapping_st, pa_h);
+ INS_FLD(map_icm_p->vpm_arr[i].pa_l >> 12, off,
+ arbelprm_virtual_physical_mapping_st, pa_l);
+ INS_FLD(map_icm_p->vpm_arr[i].log2_size, off,
+ arbelprm_virtual_physical_mapping_st, log2size);
+ }
+
+ rc = cmd_invoke(&cmd_desc);
+
+ return rc;
+}
+
+/*
+ * cmd_query_dev_lim
+ */
+static int cmd_query_dev_lim(struct dev_lim_st *dev_lim_p)
+{
+ int rc;
+ command_fields_t cmd_desc;
+
+ memset(&cmd_desc, 0, sizeof cmd_desc);
+
+ cmd_desc.opcode = MEMFREE_CMD_QUERY_DEV_LIM;
+ cmd_desc.out_trans = TRANS_MAILBOX;
+ cmd_desc.out_param = get_outprm_buf();
+ cmd_desc.out_param_size = MT_STRUCT_SIZE(arbelprm_query_dev_lim_st);
+
+ rc = cmd_invoke(&cmd_desc);
+ if (!rc) {
+ dev_lim_p->log2_rsvd_qps =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ log2_rsvd_qps);
+ dev_lim_p->qpc_entry_sz =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ qpc_entry_sz);
+
+ dev_lim_p->log2_rsvd_srqs =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ log2_rsvd_srqs);
+ dev_lim_p->srq_entry_sz =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ srq_entry_sz);
+
+ dev_lim_p->log2_rsvd_ees =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ log2_rsvd_ees);
+ dev_lim_p->eec_entry_sz =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ eec_entry_sz);
+
+ dev_lim_p->log2_rsvd_cqs =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ log2_rsvd_cqs);
+ dev_lim_p->cqc_entry_sz =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ cqc_entry_sz);
+
+ dev_lim_p->log2_rsvd_mtts =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ log2_rsvd_mtts);
+ dev_lim_p->mtt_entry_sz =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ mtt_entry_sz);
+
+ dev_lim_p->log2_rsvd_mrws =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ log2_rsvd_mrws);
+ dev_lim_p->mpt_entry_sz =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ mpt_entry_sz);
+
+ dev_lim_p->log2_rsvd_rdbs =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ log2_rsvd_rdbs);
+
+ dev_lim_p->eqc_entry_sz =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ eqc_entry_sz);
+
+ dev_lim_p->max_icm_size_l =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ max_icm_size_l);
+ dev_lim_p->max_icm_size_h =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ max_icm_size_h);
+
+ dev_lim_p->num_rsvd_uars =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ num_rsvd_uars);
+ dev_lim_p->uar_sz =
+ EX_FLD(cmd_desc.out_param, arbelprm_query_dev_lim_st,
+ uar_sz);
+ }
+
+ return rc;
+}
diff --git a/gpxe/src/drivers/net/mlx_ipoib/cmdif_priv.h b/gpxe/src/drivers/net/mlx_ipoib/cmdif_priv.h
new file mode 100644
index 00000000..dbb9a373
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/cmdif_priv.h
@@ -0,0 +1,50 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#ifndef __cmdif_priv__h__
+#define __cmdif_priv__h__
+
+typedef enum {
+ TRANS_NA,
+ TRANS_IMMEDIATE,
+ TRANS_MAILBOX
+} trans_type_t;
+
+typedef struct {
+ __u32 *in_param; /* holds the virtually contigious buffer of the parameter block passed */
+ unsigned int in_param_size;
+ trans_type_t in_trans;
+
+ __u32 input_modifier;
+
+ __u32 *out_param; /* holds the virtually contigious buffer of the parameter block passed */
+ unsigned int out_param_size;
+ trans_type_t out_trans;
+
+ __u32 opcode;
+ __u8 opcode_modifier;
+} command_fields_t;
+
+typedef int XHH_cmd_status_t;
+
+static XHH_cmd_status_t cmd_invoke(command_fields_t * cmd_prms);
+
+#endif
diff --git a/gpxe/src/drivers/net/mlx_ipoib/doc/README.boot_over_ib b/gpxe/src/drivers/net/mlx_ipoib/doc/README.boot_over_ib
new file mode 100644
index 00000000..07738628
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/doc/README.boot_over_ib
@@ -0,0 +1,176 @@
+.Boot over IB over Mellanox HCAs README - Pre-Alpha release
+==========================================================
+Document #2442, Rev 0.10, December 2005
+
+
+1. General
+-----------
+This README describes the Boot over IB package which enables booting a Linux
+kernel from a remote server using one of the Mellanox Technologies HCA
+devices. The package is based on Etherboot 5.4.1.
+
+The package actually implements a network driver for Etherboot. The wire
+protocol is compliant with IP Over IB
+(see http://www.ietf.org/html.charters/ipoib-charter.html for related
+documents).
+
+Etherboot uses a traditional setup of a DHCP server and a TFTP server to
+perform a remote boot in a similar manner to Ethernet NICs. The binary code is
+exported by the device as an expansion ROM image.
+
+
+2. Supported Devices
+---------------------
+The following Mellanox Technologies HCA devices are supported:
+
+ PCI Device ID Mellanox HCA Device
+ ----------------------------------------------
+ 23108 InfiniHost (P/N MT23108)
+ 25208 InfiniHost III Ex (P/N MT25208) (InfiniHost)
+ 25218 InfiniHost III Ex (P/N MT25208) (MemFree)
+ 25204 InfiniHost III Lx (P/N MT25204)
+
+
+ Note: For devices with more than one IB port, port 1 is used for
+ communications.
+
+
+3. Compiling
+----------------
+From the src directory:
+Run" make bin/<device>.<ext>
+where device can be any of:
+ MT23108
+ MT25218
+ MT25208
+ MT25204
+
+and ext can be rom, zrom etc. (see Etherbot doumentation for more details)
+
+4. Directory Structure
+-----------------------
+All driver files are available under src/drivers/net/mlx_ipoib/. Under this
+directory the following files can be found:
+
+ *** doc - Contains related documents including this file.
+ *** patches - Contains needed patch files.
+ *** samples - Contains sample files.
+ *** . Contains driver source files.
+
+
+5. Burning the Flash Memory
+----------------------------
+The binary code resides in the same Flash device of the device firmware.
+However the binary files are distinct and do not affect each other. Mellanox's
+'mlxburn' tool is available for burning, however, it is not possible to burn
+the expansion ROM image by itself; rather, both the firmware and expansion ROM
+images must be burnt simultaneously.
+
+'mlxburn' is part of the Mellanox Firmware Tools (MFT) package available for
+download from www.mellanox.com under 'Firmware Downloads'.
+
+Example:
+The following command burns a firmware image and an expansion ROM image to an
+InfiniHost Adapter Card (P/N MHX-CE128-T.ini):
+
+ mlxburn -fw fw-23108-a1-rel.mlx -exp_rom MT23108.bin
+ /dev/mst/mt23108_pci_cr0 -conf MHX-CE128-T.ini
+
+*** Important Note: The .ini file must support burning expansion ROMs. For
+example, the following lines must appear in the .ini file. If they do not,
+please add them manually.
+
+[ADAPTER]
+exp_rom_en = true
+
+Mellanox's web site contains firmware binary files with extension .bin.gz.
+These files contain contain EVERYTHING the goes in the flash memory and thus
+are NOT suitable for including the expansion rom image. Therefore, one use the
+files with .mlx extension also available from Mellanox's web site.
+
+
+
+6. Preparing the DHCP Server
+-----------------------------
+DHCP messages over IP Over IB are transmitted as broadcasts. In order to
+distinguish between messages belonging to a certain DHCP session, the messages
+must carry the client identifier option (see ietf documentation referred to
+above). As of November 2005, ISC DHCP servers do not support this feature.
+They are expected to support this at the end of 2005. In order to work this
+out, the appropriate patch must be applied (see patches directory). It has
+been tested on version isc-dhcpd-V3.0.4b2.
+
+The DHCP server must run on a machine which supports IP Over IB. The Mellanox
+IBGD package (gen1 or gen2) can be used to provide this.
+To run the DHCP server from the command line use: dhcpd ib0
+
+7. DHCP Server Configuration File
+----------------------------------
+In order for the DHCP server to provide configuration records for clients, an
+appropriate configuration file dhcpd.conf must be created and put under /etc/.
+A sample configuration file with comments can be found in the samples directory.
+
+
+8. OpenSM
+----------
+To successfully boot over IB, the IB network must contain a Subnet Manager
+which configures the IB network. OpenSM is part of the IBGD distribution and
+can be used to accomplish that. Note that OpenSM may be run on the same host
+running the DHCP server but it is not mandatory.
+
+
+9. TFTP Server
+---------------
+When the DHCP session is completed, the client has the IP address of the TFTP
+server from which it should download the kernel image. This TFTP server must
+run on a machine with InfiniBand support. The client loads first a loader
+image based on PXE API which then loads the kernel image. The image can be
+found in the Linux kernel homepage:
+
+http://www.kernel.org/pub/linux/boot/syslinux/
+
+
+10. BIOS Configuration
+-----------------------
+The expansion ROM image presents itself to the BIOS as a boot device. As a
+result, the BIOS will add it to the list of boot devices. The priority of this
+list can be modified when entering the BIOS setup. The boot over IB image must
+be set first for the BIOS to attempt to use it first.
+
+
+11. Operation
+--------------
+When booting the client, a message appears on the screen describing the device
+found and the revision of the code. The user has 3 seconds to press a key for
+increased functionality:
+'V' will increase verbosity.
+'I' will prinit some information to aid in configuring the DHCP configuration
+ file. In this case the display will hold for 5 seconds to let the user
+ grasp the information.
+
+Note that increasing verbosity will significantly slow boot time and will
+print lots of debug messages to the screen.
+
+
+12. Diskless Machines
+----------------------
+Most often it is required to boot a diskless machine. In these cases the
+kernel mounts its root file system over NFS over one of the interfaces. For
+this to happen on a client with only InfiniBand interfaces, the kernel image
+must be configured accordingly and must include IP Over IB support. This can
+be achieved either by compiling this into the kernel or using initrd images
+that contain IP Over IB support.
+
+
+13. Changing Defaults
+----------------------
+As stated the driver uses port 1 for its communications. To use another port
+edit the file src/drivers/net/ib_driver.h and modify the definition of
+PXE_IB_PORT.
+
+
+14. Installing a package from Mellanox
+--------------------------------------
+When using a package obtained from Mellanox Technologies' web site, the
+directory src/bin will contain the driver binary files. The files have a .bin
+extension and are equivalent to the same files with .zrom extension.
diff --git a/gpxe/src/drivers/net/mlx_ipoib/ib_driver.c b/gpxe/src/drivers/net/mlx_ipoib/ib_driver.c
new file mode 100644
index 00000000..a46db7fc
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/ib_driver.c
@@ -0,0 +1,342 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#include "ib_driver.h"
+
+static const __u8 ipv4_bcast_gid[] = {
+ 0xff, 0x12, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+};
+
+static int wait_logic_link_up(__u8 port)
+{
+ unsigned int relax_time, max_time;
+ relax_time = 500;
+ max_time = 30000; /* 30 seconds */
+ int rc;
+ unsigned int i, error = 1;
+ __u16 status;
+ struct port_info_st pi_var;
+ __u8 port_state;
+
+ for (i = 0; i < max_time; i += relax_time) {
+ rc = get_port_info(port, &pi_var, &status);
+ if (rc) {
+ eprintf("");
+ return rc;
+ } else {
+ if (status == 0) {
+ port_state = (pi_var.combined4 >> 24) & 0xf;
+ //port_state= pi_var.port_state;
+ if (port_state == 4) {
+ error = 0;
+ break;
+ }
+ }
+ }
+ printf("+");
+ mdelay(relax_time);
+ }
+
+ if (i >= max_time)
+ return -1;
+
+ return 0;
+}
+
+static int ib_driver_init(struct pci_device *pci, udqp_t * ipoib_qph_p)
+{
+ int rc;
+ __u8 port;
+ __u16 status;
+ __u32 qkey;
+ __u16 mlid;
+ ud_av_t av;
+ struct ib_eqe_st ib_eqe;
+ __u8 num_eqe;
+
+ tprintf("");
+ rc = ib_device_init(pci);
+ if (rc)
+ return rc;
+
+ tprintf("");
+
+ memcpy(ib_data.bcast_gid.raw, ipv4_bcast_gid, sizeof(ipv4_bcast_gid));
+
+ port = PXE_IB_PORT;
+ rc = setup_hca(port, &ib_data.eq);
+ if (rc)
+ return rc;
+ tprintf("setup_hca() success");
+
+ ib_data.port = port;
+
+ if(print_info)
+ printf("boot port = %d\n", ib_data.port);
+
+ rc = wait_logic_link_up(port);
+ if (rc)
+ return rc;
+
+ tprintf("wait_logic_link_up() success");
+
+ rc = get_guid_info(&status);
+ if (rc) {
+ eprintf("");
+ return rc;
+ } else if (status) {
+ eprintf("");
+ return rc;
+ }
+
+ tprintf("get_guid_info() success");
+
+ /* this to flush stdout that contains previous chars */
+ printf(" \n");
+ if(print_info) {
+ __u8 *gid=ib_data.port_gid.raw;
+
+ printf("\n");
+ printf("port GID=%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:"
+ "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\n",
+ gid[0],gid[1],gid[2],gid[3],gid[4],gid[5],gid[6],gid[7],
+ gid[8],gid[9],gid[10],gid[11],gid[12],gid[13],gid[14],gid[15]);
+ }
+
+ rc = get_pkey_tbl(NULL, &status);
+ if (rc) {
+ eprintf("");
+ return rc;
+ } else if (status) {
+ eprintf("");
+ return rc;
+ }
+ rc = create_mads_qp(&ib_data.mads_qp,
+ &ib_data.mads_snd_cq, &ib_data.mads_rcv_cq);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+
+ tprintf("attempt to join mcast group ...");
+ rc = join_mc_group(&qkey, &mlid, 1);
+ if (rc) {
+ eprintf("");
+ return rc;
+ } else {
+ tprintf("join_mc_group() successfull qkey=0x%lx, mlid=0x%x",
+ qkey, mlid);
+ }
+
+ rc = create_ipoib_qp(&ib_data.ipoib_qp,
+ &ib_data.ipoib_snd_cq,
+ &ib_data.ipoib_rcv_cq, qkey);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+
+ tprintf("create_ipoib_qp() success");
+ *ipoib_qph_p = ib_data.ipoib_qp;
+
+ tprintf("register qp to receive mcast...");
+ rc = add_qp_to_mcast_group(ib_data.bcast_gid, 1);
+ if (rc) {
+ eprintf("");
+ return rc;
+ } else {
+ tprintf("add_qp_to_mcast_group() success");
+ }
+
+ /* create a broadcast group ud AV */
+ av = alloc_ud_av();
+ if (!av) {
+ eprintf("");
+ return -1;
+ }
+ tprintf("alloc_ud_av() success");
+ modify_av_params(av, mlid, 1, 0, 0, &ib_data.bcast_gid, BCAST_QPN);
+ tprintf("modify_av_params() success");
+ ib_data.bcast_av = av;
+
+ do {
+ rc = poll_eq(&ib_eqe, &num_eqe);
+ if (rc) {
+ eprintf("");
+ return -1;
+ }
+ if (num_eqe) {
+ tprintf("num_eqe=%d", num_eqe);
+ }
+ tprintf("num_eqe=%d", num_eqe);
+ } while (num_eqe);
+ tprintf("eq is drained");
+
+ clear_interrupt();
+
+ return rc;
+}
+
+static int ib_driver_close(int fw_fatal)
+{
+ int rc, ret = 0;
+ __u32 qkey;
+ __u16 mlid;
+
+ rc = ib_device_close();
+ if (rc) {
+ eprintf("ib_device_close() failed");
+ ret = 1;
+ }
+
+ tprintf("");
+ if (!fw_fatal) {
+ rc = join_mc_group(&qkey, &mlid, 0);
+ if (rc) {
+ eprintf("");
+ ret = 1;
+ }
+ tprintf("join_mc_group(leave) success");
+
+ rc = add_qp_to_mcast_group(ib_data.bcast_gid, 0);
+ if (rc) {
+ eprintf("");
+ ret = 1;
+ }
+ tprintf("add_qp_to_mcast_group(remove) success");
+
+ rc = cmd_close_ib(ib_data.port);
+ if (rc) {
+ eprintf("");
+ ret = 1;
+ }
+ tprintf("cmd_close_ib(%d) success", ib_data.port);
+
+ if (destroy_udqp(ib_data.mads_qp)) {
+ eprintf("");
+ ret = 1;
+ }
+
+ if (destroy_udqp(ib_data.ipoib_qp)) {
+ eprintf("");
+ ret = 1;
+ }
+ }
+
+ rc = cmd_close_hca(fw_fatal);
+ if (rc) {
+ eprintf("");
+ ret = 1;
+ }
+
+ if (!fw_fatal) {
+ rc = cmd_sys_dis();
+ if (rc) {
+ eprintf("");
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+static int poll_cqe_tout(cq_t cqh, __u16 tout, void **wqe, int *good_p)
+{
+ int rc;
+ struct ib_cqe_st ib_cqe;
+ __u8 num_cqes;
+ unsigned long end;
+
+ end = currticks() + tout;
+ do {
+ rc = ib_poll_cq(cqh, &ib_cqe, &num_cqes);
+ if (rc)
+ return rc;
+
+ if (num_cqes == 1) {
+ if (good_p) {
+ *good_p = ib_cqe.is_error ? 0 : 1;
+ }
+ if (wqe)
+ *wqe = ib_cqe.wqe;
+ return 0;
+ }
+ }
+ while (currticks() < end);
+
+ return -1;
+}
+
+static u8 *get_port_gid(void)
+{
+ return ib_data.port_gid.raw;
+}
+
+static __u32 ib_get_qpn(udqp_t qph)
+{
+ __u32 qpn;
+
+ qpn = dev_get_qpn(qph);
+
+ return qpn;
+}
+
+static int drain_eq(void)
+{
+ __u8 num_eqe = 0, tot_eqe = 0;
+ int rc;
+
+ do {
+ tot_eqe += num_eqe;
+ rc = poll_eq(ib_data.eq, &num_eqe);
+ if (rc) {
+ eprintf("");
+ return -1;
+ }
+
+ tprintf("num_eqe=%d", num_eqe);
+ } while (num_eqe);
+ tprintf("eq is drained");
+ if (tot_eqe) {
+ tprintf("got %d eqes", tot_eqe);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int poll_error_buf(void)
+{
+ __u32 *ptr= dev_ib_data.error_buf_addr;
+ __u32 i;
+
+ for (i=0; i<dev_ib_data.error_buf_size; ++i, ptr++) {
+ if ( readl(ptr) ) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
diff --git a/gpxe/src/drivers/net/mlx_ipoib/ib_driver.h b/gpxe/src/drivers/net/mlx_ipoib/ib_driver.h
new file mode 100644
index 00000000..305bb5d4
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/ib_driver.h
@@ -0,0 +1,169 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#ifndef __ib_driver_h__
+#define __ib_driver_h__
+
+#define MELLANOX_VENDOR_ID 0x15b3
+
+#define GLOBAL_PD 0x123456
+#define GLOBAL_QKEY 0x80010000
+
+#define MAD_BUF_SZ 256
+#define IPOIB_RCV_BUF_SZ 2048
+#define IPOIB_SND_BUF_SZ 2048
+#define GRH_SIZE 40
+
+#define ARP_BUF_SZ 56
+
+#define FL_EOL 255 /* end of free list */
+
+#define SEND_CQE_POLL_TOUT 38 /* 2 sec */
+#define SA_RESP_POLL_TOUT 91 /* 5 seconds */
+
+#define NUM_AVS 10
+
+#define PXE_IB_PORT 1
+
+#define SA_QPN 1
+#define BCAST_QPN 0xffffff
+
+#define QPN_BASE 0x550000
+
+enum {
+ MADS_QPN_SN,
+ IPOIB_QPN_SN,
+ MAX_APP_QPS
+};
+
+enum {
+ MADS_SND_CQN_SN,
+ MADS_RCV_CQN_SN,
+ IPOIB_SND_CQN_SN,
+ IPOIB_RCV_CQN_SN,
+ MAX_APP_CQS
+};
+
+enum {
+ MTU_256 = 1,
+ MTU_512 = 2,
+ MTU_1024 = 3,
+ MTU_2048 = 4,
+};
+
+#define HCR_BASE 0x80680
+#define HCR_OFFSET_GO 0x80698
+#define HCR_OFFSET_STATUS 0x80698
+#define HCR_OFFSET_OUTPRM_H 0x8068C
+#define HCR_OFFSET_OUTPRM_L 0x80690
+
+#define MKEY_PREFIX 0x77000000
+#define MKEY_IDX_MASK 0xffffff
+
+/* event types */
+/*=============*/
+/* Completion Events */
+#define XDEV_EV_TYPE_CQ_COMP 0
+
+ /* IB - affiliated errors CQ */
+#define XDEV_EV_TYPE_CQ_ERR 0x04
+#define XDEV_EV_TYPE_LOCAL_WQ_CATAS_ERR 0x05
+
+ /* Unaffiliated errors */
+#define XDEV_EV_TYPE_PORT_ERR 0x09
+#define XDEV_EV_TYPE_LOCAL_WQ_INVALID_REQ_ERR 0x10
+#define XDEV_EV_TYPE_LOCAL_WQ_ACCESS_VIOL_ERR 0x11
+
+/* NOPCODE field enumeration for doorbells and send-WQEs */
+#define XDEV_NOPCODE_SEND 10 /* Send */
+
+struct ib_gid_u32_st {
+ __u32 dw[4];
+};
+
+union ib_gid_u {
+ __u8 raw[16];
+ struct ib_gid_u32_st as_u32;
+} __attribute__ ((packed));
+
+struct ib_cqe_st {
+ __u8 is_error;
+ __u8 is_send;
+ void *wqe;
+ __u32 count;
+};
+
+typedef void *udqp_t;
+typedef void *cq_t;
+typedef void *ud_av_t;
+typedef void *ud_send_wqe_t;
+typedef void *eq_t;
+
+struct ib_data_st {
+// __u32 mkey;
+// __u32 pd;
+// __u32 qkey;
+ udqp_t mads_qp;
+ udqp_t ipoib_qp;
+ cq_t mads_snd_cq;
+ cq_t mads_rcv_cq;
+ cq_t ipoib_snd_cq;
+ cq_t ipoib_rcv_cq;
+ eq_t eq;
+ __u16 sm_lid;
+ __u16 pkey;
+ union ib_gid_u port_gid;
+ union ib_gid_u bcast_gid;
+ ud_av_t bcast_av; /* av allocated and used solely for broadcast */
+ __u8 port;
+};
+
+static int setup_hca(__u8 port, void **eq_p);
+static int post_send_req(udqp_t qp, ud_send_wqe_t wqe, __u8 num_gather);
+static void prep_send_wqe_buf(udqp_t qp,
+ ud_av_t av,
+ ud_send_wqe_t wqe,
+ const void *buf,
+ unsigned int offset, __u16 len, __u8 e);
+
+static int create_mads_qp(void **qp_pp, void **snd_cq_pp, void **rcv_cq_pp);
+
+static int create_ipoib_qp(udqp_t * qp_p,
+ void **snd_cq_pp, void **rcv_cq_pp, __u32 qkey);
+
+static int gw_read_cr(__u32 addr, __u32 * result);
+static int gw_write_cr(__u32 addr, __u32 data);
+static ud_av_t alloc_ud_av(void);
+static void free_ud_av(ud_av_t av);
+static int ib_poll_cq(cq_t cq, struct ib_cqe_st *ib_cqe_p, __u8 * num_cqes);
+static int add_qp_to_mcast_group(union ib_gid_u mcast_gid, __u8 add);
+static int clear_interrupt(void);
+static int poll_cqe_tout(cq_t cqh, __u16 tout, void **wqe, int *good_p);
+
+static void *get_inprm_buf(void);
+static void *get_outprm_buf(void);
+static __u32 ib_get_qpn(udqp_t qph);
+
+static void dev_post_dbell(void *dbell, __u32 offset);
+
+static struct ib_data_st ib_data;
+
+#endif /* __ib_driver_h__ */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/ib_mad.c b/gpxe/src/drivers/net/mlx_ipoib/ib_mad.c
new file mode 100644
index 00000000..3e263a5b
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/ib_mad.c
@@ -0,0 +1,396 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#include "ib_mad.h"
+#include "mad_attrib.h"
+#include "cmdif.h"
+#include "ib_driver.h"
+
+#define TID_START 0x1234
+#define TID_INC 117
+
+static u32 next_tid = TID_START;
+
+/*
+ * get_port_info
+ *
+ * query the local device for the portinfo attribute
+ *
+ * port(in) port number to query
+ * buf(out) buffer to hold the result
+ */
+static int get_port_info(__u8 port, struct port_info_st *buf, __u16 * status)
+{
+ union port_info_mad_u *inprm;
+ union port_info_mad_u *outprm;
+ int rc;
+
+ inprm = get_inprm_buf();
+ outprm = get_outprm_buf();
+ memset(inprm, 0, sizeof *inprm);
+
+ inprm->mad.mad_hdr.method = IB_MGMT_METHOD_GET;
+ inprm->mad.mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+ inprm->mad.mad_hdr.class_version = 1;
+ inprm->mad.mad_hdr.base_version = IB_MGMT_BASE_VERSION;
+ inprm->mad.mad_hdr.attr_id = IB_SMP_ATTR_PORT_INFO;
+ inprm->mad.mad_hdr.attr_mod = port;
+
+ rc = cmd_mad_ifc(inprm, (struct ib_mad_st *)outprm, port);
+ if (!rc) {
+ memcpy(buf, &outprm->mad.port_info,
+ sizeof(outprm->mad.port_info));
+ *status = inprm->mad.mad_hdr.status;
+ if (!(*status)) {
+ ib_data.sm_lid = outprm->mad.port_info.mastersm_lid;
+ memcpy(&ib_data.port_gid.raw[0],
+ outprm->mad.port_info.gid_prefix, 8);
+ cpu_to_be_buf(&ib_data.port_gid.raw[0], 8);
+ }
+ }
+ return rc;
+}
+
+/*
+ * get_guid_info
+ *
+ * query the local device for the guidinfo attribute
+ *
+ * buf(out) buffer to hold the result
+ */
+static int get_guid_info(__u16 * status)
+{
+ union guid_info_mad_u *inprm;
+ union guid_info_mad_u *outprm;
+ int rc;
+
+ inprm = get_inprm_buf();
+ outprm = get_outprm_buf();
+ memset(inprm, 0, sizeof *inprm);
+
+ inprm->mad.mad_hdr.method = IB_MGMT_METHOD_GET;
+ inprm->mad.mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+ inprm->mad.mad_hdr.class_version = 1;
+ inprm->mad.mad_hdr.base_version = IB_MGMT_BASE_VERSION;
+ inprm->mad.mad_hdr.attr_id = IB_SMP_ATTR_GUID_INFO;
+ inprm->mad.mad_hdr.attr_mod = 0;
+
+ rc = cmd_mad_ifc(inprm, (struct ib_mad_st *)outprm, ib_data.port);
+ if (!rc) {
+ *status = inprm->mad.mad_hdr.status;
+ if (!(*status)) {
+ memcpy(&ib_data.port_gid.raw[8],
+ &outprm->mad.guid_info.gid_tbl[0], 8);
+ cpu_to_be_buf(&ib_data.port_gid.raw[8], 8);
+ }
+ }
+ return rc;
+}
+
+static int get_pkey_tbl(struct pkey_tbl_st *pkey_tbl, __u16 * status)
+{
+ union pkey_tbl_mad_u *inprm;
+ union pkey_tbl_mad_u *outprm;
+ int rc;
+
+ inprm = get_inprm_buf();
+ outprm = get_outprm_buf();
+ memset(inprm, 0, sizeof *inprm);
+ memset(outprm, 0, sizeof *outprm);
+
+ inprm->mad.mad_hdr.method = IB_MGMT_METHOD_GET;
+ inprm->mad.mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+ inprm->mad.mad_hdr.class_version = 1;
+ inprm->mad.mad_hdr.base_version = IB_MGMT_BASE_VERSION;
+ inprm->mad.mad_hdr.attr_id = IB_SMP_ATTR_PKEY_TABLE;
+ inprm->mad.mad_hdr.attr_mod = 0;
+
+ rc = cmd_mad_ifc(inprm, (struct ib_mad_st *)outprm, 1);
+ if (!rc) {
+ if (pkey_tbl)
+ memcpy(pkey_tbl, &outprm->mad.pkey_tbl, 2);
+ *status = inprm->mad.mad_hdr.status;
+ if (!(*status)) {
+ ib_data.pkey = outprm->mad.pkey_tbl.pkey_tbl[0][1];
+ ib_data.bcast_gid.raw[4] =
+ outprm->mad.pkey_tbl.pkey_tbl[0][1] >> 8;
+ ib_data.bcast_gid.raw[5] =
+ outprm->mad.pkey_tbl.pkey_tbl[0][1] & 0xff;
+ }
+ }
+ return rc;
+}
+
+static int join_mc_group(__u32 * qkey_p, __u16 * mlid_p, __u8 join)
+{
+ struct mc_member_mad_st *mad, *rcv_mad;
+ void *snd_wqe;
+ void *tmp_wqe;
+ udqp_t qp;
+ void *av;
+ int rc;
+ u32 tid;
+ void *rcv_wqe;
+ int is_good;
+
+ qp = ib_data.mads_qp;
+
+ snd_wqe = alloc_send_wqe(qp);
+ if (!snd_wqe) {
+ eprintf("");
+ return -1;
+ }
+ tprintf("allocated snd_wqe=0x%lx", snd_wqe);
+
+ mad = get_send_wqe_buf(snd_wqe, 0);
+ memset(mad, 0, 256);
+
+ av = alloc_ud_av();
+ if (!av) {
+ eprintf("");
+ free_wqe(snd_wqe);
+ return -1;
+ }
+ modify_av_params(av, ib_data.sm_lid, 0, 0, 0, NULL, SA_QPN);
+
+ prep_send_wqe_buf(qp, av, snd_wqe, NULL, 0, 256, 0);
+
+ mad->mad_hdr.method = join ? IB_MGMT_METHOD_SET : IB_MGMT_METHOD_DELETE;
+ mad->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
+ mad->mad_hdr.class_version = 2;
+ mad->mad_hdr.base_version = IB_MGMT_BASE_VERSION;
+ mad->mad_hdr.attr_id = IB_SA_ATTR_MC_MEMBER_REC;
+ tid = next_tid;
+ next_tid += TID_INC;
+ mad->mad_hdr.tid[1] = tid;
+
+ mad->sa_hdr.comp_mask[1] = IB_SA_MCMEMBER_REC_MGID |
+ IB_SA_MCMEMBER_REC_PORT_GID | IB_SA_MCMEMBER_REC_JOIN_STATE;
+
+ mad->mc_member.combined4 |= (1 << 24); /*mad->mc_member.join_state = 1; */
+
+ be_to_cpu_buf(mad, sizeof *mad);
+ memcpy(mad->mc_member.mgid, ib_data.bcast_gid.raw, 16);
+ memcpy(mad->mc_member.port_gid, ib_data.port_gid.raw, 16);
+
+ rc = post_send_req(qp, snd_wqe, 1);
+ if (rc) {
+ eprintf("");
+ free_ud_av(av);
+ free_wqe(snd_wqe);
+ return -1;
+ }
+
+ tprintf("");
+ /* poll the CQ to get the completions
+ on the send and the expected receive */
+
+ /* send completion */
+ rc = poll_cqe_tout(ib_data.mads_snd_cq, SEND_CQE_POLL_TOUT, &tmp_wqe,
+ &is_good);
+ if (rc) {
+ eprintf("");
+ return -1;
+ }
+
+ if (tmp_wqe != snd_wqe) {
+ eprintf("");
+ return -1;
+ }
+
+ if (free_wqe(snd_wqe)) {
+ eprintf("");
+ return -1;
+ }
+ free_ud_av(av);
+
+ if (!is_good) {
+ eprintf("");
+ return -1;
+ }
+
+ /* receive completion */
+ rc = poll_cqe_tout(ib_data.mads_rcv_cq, SA_RESP_POLL_TOUT, &rcv_wqe,
+ &is_good);
+ if (rc) {
+ eprintf("");
+ return -1;
+ }
+
+ if (is_good) {
+ rcv_mad = get_rcv_wqe_buf(rcv_wqe, 1);
+ be_to_cpu_buf(rcv_mad, sizeof *rcv_mad);
+ if (rcv_mad->mad_hdr.tid[1] == tid) {
+ /* that's our response */
+ if (mad->mad_hdr.status == 0) {
+ /* good response - save results */
+ *qkey_p = rcv_mad->mc_member.q_key;
+ *mlid_p = rcv_mad->mc_member.combined1 >> 16; // rcv_mad->mc_member.mlid;
+ } else {
+ /* join failed */
+ eprintf("");
+ return -1;
+ }
+ } else {
+ /* not our response */
+ eprintf("");
+ return -1;
+ }
+ }
+
+ if (free_wqe(rcv_wqe)) {
+ eprintf("");
+ return -1;
+ }
+
+ return is_good ? 0 : -1;
+}
+
+static int get_path_record(union ib_gid_u *dgid, __u16 * dlid_p, u8 * sl_p,
+ u8 * rate_p)
+{
+ struct path_record_mad_st *mad, *rcv_mad;
+ void *snd_wqe;
+ udqp_t qp;
+ ud_av_t av;
+ void *tmp_wqe;
+ void *rcv_wqe;
+ u32 tid;
+ int rc;
+ int is_good;
+
+ tprintf("gid=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ dgid->raw[0], dgid->raw[1], dgid->raw[2], dgid->raw[3],
+ dgid->raw[4], dgid->raw[5], dgid->raw[6], dgid->raw[7],
+ dgid->raw[8], dgid->raw[9], dgid->raw[10], dgid->raw[11],
+ dgid->raw[12], dgid->raw[13], dgid->raw[14], dgid->raw[15]);
+ qp = ib_data.mads_qp;
+
+ snd_wqe = alloc_send_wqe(qp);
+ if (!snd_wqe) {
+ eprintf("");
+ return -1;
+ }
+
+ mad = get_send_wqe_buf(snd_wqe, 0);
+ memset(mad, 0, 256);
+
+ av = alloc_ud_av();
+ if (!av) {
+ eprintf("");
+ free_wqe(snd_wqe);
+ return -1;
+ }
+ modify_av_params(av, ib_data.sm_lid, 0, 0, 0, NULL, SA_QPN);
+
+ prep_send_wqe_buf(qp, av, snd_wqe, NULL, 0, 256, 0);
+
+ mad->mad_hdr.method = IB_MGMT_METHOD_GET;
+ mad->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
+ mad->mad_hdr.class_version = 2;
+ mad->mad_hdr.base_version = IB_MGMT_BASE_VERSION;
+ mad->mad_hdr.attr_id = IB_SA_ATTR_PATH_REC;
+ tid = next_tid;
+ next_tid += TID_INC;
+ mad->mad_hdr.tid[1] = tid;
+
+ memcpy(mad->path_record.dgid.raw, dgid->raw, 16);
+ cpu_to_be_buf(mad->path_record.dgid.raw, 16);
+
+ mad->sa_hdr.comp_mask[1] = IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID;
+
+ cpu_to_be_buf(mad, sizeof *mad);
+ memcpy(mad->path_record.sgid.raw, ib_data.port_gid.raw, 16);
+
+ rc = post_send_req(qp, snd_wqe, 1);
+ if (rc) {
+ eprintf("");
+ free_ud_av(av);
+ free_wqe(snd_wqe);
+ return rc;
+ }
+
+ /* poll the CQ to get the completions
+ on the send and the expected receive */
+
+ /* send completion */
+ rc = poll_cqe_tout(ib_data.mads_snd_cq, SEND_CQE_POLL_TOUT, &tmp_wqe,
+ &is_good);
+ if (rc) {
+ eprintf("");
+ return -1;
+ }
+
+ if (tmp_wqe != snd_wqe) {
+ eprintf("");
+ return -1;
+ }
+
+ if (free_wqe(snd_wqe)) {
+ eprintf("");
+ return -1;
+ }
+ free_ud_av(av);
+
+ if (!is_good) {
+ eprintf("");
+ return -1;
+ }
+
+ /* receive completion */
+ rc = poll_cqe_tout(ib_data.mads_rcv_cq, SA_RESP_POLL_TOUT, &rcv_wqe,
+ &is_good);
+ if (rc) {
+ eprintf("");
+ return -1;
+ }
+
+ if (is_good) {
+ rcv_mad = get_rcv_wqe_buf(rcv_wqe, 1);
+ be_to_cpu_buf(rcv_mad, sizeof *rcv_mad);
+ if (rcv_mad->mad_hdr.tid[1] == tid) {
+ /* that's our response */
+ if (mad->mad_hdr.status == 0) {
+ /* good response - save results */
+ *dlid_p = rcv_mad->path_record.dlid;
+ *sl_p = (rcv_mad->path_record.combined3 >> 16) & 0xf; // rcv_mad->path_record.sl;
+ *rate_p = rcv_mad->path_record.combined3 & 0x3f; //rcv_mad->path_record.rate;
+ } else {
+ /* join failed */
+ eprintf("");
+ return -1;
+ }
+ } else {
+ /* not our response */
+ eprintf("");
+ return -1;
+ }
+ }
+
+ if (free_wqe(rcv_wqe)) {
+ eprintf("");
+ return -1;
+ }
+
+ tprintf("");
+ return is_good ? 0 : -1;
+}
diff --git a/gpxe/src/drivers/net/mlx_ipoib/ib_mad.h b/gpxe/src/drivers/net/mlx_ipoib/ib_mad.h
new file mode 100644
index 00000000..5ffb5404
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/ib_mad.h
@@ -0,0 +1,110 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#ifndef __ib_mad_h__
+#define __ib_mad_h__
+
+#include "ib_driver.h"
+
+/* Management base version */
+#define IB_MGMT_BASE_VERSION 1
+
+/* Management classes */
+#define IB_MGMT_CLASS_SUBN_LID_ROUTED 0x01
+#define IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE 0x81
+#define IB_MGMT_CLASS_SUBN_ADM 0x03
+#define IB_MGMT_CLASS_PERF_MGMT 0x04
+#define IB_MGMT_CLASS_BM 0x05
+#define IB_MGMT_CLASS_DEVICE_MGMT 0x06
+#define IB_MGMT_CLASS_CM 0x07
+#define IB_MGMT_CLASS_SNMP 0x08
+#define IB_MGMT_CLASS_VENDOR_RANGE2_START 0x30
+#define IB_MGMT_CLASS_VENDOR_RANGE2_END 0x4F
+
+/* Management methods */
+#define IB_MGMT_METHOD_GET 0x01
+#define IB_MGMT_METHOD_SET 0x02
+#define IB_MGMT_METHOD_GET_RESP 0x81
+#define IB_MGMT_METHOD_SEND 0x03
+#define IB_MGMT_METHOD_TRAP 0x05
+#define IB_MGMT_METHOD_REPORT 0x06
+#define IB_MGMT_METHOD_REPORT_RESP 0x86
+#define IB_MGMT_METHOD_TRAP_REPRESS 0x07
+#define IB_MGMT_METHOD_DELETE 0x15
+
+#define IB_MGMT_METHOD_RESP 0x80
+
+/* Subnet management attributes */
+#define IB_SMP_ATTR_NOTICE 0x0002
+#define IB_SMP_ATTR_NODE_DESC 0x0010
+#define IB_SMP_ATTR_NODE_INFO 0x0011
+#define IB_SMP_ATTR_SWITCH_INFO 0x0012
+#define IB_SMP_ATTR_GUID_INFO 0x0014
+#define IB_SMP_ATTR_PORT_INFO 0x0015
+#define IB_SMP_ATTR_PKEY_TABLE 0x0016
+#define IB_SMP_ATTR_SL_TO_VL_TABLE 0x0017
+#define IB_SMP_ATTR_VL_ARB_TABLE 0x0018
+#define IB_SMP_ATTR_LINEAR_FORWARD_TABLE 0x0019
+#define IB_SMP_ATTR_RANDOM_FORWARD_TABLE 0x001A
+#define IB_SMP_ATTR_MCAST_FORWARD_TABLE 0x001B
+#define IB_SMP_ATTR_SM_INFO 0x0020
+#define IB_SMP_ATTR_VENDOR_DIAG 0x0030
+#define IB_SMP_ATTR_LED_INFO 0x0031
+#define IB_SMP_ATTR_VENDOR_MASK 0xFF00
+
+struct ib_mad_hdr_st {
+ __u8 method;
+ __u8 class_version;
+ __u8 mgmt_class;
+ __u8 base_version;
+ __u16 class_specific;
+ __u16 status;
+ __u32 tid[2];
+ __u16 resv;
+ __u16 attr_id;
+ __u32 attr_mod;
+} __attribute__ ((packed));
+
+struct rmpp_hdr_st {
+ __u32 raw[3];
+} __attribute__ ((packed));
+
+struct sa_header_st {
+ __u32 sm_key[2];
+ __u16 attrib_offset;
+ __u16 r0;
+ __u32 comp_mask[2];
+} __attribute__ ((packed));
+
+struct ib_mad_st {
+ struct ib_mad_hdr_st mad_hdr;
+ __u8 data[232];
+} __attribute__ ((packed));
+
+union mad_u {
+ __u8 raw[256];
+ struct ib_mad_st mad;
+} __attribute__ ((packed));
+
+static int get_path_record(union ib_gid_u *dgid, __u16 * dlid_p, __u8 * sl_p,
+ __u8 * rate_p);
+
+#endif /* __ib_mad_h__ */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/ib_mt23108.c b/gpxe/src/drivers/net/mlx_ipoib/ib_mt23108.c
new file mode 100644
index 00000000..ca3abb10
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/ib_mt23108.c
@@ -0,0 +1,1701 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#include "mt23108.h"
+#include "ib_driver.h"
+#include <gpxe/pci.h>
+
+struct device_buffers_st {
+ union recv_wqe_u mads_qp_rcv_queue[NUM_MADS_RCV_WQES]
+ __attribute__ ((aligned(RECV_WQE_U_ALIGN)));
+ union recv_wqe_u ipoib_qp_rcv_queue[NUM_IPOIB_RCV_WQES]
+ __attribute__ ((aligned(RECV_WQE_U_ALIGN)));
+ union ud_send_wqe_u mads_qp_snd_queue[NUM_MADS_SND_WQES]
+ __attribute__ ((aligned(UD_SEND_WQE_U_ALIGN)));
+ union ud_send_wqe_u ipoib_qp_snd_queue[NUM_IPOIB_SND_WQES]
+ __attribute__ ((aligned(UD_SEND_WQE_U_ALIGN)));
+ u8 inprm_buf[INPRM_BUF_SZ] __attribute__ ((aligned(INPRM_BUF_ALIGN)));
+ u8 outprm_buf[OUTPRM_BUF_SZ]
+ __attribute__ ((aligned(OUTPRM_BUF_ALIGN)));
+ struct eqe_t eq_buf[1 << LOG2_EQ_SZ]
+ __attribute__ ((aligned(sizeof(struct eqe_t))));
+ union cqe_st mads_snd_cq_buf[NUM_MADS_SND_CQES]
+ __attribute__ ((aligned(sizeof(union cqe_st))));
+ union cqe_st ipoib_snd_cq_buf[NUM_IPOIB_SND_CQES]
+ __attribute__ ((aligned(sizeof(union cqe_st))));
+ union cqe_st mads_rcv_cq_buf[NUM_MADS_RCV_CQES]
+ __attribute__ ((aligned(sizeof(union cqe_st))));
+ union cqe_st ipoib_rcv_cq_buf[NUM_IPOIB_RCV_CQES]
+ __attribute__ ((aligned(sizeof(union cqe_st))));
+ union ud_av_u av_array[NUM_AVS]
+ __attribute__ ((aligned(ADDRESS_VECTOR_ST_ALIGN)));
+} __attribute__ ((packed));
+
+#define STRUCT_ALIGN_SZ 4096
+#define SRC_BUF_SZ (sizeof(struct device_buffers_st) + STRUCT_ALIGN_SZ - 1)
+
+/* the following must be kept in this order
+ for the memory region to cover the buffers */
+static u8 src_buf[SRC_BUF_SZ];
+static struct ib_buffers_st ib_buffers;
+static __u32 memreg_size;
+/* end of order constraint */
+
+static struct dev_pci_struct tavor_pci_dev;
+static struct device_buffers_st *dev_buffers_p;
+static struct device_ib_data_st dev_ib_data;
+
+static int gw_write_cr(__u32 addr, __u32 data)
+{
+ writel(htonl(data), tavor_pci_dev.cr_space + addr);
+ return 0;
+}
+
+static int gw_read_cr(__u32 addr, __u32 * result)
+{
+ *result = ntohl(readl(tavor_pci_dev.cr_space + addr));
+ return 0;
+}
+
+static int reset_hca(void)
+{
+ return gw_write_cr(TAVOR_RESET_OFFSET, 1);
+}
+
+static int find_mlx_bridge(__u8 hca_bus, __u8 * br_bus_p, __u8 * br_devfn_p)
+{
+ int bus;
+ int dev;
+ int devfn;
+ int rc;
+ __u16 vendor, dev_id;
+ __u8 sec_bus;
+
+ for (bus = 0; bus < 256; ++bus) {
+ for (dev = 0; dev < 32; ++dev) {
+ devfn = (dev << 3);
+ rc = pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID,
+ &vendor);
+ if (rc)
+ return rc;
+
+ if (vendor != MELLANOX_VENDOR_ID)
+ continue;
+
+ rc = pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID,
+ &dev_id);
+ if (rc)
+ return rc;
+
+ if (dev_id != TAVOR_BRIDGE_DEVICE_ID)
+ continue;
+
+ rc = pcibios_read_config_byte(bus, devfn,
+ PCI_SECONDARY_BUS,
+ &sec_bus);
+ if (rc)
+ return rc;
+
+ if (sec_bus == hca_bus) {
+ *br_bus_p = bus;
+ *br_devfn_p = devfn;
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static int ib_device_init(struct pci_device *dev)
+{
+ int i;
+ int rc;
+ __u8 br_bus, br_devfn;
+
+ tprintf("");
+
+ memset(&dev_ib_data, 0, sizeof dev_ib_data);
+
+ /* save bars */
+ tprintf("bus=%d devfn=0x%x", dev->bus, dev->devfn);
+ for (i = 0; i < 6; ++i) {
+ tavor_pci_dev.dev.bar[i] =
+ pci_bar_start(dev, PCI_BASE_ADDRESS_0 + (i << 2));
+ tprintf("bar[%d]= 0x%08lx", i, tavor_pci_dev.dev.bar[i]);
+ }
+
+ tprintf("");
+ /* save config space */
+ for (i = 0; i < 64; ++i) {
+ rc = pci_read_config_dword(dev, i << 2,
+ &tavor_pci_dev.dev.
+ dev_config_space[i]);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ tprintf("config[%d]= 0x%08lx", i << 2,
+ tavor_pci_dev.dev.dev_config_space[i]);
+ }
+
+ tprintf("");
+ tavor_pci_dev.dev.dev = dev;
+
+ tprintf("");
+ if (dev->dev_id == TAVOR_DEVICE_ID) {
+
+ rc = find_mlx_bridge(dev->bus, &br_bus, &br_devfn);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+
+ tavor_pci_dev.br.bus = br_bus;
+ tavor_pci_dev.br.devfn = br_devfn;
+
+ tprintf("bus=%d devfn=0x%x", br_bus, br_devfn);
+ /* save config space */
+ for (i = 0; i < 64; ++i) {
+ rc = pcibios_read_config_dword(br_bus, br_devfn, i << 2,
+ &tavor_pci_dev.br.
+ dev_config_space[i]);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ tprintf("config[%d]= 0x%08lx", i << 2,
+ tavor_pci_dev.br.dev_config_space[i]);
+ }
+ }
+
+ tprintf("");
+
+ /* map cr-space */
+ tavor_pci_dev.cr_space = ioremap(tavor_pci_dev.dev.bar[0], 0x100000);
+ if (!tavor_pci_dev.cr_space) {
+ eprintf("");
+ return -1;
+ }
+
+ /* map uar */
+ tavor_pci_dev.uar =
+ ioremap(tavor_pci_dev.dev.bar[2] + UAR_IDX * 0x1000, 0x1000);
+ if (!tavor_pci_dev.uar) {
+ eprintf("");
+ return -1;
+ }
+ tprintf("uar_base (pa:va) = 0x%lx 0x%lx",
+ tavor_pci_dev.dev.bar[2] + UAR_IDX * 0x1000, tavor_pci_dev.uar);
+
+ tprintf("");
+
+ return 0;
+}
+
+static inline unsigned long lalign(unsigned long buf, unsigned long align)
+{
+ return (unsigned long)((buf + align - 1) &
+ (~(((unsigned long)align) - 1)));
+}
+
+static int init_dev_data(void)
+{
+ unsigned long tmp;
+
+ tmp = lalign(virt_to_bus(src_buf), STRUCT_ALIGN_SZ);
+
+ dev_buffers_p = bus_to_virt(tmp);
+ memreg_size = (__u32) (&memreg_size) - (__u32) dev_buffers_p;
+ tprintf("src_buf=0x%lx, dev_buffers_p=0x%lx, memreg_size=0x%x", src_buf,
+ dev_buffers_p, memreg_size);
+
+ return 0;
+}
+
+static int restore_config(void)
+{
+ int i;
+ int rc;
+
+ if (tavor_pci_dev.dev.dev->dev_id == TAVOR_DEVICE_ID) {
+ for (i = 0; i < 64; ++i) {
+ rc = pcibios_write_config_dword(tavor_pci_dev.br.bus,
+ tavor_pci_dev.br.devfn,
+ i << 2,
+ tavor_pci_dev.br.
+ dev_config_space[i]);
+ if (rc) {
+ return rc;
+ }
+ }
+ }
+
+ for (i = 0; i < 64; ++i) {
+ if (i != 22 && i != 23) {
+ rc = pci_write_config_dword(tavor_pci_dev.dev.dev,
+ i << 2,
+ tavor_pci_dev.dev.
+ dev_config_space[i]);
+ if (rc) {
+ return rc;
+ }
+ }
+ }
+ return 0;
+}
+
+static void prep_init_hca_buf(const struct init_hca_st *init_hca_p, void *buf)
+{
+ /*struct init_hca_param_st */ void *p = buf;
+ void *tmp;
+
+ memset(buf, 0, MT_STRUCT_SIZE(tavorprm_init_hca_st));
+
+ tmp =
+ p + MT_BYTE_OFFSET(tavorprm_init_hca_st,
+ qpc_eec_cqc_eqc_rdb_parameters);
+
+ INS_FLD(init_hca_p->qpc_base_addr_h, tmp, tavorprm_qpcbaseaddr_st,
+ qpc_base_addr_h);
+ INS_FLD(init_hca_p->
+ qpc_base_addr_l >> (32 -
+ (MT_BIT_SIZE
+ (tavorprm_qpcbaseaddr_st,
+ qpc_base_addr_l))), tmp,
+ tavorprm_qpcbaseaddr_st, qpc_base_addr_l);
+ INS_FLD(init_hca_p->log_num_of_qp, tmp, tavorprm_qpcbaseaddr_st,
+ log_num_of_qp);
+
+ INS_FLD(init_hca_p->cqc_base_addr_h, tmp, tavorprm_qpcbaseaddr_st,
+ cqc_base_addr_h);
+ INS_FLD(init_hca_p->
+ cqc_base_addr_l >> (32 -
+ (MT_BIT_SIZE
+ (tavorprm_qpcbaseaddr_st,
+ cqc_base_addr_l))), tmp,
+ tavorprm_qpcbaseaddr_st, cqc_base_addr_l);
+ INS_FLD(init_hca_p->log_num_of_cq, tmp, tavorprm_qpcbaseaddr_st,
+ log_num_of_cq);
+
+ INS_FLD(init_hca_p->eqc_base_addr_h, tmp, tavorprm_qpcbaseaddr_st,
+ eqc_base_addr_h);
+ INS_FLD(init_hca_p->
+ eqc_base_addr_l >> (32 -
+ (MT_BIT_SIZE
+ (tavorprm_qpcbaseaddr_st,
+ eqc_base_addr_l))), tmp,
+ tavorprm_qpcbaseaddr_st, eqc_base_addr_l);
+ INS_FLD(LOG2_EQS, tmp, tavorprm_qpcbaseaddr_st, log_num_eq);
+
+ INS_FLD(init_hca_p->srqc_base_addr_h, tmp, tavorprm_qpcbaseaddr_st,
+ srqc_base_addr_h);
+ INS_FLD(init_hca_p->
+ srqc_base_addr_l >> (32 -
+ (MT_BIT_SIZE
+ (tavorprm_qpcbaseaddr_st,
+ srqc_base_addr_l))), tmp,
+ tavorprm_qpcbaseaddr_st, srqc_base_addr_l);
+ INS_FLD(init_hca_p->log_num_of_srq, tmp, tavorprm_qpcbaseaddr_st,
+ log_num_of_srq);
+
+ INS_FLD(init_hca_p->eqpc_base_addr_h, tmp, tavorprm_qpcbaseaddr_st,
+ eqpc_base_addr_h);
+ INS_FLD(init_hca_p->eqpc_base_addr_l, tmp, tavorprm_qpcbaseaddr_st,
+ eqpc_base_addr_l);
+
+ INS_FLD(init_hca_p->eeec_base_addr_h, tmp, tavorprm_qpcbaseaddr_st,
+ eeec_base_addr_h);
+ INS_FLD(init_hca_p->eeec_base_addr_l, tmp, tavorprm_qpcbaseaddr_st,
+ eeec_base_addr_l);
+
+ tmp = p + MT_BYTE_OFFSET(tavorprm_init_hca_st, multicast_parameters);
+
+ INS_FLD(init_hca_p->mc_base_addr_h, tmp, tavorprm_multicastparam_st,
+ mc_base_addr_h);
+ INS_FLD(init_hca_p->mc_base_addr_l, tmp, tavorprm_multicastparam_st,
+ mc_base_addr_l);
+
+ INS_FLD(init_hca_p->log_mc_table_entry_sz, tmp,
+ tavorprm_multicastparam_st, log_mc_table_entry_sz);
+ INS_FLD(init_hca_p->log_mc_table_sz, tmp, tavorprm_multicastparam_st,
+ log_mc_table_sz);
+ INS_FLD(init_hca_p->mc_table_hash_sz, tmp, tavorprm_multicastparam_st,
+ mc_table_hash_sz);
+
+ tmp = p + MT_BYTE_OFFSET(tavorprm_init_hca_st, tpt_parameters);
+
+ INS_FLD(init_hca_p->mpt_base_addr_h, tmp, tavorprm_tptparams_st,
+ mpt_base_adr_h);
+ INS_FLD(init_hca_p->mpt_base_addr_l, tmp, tavorprm_tptparams_st,
+ mpt_base_adr_l);
+ INS_FLD(init_hca_p->log_mpt_sz, tmp, tavorprm_tptparams_st, log_mpt_sz);
+
+ INS_FLD(init_hca_p->mtt_base_addr_h, tmp, tavorprm_tptparams_st,
+ mtt_base_addr_h);
+ INS_FLD(init_hca_p->mtt_base_addr_l, tmp, tavorprm_tptparams_st,
+ mtt_base_addr_l);
+
+ tmp = p + MT_BYTE_OFFSET(tavorprm_init_hca_st, uar_parameters);
+ INS_FLD(tavor_pci_dev.dev.bar[3], tmp, tavorprm_uar_params_st,
+ uar_base_addr_h);
+ INS_FLD(tavor_pci_dev.dev.bar[2] & 0xfff00000, tmp,
+ tavorprm_uar_params_st, uar_base_addr_l);
+
+}
+
+static void prep_sw2hw_mpt_buf(void *buf, __u32 mkey)
+{
+ INS_FLD(1, buf, tavorprm_mpt_st, m_io);
+ INS_FLD(1, buf, tavorprm_mpt_st, lw);
+ INS_FLD(1, buf, tavorprm_mpt_st, lr);
+ INS_FLD(1, buf, tavorprm_mpt_st, pa);
+ INS_FLD(1, buf, tavorprm_mpt_st, r_w);
+
+ INS_FLD(mkey, buf, tavorprm_mpt_st, mem_key);
+ INS_FLD(GLOBAL_PD, buf, tavorprm_mpt_st, pd);
+
+ INS_FLD(virt_to_bus(dev_buffers_p), buf, tavorprm_mpt_st,
+ start_address_l);
+ INS_FLD(memreg_size, buf, tavorprm_mpt_st, reg_wnd_len_l);
+}
+
+static void prep_sw2hw_eq_buf(void *buf, struct eqe_t *eq)
+{
+ memset(buf, 0, MT_STRUCT_SIZE(tavorprm_eqc_st));
+
+ INS_FLD(2, buf, tavorprm_eqc_st, st); /* fired */
+ INS_FLD(virt_to_bus(eq), buf, tavorprm_eqc_st, start_address_l);
+ INS_FLD(LOG2_EQ_SZ, buf, tavorprm_eqc_st, log_eq_size);
+ INS_FLD(UAR_IDX, buf, tavorprm_eqc_st, usr_page);
+ INS_FLD(GLOBAL_PD, buf, tavorprm_eqc_st, pd);
+ INS_FLD(dev_ib_data.mkey, buf, tavorprm_eqc_st, lkey);
+}
+
+static void init_eq_buf(void *eq_buf)
+{
+ int num_eqes = 1 << LOG2_EQ_SZ;
+
+ memset(eq_buf, 0xff, num_eqes * sizeof(struct eqe_t));
+}
+
+static void prep_init_ib_buf(void *buf)
+{
+ __u32 *ptr = (__u32 *) buf;
+
+ ptr[0] = 0x4310;
+ ptr[1] = 1;
+ ptr[2] = 64;
+}
+
+static void prep_sw2hw_cq_buf(void *buf, __u8 eqn, __u32 cqn,
+ union cqe_st *cq_buf)
+{
+ __u32 *ptr = (__u32 *) buf;
+
+ ptr[2] = virt_to_bus(cq_buf);
+ ptr[3] = (LOG2_CQ_SZ << 24) | UAR_IDX;
+ ptr[4] = eqn;
+ ptr[5] = eqn;
+ ptr[6] = dev_ib_data.pd;
+ ptr[7] = dev_ib_data.mkey;
+ ptr[12] = cqn;
+}
+
+static void prep_rst2init_qpee_buf(void *buf, __u32 snd_cqn, __u32 rcv_cqn,
+ __u32 qkey)
+{
+ struct qp_ee_state_tarnisition_st *prm;
+ void *tmp;
+
+ prm = (struct qp_ee_state_tarnisition_st *)buf;
+
+ INS_FLD(3, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st, st); /* service type = UD */
+ INS_FLD(3, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st, pm_state); /* required for UD QP */
+ INS_FLD(UAR_IDX, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st,
+ usr_page);
+ INS_FLD(dev_ib_data.pd, &prm->ctx,
+ tavorprm_queue_pair_ee_context_entry_st, pd);
+ INS_FLD(dev_ib_data.mkey, &prm->ctx,
+ tavorprm_queue_pair_ee_context_entry_st, wqe_lkey);
+ INS_FLD(1, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st, ssc); /* generate send CQE */
+ INS_FLD(1, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st, rsc); /* generate receive CQE */
+ INS_FLD(snd_cqn, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st,
+ cqn_snd);
+ INS_FLD(rcv_cqn, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st,
+ cqn_rcv);
+ INS_FLD(qkey, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st,
+ q_key);
+
+ tmp =
+ (void *)(&prm->ctx) +
+ MT_BYTE_OFFSET(tavorprm_queue_pair_ee_context_entry_st,
+ primary_address_path);
+ INS_FLD(dev_ib_data.port, tmp, tavorprm_address_path_st, port_number);
+
+ INS_FLD(4, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st, mtu);
+ INS_FLD(0xb, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st,
+ msg_max);
+}
+
+static void prep_init2rtr_qpee_buf(void *buf)
+{
+ struct qp_ee_state_tarnisition_st *prm;
+
+ prm = (struct qp_ee_state_tarnisition_st *)buf;
+
+ INS_FLD(4, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st, mtu);
+ INS_FLD(0xb, &prm->ctx, tavorprm_queue_pair_ee_context_entry_st,
+ msg_max);
+}
+
+static void init_av_array()
+{
+ int i;
+
+ dev_ib_data.udav.av_array = dev_buffers_p->av_array;
+ dev_ib_data.udav.udav_next_free = FL_EOL;
+ for (i = 0; i < NUM_AVS; ++i) {
+ dev_ib_data.udav.av_array[i].ud_av.next_free =
+ dev_ib_data.udav.udav_next_free;
+ dev_ib_data.udav.udav_next_free = i;
+ }
+ tprintf("dev_ib_data.udav.udav_next_free=%d", i);
+}
+
+static int setup_hca(__u8 port, void **eq_p)
+{
+ int rc;
+ __u32 key, in_key;
+ __u32 *inprm;
+ struct eqe_t *eq_buf;
+ __u32 event_mask;
+ void *cfg;
+ int ret = 0;
+ __u8 eqn;
+ struct dev_lim_st dev_lim;
+ struct init_hca_st init_hca;
+ __u32 offset, base_h, base_l;
+ const __u32 delta = 0x400000;
+ struct query_fw_st qfw;
+
+ tprintf("called");
+
+ init_dev_data();
+
+ rc = reset_hca();
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto exit;
+ } else {
+ tprintf("reset_hca() success");
+ }
+
+ mdelay(1000); /* wait for 1 sec */
+
+ rc = restore_config();
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto exit;
+ } else {
+ tprintf("restore_config() success");
+ }
+
+ dev_ib_data.pd = GLOBAL_PD;
+ dev_ib_data.port = port;
+
+ /* execute system enable command */
+ rc = cmd_sys_en();
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto exit;
+ } else {
+ tprintf("cmd_sys_en() success");
+ }
+
+ rc= cmd_query_fw(&qfw);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto exit;
+ } else {
+ tprintf("cmd_query_fw() success");
+
+ if (print_info) {
+ printf("FW ver = %d.%d.%d\n",
+ qfw.fw_rev_major,
+ qfw.fw_rev_minor,
+ qfw.fw_rev_subminor);
+ }
+ tprintf("fw_rev_major=%d", qfw.fw_rev_major);
+ tprintf("fw_rev_minor=%d", qfw.fw_rev_minor);
+ tprintf("fw_rev_subminor=%d", qfw.fw_rev_subminor);
+ tprintf("error_buf_start_h=0x%x", qfw.error_buf_start_h);
+ tprintf("error_buf_start_l=0x%x", qfw.error_buf_start_l);
+ tprintf("error_buf_size=%d", qfw.error_buf_size);
+ }
+
+ if (qfw.error_buf_start_h) {
+ eprintf("too high physical address");
+ ret = -1;
+ goto exit;
+ }
+
+ dev_ib_data.error_buf_addr= ioremap(qfw.error_buf_start_l,
+ qfw.error_buf_size*4);
+ dev_ib_data.error_buf_size= qfw.error_buf_size;
+ if (!dev_ib_data.error_buf_addr) {
+ eprintf("");
+ ret = -1;
+ goto exit;
+ }
+
+
+ rc = cmd_query_dev_lim(&dev_lim);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto exit;
+ } else {
+ tprintf("cmd_query_dev_lim() success");
+ tprintf("log2_rsvd_qps=%x", dev_lim.log2_rsvd_qps);
+ tprintf("qpc_entry_sz=%x", dev_lim.qpc_entry_sz);
+ tprintf("log2_rsvd_srqs=%x", dev_lim.log2_rsvd_srqs);
+ tprintf("srq_entry_sz=%x", dev_lim.srq_entry_sz);
+ tprintf("log2_rsvd_ees=%x", dev_lim.log2_rsvd_ees);
+ tprintf("eec_entry_sz=%x", dev_lim.eec_entry_sz);
+ tprintf("log2_rsvd_cqs=%x", dev_lim.log2_rsvd_cqs);
+ tprintf("cqc_entry_sz=%x", dev_lim.cqc_entry_sz);
+ tprintf("log2_rsvd_mtts=%x", dev_lim.log2_rsvd_mtts);
+ tprintf("mtt_entry_sz=%x", dev_lim.mtt_entry_sz);
+ tprintf("log2_rsvd_mrws=%x", dev_lim.log2_rsvd_mrws);
+ tprintf("mpt_entry_sz=%x", dev_lim.mpt_entry_sz);
+ tprintf("eqc_entry_sz=%x", dev_lim.eqc_entry_sz);
+ }
+
+ /* set the qp and cq numbers according
+ to the results of query_dev_lim */
+ dev_ib_data.mads_qp.qpn = (1 << dev_lim.log2_rsvd_qps) +
+ +QPN_BASE + MADS_QPN_SN;
+ dev_ib_data.ipoib_qp.qpn = (1 << dev_lim.log2_rsvd_qps) +
+ +QPN_BASE + IPOIB_QPN_SN;
+
+ dev_ib_data.mads_qp.snd_cq.cqn = (1 << dev_lim.log2_rsvd_cqs) +
+ MADS_SND_CQN_SN;
+ dev_ib_data.mads_qp.rcv_cq.cqn = (1 << dev_lim.log2_rsvd_cqs) +
+ MADS_RCV_CQN_SN;
+
+ dev_ib_data.ipoib_qp.snd_cq.cqn = (1 << dev_lim.log2_rsvd_cqs) +
+ IPOIB_SND_CQN_SN;
+ dev_ib_data.ipoib_qp.rcv_cq.cqn = (1 << dev_lim.log2_rsvd_cqs) +
+ IPOIB_RCV_CQN_SN;
+
+ /* disable SRQ */
+ cfg = (void *)dev_buffers_p->inprm_buf;
+ memset(cfg, 0, MT_STRUCT_SIZE(tavorprm_mod_stat_cfg_st));
+ INS_FLD(1, cfg, tavorprm_mod_stat_cfg_st, srq_m); //cfg->srq_m = 1;
+ rc = cmd_mod_stat_cfg(cfg);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto exit;
+ } else {
+ tprintf("cmd_mod_stat_cfg() success");
+ }
+
+ /* prepare the init_hca params to pass
+ to prep_init_hca_buf */
+ memset(&init_hca, 0, sizeof init_hca);
+ offset = 0;
+ base_h = tavor_pci_dev.dev.bar[5] & 0xfffffff0;
+ base_l = tavor_pci_dev.dev.bar[4] & 0xfffffff0;
+
+ tprintf("base_h=0x%lx, base_l=0x%lx", base_h, base_l);
+
+ init_hca.qpc_base_addr_h = base_h;
+ init_hca.qpc_base_addr_l = base_l + offset;
+ init_hca.log_num_of_qp = dev_lim.log2_rsvd_qps + 1;
+ offset += delta;
+
+ init_hca.eec_base_addr_h = base_h;
+ init_hca.eec_base_addr_l = base_l + offset;
+ init_hca.log_num_of_ee = dev_lim.log2_rsvd_ees;
+ offset += delta;
+
+ init_hca.srqc_base_addr_h = base_h;
+ init_hca.srqc_base_addr_l = base_l + offset;
+ init_hca.log_num_of_srq = dev_lim.log2_rsvd_srqs;
+ offset += delta;
+
+ init_hca.cqc_base_addr_h = base_h;
+ init_hca.cqc_base_addr_l = base_l + offset;
+ init_hca.log_num_of_cq = dev_lim.log2_rsvd_cqs + 1;
+ offset += delta;
+
+ init_hca.eqpc_base_addr_h = base_h;
+ init_hca.eqpc_base_addr_l = base_l + offset;
+ offset += delta;
+
+ init_hca.eeec_base_addr_h = base_h;
+ init_hca.eeec_base_addr_l = base_l + offset;
+ offset += delta;
+
+ init_hca.eqc_base_addr_h = base_h;
+ init_hca.eqc_base_addr_l = base_l + offset;
+ init_hca.log_num_of_eq = LOG2_EQS;
+ offset += delta;
+
+ init_hca.rdb_base_addr_h = base_h;
+ init_hca.rdb_base_addr_l = base_l + offset;
+ offset += delta;
+
+ init_hca.mc_base_addr_h = base_h;
+ init_hca.mc_base_addr_l = base_l + offset;
+ init_hca.log_mc_table_entry_sz = LOG2_MC_ENTRY;
+ init_hca.mc_table_hash_sz = 0;
+ init_hca.log_mc_table_sz = LOG2_MC_GROUPS;
+ offset += delta;
+
+ init_hca.mpt_base_addr_h = base_h;
+ init_hca.mpt_base_addr_l = base_l + offset;
+ init_hca.log_mpt_sz = dev_lim.log2_rsvd_mrws + 1;
+ offset += delta;
+
+ init_hca.mtt_base_addr_h = base_h;
+ init_hca.mtt_base_addr_l = base_l + offset;
+
+ /* this buffer is used for all the commands */
+ inprm = (void *)dev_buffers_p->inprm_buf;
+ /* excute init_hca command */
+ prep_init_hca_buf(&init_hca, inprm);
+
+ rc = cmd_init_hca(inprm, MT_STRUCT_SIZE(tavorprm_init_hca_st));
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_sys_en;
+ } else
+ tprintf("cmd_init_hca() success");
+
+ /* register a single memory region which covers
+ 4 GB of the address space which will be used
+ throughout the driver */
+ memset(inprm, 0, SW2HW_MPT_IBUF_SZ);
+ in_key = MKEY_PREFIX + (1 << dev_lim.log2_rsvd_mrws);
+ prep_sw2hw_mpt_buf(inprm, in_key);
+ rc = cmd_sw2hw_mpt(&key, in_key, inprm, SW2HW_MPT_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_init_hca;
+ } else {
+ tprintf("cmd_sw2hw_mpt() success, key=0x%lx", key);
+ }
+ dev_ib_data.mkey = key;
+
+ eqn = EQN;
+ /* allocate a single EQ which will receive
+ all the events */
+ eq_buf = dev_buffers_p->eq_buf;
+ init_eq_buf(eq_buf); /* put in HW ownership */
+ prep_sw2hw_eq_buf(inprm, eq_buf);
+ rc = cmd_sw2hw_eq(SW2HW_EQ_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_sw2hw_mpt;
+ } else
+ tprintf("cmd_sw2hw_eq() success");
+
+ event_mask = (1 << XDEV_EV_TYPE_CQ_COMP) |
+ (1 << XDEV_EV_TYPE_CQ_ERR) |
+ (1 << XDEV_EV_TYPE_LOCAL_WQ_CATAS_ERR) |
+ (1 << XDEV_EV_TYPE_PORT_ERR) |
+ (1 << XDEV_EV_TYPE_LOCAL_WQ_INVALID_REQ_ERR) |
+ (1 << XDEV_EV_TYPE_LOCAL_WQ_ACCESS_VIOL_ERR) |
+ (1 << TAVOR_IF_EV_TYPE_OVERRUN);
+ rc = cmd_map_eq(eqn, event_mask, 1);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_sw2hw_eq;
+ } else
+ tprintf("cmd_map_eq() success");
+
+ dev_ib_data.eq.eqn = eqn;
+ dev_ib_data.eq.eq_buf = eq_buf;
+ dev_ib_data.eq.cons_idx = 0;
+ dev_ib_data.eq.eq_size = 1 << LOG2_EQ_SZ;
+ *eq_p = &dev_ib_data.eq;
+
+ memset(inprm, 0, INIT_IB_IBUF_SZ);
+ prep_init_ib_buf(inprm);
+ rc = cmd_init_ib(port, inprm, INIT_IB_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_sw2hw_eq;
+ } else
+ tprintf("cmd_init_ib() success");
+
+ init_av_array();
+ tprintf("init_av_array() done");
+
+ goto exit;
+
+ undo_sw2hw_eq:
+ rc = cmd_hw2sw_eq(EQN);
+ if (rc) {
+ eprintf("");
+ } else
+ tprintf("cmd_hw2sw_eq() success");
+
+ undo_sw2hw_mpt:
+ rc = cmd_hw2sw_mpt(key);
+ if (rc)
+ eprintf("");
+ else
+ tprintf("cmd_hw2sw_mpt() success key=0x%lx", key);
+
+ undo_init_hca:
+ rc = cmd_close_hca(0);
+ if (rc) {
+ eprintf("");
+ goto undo_sys_en;
+ } else
+ tprintf("cmd_close_hca() success");
+
+ undo_sys_en:
+ rc = cmd_sys_dis();
+ if (rc) {
+ eprintf("");
+ goto undo_sys_en;
+ } else
+ tprintf("cmd_sys_dis() success");
+ goto exit;
+
+ exit:
+ return ret;
+}
+
+static void *get_inprm_buf(void)
+{
+ return dev_buffers_p->inprm_buf;
+}
+
+static void *get_outprm_buf(void)
+{
+ return dev_buffers_p->outprm_buf;
+}
+
+static void *get_send_wqe_buf(void *wqe, __u8 index)
+{
+ struct ud_send_wqe_st *snd_wqe = wqe;
+
+ return bus_to_virt(snd_wqe->mpointer[index].local_addr_l);
+}
+
+static void *get_rcv_wqe_buf(void *wqe, __u8 index)
+{
+ struct recv_wqe_st *rcv_wqe = wqe;
+
+ return bus_to_virt(be32_to_cpu(rcv_wqe->mpointer[index].local_addr_l));
+}
+
+static void modify_av_params(struct ud_av_st *av,
+ __u16 dlid,
+ __u8 g,
+ __u8 sl, __u8 rate, union ib_gid_u *gid, __u32 qpn)
+{
+ memset(&av->av, 0, sizeof av->av);
+ INS_FLD(dev_ib_data.port, &av->av, tavorprm_ud_address_vector_st,
+ port_number);
+ INS_FLD(dev_ib_data.pd, &av->av, tavorprm_ud_address_vector_st, pd);
+ INS_FLD(dlid, &av->av, tavorprm_ud_address_vector_st, rlid);
+ INS_FLD(g, &av->av, tavorprm_ud_address_vector_st, g);
+ INS_FLD(sl, &av->av, tavorprm_ud_address_vector_st, sl);
+ INS_FLD(3, &av->av, tavorprm_ud_address_vector_st, msg);
+
+ if (rate >= 3)
+ INS_FLD(0, &av->av, tavorprm_ud_address_vector_st, max_stat_rate); /* 4x */
+ else
+ INS_FLD(1, &av->av, tavorprm_ud_address_vector_st, max_stat_rate); /* 1x */
+
+ cpu_to_be_buf(&av->av, sizeof(av->av));
+ if (g) {
+ if (gid) {
+ INS_FLD(*((__u32 *) (&gid->raw[0])), &av->av,
+ tavorprm_ud_address_vector_st, rgid_127_96);
+ INS_FLD(*((__u32 *) (&gid->raw[4])), &av->av,
+ tavorprm_ud_address_vector_st, rgid_95_64);
+ INS_FLD(*((__u32 *) (&gid->raw[8])), &av->av,
+ tavorprm_ud_address_vector_st, rgid_63_32);
+ INS_FLD(*((__u32 *) (&gid->raw[12])), &av->av,
+ tavorprm_ud_address_vector_st, rgid_31_0);
+ } else {
+ INS_FLD(0, &av->av, tavorprm_ud_address_vector_st,
+ rgid_127_96);
+ INS_FLD(0, &av->av, tavorprm_ud_address_vector_st,
+ rgid_95_64);
+ INS_FLD(0, &av->av, tavorprm_ud_address_vector_st,
+ rgid_63_32);
+ INS_FLD(0, &av->av, tavorprm_ud_address_vector_st,
+ rgid_31_0);
+ }
+ } else {
+ INS_FLD(0, &av->av, tavorprm_ud_address_vector_st, rgid_127_96);
+ INS_FLD(0, &av->av, tavorprm_ud_address_vector_st, rgid_95_64);
+ INS_FLD(0, &av->av, tavorprm_ud_address_vector_st, rgid_63_32);
+ INS_FLD(2, &av->av, tavorprm_ud_address_vector_st, rgid_31_0);
+ }
+ av->dest_qp = qpn;
+}
+
+static void init_cq_buf(union cqe_st *cq_buf, __u8 num_cqes)
+{
+ memset(cq_buf, 0xff, num_cqes * sizeof cq_buf[0]);
+}
+
+static int post_rcv_buf(struct udqp_st *qp, struct recv_wqe_st *rcv_wqe)
+{
+ struct recv_doorbell_st dbell;
+ int rc;
+ __u32 tmp[2];
+ struct recv_wqe_st *tmp_wqe = (struct recv_wqe_st *)tmp;
+ __u32 *ptr_dst;
+
+ memset(&dbell, 0, sizeof dbell);
+ INS_FLD(sizeof(*rcv_wqe) >> 4, &dbell, tavorprm_receive_doorbell_st,
+ nds);
+ INS_FLD(virt_to_bus(rcv_wqe) >> 6, &dbell, tavorprm_receive_doorbell_st,
+ nda);
+ INS_FLD(qp->qpn, &dbell, tavorprm_receive_doorbell_st, qpn);
+ INS_FLD(1, &dbell, tavorprm_receive_doorbell_st, credits);
+
+ if (qp->last_posted_rcv_wqe) {
+ memcpy(tmp, qp->last_posted_rcv_wqe, sizeof(tmp));
+ be_to_cpu_buf(tmp, sizeof(tmp));
+ INS_FLD(1, tmp_wqe->next, wqe_segment_next_st, dbd);
+ INS_FLD(sizeof(*rcv_wqe) >> 4, tmp_wqe->next,
+ wqe_segment_next_st, nds);
+ INS_FLD(virt_to_bus(rcv_wqe) >> 6, tmp_wqe->next,
+ wqe_segment_next_st, nda_31_6);
+ /* this is not really opcode but since the struct
+ is used for both send and receive, in receive this bit must be 1
+ which coinsides with nopcode */
+ INS_FLD(1, tmp_wqe->next, wqe_segment_next_st, nopcode);
+
+ cpu_to_be_buf(tmp, sizeof(tmp));
+
+ ptr_dst = (__u32 *) (qp->last_posted_rcv_wqe);
+ ptr_dst[0] = tmp[0];
+ ptr_dst[1] = tmp[1];
+ }
+ rc = cmd_post_doorbell(&dbell, POST_RCV_OFFSET);
+ if (!rc) {
+ qp->last_posted_rcv_wqe = rcv_wqe;
+ }
+
+ return rc;
+}
+
+static int post_send_req(void *qph, void *wqeh, __u8 num_gather)
+{
+ struct send_doorbell_st dbell;
+ int rc;
+ struct udqp_st *qp = qph;
+ struct ud_send_wqe_st *snd_wqe = wqeh;
+ struct next_control_seg_st tmp;
+ __u32 *psrc, *pdst;
+ __u32 nds;
+
+ tprintf("snd_wqe=0x%lx, virt_to_bus(snd_wqe)=0x%lx", snd_wqe,
+ virt_to_bus(snd_wqe));
+
+ memset(&dbell, 0, sizeof dbell);
+ INS_FLD(XDEV_NOPCODE_SEND, &dbell, tavorprm_send_doorbell_st, nopcode);
+ INS_FLD(1, &dbell, tavorprm_send_doorbell_st, f);
+ INS_FLD(virt_to_bus(snd_wqe) >> 6, &dbell, tavorprm_send_doorbell_st,
+ nda);
+ nds =
+ (sizeof(snd_wqe->next) + sizeof(snd_wqe->udseg) +
+ sizeof(snd_wqe->mpointer[0]) * num_gather) >> 4;
+ INS_FLD(nds, &dbell, tavorprm_send_doorbell_st, nds);
+ INS_FLD(qp->qpn, &dbell, tavorprm_send_doorbell_st, qpn);
+
+ tprintf("0= %lx", ((__u32 *) ((void *)(&dbell)))[0]);
+ tprintf("1= %lx", ((__u32 *) ((void *)(&dbell)))[1]);
+
+ if (qp->last_posted_snd_wqe) {
+ memcpy(&tmp, &qp->last_posted_snd_wqe->next, sizeof tmp);
+ be_to_cpu_buf(&tmp, sizeof tmp);
+ INS_FLD(1, &tmp, wqe_segment_next_st, dbd);
+ INS_FLD(virt_to_bus(snd_wqe) >> 6, &tmp, wqe_segment_next_st,
+ nda_31_6);
+ INS_FLD(nds, &tmp, wqe_segment_next_st, nds);
+
+ psrc = (__u32 *) (&tmp);
+ pdst = (__u32 *) (&qp->last_posted_snd_wqe->next);
+ pdst[0] = htonl(psrc[0]);
+ pdst[1] = htonl(psrc[1]);
+ }
+
+ rc = cmd_post_doorbell(&dbell, POST_SND_OFFSET);
+ if (!rc) {
+ qp->last_posted_snd_wqe = snd_wqe;
+ }
+
+ return rc;
+}
+
+static int create_mads_qp(void **qp_pp, void **snd_cq_pp, void **rcv_cq_pp)
+{
+ __u8 i;
+ int rc;
+ struct udqp_st *qp;
+
+ qp = &dev_ib_data.mads_qp;
+
+ /* set the pointer to the receive WQEs buffer */
+ qp->rcv_wq = dev_buffers_p->mads_qp_rcv_queue;
+
+ qp->send_buf_sz = MAD_BUF_SZ;
+ qp->rcv_buf_sz = MAD_BUF_SZ;
+
+ qp->recv_wqe_alloc_idx = 0;
+ qp->max_recv_wqes = NUM_MADS_RCV_WQES;
+ qp->recv_wqe_cur_free = NUM_MADS_RCV_WQES;
+
+ /* iterrate through the list */
+ for (i = 0; i < NUM_MADS_RCV_WQES; ++i) {
+ /* clear the WQE */
+ memset(&qp->rcv_wq[i], 0, sizeof(qp->rcv_wq[i]));
+
+ qp->rcv_wq[i].wqe_cont.qp = qp;
+ qp->rcv_bufs[i] = ib_buffers.rcv_mad_buf[i];
+ }
+
+ /* set the pointer to the send WQEs buffer */
+ qp->snd_wq = dev_buffers_p->mads_qp_snd_queue;
+
+ qp->snd_wqe_alloc_idx = 0;
+ qp->max_snd_wqes = NUM_MADS_SND_WQES;
+ qp->snd_wqe_cur_free = NUM_MADS_SND_WQES;
+
+ /* iterrate through the list */
+ for (i = 0; i < NUM_MADS_SND_WQES; ++i) {
+ /* clear the WQE */
+ memset(&qp->snd_wq[i], 0, sizeof(qp->snd_wq[i]));
+
+ /* link the WQE to the free list */
+ qp->snd_wq[i].wqe_cont.qp = qp;
+ qp->snd_bufs[i] = ib_buffers.send_mad_buf[i];
+ }
+
+ /* qp number and cq numbers are already set up */
+ qp->snd_cq.cq_buf = dev_buffers_p->mads_snd_cq_buf;
+ qp->rcv_cq.cq_buf = dev_buffers_p->mads_rcv_cq_buf;
+ qp->snd_cq.num_cqes = NUM_MADS_SND_CQES;
+ qp->rcv_cq.num_cqes = NUM_MADS_RCV_CQES;
+ qp->qkey = GLOBAL_QKEY;
+ rc = create_udqp(qp);
+ if (!rc) {
+ *qp_pp = qp;
+ *snd_cq_pp = &qp->snd_cq;
+ *rcv_cq_pp = &qp->rcv_cq;
+ }
+
+ return rc;
+}
+
+static int create_ipoib_qp(void **qp_pp,
+ void **snd_cq_pp, void **rcv_cq_pp, __u32 qkey)
+{
+ __u8 i;
+ int rc;
+ struct udqp_st *qp;
+ qp = &dev_ib_data.ipoib_qp;
+
+ /* set the pointer to the receive WQEs buffer */
+ qp->rcv_wq = dev_buffers_p->ipoib_qp_rcv_queue;
+
+ qp->rcv_buf_sz = IPOIB_RCV_BUF_SZ;
+
+ qp->recv_wqe_alloc_idx = 0;
+ qp->max_recv_wqes = NUM_IPOIB_RCV_WQES;
+ qp->recv_wqe_cur_free = NUM_IPOIB_RCV_WQES;
+
+ /* iterrate through the list */
+ for (i = 0; i < NUM_IPOIB_RCV_WQES; ++i) {
+ /* clear the WQE */
+ memset(&qp->rcv_wq[i], 0, sizeof(qp->rcv_wq[i]));
+
+ /* update data */
+ qp->rcv_wq[i].wqe_cont.qp = qp;
+ qp->rcv_bufs[i] = ib_buffers.ipoib_rcv_buf[i];
+ tprintf("rcv_buf=%lx", qp->rcv_bufs[i]);
+ }
+
+ /* init send queue WQEs list */
+ /* set the list empty */
+ qp->snd_wqe_alloc_idx = 0;
+ qp->max_snd_wqes = NUM_IPOIB_SND_WQES;
+ qp->snd_wqe_cur_free = NUM_IPOIB_SND_WQES;
+
+ /* set the pointer to the send WQEs buffer */
+ qp->snd_wq = dev_buffers_p->ipoib_qp_snd_queue;
+
+ /* iterrate through the list */
+ for (i = 0; i < NUM_IPOIB_SND_WQES; ++i) {
+ /* clear the WQE */
+ memset(&qp->snd_wq[i], 0, sizeof(qp->snd_wq[i]));
+
+ /* update data */
+ qp->snd_wq[i].wqe_cont.qp = qp;
+ qp->snd_bufs[i] = ib_buffers.send_ipoib_buf[i];
+ qp->send_buf_sz = 4;
+ }
+
+ /* qp number and cq numbers are already set up */
+
+ qp->snd_cq.cq_buf = dev_buffers_p->ipoib_snd_cq_buf;
+ qp->rcv_cq.cq_buf = dev_buffers_p->ipoib_rcv_cq_buf;
+ qp->snd_cq.num_cqes = NUM_IPOIB_SND_CQES;
+ qp->rcv_cq.num_cqes = NUM_IPOIB_RCV_CQES;
+ qp->qkey = qkey;
+ rc = create_udqp(qp);
+ if (!rc) {
+ *qp_pp = qp;
+ *snd_cq_pp = &qp->snd_cq;
+ *rcv_cq_pp = &qp->rcv_cq;
+ }
+
+ return rc;
+}
+
+static int create_udqp(struct udqp_st *qp)
+{
+ int rc, ret = 0;
+ void *inprm;
+ struct recv_wqe_st *rcv_wqe;
+
+ inprm = dev_buffers_p->inprm_buf;
+
+ /* create send CQ */
+ init_cq_buf(qp->snd_cq.cq_buf, qp->snd_cq.num_cqes);
+ qp->snd_cq.cons_idx = 0;
+ memset(inprm, 0, SW2HW_CQ_IBUF_SZ);
+ prep_sw2hw_cq_buf(inprm, dev_ib_data.eq.eqn, qp->snd_cq.cqn,
+ qp->snd_cq.cq_buf);
+ rc = cmd_sw2hw_cq(qp->snd_cq.cqn, inprm, SW2HW_CQ_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto exit;
+ }
+
+ /* create receive CQ */
+ init_cq_buf(qp->rcv_cq.cq_buf, qp->rcv_cq.num_cqes);
+ qp->rcv_cq.cons_idx = 0;
+ memset(inprm, 0, SW2HW_CQ_IBUF_SZ);
+ prep_sw2hw_cq_buf(inprm, dev_ib_data.eq.eqn, qp->rcv_cq.cqn,
+ qp->rcv_cq.cq_buf);
+ rc = cmd_sw2hw_cq(qp->rcv_cq.cqn, inprm, SW2HW_CQ_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_snd_cq;
+ }
+
+ memset(inprm, 0, QPCTX_IBUF_SZ);
+ prep_rst2init_qpee_buf(inprm, qp->snd_cq.cqn, qp->rcv_cq.cqn, qp->qkey);
+ rc = cmd_rst2init_qpee(qp->qpn, inprm, QPCTX_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_rcv_cq;
+ }
+
+ qp->last_posted_rcv_wqe = NULL;
+ qp->last_posted_snd_wqe = NULL;
+
+ /* post all the buffers to the receive queue */
+ while (1) {
+ /* allocate wqe */
+ rcv_wqe = alloc_rcv_wqe(qp);
+ if (!rcv_wqe)
+ break;
+
+ /* post the buffer */
+ rc = post_rcv_buf(qp, rcv_wqe);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_rcv_cq;
+ }
+ }
+
+ memset(inprm, 0, QPCTX_IBUF_SZ);
+ prep_init2rtr_qpee_buf(inprm);
+ rc = cmd_init2rtr_qpee(qp->qpn, inprm, QPCTX_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_rcv_cq;
+ }
+
+ memset(inprm, 0, QPCTX_IBUF_SZ);
+ rc = cmd_rtr2rts_qpee(qp->qpn, inprm, QPCTX_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_rcv_cq;
+ }
+
+ goto exit;
+
+ undo_rcv_cq:
+ rc = cmd_hw2sw_cq(qp->rcv_cq.cqn);
+ if (rc)
+ eprintf("");
+
+ undo_snd_cq:
+ rc = cmd_hw2sw_cq(qp->snd_cq.cqn);
+ if (rc)
+ eprintf("");
+
+ exit:
+ return ret;
+}
+
+static int destroy_udqp(struct udqp_st *qp)
+{
+ int rc;
+
+ rc = cmd_2err_qpee(qp->qpn);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ tprintf("cmd_2err_qpee(0x%lx) success", qp->qpn);
+
+ rc = cmd_2rst_qpee(qp->qpn);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ tprintf("cmd_2rst_qpee(0x%lx) success", qp->qpn);
+
+ rc = cmd_hw2sw_cq(qp->rcv_cq.cqn);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ tprintf("cmd_hw2sw_cq(0x%lx) success", qp->snd_cq.cqn);
+
+ rc = cmd_hw2sw_cq(qp->snd_cq.cqn);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ tprintf("cmd_hw2sw_cq(0x%lx) success", qp->rcv_cq.cqn);
+
+ return rc;
+}
+
+static void prep_send_wqe_buf(void *qph,
+ void *avh,
+ void *wqeh,
+ const void *buf,
+ unsigned int offset, __u16 len, __u8 e)
+{
+ struct udqp_st *qp = qph;
+ struct ud_av_st *av = avh;
+ struct ud_send_wqe_st *wqe = wqeh;
+
+ INS_FLD(e, wqe->next.control, wqe_segment_ctrl_send_st, e);
+ INS_FLD(1, wqe->next.control, wqe_segment_ctrl_send_st, always1);
+
+ wqe->udseg.av_add_h = 0;
+ wqe->udseg.av_add_l = virt_to_bus(&av->av);
+ wqe->udseg.dest_qp = av->dest_qp;
+ wqe->udseg.lkey = dev_ib_data.mkey;
+ wqe->udseg.qkey = qp->qkey;
+
+ if (buf) {
+ memcpy(bus_to_virt(wqe->mpointer[0].local_addr_l) + offset, buf,
+ len);
+ len += offset;
+ }
+ wqe->mpointer[0].byte_count = len;
+ wqe->mpointer[0].lkey = dev_ib_data.mkey;
+
+ cpu_to_be_buf(wqe, sizeof *wqe);
+}
+
+static void *alloc_ud_av(void)
+{
+ u8 next_free;
+
+ if (dev_ib_data.udav.udav_next_free == FL_EOL) {
+ return NULL;
+ }
+
+ next_free = dev_ib_data.udav.udav_next_free;
+ dev_ib_data.udav.udav_next_free =
+ dev_buffers_p->av_array[next_free].ud_av.next_free;
+ tprintf("allocated udav %d", next_free);
+ return &dev_buffers_p->av_array[next_free].ud_av;
+}
+
+static void free_ud_av(void *avh)
+{
+ union ud_av_u *avu;
+ __u8 idx, old_idx;
+ struct ud_av_st *av = avh;
+
+ avu = (union ud_av_u *)av;
+
+ idx = avu - dev_buffers_p->av_array;
+ tprintf("freeing udav idx=%d", idx);
+ old_idx = dev_ib_data.udav.udav_next_free;
+ dev_ib_data.udav.udav_next_free = idx;
+ avu->ud_av.next_free = old_idx;
+}
+
+static int update_cq_cons_idx(struct cq_st *cq)
+{
+ struct cq_dbell_st dbell;
+ int rc;
+
+ memset(&dbell, 0, sizeof dbell);
+ INS_FLD(cq->cqn, &dbell, tavorprm_cq_cmd_doorbell_st, cqn);
+ INS_FLD(CQ_DBELL_CMD_INC_CONS_IDX, &dbell, tavorprm_cq_cmd_doorbell_st,
+ cq_cmd);
+ rc = cmd_post_doorbell(&dbell, CQ_DBELL_OFFSET);
+ return rc;
+}
+
+static int poll_cq(void *cqh, union cqe_st *cqe_p, u8 * num_cqes)
+{
+ union cqe_st cqe;
+ int rc;
+ u32 *ptr;
+ struct cq_st *cq = cqh;
+
+ if (cq->cqn < 0x80 || cq->cqn > 0x83) {
+ eprintf("");
+ return -1;
+ }
+ ptr = (u32 *) (&(cq->cq_buf[cq->cons_idx]));
+ barrier();
+ if ((ptr[7] & 0x80000000) == 0) {
+ cqe = cq->cq_buf[cq->cons_idx];
+ be_to_cpu_buf(&cqe, sizeof(cqe));
+ *cqe_p = cqe;
+ ptr[7] = 0x80000000;
+ barrier();
+ cq->cons_idx = (cq->cons_idx + 1) % cq->num_cqes;
+ rc = update_cq_cons_idx(cq);
+ if (rc) {
+ return rc;
+ }
+ *num_cqes = 1;
+ } else
+ *num_cqes = 0;
+
+ return 0;
+}
+
+static void dev2ib_cqe(struct ib_cqe_st *ib_cqe_p, union cqe_st *cqe_p)
+{
+ __u8 opcode;
+ __u32 wqe_addr_ba;
+
+ opcode =
+ EX_FLD(cqe_p->good_cqe, tavorprm_completion_queue_entry_st, opcode);
+ if (opcode >= CQE_ERROR_OPCODE)
+ ib_cqe_p->is_error = 1;
+ else
+ ib_cqe_p->is_error = 0;
+
+ ib_cqe_p->is_send =
+ EX_FLD(cqe_p->good_cqe, tavorprm_completion_queue_entry_st, s);
+ wqe_addr_ba =
+ EX_FLD(cqe_p->good_cqe, tavorprm_completion_queue_entry_st,
+ wqe_adr) << 6;
+ ib_cqe_p->wqe = bus_to_virt(wqe_addr_ba);
+
+// if (ib_cqe_p->is_send) {
+// be_to_cpu_buf(ib_cqe_p->wqe, sizeof(struct ud_send_wqe_st));
+// }
+// else {
+// be_to_cpu_buf(ib_cqe_p->wqe, sizeof(struct recv_wqe_st));
+// }
+ ib_cqe_p->count =
+ EX_FLD(cqe_p->good_cqe, tavorprm_completion_queue_entry_st,
+ byte_cnt);
+}
+
+static int ib_poll_cq(void *cqh, struct ib_cqe_st *ib_cqe_p, u8 * num_cqes)
+{
+ int rc;
+ union cqe_st cqe;
+ struct cq_st *cq = cqh;
+ __u8 opcode;
+
+ rc = poll_cq(cq, &cqe, num_cqes);
+ if (rc || ((*num_cqes) == 0)) {
+ return rc;
+ }
+
+ dev2ib_cqe(ib_cqe_p, &cqe);
+
+ opcode =
+ EX_FLD(cqe.good_cqe, tavorprm_completion_queue_entry_st, opcode);
+ if (opcode >= CQE_ERROR_OPCODE) {
+ struct ud_send_wqe_st *wqe_p, wqe;
+ __u32 *ptr;
+ unsigned int i;
+
+ wqe_p =
+ bus_to_virt(EX_FLD
+ (cqe.error_cqe,
+ tavorprm_completion_with_error_st,
+ wqe_addr) << 6);
+ eprintf("syndrome=0x%lx",
+ EX_FLD(cqe.error_cqe, tavorprm_completion_with_error_st,
+ syndrome));
+ eprintf("wqe_addr=0x%lx", wqe_p);
+ eprintf("wqe_size=0x%lx",
+ EX_FLD(cqe.error_cqe, tavorprm_completion_with_error_st,
+ wqe_size));
+ eprintf("myqpn=0x%lx",
+ EX_FLD(cqe.error_cqe, tavorprm_completion_with_error_st,
+ myqpn));
+ eprintf("db_cnt=0x%lx",
+ EX_FLD(cqe.error_cqe, tavorprm_completion_with_error_st,
+ db_cnt));
+ memcpy(&wqe, wqe_p, sizeof wqe);
+ be_to_cpu_buf(&wqe, sizeof wqe);
+
+ eprintf("dumping wqe...");
+ ptr = (__u32 *) (&wqe);
+ for (i = 0; i < sizeof wqe; i += 4) {
+ printf("%lx : ", ptr[i >> 2]);
+ }
+
+ }
+
+ return rc;
+}
+
+/* always work on ipoib qp */
+static int add_qp_to_mcast_group(union ib_gid_u mcast_gid, __u8 add)
+{
+ void *mg;
+ __u8 *tmp;
+ int rc;
+ __u16 mgid_hash;
+ void *mgmqp_p;
+
+ tmp = dev_buffers_p->inprm_buf;
+ memcpy(tmp, mcast_gid.raw, 16);
+ be_to_cpu_buf(tmp, 16);
+ rc = cmd_mgid_hash(tmp, &mgid_hash);
+ if (!rc) {
+ mg = (void *)dev_buffers_p->inprm_buf;
+ memset(mg, 0, MT_STRUCT_SIZE(tavorprm_mgm_entry_st));
+ INS_FLD(mcast_gid.as_u32.dw[0], mg, tavorprm_mgm_entry_st, mgid_128_96); // memcpy(&mg->mgid_128_96, &mcast_gid.raw[0], 4);
+ INS_FLD(mcast_gid.as_u32.dw[1], mg, tavorprm_mgm_entry_st, mgid_95_64); // memcpy(&mg->mgid_95_64, &mcast_gid.raw[4], 4);
+ INS_FLD(mcast_gid.as_u32.dw[2], mg, tavorprm_mgm_entry_st, mgid_63_32); //memcpy(&mg->mgid_63_32, &mcast_gid.raw[8], 4);
+ INS_FLD(mcast_gid.as_u32.dw[3], mg, tavorprm_mgm_entry_st, mgid_31_0); //memcpy(&mg->mgid_31_0, &mcast_gid.raw[12], 4);
+ be_to_cpu_buf(mg + MT_BYTE_OFFSET(tavorprm_mgm_entry_st, mgid_128_96), 16); //be_to_cpu_buf(&mg->mgid_128_96, 16);
+ mgmqp_p = mg + MT_BYTE_OFFSET(tavorprm_mgm_entry_st, mgmqp_0);
+ INS_FLD(dev_ib_data.ipoib_qp.qpn, mgmqp_p, tavorprm_mgmqp_st, qpn_i); //mg->mgmqp[0].qpn = dev_ib_data.ipoib_qp.qpn;
+ INS_FLD(add, mgmqp_p, tavorprm_mgmqp_st, qi); //mg->mgmqp[0].valid = add ? 1 : 0;
+ rc = cmd_write_mgm(mg, mgid_hash);
+ }
+ return rc;
+}
+
+static int clear_interrupt(void)
+{
+ __u32 ecr;
+ int ret = 0;
+
+ if (gw_read_cr(0x80704, &ecr)) {
+ eprintf("");
+ } else {
+ if (ecr) {
+ ret = 1;
+ }
+ }
+ gw_write_cr(0xf00d8, 0x80000000); /* clear int */
+ gw_write_cr(0x8070c, 0xffffffff);
+
+ return ret;
+}
+
+static struct ud_send_wqe_st *alloc_send_wqe(udqp_t qph)
+{
+ struct udqp_st *qp = qph;
+ __u8 new_entry;
+ struct ud_send_wqe_st *wqe;
+
+ if (qp->snd_wqe_cur_free == 0) {
+ return NULL;
+ }
+ new_entry = qp->snd_wqe_alloc_idx;
+
+ wqe = &qp->snd_wq[new_entry].wqe;
+ qp->snd_wqe_cur_free--;
+ qp->snd_wqe_alloc_idx = (qp->snd_wqe_alloc_idx + 1) % qp->max_snd_wqes;
+
+ memset(wqe, 0, sizeof *wqe);
+
+ wqe->mpointer[0].local_addr_l = virt_to_bus(qp->snd_bufs[new_entry]);
+
+ return wqe;
+}
+
+/*
+ * alloc_rcv_wqe
+ *
+ * Note: since we work directly on the work queue, wqes
+ * are left in big endian
+ */
+static struct recv_wqe_st *alloc_rcv_wqe(struct udqp_st *qp)
+{
+ __u8 new_entry;
+ struct recv_wqe_st *wqe;
+
+ if (qp->recv_wqe_cur_free == 0) {
+ return NULL;
+ }
+
+ new_entry = qp->recv_wqe_alloc_idx;
+ wqe = &qp->rcv_wq[new_entry].wqe;
+
+ qp->recv_wqe_cur_free--;
+ qp->recv_wqe_alloc_idx =
+ (qp->recv_wqe_alloc_idx + 1) % qp->max_recv_wqes;
+
+ memset(wqe, 0, sizeof *wqe);
+
+ /* GRH is always required */
+ wqe->mpointer[0].local_addr_h = 0;
+ wqe->mpointer[0].local_addr_l = virt_to_bus(qp->rcv_bufs[new_entry]);
+ wqe->mpointer[0].lkey = dev_ib_data.mkey;
+ wqe->mpointer[0].byte_count = GRH_SIZE;
+
+ wqe->mpointer[1].local_addr_h = 0;
+ wqe->mpointer[1].local_addr_l =
+ virt_to_bus(qp->rcv_bufs[new_entry] + GRH_SIZE);
+ wqe->mpointer[1].lkey = dev_ib_data.mkey;
+ wqe->mpointer[1].byte_count = qp->rcv_buf_sz;
+
+ tprintf("rcv_buf=%lx\n", qp->rcv_bufs[new_entry]);
+
+ /* we do it only on the data segment since the control
+ segment is always owned by HW */
+ cpu_to_be_buf(wqe, sizeof *wqe);
+
+// tprintf("alloc wqe= 0x%x", wqe);
+ return wqe;
+}
+
+static int free_send_wqe(struct ud_send_wqe_st *wqe)
+{
+ union ud_send_wqe_u *wqe_u;
+ struct udqp_st *qp;
+
+ wqe_u = (union ud_send_wqe_u *)wqe;
+ qp = wqe_u->wqe_cont.qp;
+
+ if (qp->snd_wqe_cur_free >= qp->max_snd_wqes) {
+ return -1;
+ }
+
+ qp->snd_wqe_cur_free++;
+
+ return 0;
+}
+
+static int free_rcv_wqe(struct recv_wqe_st *wqe)
+{
+ union recv_wqe_u *wqe_u;
+ struct udqp_st *qp;
+
+ wqe_u = (union recv_wqe_u *)wqe;
+ qp = wqe_u->wqe_cont.qp;
+
+ if (qp->recv_wqe_cur_free >= qp->max_recv_wqes) {
+ return -1;
+ }
+
+ qp->recv_wqe_cur_free++;
+
+ return 0;
+}
+
+static int free_wqe(void *wqe)
+{
+ int rc = 0;
+ struct recv_wqe_st *rcv_wqe;
+
+// tprintf("free wqe= 0x%x", wqe);
+ if ((wqe >= (void *)(dev_ib_data.ipoib_qp.rcv_wq)) &&
+ (wqe <
+ (void *)(&dev_ib_data.ipoib_qp.rcv_wq[NUM_IPOIB_RCV_WQES]))) {
+ /* ipoib receive wqe */
+ free_rcv_wqe(wqe);
+ rcv_wqe = alloc_rcv_wqe(&dev_ib_data.ipoib_qp);
+ if (rcv_wqe) {
+ rc = post_rcv_buf(&dev_ib_data.ipoib_qp, rcv_wqe);
+ if (rc) {
+ eprintf("");
+ }
+ }
+ } else if (wqe >= (void *)(dev_ib_data.ipoib_qp.snd_wq) &&
+ wqe <
+ (void *)(&dev_ib_data.ipoib_qp.snd_wq[NUM_IPOIB_SND_WQES])) {
+ /* ipoib send wqe */
+ free_send_wqe(wqe);
+ } else if (wqe >= (void *)(dev_ib_data.mads_qp.rcv_wq) &&
+ wqe <
+ (void *)(&dev_ib_data.mads_qp.rcv_wq[NUM_MADS_RCV_WQES])) {
+ /* mads receive wqe */
+ free_rcv_wqe(wqe);
+ rcv_wqe = alloc_rcv_wqe(&dev_ib_data.mads_qp);
+ if (rcv_wqe) {
+ rc = post_rcv_buf(&dev_ib_data.mads_qp, rcv_wqe);
+ if (rc) {
+ eprintf("");
+ }
+ }
+ } else if (wqe >= (void *)(dev_ib_data.mads_qp.snd_wq) &&
+ wqe <
+ (void *)(&dev_ib_data.mads_qp.snd_wq[NUM_MADS_SND_WQES])) {
+ /* mads send wqe */
+ free_send_wqe(wqe);
+ } else {
+ rc = -1;
+ eprintf("");
+ }
+
+ return rc;
+}
+
+static int update_eq_cons_idx(struct eq_st *eq)
+{
+ struct eq_dbell_st dbell;
+ int rc;
+
+ memset(&dbell, 0, sizeof dbell);
+ INS_FLD(dev_ib_data.eq.eqn, &dbell, tavorprm_eq_cmd_doorbell_st, eqn);
+ INS_FLD(EQ_DBELL_CMD_SET_CONS_IDX, &dbell, tavorprm_eq_cmd_doorbell_st,
+ eq_cmd);
+ INS_FLD(eq->cons_idx, &dbell, tavorprm_eq_cmd_doorbell_st, eq_param);
+ rc = cmd_post_doorbell(&dbell, EQ_DBELL_OFFSET);
+
+ return rc;
+}
+
+static void dev2ib_eqe(struct ib_eqe_st *ib_eqe_p, void *eqe_p)
+{
+ void *tmp;
+
+ ib_eqe_p->event_type =
+ EX_FLD(eqe_p, tavorprm_event_queue_entry_st, event_type);
+
+ tmp = eqe_p + MT_BYTE_OFFSET(tavorprm_event_queue_entry_st, event_data);
+ ib_eqe_p->cqn = EX_FLD(tmp, tavorprm_completion_event_st, cqn);
+}
+
+static int poll_eq(struct ib_eqe_st *ib_eqe_p, __u8 * num_eqes)
+{
+ struct eqe_t eqe;
+ __u8 owner;
+ int rc;
+ __u32 *ptr;
+ struct eq_st *eq = &dev_ib_data.eq;
+
+ ptr = (__u32 *) (&(eq->eq_buf[eq->cons_idx]));
+ tprintf("cons)idx=%d, addr(eqe)=%x, val=0x%x", eq->cons_idx, virt_to_bus(ptr), ptr[7]);
+ owner = (ptr[7] & 0x80000000) ? OWNER_HW : OWNER_SW;
+ if (owner == OWNER_SW) {
+ tprintf("got eqe");
+ eqe = eq->eq_buf[eq->cons_idx];
+ be_to_cpu_buf(&eqe, sizeof(eqe));
+ dev2ib_eqe(ib_eqe_p, &eqe);
+ ptr[7] |= 0x80000000;
+ eq->eq_buf[eq->cons_idx] = eqe;
+ eq->cons_idx = (eq->cons_idx + 1) % eq->eq_size;
+ rc = update_eq_cons_idx(eq);
+ if (rc) {
+ return -1;
+ }
+ *num_eqes = 1;
+ } else {
+ *num_eqes = 0;
+ }
+ return 0;
+}
+
+static int ib_device_close(void)
+{
+ iounmap(tavor_pci_dev.uar);
+ iounmap(tavor_pci_dev.cr_space);
+ iounmap(dev_ib_data.error_buf_addr);
+ return 0;
+}
+
+static __u32 dev_get_qpn(void *qph)
+{
+ struct udqp_st *qp = qph;
+
+ return qp->qpn;
+}
+
+static void dev_post_dbell(void *dbell, __u32 offset)
+{
+ __u32 *ptr;
+ unsigned long address;
+
+ ptr = dbell;
+ tprintf("ptr[0]= 0x%lx", ptr[0]);
+ tprintf("ptr[1]= 0x%lx", ptr[1]);
+ address = (unsigned long)(tavor_pci_dev.uar) + offset;
+ tprintf("va=0x%lx pa=0x%lx", address,
+ virt_to_bus((const void *)address));
+ writel(htonl(ptr[0]), tavor_pci_dev.uar + offset);
+ barrier();
+ address += 4;
+ tprintf("va=0x%lx pa=0x%lx", address,
+ virt_to_bus((const void *)address));
+ writel(htonl(ptr[1]), tavor_pci_dev.uar + offset + 4);
+}
+
+
diff --git a/gpxe/src/drivers/net/mlx_ipoib/ib_mt25218.c b/gpxe/src/drivers/net/mlx_ipoib/ib_mt25218.c
new file mode 100644
index 00000000..f16577f1
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/ib_mt25218.c
@@ -0,0 +1,1929 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#include "mt25218.h"
+#include "ib_driver.h"
+#include <gpxe/pci.h>
+
+#define MOD_INC(counter, max_count) (counter) = ((counter)+1) & ((max_count) - 1)
+
+#define breakpoint {volatile __u32 *p=(__u32 *)0x1234;printf("breakpoint\n");do {} while((*p) != 0x1234);}
+
+#define WRITE_BYTE_VOL(addr, off, val) \
+ do { \
+ (*((volatile __u8 *)(((volatile __u8 *)(addr)) + off))) = (val); \
+ } while(0)
+
+#define WRITE_WORD_VOL(addr, off, val) \
+ do { \
+ (*((volatile __u16 *)(((volatile __u8 *)(addr)) + off))) = (val); \
+ } while(0)
+
+#define WRITE_DWORD_VOL(addr, off, val) \
+ do { \
+ (*((volatile __u32 *)(((volatile __u8 *)(addr)) + off))) = (val); \
+ } while(0)
+
+struct device_buffers_st {
+ /* inprm and outprm do not have alignnemet constraint sice that
+ is acheived programatically */
+ u8 inprm_buf[INPRM_BUF_SZ];
+ u8 outprm_buf[OUTPRM_BUF_SZ];
+ union recv_wqe_u mads_qp_rcv_queue[NUM_MADS_RCV_WQES]
+ __attribute__ ((aligned(RECV_WQE_U_ALIGN)));
+ union recv_wqe_u ipoib_qp_rcv_queue[NUM_IPOIB_RCV_WQES]
+ __attribute__ ((aligned(RECV_WQE_U_ALIGN)));
+ union ud_send_wqe_u mads_qp_snd_queue[NUM_MADS_SND_WQES]
+ __attribute__ ((aligned(UD_SEND_WQE_U_ALIGN)));
+ union ud_send_wqe_u ipoib_qp_snd_queue[NUM_IPOIB_SND_WQES]
+ __attribute__ ((aligned(UD_SEND_WQE_U_ALIGN)));
+ struct eqe_t eq_buf[1 << LOG2_EQ_SZ]
+ __attribute__ ((aligned(sizeof(struct eqe_t))));
+ union cqe_st mads_snd_cq_buf[NUM_MADS_SND_CQES]
+ __attribute__ ((aligned(sizeof(union cqe_st))));
+ union cqe_st ipoib_snd_cq_buf[NUM_IPOIB_SND_CQES]
+ __attribute__ ((aligned(sizeof(union cqe_st))));
+ union cqe_st mads_rcv_cq_buf[NUM_MADS_RCV_CQES]
+ __attribute__ ((aligned(sizeof(union cqe_st))));
+ union cqe_st ipoib_rcv_cq_buf[NUM_IPOIB_RCV_CQES]
+ __attribute__ ((aligned(sizeof(union cqe_st))));
+ union ud_av_u av_array[NUM_AVS];
+} __attribute__ ((packed));
+
+#define STRUCT_ALIGN_SZ 4096
+#define SRC_BUF_SZ (sizeof(struct device_buffers_st) + STRUCT_ALIGN_SZ - 1)
+
+/* the following must be kept in this order
+ for the memory region to cover the buffers */
+static u8 src_buf[SRC_BUF_SZ];
+static struct ib_buffers_st ib_buffers;
+static __u32 memreg_size;
+/* end of order constraint */
+
+struct phys_mem_desc_st {
+ unsigned long base;
+ unsigned long offset;
+};
+
+static struct phys_mem_desc_st phys_mem;
+
+static struct dev_pci_struct memfree_pci_dev;
+static struct device_buffers_st *dev_buffers_p;
+static struct device_ib_data_st dev_ib_data;
+
+static int gw_write_cr(__u32 addr, __u32 data)
+{
+ writel(htonl(data), memfree_pci_dev.cr_space + addr);
+ return 0;
+}
+
+static int gw_read_cr(__u32 addr, __u32 * result)
+{
+ *result = ntohl(readl(memfree_pci_dev.cr_space + addr));
+ return 0;
+}
+
+static int reset_hca(void)
+{
+ return gw_write_cr(MEMFREE_RESET_OFFSET, 1);
+}
+
+static int ib_device_init(struct pci_device *dev)
+{
+ int i;
+ int rc;
+
+ tprintf("");
+
+ memset(&dev_ib_data, 0, sizeof dev_ib_data);
+
+ /* save bars */
+ tprintf("bus=%d devfn=0x%x", dev->bus, dev->devfn);
+ for (i = 0; i < 6; ++i) {
+ memfree_pci_dev.dev.bar[i] =
+ pci_bar_start(dev, PCI_BASE_ADDRESS_0 + (i << 2));
+ tprintf("bar[%d]= 0x%08lx", i, memfree_pci_dev.dev.bar[i]);
+ }
+
+ tprintf("");
+ /* save config space */
+ for (i = 0; i < 64; ++i) {
+ rc = pci_read_config_dword(dev, i << 2,
+ &memfree_pci_dev.dev.
+ dev_config_space[i]);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ tprintf("config[%d]= 0x%08lx", i << 2,
+ memfree_pci_dev.dev.dev_config_space[i]);
+ }
+
+ tprintf("");
+ memfree_pci_dev.dev.dev = dev;
+
+ /* map cr-space */
+ memfree_pci_dev.cr_space =
+ ioremap(memfree_pci_dev.dev.bar[0], 0x100000);
+ if (!memfree_pci_dev.cr_space) {
+ eprintf("");
+ return -1;
+ }
+
+ /* map uar */
+ memfree_pci_dev.uar =
+ ioremap(memfree_pci_dev.dev.bar[2] + UAR_IDX * 0x1000, 0x1000);
+ if (!memfree_pci_dev.uar) {
+ eprintf("");
+ return -1;
+ }
+ tprintf("uar_base (pa:va) = 0x%lx 0x%lx",
+ memfree_pci_dev.dev.bar[2] + UAR_IDX * 0x1000,
+ memfree_pci_dev.uar);
+
+ tprintf("");
+
+ return 0;
+}
+
+static inline unsigned long lalign(unsigned long buf, unsigned long align)
+{
+ return (unsigned long)((buf + align - 1) &
+ (~(((unsigned long)align) - 1)));
+}
+
+static int init_dev_data(void)
+{
+ unsigned long tmp;
+ unsigned long reserve_size = 32 * 1024 * 1024;
+
+ tmp = lalign(virt_to_bus(src_buf), STRUCT_ALIGN_SZ);
+
+ dev_buffers_p = bus_to_virt(tmp);
+ memreg_size = (__u32) (&memreg_size) - (__u32) dev_buffers_p;
+ tprintf("src_buf=0x%lx, dev_buffers_p=0x%lx, memreg_size=0x%x", src_buf,
+ dev_buffers_p, memreg_size);
+
+ tprintf("inprm: va=0x%lx, pa=0x%lx", dev_buffers_p->inprm_buf,
+ virt_to_bus(dev_buffers_p->inprm_buf));
+ tprintf("outprm: va=0x%lx, pa=0x%lx", dev_buffers_p->outprm_buf,
+ virt_to_bus(dev_buffers_p->outprm_buf));
+
+ phys_mem.base =
+ (virt_to_phys(_text) - reserve_size) & (~(reserve_size - 1));
+
+ phys_mem.offset = 0;
+
+ return 0;
+}
+
+static int restore_config(void)
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < 64; ++i) {
+ if (i != 22 && i != 23) {
+ rc = pci_write_config_dword(memfree_pci_dev.dev.dev,
+ i << 2,
+ memfree_pci_dev.dev.
+ dev_config_space[i]);
+ if (rc) {
+ return rc;
+ }
+ }
+ }
+ return 0;
+}
+
+static void prep_init_hca_buf(struct init_hca_st *init_hca_p, void *buf)
+{
+ unsigned long ptr;
+ __u8 shift;
+
+ memset(buf, 0, MT_STRUCT_SIZE(arbelprm_init_hca_st));
+
+ ptr = (unsigned long)buf +
+ MT_BYTE_OFFSET(arbelprm_init_hca_st,
+ qpc_eec_cqc_eqc_rdb_parameters);
+
+ shift = 32 - MT_BIT_SIZE(arbelprm_qpcbaseaddr_st, qpc_base_addr_l);
+ INS_FLD(init_hca_p->qpc_base_addr_h, ptr, arbelprm_qpcbaseaddr_st,
+ qpc_base_addr_h);
+ INS_FLD(init_hca_p->qpc_base_addr_l >> shift, ptr,
+ arbelprm_qpcbaseaddr_st, qpc_base_addr_l);
+ INS_FLD(init_hca_p->log_num_of_qp, ptr, arbelprm_qpcbaseaddr_st,
+ log_num_of_qp);
+
+ shift = 32 - MT_BIT_SIZE(arbelprm_qpcbaseaddr_st, eec_base_addr_l);
+ INS_FLD(init_hca_p->eec_base_addr_h, ptr, arbelprm_qpcbaseaddr_st,
+ eec_base_addr_h);
+ INS_FLD(init_hca_p->eec_base_addr_l >> shift, ptr,
+ arbelprm_qpcbaseaddr_st, eec_base_addr_l);
+ INS_FLD(init_hca_p->log_num_of_ee, ptr, arbelprm_qpcbaseaddr_st,
+ log_num_of_ee);
+
+ shift = 32 - MT_BIT_SIZE(arbelprm_qpcbaseaddr_st, srqc_base_addr_l);
+ INS_FLD(init_hca_p->srqc_base_addr_h, ptr, arbelprm_qpcbaseaddr_st,
+ srqc_base_addr_h);
+ INS_FLD(init_hca_p->srqc_base_addr_l >> shift, ptr,
+ arbelprm_qpcbaseaddr_st, srqc_base_addr_l);
+ INS_FLD(init_hca_p->log_num_of_srq, ptr, arbelprm_qpcbaseaddr_st,
+ log_num_of_srq);
+
+ shift = 32 - MT_BIT_SIZE(arbelprm_qpcbaseaddr_st, cqc_base_addr_l);
+ INS_FLD(init_hca_p->cqc_base_addr_h, ptr, arbelprm_qpcbaseaddr_st,
+ cqc_base_addr_h);
+ INS_FLD(init_hca_p->cqc_base_addr_l >> shift, ptr,
+ arbelprm_qpcbaseaddr_st, cqc_base_addr_l);
+ INS_FLD(init_hca_p->log_num_of_cq, ptr, arbelprm_qpcbaseaddr_st,
+ log_num_of_cq);
+
+ INS_FLD(init_hca_p->eqpc_base_addr_h, ptr, arbelprm_qpcbaseaddr_st,
+ eqpc_base_addr_h);
+ INS_FLD(init_hca_p->eqpc_base_addr_l, ptr, arbelprm_qpcbaseaddr_st,
+ eqpc_base_addr_l);
+
+ INS_FLD(init_hca_p->eeec_base_addr_h, ptr, arbelprm_qpcbaseaddr_st,
+ eeec_base_addr_h);
+ INS_FLD(init_hca_p->eeec_base_addr_l, ptr, arbelprm_qpcbaseaddr_st,
+ eeec_base_addr_l);
+
+ shift = 32 - MT_BIT_SIZE(arbelprm_qpcbaseaddr_st, eqc_base_addr_l);
+ INS_FLD(init_hca_p->eqc_base_addr_h, ptr, arbelprm_qpcbaseaddr_st,
+ eqc_base_addr_h);
+ INS_FLD(init_hca_p->eqc_base_addr_l >> shift, ptr,
+ arbelprm_qpcbaseaddr_st, eqc_base_addr_l);
+ INS_FLD(init_hca_p->log_num_of_eq, ptr, arbelprm_qpcbaseaddr_st,
+ log_num_eq);
+
+ INS_FLD(init_hca_p->rdb_base_addr_h, ptr, arbelprm_qpcbaseaddr_st,
+ rdb_base_addr_h);
+ INS_FLD(init_hca_p->rdb_base_addr_l, ptr, arbelprm_qpcbaseaddr_st,
+ rdb_base_addr_l);
+
+ ptr = (unsigned long)buf +
+ MT_BYTE_OFFSET(arbelprm_init_hca_st, multicast_parameters);
+
+ INS_FLD(init_hca_p->mc_base_addr_h, ptr, arbelprm_multicastparam_st,
+ mc_base_addr_h);
+ INS_FLD(init_hca_p->mc_base_addr_l, ptr, arbelprm_multicastparam_st,
+ mc_base_addr_l);
+ INS_FLD(init_hca_p->log_mc_table_entry_sz, ptr,
+ arbelprm_multicastparam_st, log_mc_table_entry_sz);
+ INS_FLD(init_hca_p->mc_table_hash_sz, ptr, arbelprm_multicastparam_st,
+ mc_table_hash_sz);
+ INS_FLD(init_hca_p->log_mc_table_sz, ptr, arbelprm_multicastparam_st,
+ log_mc_table_sz);
+
+ ptr = (unsigned long)buf +
+ MT_BYTE_OFFSET(arbelprm_init_hca_st, tpt_parameters);
+
+ INS_FLD(init_hca_p->mpt_base_addr_h, ptr, arbelprm_tptparams_st,
+ mpt_base_adr_h);
+ INS_FLD(init_hca_p->mpt_base_addr_l, ptr, arbelprm_tptparams_st,
+ mpt_base_adr_l);
+ INS_FLD(init_hca_p->log_mpt_sz, ptr, arbelprm_tptparams_st, log_mpt_sz);
+ INS_FLD(init_hca_p->mtt_base_addr_h, ptr, arbelprm_tptparams_st,
+ mtt_base_addr_h);
+ INS_FLD(init_hca_p->mtt_base_addr_l, ptr, arbelprm_tptparams_st,
+ mtt_base_addr_l);
+
+ ptr = (unsigned long)buf +
+ MT_BYTE_OFFSET(arbelprm_init_hca_st, uar_parameters);
+
+ INS_FLD(init_hca_p->log_max_uars, ptr, arbelprm_uar_params_st,
+ log_max_uars);
+
+}
+
+static void prep_sw2hw_mpt_buf(void *buf, __u32 mkey)
+{
+ INS_FLD(1, buf, arbelprm_mpt_st, lw);
+ INS_FLD(1, buf, arbelprm_mpt_st, lr);
+ INS_FLD(1, buf, arbelprm_mpt_st, pa);
+ INS_FLD(1, buf, arbelprm_mpt_st, r_w);
+ INS_FLD(mkey, buf, arbelprm_mpt_st, mem_key);
+ INS_FLD(GLOBAL_PD, buf, arbelprm_mpt_st, pd);
+ INS_FLD(virt_to_bus(dev_buffers_p), buf, arbelprm_mpt_st,
+ start_address_l);
+ INS_FLD(memreg_size, buf, arbelprm_mpt_st, reg_wnd_len_l);
+}
+
+static void prep_sw2hw_eq_buf(void *buf, struct eqe_t *eq_buf)
+{
+ memset(buf, 0, MT_STRUCT_SIZE(arbelprm_eqc_st));
+
+ INS_FLD(0xa, buf, arbelprm_eqc_st, st); /* fired */
+ INS_FLD(virt_to_bus(eq_buf), buf, arbelprm_eqc_st, start_address_l);
+ INS_FLD(LOG2_EQ_SZ, buf, arbelprm_eqc_st, log_eq_size);
+ INS_FLD(GLOBAL_PD, buf, arbelprm_eqc_st, pd);
+ INS_FLD(dev_ib_data.mkey, buf, arbelprm_eqc_st, lkey);
+}
+
+static void init_eq_buf(void *eq_buf)
+{
+ struct eqe_t *eq = eq_buf;
+ int i, num_eqes = 1 << LOG2_EQ_SZ;
+
+ memset(eq, 0, num_eqes * sizeof eq[0]);
+ for (i = 0; i < num_eqes; ++i)
+ WRITE_BYTE_VOL(&eq[i], EQE_OWNER_OFFSET, EQE_OWNER_VAL_HW);
+}
+
+static void prep_init_ib_buf(void *buf)
+{
+ memset(buf, 0, MT_STRUCT_SIZE(arbelprm_init_ib_st));
+
+ INS_FLD(MTU_2048, buf, arbelprm_init_ib_st, mtu_cap);
+ INS_FLD(3, buf, arbelprm_init_ib_st, port_width_cap);
+ INS_FLD(1, buf, arbelprm_init_ib_st, vl_cap);
+ INS_FLD(1, buf, arbelprm_init_ib_st, max_gid);
+ INS_FLD(64, buf, arbelprm_init_ib_st, max_pkey);
+}
+
+static void prep_sw2hw_cq_buf(void *buf, __u8 eqn,
+ __u32 cqn,
+ union cqe_st *cq_buf,
+ __u32 cq_ci_db_record, __u32 cq_state_db_record)
+{
+ memset(buf, 0, MT_STRUCT_SIZE(arbelprm_completion_queue_context_st));
+
+ INS_FLD(0xA, buf, arbelprm_completion_queue_context_st, st);
+ INS_FLD(virt_to_bus(cq_buf), buf, arbelprm_completion_queue_context_st,
+ start_address_l);
+ INS_FLD(LOG2_CQ_SZ, buf, arbelprm_completion_queue_context_st,
+ log_cq_size);
+ INS_FLD(dev_ib_data.uar_idx, buf, arbelprm_completion_queue_context_st,
+ usr_page);
+ INS_FLD(eqn, buf, arbelprm_completion_queue_context_st, c_eqn);
+ INS_FLD(GLOBAL_PD, buf, arbelprm_completion_queue_context_st, pd);
+ INS_FLD(dev_ib_data.mkey, buf, arbelprm_completion_queue_context_st,
+ l_key);
+ INS_FLD(cqn, buf, arbelprm_completion_queue_context_st, cqn);
+ INS_FLD(cq_ci_db_record, buf, arbelprm_completion_queue_context_st,
+ cq_ci_db_record);
+ INS_FLD(cq_state_db_record, buf, arbelprm_completion_queue_context_st,
+ cq_state_db_record);
+}
+
+static void prep_rst2init_qpee_buf(void *buf,
+ __u32 snd_cqn,
+ __u32 rcv_cqn,
+ __u32 qkey,
+ __u32 log_rq_size,
+ __u32 log_rq_stride,
+ __u32 log_sq_size,
+ __u32 log_sq_stride,
+ __u32 snd_wqe_base_adr_l,
+ __u32 snd_db_record_index,
+ __u32 rcv_wqe_base_adr_l,
+ __u32 rcv_db_record_index)
+{
+ void *tmp;
+ int shift;
+ struct qp_ee_state_tarnisition_st *prm = buf;
+
+ memset(buf, 0, sizeof *prm);
+
+ tprintf("snd_cqn=0x%lx", snd_cqn);
+ tprintf("rcv_cqn=0x%lx", rcv_cqn);
+ tprintf("qkey=0x%lx", qkey);
+ tprintf("log_rq_size=0x%lx", log_rq_size);
+ tprintf("log_rq_stride=0x%lx", log_rq_stride);
+ tprintf("log_sq_size=0x%lx", log_sq_size);
+ tprintf("log_sq_stride=0x%lx", log_sq_stride);
+ tprintf("snd_wqe_base_adr_l=0x%lx", snd_wqe_base_adr_l);
+ tprintf("snd_db_record_index=0x%lx", snd_db_record_index);
+ tprintf("rcv_wqe_base_adr_l=0x%lx", rcv_wqe_base_adr_l);
+ tprintf("rcv_db_record_index=0x%lx", rcv_db_record_index);
+
+ tmp = &prm->ctx;
+ INS_FLD(TS_UD, tmp, arbelprm_queue_pair_ee_context_entry_st, st);
+ INS_FLD(PM_STATE_MIGRATED, tmp, arbelprm_queue_pair_ee_context_entry_st,
+ pm_state);
+ INS_FLD(1, tmp, arbelprm_queue_pair_ee_context_entry_st, de);
+ INS_FLD(MTU_2048, tmp, arbelprm_queue_pair_ee_context_entry_st, mtu);
+ INS_FLD(11, tmp, arbelprm_queue_pair_ee_context_entry_st, msg_max);
+ INS_FLD(log_rq_size, tmp, arbelprm_queue_pair_ee_context_entry_st,
+ log_rq_size);
+ INS_FLD(log_rq_stride, tmp, arbelprm_queue_pair_ee_context_entry_st,
+ log_rq_stride);
+ INS_FLD(log_sq_size, tmp, arbelprm_queue_pair_ee_context_entry_st,
+ log_sq_size);
+ INS_FLD(log_sq_stride, tmp, arbelprm_queue_pair_ee_context_entry_st,
+ log_sq_stride);
+ INS_FLD(dev_ib_data.uar_idx, tmp,
+ arbelprm_queue_pair_ee_context_entry_st, usr_page);
+ INS_FLD(GLOBAL_PD, tmp, arbelprm_queue_pair_ee_context_entry_st, pd);
+ INS_FLD(dev_ib_data.mkey, tmp, arbelprm_queue_pair_ee_context_entry_st,
+ wqe_lkey);
+ INS_FLD(1, tmp, arbelprm_queue_pair_ee_context_entry_st, ssc);
+ INS_FLD(snd_cqn, tmp, arbelprm_queue_pair_ee_context_entry_st, cqn_snd);
+ shift =
+ 32 - MT_BIT_SIZE(arbelprm_queue_pair_ee_context_entry_st,
+ snd_wqe_base_adr_l);
+ INS_FLD(snd_wqe_base_adr_l >> shift, tmp,
+ arbelprm_queue_pair_ee_context_entry_st, snd_wqe_base_adr_l);
+ INS_FLD(snd_db_record_index, tmp,
+ arbelprm_queue_pair_ee_context_entry_st, snd_db_record_index);
+ INS_FLD(1, tmp, arbelprm_queue_pair_ee_context_entry_st, rsc);
+ INS_FLD(rcv_cqn, tmp, arbelprm_queue_pair_ee_context_entry_st, cqn_rcv);
+ shift =
+ 32 - MT_BIT_SIZE(arbelprm_queue_pair_ee_context_entry_st,
+ rcv_wqe_base_adr_l);
+ INS_FLD(rcv_wqe_base_adr_l >> shift, tmp,
+ arbelprm_queue_pair_ee_context_entry_st, rcv_wqe_base_adr_l);
+ INS_FLD(rcv_db_record_index, tmp,
+ arbelprm_queue_pair_ee_context_entry_st, rcv_db_record_index);
+ INS_FLD(qkey, tmp, arbelprm_queue_pair_ee_context_entry_st, q_key);
+
+ tmp =
+ (__u8 *) (&prm->ctx) +
+ MT_BYTE_OFFSET(arbelprm_queue_pair_ee_context_entry_st,
+ primary_address_path);
+ INS_FLD(dev_ib_data.port, tmp, arbelprm_address_path_st, port_number);
+
+}
+
+static void prep_init2rtr_qpee_buf(void *buf)
+{
+ struct qp_ee_state_tarnisition_st *prm;
+
+ prm = (struct qp_ee_state_tarnisition_st *)buf;
+
+ memset(prm, 0, sizeof *prm);
+
+ INS_FLD(MTU_2048, &prm->ctx, arbelprm_queue_pair_ee_context_entry_st,
+ mtu);
+ INS_FLD(11, &prm->ctx, arbelprm_queue_pair_ee_context_entry_st,
+ msg_max);
+}
+
+static void init_av_array(void)
+{
+}
+
+/*
+ * my_log2()
+ */
+static int my_log2(unsigned long arg)
+{
+ int i;
+ __u32 tmp;
+
+ if (arg == 0) {
+ return INT_MIN; /* log2(0) = -infinity */
+ }
+
+ tmp = 1;
+ i = 0;
+ while (tmp < arg) {
+ tmp = tmp << 1;
+ ++i;
+ }
+
+ return i;
+}
+
+/*
+ * get_req_icm_pages
+ */
+static unsigned long get_req_icm_pages(unsigned long log2_reserved,
+ unsigned long app_rsrc,
+ unsigned long entry_size,
+ unsigned long *log2_entries_p)
+{
+ unsigned long size;
+ unsigned long log2_entries;
+
+ log2_entries = my_log2((1 << log2_reserved) + app_rsrc);
+ *log2_entries_p = log2_entries;
+ size = (1 << log2_entries) * entry_size;
+
+ return (size + 4095) >> 12;
+}
+
+static void init_uar_context(void *uar_context_va)
+{
+ void *ptr;
+ /* clear all uar context */
+ memset(uar_context_va, 0, 4096);
+
+ ptr = uar_context_va + MADS_RCV_CQ_ARM_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_CQ_ARM, ptr, arbelprm_cq_arm_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.mads_qp.rcv_cq.cqn, ptr,
+ arbelprm_cq_arm_db_record_st, cq_number);
+
+ ptr = uar_context_va + MADS_SND_CQ_ARM_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_CQ_ARM, ptr, arbelprm_cq_arm_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.mads_qp.snd_cq.cqn, ptr,
+ arbelprm_cq_arm_db_record_st, cq_number);
+
+ ptr = uar_context_va + IPOIB_RCV_CQ_ARM_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_CQ_ARM, ptr, arbelprm_cq_arm_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.ipoib_qp.rcv_cq.cqn, ptr,
+ arbelprm_cq_arm_db_record_st, cq_number);
+
+ ptr = uar_context_va + IPOIB_SND_CQ_ARM_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_CQ_ARM, ptr, arbelprm_cq_arm_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.ipoib_qp.snd_cq.cqn, ptr,
+ arbelprm_cq_arm_db_record_st, cq_number);
+
+ ptr = uar_context_va + MADS_SND_QP_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_SQ_DBELL, ptr, arbelprm_qp_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.mads_qp.qpn, ptr, arbelprm_qp_db_record_st,
+ qp_number);
+
+ ptr = uar_context_va + IPOIB_SND_QP_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_SQ_DBELL, ptr, arbelprm_qp_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.ipoib_qp.qpn, ptr, arbelprm_qp_db_record_st,
+ qp_number);
+
+ ptr = uar_context_va + GROUP_SEP_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_GROUP_SEP, ptr, arbelprm_cq_arm_db_record_st,
+ res);
+
+ ptr = uar_context_va + MADS_RCV_QP_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_RQ_DBELL, ptr, arbelprm_qp_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.mads_qp.qpn, ptr, arbelprm_qp_db_record_st,
+ qp_number);
+
+ ptr = uar_context_va + IPOIB_RCV_QP_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_RQ_DBELL, ptr, arbelprm_qp_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.ipoib_qp.qpn, ptr, arbelprm_qp_db_record_st,
+ qp_number);
+
+ ptr = uar_context_va + MADS_RCV_CQ_CI_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_CQ_SET_CI, ptr, arbelprm_cq_ci_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.mads_qp.rcv_cq.cqn, ptr,
+ arbelprm_cq_ci_db_record_st, cq_number);
+
+ ptr = uar_context_va + MADS_SND_CQ_CI_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_CQ_SET_CI, ptr, arbelprm_cq_ci_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.mads_qp.snd_cq.cqn, ptr,
+ arbelprm_cq_ci_db_record_st, cq_number);
+
+ ptr = uar_context_va + IPOIB_RCV_CQ_CI_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_CQ_SET_CI, ptr, arbelprm_cq_ci_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.ipoib_qp.rcv_cq.cqn, ptr,
+ arbelprm_cq_ci_db_record_st, cq_number);
+
+ ptr = uar_context_va + IPOIB_SND_CQ_CI_DB_IDX * 8;
+ INS_FLD_TO_BE(UAR_RES_CQ_SET_CI, ptr, arbelprm_cq_ci_db_record_st, res);
+ INS_FLD_TO_BE(dev_ib_data.ipoib_qp.snd_cq.cqn, ptr,
+ arbelprm_cq_ci_db_record_st, cq_number);
+
+}
+
+static int setup_hca(__u8 port, void **eq_p)
+{
+ int ret;
+ int rc;
+ struct query_fw_st qfw;
+ struct map_icm_st map_obj;
+ struct dev_lim_st dev_lim;
+ struct init_hca_st init_hca;
+ __u8 log2_pages;
+ unsigned long icm_start, icm_size, tmp;
+ unsigned long log2_entries;
+ __u32 aux_pages;
+ __u32 mem_key, key, tmp_key;
+ __u8 eqn;
+ __u32 event_mask;
+ struct eqe_t *eq_buf;
+ void *inprm;
+ unsigned long bus_addr;
+ struct query_adapter_st qa;
+ __u8 log_max_uars = 1;
+ void *uar_context_va;
+ __u32 uar_context_pa;
+
+ tprintf("called");
+ init_dev_data();
+ inprm = get_inprm_buf();
+
+ rc = reset_hca();
+ if (rc) {
+ eprintf("");
+ return rc;
+ } else {
+ tprintf("reset_hca() success");
+ }
+
+ mdelay(1000); /* wait for 1 sec */
+
+ rc = restore_config();
+ if (rc) {
+ eprintf("");
+ return rc;
+ } else {
+ tprintf("restore_config() success");
+ }
+
+ dev_ib_data.pd = GLOBAL_PD;
+ dev_ib_data.port = port;
+ dev_ib_data.qkey = GLOBAL_QKEY;
+
+ rc = cmd_query_fw(&qfw);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ else {
+ tprintf("cmd_query_fw() success");
+
+ if (print_info) {
+ printf("FW ver = %d.%d.%d\n",
+ qfw.fw_rev_major,
+ qfw.fw_rev_minor,
+ qfw.fw_rev_subminor);
+ }
+
+ tprintf("fw_rev_major=%d", qfw.fw_rev_major);
+ tprintf("fw_rev_minor=%d", qfw.fw_rev_minor);
+ tprintf("fw_rev_subminor=%d", qfw.fw_rev_subminor);
+ tprintf("error_buf_start_h=0x%x", qfw.error_buf_start_h);
+ tprintf("error_buf_start_l=0x%x", qfw.error_buf_start_l);
+ tprintf("error_buf_size=%d", qfw.error_buf_size);
+ }
+
+
+
+ bus_addr =
+ ((unsigned long)((u64) qfw.error_buf_start_h << 32) | qfw.
+ error_buf_start_l);
+ dev_ib_data.error_buf_addr= ioremap(bus_addr,
+ qfw.error_buf_size*4);
+ dev_ib_data.error_buf_size= qfw.error_buf_size;
+ if (!dev_ib_data.error_buf_addr) {
+ eprintf("");
+ return -1;
+ }
+
+
+ bus_addr =
+ ((unsigned long)((u64) qfw.clear_int_addr.addr_h << 32) | qfw.
+ clear_int_addr.addr_l);
+ dev_ib_data.clr_int_addr = bus_to_virt(bus_addr);
+
+ rc = cmd_enable_lam();
+ if (rc == 0x22 /* LAM_NOT_PRE -- need to put a name here */ ) {
+ // ??????
+ } else if (rc == 0) {
+ // ??????
+ } else {
+ eprintf("");
+ return rc;
+ }
+
+ log2_pages = my_log2(qfw.fw_pages);
+
+ memset(&map_obj, 0, sizeof map_obj);
+ map_obj.num_vpm = 1;
+ map_obj.vpm_arr[0].log2_size = log2_pages;
+ map_obj.vpm_arr[0].pa_l = phys_mem.base + phys_mem.offset;
+ rc = cmd_map_fa(&map_obj);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ phys_mem.offset += 1 << (log2_pages + 12);
+
+ rc = cmd_run_fw();
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_map_fa;
+ }
+
+ rc = cmd_mod_stat_cfg();
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_map_fa;
+ }
+
+ rc = cmd_query_dev_lim(&dev_lim);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_map_fa;
+ }
+
+ dev_ib_data.uar_idx = dev_lim.num_rsvd_uars;
+
+ tprintf("max_icm_size_h=0x%lx", dev_lim.max_icm_size_h);
+ tprintf("max_icm_size_l=0x%lx", dev_lim.max_icm_size_l);
+
+ memset(&init_hca, 0, sizeof init_hca);
+ icm_start = 0;
+ icm_size = 0;
+
+ icm_start += ((dev_lim.num_rsvd_uars + 1) << 12);
+ icm_size += ((dev_lim.num_rsvd_uars + 1) << 12);
+
+ tmp = get_req_icm_pages(dev_lim.log2_rsvd_qps,
+ MAX_APP_QPS,
+ dev_lim.qpc_entry_sz, &log2_entries);
+ init_hca.qpc_base_addr_l = icm_start;
+ init_hca.log_num_of_qp = log2_entries;
+ icm_start += (tmp << 12);
+ icm_size += (tmp << 12);
+
+ init_hca.eqpc_base_addr_l = icm_start;
+ icm_start += (tmp << 12);
+ icm_size += (tmp << 12);
+
+ tmp = get_req_icm_pages(dev_lim.log2_rsvd_srqs,
+ 0, dev_lim.srq_entry_sz, &log2_entries);
+ init_hca.srqc_base_addr_l = icm_start;
+ init_hca.log_num_of_srq = log2_entries;
+ icm_start += (tmp << 12);
+ icm_size += (tmp << 12);
+
+ tmp = get_req_icm_pages(dev_lim.log2_rsvd_ees,
+ 0, dev_lim.eec_entry_sz, &log2_entries);
+ init_hca.eec_base_addr_l = icm_start;
+ init_hca.log_num_of_ee = log2_entries;
+ icm_start += (tmp << 12);
+ icm_size += (tmp << 12);
+
+ init_hca.eeec_base_addr_l = icm_start;
+ icm_start += (tmp << 12);
+ icm_size += (tmp << 12);
+
+ tmp = get_req_icm_pages(dev_lim.log2_rsvd_cqs,
+ MAX_APP_CQS,
+ dev_lim.cqc_entry_sz, &log2_entries);
+ init_hca.cqc_base_addr_l = icm_start;
+ init_hca.log_num_of_cq = log2_entries;
+ icm_start += (tmp << 12);
+ icm_size += (tmp << 12);
+
+ tmp = get_req_icm_pages(dev_lim.log2_rsvd_mtts,
+ 0, dev_lim.mtt_entry_sz, &log2_entries);
+ init_hca.mtt_base_addr_l = icm_start;
+ icm_start += (tmp << 12);
+ icm_size += (tmp << 12);
+
+ tmp = get_req_icm_pages(dev_lim.log2_rsvd_mrws,
+ 1, dev_lim.mpt_entry_sz, &log2_entries);
+ init_hca.mpt_base_addr_l = icm_start;
+ init_hca.log_mpt_sz = log2_entries;
+ icm_start += (tmp << 12);
+ icm_size += (tmp << 12);
+
+ tmp = get_req_icm_pages(dev_lim.log2_rsvd_rdbs, 1, 32, /* size of rdb entry */
+ &log2_entries);
+ init_hca.rdb_base_addr_l = icm_start;
+ icm_start += (tmp << 12);
+ icm_size += (tmp << 12);
+
+ init_hca.eqc_base_addr_l = icm_start;
+ init_hca.log_num_of_eq = LOG2_EQS;
+ tmp = dev_lim.eqc_entry_sz * (1 << LOG2_EQS);
+ icm_start += tmp;
+ icm_size += tmp;
+
+ init_hca.mc_base_addr_l = icm_start;
+ init_hca.log_mc_table_entry_sz =
+ my_log2(MT_STRUCT_SIZE(arbelprm_mgm_entry_st));
+ init_hca.mc_table_hash_sz = 8;
+ init_hca.log_mc_table_sz = 3;
+ icm_size +=
+ (MT_STRUCT_SIZE(arbelprm_mgm_entry_st) * init_hca.mc_table_hash_sz);
+ icm_start +=
+ (MT_STRUCT_SIZE(arbelprm_mgm_entry_st) * init_hca.mc_table_hash_sz);
+
+ rc = cmd_set_icm_size(icm_size, &aux_pages);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_map_fa;
+ }
+
+ memset(&map_obj, 0, sizeof map_obj);
+ map_obj.num_vpm = 1;
+ map_obj.vpm_arr[0].pa_l = phys_mem.base + phys_mem.offset;
+ map_obj.vpm_arr[0].log2_size = my_log2(aux_pages);
+ rc = cmd_map_icm_aux(&map_obj);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_map_fa;
+ }
+ phys_mem.offset += (1 << (map_obj.vpm_arr[0].log2_size + 12));
+
+ uar_context_pa = phys_mem.base + phys_mem.offset +
+ dev_ib_data.uar_idx * 4096;
+ uar_context_va = phys_to_virt(uar_context_pa);
+ tprintf("uar_context: va=0x%lx, pa=0x%lx", uar_context_va,
+ uar_context_pa);
+ dev_ib_data.uar_context_base = uar_context_va;
+
+ memset(&map_obj, 0, sizeof map_obj);
+ map_obj.num_vpm = 1;
+ map_obj.vpm_arr[0].pa_l = phys_mem.base + phys_mem.offset;
+ map_obj.vpm_arr[0].log2_size = my_log2((icm_size + 4095) >> 12);
+ rc = cmd_map_icm(&map_obj);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_map_fa;
+ }
+ phys_mem.offset += (1 << (map_obj.vpm_arr[0].log2_size + 12));
+
+ init_hca.log_max_uars = log_max_uars;
+ tprintf("inprm: va=0x%lx, pa=0x%lx", inprm, virt_to_bus(inprm));
+ prep_init_hca_buf(&init_hca, inprm);
+ rc = cmd_init_hca(inprm, MT_STRUCT_SIZE(arbelprm_init_hca_st));
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_map_fa;
+ }
+
+ rc = cmd_query_adapter(&qa);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ dev_ib_data.clr_int_data = 1 << qa.intapin;
+
+ tmp_key = 1 << dev_lim.log2_rsvd_mrws | MKEY_PREFIX;
+ mem_key = 1 << (dev_lim.log2_rsvd_mrws + 8) | (MKEY_PREFIX >> 24);
+ prep_sw2hw_mpt_buf(inprm, tmp_key);
+ rc = cmd_sw2hw_mpt(&key, 1 << dev_lim.log2_rsvd_mrws, inprm,
+ SW2HW_MPT_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_map_fa;
+ } else {
+ tprintf("cmd_sw2hw_mpt() success, key=0x%lx", mem_key);
+ }
+ dev_ib_data.mkey = mem_key;
+
+ eqn = EQN;
+ /* allocate a single EQ which will receive
+ all the events */
+ eq_buf = dev_buffers_p->eq_buf;
+ init_eq_buf(eq_buf); /* put in HW ownership */
+ prep_sw2hw_eq_buf(inprm, eq_buf);
+ rc = cmd_sw2hw_eq(SW2HW_EQ_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_sw2hw_mpt;
+ } else
+ tprintf("cmd_sw2hw_eq() success");
+
+ event_mask = (1 << XDEV_EV_TYPE_CQ_COMP) |
+ (1 << XDEV_EV_TYPE_CQ_ERR) |
+ (1 << XDEV_EV_TYPE_LOCAL_WQ_CATAS_ERR) |
+ (1 << XDEV_EV_TYPE_PORT_ERR) |
+ (1 << XDEV_EV_TYPE_LOCAL_WQ_INVALID_REQ_ERR) |
+ (1 << XDEV_EV_TYPE_LOCAL_WQ_ACCESS_VIOL_ERR) |
+ (1 << TAVOR_IF_EV_TYPE_OVERRUN);
+ rc = cmd_map_eq(eqn, event_mask, 1);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_sw2hw_eq;
+ } else
+ tprintf("cmd_map_eq() success");
+
+ dev_ib_data.eq.eqn = eqn;
+ dev_ib_data.eq.eq_buf = eq_buf;
+ dev_ib_data.eq.cons_counter = 0;
+ dev_ib_data.eq.eq_size = 1 << LOG2_EQ_SZ;
+ bus_addr =
+ ((unsigned long)((u64) qfw.eq_ci_table.addr_h << 32) | qfw.
+ eq_ci_table.addr_l)
+ + eqn * 8;
+ dev_ib_data.eq.ci_base_base_addr = bus_to_virt(bus_addr);
+ *eq_p = &dev_ib_data.eq;
+
+ prep_init_ib_buf(inprm);
+ rc = cmd_init_ib(port, inprm, INIT_IB_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_sw2hw_eq;
+ } else
+ tprintf("cmd_init_ib() success");
+
+ init_av_array();
+ tprintf("init_av_array() done");
+
+ /* set the qp and cq numbers according
+ to the results of query_dev_lim */
+ dev_ib_data.mads_qp.qpn = (1 << dev_lim.log2_rsvd_qps) +
+ +QPN_BASE + MADS_QPN_SN;
+ dev_ib_data.ipoib_qp.qpn = (1 << dev_lim.log2_rsvd_qps) +
+ +QPN_BASE + IPOIB_QPN_SN;
+
+ dev_ib_data.mads_qp.snd_cq.cqn = (1 << dev_lim.log2_rsvd_cqs) +
+ MADS_SND_CQN_SN;
+ dev_ib_data.mads_qp.rcv_cq.cqn = (1 << dev_lim.log2_rsvd_cqs) +
+ MADS_RCV_CQN_SN;
+
+ dev_ib_data.ipoib_qp.snd_cq.cqn = (1 << dev_lim.log2_rsvd_cqs) +
+ IPOIB_SND_CQN_SN;
+ dev_ib_data.ipoib_qp.rcv_cq.cqn = (1 << dev_lim.log2_rsvd_cqs) +
+ IPOIB_RCV_CQN_SN;
+
+ init_uar_context(uar_context_va);
+
+ ret = 0;
+ goto exit;
+
+ undo_sw2hw_eq:
+ rc = cmd_hw2sw_eq(eqn);
+ if (rc)
+ eprintf("");
+ else
+ tprintf("cmd_hw2sw_eq() success");
+
+ undo_sw2hw_mpt:
+ rc = cmd_hw2sw_mpt(tmp_key);
+ if (rc)
+ eprintf("");
+
+ undo_map_fa:
+ rc = cmd_unmap_fa();
+ if (rc)
+ eprintf("");
+
+ exit:
+ return ret;
+}
+
+static void *get_inprm_buf(void)
+{
+ return dev_buffers_p->inprm_buf;
+}
+
+static void *get_outprm_buf(void)
+{
+ return dev_buffers_p->outprm_buf;
+}
+
+static void *get_send_wqe_buf(void *wqe, __u8 index)
+{
+ struct ud_send_wqe_st *snd_wqe = wqe;
+
+ return bus_to_virt(be32_to_cpu(snd_wqe->mpointer[index].local_addr_l));
+}
+
+static void *get_rcv_wqe_buf(void *wqe, __u8 index)
+{
+ struct recv_wqe_st *rcv_wqe = wqe;
+
+ return bus_to_virt(be32_to_cpu(rcv_wqe->mpointer[index].local_addr_l));
+}
+
+static void modify_av_params(struct ud_av_st *av,
+ __u16 dlid,
+ __u8 g,
+ __u8 sl, __u8 rate, union ib_gid_u *gid, __u32 qpn)
+{
+ memset(&av->av, 0, sizeof av->av);
+
+ INS_FLD_TO_BE(dev_ib_data.port, &av->av, arbelprm_ud_address_vector_st,
+ port_number);
+ INS_FLD_TO_BE(dev_ib_data.pd, &av->av, arbelprm_ud_address_vector_st,
+ pd);
+ INS_FLD_TO_BE(dlid, &av->av, arbelprm_ud_address_vector_st, rlid);
+ INS_FLD_TO_BE(g, &av->av, arbelprm_ud_address_vector_st, g);
+ INS_FLD_TO_BE(sl, &av->av, arbelprm_ud_address_vector_st, sl);
+ INS_FLD_TO_BE(3, &av->av, arbelprm_ud_address_vector_st, msg);
+
+ if (rate >= 3)
+ INS_FLD_TO_BE(0, &av->av, arbelprm_ud_address_vector_st, max_stat_rate); /* 4x */
+ else
+ INS_FLD_TO_BE(1, &av->av, arbelprm_ud_address_vector_st, max_stat_rate); /* 1x */
+
+ if (g) {
+ if (gid) {
+ INS_FLD(*((__u32 *) (&gid->raw[0])), &av->av,
+ arbelprm_ud_address_vector_st, rgid_127_96);
+ INS_FLD(*((__u32 *) (&gid->raw[4])), &av->av,
+ arbelprm_ud_address_vector_st, rgid_95_64);
+ INS_FLD(*((__u32 *) (&gid->raw[8])), &av->av,
+ arbelprm_ud_address_vector_st, rgid_63_32);
+ INS_FLD(*((__u32 *) (&gid->raw[12])), &av->av,
+ arbelprm_ud_address_vector_st, rgid_31_0);
+ } else {
+ INS_FLD(0, &av->av, arbelprm_ud_address_vector_st,
+ rgid_127_96);
+ INS_FLD(0, &av->av, arbelprm_ud_address_vector_st,
+ rgid_95_64);
+ INS_FLD(0, &av->av, arbelprm_ud_address_vector_st,
+ rgid_63_32);
+ INS_FLD(0, &av->av, arbelprm_ud_address_vector_st,
+ rgid_31_0);
+ }
+ } else {
+ INS_FLD(0, &av->av, arbelprm_ud_address_vector_st, rgid_127_96);
+ INS_FLD(0, &av->av, arbelprm_ud_address_vector_st, rgid_95_64);
+ INS_FLD(0, &av->av, arbelprm_ud_address_vector_st, rgid_63_32);
+ INS_FLD(2, &av->av, arbelprm_ud_address_vector_st, rgid_31_0);
+ }
+ av->dest_qp = qpn;
+ av->qkey = dev_ib_data.qkey;
+}
+
+static void init_cq_buf(union cqe_st *cq_buf, __u8 num_cqes)
+{
+ int i;
+
+ memset(cq_buf, 0, sizeof(union cqe_st) * num_cqes);
+ for (i = 0; i < num_cqes; ++i) {
+ WRITE_BYTE_VOL(&cq_buf[i], CQE_OWNER_OFFSET, CQE_OWNER_VAL_HW);
+ }
+}
+
+static int post_rcv_buf(struct udqp_st *qp, struct recv_wqe_st *rcv_wqe)
+{
+ int i;
+
+ /* put a valid lkey */
+ for (i = 0; i < MAX_SCATTER; ++i) {
+ rcv_wqe->mpointer[i].lkey = cpu_to_be32(dev_ib_data.mkey);
+ }
+
+ qp->post_rcv_counter++;
+ WRITE_WORD_VOL(qp->rcv_uar_context, 2, htons(qp->post_rcv_counter));
+
+ return 0;
+}
+
+static int post_send_req(void *qph, void *wqeh, __u8 num_gather)
+{
+ int rc;
+ struct udqp_st *qp = qph;
+ struct ud_send_wqe_st *snd_wqe = wqeh;
+ struct send_doorbell_st dbell;
+ __u32 nds;
+
+ qp->post_send_counter++;
+
+ WRITE_WORD_VOL(qp->send_uar_context, 2, htons(qp->post_send_counter));
+
+ memset(&dbell, 0, sizeof dbell);
+ INS_FLD(XDEV_NOPCODE_SEND, &dbell, arbelprm_send_doorbell_st, nopcode);
+ INS_FLD(1, &dbell, arbelprm_send_doorbell_st, f);
+ INS_FLD(qp->post_send_counter - 1, &dbell, arbelprm_send_doorbell_st,
+ wqe_counter);
+ INS_FLD(1, &dbell, arbelprm_send_doorbell_st, wqe_cnt);
+ nds = (sizeof(snd_wqe->next) +
+ sizeof(snd_wqe->udseg) +
+ sizeof(snd_wqe->mpointer[0]) * num_gather) >> 4;
+ INS_FLD(nds, &dbell, arbelprm_send_doorbell_st, nds);
+ INS_FLD(qp->qpn, &dbell, arbelprm_send_doorbell_st, qpn);
+
+ if (qp->last_posted_snd_wqe) {
+ INS_FLD_TO_BE(nds,
+ &qp->last_posted_snd_wqe->next.next,
+ arbelprm_wqe_segment_next_st, nds);
+ INS_FLD_TO_BE(1,
+ &qp->last_posted_snd_wqe->next.next,
+ arbelprm_wqe_segment_next_st, f);
+ INS_FLD_TO_BE(XDEV_NOPCODE_SEND,
+ &qp->last_posted_snd_wqe->next.next,
+ arbelprm_wqe_segment_next_st, nopcode);
+ }
+
+ rc = cmd_post_doorbell(&dbell, POST_SND_OFFSET);
+ if (!rc) {
+ qp->last_posted_snd_wqe = snd_wqe;
+ }
+
+ return rc;
+
+}
+
+static int create_mads_qp(void **qp_pp, void **snd_cq_pp, void **rcv_cq_pp)
+{
+ __u8 i, next_i, j, k;
+ int rc;
+ struct udqp_st *qp;
+ __u32 bus_addr;
+ __u8 nds;
+ void *ptr;
+
+ qp = &dev_ib_data.mads_qp;
+
+ /* set the pointer to the receive WQEs buffer */
+ qp->rcv_wq = dev_buffers_p->mads_qp_rcv_queue;
+
+ qp->send_buf_sz = MAD_BUF_SZ;
+ qp->rcv_buf_sz = MAD_BUF_SZ;
+
+ qp->max_recv_wqes = NUM_MADS_RCV_WQES; /* max wqes in this work queue */
+ qp->recv_wqe_cur_free = NUM_MADS_RCV_WQES; /* current free wqes */
+ qp->recv_wqe_alloc_idx = 0; /* index from wqes can be allocated if there are free wqes */
+
+ qp->rcv_uar_context =
+ dev_ib_data.uar_context_base + 8 * MADS_RCV_QP_DB_IDX;
+ qp->send_uar_context =
+ dev_ib_data.uar_context_base + 8 * MADS_SND_QP_DB_IDX;
+
+ memset(&qp->rcv_wq[0], 0, NUM_MADS_RCV_WQES * sizeof(qp->rcv_wq[0]));
+ nds = sizeof(qp->rcv_wq[0].wqe) >> 4;
+ /* iterrate through the list */
+ for (j = 0, i = 0, next_i = 1;
+ j < NUM_MADS_RCV_WQES;
+ MOD_INC(i, NUM_MADS_RCV_WQES), MOD_INC(next_i, NUM_MADS_RCV_WQES),
+ ++j) {
+
+ qp->rcv_bufs[i] = ib_buffers.rcv_mad_buf[i];
+ /* link the WQE to the next one */
+ bus_addr = virt_to_bus(&qp->rcv_wq[next_i].wqe);
+ ptr = qp->rcv_wq[i].wqe.control +
+ MT_BYTE_OFFSET(arbelprm_wqe_segment_ctrl_recv_st,
+ wqe_segment_next);
+ INS_FLD(bus_addr >> 6, ptr, arbelprm_recv_wqe_segment_next_st,
+ nda_31_6);
+ INS_FLD(nds, ptr, arbelprm_recv_wqe_segment_next_st, nds);
+
+ /* set the allocated buffers */
+ qp->rcv_bufs[i] = ib_buffers.rcv_mad_buf[i];
+ bus_addr = virt_to_bus(qp->rcv_bufs[i]);
+ qp->rcv_wq[i].wqe.mpointer[0].local_addr_l = bus_addr;
+ qp->rcv_wq[i].wqe.mpointer[0].byte_count = GRH_SIZE;
+ bus_addr = virt_to_bus(qp->rcv_bufs[i] + GRH_SIZE);
+ qp->rcv_wq[i].wqe.mpointer[1].local_addr_l = bus_addr;
+ qp->rcv_wq[i].wqe.mpointer[1].byte_count = MAD_BUF_SZ;
+
+ for (k = 0; k < (((sizeof(qp->rcv_wq[i])) >> 4) - 1); ++k) {
+ qp->rcv_wq[i].wqe.mpointer[k].lkey = INVALID_WQE_LKEY;
+ }
+ }
+ cpu_to_be_buf(&qp->rcv_wq[0],
+ NUM_MADS_RCV_WQES * sizeof(qp->rcv_wq[0]));
+
+ for (i = 0; i < qp->max_recv_wqes; ++i) {
+ qp->rcv_wq[i].wqe_cont.qp = qp;
+ }
+
+ /* set the pointer to the send WQEs buffer */
+ qp->snd_wq = dev_buffers_p->mads_qp_snd_queue;
+
+ qp->snd_wqe_alloc_idx = 0;
+ qp->max_snd_wqes = NUM_MADS_SND_WQES;
+ qp->snd_wqe_cur_free = NUM_MADS_SND_WQES;
+
+ memset(&qp->snd_wq[0], 0, NUM_MADS_SND_WQES * sizeof(qp->snd_wq[i]));
+ /* iterrate through the list */
+ for (j = 0, i = 0, next_i = 1;
+ j < NUM_MADS_RCV_WQES;
+ MOD_INC(i, NUM_MADS_SND_WQES), MOD_INC(next_i, NUM_MADS_SND_WQES),
+ ++j) {
+
+ /* link the WQE to the next one */
+ bus_addr = virt_to_bus(&qp->snd_wq[next_i].wqe_cont.wqe);
+ INS_FLD(bus_addr >> 6, &qp->snd_wq[i].wqe_cont.wqe.next.next,
+ arbelprm_wqe_segment_next_st, nda_31_6);
+
+ /* set the allocated buffers */
+ qp->snd_bufs[i] = ib_buffers.send_mad_buf[i];
+ bus_addr = virt_to_bus(qp->snd_bufs[i]);
+ qp->snd_wq[i].wqe_cont.wqe.mpointer[0].local_addr_l = bus_addr;
+ qp->snd_wq[i].wqe_cont.wqe.mpointer[0].lkey = dev_ib_data.mkey;
+ qp->snd_wq[i].wqe_cont.wqe.mpointer[0].byte_count =
+ qp->send_buf_sz;
+
+ }
+
+ cpu_to_be_buf(&qp->snd_wq[0],
+ NUM_MADS_SND_WQES * sizeof(qp->snd_wq[i]));
+
+ for (i = 0; i < qp->max_snd_wqes; ++i) {
+ qp->snd_wq[i].wqe_cont.qp = qp;
+ }
+
+ /* qp number and cq numbers are already set up */
+ qp->snd_cq.cq_buf = dev_buffers_p->mads_snd_cq_buf;
+ qp->rcv_cq.cq_buf = dev_buffers_p->mads_rcv_cq_buf;
+ qp->snd_cq.num_cqes = NUM_MADS_SND_CQES;
+ qp->rcv_cq.num_cqes = NUM_MADS_RCV_CQES;
+ qp->snd_cq.arm_db_ctx_idx = MADS_SND_CQ_ARM_DB_IDX;
+ qp->snd_cq.ci_db_ctx_idx = MADS_SND_CQ_CI_DB_IDX;
+ qp->rcv_cq.arm_db_ctx_idx = MADS_RCV_CQ_ARM_DB_IDX;
+ qp->rcv_cq.ci_db_ctx_idx = MADS_RCV_CQ_CI_DB_IDX;
+ qp->rcv_db_record_index = MADS_RCV_QP_DB_IDX;
+ qp->snd_db_record_index = MADS_SND_QP_DB_IDX;
+ qp->qkey = GLOBAL_QKEY;
+ rc = create_udqp(qp);
+ if (!rc) {
+ *qp_pp = qp;
+ *snd_cq_pp = &qp->snd_cq;
+ *rcv_cq_pp = &qp->rcv_cq;
+ }
+
+ return rc;
+}
+
+static int create_ipoib_qp(void **qp_pp,
+ void **snd_cq_pp, void **rcv_cq_pp, __u32 qkey)
+{
+ __u8 i, next_i, j, k;
+ int rc;
+ struct udqp_st *qp;
+ __u32 bus_addr;
+ __u8 nds;
+ void *ptr;
+
+ qp = &dev_ib_data.ipoib_qp;
+
+ /* set the pointer to the receive WQEs buffer */
+ qp->rcv_wq = dev_buffers_p->ipoib_qp_rcv_queue;
+
+ qp->send_buf_sz = IPOIB_SND_BUF_SZ;
+ qp->rcv_buf_sz = IPOIB_RCV_BUF_SZ;
+
+ qp->max_recv_wqes = NUM_IPOIB_RCV_WQES;
+ qp->recv_wqe_cur_free = NUM_IPOIB_RCV_WQES;
+
+ qp->rcv_uar_context =
+ dev_ib_data.uar_context_base + 8 * IPOIB_RCV_QP_DB_IDX;
+ qp->send_uar_context =
+ dev_ib_data.uar_context_base + 8 * IPOIB_SND_QP_DB_IDX;
+
+ memset(&qp->rcv_wq[0], 0, NUM_IPOIB_RCV_WQES * sizeof(qp->rcv_wq[0]));
+ nds = sizeof(qp->rcv_wq[0].wqe) >> 4;
+ /* iterrate through the list */
+ for (j = 0, i = 0, next_i = 1;
+ j < NUM_IPOIB_RCV_WQES;
+ MOD_INC(i, NUM_IPOIB_RCV_WQES), MOD_INC(next_i,
+ NUM_IPOIB_RCV_WQES), ++j) {
+
+ /* link the WQE to the next one */
+ bus_addr = virt_to_bus(&qp->rcv_wq[next_i].wqe);
+ ptr = qp->rcv_wq[i].wqe.control +
+ MT_BYTE_OFFSET(arbelprm_wqe_segment_ctrl_recv_st,
+ wqe_segment_next);
+ INS_FLD(bus_addr >> 6, ptr, arbelprm_recv_wqe_segment_next_st,
+ nda_31_6);
+ INS_FLD(nds, ptr, arbelprm_recv_wqe_segment_next_st, nds);
+
+ /* set the allocated buffers */
+ qp->rcv_bufs[i] = ib_buffers.ipoib_rcv_buf[i];
+ bus_addr = virt_to_bus(qp->rcv_bufs[i]);
+ qp->rcv_wq[i].wqe.mpointer[0].local_addr_l = bus_addr;
+ qp->rcv_wq[i].wqe.mpointer[0].byte_count = GRH_SIZE;
+ bus_addr = virt_to_bus(qp->rcv_bufs[i] + GRH_SIZE);
+ qp->rcv_wq[i].wqe.mpointer[1].local_addr_l = bus_addr;
+ qp->rcv_wq[i].wqe.mpointer[1].byte_count = IPOIB_RCV_BUF_SZ;
+
+ for (k = 0; k < (((sizeof(qp->rcv_wq[i].wqe)) >> 4) - 1); ++k) {
+ qp->rcv_wq[i].wqe.mpointer[k].lkey = INVALID_WQE_LKEY;
+ }
+ }
+ cpu_to_be_buf(&qp->rcv_wq[0],
+ NUM_IPOIB_RCV_WQES * sizeof(qp->rcv_wq[0]));
+
+ for (i = 0; i < qp->max_recv_wqes; ++i) {
+ qp->rcv_wq[i].wqe_cont.qp = qp;
+ }
+
+ /* set the pointer to the send WQEs buffer */
+ qp->snd_wq = dev_buffers_p->ipoib_qp_snd_queue;
+
+ qp->snd_wqe_alloc_idx = 0;
+ qp->max_snd_wqes = NUM_IPOIB_SND_WQES;
+ qp->snd_wqe_cur_free = NUM_IPOIB_SND_WQES;
+
+ memset(&qp->snd_wq[0], 0, NUM_IPOIB_SND_WQES * sizeof(qp->snd_wq[i]));
+ /* iterrate through the list */
+ for (j = 0, i = 0, next_i = 1;
+ j < NUM_IPOIB_RCV_WQES;
+ MOD_INC(i, NUM_IPOIB_SND_WQES), MOD_INC(next_i,
+ NUM_IPOIB_SND_WQES), ++j) {
+
+ /* link the WQE to the next one */
+ bus_addr = virt_to_bus(&qp->snd_wq[next_i].wqe_cont.wqe);
+ INS_FLD(bus_addr >> 6, &qp->snd_wq[i].wqe_cont.wqe.next.next,
+ arbelprm_wqe_segment_next_st, nda_31_6);
+
+ /* set the allocated buffers */
+ qp->snd_bufs[i] = ib_buffers.send_ipoib_buf[i];
+ bus_addr = virt_to_bus(qp->snd_bufs[i]);
+ qp->snd_wq[i].wqe_cont.wqe.mpointer[0].local_addr_l = bus_addr;
+ qp->snd_wq[i].wqe_cont.wqe.mpointer[0].lkey = dev_ib_data.mkey;
+
+ }
+ cpu_to_be_buf(&qp->snd_wq[0],
+ NUM_IPOIB_SND_WQES * sizeof(qp->snd_wq[i]));
+
+ for (i = 0; i < qp->max_snd_wqes; ++i) {
+ qp->snd_wq[i].wqe_cont.qp = qp;
+ }
+
+ /* qp number and cq numbers are already set up */
+ qp->snd_cq.cq_buf = dev_buffers_p->ipoib_snd_cq_buf;
+ qp->rcv_cq.cq_buf = dev_buffers_p->ipoib_rcv_cq_buf;
+ qp->snd_cq.num_cqes = NUM_IPOIB_SND_CQES;
+ qp->rcv_cq.num_cqes = NUM_IPOIB_RCV_CQES;
+ qp->snd_cq.arm_db_ctx_idx = IPOIB_SND_CQ_ARM_DB_IDX;
+ qp->snd_cq.ci_db_ctx_idx = IPOIB_SND_CQ_CI_DB_IDX;
+ qp->rcv_cq.arm_db_ctx_idx = IPOIB_RCV_CQ_ARM_DB_IDX;
+ qp->rcv_cq.ci_db_ctx_idx = IPOIB_RCV_CQ_CI_DB_IDX;
+ qp->rcv_db_record_index = IPOIB_RCV_QP_DB_IDX;
+ qp->snd_db_record_index = IPOIB_SND_QP_DB_IDX;
+ qp->qkey = qkey;
+ rc = create_udqp(qp);
+ if (!rc) {
+ *qp_pp = qp;
+ *snd_cq_pp = &qp->snd_cq;
+ *rcv_cq_pp = &qp->rcv_cq;
+ }
+
+ return rc;
+}
+
+static int create_udqp(struct udqp_st *qp)
+{
+ int rc, ret = 0;
+ void *inprm;
+ struct recv_wqe_st *rcv_wqe;
+
+ inprm = dev_buffers_p->inprm_buf;
+
+ qp->rcv_cq.arm_db_ctx_pointer =
+ dev_ib_data.uar_context_base + 8 * qp->rcv_cq.arm_db_ctx_idx;
+ qp->rcv_cq.ci_db_ctx_pointer =
+ dev_ib_data.uar_context_base + 8 * qp->rcv_cq.ci_db_ctx_idx;
+ qp->snd_cq.arm_db_ctx_pointer =
+ dev_ib_data.uar_context_base + 8 * qp->snd_cq.arm_db_ctx_idx;
+ qp->snd_cq.ci_db_ctx_pointer =
+ dev_ib_data.uar_context_base + 8 * qp->snd_cq.ci_db_ctx_idx;
+
+ /* create send CQ */
+ init_cq_buf(qp->snd_cq.cq_buf, qp->snd_cq.num_cqes);
+ qp->snd_cq.cons_counter = 0;
+ prep_sw2hw_cq_buf(inprm,
+ dev_ib_data.eq.eqn,
+ qp->snd_cq.cqn,
+ qp->snd_cq.cq_buf,
+ qp->snd_cq.ci_db_ctx_idx, qp->snd_cq.arm_db_ctx_idx);
+
+ rc = cmd_sw2hw_cq(qp->snd_cq.cqn, inprm, SW2HW_CQ_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto exit;
+ }
+
+ /* create receive CQ */
+ init_cq_buf(qp->rcv_cq.cq_buf, qp->rcv_cq.num_cqes);
+ qp->rcv_cq.cons_counter = 0;
+ memset(inprm, 0, SW2HW_CQ_IBUF_SZ);
+ prep_sw2hw_cq_buf(inprm,
+ dev_ib_data.eq.eqn,
+ qp->rcv_cq.cqn,
+ qp->rcv_cq.cq_buf,
+ qp->rcv_cq.ci_db_ctx_idx, qp->rcv_cq.arm_db_ctx_idx);
+
+ rc = cmd_sw2hw_cq(qp->rcv_cq.cqn, inprm, SW2HW_CQ_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_snd_cq;
+ }
+
+ prep_rst2init_qpee_buf(inprm,
+ qp->snd_cq.cqn,
+ qp->rcv_cq.cqn,
+ qp->qkey,
+ my_log2(qp->max_recv_wqes),
+ my_log2(sizeof(qp->rcv_wq[0])) - 4,
+ my_log2(qp->max_snd_wqes),
+ my_log2(sizeof(qp->snd_wq[0])) - 4,
+ virt_to_bus(qp->snd_wq),
+ qp->snd_db_record_index,
+ virt_to_bus(qp->rcv_wq),
+ qp->rcv_db_record_index);
+
+ rc = cmd_rst2init_qpee(qp->qpn, inprm, QPCTX_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_rcv_cq;
+ }
+
+ qp->last_posted_rcv_wqe = NULL;
+ qp->last_posted_snd_wqe = NULL;
+
+ /* post all the buffers to the receive queue */
+ while (1) {
+ /* allocate wqe */
+ rcv_wqe = alloc_rcv_wqe(qp);
+ if (!rcv_wqe)
+ break;
+
+ /* post the buffer */
+ rc = post_rcv_buf(qp, rcv_wqe);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_rcv_cq;
+ }
+ }
+
+ prep_init2rtr_qpee_buf(inprm);
+ rc = cmd_init2rtr_qpee(qp->qpn, inprm, QPCTX_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_rcv_cq;
+ }
+
+ memset(inprm, 0, QPCTX_IBUF_SZ);
+ rc = cmd_rtr2rts_qpee(qp->qpn, inprm, QPCTX_IBUF_SZ);
+ if (rc) {
+ ret = -1;
+ eprintf("");
+ goto undo_rcv_cq;
+ }
+
+ goto exit;
+
+ undo_rcv_cq:
+ rc = cmd_hw2sw_cq(qp->rcv_cq.cqn);
+ if (rc)
+ eprintf("");
+
+ undo_snd_cq:
+ rc = cmd_hw2sw_cq(qp->snd_cq.cqn);
+ if (rc)
+ eprintf("");
+
+ exit:
+ return ret;
+}
+
+static int destroy_udqp(struct udqp_st *qp)
+{
+ int rc;
+
+ rc = cmd_2err_qpee(qp->qpn);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ tprintf("cmd_2err_qpee(0x%lx) success", qp->qpn);
+
+ rc = cmd_2rst_qpee(qp->qpn);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ tprintf("cmd_2rst_qpee(0x%lx) success", qp->qpn);
+
+ rc = cmd_hw2sw_cq(qp->rcv_cq.cqn);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ tprintf("cmd_hw2sw_cq(0x%lx) success", qp->snd_cq.cqn);
+
+ rc = cmd_hw2sw_cq(qp->snd_cq.cqn);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ tprintf("cmd_hw2sw_cq(0x%lx) success", qp->rcv_cq.cqn);
+
+ return rc;
+}
+
+static void prep_send_wqe_buf(void *qph,
+ void *avh,
+ void *wqeh,
+ const void *buf,
+ unsigned int offset, __u16 len, __u8 e)
+{
+ struct ud_send_wqe_st *snd_wqe = wqeh;
+ struct ud_av_st *av = avh;
+
+ if (qph) {
+ }
+ /* suppress warnings */
+ INS_FLD_TO_BE(e, &snd_wqe->next.control,
+ arbelprm_wqe_segment_ctrl_send_st, e);
+ INS_FLD_TO_BE(1, &snd_wqe->next.control,
+ arbelprm_wqe_segment_ctrl_send_st, always1);
+ INS_FLD_TO_BE(1, &snd_wqe->next.next, arbelprm_wqe_segment_next_st,
+ always1);
+ memcpy(&snd_wqe->udseg, &av->av, sizeof av->av);
+ INS_FLD_TO_BE(av->dest_qp, snd_wqe->udseg.av,
+ arbelprm_wqe_segment_ud_st, destination_qp);
+ INS_FLD_TO_BE(av->qkey, snd_wqe->udseg.av, arbelprm_wqe_segment_ud_st,
+ q_key);
+
+ if (buf) {
+ memcpy(bus_to_virt
+ (be32_to_cpu(snd_wqe->mpointer[0].local_addr_l)) +
+ offset, buf, len);
+ len += offset;
+ }
+ snd_wqe->mpointer[0].byte_count = cpu_to_be32(len);
+}
+
+static void *alloc_ud_av(void)
+{
+ u8 next_free;
+
+ if (dev_ib_data.udav.udav_next_free == FL_EOL) {
+ return NULL;
+ }
+
+ next_free = dev_ib_data.udav.udav_next_free;
+ dev_ib_data.udav.udav_next_free =
+ dev_buffers_p->av_array[next_free].ud_av.next_free;
+ tprintf("allocated udav %d", next_free);
+ return &dev_buffers_p->av_array[next_free].ud_av;
+}
+
+static void free_ud_av(void *avh)
+{
+ union ud_av_u *avu;
+ __u8 idx, old_idx;
+ struct ud_av_st *av = avh;
+
+ avu = (union ud_av_u *)av;
+
+ idx = avu - dev_buffers_p->av_array;
+ tprintf("freeing udav idx=%d", idx);
+ old_idx = dev_ib_data.udav.udav_next_free;
+ dev_ib_data.udav.udav_next_free = idx;
+ avu->ud_av.next_free = old_idx;
+}
+
+static int update_cq_cons_idx(struct cq_st *cq)
+{
+ /* write doorbell record */
+ WRITE_DWORD_VOL(cq->ci_db_ctx_pointer, 0, htonl(cq->cons_counter));
+
+ /*
+ INS_FLD_TO_BE(cq->cons_counter,
+ cq->ci_db_ctx_pointer,
+ arbelprm_cq_arm_db_record_st,
+ counter);
+
+ INS_FLD_TO_BE(cq->cqn,
+ cq->ci_db_ctx_pointer,
+ arbelprm_cq_arm_db_record_st,
+ cq_number);
+
+ INS_FLD_TO_BE(1,
+ cq->ci_db_ctx_pointer,
+ arbelprm_cq_arm_db_record_st,
+ res); */
+
+ return 0;
+}
+
+static int poll_cq(void *cqh, union cqe_st *cqe_p, u8 * num_cqes)
+{
+ union cqe_st cqe;
+ int rc;
+ u32 *ptr;
+ struct cq_st *cq = cqh;
+ __u32 cons_idx = cq->cons_counter & (cq->num_cqes - 1);
+
+ ptr = (u32 *) (&(cq->cq_buf[cons_idx]));
+ barrier();
+ if ((ptr[7] & 0x80000000) == 0) {
+ cqe = cq->cq_buf[cons_idx];
+ be_to_cpu_buf(&cqe, sizeof(cqe));
+ *cqe_p = cqe;
+ ptr[7] = 0x80000000;
+ barrier();
+ cq->cons_counter++;
+ rc = update_cq_cons_idx(cq);
+ if (rc) {
+ return rc;
+ }
+ *num_cqes = 1;
+ } else
+ *num_cqes = 0;
+
+ return 0;
+}
+
+static void dev2ib_cqe(struct ib_cqe_st *ib_cqe_p, union cqe_st *cqe_p)
+{
+ __u8 opcode;
+ __u32 wqe_addr_ba;
+
+ opcode =
+ EX_FLD(cqe_p->good_cqe, arbelprm_completion_queue_entry_st, opcode);
+ if (opcode >= CQE_ERROR_OPCODE)
+ ib_cqe_p->is_error = 1;
+ else
+ ib_cqe_p->is_error = 0;
+
+ ib_cqe_p->is_send =
+ EX_FLD(cqe_p->good_cqe, arbelprm_completion_queue_entry_st, s);
+ wqe_addr_ba =
+ EX_FLD(cqe_p->good_cqe, arbelprm_completion_queue_entry_st,
+ wqe_adr) << 6;
+ ib_cqe_p->wqe = bus_to_virt(wqe_addr_ba);
+
+ ib_cqe_p->count =
+ EX_FLD(cqe_p->good_cqe, arbelprm_completion_queue_entry_st,
+ byte_cnt);
+}
+
+static int ib_poll_cq(void *cqh, struct ib_cqe_st *ib_cqe_p, u8 * num_cqes)
+{
+ int rc;
+ union cqe_st cqe;
+ struct cq_st *cq = cqh;
+ __u8 opcode;
+
+ rc = poll_cq(cq, &cqe, num_cqes);
+ if (rc || ((*num_cqes) == 0)) {
+ return rc;
+ }
+
+ dev2ib_cqe(ib_cqe_p, &cqe);
+
+ opcode =
+ EX_FLD(cqe.good_cqe, arbelprm_completion_queue_entry_st, opcode);
+ if (opcode >= CQE_ERROR_OPCODE) {
+ struct ud_send_wqe_st *wqe_p, wqe;
+ __u32 *ptr;
+ unsigned int i;
+
+ wqe_p =
+ bus_to_virt(EX_FLD
+ (cqe.error_cqe,
+ arbelprm_completion_with_error_st,
+ wqe_addr) << 6);
+ eprintf("syndrome=0x%lx",
+ EX_FLD(cqe.error_cqe, arbelprm_completion_with_error_st,
+ syndrome));
+ eprintf("vendor_syndrome=0x%lx",
+ EX_FLD(cqe.error_cqe, arbelprm_completion_with_error_st,
+ vendor_code));
+ eprintf("wqe_addr=0x%lx", wqe_p);
+ eprintf("myqpn=0x%lx",
+ EX_FLD(cqe.error_cqe, arbelprm_completion_with_error_st,
+ myqpn));
+ memcpy(&wqe, wqe_p, sizeof wqe);
+ be_to_cpu_buf(&wqe, sizeof wqe);
+
+ eprintf("dumping wqe...");
+ ptr = (__u32 *) (&wqe);
+ for (i = 0; i < sizeof wqe; i += 4) {
+ printf("%lx : ", ptr[i >> 2]);
+ }
+
+ }
+
+ return rc;
+}
+
+/* always work on ipoib qp */
+static int add_qp_to_mcast_group(union ib_gid_u mcast_gid, __u8 add)
+{
+ void *mg;
+ __u8 *tmp;
+ int rc;
+ __u16 mgid_hash;
+ void *mgmqp_p;
+
+ tmp = dev_buffers_p->inprm_buf;
+ memcpy(tmp, mcast_gid.raw, 16);
+ be_to_cpu_buf(tmp, 16);
+ rc = cmd_mgid_hash(tmp, &mgid_hash);
+ if (!rc) {
+ mg = (void *)dev_buffers_p->inprm_buf;
+ memset(mg, 0, MT_STRUCT_SIZE(arbelprm_mgm_entry_st));
+ INS_FLD(mcast_gid.as_u32.dw[0], mg, arbelprm_mgm_entry_st,
+ mgid_128_96);
+ INS_FLD(mcast_gid.as_u32.dw[1], mg, arbelprm_mgm_entry_st,
+ mgid_95_64);
+ INS_FLD(mcast_gid.as_u32.dw[2], mg, arbelprm_mgm_entry_st,
+ mgid_63_32);
+ INS_FLD(mcast_gid.as_u32.dw[3], mg, arbelprm_mgm_entry_st,
+ mgid_31_0);
+ be_to_cpu_buf(mg +
+ MT_BYTE_OFFSET(arbelprm_mgm_entry_st,
+ mgid_128_96), 16);
+ mgmqp_p = mg + MT_BYTE_OFFSET(arbelprm_mgm_entry_st, mgmqp_0);
+ INS_FLD(dev_ib_data.ipoib_qp.qpn, mgmqp_p, arbelprm_mgmqp_st,
+ qpn_i);
+ INS_FLD(add, mgmqp_p, arbelprm_mgmqp_st, qi);
+ rc = cmd_write_mgm(mg, mgid_hash);
+ }
+ return rc;
+}
+
+static int clear_interrupt(void)
+{
+ writel(dev_ib_data.clr_int_data, dev_ib_data.clr_int_addr);
+ return 0;
+}
+
+static struct ud_send_wqe_st *alloc_send_wqe(udqp_t qph)
+{
+ struct udqp_st *qp = qph;
+ __u32 idx;
+
+ if (qp->snd_wqe_cur_free) {
+ qp->snd_wqe_cur_free--;
+ idx = qp->snd_wqe_alloc_idx;
+ qp->snd_wqe_alloc_idx =
+ (qp->snd_wqe_alloc_idx + 1) & (qp->max_snd_wqes - 1);
+ return &qp->snd_wq[idx].wqe_cont.wqe;
+ }
+
+ return NULL;
+}
+
+static struct recv_wqe_st *alloc_rcv_wqe(struct udqp_st *qp)
+{
+ __u32 idx;
+
+ if (qp->recv_wqe_cur_free) {
+ qp->recv_wqe_cur_free--;
+ idx = qp->recv_wqe_alloc_idx;
+ qp->recv_wqe_alloc_idx =
+ (qp->recv_wqe_alloc_idx + 1) & (qp->max_recv_wqes - 1);
+ return &qp->rcv_wq[idx].wqe_cont.wqe;
+ }
+
+ return NULL;
+}
+
+static int free_send_wqe(struct ud_send_wqe_st *wqe)
+{
+ struct udqp_st *qp = ((struct ude_send_wqe_cont_st *)wqe)->qp;
+ qp->snd_wqe_cur_free++;
+
+ return 0;
+}
+
+static int free_rcv_wqe(struct recv_wqe_st *wqe)
+{
+ struct udqp_st *qp = ((struct recv_wqe_cont_st *)wqe)->qp;
+ qp->recv_wqe_cur_free++;
+
+ return 0;
+}
+
+static int free_wqe(void *wqe)
+{
+ int rc = 0;
+ struct recv_wqe_st *rcv_wqe;
+
+// tprintf("free wqe= 0x%x", wqe);
+ if ((wqe >= (void *)(dev_ib_data.ipoib_qp.rcv_wq)) &&
+ (wqe <
+ (void *)(&dev_ib_data.ipoib_qp.rcv_wq[NUM_IPOIB_RCV_WQES]))) {
+ /* ipoib receive wqe */
+ free_rcv_wqe(wqe);
+ rcv_wqe = alloc_rcv_wqe(&dev_ib_data.ipoib_qp);
+ if (rcv_wqe) {
+ rc = post_rcv_buf(&dev_ib_data.ipoib_qp, rcv_wqe);
+ if (rc) {
+ eprintf("");
+ }
+ }
+ } else if (wqe >= (void *)(dev_ib_data.ipoib_qp.snd_wq) &&
+ wqe <
+ (void *)(&dev_ib_data.ipoib_qp.snd_wq[NUM_IPOIB_SND_WQES])) {
+ /* ipoib send wqe */
+ free_send_wqe(wqe);
+ } else if (wqe >= (void *)(dev_ib_data.mads_qp.rcv_wq) &&
+ wqe <
+ (void *)(&dev_ib_data.mads_qp.rcv_wq[NUM_MADS_RCV_WQES])) {
+ /* mads receive wqe */
+ free_rcv_wqe(wqe);
+ rcv_wqe = alloc_rcv_wqe(&dev_ib_data.mads_qp);
+ if (rcv_wqe) {
+ rc = post_rcv_buf(&dev_ib_data.mads_qp, rcv_wqe);
+ if (rc) {
+ eprintf("");
+ }
+ }
+ } else if (wqe >= (void *)(dev_ib_data.mads_qp.snd_wq) &&
+ wqe <
+ (void *)(&dev_ib_data.mads_qp.snd_wq[NUM_MADS_SND_WQES])) {
+ /* mads send wqe */
+ free_send_wqe(wqe);
+ } else {
+ rc = -1;
+ eprintf("");
+ }
+
+ return rc;
+}
+
+static int update_eq_cons_idx(struct eq_st *eq)
+{
+ writel(eq->cons_counter, eq->ci_base_base_addr);
+ return 0;
+}
+
+static void dev2ib_eqe(struct ib_eqe_st *ib_eqe_p, struct eqe_t *eqe_p)
+{
+ void *tmp;
+
+ ib_eqe_p->event_type =
+ EX_FLD(eqe_p, arbelprm_event_queue_entry_st, event_type);
+
+ tmp = eqe_p + MT_BYTE_OFFSET(arbelprm_event_queue_entry_st, event_data);
+ ib_eqe_p->cqn = EX_FLD(tmp, arbelprm_completion_event_st, cqn);
+}
+
+static int poll_eq(struct ib_eqe_st *ib_eqe_p, __u8 * num_eqes)
+{
+ struct eqe_t eqe;
+ u8 owner;
+ int rc;
+ u32 *ptr;
+ struct eq_st *eq = &dev_ib_data.eq;
+ __u32 cons_idx = eq->cons_counter & (eq->eq_size - 1);
+
+ ptr = (u32 *) (&(eq->eq_buf[cons_idx]));
+ owner = (ptr[7] & 0x80000000) ? OWNER_HW : OWNER_SW;
+ if (owner == OWNER_SW) {
+ eqe = eq->eq_buf[cons_idx];
+ be_to_cpu_buf(&eqe, sizeof(eqe));
+ dev2ib_eqe(ib_eqe_p, &eqe);
+ ptr[7] |= 0x80000000;
+ eq->eq_buf[cons_idx] = eqe;
+ eq->cons_counter++;
+ rc = update_eq_cons_idx(eq);
+ if (rc) {
+ return -1;
+ }
+ *num_eqes = 1;
+ } else {
+ *num_eqes = 0;
+ }
+ return 0;
+}
+
+static int ib_device_close(void)
+{
+ iounmap(memfree_pci_dev.uar);
+ iounmap(memfree_pci_dev.cr_space);
+ return 0;
+}
+
+static __u32 dev_get_qpn(void *qph)
+{
+ struct udqp_st *qp = qph;
+
+ return qp->qpn;
+}
+
+static void dev_post_dbell(void *dbell, __u32 offset)
+{
+ __u32 *ptr;
+ unsigned long address;
+
+ ptr = dbell;
+
+ if (((ptr[0] >> 24) & 0xff) != 1) {
+ eprintf("");
+ }
+ tprintf("ptr[0]= 0x%lx", ptr[0]);
+ tprintf("ptr[1]= 0x%lx", ptr[1]);
+ address = (unsigned long)(memfree_pci_dev.uar) + offset;
+ tprintf("va=0x%lx pa=0x%lx", address,
+ virt_to_bus((const void *)address));
+ writel(htonl(ptr[0]), memfree_pci_dev.uar + offset);
+ barrier();
+ address += 4;
+ tprintf("va=0x%lx pa=0x%lx", address,
+ virt_to_bus((const void *)address));
+ writel(htonl(ptr[1]), address /*memfree_pci_dev.uar + offset + 4 */ );
+}
diff --git a/gpxe/src/drivers/net/mlx_ipoib/ipoib.c b/gpxe/src/drivers/net/mlx_ipoib/ipoib.c
new file mode 100644
index 00000000..85eaac7a
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/ipoib.c
@@ -0,0 +1,1027 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#include "ipoib.h"
+#include "ib_driver.h"
+#include "ib_mad.h"
+
+static const __u8 arp_packet_template[] = {
+ 0x00, 0x20, /* hardware type */
+ 0x08, 0x00, /* protocol type */
+ 20, /* hw size */
+ 4, /* protocol size */
+ 0x00, 0x00, /* opcode */
+
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, /* sender's mac */
+ 0, 0, 0, 0, /* sender's IP address */
+
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, /* Target's mac */
+ 0, 0, 0, 0 /* targets's IP address */
+};
+
+struct ipoib_data_st {
+ __u32 ipoib_qpn;
+ udqp_t ipoib_qph;
+ ud_av_t bcast_av;
+ cq_t snd_cqh;
+ cq_t rcv_cqh;
+ __u8 *port_gid_raw;
+} ipoib_data;
+
+#define NUM_MAC_ENTRIES (NUM_AVS+2)
+
+static struct mac_xlation_st mac_tbl[NUM_MAC_ENTRIES];
+static __u32 mac_counter = 1;
+static __u32 youth_counter = 0;
+
+#define EQUAL_GUIDS(g1, g2) ( \
+ ((g1)[0]==(g2)[0]) && \
+ ((g1)[1]==(g2)[1]) && \
+ ((g1)[2]==(g2)[2]) && \
+ ((g1)[3]==(g2)[3]) && \
+ ((g1)[4]==(g2)[4]) && \
+ ((g1)[5]==(g2)[5]) && \
+ ((g1)[6]==(g2)[6]) && \
+ ((g1)[7]==(g2)[7]) )
+
+#define MAC_IDX(i) (((mac_tbl[i].eth_mac_lsb[0])<<16) | \
+ ((mac_tbl[i].eth_mac_lsb[0])<<8) | \
+ (mac_tbl[i].eth_mac_lsb[0]))
+
+static inline const void *qpn2buf(__u32 qpn, const void *buf)
+{
+ ((__u8 *) buf)[0] = qpn >> 16;
+ ((__u8 *) buf)[1] = (qpn >> 8) & 0xff;
+ ((__u8 *) buf)[2] = qpn & 0xff;
+ return buf;
+}
+
+static inline __u32 buf2qpn(const void *buf)
+{
+ __u32 qpn;
+
+ qpn = ((((__u8 *) buf)[0]) << 16) +
+ ((((__u8 *) buf)[1]) << 8) + (((__u8 *) buf)[2]);
+
+ return qpn;
+}
+
+static int is_bcast_mac(const char *dest)
+{
+ int i;
+ __u8 mac = 0xff;
+
+ for (i = 0; i < 6; ++i)
+ mac &= dest[i];
+
+ return mac == 0xff;
+}
+
+/* find a free entry. if not found kick
+ * another entry.
+ */
+static int find_free_entry(void)
+{
+ __u32 youth = 0xffffffff;
+ __u8 i, remove_idx = NUM_MAC_ENTRIES;
+
+ /* find a free entry */
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ if (!mac_tbl[i].valid) {
+ mac_tbl[i].valid = 1;
+ mac_tbl[i].youth = youth_counter;
+ youth_counter++;
+ return i;
+ }
+ }
+
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ if ((mac_tbl[i].av == NULL) && (mac_tbl[i].youth < youth)) {
+ youth = mac_tbl[i].youth;
+ remove_idx = i;
+ }
+ }
+
+ if (remove_idx < NUM_MAC_ENTRIES) {
+ /* update the new youth value */
+ mac_tbl[remove_idx].youth = youth_counter;
+ youth_counter++;
+ return remove_idx;
+ } else {
+ tprintf("did not find an entry to kick");
+ return -1;
+ }
+}
+
+static int find_qpn_gid(__u32 qpn, const __u8 * gid)
+{
+ __u16 i;
+
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ if (mac_tbl[i].valid &&
+ (mac_tbl[i].qpn == qpn) &&
+ !memcmp(mac_tbl[i].gid.raw, gid, 16)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void allocate_new_mac6(__u8 * mac_lsb)
+{
+ __u32 eth_counter;
+
+ eth_counter = mac_counter;
+ mac_counter = (mac_counter + 1) & 0xffffff;
+
+ mac_lsb[0] = eth_counter >> 16;
+ mac_lsb[1] = eth_counter >> 8;
+ mac_lsb[2] = eth_counter & 0xff;
+ tprintf("add mac: %x:%x:%x", mac_lsb[0], mac_lsb[1], mac_lsb[2]);
+}
+
+static void modify_arp_reply(__u8 * eth_mac_lsb, void *data)
+{
+ __u8 *packet;
+
+ /* skip 4 bytes */
+ packet = ((__u8 *) data) + 4;
+
+ /* modify hw type */
+ packet[0] = 0;
+ packet[1] = 1;
+
+ /* modify hw size */
+ packet[4] = 6;
+
+ /* modify sender's mac */
+ packet[8] = MLX_ETH_BYTE0;
+ packet[9] = MLX_ETH_BYTE1;
+ packet[10] = MLX_ETH_BYTE2;
+ packet[11] = eth_mac_lsb[0];
+ packet[12] = eth_mac_lsb[1];
+ packet[13] = eth_mac_lsb[2];
+
+ /* move sender's IP address */
+ memcpy(packet + 14, packet + 28, 4);
+
+ /* set target MAC - that's us */
+ packet[18] = MLX_ETH_BYTE0;
+ packet[19] = MLX_ETH_BYTE1;
+ packet[20] = MLX_ETH_BYTE2;
+ packet[21] = 0;
+ packet[22] = 0;
+ packet[23] = 0;
+
+ /* move target's IP address */
+ memcpy(packet + 24, packet + 52, 4);
+}
+
+static void modify_arp_request(__u8 * eth_mac_lsb, void *data)
+{
+ __u8 *packet;
+
+ /* skip 4 bytes */
+ packet = ((__u8 *) data) + 4;
+
+ /* modify hw type */
+ packet[0] = 0;
+ packet[1] = 1;
+
+ /* modify hw size */
+ packet[4] = 6;
+
+ /* modify sender's mac */
+ packet[8] = MLX_ETH_BYTE0;
+ packet[9] = MLX_ETH_BYTE1;
+ packet[10] = MLX_ETH_BYTE2;
+ packet[11] = eth_mac_lsb[0];
+ packet[12] = eth_mac_lsb[1];
+ packet[13] = eth_mac_lsb[2];
+
+ /* move sender's IP address */
+ memcpy(packet + 14, packet + 28, 4);
+
+ /* set target MAC - that's us */
+ packet[18] = 0;
+ packet[19] = 0;
+ packet[20] = 0;
+ packet[21] = 0;
+ packet[22] = 0;
+ packet[23] = 0;
+
+ /* move target's IP address */
+ memcpy(packet + 24, packet + 52, 4);
+}
+
+static int handle_arp_packet(void *buf, void **out_buf_p,
+ unsigned int *new_size_p)
+{
+ __u16 opcode;
+ const void *p;
+ const __u8 *gid;
+ __u32 qpn;
+ int idx;
+
+ opcode = get_opcode(buf);
+ switch (opcode) {
+ case ARP_OP_REQUESET:
+ case ARP_OP_REPLY:
+ break;
+
+ default:
+ return -1;
+ }
+
+ p = arp_mac20_get_sender_qpn(buf);
+ qpn = buf2qpn(p);
+ gid = arp_mac20_get_sender_gid(buf);
+
+ if (!memcmp(gid, get_port_gid(), 16)) {
+ /* my own gid */
+ *out_buf_p = NULL;
+ return 0;
+ }
+
+ idx = find_qpn_gid(qpn, gid);
+ if (idx == -1) {
+ /* entry not in the table */
+ idx = find_free_entry();
+ if (idx == -1) {
+ eprintf("we're in broch\n");
+ return -1;
+ }
+ allocate_new_mac6(mac_tbl[idx].eth_mac_lsb);
+ mac_tbl[idx].av = NULL; // free the av id it exists ?? !!
+ mac_tbl[idx].qpn = qpn;
+ memcpy(mac_tbl[idx].gid.raw, gid, 16);
+ }
+
+ if (opcode == ARP_OP_REQUESET) {
+ modify_arp_request(mac_tbl[idx].eth_mac_lsb, buf);
+ } else {
+ /* we want to filter possible broadcast arp
+ replies not directed to us */
+ p = arp_mac20_get_target_qpn(buf);
+ qpn = buf2qpn(p);
+ gid = arp_mac20_get_target_gid(buf);
+
+ if ((qpn != ipoib_data.ipoib_qpn) ||
+ (memcmp(gid, get_port_gid(), 16))) {
+ *out_buf_p = NULL;
+ return 0;
+ }
+
+ modify_arp_reply(mac_tbl[idx].eth_mac_lsb, buf);
+ {
+ __u8 i;
+ tprintf("arp reply dump:\n");
+ for (i = 4; i < 32; ++i) {
+ tprintf("%x: ", ((__u8 *) buf)[i]);
+ }
+ tprintf("\n");
+ }
+ }
+ *out_buf_p = ((__u8 *) buf) + 4;
+ *new_size_p = 28; /* size of eth arp packet */
+
+ tprintf("");
+
+ return 0;
+}
+
+static void modify_udp_csum(void *buf, __u16 size)
+{
+ __u8 *ptr = (__u8 *) buf;
+ __u32 csum = 0;
+ __u16 chksum;
+ __u16 buf_size;
+ __u16 *tmp;
+ int i;
+
+ buf_size = (size & 1) ? size + 1 : size;
+ tmp = (__u16 *) (ptr + 12); /* src and dst ip addresses */
+ for (i = 0; i < 4; ++i) {
+ csum += tmp[i];
+ }
+
+ csum += 0x1100; // udp protocol
+
+ tmp = (__u16 *) (ptr + 26);
+ tmp[0] = 0; /* zero the checksum */
+
+ tmp = (__u16 *) (ptr + 24);
+ csum += tmp[0];
+
+ tmp = (__u16 *) (ptr + 20);
+
+ for (i = 0; i < ((buf_size - 20) >> 1); ++i) {
+ csum += tmp[i];
+ }
+
+ chksum = ~((__u16) ((csum & 0xffff) + (csum >> 16)));
+
+ tmp = (__u16 *) (ptr + 26);
+ tmp[0] = chksum; /* set the checksum */
+}
+
+static void modify_dhcp_resp(void *buf, __u16 size)
+{
+ set_eth_hwtype(buf);
+ set_eth_hwlen(buf);
+ set_own_mac(buf);
+ modify_udp_csum(buf, size);
+}
+
+static void get_my_client_id(__u8 * my_client_id)
+{
+
+ my_client_id[0] = 0;
+ qpn2buf(ipoib_data.ipoib_qpn, my_client_id + 1);
+ memcpy(my_client_id + 4, ipoib_data.port_gid_raw, 16);
+}
+
+static const __u8 *get_client_id(const void *buf, int len)
+{
+ const __u8 *ptr;
+ int delta;
+
+ if (len < 268)
+ return NULL;
+
+ /* pointer to just after magic cookie */
+ ptr = (const __u8 *)buf + 268;
+
+ /* find last client identifier option */
+ do {
+ if (ptr[0] == 255) {
+ /* found end of options list */
+ return NULL;
+ }
+
+ if (ptr[0] == 0x3d) {
+ /* client identifer option */
+ return ptr + 3;
+ }
+
+ delta = ptr[1] + 2;
+ ptr += delta;
+ len -= delta;
+ } while (len > 0);
+
+ return NULL;
+}
+
+static int handle_ipv4_packet(void *buf, void **out_buf_p,
+ unsigned int *new_size_p, int *is_bcast_p)
+{
+ void *new_buf;
+ __u16 new_size;
+ __u8 msg_type;
+ __u8 my_client_id[20];
+
+ new_buf = (void *)(((__u8 *) buf) + 4);
+ new_size = (*new_size_p) - 4;
+ *out_buf_p = new_buf;
+ *new_size_p = new_size;
+
+ if (get_ip_protocl(new_buf) == IP_PROT_UDP) {
+ __u16 udp_dst_port;
+ const __u8 *client_id;
+
+ udp_dst_port = get_udp_dst_port(new_buf);
+
+ if (udp_dst_port == 67) {
+ /* filter dhcp requests */
+ *out_buf_p = 0;
+ return 0;
+ }
+
+ if (udp_dst_port == 68) {
+ get_my_client_id(my_client_id);
+
+ /* packet client id */
+ client_id = get_client_id(new_buf, new_size);
+ if (!client_id) {
+ *out_buf_p = 0;
+ return 0;
+ }
+
+ if (memcmp(client_id, my_client_id, 20)) {
+ *out_buf_p = 0;
+ return 0;
+ }
+ }
+ }
+
+ msg_type = get_dhcp_msg_type(new_buf);
+ if ((get_ip_protocl(new_buf) == IP_PROT_UDP) &&
+ (get_udp_dst_port(new_buf) == 68) &&
+ ((msg_type == DHCP_TYPE_RESPONSE) || (msg_type == DHCP_TYPE_ACK))
+ ) {
+ *is_bcast_p = 1;
+ modify_dhcp_resp(new_buf, new_size);
+ }
+
+ return 0;
+}
+
+static int is_valid_arp(void *buf, unsigned int size)
+{
+ __u8 *ptr = buf;
+ __u16 tmp;
+
+ if (size != 60) {
+ eprintf("");
+ return 0;
+ }
+ if (be16_to_cpu(*((__u16 *) ptr)) != ARP_PROT_TYPE)
+ return 0;
+
+ if (be16_to_cpu(*((__u16 *) (ptr + 4))) != IPOIB_HW_TYPE)
+ return 0;
+
+ if (be16_to_cpu(*((__u16 *) (ptr + 6))) != IPV4_PROT_TYPE)
+ return 0;
+
+ if (ptr[8] != 20) /* hw addr len */
+ return 0;
+
+ if (ptr[9] != 4) /* protocol len = 4 for IP */
+ return 0;
+
+ tmp = be16_to_cpu(*((__u16 *) (ptr + 10)));
+ if ((tmp != ARP_OP_REQUESET) && (tmp != ARP_OP_REPLY))
+ return 0;
+
+ return 1;
+}
+
+static int ipoib_handle_rcv(void *buf, void **out_buf_p,
+ unsigned int *new_size_p, int *is_bcast_p)
+{
+ __u16 prot_type;
+ int rc;
+
+ prot_type = get_prot_type(buf);
+ switch (prot_type) {
+ case ARP_PROT_TYPE:
+ tprintf("");
+ if (is_valid_arp(buf, *new_size_p)) {
+ tprintf("got valid arp");
+ rc = handle_arp_packet(buf, out_buf_p, new_size_p);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ if (!out_buf_p) {
+ tprintf("");
+ }
+ tprintf("arp for me");
+ *is_bcast_p = 1;
+ return rc;
+ } else {
+ tprintf("got invalid arp");
+ *out_buf_p = NULL;
+ return 0;
+ }
+
+ case IPV4_PROT_TYPE:
+ tprintf("");
+ rc = handle_ipv4_packet(buf, out_buf_p, new_size_p, is_bcast_p);
+ return rc;
+ }
+ eprintf("prot=0x%x", prot_type);
+ return -1;
+}
+
+static int is_null_mac(const __u8 * mac)
+{
+ __u8 i, tmp = 0;
+ __u8 lmac[6];
+
+ memcpy(lmac, mac, 6);
+
+ for (i = 0; i < 6; ++i) {
+ tmp |= lmac[i];
+ }
+
+ if (tmp == 0)
+ return 1;
+ else
+ return 0;
+}
+
+static int find_mac(const __u8 * mac)
+{
+ int i;
+ const __u8 *tmp = mac + 3;
+
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ tprintf("checking 0x%02x:0x%02x:0x%02x valid=%d",
+ mac_tbl[i].eth_mac_lsb[0], mac_tbl[i].eth_mac_lsb[1],
+ mac_tbl[i].eth_mac_lsb[2], mac_tbl[i].valid);
+ if (mac_tbl[i].valid && !memcmp(mac_tbl[i].eth_mac_lsb, tmp, 3))
+ return i;
+ }
+ tprintf("mac: %x:%x:%x - dumping", tmp[0], tmp[1], tmp[2]);
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ //__u8 *gid= mac_tbl[i].gid.raw;
+ //__u8 *m= mac_tbl[i].eth_mac_lsb;
+ /*if (mac_tbl[i].valid) {
+ tprintf("%d: qpn=0x%lx, "
+ "gid=%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x, "
+ "av=0x%lx, "
+ "youth= %ld, "
+ "mac=%x:%x:%x\n",
+ i, mac_tbl[i].qpn,
+ gid[0], gid[1], gid[2], gid[3], gid[4], gid[5], gid[6], gid[7],
+ gid[8], gid[9], gid[10], gid[11], gid[12], gid[13], gid[14], gid[15],
+ mac_tbl[i].av, mac_tbl[i].youth,
+ m[0], m[1], m[2]);
+ } */
+ }
+ return -1;
+}
+
+static int send_bcast_packet(__u16 protocol, const void *data, __u16 size)
+{
+ ud_send_wqe_t snd_wqe, tmp_wqe;
+ int rc;
+ int is_good;
+ void *send_buffer;
+
+ snd_wqe = alloc_send_wqe(ipoib_data.ipoib_qph);
+ if (!snd_wqe) {
+ eprintf("");
+ return -1;
+ }
+
+ send_buffer = get_send_wqe_buf(snd_wqe, 0);
+ *((__u32 *) send_buffer) = cpu_to_be32(protocol << 16);
+ prep_send_wqe_buf(ipoib_data.ipoib_qph, ipoib_data.bcast_av,
+ snd_wqe, data, 4, size, 0);
+
+ rc = post_send_req(ipoib_data.ipoib_qph, snd_wqe, 1);
+ if (rc) {
+ eprintf("");
+ goto ex;
+ }
+
+ rc = poll_cqe_tout(ipoib_data.snd_cqh, SEND_CQE_POLL_TOUT, &tmp_wqe,
+ &is_good);
+ if (rc) {
+ eprintf("");
+ goto ex;
+ }
+ if (!is_good) {
+ eprintf("");
+ rc = -1;
+ goto ex;
+ }
+ if (tmp_wqe != snd_wqe) {
+ eprintf("");
+ rc = -1;
+ goto ex;
+ }
+
+ ex:free_wqe(snd_wqe);
+ return rc;
+}
+
+static int send_ucast_packet(const __u8 * mac, __u16 protocol, const void *data,
+ __u16 size)
+{
+ ud_send_wqe_t snd_wqe, tmp_wqe;
+ ud_av_t av;
+ udqp_t qph;
+ __u16 dlid;
+ __u8 sl, rate;
+ int rc;
+ int i;
+ int is_good;
+
+ i = find_mac(mac);
+ if (i < 0) {
+ tprintf("");
+ return -1;
+ }
+
+ if (!mac_tbl[i].av) {
+ rc = get_path_record(&mac_tbl[i].gid, &dlid, &sl, &rate);
+ if (rc) {
+ eprintf("");
+ return -1;
+ } else {
+ tprintf("get_path_record() success dlid=0x%x", dlid);
+ }
+
+ /* no av - allocate one */
+ av = alloc_ud_av();
+ if (!av) {
+ eprintf("");
+ return -1;
+ }
+ modify_av_params(av, dlid, 1, sl, rate, &mac_tbl[i].gid,
+ mac_tbl[i].qpn);
+ mac_tbl[i].av = av;
+ } else {
+ av = mac_tbl[i].av;
+ }
+ qph = ipoib_data.ipoib_qph;
+ snd_wqe = alloc_send_wqe(qph);
+ if (!snd_wqe) {
+ eprintf("");
+ return -1;
+ }
+
+ *((__u32 *) get_send_wqe_buf(snd_wqe, 0)) = cpu_to_be32(protocol << 16);
+ prep_send_wqe_buf(qph, av, snd_wqe, data, 4, size, 0);
+
+ rc = post_send_req(qph, snd_wqe, 1);
+ if (rc) {
+ eprintf("");
+ return -1;
+ }
+
+ rc = poll_cqe_tout(ipoib_data.snd_cqh, SEND_CQE_POLL_TOUT, &tmp_wqe,
+ &is_good);
+ if (rc) {
+ eprintf("");
+ goto ex;
+ }
+ if (!is_good) {
+ eprintf("");
+ rc = -1;
+ goto ex;
+ }
+ if (tmp_wqe != snd_wqe) {
+ eprintf("");
+ rc = -1;
+ goto ex;
+ }
+
+ ex:free_wqe(snd_wqe);
+ return rc;
+}
+
+static void *alloc_convert_arp6_msg(const void *data,
+ struct arp_packet_st *ipoib_arp)
+{
+ void *buf;
+ const void *p1;
+ int idx;
+ __u8 qpn[3];
+
+ memcpy(ipoib_arp, arp_packet_template, sizeof arp_packet_template);
+ buf = ipoib_arp;
+
+ /* update opcode */
+ p1 = arp_mac6_get_opcode(data);
+ arp_mac20_set_opcode(p1, buf);
+
+ /* update sender ip */
+ p1 = arp_mac6_get_sender_ip(data);
+ arp_mac20_set_sender_ip(p1, buf);
+
+ /* update target ip */
+ p1 = arp_mac6_get_target_ip(data);
+ arp_mac20_set_target_ip(p1, buf);
+
+ /* update sender mac */
+ qpn2buf(ipoib_data.ipoib_qpn, qpn);
+ arp_mac20_set_sender_mac(qpn, ipoib_data.port_gid_raw, buf);
+
+ /* update target mac */
+ p1 = arp_mac6_get_target_mac(data);
+ if (!is_null_mac(p1)) {
+ idx = find_mac(p1);
+ if (idx == -1) {
+ __u8 *_ptr = (__u8 *) p1;
+ eprintf("could not find mac %x:%x:%x",
+ _ptr[3], _ptr[4], _ptr[5]);
+ return NULL;
+ }
+ qpn2buf(mac_tbl[idx].qpn, qpn);
+ arp_mac20_set_target_mac(qpn, mac_tbl[idx].gid.raw, buf);
+ }
+
+ return buf;
+}
+
+static __u16 set_client_id(__u8 * packet)
+{
+ __u8 *ptr;
+ __u8 y[3];
+ __u16 new_size;
+
+ /* pointer to just after magic cookie */
+ ptr = packet + 268;
+
+ /* find last option */
+ do {
+ if (ptr[0] == 255) {
+ /* found end of options list */
+ break;
+ }
+ ptr = ptr + ptr[1] + 2;
+ } while (1);
+
+ ptr[0] = 61; /* client id option identifier */
+ ptr[1] = 21; /* length of the option */
+ ptr[2] = IPOIB_HW_TYPE;
+ ptr[3] = 0;
+ qpn2buf(ipoib_data.ipoib_qpn, y);
+ memcpy(ptr + 4, y, 3);
+ memcpy(ptr + 7, ipoib_data.port_gid_raw, 16);
+ ptr[23] = 255;
+ new_size = (__u16) (ptr + 24 - packet);
+ if (new_size & 3) {
+ new_size += (4 - (new_size & 3));
+ }
+ return new_size;
+}
+
+static __u16 calc_udp_csum(__u8 * packet)
+{
+ __u16 *ptr;
+ int i;
+ __u32 sum = 0;
+ __u16 udp_length, udp_csum;
+
+ /* src ip, dst ip */
+ ptr = (__u16 *) (packet + 12);
+ for (i = 0; i < 4; ++i) {
+ sum += be16_to_cpu(ptr[i]);
+ }
+
+ /* udp protocol */
+ sum += IP_PROT_UDP;
+
+ /* udp length */
+ ptr = (__u16 *) (packet + 24);
+ udp_length = be16_to_cpu(*ptr);
+ sum += udp_length;
+
+ /* udp part */
+ ptr = (__u16 *) (packet + 20);
+ do {
+ sum += be16_to_cpu(*ptr);
+ ptr++;
+ udp_length -= 2;
+ } while (udp_length);
+
+ udp_csum = ~((__u16) ((sum & 0xffff) + (sum >> 16)));
+ return udp_csum;
+}
+
+static __u16 modify_dhcp_request(__u8 * packet, __u16 size)
+{
+ __u16 csum, new_size;
+
+ set_hw_type(packet);
+ zero_hw_len(packet);
+ zero_chaddr(packet);
+ set_bcast_flag(packet);
+ new_size = set_client_id(packet);
+ if (new_size > size) {
+ add_udp_len(packet, new_size - size);
+ } else
+ new_size = size;
+ set_udp_csum(packet, 0);
+ csum = calc_udp_csum(packet);
+ set_udp_csum(packet, csum);
+ return new_size;
+}
+
+static __u16 copy_dhcp_message(__u8 * buf, const void *packet, __u16 size)
+{
+ memcpy(buf, packet, size);
+ return size;
+}
+
+static void modify_ip_hdr(__u8 * buf, __u16 add_size)
+{
+ __u16 *ptr, ip_csum;
+ __u16 tmp;
+ __u32 sum = 0;
+ __u8 i;
+
+ /* update ip length */
+ ptr = (__u16 *) buf;
+ tmp = be16_to_cpu(ptr[1]);
+ ptr[1] = cpu_to_be16(tmp + add_size);
+
+ ptr[5] = 0; /* zero csum */
+ for (i = 0; i < 10; ++i) {
+ sum += be16_to_cpu(ptr[i]);
+ }
+
+ ip_csum = ~((__u16) ((sum & 0xffff) + (sum >> 16)));
+ ptr[5] = cpu_to_be16(ip_csum);
+
+}
+
+static void *update_dhcp_request(const void *packet, unsigned int size,
+ __u16 * new_size_p)
+{
+ __u8 ip_proto, dhcp_message_type;
+ __u16 dest_port, new_size, orig_size;
+ static __u8 dhcp_send_buffer[576];
+
+ ip_proto = get_ip_protocl_type(packet);
+ if (ip_proto != IP_PROT_UDP) {
+ return NULL;
+ }
+
+ dest_port = get_udp_dest_port(packet);
+ if (dest_port != 0x4300 /*67 */ )
+ return NULL;
+
+ dhcp_message_type = get_dhcp_message_type(packet);
+ if (dhcp_message_type != DHCP_TYPE_REQUEST)
+ return NULL;
+
+ memset(dhcp_send_buffer, 0, sizeof dhcp_send_buffer);
+ orig_size = copy_dhcp_message(dhcp_send_buffer, packet, size);
+
+ new_size = modify_dhcp_request(dhcp_send_buffer, orig_size);
+ if (new_size != orig_size) {
+ modify_ip_hdr(dhcp_send_buffer, new_size - orig_size);
+ }
+ *new_size_p = new_size;
+ return dhcp_send_buffer;
+}
+
+static int ipoib_send_packet(const __u8 * mac, __u16 protocol, const void *data,
+ unsigned int size)
+{
+ const void *packet;
+ __u16 new_size, dhcp_req_sz;
+ void *tmp;
+ int rc;
+ struct arp_packet_st ipoib_arp;
+
+ tprintf("");
+
+ if (protocol == ARP_PROT_TYPE) {
+ /* special treatment for ARP */
+ tmp = alloc_convert_arp6_msg(data, &ipoib_arp);
+ if (!tmp) {
+ eprintf("");
+ return -1;
+ }
+ packet = tmp;
+ new_size = sizeof(struct arp_packet_st);
+ tprintf("sending arp");
+ } else {
+ tmp = update_dhcp_request(data, size, &dhcp_req_sz);
+ if (tmp) {
+ /* it was a dhcp request so we use a special
+ buffer because we may have to enlarge the size of the packet */
+ tprintf("sending dhcp");
+ packet = tmp;
+ new_size = dhcp_req_sz;
+ } else {
+ packet = data;
+ new_size = size;
+ tprintf("sending packet");
+ }
+ }
+
+ //eprintf("press key ..."); getchar();
+ if (is_bcast_mac(mac)) {
+ tprintf("");
+ rc = send_bcast_packet(protocol, packet, new_size);
+ } else {
+ tprintf("");
+ rc = send_ucast_packet(mac, protocol, packet, new_size);
+ }
+
+ return rc;
+}
+
+static int ipoib_read_packet(__u16 * prot_p, void *data, unsigned int *size_p,
+ int *is_bcast_p)
+{
+ int rc;
+ struct ib_cqe_st ib_cqe;
+ __u8 num_cqes;
+ unsigned int new_size;
+ void *buf, *out_buf;
+ __u16 prot_type;
+
+ rc = ib_poll_cq(ipoib_data.rcv_cqh, &ib_cqe, &num_cqes);
+ if (rc) {
+ return rc;
+ }
+
+ if (num_cqes == 0) {
+ *size_p = 0;
+ return 0;
+ }
+
+ if (ib_cqe.is_error) {
+ eprintf("");
+ rc = -1;
+ goto ex_func;
+ }
+
+ new_size = ib_cqe.count - GRH_SIZE;
+ buf = get_rcv_wqe_buf(ib_cqe.wqe, 1);
+ tprintf("buf=%lx", buf);
+ rc = ipoib_handle_rcv(buf, &out_buf, &new_size, is_bcast_p);
+ if (rc) {
+ eprintf("");
+ rc = -1;
+ goto ex_func;
+ }
+ if (out_buf) {
+ tprintf("");
+ prot_type = get_prot_type(buf);
+ *size_p = new_size;
+ tprintf("new_size=%d", new_size);
+ if (new_size > 1560) {
+ eprintf("sizzzzzze = %d", new_size);
+ } else {
+ memcpy(data, out_buf, new_size);
+ }
+ *prot_p = prot_type;
+ } else {
+ tprintf("skip message");
+ *size_p = 0;
+ }
+
+ ex_func:
+ if (free_wqe(ib_cqe.wqe)) {
+ eprintf("");
+ }
+
+ return rc;
+}
+
+static int ipoib_init(struct pci_device *pci)
+{
+ int rc;
+ udqp_t qph;
+ int i;
+
+ tprintf("");
+ rc = ib_driver_init(pci, &qph);
+ if (rc)
+ return rc;
+
+ tprintf("");
+ ipoib_data.ipoib_qph = qph;
+ ipoib_data.ipoib_qpn = ib_get_qpn(qph);
+
+ if(print_info)
+ printf("local ipoib qpn=0x%x\n", ipoib_data.ipoib_qpn);
+
+ ipoib_data.bcast_av = ib_data.bcast_av;
+ ipoib_data.port_gid_raw = ib_data.port_gid.raw;
+ ipoib_data.snd_cqh = ib_data.ipoib_snd_cq;
+ ipoib_data.rcv_cqh = ib_data.ipoib_rcv_cq;
+
+ mac_counter = 1;
+ youth_counter = 0;
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ mac_tbl[i].valid = 0;
+ mac_tbl[i].av = NULL;
+ }
+
+ return 0;
+}
+
+static int ipoib_close(int fw_fatal)
+{
+ int rc;
+
+ rc = ib_driver_close(fw_fatal);
+
+ return rc;
+}
diff --git a/gpxe/src/drivers/net/mlx_ipoib/ipoib.h b/gpxe/src/drivers/net/mlx_ipoib/ipoib.h
new file mode 100644
index 00000000..c51f8a50
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/ipoib.h
@@ -0,0 +1,297 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#ifndef __ipoib_h__
+#define __ipoib_h__
+
+#define ARP_PROT_TYPE 0x806
+#define IPV4_PROT_TYPE 0x800
+
+#define IPOIB_HW_TYPE 0x20
+#define ETH_HW_TYPE 1
+
+#define ARP_OP_REQUESET 1
+#define ARP_OP_REPLY 2
+
+#define MLX_ETH_3BYTE_PREFIX 0x2c9 /* 00,02,c9 */
+#define MLX_ETH_BYTE0 0
+#define MLX_ETH_BYTE1 2
+#define MLX_ETH_BYTE2 0xC9
+
+#define IP_PROT_UDP 17
+#define DHCP_TYPE_REQUEST 1
+#define DHCP_TYPE_RESPONSE 2
+#define DHCP_TYPE_ACK 5
+
+struct ipoib_mac_st {
+ __u32 qpn:24;
+ __u32 r0:8;
+ __u8 gid[16];
+} __attribute__ ((packed));
+
+struct arp_packet_st {
+ __u16 arp_prot_type;
+ __u16 hw_type;
+ __u16 opcode;
+ __u8 prot_size;
+ __u8 hw_len;
+ struct ipoib_mac_st sender_mac;
+ __u32 sender_ip;
+ struct ipoib_mac_st target_mac;
+ __u32 target_ip;
+} __attribute__ ((packed));
+
+/* this struct is used to translate between ipoib and
+ ethernet mac addresses */
+struct mac_xlation_st {
+ __u8 valid; /* 1=entry valid 0=entry free */
+ __u32 youth; /* youth of this entry the lowest the
+ number the older in age */
+ __u8 eth_mac_lsb[3]; /* three bytes Ethernet MAC
+ LS bytes are constants */
+ union ib_gid_u gid;
+ __u32 qpn;
+ ud_av_t av; /* address vector representing neighbour */
+};
+
+static inline __u16 get_prot_type(void *data)
+{
+ __u8 *ptr = data;
+
+ return be16_to_cpu(*((__u16 *) ptr));
+}
+
+static inline __u8 get_hw_len(void *data)
+{
+ return ((__u8 *) data)[8];
+}
+
+static inline __u8 get_prot_size(void *data)
+{
+ return ((__u8 *) data)[9];
+}
+
+static inline __u16 get_opcode(const void *data)
+{
+ return be16_to_cpu(*((__u16 *) (&(((__u8 *) data)[10]))));
+}
+
+static inline __u32 get_sender_qpn(void *data)
+{
+ __u8 *ptr = data;
+
+ return (ptr[13] << 16) | (ptr[14] << 8) | ptr[15];
+}
+
+static inline const __u8 *get_sender_gid(void *data)
+{
+ return &(((__u8 *) data)[16]);
+}
+
+static inline void *arp_mac6_get_sender_ip(const void *data)
+{
+ return (__u8 *) data + 14;
+}
+
+static inline const void *arp_mac6_get_target_ip(const void *data)
+{
+ return data + 24;
+}
+
+static inline void arp_mac20_set_sender_ip(const void *ip, void *data)
+{
+ memcpy(((__u8 *) data) + 28, ip, 4);
+}
+
+static inline void arp_mac20_set_target_ip(const void *ip, void *data)
+{
+ memcpy(((__u8 *) data) + 52, ip, 4);
+}
+
+static inline void arp_mac20_set_sender_mac(const void *qpn, const void *gid,
+ void *data)
+{
+ memcpy(((__u8 *) data) + 9, qpn, 3);
+ memcpy(((__u8 *) data) + 12, gid, 16);
+}
+
+static inline void arp_mac20_set_target_mac(void *qpn, void *gid, void *data)
+{
+ memcpy(((__u8 *) data) + 33, qpn, 3);
+ memcpy(((__u8 *) data) + 36, gid, 16);
+}
+
+static inline const void *arp_mac6_get_opcode(const void *data)
+{
+ return data + 6;
+}
+
+static inline void arp_mac20_set_opcode(const void *opcode, void *data)
+{
+ memcpy(data + 6, opcode, 2);
+}
+
+static inline const void *arp_mac6_get_target_mac(const void *data)
+{
+ return data + 18;
+}
+
+static inline const void *arp_mac20_get_sender_qpn(void *data)
+{
+ return ((__u8 *) data) + 13;
+}
+
+static inline const void *arp_mac20_get_sender_gid(void *data)
+{
+ return ((__u8 *) data) + 16;
+}
+
+static inline const void *arp_mac20_get_target_qpn(void *data)
+{
+ return ((__u8 *) data) + 37;
+}
+
+static inline const void *arp_mac20_get_target_gid(void *data)
+{
+ return ((__u8 *) data) + 40;
+}
+
+static inline const void *get_lptr_by_off(const void *packet, __u16 offset)
+{
+ return packet + offset;
+}
+
+static inline __u8 get_ip_protocl_type(const void *packet)
+{
+ const void *ptr;
+ __u8 prot;
+
+ ptr = get_lptr_by_off(packet, 9);
+
+ memcpy(&prot, ptr, 1);
+ return prot;
+}
+
+static inline __u16 get_udp_dest_port(const void *packet)
+{
+ const void *ptr;
+ __u16 port;
+
+ ptr = get_lptr_by_off(packet, 22);
+
+ memcpy(&port, ptr, 2);
+ return port;
+}
+
+static inline __u8 get_dhcp_message_type(const void *packet)
+{
+ const void *ptr;
+ __u8 type;
+
+ ptr = get_lptr_by_off(packet, 28);
+
+ memcpy(&type, ptr, 1);
+ return type;
+}
+
+static inline void set_hw_type(__u8 * packet)
+{
+ packet[29] = IPOIB_HW_TYPE;
+}
+
+static inline void zero_hw_len(__u8 * packet)
+{
+ packet[30] = 0;
+}
+
+static inline void set_udp_csum(__u8 * packet, __u16 val)
+{
+ __u16 *csum_ptr;
+
+ csum_ptr = (__u16 *) (packet + 26);
+
+ *csum_ptr = htons(val);
+}
+
+static inline void zero_chaddr(__u8 * packet)
+{
+ memset(packet + 56, 0, 16);
+}
+
+static inline void set_bcast_flag(__u8 * packet)
+{
+ packet[38] = 0x80;
+}
+
+static inline __u8 get_ip_protocl(void *buf)
+{
+ return ((__u8 *) buf)[9];
+}
+
+static inline __u16 get_udp_dst_port(void *buf)
+{
+ return be16_to_cpu(*((__u16 *) (((__u8 *) buf) + 0x16)));
+}
+
+static inline __u8 get_dhcp_msg_type(void *buf)
+{
+ return ((__u8 *) buf)[0x1c];
+}
+
+static inline void set_eth_hwtype(void *buf)
+{
+ ((__u8 *) buf)[0x1d] = ETH_HW_TYPE;
+}
+
+static inline void set_eth_hwlen(void *buf)
+{
+ ((__u8 *) buf)[0x1e] = 6;
+}
+
+static inline void add_udp_len(void *buf, __u16 size_add)
+{
+ __u16 old_len, *len_ptr;
+
+ len_ptr = (__u16 *) (((__u8 *) buf) + 24);
+ old_len = ntohs(*len_ptr);
+ *len_ptr = htons(old_len + size_add);
+}
+
+static inline void set_own_mac(void *buf)
+{
+ ((__u8 *) buf)[0x38] = 0xff; //MLX_ETH_BYTE0;
+ ((__u8 *) buf)[0x39] = 0xff; //MLX_ETH_BYTE1;
+ ((__u8 *) buf)[0x3a] = 0xff; //MLX_ETH_BYTE2;
+ ((__u8 *) buf)[0x3b] = 0xff; //0;
+ ((__u8 *) buf)[0x3c] = 0xff; //0;
+ ((__u8 *) buf)[0x3d] = 0xff; //0;
+}
+
+static int ipoib_handle_rcv(void *buf, void **out_buf_p,
+ unsigned int *new_size_p, int *bcast_p);
+static int ipoib_send_packet(const __u8 * mac, __u16 protocol, const void *data,
+ unsigned int size);
+static int ipoib_init(struct pci_device *pci);
+static u8 *get_port_gid(void);
+static int ipoib_read_packet(__u16 * prot_p, void *data, unsigned int *size_p,
+ int *is_bcast_p);
+
+#endif /* __ipoib_h__ */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/mad_attrib.h b/gpxe/src/drivers/net/mlx_ipoib/mad_attrib.h
new file mode 100644
index 00000000..e7af6793
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/mad_attrib.h
@@ -0,0 +1,244 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#ifndef __mad_attrib_h_
+#define __mad_attrib_h_
+
+#include "ib_mad.h"
+
+#define IB_SA_ATTR_MC_MEMBER_REC 0x38
+#define IB_SA_ATTR_PATH_REC 0x35
+
+#define IB_SA_MCMEMBER_REC_MGID (1<<0)
+#define IB_SA_MCMEMBER_REC_PORT_GID (1<<1)
+#define IB_SA_MCMEMBER_REC_QKEY (1<<2)
+#define IB_SA_MCMEMBER_REC_MLID (1<<3)
+#define IB_SA_MCMEMBER_REC_MTU_SELECTOR (1<<4)
+#define IB_SA_MCMEMBER_REC_MTU (1<<5)
+#define IB_SA_MCMEMBER_REC_TRAFFIC_CLASS (1<<6)
+#define IB_SA_MCMEMBER_REC_PKEY (1<<7)
+#define IB_SA_MCMEMBER_REC_RATE_SELECTOR (1<<8)
+#define IB_SA_MCMEMBER_REC_RATE (1<<9)
+#define IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME_SELECTOR (1<<10)
+#define IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME (1<<11)
+#define IB_SA_MCMEMBER_REC_SL (1<<12)
+#define IB_SA_MCMEMBER_REC_FLOW_LABEL (1<<13)
+#define IB_SA_MCMEMBER_REC_HOP_LIMIT (1<<14)
+#define IB_SA_MCMEMBER_REC_SCOPE (1<<15)
+#define IB_SA_MCMEMBER_REC_JOIN_STATE (1<<16)
+#define IB_SA_MCMEMBER_REC_PROXY_JOIN (1<<17)
+
+#define IB_SA_PATH_REC_DGID (1<<2)
+#define IB_SA_PATH_REC_SGID (1<<3)
+
+struct port_info_st {
+ __u32 mkey[2];
+ __u32 gid_prefix[2];
+ __u16 mastersm_lid;
+ __u16 lid;
+ __u32 cap_mask;
+ __u32 combined2;
+ /*__u32 mkey_lease_period:16;
+ __u32 diag_code:16;*/
+ __u32 combined3;
+ /*__u32 link_width_active:8;
+ __u32 link_width_supported:8;
+ __u32 link_width_enabled:8;
+ __u32 local_port_num:8;*/
+ __u32 combined4;
+ /*__u32 link_speed_enabled:4;
+ __u32 link_speed_active:4;
+ __u32 lmc:3;
+ __u32 r1:3;
+ __u32 mkey_prot_bits:2;
+ __u32 link_down_def_state:4;
+ __u32 port_phys_state:4;
+ __u32 port_state:4;
+ __u32 link_speed_supported:4;*/
+ __u32 combined5;
+ /*__u32 vl_arb_hi_cap:8;
+ __u32 vl_hi_limit:8;
+ __u32 init_type:4;
+ __u32 vl_cap:4;
+ __u32 master_smsl:4;
+ __u32 neigh_mtu:4;*/
+ __u32 combined6;
+ /*__u32 filt_raw_oub:1;
+ __u32 filt_raw_inb:1;
+ __u32 part_enf_oub:1;
+ __u32 part_enf_inb:1;
+ __u32 op_vls:4;
+ __u32 hoq_life:5;
+ __u32 vl_stall_count:3;
+ __u32 mtu_cap:4;
+ __u32 init_type_reply:4;
+ __u32 vl_arb_lo_cap:8;*/
+ __u32 combined7;
+ /*__u32 pkey_viol:16;
+ __u32 mkey_viol:16;*/
+ __u32 combined8;
+ /*__u32 subn_tout:5;
+ __u32 r2:2;
+ __u32 client_rereg:1;
+ __u32 guid_cap:8;
+ __u32 qkey_viol:16;*/
+ __u32 combined9;
+ /*__u32 max_cred_hint:16;
+ __u32 overrun_err:4;
+ __u32 local_phy_err:4;
+ __u32 resp_t_val:5;
+ __u32 r3:3;*/
+ __u32 combined10;
+ /*__u32 r4:8;
+ __u32 link_rtrip_lat:24;*/
+} __attribute__ ((packed));
+
+struct port_info_mad_st {
+ struct ib_mad_hdr_st mad_hdr;
+ __u32 mkey[2];
+ __u32 r1[8];
+ struct port_info_st port_info;
+} __attribute__ ((packed));
+
+union port_info_mad_u {
+ __u8 raw[256];
+ struct port_info_mad_st mad;
+} __attribute__ ((packed));
+
+struct guid_info_st {
+ union ib_gid_u gid_tbl[8];
+} __attribute__ ((packed));
+
+struct guid_info_mad_st {
+ struct ib_mad_hdr_st mad_hdr;
+ __u32 mkey[2];
+ __u32 r1[8];
+ struct guid_info_st guid_info;
+} __attribute__ ((packed));
+
+union guid_info_mad_u {
+ __u8 raw[256];
+ struct guid_info_mad_st mad;
+} __attribute__ ((packed));
+
+struct mc_member_st {
+ __u8 mgid[16];
+ __u8 port_gid[16];
+ __u32 q_key;
+ __u32 combined1;
+ /*__u32 tclass:8;
+ __u32 mtu:6;
+ __u32 mtu_selector:2;
+ __u32 mlid:16;*/
+ __u32 combined2;
+ /*__u32 packet_liftime:6;
+ __u32 packet_liftime_selector:2;
+ __u32 rate:6;
+ __u32 rate_selector:2;
+ __u32 pkey:16;*/
+ __u32 combined3;
+ /*__u32 hop_limit:8;
+ __u32 flow_label:20;
+ __u32 sl:4;*/
+ __u32 combined4;
+ /*__u32 r0:23;
+ __u32 proxy_join:1;
+ __u32 join_state:4;
+ __u32 scope:4;*/
+} __attribute__ ((packed));
+
+struct mc_member_mad_st {
+ struct ib_mad_hdr_st mad_hdr;
+ struct rmpp_hdr_st rmpp_hdr;
+ struct sa_header_st sa_hdr;
+ struct mc_member_st mc_member;
+} __attribute__ ((packed));
+
+union mc_member_mad_u {
+ struct mc_member_mad_st mc_member;
+ __u8 raw[256];
+} __attribute__ ((packed));
+
+struct pkey_tbl_st {
+ __u16 pkey_tbl[16][2];
+} __attribute__ ((packed));
+
+struct pkey_tbl_mad_st {
+ struct ib_mad_hdr_st mad_hdr;
+ __u32 mkey[2];
+ __u32 r1[8];
+ struct pkey_tbl_st pkey_tbl;
+} __attribute__ ((packed));
+
+union pkey_tbl_mad_u {
+ struct pkey_tbl_mad_st mad;
+ __u8 raw[256];
+} __attribute__ ((packed));
+
+struct path_record_st {
+ __u32 r0[2];
+ union ib_gid_u dgid;
+ union ib_gid_u sgid;
+ __u16 slid;
+ __u16 dlid;
+ __u32 combined1;
+ /*__u32 hop_limit:8;
+ __u32 flow_label:20;
+ __u32 r1:3;
+ __u32 raw_traffic:1;*/
+ __u32 combined2;
+ /*__u32 pkey:16;
+ __u32 numb_path:7;
+ __u32 reversible:1;
+ __u32 tclass:8;*/
+ __u32 combined3;
+ /*__u32 rate:6;
+ __u32 rate_selector:2;
+ __u32 mtu:6;
+ __u32 mtu_selector:2;
+ __u32 sl:4;
+ __u32 reserved:12;*/
+ __u32 combined4;
+ /*__u32 r2:16;
+ __u32 preference:8;
+ __u32 packet_lifetime:6;
+ __u32 packet_lifetime_selector:2;*/
+ __u32 r3;
+} __attribute__ ((packed));
+
+struct path_record_mad_st {
+ struct ib_mad_hdr_st mad_hdr;
+ struct rmpp_hdr_st rmpp_hdr;
+ struct sa_header_st sa_hdr;
+ struct path_record_st path_record;
+} __attribute__ ((packed));
+
+union path_record_mad_u {
+ struct path_record_mad_st mad;
+ __u8 raw[256];
+} __attribute__ ((packed));
+
+static int get_port_info(__u8 port, struct port_info_st *buf, __u16 * status);
+static int get_guid_info(__u16 * status);
+static int get_pkey_tbl(struct pkey_tbl_st *pkey_tbl, __u16 * status);
+static int join_mc_group(__u32 * qkey_p, __u16 * mlid_p, __u8 join);
+
+#endif /* __mad_attrib_h_ */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/mt23108.c b/gpxe/src/drivers/net/mlx_ipoib/mt23108.c
new file mode 100644
index 00000000..492bc901
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/mt23108.c
@@ -0,0 +1,245 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Skeleton NIC driver for Etherboot
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include <gpxe/pci.h>
+/* to get the ISA support functions, if this is an ISA NIC */
+#include <gpxe/isa.h>
+
+#include "mt_version.c"
+#include "mt23108_imp.c"
+
+/* NIC specific static variables go here */
+
+int prompt_key(int secs, unsigned char *ch_p)
+{
+ unsigned long tmo;
+ unsigned char ch;
+
+ for (tmo = currticks() + secs * TICKS_PER_SEC; currticks() < tmo;) {
+ if (iskey()) {
+ ch = getchar();
+ /* toupper does not work ... */
+ if (ch == 'v')
+ ch = 'V';
+ if (ch == 'i')
+ ch = 'I';
+ if ((ch=='V') || (ch=='I')) {
+ *ch_p = ch;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**************************************************************************
+IRQ - handle interrupts
+***************************************************************************/
+static void tavor_irq(struct nic *nic, irq_action_t action)
+{
+ /* This routine is somewhat optional. Etherboot itself
+ * doesn't use interrupts, but they are required under some
+ * circumstances when we're acting as a PXE stack.
+ *
+ * If you don't implement this routine, the only effect will
+ * be that your driver cannot be used via Etherboot's UNDI
+ * API. This won't affect programs that use only the UDP
+ * portion of the PXE API, such as pxelinux.
+ */
+
+ if (0) {
+ nic = NULL;
+ }
+ switch (action) {
+ case DISABLE:
+ case ENABLE:
+ /* Set receive interrupt enabled/disabled state */
+ /*
+ outb ( action == ENABLE ? IntrMaskEnabled : IntrMaskDisabled,
+ nic->ioaddr + IntrMaskRegister );
+ */
+ break;
+ case FORCE:
+ /* Force NIC to generate a receive interrupt */
+ /*
+ outb ( ForceInterrupt, nic->ioaddr + IntrForceRegister );
+ */
+ break;
+ }
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int tavor_poll(struct nic *nic, int retrieve)
+{
+ /* Work out whether or not there's an ethernet packet ready to
+ * read. Return 0 if not.
+ */
+ /*
+ if ( ! <packet_ready> ) return 0;
+ */
+
+ /* retrieve==0 indicates that we are just checking for the
+ * presence of a packet but don't want to read it just yet.
+ */
+ /*
+ if ( ! retrieve ) return 1;
+ */
+
+ /* Copy data to nic->packet. Data should include the
+ * link-layer header (dest MAC, source MAC, type).
+ * Store length of data in nic->packetlen.
+ * Return true to indicate a packet has been read.
+ */
+ /*
+ nic->packetlen = <packet_length>;
+ memcpy ( nic->packet, <packet_data>, <packet_length> );
+ return 1;
+ */
+ unsigned int size;
+ int rc;
+ rc = poll_imp(nic, retrieve, &size);
+ if (rc) {
+ return 0;
+ }
+
+ if (size == 0) {
+ return 0;
+ }
+
+ nic->packetlen = size;
+
+ return 1;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void tavor_transmit(struct nic *nic, const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ unsigned int size, /* size */
+ const char *packet)
+{ /* Packet */
+ int rc;
+
+ /* Transmit packet to dest MAC address. You will need to
+ * construct the link-layer header (dest MAC, source MAC,
+ * type).
+ */
+ if (nic) {
+ rc = transmit_imp(dest, type, packet, size);
+ if (rc)
+ eprintf("tranmit error");
+ }
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void tavor_disable(struct dev *dev)
+{
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+ if (dev || 1) { // ????
+ disable_imp();
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+static int tavor_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ int rc;
+ unsigned char user_request;
+
+ if (pci->vendor != MELLANOX_VENDOR_ID) {
+ eprintf("");
+ return 0;
+ }
+
+ printf("\n");
+ printf("Mellanox Technologies LTD - Boot over IB implementaion\n");
+ printf("Build version = %s\n\n", build_revision);
+
+ verbose_messages = 0;
+ print_info = 0;
+ printf("Press within 3 seconds:\n");
+ printf("V - to increase verbosity\n");
+ printf("I - to print information\n");
+ if (prompt_key(3, &user_request)) {
+ if (user_request == 'V') {
+ printf("User selected verbose messages\n");
+ verbose_messages = 1;
+ }
+ else if (user_request == 'I') {
+ printf("User selected to print information\n");
+ print_info = 1;
+ }
+ }
+ printf("\n");
+
+ adjust_pci_device(pci);
+
+ nic->priv_data = NULL;
+ rc = probe_imp(pci, nic);
+
+ /* give the user a chance to look at the info */
+ if (print_info)
+ sleep(5);
+
+ if (!rc) {
+ /* store NIC parameters */
+ nic->ioaddr = pci->ioaddr & ~3;
+ nic->irqno = pci->irq;
+ /* point to NIC specific routines */
+ dev->disable = tavor_disable;
+ nic->poll = tavor_poll;
+ nic->transmit = tavor_transmit;
+ nic->irq = tavor_irq;
+
+ return 1;
+ }
+ /* else */
+ return 0;
+}
+
+static struct pci_id tavor_nics[] = {
+ PCI_ROM(0x15b3, 0x5a44, "MT23108", "MT23108 HCA driver"),
+ PCI_ROM(0x15b3, 0x6278, "MT25208", "MT25208 HCA driver"),
+};
+
+struct pci_driver tavor_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "MT23108/MT25208",
+ .probe = tavor_probe,
+ .ids = tavor_nics,
+ .id_count = sizeof(tavor_nics) / sizeof(tavor_nics[0]),
+ .class = 0,
+};
diff --git a/gpxe/src/drivers/net/mlx_ipoib/mt23108.h b/gpxe/src/drivers/net/mlx_ipoib/mt23108.h
new file mode 100644
index 00000000..1e144ee4
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/mt23108.h
@@ -0,0 +1,543 @@
+#ifndef __mt23108_h__
+#define __mt23108_h__
+
+#include "MT23108_PRM.h"
+#include "ib_mad.h"
+
+#define TAVOR_DEVICE_ID 0x5a44
+#define TAVOR_BRIDGE_DEVICE_ID 0x5a46
+#define ARTAVOR_DEVICE_ID 0x6278
+
+#define TAVOR_RESET_OFFSET 0xF0010
+
+/*
+ * Tavor specific command
+ * Only coomands that are specific to Tavor
+ * and used by the driver are listed here
+ */
+#define TAVOR_CMD_SYS_EN 0x1
+#define TAVOR_CMD_SYS_DIS 0x2
+
+#define TAVOR_CMD_WRITE_MGM 0x26
+#define TAVOR_CMD_MOD_STAT_CFG 0x34
+#define TAVOR_CMD_QUERY_DEV_LIM 0x003
+#define TAVOR_CMD_QUERY_FW 0x004
+
+/*
+ * Tavor specific event types
+ * Only event types that are specific to Tavor
+ * and are used by the driver are listed here
+ */
+#define TAVOR_IF_EV_TYPE_OVERRUN 0x0F
+
+/*
+ * EQ doorbel commands
+ */
+#define EQ_DBELL_CMD_INC_CONS_IDX 1 /* increment Consumer_indx by one */
+#define EQ_DBELL_CMD_ARM_EQ 2 /* Request notifcation for next event (Arm EQ) */
+#define EQ_DBELL_CMD_DISARM_CQ 3 /* Disarm CQ (CQ number is specified in EQ_param) */
+#define EQ_DBELL_CMD_SET_CONS_IDX 4 /* set Consumer_indx to value of EQ_param */
+#define EQ_DBELL_CMD_ALWAYS_ARM 5 /* move EQ to Always Armed state */
+
+/*
+ * CQ doorbel commands
+ */
+#define CQ_DBELL_CMD_INC_CONS_IDX 1
+#define CQ_DBELL_CMD_REQ_NOTIF_SOL_UNSOL 2
+#define CQ_DBELL_CMD_REQ_NOTIF_SOL 3
+#define CQ_DBELL_CMD_SET_CONS_IDX 4
+#define CQ_DBELL_CMD_REQ_NOTIF_MULT 5
+
+#define INPRM_BUF_SZ 0x200
+#define INPRM_BUF_ALIGN 16
+#define OUTPRM_BUF_SZ 0x200
+#define OUTPRM_BUF_ALIGN 16
+
+/*
+ * sizes of parameter blocks used in certain
+ * commands.
+ * TODO: replace them with sizeof
+ * operators of the appropriate structs
+ */
+#define SW2HW_MPT_IBUF_SZ MT_STRUCT_SIZE(tavorprm_mpt_st)
+#define SW2HW_EQ_IBUF_SZ MT_STRUCT_SIZE(tavorprm_eqc_st)
+#define INIT_IB_IBUF_SZ 0x100
+#define SW2HW_CQ_IBUF_SZ 0x40
+#define QPCTX_IBUF_SZ 0x200
+
+#define EQN 0
+#define UAR_IDX 1
+
+#define QPC_OFFSET 0
+#define CQC_OFFSET (QPC_OFFSET + 0x100000)
+#define EQPC_OFFSET (CQC_OFFSET + 0x100000)
+#define EQC_OFFSET (EQPC_OFFSET + 0x100000)
+#define MC_BASE_OFFSET (EQC_OFFSET + 0x100000)
+#define MPT_BASE_OFFSET (MC_BASE_OFFSET + 0x100000)
+#define MTT_BASE_OFFSET (MPT_BASE_OFFSET + 0x100000)
+
+#define LOG2_QPS 7
+#define LOG2_CQS 8
+#define LOG2_EQS 6
+#define LOG2_MC_ENTRY 6 /* 8 QPs per group */
+#define LOG2_MC_GROUPS 3 /* 8 groups */
+#define LOG2_MPT_ENTRIES 5
+
+#define LOG2_EQ_SZ 5
+#define LOG2_CQ_SZ 5
+
+#define NUM_PORTS 2
+
+#define EQE_OWNER_SW 0
+#define EQE_OWNER_HW 1
+
+#define OWNER_HW 1
+#define OWNER_SW 0
+
+#define POST_RCV_OFFSET 0x18
+#define POST_SND_OFFSET 0x10
+#define CQ_DBELL_OFFSET 0x20
+#define EQ_DBELL_OFFSET 0x28
+
+#define CQE_ERROR_OPCODE 0xfe
+
+#define MAX_GATHER 1 /* max gather entries used in send */
+#define MAX_SCATTER 2
+
+#define LOG2_MADS_SND_CQ_SZ LOG2_CQ_SZ
+#define LOG2_MADS_RCV_CQ_SZ LOG2_CQ_SZ
+#define LOG2_IPOIB_SND_CQ_SZ LOG2_CQ_SZ
+#define LOG2_IPOIB_RCV_CQ_SZ LOG2_CQ_SZ
+
+#define NUM_MADS_SND_CQES (1<<LOG2_MADS_SND_CQ_SZ)
+#define NUM_MADS_RCV_CQES (1<<LOG2_MADS_RCV_CQ_SZ)
+#define NUM_IPOIB_SND_CQES (1<<LOG2_IPOIB_SND_CQ_SZ)
+#define NUM_IPOIB_RCV_CQES (1<<LOG2_IPOIB_RCV_CQ_SZ)
+
+#define NUM_MADS_RCV_WQES 3
+#define NUM_IPOIB_RCV_WQES 8
+
+#if NUM_MADS_RCV_WQES > NUM_IPOIB_RCV_WQES
+#define MAX_RCV_WQES NUM_MADS_RCV_WQES
+#else
+#define MAX_RCV_WQES NUM_IPOIB_RCV_WQES
+#endif
+
+#define NUM_MADS_SND_WQES 2
+#define NUM_IPOIB_SND_WQES 2
+
+#if NUM_MADS_SND_WQES > NUM_IPOIB_SND_WQES
+#define MAX_SND_WQES NUM_MADS_SND_WQES
+#else
+#define MAX_SND_WQES NUM_IPOIB_SND_WQES
+#endif
+
+struct ib_buffers_st {
+ __u8 send_mad_buf[NUM_MADS_SND_WQES][MAD_BUF_SZ];
+ __u8 rcv_mad_buf[NUM_MADS_RCV_WQES][MAD_BUF_SZ + GRH_SIZE];
+ __u8 ipoib_rcv_buf[NUM_IPOIB_RCV_WQES][IPOIB_RCV_BUF_SZ + GRH_SIZE];
+ __u8 ipoib_rcv_grh_buf[NUM_IPOIB_RCV_WQES][IPOIB_RCV_BUF_SZ];
+ __u8 send_ipoib_buf[NUM_IPOIB_SND_WQES][IPOIB_SND_BUF_SZ];
+};
+
+struct pcidev {
+ unsigned long bar[6];
+ __u32 dev_config_space[64];
+ struct pci_device *dev;
+ __u8 bus;
+ __u8 devfn;
+};
+
+struct dev_pci_struct {
+ struct pcidev dev;
+ struct pcidev br;
+ void *cr_space;
+ void *uar;
+};
+
+struct eq_st {
+ __u8 eqn;
+ __u32 cons_idx;
+ __u32 eq_size;
+ struct eqe_t *eq_buf;
+};
+
+struct udav_st {
+ union ud_av_u *av_array;
+ __u8 udav_next_free;
+};
+
+#if 0
+struct udavtable_memory_parameters_st {
+ __u32 lkey;
+ __u32 pd:24;
+ __u32 r0:5;
+ __u32 xlation_en:1;
+ __u32 r1:2;
+} __attribute__ ((packed));
+
+struct multicast_parameters_st {
+ __u32 mc_base_addr_h;
+ __u32 mc_base_addr_l;
+ __u32 r0[2];
+ __u32 log_mc_table_entry_sz:16;
+ __u32 r1:16;
+ __u32 mc_table_hash_sz:17;
+ __u32 r2:15;
+ __u32 log_mc_table_sz:5;
+ __u32 r3:19;
+ __u32 mc_hash_fn:3;
+ __u32 r4:5;
+ __u32 r5;
+} __attribute__ ((packed));
+
+struct tpt_parameters_st {
+ __u32 mpt_base_addr_h;
+ __u32 mpt_base_addr_l;
+
+ __u32 log_mpt_sz:6;
+ __u32 r0:2;
+ __u32 pfto:5;
+ __u32 r1:3;
+ __u32 mtt_segment_size:3;
+ __u32 r2:13;
+
+ __u32 mtt_version:8;
+ __u32 r3:24;
+
+ __u32 mtt_base_addr_h;
+ __u32 mtt_base_addr_l;
+ __u32 r4[2];
+} __attribute__ ((packed));
+
+struct uar_parameters_st {
+ __u32 uar_base_addr_h;
+ __u32 uar_base_addr_l; /* 12 lsbs must be zero */
+ __u32 uar_page_sz:8;
+ __u32 r1:24;
+ __u32 r2;
+ __u32 uar_scratch_base_addr_h;
+ __u32 uar_scratch_base_addr_l;
+ __u32 r3[3];
+} __attribute__ ((packed));
+
+struct comp_event_data_st {
+ __u32 cqn:24;
+ __u32 r1:8;
+ __u32 r2[5];
+} __attribute__ ((packed));
+
+struct qp_event_data_st {
+ __u32 qpn_een:24;
+ __u32 r1:8;
+ __u32 r2;
+ __u32 r3:28;
+ __u32 e_q:1;
+ __u32 r4:3;
+ __u32 r5[3];
+} __attribute__ ((packed));
+
+struct port_state_change_event_data_st {
+ __u32 r0[2];
+ __u32 r1:28;
+ __u32 port:2;
+ __u32 r2:2;
+ __u32 r3[3];
+} __attribute__ ((packed));
+#endif
+
+struct eqe_t {
+ __u8 raw[MT_STRUCT_SIZE(tavorprm_event_queue_entry_st)];
+} __attribute__ ((packed));
+
+enum qp_state_e {
+ QP_STATE_RST = 0,
+ QP_STATE_INIT = 1,
+ QP_STATE_RTR = 2,
+ QP_STATE_RTS = 3,
+ QP_STATE_SQEr = 4,
+ QP_STATE_SQD = 5,
+ QP_STATE_ERR = 6,
+ QP_STATE_SQDING = 7,
+ QP_STATE_SUSPEND = 9
+};
+
+struct memory_pointer_st {
+ __u32 byte_count;
+ __u32 lkey;
+ __u32 local_addr_h;
+ __u32 local_addr_l;
+} __attribute__ ((packed));
+
+/* receive wqe descriptor */
+struct recv_wqe_st {
+ /* part referenced by hardware */
+ __u8 next[MT_STRUCT_SIZE(wqe_segment_next_st)];
+ __u8 control[MT_STRUCT_SIZE(wqe_segment_ctrl_recv_st)];
+ struct memory_pointer_st mpointer[MAX_SCATTER];
+} __attribute__ ((packed));
+
+struct recv_wqe_cont_st {
+ struct recv_wqe_st wqe;
+
+ struct udqp_st *qp; /* qp this wqe is used with */
+} __attribute__ ((packed));
+
+#define RECV_WQE_U_ALIGN 64
+union recv_wqe_u {
+ __u8 align[(sizeof(struct recv_wqe_cont_st) + RECV_WQE_U_ALIGN - 1) & (~(RECV_WQE_U_ALIGN - 1))]; /* this ensures proper alignment */
+ struct recv_wqe_st wqe;
+ struct recv_wqe_cont_st wqe_cont;
+} __attribute__ ((packed));
+
+struct recv_doorbell_st {
+ __u8 raw[MT_STRUCT_SIZE(tavorprm_receive_doorbell_st)];
+} __attribute__ ((packed));
+
+struct send_doorbell_st {
+ __u8 raw[MT_STRUCT_SIZE(tavorprm_send_doorbell_st)];
+} __attribute__ ((packed));
+
+struct next_control_seg_st {
+ __u8 next[MT_STRUCT_SIZE(wqe_segment_next_st)];
+ __u8 control[MT_STRUCT_SIZE(wqe_segment_ctrl_send_st)];
+} __attribute__ ((packed));
+
+struct ud_seg_st {
+ __u32 r1;
+ __u32 lkey;
+ __u32 av_add_h;
+ __u32 av_add_l;
+ __u32 r2[4];
+ __u32 dest_qp;
+ __u32 qkey;
+ __u32 r3[2];
+} __attribute__ ((packed));
+
+struct ud_send_wqe_st {
+ struct next_control_seg_st next;
+ struct ud_seg_st udseg;
+ struct memory_pointer_st mpointer[MAX_GATHER];
+} __attribute__ ((packed));
+
+struct ude_send_wqe_cont_st {
+ struct ud_send_wqe_st wqe;
+
+ struct udqp_st *qp; /* qp this wqe is used with */
+} __attribute__ ((packed));
+
+#define UD_SEND_WQE_U_ALIGN 64
+union ud_send_wqe_u {
+ __u8 align[(sizeof(struct ude_send_wqe_cont_st) + UD_SEND_WQE_U_ALIGN -
+ 1) & (~(UD_SEND_WQE_U_ALIGN - 1))];
+ struct ude_send_wqe_cont_st wqe_cont;
+ struct ud_send_wqe_st wqe;
+} __attribute__ ((packed));
+
+#define ADDRESS_VECTOR_ST_ALIGN 64
+struct address_vector_st {
+ __u8 raw[MT_STRUCT_SIZE(tavorprm_ud_address_vector_st)];
+} __attribute__ ((packed));
+
+struct ud_av_st {
+ struct address_vector_st av;
+ __u32 dest_qp; /* destination qpn */
+ __u8 next_free;
+} __attribute__ ((packed));
+
+union ud_av_u {
+ __u8 raw[(sizeof(struct ud_av_st) + ADDRESS_VECTOR_ST_ALIGN -
+ 1) & (~(ADDRESS_VECTOR_ST_ALIGN - 1))];
+ struct ud_av_st ud_av;
+} __attribute__ ((packed));
+
+union cqe_st {
+ __u8 good_cqe[MT_STRUCT_SIZE(tavorprm_completion_queue_entry_st)];
+ __u8 error_cqe[MT_STRUCT_SIZE(tavorprm_completion_with_error_st)];
+} __attribute__ ((packed));
+
+struct address_path_st {
+ __u8 raw[MT_STRUCT_SIZE(tavorprm_address_path_st)];
+};
+
+struct qp_ee_ctx_t {
+ __u8 raw[MT_STRUCT_SIZE(tavorprm_queue_pair_ee_context_entry_st)];
+} __attribute__ ((packed));
+
+struct qp_ee_state_tarnisition_st {
+ __u32 opt_param_mask;
+ __u32 r1;
+ struct qp_ee_ctx_t ctx;
+ __u32 r2[62];
+} __attribute__ ((packed));
+
+struct eq_dbell_st {
+ __u8 raw[MT_STRUCT_SIZE(tavorprm_eq_cmd_doorbell_st)];
+} __attribute__ ((packed));
+
+struct cq_dbell_st {
+ __u8 raw[MT_STRUCT_SIZE(tavorprm_cq_cmd_doorbell_st)];
+} __attribute__ ((packed));
+
+struct mad_ifc_inprm_st {
+ union mad_u mad;
+} __attribute__ ((packed));
+
+struct wqe_buf_st {
+ struct ud_send_wqe_st *sndq;
+ struct recv_wqe_st *rcvq;
+};
+
+struct mad_buffer_st {
+ void *buf; /* pointer to a 256 byte buffer */
+ __u8 owner; /* sw or hw ownership BUF_OWNER_SW or BUF_OWNER_HW */
+};
+
+struct rcv_buf_st {
+ void *buf;
+ __u8 busy;
+};
+
+struct ib_eqe_st {
+ __u8 event_type;
+ __u32 cqn;
+};
+
+struct cq_st {
+ __u32 cqn;
+ union cqe_st *cq_buf;
+ __u32 cons_idx;
+ __u8 num_cqes;
+};
+
+struct udqp_st {
+ /* cq used by this QP */
+ struct cq_st snd_cq;
+ struct cq_st rcv_cq;
+
+ /* QP related data */
+ __u32 qpn; /* QP number */
+
+ __u32 qkey;
+
+ __u8 recv_wqe_cur_free;
+ __u8 recv_wqe_alloc_idx;
+ __u8 max_recv_wqes;
+ void *rcv_bufs[MAX_RCV_WQES];
+ union recv_wqe_u *rcv_wq; /* receive work queue */
+ struct recv_wqe_st *last_posted_rcv_wqe;
+
+ __u8 snd_wqe_cur_free;
+ __u8 snd_wqe_alloc_idx;
+ __u8 max_snd_wqes;
+ void *snd_bufs[MAX_SND_WQES];
+ __u16 send_buf_sz;
+ __u16 rcv_buf_sz;
+ union ud_send_wqe_u *snd_wq; /* send work queue */
+ struct ud_send_wqe_st *last_posted_snd_wqe;
+};
+
+struct device_ib_data_st {
+ __u32 mkey;
+ __u32 pd;
+ __u8 port;
+ __u32 qkey;
+ struct eq_st eq;
+ struct udav_st udav;
+ struct udqp_st mads_qp;
+ struct udqp_st ipoib_qp;
+ void *error_buf_addr;
+ __u32 error_buf_size;
+};
+
+
+
+struct query_fw_st {
+ __u16 fw_rev_major;
+ __u16 fw_rev_minor;
+ __u16 fw_rev_subminor;
+ __u32 error_buf_start_h;
+ __u32 error_buf_start_l;
+ __u32 error_buf_size;
+};
+
+
+struct dev_lim_st {
+ __u8 log2_rsvd_qps;
+ __u16 qpc_entry_sz;
+
+ __u8 log2_rsvd_srqs;
+ __u16 srq_entry_sz;
+
+ __u8 log2_rsvd_ees;
+ __u16 eec_entry_sz;
+
+ __u8 log2_rsvd_cqs;
+ __u16 cqc_entry_sz;
+
+ __u8 log2_rsvd_mtts;
+ __u16 mtt_entry_sz;
+
+ __u8 log2_rsvd_mrws;
+ __u16 mpt_entry_sz;
+
+ __u16 eqc_entry_sz;
+};
+
+struct init_hca_st {
+ __u32 qpc_base_addr_h;
+ __u32 qpc_base_addr_l;
+ __u8 log_num_of_qp;
+
+ __u32 eec_base_addr_h;
+ __u32 eec_base_addr_l;
+ __u8 log_num_of_ee;
+
+ __u32 srqc_base_addr_h;
+ __u32 srqc_base_addr_l;
+ __u8 log_num_of_srq;
+
+ __u32 cqc_base_addr_h;
+ __u32 cqc_base_addr_l;
+ __u8 log_num_of_cq;
+
+ __u32 eqpc_base_addr_h;
+ __u32 eqpc_base_addr_l;
+
+ __u32 eeec_base_addr_h;
+ __u32 eeec_base_addr_l;
+
+ __u32 eqc_base_addr_h;
+ __u32 eqc_base_addr_l;
+ __u8 log_num_of_eq;
+
+ __u32 rdb_base_addr_h;
+ __u32 rdb_base_addr_l;
+
+ __u32 mc_base_addr_h;
+ __u32 mc_base_addr_l;
+ __u16 log_mc_table_entry_sz;
+ __u32 mc_table_hash_sz;
+ __u8 log_mc_table_sz;
+
+ __u32 mpt_base_addr_h;
+ __u32 mpt_base_addr_l;
+ __u8 log_mpt_sz;
+ __u32 mtt_base_addr_h;
+ __u32 mtt_base_addr_l;
+ __u8 log_max_uars;
+};
+
+static int create_udqp(struct udqp_st *qp);
+static int destroy_udqp(struct udqp_st *qp);
+static void *get_send_wqe_buf(void *wqe, __u8 index);
+static void *get_rcv_wqe_buf(void *wqe, __u8 index);
+
+static struct recv_wqe_st *alloc_rcv_wqe(struct udqp_st *qp);
+static int free_wqe(void *wqe);
+static int poll_cq(void *cqh, union cqe_st *cqe_p, __u8 * num_cqes);
+static int poll_eq(struct ib_eqe_st *ib_eqe_p, __u8 * num_eqes);
+static int post_rcv_buf(struct udqp_st *qp, struct recv_wqe_st *rcv_wqe);
+static __u32 dev_get_qpn(void *qph);
+
+#endif /* __mt23108_h__ */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/mt23108_imp.c b/gpxe/src/drivers/net/mlx_ipoib/mt23108_imp.c
new file mode 100644
index 00000000..bb2383c5
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/mt23108_imp.c
@@ -0,0 +1,229 @@
+typedef uint32_t __u32;
+typedef uint16_t __u16;
+typedef uint8_t __u8;
+
+static int verbose_messages=0;
+static int print_info=0;
+static int fatal_condition=0;
+static int fw_fatal;
+
+#define tprintf(fmt, a...) \
+ do { \
+ if ( verbose_messages ) { \
+ printf("%s:%d: " fmt "\n", __func__, __LINE__, ##a); \
+ } \
+ } \
+ while(0)
+
+#define eprintf(fmt, a...) \
+ printf("%s:%d: " fmt "\n", __func__, __LINE__, ##a)
+
+static void cpu_to_be_buf(void *buf, int size)
+{
+ int dw_sz = size >> 2, i;
+
+ for (i = 0; i < dw_sz; ++i) {
+ ((__u32 *) buf)[i] = cpu_to_be32(((__u32 *) buf)[i]);
+ }
+}
+
+static void be_to_cpu_buf(void *buf, int size)
+{
+ int dw_sz = size >> 2, i;
+ u32 *p = buf;
+
+ for (i = 0; i < dw_sz; ++i) {
+ p[i] = be32_to_cpu(p[i]);
+ }
+}
+
+#include "cmdif_mt23108.c"
+#include "cmdif_comm.c"
+#include "ib_mt23108.c"
+#include "ib_mad.c"
+#include "ib_driver.c"
+#include "ipoib.c"
+
+static int probe_imp(struct pci_device *pci, struct nic *nic)
+{
+ int rc;
+
+ if (0 && nic) { /* just to supress warning */
+ return 0;
+ }
+
+ fatal_condition= 0;
+ fw_fatal= 0;
+
+ tprintf("");
+ rc = ipoib_init(pci);
+ if (rc)
+ return rc;
+
+ tprintf("");
+
+ return rc;
+}
+
+static int disable_imp(void)
+{
+ int rc;
+
+ rc = ipoib_close(fw_fatal);
+
+ return rc;
+}
+
+static int transmit_imp(const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ const char *packet, /* Packet */
+ unsigned int size)
+{ /* size */
+ int rc;
+
+ if (fatal_condition) {
+ /* since the transmit function does not return a value
+ we return success but do nothing to suppress error messages */
+ return 0;
+ }
+
+ rc = ipoib_send_packet(dest, type, packet, size);
+ if (rc) {
+ printf("*** ERROR IN SEND FLOW ***\n");
+ printf("restarting Etherboot\n");
+ sleep(1);
+ longjmp(restart_etherboot, -1);
+ /* we should not be here ... */
+ return -1;
+ }
+
+ return rc;
+}
+
+static void hd(void *where, int n)
+{
+ int i;
+
+ while (n > 0) {
+ printf("%X ", where);
+ for (i = 0; i < ((n > 16) ? 16 : n); i++)
+ printf(" %hhX", ((char *)where)[i]);
+ printf("\n");
+ n -= 16;
+ where += 16;
+ }
+}
+
+static int poll_imp(struct nic *nic, int retrieve, unsigned int *size_p)
+{
+ static char packet[2048];
+ static char *last_packet_p = NULL;
+ static unsigned long last_packet_size;
+ char *packet_p;
+ const int eth_header_len = 14;
+ unsigned int packet_len;
+ int is_bcast = 0;
+ __u16 prot, *ptr;
+ int rc;
+
+ if (0 && nic) { /* just to supress warning */
+ return -1;
+ }
+
+ if (fatal_condition) {
+ *size_p = 0;
+ return 0;
+ }
+
+ if (poll_error_buf()) {
+ fatal_condition= 1;
+ fw_fatal= 1;
+ printf("\n *** DEVICE FATAL ERROR ***\n");
+ goto fatal_handling;
+ }
+ else if (drain_eq()) {
+ fatal_condition= 1;
+ printf("\n *** FATAL ERROR ***\n");
+ goto fatal_handling;
+ }
+
+
+ if (retrieve) {
+ /* we actually want to read the packet */
+ if (last_packet_p) {
+ eprintf("");
+ /* there is already a packet that was previously read */
+ memcpy(nic->packet, last_packet_p, last_packet_size);
+ *size_p = last_packet_size;
+ last_packet_p = NULL;
+ return 0;
+ }
+ packet_p = nic->packet;
+ } else {
+ /* we don't want to read the packet,
+ just know if there is one. so we
+ read the packet to a local buffer and
+ we will return that buffer when the ip layer wants
+ another packet */
+ if (last_packet_p) {
+ /* there is already a packet that
+ was not consumend */
+ eprintf("overflow receive packets");
+ return -1;
+ }
+ packet_p = packet;
+ }
+
+ rc = ipoib_read_packet(&prot, packet_p + eth_header_len, &packet_len,
+ &is_bcast);
+ if (rc) {
+ printf("*** FATAL IN RECEIVE FLOW ****\n");
+ goto fatal_handling;
+ }
+
+ if (packet_len == 0) {
+ *size_p = 0;
+ return 0;
+ }
+
+ if (is_bcast) {
+ int i;
+ for (i = 0; i < 6; ++i) {
+ packet_p[i] = 0xff;
+ }
+ } else {
+ packet_p[0] = MLX_ETH_BYTE0;
+ packet_p[1] = MLX_ETH_BYTE1;
+ packet_p[2] = MLX_ETH_BYTE2;
+ packet_p[3] = 0;
+ packet_p[4] = 0;
+ packet_p[5] = 0;
+ }
+
+ memset(packet_p + 6, 0, 6);
+
+ ptr = (__u16 *) (packet_p + 12);
+ *ptr = htons(prot);
+
+ if (!retrieve) {
+ last_packet_p = packet;
+ last_packet_size = packet_len + eth_header_len;
+ *size_p = 0;
+ }
+
+ *size_p = packet_len + eth_header_len;
+ tprintf("packet size=%d, prot=%x\n", *size_p, prot);
+ if (0) {
+ hd(nic->packet, 42);
+ }
+
+ return 0;
+
+fatal_handling:
+ printf("restarting Etherboot\n");
+ sleep(1);
+ longjmp(restart_etherboot, -1);
+ /* we should not be here ... */
+ return -1;
+
+}
diff --git a/gpxe/src/drivers/net/mlx_ipoib/mt25218.c b/gpxe/src/drivers/net/mlx_ipoib/mt25218.c
new file mode 100644
index 00000000..a603cdeb
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/mt25218.c
@@ -0,0 +1,245 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Skeleton NIC driver for Etherboot
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include <gpxe/pci.h>
+/* to get the ISA support functions, if this is an ISA NIC */
+#include <gpxe/isa.h>
+
+#include "mt_version.c"
+#include "mt25218_imp.c"
+
+/* NIC specific static variables go here */
+
+int prompt_key(int secs, unsigned char *ch_p)
+{
+ unsigned long tmo;
+ unsigned char ch;
+
+ for (tmo = currticks() + secs * TICKS_PER_SEC; currticks() < tmo;) {
+ if (iskey()) {
+ ch = getchar();
+ /* toupper does not work ... */
+ if (ch == 'v')
+ ch = 'V';
+ if (ch == 'i')
+ ch = 'I';
+ if ((ch=='V') || (ch=='I')) {
+ *ch_p = ch;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**************************************************************************
+IRQ - handle interrupts
+***************************************************************************/
+static void mt25218_irq(struct nic *nic, irq_action_t action)
+{
+ /* This routine is somewhat optional. Etherboot itself
+ * doesn't use interrupts, but they are required under some
+ * circumstances when we're acting as a PXE stack.
+ *
+ * If you don't implement this routine, the only effect will
+ * be that your driver cannot be used via Etherboot's UNDI
+ * API. This won't affect programs that use only the UDP
+ * portion of the PXE API, such as pxelinux.
+ */
+
+ if (0) {
+ nic = NULL;
+ }
+ switch (action) {
+ case DISABLE:
+ case ENABLE:
+ /* Set receive interrupt enabled/disabled state */
+ /*
+ outb ( action == ENABLE ? IntrMaskEnabled : IntrMaskDisabled,
+ nic->ioaddr + IntrMaskRegister );
+ */
+ break;
+ case FORCE:
+ /* Force NIC to generate a receive interrupt */
+ /*
+ outb ( ForceInterrupt, nic->ioaddr + IntrForceRegister );
+ */
+ break;
+ }
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int mt25218_poll(struct nic *nic, int retrieve)
+{
+ /* Work out whether or not there's an ethernet packet ready to
+ * read. Return 0 if not.
+ */
+ /*
+ if ( ! <packet_ready> ) return 0;
+ */
+
+ /* retrieve==0 indicates that we are just checking for the
+ * presence of a packet but don't want to read it just yet.
+ */
+ /*
+ if ( ! retrieve ) return 1;
+ */
+
+ /* Copy data to nic->packet. Data should include the
+ * link-layer header (dest MAC, source MAC, type).
+ * Store length of data in nic->packetlen.
+ * Return true to indicate a packet has been read.
+ */
+ /*
+ nic->packetlen = <packet_length>;
+ memcpy ( nic->packet, <packet_data>, <packet_length> );
+ return 1;
+ */
+ unsigned int size;
+ int rc;
+ rc = poll_imp(nic, retrieve, &size);
+ if (rc) {
+ return 0;
+ }
+
+ if (size == 0) {
+ return 0;
+ }
+
+ nic->packetlen = size;
+
+ return 1;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void mt25218_transmit(struct nic *nic, const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ unsigned int size, /* size */
+ const char *packet)
+{ /* Packet */
+ int rc;
+
+ /* Transmit packet to dest MAC address. You will need to
+ * construct the link-layer header (dest MAC, source MAC,
+ * type).
+ */
+ if (nic) {
+ rc = transmit_imp(dest, type, packet, size);
+ if (rc)
+ eprintf("tranmit error");
+ }
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void mt25218_disable(struct dev *dev)
+{
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+ if (dev || 1) { // ????
+ disable_imp();
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+static int mt25218_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ int rc;
+ unsigned char user_request;
+
+ if (pci->vendor != MELLANOX_VENDOR_ID) {
+ eprintf("");
+ return 0;
+ }
+
+ printf("\n");
+ printf("Mellanox Technologies LTD - Boot over IB implementaion\n");
+ printf("Build version = %s\n\n", build_revision);
+
+ verbose_messages = 0;
+ print_info = 0;
+ printf("Press within 3 seconds:\n");
+ printf("V - to increase verbosity\n");
+ printf("I - to print information\n");
+ if (prompt_key(3, &user_request)) {
+ if (user_request == 'V') {
+ printf("User selected verbose messages\n");
+ verbose_messages = 1;
+ }
+ else if (user_request == 'I') {
+ printf("User selected to print information\n");
+ print_info = 1;
+ }
+ }
+ printf("\n");
+
+ adjust_pci_device(pci);
+
+ nic->priv_data = NULL;
+ rc = probe_imp(pci, nic);
+
+ /* give the user a chance to look at the info */
+ if (print_info)
+ sleep(5);
+
+ if (!rc) {
+ /* store NIC parameters */
+ nic->ioaddr = pci->ioaddr & ~3;
+ nic->irqno = pci->irq;
+ /* point to NIC specific routines */
+ dev->disable = mt25218_disable;
+ nic->poll = mt25218_poll;
+ nic->transmit = mt25218_transmit;
+ nic->irq = mt25218_irq;
+
+ return 1;
+ }
+ /* else */
+ return 0;
+}
+
+static struct pci_id mt25218_nics[] = {
+ PCI_ROM(0x15b3, 0x6282, "MT25218", "MT25218 HCA driver"),
+ PCI_ROM(0x15b3, 0x6274, "MT25204", "MT25204 HCA driver"),
+};
+
+struct pci_driver mt25218_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "MT25218",
+ .probe = mt25218_probe,
+ .ids = mt25218_nics,
+ .id_count = sizeof(mt25218_nics) / sizeof(mt25218_nics[0]),
+ .class = 0,
+};
diff --git a/gpxe/src/drivers/net/mlx_ipoib/mt25218.h b/gpxe/src/drivers/net/mlx_ipoib/mt25218.h
new file mode 100644
index 00000000..15a3feaf
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/mt25218.h
@@ -0,0 +1,546 @@
+#ifndef __mt25218_h__
+#define __mt25218_h__
+
+#include "MT25218_PRM.h"
+#include "ib_mad.h"
+
+#define TAVOR_DEVICE_ID 0x5a44
+#define TAVOR_BRIDGE_DEVICE_ID 0x5a46
+#define ARTAVOR_DEVICE_ID 0x6278
+
+#define MEMFREE_RESET_OFFSET 0xF0010
+
+#define INVALID_WQE_LKEY 0x00000100
+
+/*
+ * memfree specific command
+ *
+ */
+#define MEMFREE_CMD_QUERY_ADAPTER 0x006
+#define MEMFREE_CMD_WRITE_MGM 0x026
+#define MEMFREE_CMD_MOD_STAT_CFG 0x034
+#define MEMFREE_CMD_QUERY_FW 0x004
+#define MEMFREE_CMD_ENABLE_LAM 0xff8
+#define MEMFREE_CMD_MAP_FA 0xfff
+#define MEMFREE_CMD_UNMAP_FA 0xffe
+#define MEMFREE_CMD_RUN_FW 0xff6
+#define MEMFREE_CMD_SET_ICM_SIZE 0xffd
+#define MEMFREE_CMD_MAP_ICM_AUX 0xffc
+#define MEMFREE_CMD_MAP_ICM 0xffa
+#define MEMFREE_CMD_QUERY_DEV_LIM 0x003
+
+/*
+ * Tavor specific event types
+ * Only event types that are specific to Tavor
+ * and are used by the driver are listed here
+ */
+#define TAVOR_IF_EV_TYPE_OVERRUN 0x0F
+
+/*
+ * EQ doorbel commands
+ */
+#define EQ_DBELL_CMD_INC_CONS_IDX 1 /* increment Consumer_indx by one */
+#define EQ_DBELL_CMD_ARM_EQ 2 /* Request notifcation for next event (Arm EQ) */
+#define EQ_DBELL_CMD_DISARM_CQ 3 /* Disarm CQ (CQ number is specified in EQ_param) */
+#define EQ_DBELL_CMD_SET_CONS_IDX 4 /* set Consumer_indx to value of EQ_param */
+#define EQ_DBELL_CMD_ALWAYS_ARM 5 /* move EQ to Always Armed state */
+
+/*
+ * CQ doorbel commands
+ */
+#define CQ_DBELL_CMD_INC_CONS_IDX 1
+#define CQ_DBELL_CMD_REQ_NOTIF_SOL_UNSOL 2
+#define CQ_DBELL_CMD_REQ_NOTIF_SOL 3
+#define CQ_DBELL_CMD_SET_CONS_IDX 4
+#define CQ_DBELL_CMD_REQ_NOTIF_MULT 5
+
+#define INPRM_BUF_SZ 4096
+#define INPRM_BUF_ALIGN 4096
+#define OUTPRM_BUF_SZ 4096
+#define OUTPRM_BUF_ALIGN 4096
+
+/*
+ * sizes of parameter blocks used in certain
+ * commands.
+ * TODO: replace them with sizeof
+ * operators of the appropriate structs
+ */
+#define SW2HW_MPT_IBUF_SZ MT_STRUCT_SIZE(arbelprm_mpt_st)
+#define SW2HW_EQ_IBUF_SZ MT_STRUCT_SIZE(arbelprm_eqc_st)
+#define INIT_IB_IBUF_SZ MT_STRUCT_SIZE(arbelprm_init_ib_st)
+#define SW2HW_CQ_IBUF_SZ MT_STRUCT_SIZE(arbelprm_completion_queue_context_st)
+#define QPCTX_IBUF_SZ MT_STRUCT_SIZE(arbelprm_queue_pair_ee_context_entry_st)
+
+#define EQN 0
+#define UAR_IDX 1
+
+#define QPC_OFFSET 0
+#define CQC_OFFSET (QPC_OFFSET + 0x100000)
+#define EQPC_OFFSET (CQC_OFFSET + 0x100000)
+#define EQC_OFFSET (EQPC_OFFSET + 0x100000)
+#define MC_BASE_OFFSET (EQC_OFFSET + 0x100000)
+#define MPT_BASE_OFFSET (MC_BASE_OFFSET + 0x100000)
+#define MTT_BASE_OFFSET (MPT_BASE_OFFSET + 0x100000)
+
+#define LOG2_QPS 7
+#define LOG2_CQS 8
+#define LOG2_EQS 6
+#define LOG2_MC_ENTRY 6 /* 8 QPs per group */
+#define LOG2_MC_GROUPS 3 /* 8 groups */
+#define LOG2_MPT_ENTRIES 5
+
+#define LOG2_EQ_SZ 5
+#define LOG2_CQ_SZ 5
+
+#define NUM_PORTS 2
+
+#define EQE_OWNER_OFFSET 31
+#define EQE_OWNER_VAL_HW 0x80
+
+#define CQE_OWNER_OFFSET 31
+#define CQE_OWNER_VAL_HW 0x80
+
+#define POST_RCV_OFFSET 0x18
+#define POST_SND_OFFSET 0x10
+#define CQ_DBELL_OFFSET 0x20
+#define EQ_DBELL_OFFSET 0x28
+
+#define CQE_ERROR_OPCODE 0xfe
+
+#define OWNER_HW 1
+#define OWNER_SW 0
+
+#define MAX_GATHER 1 /* max gather entries used in send */
+#define MAX_SCATTER 2
+
+#define LOG2_MADS_SND_CQ_SZ LOG2_CQ_SZ
+#define LOG2_MADS_RCV_CQ_SZ LOG2_CQ_SZ
+#define LOG2_IPOIB_SND_CQ_SZ LOG2_CQ_SZ
+#define LOG2_IPOIB_RCV_CQ_SZ LOG2_CQ_SZ
+
+#define NUM_MADS_SND_CQES (1<<LOG2_MADS_SND_CQ_SZ)
+#define NUM_MADS_RCV_CQES (1<<LOG2_MADS_RCV_CQ_SZ)
+#define NUM_IPOIB_SND_CQES (1<<LOG2_IPOIB_SND_CQ_SZ)
+#define NUM_IPOIB_RCV_CQES (1<<LOG2_IPOIB_RCV_CQ_SZ)
+
+/* work queues must be 2^n size with n=0.. */
+#define NUM_MADS_RCV_WQES (1<<1)
+#define NUM_IPOIB_RCV_WQES (1<<1)
+
+#if NUM_MADS_RCV_WQES > NUM_IPOIB_RCV_WQES
+#define MAX_RCV_WQES NUM_MADS_RCV_WQES
+#else
+#define MAX_RCV_WQES NUM_IPOIB_RCV_WQES
+#endif
+
+#define NUM_MADS_SND_WQES (1<<1)
+#define NUM_IPOIB_SND_WQES (1<<1)
+
+#if NUM_MADS_SND_WQES > NUM_IPOIB_SND_WQES
+#define MAX_SND_WQES NUM_MADS_SND_WQES
+#else
+#define MAX_SND_WQES NUM_IPOIB_SND_WQES
+#endif
+
+/* uar context indexes */
+enum {
+ MADS_RCV_CQ_ARM_DB_IDX,
+ MADS_SND_CQ_ARM_DB_IDX,
+ IPOIB_RCV_CQ_ARM_DB_IDX,
+ IPOIB_SND_CQ_ARM_DB_IDX,
+ MADS_SND_QP_DB_IDX,
+ IPOIB_SND_QP_DB_IDX,
+ GROUP_SEP_IDX,
+ START_UNMAPPED_DB_IDX,
+ /* --------------------------
+ unmapped doorbell records
+ -------------------------- */
+ END_UNMAPPED_DB_IDX = 505,
+ MADS_RCV_QP_DB_IDX = 506,
+ IPOIB_RCV_QP_DB_IDX = 507,
+ MADS_RCV_CQ_CI_DB_IDX = 508,
+ MADS_SND_CQ_CI_DB_IDX = 509,
+ IPOIB_RCV_CQ_CI_DB_IDX = 510,
+ IPOIB_SND_CQ_CI_DB_IDX = 511
+};
+
+/* uar resources types */
+enum {
+ UAR_RES_INVALID = 0x0, /* Invalid (not allocated) DoorBell record */
+ UAR_RES_CQ_SET_CI = 0x1, /* CQ SET_CI DoorBell record */
+ UAR_RES_CQ_ARM = 0x2, /* CQ ARM DoorBell record */
+ UAR_RES_SQ_DBELL = 0x3, /* Send Queue DoorBell record */
+ UAR_RES_RQ_DBELL = 0x4, /* Receive Queue DoorBell record */
+ UAR_RES_SRQ_DBELL = 0x5, /* Shared Receive Queue DoorBell record */
+ UAR_RES_GROUP_SEP = 0x7 /* Group Separator record */
+};
+
+enum {
+ TS_RC,
+ TS_UC,
+ TS_RD,
+ TS_UD,
+ TS_MLX
+};
+
+enum {
+ PM_STATE_ARMED = 0,
+ PM_STATE_REARM = 1,
+ PM_STATE_MIGRATED = 3
+};
+
+enum {
+ DOORBEL_RES_SQ = 3,
+ DOORBEL_RES_RQ = 4,
+ DOORBEL_RES_SRQ = 5
+};
+
+struct ib_buffers_st {
+ __u8 send_mad_buf[NUM_MADS_SND_WQES][MAD_BUF_SZ];
+ __u8 rcv_mad_buf[NUM_MADS_RCV_WQES][MAD_BUF_SZ + GRH_SIZE];
+ __u8 ipoib_rcv_buf[NUM_IPOIB_RCV_WQES][IPOIB_RCV_BUF_SZ + GRH_SIZE];
+ __u8 ipoib_rcv_grh_buf[NUM_IPOIB_RCV_WQES][IPOIB_RCV_BUF_SZ];
+ __u8 send_ipoib_buf[NUM_IPOIB_SND_WQES][IPOIB_SND_BUF_SZ];
+};
+
+struct pcidev {
+ unsigned long bar[6];
+ __u32 dev_config_space[64];
+ struct pci_device *dev;
+ __u8 bus;
+ __u8 devfn;
+};
+
+struct dev_pci_struct {
+ struct pcidev dev;
+ struct pcidev br;
+ void *cr_space;
+ void *uar;
+};
+
+struct eq_st {
+ __u8 eqn;
+ __u32 cons_counter;
+ __u32 eq_size;
+ void *ci_base_base_addr;
+ struct eqe_t *eq_buf;
+};
+
+struct eqe_t {
+ __u8 raw[MT_STRUCT_SIZE(arbelprm_event_queue_entry_st)];
+} __attribute__ ((packed));
+
+enum qp_state_e {
+ QP_STATE_RST = 0,
+ QP_STATE_INIT = 1,
+ QP_STATE_RTR = 2,
+ QP_STATE_RTS = 3,
+ QP_STATE_SQEr = 4,
+ QP_STATE_SQD = 5,
+ QP_STATE_ERR = 6,
+ QP_STATE_SQDING = 7,
+ QP_STATE_SUSPEND = 9
+};
+
+struct memory_pointer_st {
+ __u32 byte_count;
+ __u32 lkey;
+ __u32 local_addr_h;
+ __u32 local_addr_l;
+} __attribute__ ((packed));
+
+/* receive wqe descriptor */
+struct recv_wqe_st {
+ /* part referenced by hardware */
+ __u8 control[MT_STRUCT_SIZE(arbelprm_wqe_segment_ctrl_recv_st)];
+ struct memory_pointer_st mpointer[MAX_SCATTER];
+} __attribute__ ((packed));
+
+struct recv_wqe_cont_st {
+ struct recv_wqe_st wqe;
+
+ struct udqp_st *qp; /* qp this wqe is used with */
+} __attribute__ ((packed));
+
+#define RECV_WQE_U_ALIGN 64
+union recv_wqe_u {
+ __u8 align[RECV_WQE_U_ALIGN]; /* this ensures proper alignment */
+ struct recv_wqe_st wqe;
+ struct recv_wqe_cont_st wqe_cont;
+} __attribute__ ((packed));
+
+struct send_doorbell_st {
+ __u8 raw[MT_STRUCT_SIZE(arbelprm_send_doorbell_st)];
+} __attribute__ ((packed));
+
+struct next_control_seg_st {
+ __u8 next[MT_STRUCT_SIZE(arbelprm_wqe_segment_next_st)];
+ __u8 control[MT_STRUCT_SIZE(arbelprm_wqe_segment_ctrl_send_st)];
+} __attribute__ ((packed));
+
+struct ud_seg_st {
+ __u8 av[MT_STRUCT_SIZE(arbelprm_wqe_segment_ud_st)];
+} __attribute__ ((packed));
+
+struct ud_send_wqe_st {
+ struct next_control_seg_st next; /* 16 bytes */
+ struct ud_seg_st udseg; /* 48 bytes */
+ struct memory_pointer_st mpointer[MAX_GATHER]; /* 16 * MAX_GATHER bytes */
+} __attribute__ ((packed));
+
+struct ude_send_wqe_cont_st {
+ struct ud_send_wqe_st wqe;
+
+ struct udqp_st *qp; /* qp this wqe is used with */
+} __attribute__ ((packed));
+
+#define UD_SEND_WQE_U_ALIGN 128
+union ud_send_wqe_u {
+ __u8 align[UD_SEND_WQE_U_ALIGN];
+ struct ude_send_wqe_cont_st wqe_cont;
+} __attribute__ ((packed));
+
+struct address_vector_st {
+ __u8 raw[MT_STRUCT_SIZE(arbelprm_ud_address_vector_st)];
+} __attribute__ ((packed));
+
+struct ud_av_st {
+ struct address_vector_st av;
+ __u32 dest_qp; /* destination qpn */
+ __u32 qkey;
+ __u8 next_free;
+} __attribute__ ((packed));
+
+union ud_av_u {
+ struct ud_av_st ud_av;
+} __attribute__ ((packed));
+
+struct udav_st {
+ union ud_av_u av_array[NUM_AVS];
+ __u8 udav_next_free;
+};
+
+union cqe_st {
+ __u8 good_cqe[MT_STRUCT_SIZE(arbelprm_completion_queue_entry_st)];
+ __u8 error_cqe[MT_STRUCT_SIZE(arbelprm_completion_with_error_st)];
+} __attribute__ ((packed));
+
+struct qp_ee_ctx_t {
+ __u8 raw[MT_STRUCT_SIZE(arbelprm_queue_pair_ee_context_entry_st)];
+} __attribute__ ((packed));
+
+struct qp_ee_state_tarnisition_st {
+ __u32 opt_param_mask;
+ __u32 r1;
+ struct qp_ee_ctx_t ctx;
+ __u32 r2[62];
+} __attribute__ ((packed));
+
+struct cq_dbell_st {
+ __u8 raw[MT_STRUCT_SIZE(arbelprm_cq_cmd_doorbell_st)];
+} __attribute__ ((packed));
+
+struct mad_ifc_inprm_st {
+ union mad_u mad;
+} __attribute__ ((packed));
+
+struct wqe_buf_st {
+ struct ud_send_wqe_st *sndq;
+ struct recv_wqe_st *rcvq;
+};
+
+struct mad_buffer_st {
+ void *buf; /* pointer to a 256 byte buffer */
+ __u8 owner; /* sw or hw ownership BUF_OWNER_SW or BUF_OWNER_HW */
+};
+
+struct rcv_buf_st {
+ void *buf;
+ __u8 busy;
+};
+
+struct ib_eqe_st {
+ __u8 event_type;
+ __u32 cqn;
+};
+
+struct cq_st {
+ __u32 cqn;
+ union cqe_st *cq_buf;
+ __u32 cons_counter; /* consuner counter */
+ __u8 num_cqes;
+ __u32 arm_db_ctx_idx;
+ void *arm_db_ctx_pointer;
+ __u32 ci_db_ctx_idx;
+ void *ci_db_ctx_pointer;
+};
+
+struct udqp_st {
+ /* cq used by this QP */
+ struct cq_st snd_cq;
+ struct cq_st rcv_cq;
+
+ /* QP related data */
+ __u32 qpn; /* QP number */
+
+ __u32 qkey;
+
+ __u8 recv_wqe_cur_free;
+ __u8 recv_wqe_alloc_idx;
+ __u8 max_recv_wqes;
+ void *rcv_bufs[MAX_RCV_WQES];
+ union recv_wqe_u *rcv_wq; /* receive work queue */
+ struct recv_wqe_st *last_posted_rcv_wqe;
+
+ __u8 snd_wqe_cur_free;
+ __u8 snd_wqe_alloc_idx;
+ __u8 max_snd_wqes;
+ void *snd_bufs[MAX_SND_WQES];
+ __u16 send_buf_sz;
+ __u16 rcv_buf_sz;
+ union ud_send_wqe_u *snd_wq; /* send work queue */
+ struct ud_send_wqe_st *last_posted_snd_wqe;
+ /* pointers to uar context entries */
+ void *send_uar_context;
+ __u16 post_send_counter;
+ void *rcv_uar_context;
+ __u16 post_rcv_counter;
+ __u32 snd_db_record_index;
+ __u32 rcv_db_record_index;
+};
+
+struct device_ib_data_st {
+ __u32 mkey;
+ __u32 pd;
+ __u8 port;
+ __u32 qkey;
+ struct eq_st eq;
+ struct udav_st udav;
+ struct udqp_st mads_qp;
+ struct udqp_st ipoib_qp;
+ void *clr_int_addr;
+ __u32 clr_int_data;
+ __u32 uar_idx;
+ void *uar_context_base;
+ void *error_buf_addr;
+ __u32 error_buf_size;
+};
+
+struct query_fw_st {
+ __u16 fw_rev_major;
+ __u16 fw_rev_minor;
+ __u16 fw_rev_subminor;
+ __u32 error_buf_start_h;
+ __u32 error_buf_start_l;
+ __u32 error_buf_size;
+ __u32 fw_pages;
+ struct addr_64_st eq_ci_table;
+ struct addr_64_st clear_int_addr;
+};
+
+struct query_adapter_st {
+ __u8 intapin;
+};
+
+struct vpm_entry_st {
+ __u32 va_h;
+ __u32 va_l;
+ __u32 pa_h;
+ __u32 pa_l;
+ __u8 log2_size;
+};
+
+#define MAX_VPM_PER_CALL 1
+
+struct map_icm_st {
+ __u32 num_vpm;
+ struct vpm_entry_st vpm_arr[MAX_VPM_PER_CALL];
+};
+
+struct init_hca_st {
+ __u32 qpc_base_addr_h;
+ __u32 qpc_base_addr_l;
+ __u8 log_num_of_qp;
+
+ __u32 eec_base_addr_h;
+ __u32 eec_base_addr_l;
+ __u8 log_num_of_ee;
+
+ __u32 srqc_base_addr_h;
+ __u32 srqc_base_addr_l;
+ __u8 log_num_of_srq;
+
+ __u32 cqc_base_addr_h;
+ __u32 cqc_base_addr_l;
+ __u8 log_num_of_cq;
+
+ __u32 eqpc_base_addr_h;
+ __u32 eqpc_base_addr_l;
+
+ __u32 eeec_base_addr_h;
+ __u32 eeec_base_addr_l;
+
+ __u32 eqc_base_addr_h;
+ __u32 eqc_base_addr_l;
+ __u8 log_num_of_eq;
+
+ __u32 rdb_base_addr_h;
+ __u32 rdb_base_addr_l;
+
+ __u32 mc_base_addr_h;
+ __u32 mc_base_addr_l;
+ __u16 log_mc_table_entry_sz;
+ __u32 mc_table_hash_sz;
+ __u8 log_mc_table_sz;
+
+ __u32 mpt_base_addr_h;
+ __u32 mpt_base_addr_l;
+ __u8 log_mpt_sz;
+ __u32 mtt_base_addr_h;
+ __u32 mtt_base_addr_l;
+ __u8 log_max_uars;
+};
+
+struct dev_lim_st {
+ __u8 log2_rsvd_qps;
+ __u16 qpc_entry_sz;
+
+ __u8 log2_rsvd_srqs;
+ __u16 srq_entry_sz;
+
+ __u8 log2_rsvd_ees;
+ __u16 eec_entry_sz;
+
+ __u8 log2_rsvd_cqs;
+ __u16 cqc_entry_sz;
+
+ __u8 log2_rsvd_mtts;
+ __u16 mtt_entry_sz;
+
+ __u8 log2_rsvd_mrws;
+ __u16 mpt_entry_sz;
+
+ __u8 log2_rsvd_rdbs;
+
+ __u16 eqc_entry_sz;
+
+ __u32 max_icm_size_l;
+ __u32 max_icm_size_h;
+
+ __u8 uar_sz;
+ __u8 num_rsvd_uars;
+};
+
+static int create_udqp(struct udqp_st *qp);
+static int destroy_udqp(struct udqp_st *qp);
+static void *get_send_wqe_buf(void *wqe, __u8 index);
+static void *get_rcv_wqe_buf(void *wqe, __u8 index);
+
+static struct recv_wqe_st *alloc_rcv_wqe(struct udqp_st *qp);
+static int free_wqe(void *wqe);
+static int poll_cq(void *cqh, union cqe_st *cqe_p, __u8 * num_cqes);
+static int poll_eq(struct ib_eqe_st *ib_eqe_p, __u8 * num_eqes);
+static int post_rcv_buf(struct udqp_st *qp, struct recv_wqe_st *rcv_wqe);
+static __u32 dev_get_qpn(void *qph);
+
+#endif /* __mt25218_h__ */
diff --git a/gpxe/src/drivers/net/mlx_ipoib/mt25218_imp.c b/gpxe/src/drivers/net/mlx_ipoib/mt25218_imp.c
new file mode 100644
index 00000000..007ee653
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/mt25218_imp.c
@@ -0,0 +1,229 @@
+typedef uint32_t __u32;
+typedef uint16_t __u16;
+typedef uint8_t __u8;
+
+static int verbose_messages=0;
+static int print_info=0;
+static int fatal_condition=0;
+static int fw_fatal;
+
+#define tprintf(fmt, a...) \
+ do { \
+ if ( verbose_messages ) { \
+ printf("%s:%d: " fmt "\n", __func__, __LINE__, ##a); \
+ } \
+ } \
+ while(0)
+
+#define eprintf(fmt, a...) \
+ printf("%s:%d: " fmt "\n", __func__, __LINE__, ##a)
+
+static void cpu_to_be_buf(void *buf, int size)
+{
+ int dw_sz = size >> 2, i;
+
+ for (i = 0; i < dw_sz; ++i) {
+ ((__u32 *) buf)[i] = cpu_to_be32(((__u32 *) buf)[i]);
+ }
+}
+
+static void be_to_cpu_buf(void *buf, int size)
+{
+ int dw_sz = size >> 2, i;
+ u32 *p = buf;
+
+ for (i = 0; i < dw_sz; ++i) {
+ p[i] = be32_to_cpu(p[i]);
+ }
+}
+
+#include "cmdif_mt25218.c"
+#include "cmdif_comm.c"
+#include "ib_mt25218.c"
+#include "ib_mad.c"
+#include "ib_driver.c"
+#include "ipoib.c"
+
+static int probe_imp(struct pci_device *pci, struct nic *nic)
+{
+ int rc;
+
+ if (0 && nic) { /* just to supress warning */
+ return 0;
+ }
+
+ fatal_condition= 0;
+ fw_fatal= 0;
+
+ tprintf("");
+ rc = ipoib_init(pci);
+ if (rc)
+ return rc;
+
+ tprintf("");
+
+ return rc;
+}
+
+static int disable_imp(void)
+{
+ int rc;
+
+ rc = ipoib_close(fw_fatal);
+
+ return rc;
+}
+
+static int transmit_imp(const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ const char *packet, /* Packet */
+ unsigned int size)
+{ /* size */
+ int rc;
+
+ if (fatal_condition) {
+ /* since the transmit function does not return a value
+ we return success but do nothing to suppress error messages */
+ return 0;
+ }
+
+ rc = ipoib_send_packet(dest, type, packet, size);
+ if (rc) {
+ printf("*** ERROR IN SEND FLOW ***\n");
+ printf("restarting Etherboot\n");
+ sleep(1);
+ longjmp(restart_etherboot, -1);
+ /* we should not be here ... */
+ return -1;
+ }
+
+ return rc;
+}
+
+static void hd(void *where, int n)
+{
+ int i;
+
+ while (n > 0) {
+ printf("%X ", where);
+ for (i = 0; i < ((n > 16) ? 16 : n); i++)
+ printf(" %hhX", ((char *)where)[i]);
+ printf("\n");
+ n -= 16;
+ where += 16;
+ }
+}
+
+static int poll_imp(struct nic *nic, int retrieve, unsigned int *size_p)
+{
+ static char packet[2048];
+ static char *last_packet_p = NULL;
+ static unsigned long last_packet_size;
+ char *packet_p;
+ const int eth_header_len = 14;
+ unsigned int packet_len;
+ int is_bcast = 0;
+ __u16 prot, *ptr;
+ int rc;
+
+ if (0 && nic) { /* just to supress warning */
+ return -1;
+ }
+
+ if (fatal_condition) {
+ *size_p = 0;
+ return 0;
+ }
+
+ if (poll_error_buf()) {
+ fatal_condition= 1;
+ fw_fatal= 1;
+ printf("\n *** DEVICE FATAL ERROR ***\n");
+ goto fatal_handling;
+ }
+ else if (drain_eq()) {
+ fatal_condition= 1;
+ printf("\n *** FATAL ERROR ***\n");
+ goto fatal_handling;
+ }
+
+
+ if (retrieve) {
+ /* we actually want to read the packet */
+ if (last_packet_p) {
+ eprintf("");
+ /* there is already a packet that was previously read */
+ memcpy(nic->packet, last_packet_p, last_packet_size);
+ *size_p = last_packet_size;
+ last_packet_p = NULL;
+ return 0;
+ }
+ packet_p = nic->packet;
+ } else {
+ /* we don't want to read the packet,
+ just know if there is one. so we
+ read the packet to a local buffer and
+ we will return that buffer when the ip layer wants
+ another packet */
+ if (last_packet_p) {
+ /* there is already a packet that
+ was not consumend */
+ eprintf("overflow receive packets");
+ return -1;
+ }
+ packet_p = packet;
+ }
+
+ rc = ipoib_read_packet(&prot, packet_p + eth_header_len, &packet_len,
+ &is_bcast);
+ if (rc) {
+ printf("*** FATAL IN RECEIVE FLOW ****\n");
+ goto fatal_handling;
+ }
+
+ if (packet_len == 0) {
+ *size_p = 0;
+ return 0;
+ }
+
+ if (is_bcast) {
+ int i;
+ for (i = 0; i < 6; ++i) {
+ packet_p[i] = 0xff;
+ }
+ } else {
+ packet_p[0] = MLX_ETH_BYTE0;
+ packet_p[1] = MLX_ETH_BYTE1;
+ packet_p[2] = MLX_ETH_BYTE2;
+ packet_p[3] = 0;
+ packet_p[4] = 0;
+ packet_p[5] = 0;
+ }
+
+ memset(packet_p + 6, 0, 6);
+
+ ptr = (__u16 *) (packet_p + 12);
+ *ptr = htons(prot);
+
+ if (!retrieve) {
+ last_packet_p = packet;
+ last_packet_size = packet_len + eth_header_len;
+ *size_p = 0;
+ }
+
+ *size_p = packet_len + eth_header_len;
+ tprintf("packet size=%d, prot=%x\n", *size_p, prot);
+ if (0) {
+ hd(nic->packet, 42);
+ }
+
+ return 0;
+
+fatal_handling:
+ printf("restarting Etherboot\n");
+ sleep(1);
+ longjmp(restart_etherboot, -1);
+ /* we should not be here ... */
+ return -1;
+
+}
diff --git a/gpxe/src/drivers/net/mlx_ipoib/mt_version.c b/gpxe/src/drivers/net/mlx_ipoib/mt_version.c
new file mode 100644
index 00000000..2dbd67a6
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/mt_version.c
@@ -0,0 +1,23 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+/* definition of the build version goes here */
+const char *build_revision= "113";
diff --git a/gpxe/src/drivers/net/mlx_ipoib/patches/dhcpd.patch b/gpxe/src/drivers/net/mlx_ipoib/patches/dhcpd.patch
new file mode 100644
index 00000000..e2d0a202
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/patches/dhcpd.patch
@@ -0,0 +1,23 @@
+diff -ru ../../orig/dhcp-3.0.4b2/common/options.c ./common/options.c
+--- ../../orig/dhcp-3.0.4b2/common/options.c 2005-11-02 01:19:03.000000000 +0200
++++ ./common/options.c 2005-12-06 14:38:17.000000000 +0200
+@@ -537,6 +537,7 @@
+ priority_list [priority_len++] = DHO_DHCP_LEASE_TIME;
+ priority_list [priority_len++] = DHO_DHCP_MESSAGE;
+ priority_list [priority_len++] = DHO_DHCP_REQUESTED_ADDRESS;
++ priority_list [priority_len++] = DHO_DHCP_CLIENT_IDENTIFIER;
+ priority_list [priority_len++] = DHO_FQDN;
+
+ if (prl && prl -> len > 0) {
+diff -ru ../../orig/dhcp-3.0.4b2/includes/site.h ./includes/site.h
+--- ../../orig/dhcp-3.0.4b2/includes/site.h 2002-03-12 20:33:39.000000000 +0200
++++ ./includes/site.h 2005-12-06 14:36:55.000000000 +0200
+@@ -135,7 +135,7 @@
+ the aforementioned problems do not matter to you, or if no other
+ API is supported for your system, you may want to go with it. */
+
+-/* #define USE_SOCKETS */
++#define USE_SOCKETS
+
+ /* Define this to use the Sun Streams NIT API.
+
diff --git a/gpxe/src/drivers/net/mlx_ipoib/samples/dhcpd.conf b/gpxe/src/drivers/net/mlx_ipoib/samples/dhcpd.conf
new file mode 100644
index 00000000..ed6975f9
--- /dev/null
+++ b/gpxe/src/drivers/net/mlx_ipoib/samples/dhcpd.conf
@@ -0,0 +1,56 @@
+# dhcpd.conf
+#
+# Sample configuration file for ISC dhcpd
+#
+
+# option definitions common to all supported networks...
+
+DHCPD_INTERFACE = "ib0";
+
+# if you do not use dynamical DNS updates:
+#
+# this statement is needed by dhcpd-3 needs at least this statement.
+# you have to delete it for dhcpd-2, because it does not know it.
+#
+# if you want to use dynamical DNS updates, you should first read
+# read /usr/share/doc/packages/dhcp-server/DDNS-howto.txt
+ddns-update-style none; ddns-updates off;
+
+filename "pxelinux.bin";
+
+# If this DHCP server is the official DHCP server for the local
+# network, the authoritative directive should be uncommented.
+#authoritative;
+
+# No service will be given on this subnet, but declaring it helps the
+# DHCP server to understand the network topology.
+
+subnet 10.152.187.0 netmask 255.255.255.0 {
+}
+
+# This declaration allows BOOTP clients to get dynamic addresses,
+# which we don't really recommend.
+
+shared-network "ipoib_network" {
+ subnet 11.4.8.0 netmask 255.255.255.0 {
+ option dhcp-client-identifier = option dhcp-client-identifier;
+ option subnet-mask 255.255.255.0;
+ option domain-name "yok.mtl.com";
+ option domain-name-servers 10.0.0.1;
+ default-lease-time 28800;
+ max-lease-time 86400;
+ next-server 11.4.8.99;
+
+ }
+}
+
+
+# You need one such entry for each client
+host swlab35 {
+ fixed-address 11.4.8.35; # the IP address to be assigned to the client
+ # The value of the client identifier must be comprised from the prefix 20:00:
+ # folowed by the client's ipoib qp number - 55:04:01 in this example -
+ # followed by the GID of the port
+ option dhcp-client-identifier = 20:00:55:04:01:fe:80:00:00:00:00:00:00:00:02:c9:00:01:70:8a:81;
+}
+
diff --git a/gpxe/src/drivers/net/mtd80x.c b/gpxe/src/drivers/net/mtd80x.c
new file mode 100644
index 00000000..7cc8ce2f
--- /dev/null
+++ b/gpxe/src/drivers/net/mtd80x.c
@@ -0,0 +1,1084 @@
+/**************************************************************************
+*
+* mtd80x.c: Etherboot device driver for the mtd80x Ethernet chip.
+* Written 2004-2004 by Erdem Güven <zuencap@yahoo.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* fealnx.c: A Linux device driver for the mtd80x Ethernet chip
+* Written 1998-2000 by Donald Becker
+*
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+#define get_unaligned(ptr) (*(ptr))
+
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency. */
+/* The compiler will convert <unsigned>'%'<2^N> into a bit mask. */
+/* Making the Tx ring too large decreases the effectiveness of channel */
+/* bonding and packet priority. */
+/* There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE 2
+#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */
+#define RX_RING_SIZE 4
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define HZ 100
+#define TX_TIME_OUT (6*HZ)
+
+/* Allocation size of Rx buffers with normal sized Ethernet frames.
+ Do not change this value without good reason. This is not a limit,
+ but a way to keep a consistent allocation size among drivers.
+ */
+#define PKT_BUF_SZ 1536
+
+/* Generic MII registers. */
+
+#define MII_BMCR 0x00 /* Basic mode control register */
+#define MII_BMSR 0x01 /* Basic mode status register */
+#define MII_PHYSID1 0x02 /* PHYS ID 1 */
+#define MII_PHYSID2 0x03 /* PHYS ID 2 */
+#define MII_ADVERTISE 0x04 /* Advertisement control reg */
+#define MII_LPA 0x05 /* Link partner ability reg */
+#define MII_EXPANSION 0x06 /* Expansion register */
+#define MII_DCOUNTER 0x12 /* Disconnect counter */
+#define MII_FCSCOUNTER 0x13 /* False carrier counter */
+#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
+#define MII_RERRCOUNTER 0x15 /* Receive error counter */
+#define MII_SREVISION 0x16 /* Silicon revision */
+#define MII_RESV1 0x17 /* Reserved... */
+#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */
+#define MII_PHYADDR 0x19 /* PHY address */
+#define MII_RESV2 0x1a /* Reserved... */
+#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
+#define MII_NCONFIG 0x1c /* Network interface config */
+
+/* Basic mode control register. */
+#define BMCR_RESV 0x007f /* Unused... */
+#define BMCR_CTST 0x0080 /* Collision test */
+#define BMCR_FULLDPLX 0x0100 /* Full duplex */
+#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
+#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */
+#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */
+#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */
+#define BMCR_SPEED100 0x2000 /* Select 100Mbps */
+#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */
+#define BMCR_RESET 0x8000 /* Reset the DP83840 */
+
+/* Basic mode status register. */
+#define BMSR_ERCAP 0x0001 /* Ext-reg capability */
+#define BMSR_JCD 0x0002 /* Jabber detected */
+#define BMSR_LSTATUS 0x0004 /* Link status */
+#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */
+#define BMSR_RFAULT 0x0010 /* Remote fault detected */
+#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
+#define BMSR_RESV 0x07c0 /* Unused... */
+#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */
+#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */
+#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */
+#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */
+#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */
+
+/* Advertisement control register. */
+#define ADVERTISE_SLCT 0x001f /* Selector bits */
+#define ADVERTISE_CSMA 0x0001 /* Only selector supported */
+#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
+#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
+#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */
+#define ADVERTISE_RESV 0x1c00 /* Unused... */
+#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */
+#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */
+#define ADVERTISE_NPAGE 0x8000 /* Next page bit */
+
+#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
+ ADVERTISE_CSMA)
+#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
+ ADVERTISE_100HALF | ADVERTISE_100FULL)
+
+/* for different PHY */
+enum phy_type_flags {
+ MysonPHY = 1,
+ AhdocPHY = 2,
+ SeeqPHY = 3,
+ MarvellPHY = 4,
+ Myson981 = 5,
+ LevelOnePHY = 6,
+ OtherPHY = 10,
+};
+
+/* A chip capabilities table*/
+enum chip_capability_flags {
+ HAS_MII_XCVR,
+ HAS_CHIP_XCVR,
+};
+
+#if 0 /* not used */
+static
+struct chip_info
+{
+ u16 dev_id;
+ int flag;
+}
+mtd80x_chips[] = {
+ {0x0800, HAS_MII_XCVR},
+ {0x0803, HAS_CHIP_XCVR},
+ {0x0891, HAS_MII_XCVR}
+ };
+static int chip_cnt = sizeof( mtd80x_chips ) / sizeof( struct chip_info );
+#endif
+
+/* Offsets to the Command and Status Registers. */
+enum mtd_offsets {
+ PAR0 = 0x0, /* physical address 0-3 */
+ PAR1 = 0x04, /* physical address 4-5 */
+ MAR0 = 0x08, /* multicast address 0-3 */
+ MAR1 = 0x0C, /* multicast address 4-7 */
+ FAR0 = 0x10, /* flow-control address 0-3 */
+ FAR1 = 0x14, /* flow-control address 4-5 */
+ TCRRCR = 0x18, /* receive & transmit configuration */
+ BCR = 0x1C, /* bus command */
+ TXPDR = 0x20, /* transmit polling demand */
+ RXPDR = 0x24, /* receive polling demand */
+ RXCWP = 0x28, /* receive current word pointer */
+ TXLBA = 0x2C, /* transmit list base address */
+ RXLBA = 0x30, /* receive list base address */
+ ISR = 0x34, /* interrupt status */
+ IMR = 0x38, /* interrupt mask */
+ FTH = 0x3C, /* flow control high/low threshold */
+ MANAGEMENT = 0x40, /* bootrom/eeprom and mii management */
+ TALLY = 0x44, /* tally counters for crc and mpa */
+ TSR = 0x48, /* tally counter for transmit status */
+ BMCRSR = 0x4c, /* basic mode control and status */
+ PHYIDENTIFIER = 0x50, /* phy identifier */
+ ANARANLPAR = 0x54, /* auto-negotiation advertisement and link
+ partner ability */
+ ANEROCR = 0x58, /* auto-negotiation expansion and pci conf. */
+ BPREMRPSR = 0x5c, /* bypass & receive error mask and phy status */
+};
+
+/* Bits in the interrupt status/enable registers. */
+/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
+enum intr_status_bits {
+ RFCON = 0x00020000, /* receive flow control xon packet */
+ RFCOFF = 0x00010000, /* receive flow control xoff packet */
+ LSCStatus = 0x00008000, /* link status change */
+ ANCStatus = 0x00004000, /* autonegotiation completed */
+ FBE = 0x00002000, /* fatal bus error */
+ FBEMask = 0x00001800, /* mask bit12-11 */
+ ParityErr = 0x00000000, /* parity error */
+ TargetErr = 0x00001000, /* target abort */
+ MasterErr = 0x00000800, /* master error */
+ TUNF = 0x00000400, /* transmit underflow */
+ ROVF = 0x00000200, /* receive overflow */
+ ETI = 0x00000100, /* transmit early int */
+ ERI = 0x00000080, /* receive early int */
+ CNTOVF = 0x00000040, /* counter overflow */
+ RBU = 0x00000020, /* receive buffer unavailable */
+ TBU = 0x00000010, /* transmit buffer unavilable */
+ TI = 0x00000008, /* transmit interrupt */
+ RI = 0x00000004, /* receive interrupt */
+ RxErr = 0x00000002, /* receive error */
+};
+
+/* Bits in the NetworkConfig register. */
+enum rx_mode_bits {
+ RxModeMask = 0xe0,
+ AcceptAllPhys = 0x80, /* promiscuous mode */
+ AcceptBroadcast = 0x40, /* accept broadcast */
+ AcceptMulticast = 0x20, /* accept mutlicast */
+ AcceptRunt = 0x08, /* receive runt pkt */
+ ALP = 0x04, /* receive long pkt */
+ AcceptErr = 0x02, /* receive error pkt */
+
+ AcceptMyPhys = 0x00000000,
+ RxEnable = 0x00000001,
+ RxFlowCtrl = 0x00002000,
+ TxEnable = 0x00040000,
+ TxModeFDX = 0x00100000,
+ TxThreshold = 0x00e00000,
+
+ PS1000 = 0x00010000,
+ PS10 = 0x00080000,
+ FD = 0x00100000,
+};
+
+/* Bits in network_desc.status */
+enum rx_desc_status_bits {
+ RXOWN = 0x80000000, /* own bit */
+ FLNGMASK = 0x0fff0000, /* frame length */
+ FLNGShift = 16,
+ MARSTATUS = 0x00004000, /* multicast address received */
+ BARSTATUS = 0x00002000, /* broadcast address received */
+ PHYSTATUS = 0x00001000, /* physical address received */
+ RXFSD = 0x00000800, /* first descriptor */
+ RXLSD = 0x00000400, /* last descriptor */
+ ErrorSummary = 0x80, /* error summary */
+ RUNT = 0x40, /* runt packet received */
+ LONG = 0x20, /* long packet received */
+ FAE = 0x10, /* frame align error */
+ CRC = 0x08, /* crc error */
+ RXER = 0x04, /* receive error */
+};
+
+enum rx_desc_control_bits {
+ RXIC = 0x00800000, /* interrupt control */
+ RBSShift = 0,
+};
+
+enum tx_desc_status_bits {
+ TXOWN = 0x80000000, /* own bit */
+ JABTO = 0x00004000, /* jabber timeout */
+ CSL = 0x00002000, /* carrier sense lost */
+ LC = 0x00001000, /* late collision */
+ EC = 0x00000800, /* excessive collision */
+ UDF = 0x00000400, /* fifo underflow */
+ DFR = 0x00000200, /* deferred */
+ HF = 0x00000100, /* heartbeat fail */
+ NCRMask = 0x000000ff, /* collision retry count */
+ NCRShift = 0,
+};
+
+enum tx_desc_control_bits {
+ TXIC = 0x80000000, /* interrupt control */
+ ETIControl = 0x40000000, /* early transmit interrupt */
+ TXLD = 0x20000000, /* last descriptor */
+ TXFD = 0x10000000, /* first descriptor */
+ CRCEnable = 0x08000000, /* crc control */
+ PADEnable = 0x04000000, /* padding control */
+ RetryTxLC = 0x02000000, /* retry late collision */
+ PKTSMask = 0x3ff800, /* packet size bit21-11 */
+ PKTSShift = 11,
+ TBSMask = 0x000007ff, /* transmit buffer bit 10-0 */
+ TBSShift = 0,
+};
+
+/* BootROM/EEPROM/MII Management Register */
+#define MASK_MIIR_MII_READ 0x00000000
+#define MASK_MIIR_MII_WRITE 0x00000008
+#define MASK_MIIR_MII_MDO 0x00000004
+#define MASK_MIIR_MII_MDI 0x00000002
+#define MASK_MIIR_MII_MDC 0x00000001
+
+/* ST+OP+PHYAD+REGAD+TA */
+#define OP_READ 0x6000 /* ST:01+OP:10+PHYAD+REGAD+TA:Z0 */
+#define OP_WRITE 0x5002 /* ST:01+OP:01+PHYAD+REGAD+TA:10 */
+
+/* ------------------------------------------------------------------------- */
+/* Constants for Myson PHY */
+/* ------------------------------------------------------------------------- */
+#define MysonPHYID 0xd0000302
+/* 89-7-27 add, (begin) */
+#define MysonPHYID0 0x0302
+#define StatusRegister 18
+#define SPEED100 0x0400 // bit10
+#define FULLMODE 0x0800 // bit11
+/* 89-7-27 add, (end) */
+
+/* ------------------------------------------------------------------------- */
+/* Constants for Seeq 80225 PHY */
+/* ------------------------------------------------------------------------- */
+#define SeeqPHYID0 0x0016
+
+#define MIIRegister18 18
+#define SPD_DET_100 0x80
+#define DPLX_DET_FULL 0x40
+
+/* ------------------------------------------------------------------------- */
+/* Constants for Ahdoc 101 PHY */
+/* ------------------------------------------------------------------------- */
+#define AhdocPHYID0 0x0022
+
+#define DiagnosticReg 18
+#define DPLX_FULL 0x0800
+#define Speed_100 0x0400
+
+/* 89/6/13 add, */
+/* -------------------------------------------------------------------------- */
+/* Constants */
+/* -------------------------------------------------------------------------- */
+#define MarvellPHYID0 0x0141
+#define LevelOnePHYID0 0x0013
+
+#define MII1000BaseTControlReg 9
+#define MII1000BaseTStatusReg 10
+#define SpecificReg 17
+
+/* for 1000BaseT Control Register */
+#define PHYAbletoPerform1000FullDuplex 0x0200
+#define PHYAbletoPerform1000HalfDuplex 0x0100
+#define PHY1000AbilityMask 0x300
+
+// for phy specific status register, marvell phy.
+#define SpeedMask 0x0c000
+#define Speed_1000M 0x08000
+#define Speed_100M 0x4000
+#define Speed_10M 0
+#define Full_Duplex 0x2000
+
+// 89/12/29 add, for phy specific status register, levelone phy, (begin)
+#define LXT1000_100M 0x08000
+#define LXT1000_1000M 0x0c000
+#define LXT1000_Full 0x200
+// 89/12/29 add, for phy specific status register, levelone phy, (end)
+
+#if 0
+/* for 3-in-1 case */
+#define PS10 0x00080000
+#define FD 0x00100000
+#define PS1000 0x00010000
+#endif
+
+/* for PHY */
+#define LinkIsUp 0x0004
+#define LinkIsUp2 0x00040000
+
+/* Create a static buffer of size PKT_BUF_SZ for each
+RX and TX Descriptor. All descriptors point to a
+part of this buffer */
+struct {
+ u8 txb[PKT_BUF_SZ * TX_RING_SIZE] __attribute__ ((aligned(8)));
+ u8 rxb[PKT_BUF_SZ * RX_RING_SIZE] __attribute__ ((aligned(8)));
+} mtd80x_bufs __shared;
+#define txb mtd80x_bufs.txb
+#define rxb mtd80x_bufs.rxb
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct mtd_desc
+{
+ s32 status;
+ s32 control;
+ u32 buffer;
+ u32 next_desc;
+ struct mtd_desc *next_desc_logical;
+ u8* skbuff;
+ u32 reserved1;
+ u32 reserved2;
+};
+
+struct mtd_private
+{
+ struct mtd_desc rx_ring[RX_RING_SIZE];
+ struct mtd_desc tx_ring[TX_RING_SIZE];
+
+ /* Frequently used values: keep some adjacent for cache effect. */
+ int flags;
+ struct pci_dev *pci_dev;
+ unsigned long crvalue;
+ unsigned long bcrvalue;
+ /*unsigned long imrvalue;*/
+ struct mtd_desc *cur_rx;
+ struct mtd_desc *lack_rxbuf;
+ int really_rx_count;
+ struct mtd_desc *cur_tx;
+ struct mtd_desc *cur_tx_copy;
+ int really_tx_count;
+ int free_tx_count;
+ unsigned int rx_buf_sz; /* Based on MTU+slack. */
+
+ /* These values are keep track of the transceiver/media in use. */
+ unsigned int linkok;
+ unsigned int line_speed;
+ unsigned int duplexmode;
+ unsigned int default_port:
+ 4; /* Last dev->if_port value. */
+ unsigned int PHYType;
+
+ /* MII transceiver section. */
+ int mii_cnt; /* MII device addresses. */
+ unsigned char phys[1]; /* MII device addresses. */
+
+ /*other*/
+ const char *nic_name;
+ int ioaddr;
+ u16 dev_id;
+};
+
+static struct mtd_private mtdx;
+
+static int mdio_read(struct nic * , int phy_id, int location);
+static void getlinktype(struct nic * );
+static void getlinkstatus(struct nic * );
+static void set_rx_mode(struct nic *);
+
+/**************************************************************************
+ * init_ring - setup the tx and rx descriptors
+ *************************************************************************/
+static void init_ring(struct nic *nic __unused)
+{
+ int i;
+
+ mtdx.cur_rx = &mtdx.rx_ring[0];
+
+ mtdx.rx_buf_sz = PKT_BUF_SZ;
+ /*mtdx.rx_head_desc = &mtdx.rx_ring[0];*/
+
+ /* Initialize all Rx descriptors. */
+ /* Fill in the Rx buffers. Handle allocation failure gracefully. */
+ for (i = 0; i < RX_RING_SIZE; i++)
+ {
+ mtdx.rx_ring[i].status = RXOWN;
+ mtdx.rx_ring[i].control = mtdx.rx_buf_sz << RBSShift;
+ mtdx.rx_ring[i].next_desc = virt_to_le32desc(&mtdx.rx_ring[i+1]);
+ mtdx.rx_ring[i].next_desc_logical = &mtdx.rx_ring[i+1];
+ mtdx.rx_ring[i].buffer = virt_to_le32desc(&rxb[i * PKT_BUF_SZ]);
+ mtdx.rx_ring[i].skbuff = &rxb[i * PKT_BUF_SZ];
+ }
+ /* Mark the last entry as wrapping the ring. */
+ mtdx.rx_ring[i-1].next_desc = virt_to_le32desc(&mtdx.rx_ring[0]);
+ mtdx.rx_ring[i-1].next_desc_logical = &mtdx.rx_ring[0];
+
+ /* We only use one transmit buffer, but two
+ * descriptors so transmit engines have somewhere
+ * to point should they feel the need */
+ mtdx.tx_ring[0].status = 0x00000000;
+ mtdx.tx_ring[0].buffer = virt_to_bus(&txb[0]);
+ mtdx.tx_ring[0].next_desc = virt_to_le32desc(&mtdx.tx_ring[1]);
+
+ /* This descriptor is never used */
+ mtdx.tx_ring[1].status = 0x00000000;
+ mtdx.tx_ring[1].buffer = 0; /*virt_to_bus(&txb[1]); */
+ mtdx.tx_ring[1].next_desc = virt_to_le32desc(&mtdx.tx_ring[0]);
+
+ return;
+}
+
+/**************************************************************************
+RESET - Reset Adapter
+***************************************************************************/
+static void mtd_reset( struct nic *nic )
+{
+ /* Reset the chip to erase previous misconfiguration. */
+ outl(0x00000001, mtdx.ioaddr + BCR);
+
+ init_ring(nic);
+
+ outl(virt_to_bus(mtdx.rx_ring), mtdx.ioaddr + RXLBA);
+ outl(virt_to_bus(mtdx.tx_ring), mtdx.ioaddr + TXLBA);
+
+ /* Initialize other registers. */
+ /* Configure the PCI bus bursts and FIFO thresholds. */
+ mtdx.bcrvalue = 0x10; /* little-endian, 8 burst length */
+ mtdx.crvalue = 0xa00; /* rx 128 burst length */
+
+ if ( mtdx.dev_id == 0x891 ) {
+ mtdx.bcrvalue |= 0x200; /* set PROG bit */
+ mtdx.crvalue |= 0x02000000; /* set enhanced bit */
+ }
+
+ outl( mtdx.bcrvalue, mtdx.ioaddr + BCR);
+
+ /* Restart Rx engine if stopped. */
+ outl(0, mtdx.ioaddr + RXPDR);
+
+ getlinkstatus(nic);
+ if (mtdx.linkok)
+ {
+ static const char* texts[]={"half","full","10","100","1000"};
+ getlinktype(nic);
+ DBG ( "Link is OK : %s %s\n", texts[mtdx.duplexmode-1], texts[mtdx.line_speed+1] );
+ } else
+ {
+ DBG ( "No link!!!\n" );
+ }
+
+ mtdx.crvalue |= /*TxEnable |*/ RxEnable | TxThreshold;
+ set_rx_mode(nic);
+
+ /* Clear interrupts by setting the interrupt mask. */
+ outl(FBE | TUNF | CNTOVF | RBU | TI | RI, mtdx.ioaddr + ISR);
+ outl( 0, mtdx.ioaddr + IMR);
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int mtd_poll(struct nic *nic, __unused int retrieve)
+{
+ s32 rx_status = mtdx.cur_rx->status;
+ int retval = 0;
+
+ if( ( rx_status & RXOWN ) != 0 )
+ {
+ return 0;
+ }
+
+ if (rx_status & ErrorSummary)
+ { /* there was a fatal error */
+ printf( "%s: Receive error, Rx status %8.8x, Error(s) %s%s%s\n",
+ mtdx.nic_name, (unsigned int) rx_status,
+ (rx_status & (LONG | RUNT)) ? "length_error ":"",
+ (rx_status & RXER) ? "frame_error ":"",
+ (rx_status & CRC) ? "crc_error ":"" );
+ retval = 0;
+ } else if( !((rx_status & RXFSD) && (rx_status & RXLSD)) )
+ {
+ /* this pkt is too long, over one rx buffer */
+ printf("Pkt is too long, over one rx buffer.\n");
+ retval = 0;
+ } else
+ { /* this received pkt is ok */
+ /* Omit the four octet CRC from the length. */
+ short pkt_len = ((rx_status & FLNGMASK) >> FLNGShift) - 4;
+
+ DBG ( " netdev_rx() normal Rx pkt length %d"
+ " status %x.\n", pkt_len, (unsigned int) rx_status );
+
+ nic->packetlen = pkt_len;
+ memcpy(nic->packet, mtdx.cur_rx->skbuff, pkt_len);
+
+ retval = 1;
+ }
+
+ while( ( mtdx.cur_rx->status & RXOWN ) == 0 )
+ {
+ mtdx.cur_rx->status = RXOWN;
+ mtdx.cur_rx = mtdx.cur_rx->next_desc_logical;
+ }
+
+ /* Restart Rx engine if stopped. */
+ outl(0, mtdx.ioaddr + RXPDR);
+
+ return retval;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void mtd_transmit(
+ struct nic *nic,
+ const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ unsigned int size, /* size */
+ const char *data) /* Packet */
+{
+ u32 to;
+ u32 tx_status;
+ unsigned int nstype = htons ( type );
+
+ memcpy( txb, dest, ETH_ALEN );
+ memcpy( txb + ETH_ALEN, nic->node_addr, ETH_ALEN );
+ memcpy( txb + 2 * ETH_ALEN, &nstype, 2 );
+ memcpy( txb + ETH_HLEN, data, size );
+
+ size += ETH_HLEN;
+ size &= 0x0FFF;
+ while( size < ETH_ZLEN )
+ {
+ txb[size++] = '\0';
+ }
+
+ mtdx.tx_ring[0].control = TXLD | TXFD | CRCEnable | PADEnable;
+ mtdx.tx_ring[0].control |= (size << PKTSShift); /* pkt size */
+ mtdx.tx_ring[0].control |= (size << TBSShift); /* buffer size */
+ mtdx.tx_ring[0].status = TXOWN;
+
+ /* Point to transmit descriptor */
+ outl(virt_to_bus(mtdx.tx_ring), mtdx.ioaddr + TXLBA);
+ /* Enable Tx */
+ outl( mtdx.crvalue | TxEnable, mtdx.ioaddr + TCRRCR);
+ /* Wake the potentially-idle transmit channel. */
+ outl(0, mtdx.ioaddr + TXPDR);
+
+ to = currticks() + TX_TIME_OUT;
+ while(( mtdx.tx_ring[0].status & TXOWN) && (currticks() < to));
+
+ /* Disable Tx */
+ outl( mtdx.crvalue & (~TxEnable), mtdx.ioaddr + TCRRCR);
+
+ tx_status = mtdx.tx_ring[0].status;
+ if (currticks() >= to){
+ DBG ( "TX Time Out" );
+ } else if( tx_status & (CSL | LC | EC | UDF | HF)){
+ printf( "Transmit error: %8.8x %s %s %s %s %s\n",
+ (unsigned int) tx_status,
+ tx_status & EC ? "abort" : "",
+ tx_status & CSL ? "carrier" : "",
+ tx_status & LC ? "late" : "",
+ tx_status & UDF ? "fifo" : "",
+ tx_status & HF ? "heartbeat" : "" );
+ }
+
+ /*hex_dump( txb, size );*/
+ /*pause();*/
+
+ DBG ( "TRANSMIT\n" );
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void mtd_disable ( struct nic *nic ) {
+
+ /* Disable Tx Rx*/
+ outl( mtdx.crvalue & (~TxEnable) & (~RxEnable), mtdx.ioaddr + TCRRCR );
+
+ /* Reset the chip to erase previous misconfiguration. */
+ mtd_reset(nic);
+
+ DBG ( "DISABLE\n" );
+}
+
+static struct nic_operations mtd_operations = {
+ .connect = dummy_connect,
+ .poll = mtd_poll,
+ .transmit = mtd_transmit,
+ .irq = dummy_irq,
+
+};
+
+static struct pci_device_id mtd80x_nics[] = {
+ PCI_ROM(0x1516, 0x0800, "MTD800", "Myson MTD800"),
+ PCI_ROM(0x1516, 0x0803, "MTD803", "Surecom EP-320X"),
+ PCI_ROM(0x1516, 0x0891, "MTD891", "Myson MTD891"),
+};
+
+PCI_DRIVER ( mtd80x_driver, mtd80x_nics, PCI_NO_CLASS );
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+static int mtd_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ int i;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ adjust_pci_device(pci);
+
+ nic->ioaddr = pci->ioaddr;
+ nic->irqno = 0;
+
+ mtdx.nic_name = pci->driver_name;
+ mtdx.dev_id = pci->device;
+ mtdx.ioaddr = nic->ioaddr;
+
+ /* read ethernet id */
+ for (i = 0; i < 6; ++i)
+ {
+ nic->node_addr[i] = inb(mtdx.ioaddr + PAR0 + i);
+ }
+
+ if (memcmp(nic->node_addr, "\0\0\0\0\0\0", 6) == 0)
+ {
+ return 0;
+ }
+
+ DBG ( "%s: ioaddr %4.4x MAC %s\n", mtdx.nic_name, mtdx.ioaddr, eth_ntoa ( nic->node_addr ) );
+
+ /* Reset the chip to erase previous misconfiguration. */
+ outl(0x00000001, mtdx.ioaddr + BCR);
+
+ /* find the connected MII xcvrs */
+
+ if( mtdx.dev_id != 0x803 )
+ {
+ int phy, phy_idx = 0;
+
+ for (phy = 1; phy < 32 && phy_idx < 1; phy++) {
+ int mii_status = mdio_read(nic, phy, 1);
+
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ mtdx.phys[phy_idx] = phy;
+
+ DBG ( "%s: MII PHY found at address %d, status "
+ "0x%4.4x.\n", mtdx.nic_name, phy, mii_status );
+ /* get phy type */
+ {
+ unsigned int data;
+
+ data = mdio_read(nic, mtdx.phys[phy_idx], 2);
+ if (data == SeeqPHYID0)
+ mtdx.PHYType = SeeqPHY;
+ else if (data == AhdocPHYID0)
+ mtdx.PHYType = AhdocPHY;
+ else if (data == MarvellPHYID0)
+ mtdx.PHYType = MarvellPHY;
+ else if (data == MysonPHYID0)
+ mtdx.PHYType = Myson981;
+ else if (data == LevelOnePHYID0)
+ mtdx.PHYType = LevelOnePHY;
+ else
+ mtdx.PHYType = OtherPHY;
+ }
+ phy_idx++;
+ }
+ }
+
+ mtdx.mii_cnt = phy_idx;
+ if (phy_idx == 0) {
+ printf("%s: MII PHY not found -- this device may "
+ "not operate correctly.\n", mtdx.nic_name);
+ }
+ } else {
+ mtdx.phys[0] = 32;
+ /* get phy type */
+ if (inl(mtdx.ioaddr + PHYIDENTIFIER) == MysonPHYID ) {
+ mtdx.PHYType = MysonPHY;
+ DBG ( "MysonPHY\n" );
+ } else {
+ mtdx.PHYType = OtherPHY;
+ DBG ( "OtherPHY\n" );
+ }
+ }
+
+ getlinkstatus(nic);
+ if( !mtdx.linkok )
+ {
+ printf("No link!!!\n");
+ return 0;
+ }
+
+ mtd_reset( nic );
+
+ /* point to NIC specific routines */
+ nic->nic_op = &mtd_operations;
+ return 1;
+}
+
+
+/**************************************************************************/
+static void set_rx_mode(struct nic *nic __unused)
+{
+ u32 mc_filter[2]; /* Multicast hash filter */
+ u32 rx_mode;
+
+ /* Too many to match, or accept all multicasts. */
+ mc_filter[1] = mc_filter[0] = ~0;
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+
+ outl(mc_filter[0], mtdx.ioaddr + MAR0);
+ outl(mc_filter[1], mtdx.ioaddr + MAR1);
+
+ mtdx.crvalue = ( mtdx.crvalue & ~RxModeMask ) | rx_mode;
+ outb( mtdx.crvalue, mtdx.ioaddr + TCRRCR);
+}
+/**************************************************************************/
+static unsigned int m80x_read_tick(void)
+/* function: Reads the Timer tick count register which decrements by 2 from */
+/* 65536 to 0 every 1/36.414 of a second. Each 2 decrements of the */
+/* count represents 838 nsec's. */
+/* input : none. */
+/* output : none. */
+{
+ unsigned char tmp;
+ int value;
+
+ outb((char) 0x06, 0x43); // Command 8254 to latch T0's count
+
+ // now read the count.
+ tmp = (unsigned char) inb(0x40);
+ value = ((int) tmp) << 8;
+ tmp = (unsigned char) inb(0x40);
+ value |= (((int) tmp) & 0xff);
+ return (value);
+}
+
+static void m80x_delay(unsigned int interval)
+/* function: to wait for a specified time. */
+/* input : interval ... the specified time. */
+/* output : none. */
+{
+ unsigned int interval1, interval2, i = 0;
+
+ interval1 = m80x_read_tick(); // get initial value
+ do
+ {
+ interval2 = m80x_read_tick();
+ if (interval1 < interval2)
+ interval1 += 65536;
+ ++i;
+ } while (((interval1 - interval2) < (u16) interval) && (i < 65535));
+}
+
+
+static u32 m80x_send_cmd_to_phy(long miiport, int opcode, int phyad, int regad)
+{
+ u32 miir;
+ int i;
+ unsigned int mask, data;
+
+ /* enable MII output */
+ miir = (u32) inl(miiport);
+ miir &= 0xfffffff0;
+
+ miir |= MASK_MIIR_MII_WRITE + MASK_MIIR_MII_MDO;
+
+ /* send 32 1's preamble */
+ for (i = 0; i < 32; i++) {
+ /* low MDC; MDO is already high (miir) */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ /* high MDC */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+ }
+
+ /* calculate ST+OP+PHYAD+REGAD+TA */
+ data = opcode | (phyad << 7) | (regad << 2);
+
+ /* sent out */
+ mask = 0x8000;
+ while (mask) {
+ /* low MDC, prepare MDO */
+ miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO);
+ if (mask & data)
+ miir |= MASK_MIIR_MII_MDO;
+
+ outl(miir, miiport);
+ /* high MDC */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+ m80x_delay(30);
+
+ /* next */
+ mask >>= 1;
+ if (mask == 0x2 && opcode == OP_READ)
+ miir &= ~MASK_MIIR_MII_WRITE;
+ }
+ return miir;
+}
+
+static int mdio_read(struct nic *nic __unused, int phyad, int regad)
+{
+ long miiport = mtdx.ioaddr + MANAGEMENT;
+ u32 miir;
+ unsigned int mask, data;
+
+ miir = m80x_send_cmd_to_phy(miiport, OP_READ, phyad, regad);
+
+ /* read data */
+ mask = 0x8000;
+ data = 0;
+ while (mask)
+ {
+ /* low MDC */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ /* read MDI */
+ miir = inl(miiport);
+ if (miir & MASK_MIIR_MII_MDI)
+ data |= mask;
+
+ /* high MDC, and wait */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+ m80x_delay((int) 30);
+
+ /* next */
+ mask >>= 1;
+ }
+
+ /* low MDC */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ return data & 0xffff;
+}
+
+#if 0 /* not used */
+static void mdio_write(struct nic *nic __unused, int phyad, int regad,
+ int data)
+{
+ long miiport = mtdx.ioaddr + MANAGEMENT;
+ u32 miir;
+ unsigned int mask;
+
+ miir = m80x_send_cmd_to_phy(miiport, OP_WRITE, phyad, regad);
+
+ /* write data */
+ mask = 0x8000;
+ while (mask)
+ {
+ /* low MDC, prepare MDO */
+ miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO);
+ if (mask & data)
+ miir |= MASK_MIIR_MII_MDO;
+ outl(miir, miiport);
+
+ /* high MDC */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ /* next */
+ mask >>= 1;
+ }
+
+ /* low MDC */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ return;
+}
+#endif
+
+static void getlinkstatus(struct nic *nic)
+/* function: Routine will read MII Status Register to get link status. */
+/* input : dev... pointer to the adapter block. */
+/* output : none. */
+{
+ unsigned int i, DelayTime = 0x1000;
+
+ mtdx.linkok = 0;
+
+ if (mtdx.PHYType == MysonPHY)
+ {
+ for (i = 0; i < DelayTime; ++i) {
+ if (inl(mtdx.ioaddr + BMCRSR) & LinkIsUp2) {
+ mtdx.linkok = 1;
+ return;
+ }
+ // delay
+ m80x_delay(100);
+ }
+ } else
+ {
+ for (i = 0; i < DelayTime; ++i) {
+ if (mdio_read(nic, mtdx.phys[0], MII_BMSR) & BMSR_LSTATUS) {
+ mtdx.linkok = 1;
+ return;
+ }
+ // delay
+ m80x_delay(100);
+ }
+ }
+}
+
+
+static void getlinktype(struct nic *dev)
+{
+ if (mtdx.PHYType == MysonPHY)
+ { /* 3-in-1 case */
+ if (inl(mtdx.ioaddr + TCRRCR) & FD)
+ mtdx.duplexmode = 2; /* full duplex */
+ else
+ mtdx.duplexmode = 1; /* half duplex */
+ if (inl(mtdx.ioaddr + TCRRCR) & PS10)
+ mtdx.line_speed = 1; /* 10M */
+ else
+ mtdx.line_speed = 2; /* 100M */
+ } else
+ {
+ if (mtdx.PHYType == SeeqPHY) { /* this PHY is SEEQ 80225 */
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], MIIRegister18);
+ if (data & SPD_DET_100)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ if (data & DPLX_DET_FULL)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ } else if (mtdx.PHYType == AhdocPHY) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], DiagnosticReg);
+ if (data & Speed_100)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ if (data & DPLX_FULL)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ }
+ /* 89/6/13 add, (begin) */
+ else if (mtdx.PHYType == MarvellPHY) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], SpecificReg);
+ if (data & Full_Duplex)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ data &= SpeedMask;
+ if (data == Speed_1000M)
+ mtdx.line_speed = 3; /* 1000M */
+ else if (data == Speed_100M)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ }
+ /* 89/6/13 add, (end) */
+ /* 89/7/27 add, (begin) */
+ else if (mtdx.PHYType == Myson981) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], StatusRegister);
+
+ if (data & SPEED100)
+ mtdx.line_speed = 2;
+ else
+ mtdx.line_speed = 1;
+
+ if (data & FULLMODE)
+ mtdx.duplexmode = 2;
+ else
+ mtdx.duplexmode = 1;
+ }
+ /* 89/7/27 add, (end) */
+ /* 89/12/29 add */
+ else if (mtdx.PHYType == LevelOnePHY) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], SpecificReg);
+ if (data & LXT1000_Full)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ data &= SpeedMask;
+ if (data == LXT1000_1000M)
+ mtdx.line_speed = 3; /* 1000M */
+ else if (data == LXT1000_100M)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ }
+ // chage crvalue
+ // mtdx.crvalue&=(~PS10)&(~FD);
+ mtdx.crvalue &= (~PS10) & (~FD) & (~PS1000);
+ if (mtdx.line_speed == 1)
+ mtdx.crvalue |= PS10;
+ else if (mtdx.line_speed == 3)
+ mtdx.crvalue |= PS1000;
+ if (mtdx.duplexmode == 2)
+ mtdx.crvalue |= FD;
+ }
+}
+
+DRIVER ( "MTD80X", nic_driver, pci_driver, mtd80x_driver,
+ mtd_probe, mtd_disable );
diff --git a/gpxe/src/drivers/net/mtnic.c b/gpxe/src/drivers/net/mtnic.c
new file mode 100755
index 00000000..536fcb8d
--- /dev/null
+++ b/gpxe/src/drivers/net/mtnic.c
@@ -0,0 +1,1758 @@
+/*
+ * Copyright (c) 2007 Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <strings.h>
+#include <errno.h>
+#include <gpxe/malloc.h>
+#include <gpxe/umalloc.h>
+#include <bits/byteswap.h>
+#include <little_bswap.h>
+#include <unistd.h>
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/iobuf.h>
+#include "mtnic.h"
+
+
+/*
+
+
+ mtnic.c - gPXE driver for Mellanox 10Gig ConnectX EN
+
+
+*/
+
+
+
+
+/** Set port number to use
+ *
+ * 0 - port 1
+ * 1 - port 2
+ */
+#define MTNIC_PORT_NUM 0
+/* Note: for verbose printing do Make ... DEBUG=mtnic */
+
+
+
+
+/********************************************************************
+*
+* MTNIC allocation functions
+*
+*********************************************************************/
+/**
+* mtnic_alloc_aligned
+*
+* @v unsigned int size size
+* @v void **va virtual address
+* @v u32 *pa physical address
+* @v u32 aligment aligment
+*
+* Function allocate aligned buffer and put it's virtual address in 'va'
+* and it's physical aligned address in 'pa'
+*/
+static int
+mtnic_alloc_aligned(unsigned int size, void **va, u32 *pa, unsigned int alignment)
+{
+ *va = alloc_memblock(size, alignment);
+ if (!*va) {
+ return MTNIC_ERROR;
+ }
+ *pa = (u32)virt_to_bus(*va);
+ return 0;
+}
+
+
+
+/**
+ *
+ * mtnic alloc command interface
+ *
+ */
+static int
+mtnic_alloc_cmdif(struct mtnic_priv *priv)
+{
+ u32 bar = mtnic_pci_dev.dev.bar[0];
+
+ priv->hcr = ioremap(bar + MTNIC_HCR_BASE, MTNIC_HCR_SIZE);
+ if (!priv->hcr) {
+ eprintf("Couldn't map command register.");
+ return MTNIC_ERROR;
+ }
+ mtnic_alloc_aligned(PAGE_SIZE, (void *)&priv->cmd.buf, &priv->cmd.mapping, PAGE_SIZE);
+ if (!priv->cmd.buf) {
+ eprintf("Error in allocating buffer for command interface\n");
+ return MTNIC_ERROR;
+ }
+ return 0;
+}
+
+/**
+ * Free RX io buffers
+ */
+static void
+mtnic_free_io_buffers(struct mtnic_ring *ring)
+{
+ int index;
+
+ for (; ring->cons <= ring->prod; ++ring->cons) {
+ index = ring->cons & ring->size_mask;
+ if (ring->iobuf[index])
+ free_iob(ring->iobuf[index]);
+ }
+}
+
+
+
+/**
+ *
+ * mtnic alloc and attach io buffers
+ *
+ */
+static int
+mtnic_alloc_iobuf(struct mtnic_priv *priv, struct mtnic_ring *ring,
+ unsigned int size)
+{
+ struct mtnic_rx_desc *rx_desc_ptr = ring->buf;
+ u32 index;
+
+ while ((u32)(ring->prod - ring->cons) < UNITS_BUFFER_SIZE) {
+ index = ring->prod & ring->size_mask;
+ ring->iobuf[index] = alloc_iob(size);
+ if (!&ring->iobuf[index]) {
+ if (ring->prod <= (ring->cons + 1)) {
+ eprintf("Error allocating Rx io "
+ "buffer number %lx", index);
+ /* In case of error freeing io buffer */
+ mtnic_free_io_buffers(ring);
+ return MTNIC_ERROR;
+ }
+
+ break;
+ }
+
+ /* Attach io_buffer to descriptor */
+ rx_desc_ptr = ring->buf +
+ (sizeof(struct mtnic_rx_desc) * index);
+ rx_desc_ptr->data.count = cpu_to_be32(size);
+ rx_desc_ptr->data.mem_type = priv->fw.mem_type_snoop_be;
+ rx_desc_ptr->data.addr_l = cpu_to_be32(
+ virt_to_bus(ring->iobuf[index]->data));
+
+ ++ ring->prod;
+ }
+
+ /* Update RX producer index (PI) */
+ ring->db->count = cpu_to_be32(ring->prod & 0xffff);
+ return 0;
+}
+
+
+/**
+ * mtnic alloc ring
+ *
+ * Alloc and configure TX or RX ring
+ *
+ */
+static int
+mtnic_alloc_ring(struct mtnic_priv *priv, struct mtnic_ring *ring,
+ u32 size, u16 stride, u16 cq, u8 is_rx)
+{
+ unsigned int i;
+ int err;
+ struct mtnic_rx_desc *rx_desc;
+ struct mtnic_tx_desc *tx_desc;
+
+ ring->size = size; /* Number of descriptors */
+ ring->size_mask = size - 1;
+ ring->stride = stride; /* Size of each entry */
+ ring->cq = cq; /* CQ number associated with this ring */
+ ring->cons = 0;
+ ring->prod = 0;
+
+ /* Alloc descriptors buffer */
+ ring->buf_size = ring->size * ((is_rx) ? sizeof(struct mtnic_rx_desc) :
+ sizeof(struct mtnic_tx_desc));
+ err = mtnic_alloc_aligned(ring->buf_size, (void *)&ring->buf,
+ &ring->dma, PAGE_SIZE);
+ if (err) {
+ eprintf("Failed allocating descriptor ring sizeof %lx\n",
+ ring->buf_size);
+ return MTNIC_ERROR;
+ }
+ memset(ring->buf, 0, ring->buf_size);
+
+ DBG("Allocated %s ring (addr:%p) - buf:%p size:%lx"
+ "buf_size:%lx dma:%lx\n",
+ is_rx ? "Rx" : "Tx", ring, ring->buf, ring->size,
+ ring->buf_size, ring->dma);
+
+
+ if (is_rx) { /* RX ring */
+ /* Alloc doorbell */
+ err = mtnic_alloc_aligned(sizeof(struct mtnic_cq_db_record),
+ (void *)&ring->db, &ring->db_dma, 32);
+ if (err) {
+ eprintf("Failed allocating Rx ring doorbell record\n");
+ free(ring->buf);
+ return MTNIC_ERROR;
+ }
+
+ /* ==- Configure Descriptor -== */
+ /* Init ctrl seg of rx desc */
+ for (i = 0; i < UNITS_BUFFER_SIZE; ++i) {
+ rx_desc = ring->buf +
+ (sizeof(struct mtnic_rx_desc) * i);
+ /* Pre-link descriptor */
+ rx_desc->next = cpu_to_be16(i + 1);
+ }
+ /*The last ctrl descriptor is '0' and points to the first one*/
+
+ /* Alloc IO_BUFFERS */
+ err = mtnic_alloc_iobuf(priv, ring, DEF_IOBUF_SIZE);
+ if (err) {
+ eprintf("ERROR Allocating io buffer");
+ free(ring->buf);
+ return MTNIC_ERROR;
+ }
+
+ } else { /* TX ring */
+ /* Set initial ownership of all Tx Desc' to SW (1) */
+ for (i = 0; i < ring->size; i++) {
+ tx_desc = ring->buf + ring->stride * i;
+ tx_desc->ctrl.op_own = cpu_to_be32(MTNIC_BIT_DESC_OWN);
+ }
+ /* DB */
+ ring->db_offset = cpu_to_be32(
+ ((u32) priv->fw.tx_offset[priv->port]) << 8);
+
+ /* Map Tx+CQ doorbells */
+ DBG("Mapping TxCQ doorbell at offset:0x%lx\n",
+ priv->fw.txcq_db_offset);
+ ring->txcq_db = ioremap(mtnic_pci_dev.dev.bar[2] +
+ priv->fw.txcq_db_offset, PAGE_SIZE);
+ if (!ring->txcq_db) {
+ eprintf("Couldn't map txcq doorbell, aborting...\n");
+ free(ring->buf);
+ return MTNIC_ERROR;
+ }
+ }
+
+ return 0;
+}
+
+
+
+/**
+ * mtnic alloc CQ
+ *
+ * Alloc and configure CQ.
+ *
+ */
+static int
+mtnic_alloc_cq(struct net_device *dev, int num, struct mtnic_cq *cq,
+ u8 is_rx, u32 size, u32 offset_ind)
+{
+ int err ;
+ unsigned int i;
+
+ cq->num = num;
+ cq->dev = dev;
+ cq->size = size;
+ cq->last = 0;
+ cq->is_rx = is_rx;
+ cq->offset_ind = offset_ind;
+
+ /* Alloc doorbell */
+ err = mtnic_alloc_aligned(sizeof(struct mtnic_cq_db_record),
+ (void *)&cq->db, &cq->db_dma, 32);
+ if (err) {
+ eprintf("Failed allocating CQ doorbell record\n");
+ return MTNIC_ERROR;
+ }
+ memset(cq->db, 0, sizeof(struct mtnic_cq_db_record));
+
+ /* Alloc CQEs buffer */
+ cq->buf_size = size * sizeof(struct mtnic_cqe);
+ err = mtnic_alloc_aligned(cq->buf_size,
+ (void *)&cq->buf, &cq->dma, PAGE_SIZE);
+ if (err) {
+ eprintf("Failed allocating CQ buffer\n");
+ free(cq->db);
+ return MTNIC_ERROR;
+ }
+ memset(cq->buf, 0, cq->buf_size);
+ DBG("Allocated CQ (addr:%p) - size:%lx buf:%p buf_size:%lx "
+ "dma:%lx db:%p db_dma:%lx\n"
+ "cqn offset:%lx \n", cq, cq->size, cq->buf,
+ cq->buf_size, cq->dma, cq->db,
+ cq->db_dma, offset_ind);
+
+
+ /* Set ownership of all CQEs to HW */
+ DBG("Setting HW ownership for CQ:%d\n", num);
+ for (i = 0; i < cq->size; i++) {
+ /* Initial HW ownership is 1 */
+ cq->buf[i].op_tr_own = MTNIC_BIT_CQ_OWN;
+ }
+ return 0;
+}
+
+
+
+/**
+ * mtnic_alloc_resources
+ *
+ * Alloc and configure CQs, Tx, Rx
+ */
+unsigned int
+mtnic_alloc_resources(struct net_device *dev)
+{
+ struct mtnic_priv *priv = netdev_priv(dev);
+ int err;
+ int cq_ind = 0;
+ int cq_offset = priv->fw.cq_offset;
+
+ /* Alloc 1st CQ */
+ err = mtnic_alloc_cq(dev, cq_ind, &priv->cq[cq_ind], 1 /* RX */,
+ UNITS_BUFFER_SIZE, cq_offset + cq_ind);
+ if (err) {
+ eprintf("Failed allocating Rx CQ\n");
+ return MTNIC_ERROR;
+ }
+
+ /* Alloc RX */
+ err = mtnic_alloc_ring(priv, &priv->rx_ring, UNITS_BUFFER_SIZE,
+ sizeof(struct mtnic_rx_desc), cq_ind, /* RX */1);
+ if (err) {
+ eprintf("Failed allocating Rx Ring\n");
+ goto cq0_error;
+ }
+
+ ++cq_ind;
+
+ /* alloc 2nd CQ */
+ err = mtnic_alloc_cq(dev, cq_ind, &priv->cq[cq_ind], 0 /* TX */,
+ UNITS_BUFFER_SIZE, cq_offset + cq_ind);
+ if (err) {
+ eprintf("Failed allocating Tx CQ\n");
+ goto rx_error;
+ }
+
+ /* Alloc TX */
+ err = mtnic_alloc_ring(priv, &priv->tx_ring, UNITS_BUFFER_SIZE,
+ sizeof(struct mtnic_tx_desc), cq_ind, /* TX */ 0);
+ if (err) {
+ eprintf("Failed allocating Tx ring\n");
+ goto cq1_error;
+ }
+
+ return 0;
+
+cq1_error:
+ free(priv->cq[1].buf);
+ free(priv->cq[1].db);
+rx_error:
+ free(priv->rx_ring.buf);
+ free(priv->rx_ring.db);
+ mtnic_free_io_buffers(&priv->rx_ring);
+cq0_error:
+ free(priv->cq[0].buf);
+ free(priv->cq[0].db);
+
+ return MTNIC_ERROR;
+}
+
+
+/**
+ * mtnic alloc_eq
+ *
+ * Note: EQ is not used by the driver but must be allocated
+ */
+static int
+mtnic_alloc_eq(struct mtnic_priv *priv)
+{
+ int err;
+ unsigned int i;
+ struct mtnic_eqe *eqe_desc = NULL;
+
+ /* Allocating doorbell */
+ priv->eq_db = ioremap(mtnic_pci_dev.dev.bar[2] +
+ priv->fw.eq_db_offset, sizeof(u32));
+ if (!priv->eq_db) {
+ eprintf("Couldn't map EQ doorbell, aborting...\n");
+ return MTNIC_ERROR;
+ }
+
+ /* Allocating buffer */
+ priv->eq.size = NUM_EQES;
+ priv->eq.buf_size = priv->eq.size * sizeof(struct mtnic_eqe);
+ err = mtnic_alloc_aligned(priv->eq.buf_size, (void *)&priv->eq.buf,
+ &priv->eq.dma, PAGE_SIZE);
+ if (err) {
+ eprintf("Failed allocating EQ buffer\n");
+ iounmap(priv->eq_db);
+ return MTNIC_ERROR;
+ }
+ memset(priv->eq.buf, 0, priv->eq.buf_size);
+
+ for (i = 0; i < priv->eq.size; i++)
+ eqe_desc = priv->eq.buf + (sizeof(struct mtnic_eqe) * i);
+ eqe_desc->own |= MTNIC_BIT_EQE_OWN;
+
+ mdelay(20);
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+/********************************************************************
+*
+* Mtnic commands functions
+* -=-=-=-=-=-=-=-=-=-=-=-=
+*
+*
+*
+*********************************************************************/
+static inline int
+cmdif_go_bit(struct mtnic_priv *priv)
+{
+ struct mtnic_if_cmd_reg *hcr = priv->hcr;
+ u32 status;
+ int i;
+
+ for (i = 0; i < TBIT_RETRIES; i++) {
+ status = be32_to_cpu(readl(&hcr->status_go_opcode));
+ if ((status & MTNIC_BC_MASK(MTNIC_MASK_CMD_REG_T_BIT)) ==
+ (priv->cmd.tbit << MTNIC_BC_OFF(MTNIC_MASK_CMD_REG_T_BIT))) {
+ /* Read expected t-bit - now return go-bit value */
+ return status & MTNIC_BC_MASK(MTNIC_MASK_CMD_REG_GO_BIT);
+ }
+ }
+
+ eprintf("Invalid tbit after %d retries!\n", TBIT_RETRIES);
+ return 1; /* Return busy... */
+}
+
+/* Base Command interface */
+static int
+mtnic_cmd(struct mtnic_priv *priv, void *in_imm,
+ void *out_imm, u32 in_modifier, u16 op)
+{
+
+ struct mtnic_if_cmd_reg *hcr = priv->hcr;
+ int err = 0;
+ u32 out_param_h = 0;
+ u32 out_param_l = 0;
+ u32 in_param_h = 0;
+ u32 in_param_l = 0;
+
+
+ static u16 token = 0x8000;
+ u32 status;
+ unsigned int timeout = 0;
+
+ token++;
+
+ if (cmdif_go_bit(priv)) {
+ eprintf("GO BIT BUSY:%p.\n", hcr + 6);
+ err = MTNIC_ERROR;
+ goto out;
+ }
+ if (in_imm) {
+ in_param_h = *((u32*)in_imm);
+ in_param_l = *((u32*)in_imm + 1);
+ } else {
+ in_param_l = cpu_to_be32(priv->cmd.mapping);
+ }
+ out_param_l = cpu_to_be32(priv->cmd.mapping);
+
+ /* writing to MCR */
+ writel(in_param_h, &hcr->in_param_h);
+ writel(in_param_l, &hcr->in_param_l);
+ writel((u32) cpu_to_be32(in_modifier), &hcr->input_modifier);
+ writel(out_param_h, &hcr->out_param_h);
+ writel(out_param_l, &hcr->out_param_l);
+ writel((u32)cpu_to_be32(token << 16), &hcr->token);
+ wmb();
+
+ /* flip toggle bit before each write to the HCR */
+ priv->cmd.tbit = !priv->cmd.tbit;
+ writel((u32)
+ cpu_to_be32(MTNIC_BC_MASK(MTNIC_MASK_CMD_REG_GO_BIT) |
+ (priv->cmd.tbit << MTNIC_BC_OFF(MTNIC_MASK_CMD_REG_T_BIT)) | op),
+ &hcr->status_go_opcode);
+
+ while (cmdif_go_bit(priv) && (timeout <= GO_BIT_TIMEOUT)) {
+ mdelay(1);
+ ++timeout;
+ }
+
+ if (cmdif_go_bit(priv)) {
+ eprintf("Command opcode:0x%x token:0x%x TIMEOUT.\n", op, token);
+ err = MTNIC_ERROR;
+ goto out;
+ }
+
+ if (out_imm) {
+ *((u32 *)out_imm) = readl(&hcr->out_param_h);
+ *((u32 *)out_imm + 1) = readl(&hcr->out_param_l);
+ }
+
+ status = be32_to_cpu((u32)readl(&hcr->status_go_opcode)) >> 24;
+ /*DBG("Command opcode:0x%x token:0x%x returned:0x%lx\n",
+ op, token, status);*/
+
+ if (status) {
+ return status;
+ }
+
+out:
+ return err;
+}
+
+/* MAP PAGES wrapper */
+static int
+mtnic_map_cmd(struct mtnic_priv *priv, u16 op, struct mtnic_pages pages)
+{
+ unsigned int j;
+ u32 addr;
+ unsigned int len;
+ u32 *page_arr = priv->cmd.buf;
+ int nent = 0;
+ int err = 0;
+
+ memset(page_arr, 0, PAGE_SIZE);
+
+ len = PAGE_SIZE * pages.num;
+ pages.buf = (u32 *)umalloc(PAGE_SIZE * (pages.num + 1));
+ addr = PAGE_SIZE + ((virt_to_bus(pages.buf) & 0xfffff000) + PAGE_SIZE);
+ DBG("Mapping pages: size: %lx address: %p\n", pages.num, pages.buf);
+
+ if (addr & (PAGE_MASK)) {
+ eprintf("Got FW area not aligned to %d (%llx/%x)\n",
+ PAGE_SIZE, (u64) addr, len);
+ return MTNIC_ERROR;
+ }
+
+ /* Function maps each PAGE seperately */
+ for (j = 0; j < len; j+= PAGE_SIZE) {
+ page_arr[nent * 4 + 3] = cpu_to_be32(addr + j);
+ if (++nent == MTNIC_MAILBOX_SIZE / 16) {
+ err = mtnic_cmd(priv, NULL, NULL, nent, op);
+ if (err)
+ return MTNIC_ERROR;
+ nent = 0;
+ }
+ }
+
+ if (nent)
+ err = mtnic_cmd(priv, NULL, NULL, nent, op);
+
+ return err;
+}
+
+
+
+/*
+ * Query FW
+ */
+static int
+mtnic_QUERY_FW(struct mtnic_priv *priv)
+{
+ int err;
+ struct mtnic_if_query_fw_out_mbox *cmd = priv->cmd.buf;
+
+ err = mtnic_cmd(priv, NULL, NULL, 0, MTNIC_IF_CMD_QUERY_FW);
+ if (err)
+ return MTNIC_ERROR;
+
+ /* Get FW and interface versions */
+ priv->fw_ver = ((u64) be16_to_cpu(cmd->rev_maj) << 32) |
+ ((u64) be16_to_cpu(cmd->rev_min) << 16) |
+ (u64) be16_to_cpu(cmd->rev_smin);
+ priv->fw.ifc_rev = be16_to_cpu(cmd->ifc_rev);
+
+ /* Get offset for internal error reports (debug) */
+ priv->fw.err_buf.offset = be64_to_cpu(cmd->err_buf_start);
+ priv->fw.err_buf.size = be32_to_cpu(cmd->err_buf_size);
+
+ DBG("Error buf offset is %llx\n", priv->fw.err_buf.offset);
+
+ /* Get number of required FW (4k) pages */
+ priv->fw.fw_pages.num = be16_to_cpu(cmd->fw_pages);
+
+ return 0;
+}
+
+
+static int
+mtnic_OPEN_NIC(struct mtnic_priv *priv)
+{
+
+ struct mtnic_if_open_nic_in_mbox *open_nic = priv->cmd.buf;
+ u32 extra_pages[2] = {0};
+ int err;
+
+ memset(open_nic, 0, sizeof *open_nic);
+
+ /* port 1 */
+ open_nic->log_rx_p1 = 0;
+ open_nic->log_cq_p1 = 1;
+
+ open_nic->log_tx_p1 = 0;
+ open_nic->steer_p1 = MTNIC_IF_STEER_RSS;
+ /* MAC + VLAN - leave reserved */
+
+ /* port 2 */
+ open_nic->log_rx_p2 = 0;
+ open_nic->log_cq_p2 = 1;
+
+ open_nic->log_tx_p2 = 0;
+ open_nic->steer_p2 = MTNIC_IF_STEER_RSS;
+ /* MAC + VLAN - leave reserved */
+
+ err = mtnic_cmd(priv, NULL, extra_pages, 0, MTNIC_IF_CMD_OPEN_NIC);
+ priv->fw.extra_pages.num = be32_to_cpu(*(extra_pages+1));
+ DBG("Extra pages num is %lx\n", priv->fw.extra_pages.num);
+ return err;
+}
+
+static int
+mtnic_CONFIG_RX(struct mtnic_priv *priv)
+{
+ struct mtnic_if_config_rx_in_imm config_rx;
+
+ memset(&config_rx, 0, sizeof config_rx);
+ return mtnic_cmd(priv, &config_rx, NULL, 0, MTNIC_IF_CMD_CONFIG_RX);
+}
+
+static int
+mtnic_CONFIG_TX(struct mtnic_priv *priv)
+{
+ struct mtnic_if_config_send_in_imm config_tx;
+
+ config_tx.enph_gpf = 0;
+ return mtnic_cmd(priv, &config_tx, NULL, 0, MTNIC_IF_CMD_CONFIG_TX);
+}
+
+static int
+mtnic_HEART_BEAT(struct mtnic_priv *priv, u32 *link_state)
+{
+ struct mtnic_if_heart_beat_out_imm heart_beat;
+
+ int err;
+ u32 flags;
+ err = mtnic_cmd(priv, NULL, &heart_beat, 0, MTNIC_IF_CMD_HEART_BEAT);
+ if (!err) {
+ flags = be32_to_cpu(heart_beat.flags);
+ if (flags & MTNIC_BC_MASK(MTNIC_MASK_HEAR_BEAT_INT_ERROR)) {
+ eprintf("Internal error detected\n");
+ return MTNIC_ERROR;
+ }
+ *link_state = flags &
+ ~((u32) MTNIC_BC_MASK(MTNIC_MASK_HEAR_BEAT_INT_ERROR));
+ }
+ return err;
+}
+
+
+/*
+ * Port commands
+ */
+
+static int
+mtnic_SET_PORT_DEFAULT_RING(struct mtnic_priv *priv, u8 port, u16 ring)
+{
+ struct mtnic_if_set_port_default_ring_in_imm def_ring;
+
+ memset(&def_ring, 0, sizeof(def_ring));
+ def_ring.ring = ring;
+ return mtnic_cmd(priv, &def_ring, NULL, port + 1,
+ MTNIC_IF_CMD_SET_PORT_DEFAULT_RING);
+}
+
+static int
+mtnic_CONFIG_PORT_RSS_STEER(struct mtnic_priv *priv, int port)
+{
+ memset(priv->cmd.buf, 0, PAGE_SIZE);
+ return mtnic_cmd(priv, NULL, NULL, port + 1,
+ MTNIC_IF_CMD_CONFIG_PORT_RSS_STEER);
+}
+
+static int
+mtnic_SET_PORT_RSS_INDIRECTION(struct mtnic_priv *priv, int port)
+
+{
+ memset(priv->cmd.buf, 0, PAGE_SIZE);
+ return mtnic_cmd(priv, NULL, NULL, port + 1,
+ MTNIC_IF_CMD_SET_PORT_RSS_INDIRECTION);
+}
+
+
+/*
+ * Config commands
+ */
+static int
+mtnic_CONFIG_CQ(struct mtnic_priv *priv, int port,
+ u16 cq_ind, struct mtnic_cq *cq)
+{
+ struct mtnic_if_config_cq_in_mbox *config_cq = priv->cmd.buf;
+
+ memset(config_cq, 0, sizeof *config_cq);
+ config_cq->cq = cq_ind;
+ config_cq->size = fls(UNITS_BUFFER_SIZE - 1);
+ config_cq->offset = ((cq->dma) & (PAGE_MASK)) >> 6;
+ config_cq->db_record_addr_l = cpu_to_be32(cq->db_dma);
+ config_cq->page_address[1] = cpu_to_be32(cq->dma);
+ DBG("config cq address: %lx dma_address: %lx"
+ "offset: %d size %d index: %d "
+ , config_cq->page_address[1],cq->dma,
+ config_cq->offset, config_cq->size, config_cq->cq );
+
+ return mtnic_cmd(priv, NULL, NULL, port + 1,
+ MTNIC_IF_CMD_CONFIG_CQ);
+}
+
+
+static int
+mtnic_CONFIG_TX_RING(struct mtnic_priv *priv, u8 port,
+ u16 ring_ind, struct mtnic_ring *ring)
+{
+ struct mtnic_if_config_send_ring_in_mbox *config_tx_ring = priv->cmd.buf;
+ memset(config_tx_ring, 0, sizeof *config_tx_ring);
+ config_tx_ring->ring = cpu_to_be16(ring_ind);
+ config_tx_ring->size = fls(UNITS_BUFFER_SIZE - 1);
+ config_tx_ring->cq = cpu_to_be16(ring->cq);
+ config_tx_ring->page_address[1] = cpu_to_be32(ring->dma);
+
+ return mtnic_cmd(priv, NULL, NULL, port + 1,
+ MTNIC_IF_CMD_CONFIG_TX_RING);
+}
+
+static int
+mtnic_CONFIG_RX_RING(struct mtnic_priv *priv, u8 port,
+ u16 ring_ind, struct mtnic_ring *ring)
+{
+ struct mtnic_if_config_rx_ring_in_mbox *config_rx_ring = priv->cmd.buf;
+ memset(config_rx_ring, 0, sizeof *config_rx_ring);
+ config_rx_ring->ring = ring_ind;
+ MTNIC_BC_PUT(config_rx_ring->stride_size, fls(UNITS_BUFFER_SIZE - 1),
+ MTNIC_MASK_CONFIG_RX_RING_SIZE);
+ MTNIC_BC_PUT(config_rx_ring->stride_size, 1,
+ MTNIC_MASK_CONFIG_RX_RING_STRIDE);
+ config_rx_ring->cq = cpu_to_be16(ring->cq);
+ config_rx_ring->db_record_addr_l = cpu_to_be32(ring->db_dma);
+
+ DBG("Config RX ring starting at address:%lx\n", ring->dma);
+
+ config_rx_ring->page_address[1] = cpu_to_be32(ring->dma);
+
+ return mtnic_cmd(priv, NULL, NULL, port + 1,
+ MTNIC_IF_CMD_CONFIG_RX_RING);
+}
+
+static int
+mtnic_CONFIG_EQ(struct mtnic_priv *priv)
+{
+ struct mtnic_if_config_eq_in_mbox *eq = priv->cmd.buf;
+
+ if (priv->eq.dma & (PAGE_MASK)) {
+ eprintf("misalligned eq buffer:%lx\n",
+ priv->eq.dma);
+ return MTNIC_ERROR;
+ }
+
+ memset(eq, 0, sizeof *eq);
+ MTNIC_BC_PUT(eq->offset, priv->eq.dma >> 6, MTNIC_MASK_CONFIG_EQ_OFFSET);
+ MTNIC_BC_PUT(eq->size, fls(priv->eq.size - 1) - 1, MTNIC_MASK_CONFIG_EQ_SIZE);
+ MTNIC_BC_PUT(eq->int_vector, 0, MTNIC_MASK_CONFIG_EQ_INT_VEC);
+ eq->page_address[1] = cpu_to_be32(priv->eq.dma);
+
+ return mtnic_cmd(priv, NULL, NULL, 0, MTNIC_IF_CMD_CONFIG_EQ);
+}
+
+
+
+
+static int
+mtnic_SET_RX_RING_ADDR(struct mtnic_priv *priv, u8 port, u64* mac)
+{
+ struct mtnic_if_set_rx_ring_addr_in_imm ring_addr;
+ u32 modifier = ((u32) port + 1) << 16;
+
+ memset(&ring_addr, 0, sizeof(ring_addr));
+
+ ring_addr.mac_31_0 = cpu_to_be32(*mac & 0xffffffff);
+ ring_addr.mac_47_32 = cpu_to_be16((*mac >> 32) & 0xffff);
+ ring_addr.flags_vlan_id |= cpu_to_be16(
+ MTNIC_BC_MASK(MTNIC_MASK_SET_RX_RING_ADDR_BY_MAC));
+
+ return mtnic_cmd(priv, &ring_addr, NULL, modifier, MTNIC_IF_CMD_SET_RX_RING_ADDR);
+}
+
+static int
+mtnic_SET_PORT_STATE(struct mtnic_priv *priv, u8 port, u8 state)
+{
+ struct mtnic_if_set_port_state_in_imm port_state;
+
+ port_state.state = state ? cpu_to_be32(
+ MTNIC_BC_MASK(MTNIC_MASK_CONFIG_PORT_STATE)) : 0;
+ port_state.reserved = 0;
+ return mtnic_cmd(priv, &port_state, NULL, port + 1,
+ MTNIC_IF_CMD_SET_PORT_STATE);
+}
+
+static int
+mtnic_SET_PORT_MTU(struct mtnic_priv *priv, u8 port, u16 mtu)
+{
+ struct mtnic_if_set_port_mtu_in_imm set_mtu;
+
+ memset(&set_mtu, 0, sizeof(set_mtu));
+ set_mtu.mtu = cpu_to_be16(mtu);
+ return mtnic_cmd(priv, &set_mtu, NULL, port + 1,
+ MTNIC_IF_CMD_SET_PORT_MTU);
+}
+
+
+static int
+mtnic_CONFIG_PORT_VLAN_FILTER(struct mtnic_priv *priv, int port)
+{
+ struct mtnic_if_config_port_vlan_filter_in_mbox *vlan_filter = priv->cmd.buf;
+
+ /* When no vlans are configured we disable the filter
+ * (i.e., pass all vlans) because we ignore them anyhow */
+ memset(vlan_filter, 0xff, sizeof(*vlan_filter));
+ return mtnic_cmd(priv, NULL, NULL, port + 1,
+ MTNIC_IF_CMD_CONFIG_PORT_VLAN_FILTER);
+}
+
+
+static int
+mtnic_RELEASE_RESOURCE(struct mtnic_priv *priv, u8 port, u8 type, u8 index)
+{
+ struct mtnic_if_release_resource_in_imm rel;
+ memset(&rel, 0, sizeof rel);
+ rel.index = index;
+ rel.type = type;
+ return mtnic_cmd(priv,
+ &rel, NULL, (type == MTNIC_IF_RESOURCE_TYPE_EQ) ?
+ 0 : port + 1, MTNIC_IF_CMD_RELEASE_RESOURCE);
+}
+
+
+static int
+mtnic_QUERY_CAP(struct mtnic_priv *priv, u8 index, u8 mod, u64 *result)
+{
+ struct mtnic_if_query_cap_in_imm cap;
+ u32 out_imm[2];
+ int err;
+
+ memset(&cap, 0, sizeof cap);
+ cap.cap_index = index;
+ cap.cap_modifier = mod;
+ err = mtnic_cmd(priv, &cap, &out_imm, 0, MTNIC_IF_CMD_QUERY_CAP);
+
+ *((u32*)result) = be32_to_cpu(*(out_imm+1));
+ *((u32*)result + 1) = be32_to_cpu(*out_imm);
+
+ DBG("Called Query cap with index:0x%x mod:%d result:0x%llx"
+ " error:%d\n", index, mod, *result, err);
+ return err;
+}
+
+
+#define DO_QUERY_CAP(cap, mod, var) \
+ err = mtnic_QUERY_CAP(priv, cap, mod, &result); \
+ if (err) \
+ return err; \
+ (var) = result
+
+static int
+mtnic_query_cap(struct mtnic_priv *priv)
+{
+ int err = 0;
+ int i;
+ u64 result;
+
+ DO_QUERY_CAP(MTNIC_IF_CAP_NUM_PORTS, 0, priv->fw.num_ports);
+ for (i = 0; i < priv->fw.num_ports; i++) {
+ DO_QUERY_CAP(MTNIC_IF_CAP_DEFAULT_MAC, i + 1, priv->fw.mac[i]);
+ }
+
+ return 0;
+}
+
+static int
+mtnic_query_offsets(struct mtnic_priv *priv)
+{
+ int err;
+ int i;
+ u64 result;
+
+ DO_QUERY_CAP(MTNIC_IF_CAP_MEM_KEY,
+ MTNIC_IF_MEM_TYPE_SNOOP,
+ priv->fw.mem_type_snoop_be);
+ priv->fw.mem_type_snoop_be = cpu_to_be32(priv->fw.mem_type_snoop_be);
+ DO_QUERY_CAP(MTNIC_IF_CAP_TX_CQ_DB_OFFSET, 0, priv->fw.txcq_db_offset);
+ DO_QUERY_CAP(MTNIC_IF_CAP_EQ_DB_OFFSET, 0, priv->fw.eq_db_offset);
+
+ for (i = 0; i < priv->fw.num_ports; i++) {
+ DO_QUERY_CAP(MTNIC_IF_CAP_CQ_OFFSET, i + 1, priv->fw.cq_offset);
+ DO_QUERY_CAP(MTNIC_IF_CAP_TX_OFFSET, i + 1, priv->fw.tx_offset[i]);
+ DO_QUERY_CAP(MTNIC_IF_CAP_RX_OFFSET, i + 1, priv->fw.rx_offset[i]);
+ DBG("--> Port %d CQ offset:0x%x\n", i, priv->fw.cq_offset);
+ DBG("--> Port %d Tx offset:0x%x\n", i, priv->fw.tx_offset[i]);
+ DBG("--> Port %d Rx offset:0x%x\n", i, priv->fw.rx_offset[i]);
+ }
+
+ mdelay(20);
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+/********************************************************************
+*
+* MTNIC initalization functions
+*
+*
+*
+*
+*********************************************************************/
+
+/**
+ * Reset device
+ */
+void
+mtnic_reset(void)
+{
+ void *reset = ioremap(mtnic_pci_dev.dev.bar[0] + MTNIC_RESET_OFFSET, 4);
+ writel(cpu_to_be32(1), reset);
+ iounmap(reset);
+}
+
+
+/**
+ * Restore PCI config
+ */
+static int
+restore_config(void)
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < 64; ++i) {
+ if (i != 22 && i != 23) {
+ rc = pci_write_config_dword(mtnic_pci_dev.dev.dev,
+ i << 2,
+ mtnic_pci_dev.dev.
+ dev_config_space[i]);
+ if (rc)
+ return rc;
+ }
+ }
+ return 0;
+}
+
+
+
+/**
+ * Init PCI configuration
+ */
+static int
+mtnic_init_pci(struct pci_device *dev)
+{
+ int i;
+ int err;
+
+ /* save bars */
+ DBG("bus=%d devfn=0x%x", dev->bus, dev->devfn);
+ for (i = 0; i < 6; ++i) {
+ mtnic_pci_dev.dev.bar[i] =
+ pci_bar_start(dev, PCI_BASE_ADDRESS_0 + (i << 2));
+ DBG("bar[%d]= 0x%08lx \n", i, mtnic_pci_dev.dev.bar[i]);
+ }
+
+ /* save config space */
+ for (i = 0; i < 64; ++i) {
+ err = pci_read_config_dword(dev, i << 2,
+ &mtnic_pci_dev.dev.
+ dev_config_space[i]);
+ if (err) {
+ eprintf("Can not save configuration space");
+ return err;
+ }
+ }
+
+ mtnic_pci_dev.dev.dev = dev;
+
+ return 0;
+}
+
+/**
+ * Initial hardware
+ */
+static inline
+int mtnic_init_card(struct net_device *dev)
+{
+ struct mtnic_priv *priv = netdev_priv(dev);
+ int err = 0;
+
+
+ /* Set state */
+ priv->state = CARD_DOWN;
+ /* Set port */
+ priv->port = MTNIC_PORT_NUM;
+
+ /* Alloc command interface */
+ err = mtnic_alloc_cmdif(priv);
+ if (err) {
+ eprintf("Failed to init command interface, aborting.\n");
+ return MTNIC_ERROR;
+ }
+
+
+
+ /**
+ * Bring up HW
+ */
+ err = mtnic_QUERY_FW(priv);
+ if (err) {
+ eprintf("QUERY_FW command failed, aborting.\n");
+ goto cmd_error;
+ }
+
+ DBG("Command interface revision:%d\n", priv->fw.ifc_rev);
+
+ /* Allocate memory for FW and start it */
+ err = mtnic_map_cmd(priv, MTNIC_IF_CMD_MAP_FW, priv->fw.fw_pages);
+ if (err) {
+ eprintf("Eror In MAP_FW\n");
+ if (priv->fw.fw_pages.buf)
+ free(priv->fw.fw_pages.buf);
+ goto cmd_error;
+ }
+
+ /* Run firmware */
+ err = mtnic_cmd(priv, NULL, NULL, 0, MTNIC_IF_CMD_RUN_FW);
+ if (err) {
+ eprintf("Eror In RUN FW\n");
+ goto map_fw_error;
+ }
+
+ DBG("FW version:%d.%d.%d\n",
+ (u16) (priv->fw_ver >> 32),
+ (u16) ((priv->fw_ver >> 16) & 0xffff),
+ (u16) (priv->fw_ver & 0xffff));
+
+
+ /* Get device information */
+ err = mtnic_query_cap(priv);
+ if (err) {
+ eprintf("Insufficient resources, aborting.\n");
+ goto map_fw_error;
+ }
+
+ /* Open NIC */
+ err = mtnic_OPEN_NIC(priv);
+ if (err) {
+ eprintf("Failed opening NIC, aborting.\n");
+ goto map_fw_error;
+ }
+
+ /* Allocate and map pages worksace */
+ err = mtnic_map_cmd(priv, MTNIC_IF_CMD_MAP_PAGES, priv->fw.extra_pages);
+ if (err) {
+ eprintf("Couldn't allocate %lx FW extra pages, aborting.\n",
+ priv->fw.extra_pages.num);
+ if (priv->fw.extra_pages.buf)
+ free(priv->fw.extra_pages.buf);
+ goto map_fw_error;
+ }
+
+ /* Get device offsets */
+ err = mtnic_query_offsets(priv);
+ if (err) {
+ eprintf("Failed retrieving resource offests, aborting.\n");
+ free(priv->fw.extra_pages.buf);
+ goto map_extra_error;
+ }
+
+
+ /* Alloc EQ */
+ err = mtnic_alloc_eq(priv);
+ if (err) {
+ eprintf("Failed init shared resources. error: %d\n", err);
+ goto map_extra_error;
+ }
+
+ /* Configure HW */
+ err = mtnic_CONFIG_EQ(priv);
+ if (err) {
+ eprintf("Failed configuring EQ\n");
+ goto eq_error;
+ }
+ err = mtnic_CONFIG_RX(priv);
+ if (err) {
+ eprintf("Failed Rx configuration\n");
+ goto eq_error;
+ }
+ err = mtnic_CONFIG_TX(priv);
+ if (err) {
+ eprintf("Failed Tx configuration\n");
+ goto eq_error;
+ }
+
+ DBG("Activating port:%d\n", MTNIC_PORT_NUM + 1);
+
+ priv->state = CARD_INITIALIZED;
+
+ return 0;
+
+
+eq_error:
+ iounmap(priv->eq_db);
+ free(priv->eq.buf);
+map_extra_error:
+ free(priv->fw.extra_pages.buf);
+map_fw_error:
+ free(priv->fw.fw_pages.buf);
+
+cmd_error:
+ iounmap(priv->hcr);
+ free(priv->cmd.buf);
+ free(priv);
+
+ return MTNIC_ERROR;
+}
+
+
+
+
+
+
+
+
+
+
+/*******************************************************************
+*
+* Process functions
+*
+* process compliations of TX and RX
+*
+*
+********************************************************************/
+void mtnic_process_tx_cq(struct mtnic_priv *priv, struct net_device *dev,
+ struct mtnic_cq *cq)
+{
+ struct mtnic_cqe *cqe = cq->buf;
+ struct mtnic_ring *ring = &priv->tx_ring;
+ u16 index;
+
+
+ index = cq->last & (cq->size-1);
+ cqe = &cq->buf[index];
+
+ /* Owner bit changes every round */
+ while (XNOR(cqe->op_tr_own & MTNIC_BIT_CQ_OWN, cq->last & cq->size)) {
+ netdev_tx_complete (dev, ring->iobuf[index]);
+ ++cq->last;
+ index = cq->last & (cq->size-1);
+ cqe = &cq->buf[index];
+ }
+
+ /* Update consumer index */
+ cq->db->update_ci = cpu_to_be32(cq->last & 0xffffff);
+ wmb(); /* ensure HW sees CQ consumer before we post new buffers */
+ ring->cons = cq->last;
+}
+
+
+int mtnic_process_rx_cq(struct mtnic_priv *priv, struct net_device *dev, struct mtnic_cq *cq)
+{
+ struct mtnic_cqe *cqe;
+ struct mtnic_ring *ring = &priv->rx_ring;
+ int index;
+ int err;
+ struct io_buffer *rx_iob;
+
+
+ /* We assume a 1:1 mapping between CQEs and Rx descriptors, so Rx
+ * descriptor offset can be deduced from the CQE index instead of
+ * reading 'cqe->index' */
+ index = cq->last & (cq->size-1);
+ cqe = &cq->buf[index];
+
+ /* Process all completed CQEs */
+ while (XNOR(cqe->op_tr_own & MTNIC_BIT_CQ_OWN, cq->last & cq->size)) {
+ /* Drop packet on bad receive or bad checksum */
+ if ((cqe->op_tr_own & 0x1f) == MTNIC_OPCODE_ERROR) {
+ DBG("CQE completed with error - vendor \n");
+ free_iob(ring->iobuf[index]);
+ goto next;
+ }
+ if (cqe->enc_bf & MTNIC_BIT_BAD_FCS) {
+ DBG("Accepted packet with bad FCS\n");
+ free_iob(ring->iobuf[index]);
+ goto next;
+ }
+
+ /*
+ * Packet is OK - process it.
+ */
+ rx_iob = ring->iobuf[index];
+ iob_put(rx_iob, DEF_IOBUF_SIZE);
+ /* Add this packet to the receive queue. */
+ netdev_rx(dev, rx_iob);
+ ring->iobuf[index] = NULL;
+
+next:
+ ++cq->last;
+ index = cq->last & (cq->size-1);
+ cqe = &cq->buf[index];
+ }
+
+ /* Update consumer index */
+ cq->db->update_ci = cpu_to_be32(cq->last & 0xffffff);
+ wmb(); /* ensure HW sees CQ consumer before we post new buffers */
+ ring->cons = cq->last;
+
+ if (ring->prod - ring->cons < (MAX_GAP_PROD_CONS)) {
+ err = mtnic_alloc_iobuf(priv, &priv->rx_ring, DEF_IOBUF_SIZE);
+ if (err) {
+ eprintf("ERROR Allocating io buffer");
+ return MTNIC_ERROR;
+ }
+ }
+
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/********************************************************************
+*
+* net_device functions
+*
+*
+* open, poll, close, probe, disable, irq
+*
+*********************************************************************/
+static int
+mtnic_open(struct net_device *dev)
+{
+ struct mtnic_priv *priv = netdev_priv(dev);
+ int err = 0;
+ struct mtnic_ring *ring;
+ struct mtnic_cq *cq;
+ int cq_ind = 0;
+ u32 dev_link_state;
+
+ DBG("starting port:%d", priv->port);
+
+ /* Alloc and configure CQs, TX, RX */
+ err = mtnic_alloc_resources(dev);
+ if (err) {
+ eprintf("Error allocating resources\n");
+ return MTNIC_ERROR;
+ }
+
+ /* Pass CQs configuration to HW */
+ for (cq_ind = 0; cq_ind < NUM_CQS; ++cq_ind) {
+ cq = &priv->cq[cq_ind];
+ err = mtnic_CONFIG_CQ(priv, priv->port, cq_ind, cq);
+ if (err) {
+ eprintf("Failed configuring CQ:%d error %d\n",
+ cq_ind, err);
+ if (cq_ind)
+ goto cq_error;
+ else
+ return MTNIC_ERROR;
+ }
+ /* Update consumer index */
+ cq->db->update_ci = cpu_to_be32(cq->last & 0xffffff);
+ }
+
+
+ /* Pass Tx configuration to HW */
+ ring = &priv->tx_ring;
+ err = mtnic_CONFIG_TX_RING(priv, priv->port, 0, ring);
+ if (err) {
+ eprintf("Failed configuring Tx ring:0\n");
+ goto cq_error;
+ }
+
+ /* Pass RX configuration to HW */
+ ring = &priv->rx_ring;
+ err = mtnic_CONFIG_RX_RING(priv, priv->port, 0, ring);
+ if (err) {
+ eprintf("Failed configuring Rx ring:0\n");
+ goto tx_error;
+ }
+
+ /* Configure Rx steering */
+ err = mtnic_CONFIG_PORT_RSS_STEER(priv, priv->port);
+ if (!err)
+ err = mtnic_SET_PORT_RSS_INDIRECTION(priv, priv->port);
+ if (err) {
+ eprintf("Failed configuring RSS steering\n");
+ goto rx_error;
+ }
+
+ /* Set the port default ring to ring 0 */
+ err = mtnic_SET_PORT_DEFAULT_RING(priv, priv->port, 0);
+ if (err) {
+ eprintf("Failed setting default ring\n");
+ goto rx_error;
+ }
+
+ /* Set Mac address */
+ err = mtnic_SET_RX_RING_ADDR(priv, priv->port, &priv->fw.mac[priv->port]);
+ if (err) {
+ eprintf("Failed setting default MAC address\n");
+ goto rx_error;
+ }
+
+ /* Set MTU */
+ err = mtnic_SET_PORT_MTU(priv, priv->port, DEF_MTU);
+ if (err) {
+ eprintf("Failed setting MTU\n");
+ goto rx_error;
+ }
+
+ /* Configure VLAN filter */
+ err = mtnic_CONFIG_PORT_VLAN_FILTER(priv, priv->port);
+ if (err) {
+ eprintf("Failed configuring VLAN filter\n");
+ goto rx_error;
+ }
+
+ /* Bring up physical link */
+ err = mtnic_SET_PORT_STATE(priv, priv->port, 1);
+ if (err) {
+ eprintf("Failed bringing up port\n");
+ goto rx_error;
+ }
+ mdelay(300); /* Let link state stabilize if cable was connected */
+
+ priv->state = CARD_UP;
+
+ err = mtnic_HEART_BEAT(priv, &dev_link_state);
+ if (err) {
+ eprintf("Failed getting device link state\n");
+ return MTNIC_ERROR;
+ }
+ if (!(dev_link_state & 0x3)) {
+ eprintf("Link down, check cables and restart\n");
+ return MTNIC_ERROR;
+ }
+
+ return 0;
+
+
+rx_error:
+ err = mtnic_RELEASE_RESOURCE(priv, priv->port,
+ MTNIC_IF_RESOURCE_TYPE_RX_RING, 0);
+tx_error:
+ err |= mtnic_RELEASE_RESOURCE(priv, priv->port,
+ MTNIC_IF_RESOURCE_TYPE_TX_RING, 0);
+cq_error:
+ while (cq_ind) {
+ err |= mtnic_RELEASE_RESOURCE(priv, priv->port,
+ MTNIC_IF_RESOURCE_TYPE_CQ, --cq_ind);
+ }
+ if (err)
+ DBG("Eror Releasing resources\n");
+
+ return MTNIC_ERROR;
+}
+
+
+
+/** Check if we got completion for receive and transmit and
+ * check the line with heart_bit command */
+static void
+mtnic_poll(struct net_device *dev)
+{
+ struct mtnic_priv *priv = netdev_priv(dev);
+ struct mtnic_cq *cq;
+ u32 dev_link_state;
+ int err;
+ unsigned int i;
+
+ /* In case of an old error then return */
+ if (priv->state != CARD_UP)
+ return;
+
+ /* We do not check the device every call _poll call,
+ since it will slow it down */
+ if ((priv->poll_counter % ROUND_TO_CHECK) == 0) {
+ /* Check device */
+ err = mtnic_HEART_BEAT(priv, &dev_link_state);
+ if (err) {
+ eprintf("Device has internal error\n");
+ priv->state = CARD_DOWN;
+ return;
+ }
+ if (!(dev_link_state & 0x3)) {
+ eprintf("Link down, check cables and restart\n");
+ priv->state = CARD_DOWN;
+ return;
+ }
+ }
+
+ /* Polling CQ */
+ for (i = 0; i < NUM_CQS; i++) {
+ cq = &priv->cq[i]; //Passing on the 2 cqs.
+
+ if (cq->is_rx) {
+ err = mtnic_process_rx_cq(priv, cq->dev, cq);
+ if (err) {
+ priv->state = CARD_DOWN;
+ eprintf(" Error allocating RX buffers\n");
+ return;
+ }
+ } else {
+ mtnic_process_tx_cq(priv, cq->dev, cq);
+ }
+ }
+ ++ priv->poll_counter;
+}
+
+static int
+mtnic_transmit( struct net_device *dev, struct io_buffer *iobuf )
+{
+
+ struct mtnic_priv *priv = netdev_priv(dev);
+ struct mtnic_ring *ring;
+ struct mtnic_tx_desc *tx_desc;
+ struct mtnic_data_seg *data;
+ u32 index;
+
+ /* In case of an error then return */
+ if (priv->state != CARD_UP)
+ return MTNIC_ERROR;
+
+ ring = &priv->tx_ring;
+
+ index = ring->prod & ring->size_mask;
+ if ((ring->prod - ring->cons) >= ring->size) {
+ DBG("No space left for descriptors!!! cons: %lx prod: %lx\n",
+ ring->cons, ring->prod);
+ mdelay(5);
+ return MTNIC_ERROR;/* no space left */
+ }
+
+ /* get current descriptor */
+ tx_desc = ring->buf + (index * sizeof(struct mtnic_tx_desc));
+
+ /* Prepare ctrl segement */
+ tx_desc->ctrl.size_vlan = cpu_to_be32(2);
+ tx_desc->ctrl.flags = cpu_to_be32(MTNIC_BIT_TX_COMP |
+ MTNIC_BIT_NO_ICRC);
+ tx_desc->ctrl.op_own = cpu_to_be32(MTNIC_OPCODE_SEND) |
+ ((ring->prod & ring->size) ?
+ cpu_to_be32(MTNIC_BIT_DESC_OWN) : 0);
+
+ /* Prepare Data Seg */
+ data = &tx_desc->data;
+ data->addr_l = cpu_to_be32((u32)virt_to_bus(iobuf->data));
+ data->count = cpu_to_be32(iob_len(iobuf));
+ data->mem_type = priv->fw.mem_type_snoop_be;
+
+ /* Attach io_buffer */
+ ring->iobuf[index] = iobuf;
+
+ /* Update producer index */
+ ++ring->prod;
+
+ /* Ring doorbell! */
+ wmb();
+ writel((u32) ring->db_offset, &ring->txcq_db->send_db);
+
+ return 0;
+}
+
+
+static void
+mtnic_close(struct net_device *dev)
+{
+ struct mtnic_priv *priv = netdev_priv(dev);
+ int err = 0;
+ DBG("Close called for port:%d\n", priv->port);
+
+ if (priv->state == CARD_UP) {
+ /* Disable port */
+ err |= mtnic_SET_PORT_STATE(priv, priv->port, 0);
+ /*
+ * Stop HW associated with this port
+ */
+ mdelay(5);
+
+ /* Stop RX */
+ err |= mtnic_RELEASE_RESOURCE(priv, priv->port,
+ MTNIC_IF_RESOURCE_TYPE_RX_RING, 0);
+
+ /* Stop TX */
+ err |= mtnic_RELEASE_RESOURCE(priv, priv->port,
+ MTNIC_IF_RESOURCE_TYPE_TX_RING, 0);
+
+ /* Stop CQs */
+ err |= mtnic_RELEASE_RESOURCE(priv, priv->port,
+ MTNIC_IF_RESOURCE_TYPE_CQ, 0);
+ err |= mtnic_RELEASE_RESOURCE(priv, priv->port,
+ MTNIC_IF_RESOURCE_TYPE_CQ, 1);
+ if (err) {
+ DBG("Close reported error %d", err);
+ }
+
+ /* Free memory */
+ free(priv->tx_ring.buf);
+ iounmap(priv->tx_ring.txcq_db);
+ free(priv->cq[1].buf);
+ free(priv->cq[1].db);
+
+ /* Free RX buffers */
+ mtnic_free_io_buffers(&priv->rx_ring);
+
+ free(priv->rx_ring.buf);
+ free(priv->rx_ring.db);
+ free(priv->cq[0].buf);
+ free(priv->cq[0].db);
+
+ priv->state = CARD_INITIALIZED;
+
+ }
+
+
+}
+
+
+static void
+mtnic_disable(struct pci_device *pci)
+{
+
+ int err;
+ struct net_device *dev = pci_get_drvdata(pci);
+ struct mtnic_priv *priv = netdev_priv(dev);
+
+ /* Should NOT happen! but just in case */
+ if (priv->state == CARD_UP)
+ mtnic_close(dev);
+
+ if (priv->state == CARD_INITIALIZED) {
+ err = mtnic_RELEASE_RESOURCE(priv, 0,
+ MTNIC_IF_RESOURCE_TYPE_EQ, 0);
+ DBG("Calling MTNIC_CLOSE command\n");
+ err |= mtnic_cmd(priv, NULL, NULL, 0,
+ MTNIC_IF_CMD_CLOSE_NIC);
+ if (err) {
+ DBG("Error Releasing resources %d\n", err);
+ }
+
+ free(priv->cmd.buf);
+ iounmap(priv->hcr);
+ ufree((u32)priv->fw.fw_pages.buf);
+ ufree((u32)priv->fw.extra_pages.buf);
+ free(priv->eq.buf);
+ iounmap(priv->eq_db);
+ priv->state = CARD_DOWN;
+ }
+
+ unregister_netdev(dev);
+ netdev_nullify(dev);
+ netdev_put(dev);
+}
+
+
+
+static void
+mtnic_irq(struct net_device *netdev __unused, int enable __unused)
+{
+ /* Not implemented */
+}
+
+
+
+/** mtnic net device operations */
+static struct net_device_operations mtnic_operations = {
+ .open = mtnic_open,
+ .close = mtnic_close,
+ .transmit = mtnic_transmit,
+ .poll = mtnic_poll,
+ .irq = mtnic_irq,
+};
+
+
+
+
+
+
+
+static int
+mtnic_probe(struct pci_device *pci,
+ const struct pci_device_id *id __unused)
+{
+ struct net_device *dev;
+ struct mtnic_priv *priv;
+ int err;
+ u64 mac;
+ u32 result = 0;
+ void *dev_id;
+ int i;
+
+ if (pci->vendor != MELLANOX_VENDOR_ID) {
+ eprintf("");
+ return 0;
+ }
+ printf("\nMellanox Technologies LTD - Boot over MTNIC implementaion\n");
+
+ adjust_pci_device(pci);
+
+ err = mtnic_init_pci(pci);
+ if (err) {
+ eprintf("Error in pci_init\n");
+ return MTNIC_ERROR;
+ }
+
+ mtnic_reset();
+ mdelay(1000);
+
+ err = restore_config();
+ if (err) {
+ eprintf("");
+ return err;
+ }
+
+ /* Checking MTNIC device ID */
+ dev_id = ioremap(mtnic_pci_dev.dev.bar[0] +
+ MTNIC_DEVICE_ID_OFFSET, 4);
+ result = ntohl(readl(dev_id));
+ iounmap(dev_id);
+ if (result != MTNIC_DEVICE_ID) {
+ eprintf("Wrong Devie ID (0x%lx) !!!", result);
+ return MTNIC_ERROR;
+ }
+
+ /* Initializing net device */
+ dev = alloc_etherdev(sizeof(struct mtnic_priv));
+ if (dev == NULL) {
+ eprintf("Net device allocation failed\n");
+ return MTNIC_ERROR;
+ }
+ /*
+ * Initialize driver private data
+ */
+ priv = netdev_priv(dev);
+ memset(priv, 0, sizeof(struct mtnic_priv));
+ priv->dev = dev;
+ priv->pdev = pci;
+ priv->dev->dev = &pci->dev;
+ /* Attach pci device */
+ pci_set_drvdata(pci, priv->dev);
+ netdev_init(dev, &mtnic_operations);
+
+
+ /* Initialize hardware */
+ err = mtnic_init_card(dev);
+ if (err) {
+ eprintf("Error in init_card\n");
+ return MTNIC_ERROR;
+ }
+
+ /* Program the MAC address */
+ mac = priv->fw.mac[priv->port];
+ printf("Port %d Mac address: 0x%12llx\n", MTNIC_PORT_NUM + 1, mac);
+ for (i = 0;i < MAC_ADDRESS_SIZE; ++i) {
+ dev->ll_addr[MAC_ADDRESS_SIZE - i - 1] = mac & 0xFF;
+ mac = mac >> 8;
+ }
+
+ if (register_netdev(dev)) {
+ eprintf("Netdev registration failed\n");
+ return MTNIC_ERROR;
+ }
+
+
+ return 0;
+}
+
+
+
+
+
+
+static struct pci_device_id mtnic_nics[] = {
+ PCI_ROM(0x15b3, 0x6368, "mtnic", "Mellanox MTNIC driver"),
+};
+
+struct pci_driver mtnic_driver __pci_driver = {
+ .ids = mtnic_nics,
+ .id_count = sizeof(mtnic_nics) / sizeof(mtnic_nics[0]),
+ .probe = mtnic_probe,
+ .remove = mtnic_disable,
+};
+
diff --git a/gpxe/src/drivers/net/mtnic.h b/gpxe/src/drivers/net/mtnic.h
new file mode 100755
index 00000000..f1d481ce
--- /dev/null
+++ b/gpxe/src/drivers/net/mtnic.h
@@ -0,0 +1,716 @@
+/*
+ * Copyright (c) 2007 Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+#ifndef H_MTNIC_IF_DEFS_H
+#define H_MTNIC_IF_DEFS_H
+
+
+
+/*
+* Device setup
+*/
+
+/*
+ Note port number can be changed under mtnic.c !
+*/
+#define MTNIC_MAX_PORTS 2
+#define NUM_TX_RINGS 1
+#define NUM_RX_RINGS 1
+#define NUM_CQS (NUM_RX_RINGS + NUM_TX_RINGS)
+#define GO_BIT_TIMEOUT 6000
+#define TBIT_RETRIES 100
+#define UNITS_BUFFER_SIZE 8 /* can be configured to 4/8/16 */
+#define MAX_GAP_PROD_CONS (UNITS_BUFFER_SIZE/4)
+#define DEF_MTU 1600
+#define DEF_IOBUF_SIZE 1600
+#define MAC_ADDRESS_SIZE 6
+#define NUM_EQES 16
+#define ROUND_TO_CHECK 0x400
+
+
+/*
+* Helper macros
+*/
+/* Print in case of an error */
+#define eprintf(fmt, a...) \
+ printf("%s:%d: " fmt "\n", __func__, __LINE__, ##a)
+
+#define XNOR(x,y) (!(x) == !(y))
+#define dma_addr_t unsigned long
+#define PAGE_SIZE 4096
+#define PAGE_MASK (PAGE_SIZE - 1)
+#define MTNIC_MAILBOX_SIZE PAGE_SIZE
+#define MTNIC_ERROR 1
+
+
+
+
+/* BITOPS */
+#define MTNIC_BC_OFF(bc) ((bc) >> 8)
+#define MTNIC_BC_SZ(bc) ((bc) & 0xff)
+#define MTNIC_BC_ONES(size) (~((int)0x80000000 >> (31 - size)))
+#define MTNIC_BC_MASK(bc) \
+ (MTNIC_BC_ONES(MTNIC_BC_SZ(bc)) << MTNIC_BC_OFF(bc))
+#define MTNIC_BC_VAL(val, bc) \
+ (((val) & MTNIC_BC_ONES(MTNIC_BC_SZ(bc))) << MTNIC_BC_OFF(bc))
+/*
+ * Sub word fields - bit code base extraction/setting etc
+ */
+
+/* Encode two values */
+#define MTNIC_BC(off, size) ((off << 8) | (size & 0xff))
+
+/* Get value of field 'bc' from 'x' */
+#define MTNIC_BC_GET(x, bc) \
+ (((x) >> MTNIC_BC_OFF(bc)) & MTNIC_BC_ONES(MTNIC_BC_SZ(bc)))
+
+/* Set value of field 'bc' of 'x' to 'val' */
+#define MTNIC_BC_SET(x, val, bc) \
+ ((x) = ((x) & ~MTNIC_BC_MASK(bc)) | MTNIC_BC_VAL(val, bc))
+
+/* Like MTNIC_BC_SET, except the previous value is assumed to be 0 */
+#define MTNIC_BC_PUT(x, val, bc) ((x) |= MTNIC_BC_VAL(val, bc))
+
+
+
+/*
+ * Device constants
+ */
+typedef enum mtnic_if_cmd {
+ /* NIC commands: */
+ MTNIC_IF_CMD_QUERY_FW = 0x004, /* query FW (size, version, etc) */
+ MTNIC_IF_CMD_MAP_FW = 0xfff, /* map pages for FW image */
+ MTNIC_IF_CMD_RUN_FW = 0xff6, /* run the FW */
+ MTNIC_IF_CMD_QUERY_CAP = 0x001, /* query MTNIC capabilities */
+ MTNIC_IF_CMD_MAP_PAGES = 0x002, /* map physical pages to HW */
+ MTNIC_IF_CMD_OPEN_NIC = 0x003, /* run the firmware */
+ MTNIC_IF_CMD_CONFIG_RX = 0x005, /* general receive configuration */
+ MTNIC_IF_CMD_CONFIG_TX = 0x006, /* general transmit configuration */
+ MTNIC_IF_CMD_CONFIG_INT_FREQ = 0x007, /* interrupt timers freq limits */
+ MTNIC_IF_CMD_HEART_BEAT = 0x008, /* NOP command testing liveliness */
+ MTNIC_IF_CMD_CLOSE_NIC = 0x009, /* release memory and stop the NIC */
+
+ /* Port commands: */
+ MTNIC_IF_CMD_CONFIG_PORT_RSS_STEER = 0x10, /* set RSS mode */
+ MTNIC_IF_CMD_SET_PORT_RSS_INDIRECTION = 0x11, /* set RSS indirection tbl */
+ MTNIC_IF_CMD_CONFIG_PORT_PRIO_STEERING = 0x12, /* set PRIORITY mode */
+ MTNIC_IF_CMD_CONFIG_PORT_ADDR_STEER = 0x13, /* set Address steer mode */
+ MTNIC_IF_CMD_CONFIG_PORT_VLAN_FILTER = 0x14, /* configure VLAN filter */
+ MTNIC_IF_CMD_CONFIG_PORT_MCAST_FILTER = 0x15, /* configure mcast filter */
+ MTNIC_IF_CMD_ENABLE_PORT_MCAST_FILTER = 0x16, /* enable/disable */
+ MTNIC_IF_CMD_SET_PORT_MTU = 0x17, /* set port MTU */
+ MTNIC_IF_CMD_SET_PORT_PROMISCUOUS_MODE = 0x18, /* enable/disable promisc */
+ MTNIC_IF_CMD_SET_PORT_DEFAULT_RING = 0x19, /* set the default ring */
+ MTNIC_IF_CMD_SET_PORT_STATE = 0x1a, /* set link up/down */
+ MTNIC_IF_CMD_DUMP_STAT = 0x1b, /* dump statistics */
+ MTNIC_IF_CMD_ARM_PORT_STATE_EVENT = 0x1c, /* arm the port state event */
+
+ /* Ring / Completion queue commands: */
+ MTNIC_IF_CMD_CONFIG_CQ = 0x20, /* set up completion queue */
+ MTNIC_IF_CMD_CONFIG_RX_RING = 0x21, /* setup Rx ring */
+ MTNIC_IF_CMD_SET_RX_RING_ADDR = 0x22, /* set Rx ring filter by address */
+ MTNIC_IF_CMD_SET_RX_RING_MCAST = 0x23, /* set Rx ring mcast filter */
+ MTNIC_IF_CMD_ARM_RX_RING_WM = 0x24, /* one-time low-watermark INT */
+ MTNIC_IF_CMD_CONFIG_TX_RING = 0x25, /* set up Tx ring */
+ MTNIC_IF_CMD_ENFORCE_TX_RING_ADDR = 0x26, /* setup anti spoofing */
+ MTNIC_IF_CMD_CONFIG_EQ = 0x27, /* config EQ ring */
+ MTNIC_IF_CMD_RELEASE_RESOURCE = 0x28, /* release internal ref to resource */
+}
+mtnic_if_cmd_t;
+
+
+/** selectors for MTNIC_IF_CMD_QUERY_CAP */
+typedef enum mtnic_if_caps {
+ MTNIC_IF_CAP_MAX_TX_RING_PER_PORT = 0x0,
+ MTNIC_IF_CAP_MAX_RX_RING_PER_PORT = 0x1,
+ MTNIC_IF_CAP_MAX_CQ_PER_PORT = 0x2,
+ MTNIC_IF_CAP_NUM_PORTS = 0x3,
+ MTNIC_IF_CAP_MAX_TX_DESC = 0x4,
+ MTNIC_IF_CAP_MAX_RX_DESC = 0x5,
+ MTNIC_IF_CAP_MAX_CQES = 0x6,
+ MTNIC_IF_CAP_MAX_TX_SG_ENTRIES = 0x7,
+ MTNIC_IF_CAP_MAX_RX_SG_ENTRIES = 0x8,
+ MTNIC_IF_CAP_MEM_KEY = 0x9, /* key to mem (after map_pages) */
+ MTNIC_IF_CAP_RSS_HASH_TYPE = 0xa, /* one of mtnic_if_rss_types_t */
+ MTNIC_IF_CAP_MAX_PORT_UCAST_ADDR = 0xc,
+ MTNIC_IF_CAP_MAX_RING_UCAST_ADDR = 0xd, /* only for ADDR steer */
+ MTNIC_IF_CAP_MAX_PORT_MCAST_ADDR = 0xe,
+ MTNIC_IF_CAP_MAX_RING_MCAST_ADDR = 0xf, /* only for ADDR steer */
+ MTNIC_IF_CAP_INTA = 0x10,
+ MTNIC_IF_CAP_BOARD_ID_LOW = 0x11,
+ MTNIC_IF_CAP_BOARD_ID_HIGH = 0x12,
+ MTNIC_IF_CAP_TX_CQ_DB_OFFSET = 0x13, /* offset in bytes for TX, CQ doorbell record */
+ MTNIC_IF_CAP_EQ_DB_OFFSET = 0x14, /* offset in bytes for EQ doorbell record */
+
+ /* These are per port - using port number from cap modifier field */
+ MTNIC_IF_CAP_SPEED = 0x20,
+ MTNIC_IF_CAP_DEFAULT_MAC = 0x21,
+ MTNIC_IF_CAP_EQ_OFFSET = 0x22,
+ MTNIC_IF_CAP_CQ_OFFSET = 0x23,
+ MTNIC_IF_CAP_TX_OFFSET = 0x24,
+ MTNIC_IF_CAP_RX_OFFSET = 0x25,
+
+} mtnic_if_caps_t;
+
+typedef enum mtnic_if_steer_types {
+ MTNIC_IF_STEER_NONE = 0,
+ MTNIC_IF_STEER_PRIORITY = 1,
+ MTNIC_IF_STEER_RSS = 2,
+ MTNIC_IF_STEER_ADDRESS = 3,
+} mtnic_if_steer_types_t;
+
+/** types of memory access modes */
+typedef enum mtnic_if_memory_types {
+ MTNIC_IF_MEM_TYPE_SNOOP = 1,
+ MTNIC_IF_MEM_TYPE_NO_SNOOP = 2
+} mtnic_if_memory_types_t;
+
+
+enum {
+ MTNIC_HCR_BASE = 0x1f000,
+ MTNIC_HCR_SIZE = 0x0001c,
+ MTNIC_CLR_INT_SIZE = 0x00008,
+};
+
+#define MELLANOX_VENDOR_ID 0x15b3
+#define MTNIC_DEVICE_ID 0x00a00190
+#define MTNIC_RESET_OFFSET 0xF0010
+#define MTNIC_DEVICE_ID_OFFSET 0xF0014
+
+
+
+
+
+
+
+/********************************************************************
+* Device private data structures
+*
+* This section contains structures of all device private data:
+* descriptors, rings, CQs, EQ ....
+*
+*
+*********************************************************************/
+/*
+ * Descriptor format
+ */
+struct mtnic_ctrl_seg {
+ u32 op_own;
+#define MTNIC_BIT_DESC_OWN 0x80000000
+#define MTNIC_OPCODE_SEND 0xa
+ u32 size_vlan;
+ u32 flags;
+#define MTNIC_BIT_NO_ICRC 0x2
+#define MTNIC_BIT_TX_COMP 0xc
+ u32 reserved;
+};
+
+struct mtnic_data_seg {
+ u32 count;
+#define MTNIC_INLINE 0x80000000
+ u32 mem_type;
+#define MTNIC_MEMTYPE_PAD 0x100
+ u32 addr_h;
+ u32 addr_l;
+};
+
+struct mtnic_tx_desc {
+ struct mtnic_ctrl_seg ctrl;
+ struct mtnic_data_seg data; /* at least one data segment */
+};
+
+struct mtnic_rx_desc {
+ u16 reserved1;
+ u16 next;
+ u32 reserved2[3];
+ struct mtnic_data_seg data; /* actual number of entries depends on
+ * rx ring stride */
+};
+
+/*
+ * Rings
+ */
+struct mtnic_rx_db_record {
+ u32 count;
+};
+
+struct mtnic_ring {
+ u32 size; /* REMOVE ____cacheline_aligned_in_smp; *//* number of Rx descs or TXBBs */
+ u32 size_mask;
+ u16 stride;
+ u16 cq; /* index of port CQ associated with this ring */
+ u32 prod;
+ u32 cons; /* holds the last consumed index */
+
+ /* Buffers */
+ u32 buf_size; /* ring buffer size in bytes */
+ dma_addr_t dma;
+ void *buf;
+ struct io_buffer *iobuf[UNITS_BUFFER_SIZE];
+
+ /* Tx only */
+ struct mtnic_txcq_db *txcq_db;
+ u32 db_offset;
+
+ /* Rx ring only */
+ dma_addr_t iobuf_dma;
+ struct mtnic_rx_db_record *db;
+ dma_addr_t db_dma;
+};
+
+/*
+ * CQ
+ */
+
+struct mtnic_cqe {
+ u8 vp; /* VLAN present */
+ u8 reserved1[3];
+ u32 rss_hash;
+ u32 reserved2;
+ u16 vlan_prio;
+ u16 reserved3;
+ u8 flags_h;
+ u8 flags_l_rht;
+ u8 ipv6_mask;
+ u8 enc_bf;
+#define MTNIC_BIT_BAD_FCS 0x10
+#define MTNIC_OPCODE_ERROR 0x1e
+ u32 byte_cnt;
+ u16 index;
+ u16 chksum;
+ u8 reserved4[3];
+ u8 op_tr_own;
+#define MTNIC_BIT_CQ_OWN 0x80
+};
+
+
+struct mtnic_cq_db_record {
+ u32 update_ci;
+ u32 cmd_ci;
+};
+
+struct mtnic_cq {
+ int num; /* CQ number (on attached port) */
+ u32 size; /* number of CQEs in CQ */
+ u32 last; /* number of CQEs consumed */
+ struct mtnic_cq_db_record *db;
+ struct net_device *dev;
+
+ dma_addr_t db_dma;
+ u8 is_rx;
+ u16 ring; /* ring associated with this CQ */
+ u32 offset_ind;
+
+ /* CQE ring */
+ u32 buf_size; /* ring size in bytes */
+ struct mtnic_cqe *buf;
+ dma_addr_t dma;
+};
+
+/*
+ * EQ
+ */
+
+struct mtnic_eqe {
+ u8 reserved1;
+ u8 type;
+ u8 reserved2;
+ u8 subtype;
+ u8 reserved3[3];
+ u8 ring_cq;
+ u32 reserved4;
+ u8 port;
+#define MTNIC_MASK_EQE_PORT MTNIC_BC(4,2)
+ u8 reserved5[2];
+ u8 syndrome;
+ u8 reserved6[15];
+ u8 own;
+#define MTNIC_BIT_EQE_OWN 0x80
+};
+
+struct mtnic_eq {
+ u32 size; /* number of EQEs in ring */
+ u32 buf_size; /* EQ size in bytes */
+ void *buf;
+ dma_addr_t dma;
+};
+
+enum mtnic_state {
+ CARD_DOWN,
+ CARD_INITIALIZED,
+ CARD_UP
+};
+
+/* FW */
+struct mtnic_pages {
+ u32 num;
+ u32 *buf;
+};
+struct mtnic_err_buf {
+ u64 offset;
+ u32 size;
+};
+
+
+
+struct mtnic_cmd {
+ void *buf;
+ u32 mapping;
+ u32 tbit;
+};
+
+
+struct mtnic_txcq_db {
+ u32 reserved1[5];
+ u32 send_db;
+ u32 reserved2[2];
+ u32 cq_arm;
+ u32 cq_ci;
+};
+
+
+
+/*
+ * Device private data
+ *
+ */
+struct mtnic_priv {
+ struct net_device *dev;
+ struct pci_device *pdev;
+ u8 port;
+
+ enum mtnic_state state;
+ /* Firmware and board info */
+ u64 fw_ver;
+ struct {
+ struct mtnic_pages fw_pages;
+ struct mtnic_pages extra_pages;
+ struct mtnic_err_buf err_buf;
+ u16 ifc_rev;
+ u8 num_ports;
+ u64 mac[MTNIC_MAX_PORTS];
+ u16 cq_offset;
+ u16 tx_offset[MTNIC_MAX_PORTS];
+ u16 rx_offset[MTNIC_MAX_PORTS];
+ u32 mem_type_snoop_be;
+ u32 txcq_db_offset;
+ u32 eq_db_offset;
+ } fw;
+
+
+ struct mtnic_if_cmd_reg *hcr;
+ struct mtnic_cmd cmd;
+
+ /* TX, RX, CQs, EQ */
+ struct mtnic_ring tx_ring;
+ struct mtnic_ring rx_ring;
+ struct mtnic_cq cq[NUM_CQS];
+ struct mtnic_eq eq;
+ u32 *eq_db;
+ u32 poll_counter;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+/***************************************************************************
+ * NIC COMMANDS
+ *
+ * The section below provides struct definition for commands parameters,
+ * and arguments values enumeration.
+ *
+ * The format used for the struct names is:
+ * mtnic_if_<cmd name>_<in|out>_<imm|mbox>
+ *
+ ***************************************************************************/
+/**
+ * Command Register (Command interface)
+ */
+struct mtnic_if_cmd_reg {
+ unsigned long in_param_h;
+ u32 in_param_l;
+ u32 input_modifier;
+ u32 out_param_h;
+ u32 out_param_l;
+ u32 token;
+#define MTNIC_MASK_CMD_REG_TOKEN MTNIC_BC(16,32)
+ u32 status_go_opcode;
+#define MTNIC_MASK_CMD_REG_OPCODE MTNIC_BC(0,16)
+#define MTNIC_MASK_CMD_REG_T_BIT MTNIC_BC(21,1)
+#define MTNIC_MASK_CMD_REG_GO_BIT MTNIC_BC(23,1)
+#define MTNIC_MASK_CMD_REG_STATUS MTNIC_BC(24,8)
+};
+
+
+
+/* CMD QUERY_FW */
+struct mtnic_if_query_fw_out_mbox {
+ u16 fw_pages; /* Total number of memory pages the device requires */
+ u16 rev_maj;
+ u16 rev_smin;
+ u16 rev_min;
+ u16 reserved1;
+ u16 ifc_rev; /* major revision of the command interface */
+ u8 ft;
+ u8 reserved2[3];
+ u32 reserved3[4];
+ u64 clr_int_base;
+ u32 reserved4[2];
+ u64 err_buf_start;
+ u32 err_buf_size;
+};
+
+/* CMD MTNIC_IF_CMD_QUERY_CAP */
+struct mtnic_if_query_cap_in_imm {
+ u16 reserved1;
+ u8 cap_modifier; /* a modifier for the particular capability */
+ u8 cap_index; /* the index of the capability queried */
+ u32 reserved2;
+};
+
+/* CMD OPEN_NIC */
+struct mtnic_if_open_nic_in_mbox {
+ u16 reserved1;
+ u16 mkey; /* number of mem keys for all chip*/
+ u32 mkey_entry; /* mem key entries for each key*/
+ u8 log_rx_p1; /* log2 rx rings for port1 */
+ u8 log_cq_p1; /* log2 cq for port1 */
+ u8 log_tx_p1; /* log2 tx rings for port1 */
+ u8 steer_p1; /* port 1 steering mode */
+ u16 reserved2;
+ u8 log_vlan_p1; /* log2 vlan per rx port1 */
+ u8 log_mac_p1; /* log2 mac per rx port1 */
+
+ u8 log_rx_p2; /* log2 rx rings for port1 */
+ u8 log_cq_p2; /* log2 cq for port1 */
+ u8 log_tx_p2; /* log2 tx rings for port1 */
+ u8 steer_p2; /* port 1 steering mode */
+ u16 reserved3;
+ u8 log_vlan_p2; /* log2 vlan per rx port1 */
+ u8 log_mac_p2; /* log2 mac per rx port1 */
+};
+
+/* CMD CONFIG_RX */
+struct mtnic_if_config_rx_in_imm {
+ u16 spkt_size; /* size of small packets interrupts enabled on CQ */
+ u16 resp_rcv_pause_frm_mcast_vlan_comp; /* Two flags see MASK below */
+ /* Enable response to receive pause frames */
+ /* Use VLAN in exact-match multicast checks (see SET_RX_RING_MCAST) */
+};
+
+/* CMD CONFIG_TX */
+struct mtnic_if_config_send_in_imm {
+ u32 enph_gpf; /* Enable PseudoHeader and GeneratePauseFrames flags */
+ u32 reserved;
+};
+
+/* CMD HEART_BEAT */
+struct mtnic_if_heart_beat_out_imm {
+ u32 flags; /* several flags */
+#define MTNIC_MASK_HEAR_BEAT_INT_ERROR MTNIC_BC(31,1)
+ u32 reserved;
+};
+
+
+/*
+ * PORT COMMANDS
+ */
+/* CMD CONFIG_PORT_VLAN_FILTER */
+/* in mbox is a 4K bits mask - bit per VLAN */
+struct mtnic_if_config_port_vlan_filter_in_mbox {
+ u64 filter[64]; /* vlans[63:0] sit in filter[0], vlans[127:64] sit in filter[1] .. */
+};
+
+
+/* CMD SET_PORT_MTU */
+struct mtnic_if_set_port_mtu_in_imm {
+ u16 reserved1;
+ u16 mtu; /* The MTU of the port in bytes */
+ u32 reserved2;
+};
+
+/* CMD SET_PORT_DEFAULT_RING */
+struct mtnic_if_set_port_default_ring_in_imm {
+ u8 reserved1[3];
+ u8 ring; /* Index of ring that collects promiscuous traffic */
+ u32 reserved2;
+};
+
+/* CMD SET_PORT_STATE */
+struct mtnic_if_set_port_state_in_imm {
+ u32 state; /* if 1 the port state should be up */
+#define MTNIC_MASK_CONFIG_PORT_STATE MTNIC_BC(0,1)
+ u32 reserved;
+};
+
+/* CMD CONFIG_CQ */
+struct mtnic_if_config_cq_in_mbox {
+ u8 reserved1;
+ u8 cq;
+ u8 size; /* Num CQs is 2^size (size <= 22) */
+ u8 offset; /* start address of CQE in first page (11:6) */
+ u16 tlast; /* interrupt moderation timer from last completion usec */
+ u8 flags; /* flags */
+ u8 int_vector; /* MSI index if MSI is enabled, otherwise reserved */
+ u16 reserved2;
+ u16 max_cnt; /* interrupt moderation counter */
+ u8 page_size; /* each mapped page is 2^(12+page_size) bytes */
+ u8 reserved4[3];
+ u32 db_record_addr_h; /*physical address of CQ doorbell record */
+ u32 db_record_addr_l; /*physical address of CQ doorbell record */
+ u32 page_address[0]; /* 64 bit page addresses of CQ buffer */
+};
+
+/* CMD CONFIG_RX_RING */
+struct mtnic_if_config_rx_ring_in_mbox {
+ u8 reserved1;
+ u8 ring; /* The ring index (with offset) */
+ u8 stride_size; /* stride and size */
+ /* Entry size = 16* (2^stride) bytes */
+#define MTNIC_MASK_CONFIG_RX_RING_STRIDE MTNIC_BC(4,3)
+ /* Rx ring size is 2^size entries */
+#define MTNIC_MASK_CONFIG_RX_RING_SIZE MTNIC_BC(0,4)
+ u8 flags; /* Bit0 - header separation */
+ u8 page_size; /* Each mapped page is 2^(12+page_size) bytes */
+ u8 reserved2[2];
+ u8 cq; /* CQ associated with this ring */
+ u32 db_record_addr_h;
+ u32 db_record_addr_l;
+ u32 page_address[0];/* Array of 2^size 64b page descriptor addresses */
+ /* Must hold all Rx descriptors + doorbell record. */
+};
+
+/* The modifier for SET_RX_RING_ADDR */
+struct mtnic_if_set_rx_ring_modifier {
+ u8 reserved;
+ u8 port_num;
+ u8 index;
+ u8 ring;
+};
+
+/* CMD SET_RX_RING_ADDR */
+struct mtnic_if_set_rx_ring_addr_in_imm {
+ u16 mac_47_32; /* UCAST MAC Address bits 47:32 */
+ u16 flags_vlan_id; /* MAC/VLAN flags and vlan id */
+#define MTNIC_MASK_SET_RX_RING_ADDR_VLAN_ID MTNIC_BC(0,12)
+#define MTNIC_MASK_SET_RX_RING_ADDR_BY_MAC MTNIC_BC(12,1)
+#define MTNIC_MASK_SET_RX_RING_ADDR_BY_VLAN MTNIC_BC(13,1)
+ u32 mac_31_0; /* UCAST MAC Address bits 31:0 */
+};
+
+/* CMD CONFIG_TX_RING */
+struct mtnic_if_config_send_ring_in_mbox {
+ u16 ring; /* The ring index (with offset) */
+#define MTNIC_MASK_CONFIG_TX_RING_INDEX MTNIC_BC(0,8)
+ u8 size; /* Tx ring size is 32*2^size bytes */
+#define MTNIC_MASK_CONFIG_TX_RING_SIZE MTNIC_BC(0,4)
+ u8 reserved;
+ u8 page_size; /* Each mapped page is 2^(12+page_size) bytes */
+ u8 qos_class; /* The COS used for this Tx */
+ u16 cq; /* CQ associated with this ring */
+#define MTNIC_MASK_CONFIG_TX_CQ_INDEX MTNIC_BC(0,8)
+ u32 page_address[0]; /* 64 bit page addresses of descriptor buffer. */
+ /* The buffer must accommodate all Tx descriptors */
+};
+
+/* CMD CONFIG_EQ */
+struct mtnic_if_config_eq_in_mbox {
+ u8 reserved1;
+ u8 int_vector; /* MSI index if MSI enabled; otherwise reserved */
+#define MTNIC_MASK_CONFIG_EQ_INT_VEC MTNIC_BC(0,6)
+ u8 size; /* Num CQs is 2^size entries (size <= 22) */
+#define MTNIC_MASK_CONFIG_EQ_SIZE MTNIC_BC(0,5)
+ u8 offset; /* Start address of CQE in first page (11:6) */
+#define MTNIC_MASK_CONFIG_EQ_OFFSET MTNIC_BC(0,6)
+ u8 page_size; /* Each mapped page is 2^(12+page_size) bytes*/
+ u8 reserved[3];
+ u32 page_address[0]; /* 64 bit page addresses of EQ buffer */
+};
+
+/* CMD RELEASE_RESOURCE */
+enum mtnic_if_resource_types {
+ MTNIC_IF_RESOURCE_TYPE_CQ = 0,
+ MTNIC_IF_RESOURCE_TYPE_RX_RING,
+ MTNIC_IF_RESOURCE_TYPE_TX_RING,
+ MTNIC_IF_RESOURCE_TYPE_EQ
+};
+
+struct mtnic_if_release_resource_in_imm {
+ u8 reserved1;
+ u8 index; /* must be 0 for TYPE_EQ */
+ u8 reserved2;
+ u8 type; /* see enum mtnic_if_resource_types */
+ u32 reserved3;
+};
+
+
+
+
+
+
+
+
+
+/*******************************************************************
+*
+* PCI addon structures
+*
+********************************************************************/
+
+struct pcidev {
+ unsigned long bar[6];
+ u32 dev_config_space[64];
+ struct pci_device *dev;
+ u8 bus;
+ u8 devfn;
+};
+
+struct dev_pci_struct {
+ struct pcidev dev;
+ struct pcidev br;
+};
+
+/* The only global var */
+struct dev_pci_struct mtnic_pci_dev;
+
+
+
+#endif /* H_MTNIC_IF_DEFS_H */
+
diff --git a/gpxe/src/drivers/net/natsemi.c b/gpxe/src/drivers/net/natsemi.c
new file mode 100644
index 00000000..98a5ff84
--- /dev/null
+++ b/gpxe/src/drivers/net/natsemi.c
@@ -0,0 +1,603 @@
+/*
+ natsemi.c - gPXE driver for the NatSemi DP8381x series.
+
+ Based on:
+
+ natsemi.c: An Etherboot driver for the NatSemi DP8381x series.
+
+ Copyright (C) 2001 Entity Cyber, Inc.
+
+ This development of this Etherboot driver was funded by
+
+ Sicom Systems: http://www.sicompos.com/
+
+ Author: Marty Connor <mdc@etherboot.org>
+ Adapted from a Linux driver which was written by Donald Becker
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ Original Copyright Notice:
+
+ Written/copyright 1999-2001 by Donald Becker.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL. License for under other terms may be
+ available. Contact the original author for details.
+
+ The original author may be reached as becker@scyld.com, or at
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Support information and updates available at
+ http://www.scyld.com/network/netsemi.html
+
+ References:
+
+ http://www.scyld.com/expert/100mbps.html
+ http://www.scyld.com/expert/NWay.html
+ Datasheet is available from:
+ http://www.national.com/pf/DP/DP83815.html
+
+*/
+
+/* Revision History */
+
+/*
+ 02 Jul 2007 Udayan Kumar 1.2 ported the driver from etherboot to gPXE API.
+ Fully rewritten,adapting the old driver.
+ Added a circular buffer for transmit and receive.
+ transmit routine will not wait for transmission to finish.
+ poll routine deals with it.
+ 13 Dec 2003 Tim Legge 1.1 Enabled Multicast Support
+ 29 May 2001 Marty Connor 1.0 Initial Release. Tested with Netgear FA311 and FA312 boards
+*/
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <io.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <unistd.h>
+#include <gpxe/pci.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/spi_bit.h>
+#include <gpxe/threewire.h>
+#include <gpxe/nvo.h>
+#include "natsemi.h"
+
+/* Function Prototypes: */
+
+static int natsemi_spi_read_bit ( struct bit_basher *, unsigned int );
+static void natsemi_spi_write_bit ( struct bit_basher *,unsigned int, unsigned long );
+static void natsemi_init_eeprom ( struct natsemi_private * );
+static int natsemi_probe (struct pci_device *pci, const struct pci_device_id *id);
+static void natsemi_reset (struct net_device *netdev);
+static int natsemi_open (struct net_device *netdev);
+static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf);
+static void natsemi_poll (struct net_device *netdev);
+static void natsemi_close (struct net_device *netdev);
+static void natsemi_irq (struct net_device *netdev, int enable);
+static void natsemi_remove (struct pci_device *pci);
+
+/** natsemi net device operations */
+static struct net_device_operations natsemi_operations = {
+ .open = natsemi_open,
+ .close = natsemi_close,
+ .transmit = natsemi_transmit,
+ .poll = natsemi_poll,
+ .irq = natsemi_irq,
+};
+
+static int natsemi_spi_read_bit ( struct bit_basher *basher,
+ unsigned int bit_id ) {
+ struct natsemi_private *np = container_of ( basher, struct natsemi_private,
+ spibit.basher );
+ uint8_t mask = natsemi_ee_bits[bit_id];
+ uint8_t eereg;
+
+ eereg = inb ( np->ioaddr + EE_REG );
+ return ( eereg & mask );
+}
+
+static void natsemi_spi_write_bit ( struct bit_basher *basher,
+ unsigned int bit_id, unsigned long data ) {
+ struct natsemi_private *np = container_of ( basher, struct natsemi_private,
+ spibit.basher );
+ uint8_t mask = natsemi_ee_bits[bit_id];
+ uint8_t eereg;
+
+ eereg = inb ( np->ioaddr + EE_REG );
+ eereg &= ~mask;
+ eereg |= ( data & mask );
+ outb ( eereg, np->ioaddr + EE_REG );
+}
+
+static struct bit_basher_operations natsemi_basher_ops = {
+ .read = natsemi_spi_read_bit,
+ .write = natsemi_spi_write_bit,
+};
+
+/* It looks that this portion of EEPROM can be used for
+ * non-volatile stored options. Data sheet does not talk about this region.
+ * Currently it is not working. But with some efforts it can.
+ */
+static struct nvo_fragment natsemi_nvo_fragments[] = {
+ { 0x0c, 0x68 },
+ { 0, 0 }
+};
+
+/*
+ * Set up for EEPROM access
+ *
+ * @v NAT NATSEMI NIC
+ */
+static void natsemi_init_eeprom ( struct natsemi_private *np ) {
+
+ /* Initialise three-wire bus
+ */
+ np->spibit.basher.op = &natsemi_basher_ops;
+ np->spibit.bus.mode = SPI_MODE_THREEWIRE;
+ np->spibit.endianness = SPI_BIT_LITTLE_ENDIAN;
+ init_spi_bit_basher ( &np->spibit );
+
+ /*natsemi DP 83815 only supports at93c46
+ */
+ init_at93c46 ( &np->eeprom, 16 );
+ np->eeprom.bus = &np->spibit.bus;
+ np->nvo.nvs = &np->eeprom.nvs;
+ np->nvo.fragments = natsemi_nvo_fragments;
+}
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @v id PCI ID
+ * @ret rc Return status code
+ */
+static int natsemi_probe (struct pci_device *pci,
+ const struct pci_device_id *id __unused) {
+ struct net_device *netdev;
+ struct natsemi_private *np = NULL;
+ uint8_t ll_addr_encoded[MAX_LL_ADDR_LEN];
+ uint8_t last=0,last1=0;
+ uint8_t prev_bytes[2];
+ int i;
+ int rc;
+
+ /* Allocate net device
+ */
+ netdev = alloc_etherdev (sizeof (*np));
+ if (! netdev)
+ return -ENOMEM;
+
+ netdev_init (netdev, &natsemi_operations);
+ np = netdev->priv;
+ pci_set_drvdata (pci, netdev);
+ netdev->dev = &pci->dev;
+ memset (np, 0, sizeof (*np));
+ np->ioaddr = pci->ioaddr;
+
+ adjust_pci_device (pci);
+
+ natsemi_reset (netdev);
+ natsemi_init_eeprom ( np );
+ nvs_read ( &np->eeprom.nvs, EE_MAC-1, prev_bytes, 1 );
+ nvs_read ( &np->eeprom.nvs, EE_MAC, ll_addr_encoded, ETH_ALEN );
+
+ /* decoding the MAC address read from NVS
+ * and save it in netdev->ll_addr
+ */
+ last = prev_bytes[1] >> 7;
+ for ( i = 0 ; i < ETH_ALEN ; i++ ) {
+ last1 = ll_addr_encoded[i] >> 7;
+ netdev->ll_addr[i] = ll_addr_encoded[i] << 1 | last;
+ last = last1;
+ }
+
+ if ((rc = register_netdev (netdev)) != 0)
+ goto err_register_netdev;
+
+ return 0;
+
+err_register_netdev:
+
+ natsemi_reset (netdev);
+ netdev_put (netdev);
+ return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void natsemi_remove (struct pci_device *pci) {
+ struct net_device *netdev = pci_get_drvdata (pci);
+
+ unregister_netdev (netdev);
+ natsemi_reset (netdev);
+ netdev_nullify ( netdev );
+ netdev_put (netdev);
+}
+
+/**
+ * Reset NIC
+ *
+ * @v NATSEMI NIC
+ *
+ * Issues a hardware reset and waits for the reset to complete.
+ */
+static void natsemi_reset (struct net_device *netdev)
+{
+ struct natsemi_private *np = netdev->priv;
+ int i;
+ u32 cfg;
+ u32 wcsr;
+ u32 rfcr;
+ u16 pmatch[3];
+ u16 sopass[3];
+
+ natsemi_irq (netdev, 0);
+
+ /*
+ * Resetting the chip causes some registers to be lost.
+ * Natsemi suggests NOT reloading the EEPROM while live, so instead
+ * we save the state that would have been loaded from EEPROM
+ * on a normal power-up (see the spec EEPROM map).
+ */
+
+ /* CFG */
+ cfg = inl (np->ioaddr + ChipConfig) & CFG_RESET_SAVE;
+
+ /* WCSR */
+ wcsr = inl (np->ioaddr + WOLCmd) & WCSR_RESET_SAVE;
+
+ /* RFCR */
+ rfcr = readl (np->ioaddr + RxFilterAddr) & RFCR_RESET_SAVE;
+
+ /* PMATCH */
+ for (i = 0; i < 3; i++) {
+ outl(i*2, np->ioaddr + RxFilterAddr);
+ pmatch[i] = inw(np->ioaddr + RxFilterData);
+ }
+
+ /* SOPAS */
+ for (i = 0; i < 3; i++) {
+ outl(0xa+(i*2), np->ioaddr + RxFilterAddr);
+ sopass[i] = inw(np->ioaddr + RxFilterData);
+ }
+
+ /* now whack the chip */
+ outl(ChipReset, np->ioaddr + ChipCmd);
+ for (i=0; i<NATSEMI_HW_TIMEOUT; i++) {
+ if (! (inl (np->ioaddr + ChipCmd) & ChipReset))
+ break;
+ udelay(5);
+ }
+ if (i == NATSEMI_HW_TIMEOUT) {
+ DBG ("natsemi_reset: reset did not complete in %d usec.\n", i*5);
+ }
+
+ /* restore CFG */
+ cfg |= inl(np->ioaddr + ChipConfig) & ~CFG_RESET_SAVE;
+ cfg &= ~(CfgExtPhy | CfgPhyDis);
+ outl (cfg, np->ioaddr + ChipConfig);
+
+ /* restore WCSR */
+ wcsr |= inl (np->ioaddr + WOLCmd) & ~WCSR_RESET_SAVE;
+ outl (wcsr, np->ioaddr + WOLCmd);
+
+ /* read RFCR */
+ rfcr |= inl (np->ioaddr + RxFilterAddr) & ~RFCR_RESET_SAVE;
+
+ /* restore PMATCH */
+ for (i = 0; i < 3; i++) {
+ outl (i*2, np->ioaddr + RxFilterAddr);
+ outw (pmatch[i], np->ioaddr + RxFilterData);
+ }
+ for (i = 0; i < 3; i++) {
+ outl (0xa+(i*2), np->ioaddr + RxFilterAddr);
+ outw (sopass[i], np->ioaddr + RxFilterData);
+ }
+ /* restore RFCR */
+ outl (rfcr, np->ioaddr + RxFilterAddr);
+}
+
+/**
+ * Open NIC
+ *
+ * @v netdev Net device
+ * @ret rc Return status code
+ */
+static int natsemi_open (struct net_device *netdev)
+{
+ struct natsemi_private *np = netdev->priv;
+ uint32_t tx_config, rx_config;
+ int i;
+
+ /* Disable PME:
+ * The PME bit is initialized from the EEPROM contents.
+ * PCI cards probably have PME disabled, but motherboard
+ * implementations may have PME set to enable WakeOnLan.
+ * With PME set the chip will scan incoming packets but
+ * nothing will be written to memory.
+ */
+ outl (inl (np->ioaddr + ClkRun) & ~0x100, np->ioaddr + ClkRun);
+
+ /* Set MAC address in NIC
+ */
+ for (i = 0 ; i < ETH_ALEN ; i+=2) {
+ outl (i, np->ioaddr + RxFilterAddr);
+ outw (netdev->ll_addr[i] + (netdev->ll_addr[i + 1] << 8),
+ np->ioaddr + RxFilterData);
+ }
+
+ /* Setup Tx Ring
+ */
+ np->tx_cur = 0;
+ np->tx_dirty = 0;
+ for (i = 0 ; i < TX_RING_SIZE ; i++) {
+ np->tx[i].link = virt_to_bus ((i + 1 < TX_RING_SIZE) ? &np->tx[i + 1] : &np->tx[0]);
+ np->tx[i].cmdsts = 0;
+ np->tx[i].bufptr = 0;
+ }
+ outl (virt_to_bus (&np->tx[0]),np->ioaddr + TxRingPtr);
+
+ DBG ("Natsemi Tx descriptor loaded with: %#08lx\n",
+ inl (np->ioaddr + TxRingPtr));
+
+ /* Setup RX ring
+ */
+ np->rx_cur = 0;
+ for (i = 0 ; i < NUM_RX_DESC ; i++) {
+ np->iobuf[i] = alloc_iob (RX_BUF_SIZE);
+ if (! np->iobuf[i])
+ goto memory_alloc_err;
+ np->rx[i].link = virt_to_bus ((i + 1 < NUM_RX_DESC)
+ ? &np->rx[i + 1] : &np->rx[0]);
+ np->rx[i].cmdsts = RX_BUF_SIZE;
+ np->rx[i].bufptr = virt_to_bus (np->iobuf[i]->data);
+ DBG (" Address of iobuf [%d] = %p and iobuf->data = %p \n", i,
+ &np->iobuf[i], &np->iobuf[i]->data);
+ }
+ outl (virt_to_bus (&np->rx[0]), np->ioaddr + RxRingPtr);
+
+ DBG ("Natsemi Rx descriptor loaded with: %#08lx\n",
+ inl (np->ioaddr + RxRingPtr));
+
+ /* Setup RX Filter
+ */
+ outl (RxFilterEnable | AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys,
+ np->ioaddr + RxFilterAddr);
+
+ /* Initialize other registers.
+ * Configure the PCI bus bursts and FIFO thresholds.
+ * Configure for standard, in-spec Ethernet.
+ */
+ if (inl (np->ioaddr + ChipConfig) & 0x20000000) { /* Full duplex */
+ DBG ("Full duplex\n");
+ tx_config = 0xD0801002 | 0xC0000000;
+ rx_config = 0x10000020 | 0x10000000;
+ } else {
+ DBG ("Half duplex\n");
+ tx_config = 0x10801002 & ~0xC0000000;
+ rx_config = 0x00000020 & ~0x10000000;
+ }
+ outl (tx_config, np->ioaddr + TxConfig);
+ outl (rx_config, np->ioaddr + RxConfig);
+
+ DBG ("Tx config register = %#08lx Rx config register = %#08lx\n",
+ inl (np->ioaddr + TxConfig),
+ inl (np->ioaddr + RxConfig));
+
+ /*Set the Interrupt Mask register
+ */
+ outl((RxOk|RxErr|TxOk|TxErr),np->ioaddr + IntrMask);
+ /*start the receiver
+ */
+ outl (RxOn, np->ioaddr + ChipCmd);
+
+ return 0;
+
+memory_alloc_err:
+
+ /* Frees any allocated buffers when memory
+ * for all buffers requested is not available
+ */
+ i = 0;
+ while (np->rx[i].cmdsts == RX_BUF_SIZE) {
+ free_iob (np->iobuf[i]);
+ i++;
+ }
+ return -ENOMEM;
+}
+
+/**
+ * Close NIC
+ *
+ * @v netdev Net device
+ */
+static void natsemi_close (struct net_device *netdev)
+{
+ struct natsemi_private *np = netdev->priv;
+ int i;
+
+ natsemi_reset (netdev);
+
+ for (i = 0; i < NUM_RX_DESC ; i++) {
+ free_iob (np->iobuf[i]);
+ }
+}
+
+/**
+ * Transmit packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf)
+{
+ struct natsemi_private *np = netdev->priv;
+
+ if (np->tx[np->tx_cur].cmdsts != 0) {
+ DBG ("TX overflow\n");
+ return -ENOBUFS;
+ }
+
+ /* Used by netdev_tx_complete ()
+ */
+ np->tx_iobuf[np->tx_cur] = iobuf;
+
+ /* Pad and align packet has not been used because its not required
+ * by the hardware.
+ * iob_pad (iobuf, ETH_ZLEN);
+ * can be used to achieve it, if required
+ */
+
+ /* Add the packet to TX ring
+ */
+ np->tx[np->tx_cur].bufptr = virt_to_bus (iobuf->data);
+ np->tx[np->tx_cur].cmdsts = iob_len (iobuf) | OWN;
+
+ DBG ("TX id %d at %#08lx + %#08zx\n", np->tx_cur,
+ virt_to_bus (&iobuf->data), iob_len (iobuf));
+
+ /* increment the circular buffer pointer to the next buffer location
+ */
+ np->tx_cur = (np->tx_cur + 1) % TX_RING_SIZE;
+
+ /*start the transmitter
+ */
+ outl (TxOn, np->ioaddr + ChipCmd);
+
+ return 0;
+}
+
+/**
+ * Poll for received packets
+ *
+ * @v netdev Network device
+ */
+static void natsemi_poll (struct net_device *netdev)
+{
+ struct natsemi_private *np = netdev->priv;
+ unsigned int tx_status;
+ unsigned int rx_status;
+ unsigned int intr_status;
+ unsigned int rx_len;
+ struct io_buffer *rx_iob;
+ int i;
+
+ /* read the interrupt register
+ */
+ intr_status = inl (np->ioaddr + IntrStatus);
+
+ if (!intr_status)
+ goto end;
+
+ DBG ("natsemi_poll: intr_status = %#08x\n", intr_status);
+
+ /* Check status of transmitted packets
+ */
+ i = np->tx_dirty;
+ while (i != np->tx_cur) {
+ tx_status = np->tx[np->tx_dirty].cmdsts;
+
+ DBG ("tx_dirty = %d tx_cur=%d tx_status=%#08x\n",
+ np->tx_dirty, np->tx_cur, tx_status);
+
+ if (tx_status & OWN)
+ break;
+
+ if (! (tx_status & DescPktOK)) {
+ netdev_tx_complete_err (netdev,np->tx_iobuf[np->tx_dirty],-EINVAL);
+ DBG ("Error transmitting packet, tx_status: %#08x\n",
+ tx_status);
+ } else {
+ netdev_tx_complete (netdev, np->tx_iobuf[np->tx_dirty]);
+ DBG ("Success transmitting packet\n");
+ }
+
+ np->tx[np->tx_dirty].cmdsts = 0;
+ np->tx_dirty = (np->tx_dirty + 1) % TX_RING_SIZE;
+ i = (i + 1) % TX_RING_SIZE;
+ }
+
+ /* Process received packets
+ */
+ rx_status = (unsigned int) np->rx[np->rx_cur].cmdsts;
+ while ((rx_status & OWN)) {
+ rx_len = (rx_status & DSIZE) - CRC_SIZE;
+
+ DBG ("Received packet, rx_curr = %d, rx_status = %#08x, rx_len = %d\n",
+ np->rx_cur, rx_status, rx_len);
+
+ if ((rx_status & (DescMore | DescPktOK | RxTooLong)) != DescPktOK) {
+ netdev_rx_err (netdev, NULL, -EINVAL);
+
+ DBG ("natsemi_poll: Corrupted packet received!"
+ " Status = %#08lx\n",
+ np->rx[np->rx_cur].cmdsts);
+
+ } else {
+
+
+ /* If unable allocate space for this packet,
+ * try again next poll
+ */
+ rx_iob = alloc_iob (rx_len);
+ if (! rx_iob)
+ goto end;
+ memcpy (iob_put (rx_iob, rx_len),
+ np->iobuf[np->rx_cur]->data, rx_len);
+ /* Add this packet to the receive queue.
+ */
+ netdev_rx (netdev, rx_iob);
+ }
+ np->rx[np->rx_cur].cmdsts = RX_BUF_SIZE;
+ np->rx_cur = (np->rx_cur + 1) % NUM_RX_DESC;
+ rx_status = np->rx[np->rx_cur].cmdsts;
+ }
+end:
+ /* re-enable the potentially idle receive state machine
+ */
+ outl (RxOn, np->ioaddr + ChipCmd);
+}
+
+/**
+ * Enable/disable interrupts
+ *
+ * @v netdev Network device
+ * @v enable Non-zero for enable, zero for disable
+ */
+static void natsemi_irq (struct net_device *netdev, int enable)
+{
+ struct natsemi_private *np = netdev->priv;
+
+ outl ((enable ? (RxOk | RxErr | TxOk|TxErr) : 0),
+ np->ioaddr + IntrMask);
+ outl ((enable ? 1 : 0), np->ioaddr + IntrEnable);
+}
+
+static struct pci_device_id natsemi_nics[] = {
+ PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815"),
+};
+
+struct pci_driver natsemi_driver __pci_driver = {
+ .ids = natsemi_nics,
+ .id_count = (sizeof (natsemi_nics) / sizeof (natsemi_nics[0])),
+ .probe = natsemi_probe,
+ .remove = natsemi_remove,
+};
diff --git a/gpxe/src/drivers/net/natsemi.h b/gpxe/src/drivers/net/natsemi.h
new file mode 100644
index 00000000..13b5545a
--- /dev/null
+++ b/gpxe/src/drivers/net/natsemi.h
@@ -0,0 +1,230 @@
+#define NATSEMI_HW_TIMEOUT 400
+
+#define TX_RING_SIZE 4
+#define NUM_RX_DESC 4
+#define RX_BUF_SIZE 1536
+#define OWN 0x80000000
+#define DSIZE 0x00000FFF
+#define CRC_SIZE 4
+
+struct natsemi_tx {
+ uint32_t link;
+ uint32_t cmdsts;
+ uint32_t bufptr;
+};
+
+struct natsemi_rx {
+ uint32_t link;
+ uint32_t cmdsts;
+ uint32_t bufptr;
+};
+
+struct natsemi_private {
+ unsigned short ioaddr;
+ unsigned short tx_cur;
+ unsigned short tx_dirty;
+ unsigned short rx_cur;
+ struct natsemi_tx tx[TX_RING_SIZE];
+ struct natsemi_rx rx[NUM_RX_DESC];
+
+ /* need to add iobuf as we cannot free iobuf->data in close without this
+ * alternatively substracting sizeof(head) and sizeof(list_head) can also
+ * give the same.
+ */
+ struct io_buffer *iobuf[NUM_RX_DESC];
+
+ /* netdev_tx_complete needs pointer to the iobuf of the data so as to free
+ * it from the memory.
+ */
+ struct io_buffer *tx_iobuf[TX_RING_SIZE];
+ struct spi_bit_basher spibit;
+ struct spi_device eeprom;
+ struct nvo_block nvo;
+};
+
+/*
+ * Support for fibre connections on Am79C874:
+ * This phy needs a special setup when connected to a fibre cable.
+ * http://www.amd.com/files/connectivitysolutions/networking/archivednetworking/22235.pdf
+ */
+#define PHYID_AM79C874 0x0022561b
+
+enum {
+ MII_MCTRL = 0x15, /* mode control register */
+ MII_FX_SEL = 0x0001, /* 100BASE-FX (fiber) */
+ MII_EN_SCRM = 0x0004, /* enable scrambler (tp) */
+};
+
+
+
+/* values we might find in the silicon revision register */
+#define SRR_DP83815_C 0x0302
+#define SRR_DP83815_D 0x0403
+#define SRR_DP83816_A4 0x0504
+#define SRR_DP83816_A5 0x0505
+
+/* NATSEMI: Offsets to the device registers.
+ * Unlike software-only systems, device drivers interact with complex hardware.
+ * It's not useful to define symbolic names for every register bit in the
+ * device.
+ */
+enum register_offsets {
+ ChipCmd = 0x00,
+ ChipConfig = 0x04,
+ EECtrl = 0x08,
+ PCIBusCfg = 0x0C,
+ IntrStatus = 0x10,
+ IntrMask = 0x14,
+ IntrEnable = 0x18,
+ TxRingPtr = 0x20,
+ TxConfig = 0x24,
+ RxRingPtr = 0x30,
+ RxConfig = 0x34,
+ ClkRun = 0x3C,
+ WOLCmd = 0x40,
+ PauseCmd = 0x44,
+ RxFilterAddr = 0x48,
+ RxFilterData = 0x4C,
+ BootRomAddr = 0x50,
+ BootRomData = 0x54,
+ SiliconRev = 0x58,
+ StatsCtrl = 0x5C,
+ StatsData = 0x60,
+ RxPktErrs = 0x60,
+ RxMissed = 0x68,
+ RxCRCErrs = 0x64,
+ PCIPM = 0x44,
+ PhyStatus = 0xC0,
+ MIntrCtrl = 0xC4,
+ MIntrStatus = 0xC8,
+
+ /* These are from the spec, around page 78... on a separate table.
+ */
+ PGSEL = 0xCC,
+ PMDCSR = 0xE4,
+ TSTDAT = 0xFC,
+ DSPCFG = 0xF4,
+ SDCFG = 0x8C,
+ BasicControl = 0x80,
+ BasicStatus = 0x84
+
+};
+
+/* the values for the 'magic' registers above (PGSEL=1) */
+#define PMDCSR_VAL 0x189c /* enable preferred adaptation circuitry */
+#define TSTDAT_VAL 0x0
+#define DSPCFG_VAL 0x5040
+#define SDCFG_VAL 0x008c /* set voltage thresholds for Signal Detect */
+#define DSPCFG_LOCK 0x20 /* coefficient lock bit in DSPCFG */
+#define DSPCFG_COEF 0x1000 /* see coefficient (in TSTDAT) bit in DSPCFG */
+#define TSTDAT_FIXED 0xe8 /* magic number for bad coefficients */
+
+/* Bit in ChipCmd.
+ */
+enum ChipCmdBits {
+ ChipReset = 0x100,
+ RxReset = 0x20,
+ TxReset = 0x10,
+ RxOff = 0x08,
+ RxOn = 0x04,
+ TxOff = 0x02,
+ TxOn = 0x01
+};
+
+enum ChipConfig_bits {
+ CfgPhyDis = 0x200,
+ CfgPhyRst = 0x400,
+ CfgExtPhy = 0x1000,
+ CfgAnegEnable = 0x2000,
+ CfgAneg100 = 0x4000,
+ CfgAnegFull = 0x8000,
+ CfgAnegDone = 0x8000000,
+ CfgFullDuplex = 0x20000000,
+ CfgSpeed100 = 0x40000000,
+ CfgLink = 0x80000000,
+};
+
+
+/* Bits in the RxMode register.
+ */
+enum rx_mode_bits {
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+ AcceptBroadcast = 0xC0000000,
+ AcceptMulticast = 0x00200000,
+ AcceptAllMulticast = 0x20000000,
+ AcceptAllPhys = 0x10000000,
+ AcceptMyPhys = 0x08000000,
+ RxFilterEnable = 0x80000000
+};
+
+/* Bits in network_desc.status
+ */
+enum desc_status_bits {
+ DescOwn = 0x80000000,
+ DescMore = 0x40000000,
+ DescIntr = 0x20000000,
+ DescNoCRC = 0x10000000,
+ DescPktOK = 0x08000000,
+ RxTooLong = 0x00400000
+};
+
+/*Bits in Interrupt Mask register
+ */
+enum Intr_mask_register_bits {
+ RxOk = 0x001,
+ RxErr = 0x004,
+ TxOk = 0x040,
+ TxErr = 0x100
+};
+
+enum MIntrCtrl_bits {
+ MICRIntEn = 0x2,
+};
+
+/* CFG bits [13:16] [18:23] */
+#define CFG_RESET_SAVE 0xfde000
+/* WCSR bits [0:4] [9:10] */
+#define WCSR_RESET_SAVE 0x61f
+/* RFCR bits [20] [22] [27:31] */
+#define RFCR_RESET_SAVE 0xf8500000;
+
+/* Delay between EEPROM clock transitions.
+ No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
+ a delay. */
+#define eeprom_delay(ee_addr) inl(ee_addr)
+
+enum EEPROM_Ctrl_Bits {
+ EE_ShiftClk = 0x04,
+ EE_DataIn = 0x01,
+ EE_ChipSelect = 0x08,
+ EE_DataOut = 0x02
+};
+
+#define EE_Write0 (EE_ChipSelect)
+#define EE_Write1 (EE_ChipSelect | EE_DataIn)
+
+/* The EEPROM commands include the alway-set leading bit. */
+enum EEPROM_Cmds {
+ EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
+};
+
+/* EEPROM access , values are devices specific
+ */
+#define EE_CS 0x08 /* EEPROM chip select */
+#define EE_SK 0x04 /* EEPROM shift clock */
+#define EE_DI 0x01 /* Data in */
+#define EE_DO 0x02 /* Data out */
+
+/* Offsets within EEPROM (these are word offsets)
+ */
+#define EE_MAC 7
+#define EE_REG EECtrl
+
+static const uint8_t natsemi_ee_bits[] = {
+ [SPI_BIT_SCLK] = EE_SK,
+ [SPI_BIT_MOSI] = EE_DI,
+ [SPI_BIT_MISO] = EE_DO,
+ [SPI_BIT_SS(0)] = EE_CS,
+};
+
diff --git a/gpxe/src/drivers/net/ns83820.c b/gpxe/src/drivers/net/ns83820.c
new file mode 100755
index 00000000..36091c0a
--- /dev/null
+++ b/gpxe/src/drivers/net/ns83820.c
@@ -0,0 +1,1012 @@
+/**************************************************************************
+* ns83820.c: Etherboot device driver for the National Semiconductor 83820
+* Written 2004 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* ns83820.c by Benjamin LaHaise with contributions
+* for Linux kernel 2.4.x.
+*
+* Linux Driver Version 0.20, 20020610
+*
+* This development of this Etherboot driver was funded by:
+*
+* NXTV: http://www.nxtv.com/
+*
+* REVISION HISTORY:
+* ================
+*
+* v1.0 02-16-2004 timlegge Initial port of Linux driver
+* v1.1 02-19-2004 timlegge More rohbust transmit and poll
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include <gpxe/pci.h>
+
+#if ARCH == ia64 /* Support 64-bit addressing */
+#define USE_64BIT_ADDR
+#endif
+
+//#define DDEBUG
+#ifdef DDEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+#define HZ 100
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+/* NIC specific static variables go here */
+
+/* Global parameters. See MODULE_PARM near the bottom. */
+// static int ihr = 2;
+static int reset_phy = 0;
+static int lnksts = 0; /* CFG_LNKSTS bit polarity */
+
+#if defined(CONFIG_HIGHMEM64G) || defined(__ia64__)
+#define USE_64BIT_ADDR "+"
+#endif
+
+#if defined(USE_64BIT_ADDR)
+#define TRY_DAC 1
+#else
+#define TRY_DAC 0
+#endif
+
+/* tunables */
+#define RX_BUF_SIZE 1500 /* 8192 */
+
+/* Must not exceed ~65000. */
+#define NR_RX_DESC 64
+#define NR_TX_DESC 1
+
+ /* not tunable *//* Extra 6 bytes for 64 bit alignment (divisable by 8) */
+#define REAL_RX_BUF_SIZE (RX_BUF_SIZE + 14 + 6) /* rx/tx mac addr + type */
+
+#define MIN_TX_DESC_FREE 8
+
+/* register defines */
+#define CFGCS 0x04
+
+#define CR_TXE 0x00000001
+#define CR_TXD 0x00000002
+/* Ramit : Here's a tip, don't do a RXD immediately followed by an RXE
+ * The Receive engine skips one descriptor and moves
+ * onto the next one!! */
+#define CR_RXE 0x00000004
+#define CR_RXD 0x00000008
+#define CR_TXR 0x00000010
+#define CR_RXR 0x00000020
+#define CR_SWI 0x00000080
+#define CR_RST 0x00000100
+
+#define PTSCR_EEBIST_FAIL 0x00000001
+#define PTSCR_EEBIST_EN 0x00000002
+#define PTSCR_EELOAD_EN 0x00000004
+#define PTSCR_RBIST_FAIL 0x000001b8
+#define PTSCR_RBIST_DONE 0x00000200
+#define PTSCR_RBIST_EN 0x00000400
+#define PTSCR_RBIST_RST 0x00002000
+
+#define MEAR_EEDI 0x00000001
+#define MEAR_EEDO 0x00000002
+#define MEAR_EECLK 0x00000004
+#define MEAR_EESEL 0x00000008
+#define MEAR_MDIO 0x00000010
+#define MEAR_MDDIR 0x00000020
+#define MEAR_MDC 0x00000040
+
+#define ISR_TXDESC3 0x40000000
+#define ISR_TXDESC2 0x20000000
+#define ISR_TXDESC1 0x10000000
+#define ISR_TXDESC0 0x08000000
+#define ISR_RXDESC3 0x04000000
+#define ISR_RXDESC2 0x02000000
+#define ISR_RXDESC1 0x01000000
+#define ISR_RXDESC0 0x00800000
+#define ISR_TXRCMP 0x00400000
+#define ISR_RXRCMP 0x00200000
+#define ISR_DPERR 0x00100000
+#define ISR_SSERR 0x00080000
+#define ISR_RMABT 0x00040000
+#define ISR_RTABT 0x00020000
+#define ISR_RXSOVR 0x00010000
+#define ISR_HIBINT 0x00008000
+#define ISR_PHY 0x00004000
+#define ISR_PME 0x00002000
+#define ISR_SWI 0x00001000
+#define ISR_MIB 0x00000800
+#define ISR_TXURN 0x00000400
+#define ISR_TXIDLE 0x00000200
+#define ISR_TXERR 0x00000100
+#define ISR_TXDESC 0x00000080
+#define ISR_TXOK 0x00000040
+#define ISR_RXORN 0x00000020
+#define ISR_RXIDLE 0x00000010
+#define ISR_RXEARLY 0x00000008
+#define ISR_RXERR 0x00000004
+#define ISR_RXDESC 0x00000002
+#define ISR_RXOK 0x00000001
+
+#define TXCFG_CSI 0x80000000
+#define TXCFG_HBI 0x40000000
+#define TXCFG_MLB 0x20000000
+#define TXCFG_ATP 0x10000000
+#define TXCFG_ECRETRY 0x00800000
+#define TXCFG_BRST_DIS 0x00080000
+#define TXCFG_MXDMA1024 0x00000000
+#define TXCFG_MXDMA512 0x00700000
+#define TXCFG_MXDMA256 0x00600000
+#define TXCFG_MXDMA128 0x00500000
+#define TXCFG_MXDMA64 0x00400000
+#define TXCFG_MXDMA32 0x00300000
+#define TXCFG_MXDMA16 0x00200000
+#define TXCFG_MXDMA8 0x00100000
+
+#define CFG_LNKSTS 0x80000000
+#define CFG_SPDSTS 0x60000000
+#define CFG_SPDSTS1 0x40000000
+#define CFG_SPDSTS0 0x20000000
+#define CFG_DUPSTS 0x10000000
+#define CFG_TBI_EN 0x01000000
+#define CFG_MODE_1000 0x00400000
+/* Ramit : Dont' ever use AUTO_1000, it never works and is buggy.
+ * Read the Phy response and then configure the MAC accordingly */
+#define CFG_AUTO_1000 0x00200000
+#define CFG_PINT_CTL 0x001c0000
+#define CFG_PINT_DUPSTS 0x00100000
+#define CFG_PINT_LNKSTS 0x00080000
+#define CFG_PINT_SPDSTS 0x00040000
+#define CFG_TMRTEST 0x00020000
+#define CFG_MRM_DIS 0x00010000
+#define CFG_MWI_DIS 0x00008000
+#define CFG_T64ADDR 0x00004000
+#define CFG_PCI64_DET 0x00002000
+#define CFG_DATA64_EN 0x00001000
+#define CFG_M64ADDR 0x00000800
+#define CFG_PHY_RST 0x00000400
+#define CFG_PHY_DIS 0x00000200
+#define CFG_EXTSTS_EN 0x00000100
+#define CFG_REQALG 0x00000080
+#define CFG_SB 0x00000040
+#define CFG_POW 0x00000020
+#define CFG_EXD 0x00000010
+#define CFG_PESEL 0x00000008
+#define CFG_BROM_DIS 0x00000004
+#define CFG_EXT_125 0x00000002
+#define CFG_BEM 0x00000001
+
+#define EXTSTS_UDPPKT 0x00200000
+#define EXTSTS_TCPPKT 0x00080000
+#define EXTSTS_IPPKT 0x00020000
+
+#define SPDSTS_POLARITY (CFG_SPDSTS1 | CFG_SPDSTS0 | CFG_DUPSTS | (lnksts ? CFG_LNKSTS : 0))
+
+#define MIBC_MIBS 0x00000008
+#define MIBC_ACLR 0x00000004
+#define MIBC_FRZ 0x00000002
+#define MIBC_WRN 0x00000001
+
+#define PCR_PSEN (1 << 31)
+#define PCR_PS_MCAST (1 << 30)
+#define PCR_PS_DA (1 << 29)
+#define PCR_STHI_8 (3 << 23)
+#define PCR_STLO_4 (1 << 23)
+#define PCR_FFHI_8K (3 << 21)
+#define PCR_FFLO_4K (1 << 21)
+#define PCR_PAUSE_CNT 0xFFFE
+
+#define RXCFG_AEP 0x80000000
+#define RXCFG_ARP 0x40000000
+#define RXCFG_STRIPCRC 0x20000000
+#define RXCFG_RX_FD 0x10000000
+#define RXCFG_ALP 0x08000000
+#define RXCFG_AIRL 0x04000000
+#define RXCFG_MXDMA512 0x00700000
+#define RXCFG_DRTH 0x0000003e
+#define RXCFG_DRTH0 0x00000002
+
+#define RFCR_RFEN 0x80000000
+#define RFCR_AAB 0x40000000
+#define RFCR_AAM 0x20000000
+#define RFCR_AAU 0x10000000
+#define RFCR_APM 0x08000000
+#define RFCR_APAT 0x07800000
+#define RFCR_APAT3 0x04000000
+#define RFCR_APAT2 0x02000000
+#define RFCR_APAT1 0x01000000
+#define RFCR_APAT0 0x00800000
+#define RFCR_AARP 0x00400000
+#define RFCR_MHEN 0x00200000
+#define RFCR_UHEN 0x00100000
+#define RFCR_ULM 0x00080000
+
+#define VRCR_RUDPE 0x00000080
+#define VRCR_RTCPE 0x00000040
+#define VRCR_RIPE 0x00000020
+#define VRCR_IPEN 0x00000010
+#define VRCR_DUTF 0x00000008
+#define VRCR_DVTF 0x00000004
+#define VRCR_VTREN 0x00000002
+#define VRCR_VTDEN 0x00000001
+
+#define VTCR_PPCHK 0x00000008
+#define VTCR_GCHK 0x00000004
+#define VTCR_VPPTI 0x00000002
+#define VTCR_VGTI 0x00000001
+
+#define CR 0x00
+#define CFG 0x04
+#define MEAR 0x08
+#define PTSCR 0x0c
+#define ISR 0x10
+#define IMR 0x14
+#define IER 0x18
+#define IHR 0x1c
+#define TXDP 0x20
+#define TXDP_HI 0x24
+#define TXCFG 0x28
+#define GPIOR 0x2c
+#define RXDP 0x30
+#define RXDP_HI 0x34
+#define RXCFG 0x38
+#define PQCR 0x3c
+#define WCSR 0x40
+#define PCR 0x44
+#define RFCR 0x48
+#define RFDR 0x4c
+
+#define SRR 0x58
+
+#define VRCR 0xbc
+#define VTCR 0xc0
+#define VDR 0xc4
+#define CCSR 0xcc
+
+#define TBICR 0xe0
+#define TBISR 0xe4
+#define TANAR 0xe8
+#define TANLPAR 0xec
+#define TANER 0xf0
+#define TESR 0xf4
+
+#define TBICR_MR_AN_ENABLE 0x00001000
+#define TBICR_MR_RESTART_AN 0x00000200
+
+#define TBISR_MR_LINK_STATUS 0x00000020
+#define TBISR_MR_AN_COMPLETE 0x00000004
+
+#define TANAR_PS2 0x00000100
+#define TANAR_PS1 0x00000080
+#define TANAR_HALF_DUP 0x00000040
+#define TANAR_FULL_DUP 0x00000020
+
+#define GPIOR_GP5_OE 0x00000200
+#define GPIOR_GP4_OE 0x00000100
+#define GPIOR_GP3_OE 0x00000080
+#define GPIOR_GP2_OE 0x00000040
+#define GPIOR_GP1_OE 0x00000020
+#define GPIOR_GP3_OUT 0x00000004
+#define GPIOR_GP1_OUT 0x00000001
+
+#define LINK_AUTONEGOTIATE 0x01
+#define LINK_DOWN 0x02
+#define LINK_UP 0x04
+
+
+#define __kick_rx() writel(CR_RXE, ns->base + CR)
+
+#define kick_rx() do { \
+ dprintf(("kick_rx: maybe kicking\n")); \
+ writel(virt_to_le32desc(&rx_ring[ns->cur_rx]), ns->base + RXDP); \
+ if (ns->next_rx == ns->next_empty) \
+ printf("uh-oh: next_rx == next_empty???\n"); \
+ __kick_rx(); \
+} while(0)
+
+
+#ifdef USE_64BIT_ADDR
+#define HW_ADDR_LEN 8
+#else
+#define HW_ADDR_LEN 4
+#endif
+
+#define CMDSTS_OWN 0x80000000
+#define CMDSTS_MORE 0x40000000
+#define CMDSTS_INTR 0x20000000
+#define CMDSTS_ERR 0x10000000
+#define CMDSTS_OK 0x08000000
+#define CMDSTS_LEN_MASK 0x0000ffff
+
+#define CMDSTS_DEST_MASK 0x01800000
+#define CMDSTS_DEST_SELF 0x00800000
+#define CMDSTS_DEST_MULTI 0x01000000
+
+#define DESC_SIZE 8 /* Should be cache line sized */
+
+#ifdef USE_64BIT_ADDR
+struct ring_desc {
+ uint64_t link;
+ uint64_t bufptr;
+ u32 cmdsts;
+ u32 extsts; /* Extended status field */
+};
+#else
+struct ring_desc {
+ u32 link;
+ u32 bufptr;
+ u32 cmdsts;
+ u32 extsts; /* Extended status field */
+};
+#endif
+
+/* Private Storage for the NIC */
+static struct ns83820_private {
+ u8 *base;
+ int up;
+ long idle;
+ u32 *next_rx_desc;
+ u16 next_rx, next_empty;
+ u32 cur_rx;
+ u32 *descs;
+ unsigned ihr;
+ u32 CFG_cache;
+ u32 MEAR_cache;
+ u32 IMR_cache;
+ int linkstate;
+ u16 tx_done_idx;
+ u16 tx_idx;
+ u16 tx_intr_idx;
+ u32 phy_descs;
+ u32 *tx_descs;
+
+} nsx;
+static struct ns83820_private *ns;
+
+/* Define the TX and RX Descriptor and Buffers */
+struct {
+ struct ring_desc tx_ring[NR_TX_DESC] __attribute__ ((aligned(8)));
+ unsigned char txb[NR_TX_DESC * REAL_RX_BUF_SIZE];
+ struct ring_desc rx_ring[NR_RX_DESC] __attribute__ ((aligned(8)));
+ unsigned char rxb[NR_RX_DESC * REAL_RX_BUF_SIZE]
+ __attribute__ ((aligned(8)));
+} ns83820_bufs __shared;
+#define tx_ring ns83820_bufs.tx_ring
+#define rx_ring ns83820_bufs.rx_ring
+#define txb ns83820_bufs.txb
+#define rxb ns83820_bufs.rxb
+
+static void phy_intr(struct nic *nic __unused)
+{
+ static char *speeds[] =
+ { "10", "100", "1000", "1000(?)", "1000F" };
+ u32 cfg, new_cfg;
+ u32 tbisr, tanar, tanlpar;
+ int speed, fullduplex, newlinkstate;
+
+ cfg = readl(ns->base + CFG) ^ SPDSTS_POLARITY;
+ if (ns->CFG_cache & CFG_TBI_EN) {
+ /* we have an optical transceiver */
+ tbisr = readl(ns->base + TBISR);
+ tanar = readl(ns->base + TANAR);
+ tanlpar = readl(ns->base + TANLPAR);
+ dprintf(("phy_intr: tbisr=%hX, tanar=%hX, tanlpar=%hX\n",
+ tbisr, tanar, tanlpar));
+
+ if ((fullduplex = (tanlpar & TANAR_FULL_DUP)
+ && (tanar & TANAR_FULL_DUP))) {
+
+ /* both of us are full duplex */
+ writel(readl(ns->base + TXCFG)
+ | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP,
+ ns->base + TXCFG);
+ writel(readl(ns->base + RXCFG) | RXCFG_RX_FD,
+ ns->base + RXCFG);
+ /* Light up full duplex LED */
+ writel(readl(ns->base + GPIOR) | GPIOR_GP1_OUT,
+ ns->base + GPIOR);
+
+ } else if (((tanlpar & TANAR_HALF_DUP)
+ && (tanar & TANAR_HALF_DUP))
+ || ((tanlpar & TANAR_FULL_DUP)
+ && (tanar & TANAR_HALF_DUP))
+ || ((tanlpar & TANAR_HALF_DUP)
+ && (tanar & TANAR_FULL_DUP))) {
+
+ /* one or both of us are half duplex */
+ writel((readl(ns->base + TXCFG)
+ & ~(TXCFG_CSI | TXCFG_HBI)) | TXCFG_ATP,
+ ns->base + TXCFG);
+ writel(readl(ns->base + RXCFG) & ~RXCFG_RX_FD,
+ ns->base + RXCFG);
+ /* Turn off full duplex LED */
+ writel(readl(ns->base + GPIOR) & ~GPIOR_GP1_OUT,
+ ns->base + GPIOR);
+ }
+
+ speed = 4; /* 1000F */
+
+ } else {
+ /* we have a copper transceiver */
+ new_cfg =
+ ns->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS);
+
+ if (cfg & CFG_SPDSTS1)
+ new_cfg |= CFG_MODE_1000;
+ else
+ new_cfg &= ~CFG_MODE_1000;
+
+ speed = ((cfg / CFG_SPDSTS0) & 3);
+ fullduplex = (cfg & CFG_DUPSTS);
+
+ if (fullduplex)
+ new_cfg |= CFG_SB;
+
+ if ((cfg & CFG_LNKSTS) &&
+ ((new_cfg ^ ns->CFG_cache) & CFG_MODE_1000)) {
+ writel(new_cfg, ns->base + CFG);
+ ns->CFG_cache = new_cfg;
+ }
+
+ ns->CFG_cache &= ~CFG_SPDSTS;
+ ns->CFG_cache |= cfg & CFG_SPDSTS;
+ }
+
+ newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN;
+
+ if (newlinkstate & LINK_UP && ns->linkstate != newlinkstate) {
+ printf("link now %s mbps, %s duplex and up.\n",
+ speeds[speed], fullduplex ? "full" : "half");
+ } else if (newlinkstate & LINK_DOWN
+ && ns->linkstate != newlinkstate) {
+ printf("link now down.\n");
+ }
+ ns->linkstate = newlinkstate;
+}
+static void ns83820_set_multicast(struct nic *nic __unused);
+static void ns83820_setup_rx(struct nic *nic)
+{
+ unsigned i;
+ ns->idle = 1;
+ ns->next_rx = 0;
+ ns->next_rx_desc = ns->descs;
+ ns->next_empty = 0;
+ ns->cur_rx = 0;
+
+
+ for (i = 0; i < NR_RX_DESC; i++) {
+ rx_ring[i].link = virt_to_le32desc(&rx_ring[i + 1]);
+ rx_ring[i].bufptr =
+ virt_to_le32desc(&rxb[i * REAL_RX_BUF_SIZE]);
+ rx_ring[i].cmdsts = cpu_to_le32(REAL_RX_BUF_SIZE);
+ rx_ring[i].extsts = cpu_to_le32(0);
+ }
+// No need to wrap the ring
+// rx_ring[i].link = virt_to_le32desc(&rx_ring[0]);
+ writel(0, ns->base + RXDP_HI);
+ writel(virt_to_le32desc(&rx_ring[0]), ns->base + RXDP);
+
+ dprintf(("starting receiver\n"));
+
+ writel(0x0001, ns->base + CCSR);
+ writel(0, ns->base + RFCR);
+ writel(0x7fc00000, ns->base + RFCR);
+ writel(0xffc00000, ns->base + RFCR);
+
+ ns->up = 1;
+
+ phy_intr(nic);
+
+ /* Okay, let it rip */
+ ns->IMR_cache |= ISR_PHY;
+ ns->IMR_cache |= ISR_RXRCMP;
+ //dev->IMR_cache |= ISR_RXERR;
+ //dev->IMR_cache |= ISR_RXOK;
+ ns->IMR_cache |= ISR_RXORN;
+ ns->IMR_cache |= ISR_RXSOVR;
+ ns->IMR_cache |= ISR_RXDESC;
+ ns->IMR_cache |= ISR_RXIDLE;
+ ns->IMR_cache |= ISR_TXDESC;
+ ns->IMR_cache |= ISR_TXIDLE;
+
+ // No reason to enable interupts...
+ // writel(ns->IMR_cache, ns->base + IMR);
+ // writel(1, ns->base + IER);
+ ns83820_set_multicast(nic);
+ kick_rx();
+}
+
+
+static void ns83820_do_reset(struct nic *nic __unused, u32 which)
+{
+ dprintf(("resetting chip...\n"));
+ writel(which, ns->base + CR);
+ do {
+
+ } while (readl(ns->base + CR) & which);
+ dprintf(("okay!\n"));
+}
+
+static void ns83820_reset(struct nic *nic)
+{
+ unsigned i;
+ dprintf(("ns83820_reset\n"));
+
+ writel(0, ns->base + PQCR);
+
+ ns83820_setup_rx(nic);
+
+ for (i = 0; i < NR_TX_DESC; i++) {
+ tx_ring[i].link = 0;
+ tx_ring[i].bufptr = 0;
+ tx_ring[i].cmdsts = cpu_to_le32(0);
+ tx_ring[i].extsts = cpu_to_le32(0);
+ }
+
+ ns->tx_idx = 0;
+ ns->tx_done_idx = 0;
+ writel(0, ns->base + TXDP_HI);
+ return;
+}
+static void ns83820_getmac(struct nic *nic __unused, u8 * mac)
+{
+ unsigned i;
+ for (i = 0; i < 3; i++) {
+ u32 data;
+ /* Read from the perfect match memory: this is loaded by
+ * the chip from the EEPROM via the EELOAD self test.
+ */
+ writel(i * 2, ns->base + RFCR);
+ data = readl(ns->base + RFDR);
+ *mac++ = data;
+ *mac++ = data >> 8;
+ }
+}
+
+static void ns83820_set_multicast(struct nic *nic __unused)
+{
+ u8 *rfcr = ns->base + RFCR;
+ u32 and_mask = 0xffffffff;
+ u32 or_mask = 0;
+ u32 val;
+
+ /* Support Multicast */
+ and_mask &= ~(RFCR_AAU | RFCR_AAM);
+ or_mask |= RFCR_AAM;
+ val = (readl(rfcr) & and_mask) | or_mask;
+ /* Ramit : RFCR Write Fix doc says RFEN must be 0 modify other bits */
+ writel(val & ~RFCR_RFEN, rfcr);
+ writel(val, rfcr);
+
+}
+static void ns83820_run_bist(struct nic *nic __unused, const char *name,
+ u32 enable, u32 done, u32 fail)
+{
+ int timed_out = 0;
+ long start;
+ u32 status;
+ int loops = 0;
+
+ dprintf(("start %s\n", name))
+
+ start = currticks();
+
+ writel(enable, ns->base + PTSCR);
+ for (;;) {
+ loops++;
+ status = readl(ns->base + PTSCR);
+ if (!(status & enable))
+ break;
+ if (status & done)
+ break;
+ if (status & fail)
+ break;
+ if ((currticks() - start) >= HZ) {
+ timed_out = 1;
+ break;
+ }
+ }
+
+ if (status & fail)
+ printf("%s failed! (0x%hX & 0x%hX)\n", name, (unsigned int) status,
+ (unsigned int) fail);
+ else if (timed_out)
+ printf("run_bist %s timed out! (%hX)\n", name, (unsigned int) status);
+ dprintf(("done %s in %d loops\n", name, loops));
+}
+
+/*************************************
+Check Link
+*************************************/
+static void ns83820_check_intr(struct nic *nic) {
+ int i;
+ u32 isr = readl(ns->base + ISR);
+ if(ISR_PHY & isr)
+ phy_intr(nic);
+ if(( ISR_RXIDLE | ISR_RXDESC | ISR_RXERR) & isr)
+ kick_rx();
+ for (i = 0; i < NR_RX_DESC; i++) {
+ if (rx_ring[i].cmdsts == CMDSTS_OWN) {
+// rx_ring[i].link = virt_to_le32desc(&rx_ring[i + 1]);
+ rx_ring[i].cmdsts = cpu_to_le32(REAL_RX_BUF_SIZE);
+ }
+ }
+}
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int ns83820_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+ u32 cmdsts;
+ int entry = ns->cur_rx;
+
+ ns83820_check_intr(nic);
+
+ cmdsts = le32_to_cpu(rx_ring[entry].cmdsts);
+
+ if ( ! ( (CMDSTS_OWN & (cmdsts)) && (cmdsts != (CMDSTS_OWN)) ) )
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ if (! (CMDSTS_OK & cmdsts) )
+ return 0;
+
+ nic->packetlen = cmdsts & 0xffff;
+ memcpy(nic->packet,
+ rxb + (entry * REAL_RX_BUF_SIZE),
+ nic->packetlen);
+ // rx_ring[entry].link = 0;
+ rx_ring[entry].cmdsts = cpu_to_le32(CMDSTS_OWN);
+
+ ns->cur_rx = ++ns->cur_rx % NR_RX_DESC;
+
+ if (ns->cur_rx == 0) /* We have wrapped the ring */
+ kick_rx();
+
+ return 1;
+}
+
+static inline void kick_tx(struct nic *nic __unused)
+{
+ dprintf(("kick_tx\n"));
+ writel(CR_TXE, ns->base + CR);
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void ns83820_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ /* send the packet to destination */
+
+ u16 nstype;
+ u32 cmdsts, extsts;
+ int cur_tx = 0;
+ u32 isr = readl(ns->base + ISR);
+ if (ISR_TXIDLE & isr)
+ kick_tx(nic);
+ /* point to the current txb incase multiple tx_rings are used */
+ memcpy(txb, d, ETH_ALEN);
+ memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) t);
+ memcpy(txb + 2 * ETH_ALEN, (u8 *) & nstype, 2);
+ memcpy(txb + ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ s &= 0x0FFF;
+ while (s < ETH_ZLEN)
+ txb[s++] = '\0';
+
+ /* Setup the transmit descriptor */
+ extsts = 0;
+ extsts |= EXTSTS_UDPPKT;
+
+ tx_ring[cur_tx].bufptr = virt_to_le32desc(&txb);
+ tx_ring[cur_tx].extsts = cpu_to_le32(extsts);
+
+ cmdsts = cpu_to_le32(0);
+ cmdsts |= cpu_to_le32(CMDSTS_OWN | s);
+ tx_ring[cur_tx].cmdsts = cpu_to_le32(cmdsts);
+
+ writel(virt_to_le32desc(&tx_ring[0]), ns->base + TXDP);
+ kick_tx(nic);
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void ns83820_disable ( struct nic *nic ) {
+
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+ /* disable interrupts */
+ writel(0, ns->base + IMR);
+ writel(0, ns->base + IER);
+ readl(ns->base + IER);
+
+ ns->up = 0;
+
+ ns83820_do_reset(nic, CR_RST);
+
+ ns->IMR_cache &=
+ ~(ISR_RXOK | ISR_RXDESC | ISR_RXERR | ISR_RXEARLY |
+ ISR_RXIDLE);
+ writel(ns->IMR_cache, ns->base + IMR);
+
+ /* touch the pci bus... */
+ readl(ns->base + IMR);
+
+ /* assumes the transmitter is already disabled and reset */
+ writel(0, ns->base + RXDP_HI);
+ writel(0, ns->base + RXDP);
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void ns83820_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct nic_operations ns83820_operations = {
+ .connect = dummy_connect,
+ .poll = ns83820_poll,
+ .transmit = ns83820_transmit,
+ .irq = ns83820_irq,
+
+};
+
+static struct pci_device_id ns83820_nics[] = {
+ PCI_ROM(0x100b, 0x0022, "ns83820", "National Semiconductor 83820"),
+};
+
+PCI_DRIVER ( ns83820_driver, ns83820_nics, PCI_NO_CLASS );
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+#define board_found 1
+#define valid_link 0
+static int ns83820_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ long addr;
+ int using_dac = 0;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ printf("ns83820.c: Found %s, vendor=0x%hX, device=0x%hX\n",
+ pci->driver_name, pci->vendor, pci->device);
+
+ /* point to private storage */
+ ns = &nsx;
+
+ adjust_pci_device(pci);
+
+ addr = pci_bar_start(pci, PCI_BASE_ADDRESS_1);
+
+ ns->base = ioremap(addr, (1UL << 12));
+
+ if (!ns->base)
+ return 0;
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ /* disable interrupts */
+ writel(0, ns->base + IMR);
+ writel(0, ns->base + IER);
+ readl(ns->base + IER);
+
+ ns->IMR_cache = 0;
+
+ ns83820_do_reset(nic, CR_RST);
+
+ /* Must reset the ram bist before running it */
+ writel(PTSCR_RBIST_RST, ns->base + PTSCR);
+ ns83820_run_bist(nic, "sram bist", PTSCR_RBIST_EN,
+ PTSCR_RBIST_DONE, PTSCR_RBIST_FAIL);
+ ns83820_run_bist(nic, "eeprom bist", PTSCR_EEBIST_EN, 0,
+ PTSCR_EEBIST_FAIL);
+ ns83820_run_bist(nic, "eeprom load", PTSCR_EELOAD_EN, 0, 0);
+
+ /* I love config registers */
+ ns->CFG_cache = readl(ns->base + CFG);
+
+ if ((ns->CFG_cache & CFG_PCI64_DET)) {
+ printf("%s: detected 64 bit PCI data bus.\n", pci->driver_name);
+ /*dev->CFG_cache |= CFG_DATA64_EN; */
+ if (!(ns->CFG_cache & CFG_DATA64_EN))
+ printf
+ ("%s: EEPROM did not enable 64 bit bus. Disabled.\n",
+ pci->driver_name);
+ } else
+ ns->CFG_cache &= ~(CFG_DATA64_EN);
+
+ ns->CFG_cache &= (CFG_TBI_EN | CFG_MRM_DIS | CFG_MWI_DIS |
+ CFG_T64ADDR | CFG_DATA64_EN | CFG_EXT_125 |
+ CFG_M64ADDR);
+ ns->CFG_cache |=
+ CFG_PINT_DUPSTS | CFG_PINT_LNKSTS | CFG_PINT_SPDSTS |
+ CFG_EXTSTS_EN | CFG_EXD | CFG_PESEL;
+ ns->CFG_cache |= CFG_REQALG;
+ ns->CFG_cache |= CFG_POW;
+ ns->CFG_cache |= CFG_TMRTEST;
+
+ /* When compiled with 64 bit addressing, we must always enable
+ * the 64 bit descriptor format.
+ */
+#ifdef USE_64BIT_ADDR
+ ns->CFG_cache |= CFG_M64ADDR;
+#endif
+
+//FIXME: Enable section on dac or remove this
+ if (using_dac)
+ ns->CFG_cache |= CFG_T64ADDR;
+
+ /* Big endian mode does not seem to do what the docs suggest */
+ ns->CFG_cache &= ~CFG_BEM;
+
+ /* setup optical transceiver if we have one */
+ if (ns->CFG_cache & CFG_TBI_EN) {
+ dprintf(("%s: enabling optical transceiver\n", pci->driver_name));
+ writel(readl(ns->base + GPIOR) | 0x3e8, ns->base + GPIOR);
+
+ /* setup auto negotiation feature advertisement */
+ writel(readl(ns->base + TANAR)
+ | TANAR_HALF_DUP | TANAR_FULL_DUP,
+ ns->base + TANAR);
+
+ /* start auto negotiation */
+ writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN,
+ ns->base + TBICR);
+ writel(TBICR_MR_AN_ENABLE, ns->base + TBICR);
+ ns->linkstate = LINK_AUTONEGOTIATE;
+
+ ns->CFG_cache |= CFG_MODE_1000;
+ }
+ writel(ns->CFG_cache, ns->base + CFG);
+ dprintf(("CFG: %hX\n", ns->CFG_cache));
+
+ /* FIXME: reset_phy is defaulted to 0, should we reset anyway? */
+ if (reset_phy) {
+ dprintf(("%s: resetting phy\n", pci->driver_name));
+ writel(ns->CFG_cache | CFG_PHY_RST, ns->base + CFG);
+ writel(ns->CFG_cache, ns->base + CFG);
+ }
+#if 0 /* Huh? This sets the PCI latency register. Should be done via
+ * the PCI layer. FIXME.
+ */
+ if (readl(dev->base + SRR))
+ writel(readl(dev->base + 0x20c) | 0xfe00,
+ dev->base + 0x20c);
+#endif
+
+ /* Note! The DMA burst size interacts with packet
+ * transmission, such that the largest packet that
+ * can be transmitted is 8192 - FLTH - burst size.
+ * If only the transmit fifo was larger...
+ */
+ /* Ramit : 1024 DMA is not a good idea, it ends up banging
+ * some DELL and COMPAQ SMP systems */
+ writel(TXCFG_CSI | TXCFG_HBI | TXCFG_ATP | TXCFG_MXDMA512
+ | ((1600 / 32) * 0x100), ns->base + TXCFG);
+
+ /* Set Rx to full duplex, don't accept runt, errored, long or length
+ * range errored packets. Use 512 byte DMA.
+ */
+ /* Ramit : 1024 DMA is not a good idea, it ends up banging
+ * some DELL and COMPAQ SMP systems
+ * Turn on ALP, only we are accpeting Jumbo Packets */
+ writel(RXCFG_AEP | RXCFG_ARP | RXCFG_AIRL | RXCFG_RX_FD
+ | RXCFG_STRIPCRC
+ //| RXCFG_ALP
+ | (RXCFG_MXDMA512) | 0, ns->base + RXCFG);
+
+ /* Disable priority queueing */
+ writel(0, ns->base + PQCR);
+
+ /* Enable IP checksum validation and detetion of VLAN headers.
+ * Note: do not set the reject options as at least the 0x102
+ * revision of the chip does not properly accept IP fragments
+ * at least for UDP.
+ */
+ /* Ramit : Be sure to turn on RXCFG_ARP if VLAN's are enabled, since
+ * the MAC it calculates the packetsize AFTER stripping the VLAN
+ * header, and if a VLAN Tagged packet of 64 bytes is received (like
+ * a ping with a VLAN header) then the card, strips the 4 byte VLAN
+ * tag and then checks the packet size, so if RXCFG_ARP is not enabled,
+ * it discrards it!. These guys......
+ */
+ writel(VRCR_IPEN | VRCR_VTDEN, ns->base + VRCR);
+
+ /* Enable per-packet TCP/UDP/IP checksumming */
+ writel(VTCR_PPCHK, ns->base + VTCR);
+
+ /* Ramit : Enable async and sync pause frames */
+// writel(0, ns->base + PCR);
+ writel((PCR_PS_MCAST | PCR_PS_DA | PCR_PSEN | PCR_FFLO_4K |
+ PCR_FFHI_8K | PCR_STLO_4 | PCR_STHI_8 | PCR_PAUSE_CNT),
+ ns->base + PCR);
+
+ /* Disable Wake On Lan */
+ writel(0, ns->base + WCSR);
+
+ ns83820_getmac(nic, nic->node_addr);
+
+ if (using_dac) {
+ dprintf(("%s: using 64 bit addressing.\n", pci->driver_name));
+ }
+
+ dprintf(("%s: DP83820 %d.%d: %! io=0x%hX\n",
+ pci->driver_name,
+ (unsigned) readl(ns->base + SRR) >> 8,
+ (unsigned) readl(ns->base + SRR) & 0xff,
+ nic->node_addr, pci->ioaddr));
+
+#ifdef PHY_CODE_IS_FINISHED
+ ns83820_probe_phy(dev);
+#endif
+
+ ns83820_reset(nic);
+ /* point to NIC specific routines */
+ nic->nic_op = &ns83820_operations;
+ return 1;
+}
+
+DRIVER ( "NS83820/PCI", nic_driver, pci_driver, ns83820_driver,
+ ns83820_probe, ns83820_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/ns8390.c b/gpxe/src/drivers/net/ns8390.c
new file mode 100644
index 00000000..c57ed7a4
--- /dev/null
+++ b/gpxe/src/drivers/net/ns8390.c
@@ -0,0 +1,1029 @@
+/**************************************************************************
+ETHERBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: May/94
+
+ This code is based heavily on David Greenman's if_ed.c driver
+
+ Copyright (C) 1993-1994, David Greenman, Martin Renters.
+ This software may be used, modified, copied, distributed, and sold, in
+ both source and binary form provided that the above copyright and these
+ terms are retained. Under no circumstances are the authors responsible for
+ the proper functioning of this software, nor do the authors assume any
+ responsibility for damages incurred with its use.
+
+Multicast support added by Timothy Legge (timlegge@users.sourceforge.net) 09/28/2003
+Relocation support added by Ken Yap (ken_yap@users.sourceforge.net) 28/12/02
+3c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94
+SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94
+3c503 PIO support added by Jim Hague (jim.hague@acm.org) on 2/17/98
+RX overrun by Klaus Espenlaub (espenlaub@informatik.uni-ulm.de) on 3/10/99
+ parts taken from the Linux 8390 driver (by Donald Becker and Paul Gortmaker)
+SMC8416 PIO support added by Andrew Bettison (andrewb@zip.com.au) on 4/3/02
+ based on the Linux 8390 driver (by Donald Becker and Paul Gortmaker)
+
+**************************************************************************/
+
+/* #warning "ns8390.c: FIXME: split ISA and PCI, clean up" */
+
+#if 1
+
+
+#include "etherboot.h"
+#include "nic.h"
+#include "ns8390.h"
+#include <gpxe/ethernet.h>
+#ifdef INCLUDE_NS8390
+#include <gpxe/pci.h>
+#else
+#include <gpxe/isa.h>
+#endif
+
+static unsigned char eth_vendor, eth_flags;
+#ifdef INCLUDE_WD
+static unsigned char eth_laar;
+#endif
+static unsigned short eth_nic_base, eth_asic_base;
+static unsigned char eth_memsize, eth_rx_start, eth_tx_start;
+static Address eth_bmem, eth_rmem;
+static unsigned char eth_drain_receiver;
+
+#ifdef INCLUDE_WD
+static struct wd_board {
+ const char *name;
+ char id;
+ char flags;
+ char memsize;
+} wd_boards[] = {
+ {"WD8003S", TYPE_WD8003S, 0, MEM_8192},
+ {"WD8003E", TYPE_WD8003E, 0, MEM_8192},
+ {"WD8013EBT", TYPE_WD8013EBT, FLAG_16BIT, MEM_16384},
+ {"WD8003W", TYPE_WD8003W, 0, MEM_8192},
+ {"WD8003EB", TYPE_WD8003EB, 0, MEM_8192},
+ {"WD8013W", TYPE_WD8013W, FLAG_16BIT, MEM_16384},
+ {"WD8003EP/WD8013EP",
+ TYPE_WD8013EP, 0, MEM_8192},
+ {"WD8013WC", TYPE_WD8013WC, FLAG_16BIT, MEM_16384},
+ {"WD8013EPC", TYPE_WD8013EPC, FLAG_16BIT, MEM_16384},
+ {"SMC8216T", TYPE_SMC8216T, FLAG_16BIT | FLAG_790, MEM_16384},
+ {"SMC8216C", TYPE_SMC8216C, FLAG_16BIT | FLAG_790, MEM_16384},
+ {"SMC8416T", TYPE_SMC8416T, FLAG_16BIT | FLAG_790, MEM_8192},
+ {"SMC8416C/BT", TYPE_SMC8416C, FLAG_16BIT | FLAG_790, MEM_8192},
+ {"SMC8013EBP", TYPE_SMC8013EBP,FLAG_16BIT, MEM_16384},
+ {NULL, 0, 0, 0}
+};
+#endif
+
+#ifdef INCLUDE_3C503
+static unsigned char t503_output; /* AUI or internal xcvr (Thinnet) */
+#endif
+
+#if defined(INCLUDE_WD)
+#define ASIC_PIO WD_IAR
+#define eth_probe wd_probe
+#if defined(INCLUDE_3C503) || defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if defined(INCLUDE_3C503)
+#define eth_probe t503_probe
+#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || defined(INCLUDE_WD)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if defined(INCLUDE_NE)
+#define eth_probe ne_probe
+#if defined(INCLUDE_NS8390) || defined(INCLUDE_3C503) || defined(INCLUDE_WD)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if defined(INCLUDE_NS8390)
+#define eth_probe nepci_probe
+#if defined(INCLUDE_NE) || defined(INCLUDE_3C503) || defined(INCLUDE_WD)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if defined(INCLUDE_3C503)
+#define ASIC_PIO _3COM_RFMSB
+#else
+#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
+#define ASIC_PIO NE_DATA
+#endif
+#endif
+
+#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM)) || (defined(INCLUDE_WD) && defined(WD_790_PIO))
+/**************************************************************************
+ETH_PIO_READ - Read a frame via Programmed I/O
+**************************************************************************/
+static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt)
+{
+#ifdef INCLUDE_WD
+ outb(src & 0xff, eth_asic_base + WD_GP2);
+ outb(src >> 8, eth_asic_base + WD_GP2);
+#else
+ outb(D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+ outb(cnt, eth_nic_base + D8390_P0_RBCR0);
+ outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1);
+ outb(src, eth_nic_base + D8390_P0_RSAR0);
+ outb(src>>8, eth_nic_base + D8390_P0_RSAR1);
+ outb(D8390_COMMAND_RD0 |
+ D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+
+#ifdef INCLUDE_3C503
+ outb(src & 0xff, eth_asic_base + _3COM_DALSB);
+ outb(src >> 8, eth_asic_base + _3COM_DAMSB);
+ outb(t503_output | _3COM_CR_START, eth_asic_base + _3COM_CR);
+#endif
+#endif
+
+ if (eth_flags & FLAG_16BIT)
+ cnt = (cnt + 1) >> 1;
+
+ while(cnt--) {
+#ifdef INCLUDE_3C503
+ while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0)
+ ;
+#endif
+
+ if (eth_flags & FLAG_16BIT) {
+ *((unsigned short *)dst) = inw(eth_asic_base + ASIC_PIO);
+ dst += 2;
+ }
+ else
+ *(dst++) = inb(eth_asic_base + ASIC_PIO);
+ }
+
+#ifdef INCLUDE_3C503
+ outb(t503_output, eth_asic_base + _3COM_CR);
+#endif
+}
+
+/**************************************************************************
+ETH_PIO_WRITE - Write a frame via Programmed I/O
+**************************************************************************/
+static void eth_pio_write(const unsigned char *src, unsigned int dst, unsigned int cnt)
+{
+#ifdef COMPEX_RL2000_FIX
+ unsigned int x;
+#endif /* COMPEX_RL2000_FIX */
+#ifdef INCLUDE_WD
+ outb(dst & 0xff, eth_asic_base + WD_GP2);
+ outb(dst >> 8, eth_asic_base + WD_GP2);
+#else
+ outb(D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+ outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
+ outb(cnt, eth_nic_base + D8390_P0_RBCR0);
+ outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1);
+ outb(dst, eth_nic_base + D8390_P0_RSAR0);
+ outb(dst>>8, eth_nic_base + D8390_P0_RSAR1);
+ outb(D8390_COMMAND_RD1 |
+ D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+
+#ifdef INCLUDE_3C503
+ outb(dst & 0xff, eth_asic_base + _3COM_DALSB);
+ outb(dst >> 8, eth_asic_base + _3COM_DAMSB);
+
+ outb(t503_output | _3COM_CR_DDIR | _3COM_CR_START, eth_asic_base + _3COM_CR);
+#endif
+#endif
+
+ if (eth_flags & FLAG_16BIT)
+ cnt = (cnt + 1) >> 1;
+
+ while(cnt--)
+ {
+#ifdef INCLUDE_3C503
+ while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0)
+ ;
+#endif
+
+ if (eth_flags & FLAG_16BIT) {
+ outw(*((unsigned short *)src), eth_asic_base + ASIC_PIO);
+ src += 2;
+ }
+ else
+ outb(*(src++), eth_asic_base + ASIC_PIO);
+ }
+
+#ifdef INCLUDE_3C503
+ outb(t503_output, eth_asic_base + _3COM_CR);
+#else
+#ifdef COMPEX_RL2000_FIX
+ for (x = 0;
+ x < COMPEX_RL2000_TRIES &&
+ (inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
+ != D8390_ISR_RDC;
+ ++x);
+ if (x >= COMPEX_RL2000_TRIES)
+ printf("Warning: Compex RL2000 aborted wait!\n");
+#endif /* COMPEX_RL2000_FIX */
+#ifndef INCLUDE_WD
+ while((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
+ != D8390_ISR_RDC);
+#endif
+#endif
+}
+#else
+/**************************************************************************
+ETH_PIO_READ - Dummy routine when NE2000 not compiled in
+**************************************************************************/
+static void eth_pio_read(unsigned int src __unused, unsigned char *dst __unused, unsigned int cnt __unused) {}
+#endif
+
+
+/**************************************************************************
+enable_multycast - Enable Multicast
+**************************************************************************/
+static void enable_multicast(unsigned short eth_nic_base)
+{
+ unsigned char mcfilter[8];
+ int i;
+ memset(mcfilter, 0xFF, 8);
+ outb(4, eth_nic_base+D8390_P0_RCR);
+ outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1, eth_nic_base + D8390_P0_COMMAND);
+ for(i=0;i<8;i++)
+ {
+ outb(mcfilter[i], eth_nic_base + 8 + i);
+ if(inb(eth_nic_base + 8 + i)!=mcfilter[i])
+ printf("Error SMC 83C690 Multicast filter read/write mishap %d\n",i);
+ }
+ outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0, eth_nic_base + D8390_P0_COMMAND);
+ outb(4 | 0x08, eth_nic_base+D8390_P0_RCR);
+}
+
+/**************************************************************************
+NS8390_RESET - Reset adapter
+**************************************************************************/
+static void ns8390_reset(struct nic *nic)
+{
+ int i;
+
+ eth_drain_receiver = 0;
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+ if (eth_flags & FLAG_16BIT)
+ outb(0x49, eth_nic_base+D8390_P0_DCR);
+ else
+ outb(0x48, eth_nic_base+D8390_P0_DCR);
+ outb(0, eth_nic_base+D8390_P0_RBCR0);
+ outb(0, eth_nic_base+D8390_P0_RBCR1);
+ outb(0x20, eth_nic_base+D8390_P0_RCR); /* monitor mode */
+ outb(2, eth_nic_base+D8390_P0_TCR);
+ outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
+ outb(eth_rx_start, eth_nic_base+D8390_P0_PSTART);
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790) {
+#ifdef WD_790_PIO
+ outb(0x10, eth_asic_base + 0x06); /* disable interrupts, enable PIO */
+ outb(0x01, eth_nic_base + 0x09); /* enable ring read auto-wrap */
+#else
+ outb(0, eth_nic_base + 0x09);
+#endif
+ }
+#endif
+ outb(eth_memsize, eth_nic_base+D8390_P0_PSTOP);
+ outb(eth_memsize - 1, eth_nic_base+D8390_P0_BOUND);
+ outb(0xFF, eth_nic_base+D8390_P0_ISR);
+ outb(0, eth_nic_base+D8390_P0_IMR);
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS1 |
+ D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS1 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+ for (i=0; i<ETH_ALEN; i++)
+ outb(nic->node_addr[i], eth_nic_base+D8390_P1_PAR0+i);
+ for (i=0; i<ETH_ALEN; i++)
+ outb(0xFF, eth_nic_base+D8390_P1_MAR0+i);
+ outb(eth_rx_start, eth_nic_base+D8390_P1_CURR);
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ outb(0xFF, eth_nic_base+D8390_P0_ISR);
+ outb(0, eth_nic_base+D8390_P0_TCR); /* transmitter on */
+ outb(4, eth_nic_base+D8390_P0_RCR); /* allow rx broadcast frames */
+
+ enable_multicast(eth_nic_base);
+
+#ifdef INCLUDE_3C503
+ /*
+ * No way to tell whether or not we're supposed to use
+ * the 3Com's transceiver unless the user tells us.
+ * 'flags' should have some compile time default value
+ * which can be changed from the command menu.
+ */
+ t503_output = (nic->flags) ? 0 : _3COM_CR_XSEL;
+ outb(t503_output, eth_asic_base + _3COM_CR);
+#endif
+}
+
+static int ns8390_poll(struct nic *nic, int retrieve);
+
+#ifndef INCLUDE_3C503
+/**************************************************************************
+ETH_RX_OVERRUN - Bring adapter back to work after an RX overrun
+**************************************************************************/
+static void eth_rx_overrun(struct nic *nic)
+{
+ int start_time;
+
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+
+ /* wait for at least 1.6ms - we wait one timer tick */
+ start_time = currticks();
+ while (currticks() - start_time <= 1)
+ /* Nothing */;
+
+ outb(0, eth_nic_base+D8390_P0_RBCR0); /* reset byte counter */
+ outb(0, eth_nic_base+D8390_P0_RBCR1);
+
+ /*
+ * Linux driver checks for interrupted TX here. This is not necessary,
+ * because the transmit routine waits until the frame is sent.
+ */
+
+ /* enter loopback mode and restart NIC */
+ outb(2, eth_nic_base+D8390_P0_TCR);
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+
+ /* clear the RX ring, acknowledge overrun interrupt */
+ eth_drain_receiver = 1;
+ while (ns8390_poll(nic, 1))
+ /* Nothing */;
+ eth_drain_receiver = 0;
+ outb(D8390_ISR_OVW, eth_nic_base+D8390_P0_ISR);
+
+ /* leave loopback mode - no packets to be resent (see Linux driver) */
+ outb(0, eth_nic_base+D8390_P0_TCR);
+}
+#endif /* INCLUDE_3C503 */
+
+/**************************************************************************
+NS8390_TRANSMIT - Transmit a frame
+**************************************************************************/
+static void ns8390_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+#if defined(INCLUDE_3C503) || (defined(INCLUDE_WD) && ! defined(WD_790_PIO))
+ Address eth_vmem = bus_to_virt(eth_bmem);
+#endif
+#ifdef INCLUDE_3C503
+ if (!(eth_flags & FLAG_PIO)) {
+ memcpy((char *)eth_vmem, d, ETH_ALEN); /* dst */
+ memcpy((char *)eth_vmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+ *((char *)eth_vmem+12) = t>>8; /* type */
+ *((char *)eth_vmem+13) = t;
+ memcpy((char *)eth_vmem+ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ while (s < ETH_ZLEN) *((char *)eth_vmem+(s++)) = 0;
+ }
+#endif
+
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+ inb(0x84);
+ }
+#ifndef WD_790_PIO
+ /* Memory interface */
+ if (eth_flags & FLAG_790) {
+ outb(WD_MSR_MENB, eth_asic_base + WD_MSR);
+ inb(0x84);
+ }
+ inb(0x84);
+ memcpy((char *)eth_vmem, d, ETH_ALEN); /* dst */
+ memcpy((char *)eth_vmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+ *((char *)eth_vmem+12) = t>>8; /* type */
+ *((char *)eth_vmem+13) = t;
+ memcpy((char *)eth_vmem+ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ while (s < ETH_ZLEN) *((char *)eth_vmem+(s++)) = 0;
+ if (eth_flags & FLAG_790) {
+ outb(0, eth_asic_base + WD_MSR);
+ inb(0x84);
+ }
+#else
+ inb(0x84);
+#endif
+#endif
+
+#if defined(INCLUDE_3C503)
+ if (eth_flags & FLAG_PIO)
+#endif
+#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM)) || (defined(INCLUDE_WD) && defined(WD_790_PIO))
+ {
+ /* Programmed I/O */
+ unsigned short type;
+ type = (t >> 8) | (t << 8);
+ eth_pio_write( (unsigned char *) d, eth_tx_start<<8, ETH_ALEN);
+ eth_pio_write(nic->node_addr, (eth_tx_start<<8)+ETH_ALEN, ETH_ALEN);
+ /* bcc generates worse code without (const+const) below */
+ eth_pio_write((unsigned char *)&type, (eth_tx_start<<8)+(ETH_ALEN+ETH_ALEN), 2);
+ eth_pio_write( (unsigned char *) p, (eth_tx_start<<8)+ETH_HLEN, s);
+ s += ETH_HLEN;
+ if (s < ETH_ZLEN) s = ETH_ZLEN;
+ }
+#endif
+#if defined(INCLUDE_3C503)
+#endif
+
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+ inb(0x84);
+ }
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
+ outb(s, eth_nic_base+D8390_P0_TBCR0);
+ outb(s>>8, eth_nic_base+D8390_P0_TBCR1);
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_TXP | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_TXP | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+}
+
+/**************************************************************************
+NS8390_POLL - Wait for a frame
+**************************************************************************/
+static int ns8390_poll(struct nic *nic, int retrieve)
+{
+ int ret = 0;
+ unsigned char rstat, curr, next;
+ unsigned short len, frag;
+ unsigned short pktoff;
+ unsigned char *p;
+ struct ringbuffer pkthdr;
+
+#ifndef INCLUDE_3C503
+ /* avoid infinite recursion: see eth_rx_overrun() */
+ if (!eth_drain_receiver && (inb(eth_nic_base+D8390_P0_ISR) & D8390_ISR_OVW)) {
+ eth_rx_overrun(nic);
+ return(0);
+ }
+#endif /* INCLUDE_3C503 */
+ rstat = inb(eth_nic_base+D8390_P0_RSR);
+ if (!(rstat & D8390_RSTAT_PRX)) return(0);
+ next = inb(eth_nic_base+D8390_P0_BOUND)+1;
+ if (next >= eth_memsize) next = eth_rx_start;
+ outb(D8390_COMMAND_PS1, eth_nic_base+D8390_P0_COMMAND);
+ curr = inb(eth_nic_base+D8390_P1_CURR);
+ outb(D8390_COMMAND_PS0, eth_nic_base+D8390_P0_COMMAND);
+ if (curr >= eth_memsize) curr=eth_rx_start;
+ if (curr == next) return(0);
+
+ if ( ! retrieve ) return 1;
+
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+ inb(0x84);
+ }
+#ifndef WD_790_PIO
+ if (eth_flags & FLAG_790) {
+ outb(WD_MSR_MENB, eth_asic_base + WD_MSR);
+ inb(0x84);
+ }
+#endif
+ inb(0x84);
+#endif
+ pktoff = next << 8;
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, (unsigned char *)&pkthdr, 4);
+ else
+ memcpy(&pkthdr, bus_to_virt(eth_rmem + pktoff), 4);
+ pktoff += sizeof(pkthdr);
+ /* incoming length includes FCS so must sub 4 */
+ len = pkthdr.len - 4;
+ if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN
+ || len > ETH_FRAME_LEN) {
+ printf("Bogus packet, ignoring\n");
+ return (0);
+ }
+ else {
+ p = nic->packet;
+ nic->packetlen = len; /* available to caller */
+ frag = (eth_memsize << 8) - pktoff;
+ if (len > frag) { /* We have a wrap-around */
+ /* read first part */
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, p, frag);
+ else
+ memcpy(p, bus_to_virt(eth_rmem + pktoff), frag);
+ pktoff = eth_rx_start << 8;
+ p += frag;
+ len -= frag;
+ }
+ /* read second part */
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, p, len);
+ else
+ memcpy(p, bus_to_virt(eth_rmem + pktoff), len);
+ ret = 1;
+ }
+#ifdef INCLUDE_WD
+#ifndef WD_790_PIO
+ if (eth_flags & FLAG_790) {
+ outb(0, eth_asic_base + WD_MSR);
+ inb(0x84);
+ }
+#endif
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+ inb(0x84);
+ }
+ inb(0x84);
+#endif
+ next = pkthdr.next; /* frame number of next packet */
+ if (next == eth_rx_start)
+ next = eth_memsize;
+ outb(next-1, eth_nic_base+D8390_P0_BOUND);
+ return(ret);
+}
+
+/**************************************************************************
+NS8390_DISABLE - Turn off adapter
+**************************************************************************/
+static void ns8390_disable ( struct nic *nic ) {
+ ns8390_reset(nic);
+}
+
+/**************************************************************************
+NS8390_IRQ - Enable, Disable, or Force interrupts
+**************************************************************************/
+static void ns8390_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct nic_operations ns8390_operations;
+static struct nic_operations ns8390_operations = {
+ .connect = dummy_connect,
+ .poll = ns8390_poll,
+ .transmit = ns8390_transmit,
+ .irq = ns8390_irq,
+};
+
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+**************************************************************************/
+#ifdef INCLUDE_NS8390
+static int eth_probe (struct nic *nic, struct pci_device *pci)
+#else
+static int eth_probe (struct dev *dev, unsigned short *probe_addrs __unused)
+#endif
+{
+ int i;
+#ifdef INCLUDE_NS8390
+ unsigned short pci_probe_addrs[] = { pci->ioaddr, 0 };
+ unsigned short *probe_addrs = pci_probe_addrs;
+#endif
+ eth_vendor = VENDOR_NONE;
+ eth_drain_receiver = 0;
+
+ nic->irqno = 0;
+
+#ifdef INCLUDE_WD
+{
+ /******************************************************************
+ Search for WD/SMC cards
+ ******************************************************************/
+ struct wd_board *brd;
+ unsigned short chksum;
+ unsigned char c;
+ for (eth_asic_base = WD_LOW_BASE; eth_asic_base <= WD_HIGH_BASE;
+ eth_asic_base += 0x20) {
+ chksum = 0;
+ for (i=8; i<16; i++)
+ chksum += inb(eth_asic_base+i);
+ /* Extra checks to avoid soundcard */
+ if ((chksum & 0xFF) == 0xFF &&
+ inb(eth_asic_base+8) != 0xFF &&
+ inb(eth_asic_base+9) != 0xFF)
+ break;
+ }
+ if (eth_asic_base > WD_HIGH_BASE)
+ return (0);
+ /* We've found a board */
+ eth_vendor = VENDOR_WD;
+ eth_nic_base = eth_asic_base + WD_NIC_ADDR;
+
+ nic->ioaddr = eth_nic_base;
+
+ c = inb(eth_asic_base+WD_BID); /* Get board id */
+ for (brd = wd_boards; brd->name; brd++)
+ if (brd->id == c) break;
+ if (!brd->name) {
+ printf("Unknown WD/SMC NIC type %hhX\n", c);
+ return (0); /* Unknown type */
+ }
+ eth_flags = brd->flags;
+ eth_memsize = brd->memsize;
+ eth_tx_start = 0;
+ eth_rx_start = D8390_TXBUF_SIZE;
+ if ((c == TYPE_WD8013EP) &&
+ (inb(eth_asic_base + WD_ICR) & WD_ICR_16BIT)) {
+ eth_flags = FLAG_16BIT;
+ eth_memsize = MEM_16384;
+ }
+ if ((c & WD_SOFTCONFIG) && (!(eth_flags & FLAG_790))) {
+ eth_bmem = (0x80000 |
+ ((inb(eth_asic_base + WD_MSR) & 0x3F) << 13));
+ } else
+ eth_bmem = WD_DEFAULT_MEM;
+ if (brd->id == TYPE_SMC8216T || brd->id == TYPE_SMC8216C) {
+ /* from Linux driver, 8416BT detects as 8216 sometimes */
+ unsigned int addr = inb(eth_asic_base + 0xb);
+ if (((addr >> 4) & 3) == 0) {
+ brd += 2;
+ eth_memsize = brd->memsize;
+ }
+ }
+ outb(0x80, eth_asic_base + WD_MSR); /* Reset */
+ for (i=0; i<ETH_ALEN; i++) {
+ nic->node_addr[i] = inb(i+eth_asic_base+WD_LAR);
+ }
+ DBG ( "\n%s base %4.4x", brd->name, eth_asic_base );
+ if (eth_flags & FLAG_790) {
+#ifdef WD_790_PIO
+ DBG ( ", PIO mode, addr %s\n", eth_ntoa ( nic->node_addr ) );
+ eth_bmem = 0;
+ eth_flags |= FLAG_PIO; /* force PIO mode */
+ outb(0, eth_asic_base+WD_MSR);
+#else
+ DBG ( ", Memory %x, MAC Addr %s\n", eth_bmem, eth_ntoa ( nic->node_addr) );
+
+ outb(WD_MSR_MENB, eth_asic_base+WD_MSR);
+ outb((inb(eth_asic_base+0x04) |
+ 0x80), eth_asic_base+0x04);
+ outb(((unsigned)(eth_bmem >> 13) & 0x0F) |
+ ((unsigned)(eth_bmem >> 11) & 0x40) |
+ (inb(eth_asic_base+0x0B) & 0xB0), eth_asic_base+0x0B);
+ outb((inb(eth_asic_base+0x04) &
+ ~0x80), eth_asic_base+0x04);
+#endif
+ } else {
+
+ DBG (", Memory %x, MAC Addr %s\n", eth_bmem, eth_ntoa ( nic->node_addr) );
+
+ outb(((unsigned)(eth_bmem >> 13) & 0x3F) | 0x40, eth_asic_base+WD_MSR);
+ }
+ if (eth_flags & FLAG_16BIT) {
+ if (eth_flags & FLAG_790) {
+ eth_laar = inb(eth_asic_base + WD_LAAR);
+ outb(WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+ } else {
+ outb((eth_laar =
+ WD_LAAR_L16EN | 1), eth_asic_base + WD_LAAR);
+/*
+ The previous line used to be
+ WD_LAAR_M16EN | WD_LAAR_L16EN | 1));
+ jluke@deakin.edu.au reported that removing WD_LAAR_M16EN made
+ it work for WD8013s. This seems to work for my 8013 boards. I
+ don't know what is really happening. I wish I had data sheets
+ or more time to decode the Linux driver. - Ken
+*/
+ }
+ inb(0x84);
+ }
+}
+#endif
+#ifdef INCLUDE_3C503
+#ifdef T503_AUI
+ nic->flags = 1; /* aui */
+#else
+ nic->flags = 0; /* no aui */
+#endif
+ /******************************************************************
+ Search for 3Com 3c503 if no WD/SMC cards
+ ******************************************************************/
+ if (eth_vendor == VENDOR_NONE) {
+ int idx;
+ int iobase_reg, membase_reg;
+ static unsigned short base[] = {
+ 0x300, 0x310, 0x330, 0x350,
+ 0x250, 0x280, 0x2A0, 0x2E0, 0 };
+
+ /* Loop through possible addresses checking each one */
+
+ for (idx = 0; (eth_nic_base = base[idx]) != 0; ++idx) {
+
+ eth_asic_base = eth_nic_base + _3COM_ASIC_OFFSET;
+/*
+ * Note that we use the same settings for both 8 and 16 bit cards:
+ * both have an 8K bank of memory at page 1 while only the 16 bit
+ * cards have a bank at page 0.
+ */
+ eth_memsize = MEM_16384;
+ eth_tx_start = 32;
+ eth_rx_start = 32 + D8390_TXBUF_SIZE;
+
+ /* Check our base address. iobase and membase should */
+ /* both have a maximum of 1 bit set or be 0. */
+
+ iobase_reg = inb(eth_asic_base + _3COM_BCFR);
+ membase_reg = inb(eth_asic_base + _3COM_PCFR);
+
+ if ((iobase_reg & (iobase_reg - 1)) ||
+ (membase_reg & (membase_reg - 1)))
+ continue; /* nope */
+
+ /* Now get the shared memory address */
+
+ eth_flags = 0;
+
+ switch (membase_reg) {
+ case _3COM_PCFR_DC000:
+ eth_bmem = 0xdc000;
+ break;
+ case _3COM_PCFR_D8000:
+ eth_bmem = 0xd8000;
+ break;
+ case _3COM_PCFR_CC000:
+ eth_bmem = 0xcc000;
+ break;
+ case _3COM_PCFR_C8000:
+ eth_bmem = 0xc8000;
+ break;
+ case _3COM_PCFR_PIO:
+ eth_flags |= FLAG_PIO;
+ eth_bmem = 0;
+ break;
+ default:
+ continue; /* nope */
+ }
+ break;
+ }
+
+ if (base[idx] == 0) /* not found */
+ return (0);
+#ifndef T503_SHMEM
+ eth_flags |= FLAG_PIO; /* force PIO mode */
+ eth_bmem = 0;
+#endif
+ eth_vendor = VENDOR_3COM;
+
+
+ /* Need this to make ns8390_poll() happy. */
+
+ eth_rmem = eth_bmem - 0x2000;
+
+ /* Reset NIC and ASIC */
+
+ outb(_3COM_CR_RST | _3COM_CR_XSEL, eth_asic_base + _3COM_CR );
+ outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR );
+
+ /* Get our ethernet address */
+
+ outb(_3COM_CR_EALO | _3COM_CR_XSEL, eth_asic_base + _3COM_CR);
+ nic->ioaddr = eth_nic_base;
+ DBG ( "\n3Com 3c503 base %4.4x, ", eth_nic_base );
+ if (eth_flags & FLAG_PIO)
+ DBG ( "PIO mode" );
+ else
+ DBG ( "memory %4.4x", eth_bmem );
+ for (i=0; i<ETH_ALEN; i++) {
+ nic->node_addr[i] = inb(eth_nic_base+i);
+ }
+ DBG ( ", %s, MAC Addr %s\n", nic->flags ? "AUI" : "internal xcvr",
+ eth_ntoa ( nic->node_addr ) );
+
+ outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR);
+ /*
+ * Initialize GA configuration register. Set bank and enable shared
+ * mem. We always use bank 1. Disable interrupts.
+ */
+ outb(_3COM_GACFR_RSEL |
+ _3COM_GACFR_MBS0 | _3COM_GACFR_TCM | _3COM_GACFR_NIM, eth_asic_base + _3COM_GACFR);
+
+ outb(0xff, eth_asic_base + _3COM_VPTR2);
+ outb(0xff, eth_asic_base + _3COM_VPTR1);
+ outb(0x00, eth_asic_base + _3COM_VPTR0);
+ /*
+ * Clear memory and verify that it worked (we use only 8K)
+ */
+
+ if (!(eth_flags & FLAG_PIO)) {
+ memset(bus_to_virt(eth_bmem), 0, 0x2000);
+ for(i = 0; i < 0x2000; ++i)
+ if (*((char *)(bus_to_virt(eth_bmem+i)))) {
+ printf ("Failed to clear 3c503 shared mem.\n");
+ return (0);
+ }
+ }
+ /*
+ * Initialize GA page/start/stop registers.
+ */
+ outb(eth_tx_start, eth_asic_base + _3COM_PSTR);
+ outb(eth_memsize, eth_asic_base + _3COM_PSPR);
+ }
+#endif
+#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
+{
+ /******************************************************************
+ Search for NE1000/2000 if no WD/SMC or 3com cards
+ ******************************************************************/
+ unsigned char c;
+ if (eth_vendor == VENDOR_NONE) {
+ unsigned char romdata[16];
+ unsigned char testbuf[32];
+ int idx;
+ static unsigned char test[] = "NE*000 memory";
+ static unsigned short base[] = {
+#ifdef NE_SCAN
+ NE_SCAN,
+#endif
+ 0 };
+ /* if no addresses supplied, fall back on defaults */
+ if (probe_addrs == 0 || probe_addrs[0] == 0)
+ probe_addrs = base;
+ eth_bmem = 0; /* No shared memory */
+ for (idx = 0; (eth_nic_base = probe_addrs[idx]) != 0; ++idx) {
+ eth_flags = FLAG_PIO;
+ eth_asic_base = eth_nic_base + NE_ASIC_OFFSET;
+ eth_memsize = MEM_16384;
+ eth_tx_start = 32;
+ eth_rx_start = 32 + D8390_TXBUF_SIZE;
+ c = inb(eth_asic_base + NE_RESET);
+ outb(c, eth_asic_base + NE_RESET);
+ (void) inb(0x84);
+ outb(D8390_COMMAND_STP |
+ D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND);
+ outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
+ outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
+ outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
+ outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
+#ifdef NS8390_FORCE_16BIT
+ eth_flags |= FLAG_16BIT; /* force 16-bit mode */
+#endif
+
+ eth_pio_write( (unsigned char *) test, 8192, sizeof(test));
+ eth_pio_read(8192, testbuf, sizeof(test));
+ if (!memcmp(test, testbuf, sizeof(test)))
+ break;
+ eth_flags |= FLAG_16BIT;
+ eth_memsize = MEM_32768;
+ eth_tx_start = 64;
+ eth_rx_start = 64 + D8390_TXBUF_SIZE;
+ outb(D8390_DCR_WTS |
+ D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
+ outb(MEM_16384, eth_nic_base + D8390_P0_PSTART);
+ outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP);
+ eth_pio_write( (unsigned char *) test, 16384, sizeof(test));
+ eth_pio_read(16384, testbuf, sizeof(test));
+ if (!memcmp(testbuf, test, sizeof(test)))
+ break;
+ }
+ if (eth_nic_base == 0)
+ return (0);
+ if (eth_nic_base > ISA_MAX_ADDR) /* PCI probably */
+ eth_flags |= FLAG_16BIT;
+ eth_vendor = VENDOR_NOVELL;
+ eth_pio_read(0, romdata, sizeof(romdata));
+ for (i=0; i<ETH_ALEN; i++) {
+ nic->node_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)];
+ }
+ nic->ioaddr = eth_nic_base;
+ DBG ( "\nNE%c000 base %4.4x, MAC Addr %s\n",
+ (eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base,
+ eth_ntoa ( nic->node_addr ) );
+ }
+}
+#endif
+ if (eth_vendor == VENDOR_NONE)
+ return(0);
+ if (eth_vendor != VENDOR_3COM)
+ eth_rmem = eth_bmem;
+ ns8390_reset(nic);
+ nic->nic_op = &ns8390_operations;
+
+ /* Based on PnP ISA map */
+#ifdef INCLUDE_WD
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.device_id = htons(0x812a);
+#endif
+#ifdef INCLUDE_3C503
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.device_id = htons(0x80f3);
+#endif
+#ifdef INCLUDE_NE
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.device_id = htons(0x80d6);
+#endif
+ return 1;
+}
+
+#ifdef INCLUDE_WD
+struct isa_driver wd_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "WD",
+ .probe = wd_probe,
+ .ioaddrs = 0,
+};
+ISA_ROM("wd","WD8003/8013, SMC8216/8416, SMC 83c790 (EtherEZ)");
+#endif
+
+#ifdef INCLUDE_3C503
+struct isa_driver t503_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "3C503",
+ .probe = t503_probe,
+ .ioaddrs = 0,
+};
+ISA_ROM("3c503","3Com503, Etherlink II[/16]");
+#endif
+
+#ifdef INCLUDE_NE
+struct isa_driver ne_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "NE*000",
+ .probe = ne_probe,
+ .ioaddrs = 0,
+};
+ISA_ROM("ne","NE1000/2000 and clones");
+#endif
+
+#ifdef INCLUDE_NS8390
+static struct pci_device_id nepci_nics[] = {
+/* A few NE2000 PCI clones, list not exhaustive */
+PCI_ROM(0x10ec, 0x8029, "rtl8029", "Realtek 8029"),
+PCI_ROM(0x1186, 0x0300, "dlink-528", "D-Link DE-528"),
+PCI_ROM(0x1050, 0x0940, "winbond940", "Winbond NE2000-PCI"), /* Winbond 86C940 / 89C940 */
+PCI_ROM(0x1050, 0x5a5a, "winbond940f", "Winbond W89c940F"), /* Winbond 89C940F */
+PCI_ROM(0x11f6, 0x1401, "compexrl2000", "Compex ReadyLink 2000"),
+PCI_ROM(0x8e2e, 0x3000, "ktiet32p2", "KTI ET32P2"),
+PCI_ROM(0x4a14, 0x5000, "nv5000sc", "NetVin NV5000SC"),
+PCI_ROM(0x12c3, 0x0058, "holtek80232", "Holtek HT80232"),
+PCI_ROM(0x12c3, 0x5598, "holtek80229", "Holtek HT80229"),
+PCI_ROM(0x10bd, 0x0e34, "surecom-ne34", "Surecom NE34"),
+PCI_ROM(0x1106, 0x0926, "via86c926", "Via 86c926"),
+};
+
+PCI_DRIVER ( nepci_driver, nepci_nics, PCI_NO_CLASS );
+
+DRIVER ( "NE2000/PCI", nic_driver, pci_driver, nepci_driver,
+ nepci_probe, ns8390_disable );
+
+#endif /* INCLUDE_NS8390 */
+
+#endif
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/ns8390.h b/gpxe/src/drivers/net/ns8390.h
new file mode 100644
index 00000000..2c4e972d
--- /dev/null
+++ b/gpxe/src/drivers/net/ns8390.h
@@ -0,0 +1,238 @@
+/**************************************************************************
+ETHERBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: Jun/94
+
+**************************************************************************/
+
+#define VENDOR_NONE 0
+#define VENDOR_WD 1
+#define VENDOR_NOVELL 2
+#define VENDOR_3COM 3
+
+#define FLAG_PIO 0x01
+#define FLAG_16BIT 0x02
+#define FLAG_790 0x04
+
+#define MEM_8192 32
+#define MEM_16384 64
+#define MEM_32768 128
+
+#define ISA_MAX_ADDR 0x400
+
+/**************************************************************************
+Western Digital/SMC Board Definitions
+**************************************************************************/
+#define WD_LOW_BASE 0x200
+#define WD_HIGH_BASE 0x3e0
+#ifndef WD_DEFAULT_MEM
+#define WD_DEFAULT_MEM 0xD0000
+#endif
+#define WD_NIC_ADDR 0x10
+
+/**************************************************************************
+Western Digital/SMC ASIC Addresses
+**************************************************************************/
+#define WD_MSR 0x00
+#define WD_ICR 0x01
+#define WD_IAR 0x02
+#define WD_BIO 0x03
+#define WD_IRR 0x04
+#define WD_LAAR 0x05
+#define WD_IJR 0x06
+#define WD_GP2 0x07
+#define WD_LAR 0x08
+#define WD_BID 0x0E
+
+#define WD_ICR_16BIT 0x01
+
+#define WD_MSR_MENB 0x40
+
+#define WD_LAAR_L16EN 0x40
+#define WD_LAAR_M16EN 0x80
+
+#define WD_SOFTCONFIG 0x20
+
+/**************************************************************************
+Western Digital/SMC Board Types
+**************************************************************************/
+#define TYPE_WD8003S 0x02
+#define TYPE_WD8003E 0x03
+#define TYPE_WD8013EBT 0x05
+#define TYPE_WD8003W 0x24
+#define TYPE_WD8003EB 0x25
+#define TYPE_WD8013W 0x26
+#define TYPE_WD8013EP 0x27
+#define TYPE_WD8013WC 0x28
+#define TYPE_WD8013EPC 0x29
+#define TYPE_SMC8216T 0x2a
+#define TYPE_SMC8216C 0x2b
+#define TYPE_SMC8416T 0x00 /* Bogus entries: the 8416 generates the */
+#define TYPE_SMC8416C 0x00 /* the same codes as the 8216. */
+#define TYPE_SMC8013EBP 0x2c
+
+/**************************************************************************
+3com 3c503 definitions
+**************************************************************************/
+
+#ifndef _3COM_BASE
+#define _3COM_BASE 0x300
+#endif
+
+#define _3COM_TX_PAGE_OFFSET_8BIT 0x20
+#define _3COM_TX_PAGE_OFFSET_16BIT 0x0
+#define _3COM_RX_PAGE_OFFSET_16BIT 0x20
+
+#define _3COM_ASIC_OFFSET 0x400
+#define _3COM_NIC_OFFSET 0x0
+
+#define _3COM_PSTR 0
+#define _3COM_PSPR 1
+
+#define _3COM_BCFR 3
+#define _3COM_BCFR_2E0 0x01
+#define _3COM_BCFR_2A0 0x02
+#define _3COM_BCFR_280 0x04
+#define _3COM_BCFR_250 0x08
+#define _3COM_BCFR_350 0x10
+#define _3COM_BCFR_330 0x20
+#define _3COM_BCFR_310 0x40
+#define _3COM_BCFR_300 0x80
+#define _3COM_PCFR 4
+#define _3COM_PCFR_PIO 0
+#define _3COM_PCFR_C8000 0x10
+#define _3COM_PCFR_CC000 0x20
+#define _3COM_PCFR_D8000 0x40
+#define _3COM_PCFR_DC000 0x80
+#define _3COM_CR 6
+#define _3COM_CR_RST 0x01 /* Reset GA and NIC */
+#define _3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
+#define _3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
+#define _3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
+#define _3COM_CR_SHARE 0x10 /* select interrupt sharing option */
+#define _3COM_CR_DBSEL 0x20 /* Double buffer select */
+#define _3COM_CR_DDIR 0x40 /* DMA direction select */
+#define _3COM_CR_START 0x80 /* Start DMA controller */
+#define _3COM_GACFR 5
+#define _3COM_GACFR_MBS0 0x01
+#define _3COM_GACFR_MBS1 0x02
+#define _3COM_GACFR_MBS2 0x04
+#define _3COM_GACFR_RSEL 0x08 /* enable shared memory */
+#define _3COM_GACFR_TEST 0x10 /* for GA testing */
+#define _3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
+#define _3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
+#define _3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
+#define _3COM_STREG 7
+#define _3COM_STREG_REV 0x07 /* GA revision */
+#define _3COM_STREG_DIP 0x08 /* DMA in progress */
+#define _3COM_STREG_DTC 0x10 /* DMA terminal count */
+#define _3COM_STREG_OFLW 0x20 /* Overflow */
+#define _3COM_STREG_UFLW 0x40 /* Underflow */
+#define _3COM_STREG_DPRDY 0x80 /* Data port ready */
+#define _3COM_IDCFR 8
+#define _3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
+#define _3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
+#define _3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
+#define _3COM_IDCFR_UNUSED 0x08 /* not used */
+#define _3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
+#define _3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
+#define _3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
+#define _3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
+#define _3COM_IRQ2 2
+#define _3COM_IRQ3 3
+#define _3COM_IRQ4 4
+#define _3COM_IRQ5 5
+#define _3COM_DAMSB 9
+#define _3COM_DALSB 0x0a
+#define _3COM_VPTR2 0x0b
+#define _3COM_VPTR1 0x0c
+#define _3COM_VPTR0 0x0d
+#define _3COM_RFMSB 0x0e
+#define _3COM_RFLSB 0x0f
+
+/**************************************************************************
+NE1000/2000 definitions
+**************************************************************************/
+#define NE_ASIC_OFFSET 0x10
+#define NE_RESET 0x0F /* Used to reset card */
+#define NE_DATA 0x00 /* Used to read/write NIC mem */
+
+#define COMPEX_RL2000_TRIES 200
+
+/**************************************************************************
+8390 Register Definitions
+**************************************************************************/
+#define D8390_P0_COMMAND 0x00
+#define D8390_P0_PSTART 0x01
+#define D8390_P0_PSTOP 0x02
+#define D8390_P0_BOUND 0x03
+#define D8390_P0_TSR 0x04
+#define D8390_P0_TPSR 0x04
+#define D8390_P0_TBCR0 0x05
+#define D8390_P0_TBCR1 0x06
+#define D8390_P0_ISR 0x07
+#define D8390_P0_RSAR0 0x08
+#define D8390_P0_RSAR1 0x09
+#define D8390_P0_RBCR0 0x0A
+#define D8390_P0_RBCR1 0x0B
+#define D8390_P0_RSR 0x0C
+#define D8390_P0_RCR 0x0C
+#define D8390_P0_TCR 0x0D
+#define D8390_P0_DCR 0x0E
+#define D8390_P0_IMR 0x0F
+#define D8390_P1_COMMAND 0x00
+#define D8390_P1_PAR0 0x01
+#define D8390_P1_PAR1 0x02
+#define D8390_P1_PAR2 0x03
+#define D8390_P1_PAR3 0x04
+#define D8390_P1_PAR4 0x05
+#define D8390_P1_PAR5 0x06
+#define D8390_P1_CURR 0x07
+#define D8390_P1_MAR0 0x08
+
+#define D8390_COMMAND_PS0 0x0 /* Page 0 select */
+#define D8390_COMMAND_PS1 0x40 /* Page 1 select */
+#define D8390_COMMAND_PS2 0x80 /* Page 2 select */
+#define D8390_COMMAND_RD2 0x20 /* Remote DMA control */
+#define D8390_COMMAND_RD1 0x10
+#define D8390_COMMAND_RD0 0x08
+#define D8390_COMMAND_TXP 0x04 /* transmit packet */
+#define D8390_COMMAND_STA 0x02 /* start */
+#define D8390_COMMAND_STP 0x01 /* stop */
+
+#define D8390_RCR_MON 0x20 /* monitor mode */
+
+#define D8390_DCR_FT1 0x40
+#define D8390_DCR_LS 0x08 /* Loopback select */
+#define D8390_DCR_WTS 0x01 /* Word transfer select */
+
+#define D8390_ISR_PRX 0x01 /* successful recv */
+#define D8390_ISR_PTX 0x02 /* successful xmit */
+#define D8390_ISR_RXE 0x04 /* receive error */
+#define D8390_ISR_TXE 0x08 /* transmit error */
+#define D8390_ISR_OVW 0x10 /* Overflow */
+#define D8390_ISR_CNT 0x20 /* Counter overflow */
+#define D8390_ISR_RDC 0x40 /* Remote DMA complete */
+#define D8390_ISR_RST 0x80 /* reset */
+
+#define D8390_RSTAT_PRX 0x01 /* successful recv */
+#define D8390_RSTAT_CRC 0x02 /* CRC error */
+#define D8390_RSTAT_FAE 0x04 /* Frame alignment error */
+#define D8390_RSTAT_OVER 0x08 /* FIFO overrun */
+
+#define D8390_TXBUF_SIZE 6
+#define D8390_RXBUF_END 32
+#define D8390_PAGE_SIZE 256
+
+struct ringbuffer {
+ unsigned char status;
+ unsigned char next;
+ unsigned short len;
+};
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/gpxe/src/drivers/net/p80211hdr.h b/gpxe/src/drivers/net/p80211hdr.h
new file mode 100644
index 00000000..d9a8bbbe
--- /dev/null
+++ b/gpxe/src/drivers/net/p80211hdr.h
@@ -0,0 +1,299 @@
+/* src/include/wlan/p80211hdr.h
+*
+* Macros, types, and functions for handling 802.11 MAC headers
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file declares the constants and types used in the interface
+* between a wlan driver and the user mode utilities.
+*
+* Note:
+* - Constant values are always in HOST byte order. To assign
+* values to multi-byte fields they _must_ be converted to
+* ieee byte order. To retrieve multi-byte values from incoming
+* frames, they must be converted to host order.
+*
+* All functions declared here are implemented in p80211.c
+* --------------------------------------------------------------------
+*/
+
+#ifndef _P80211HDR_H
+#define _P80211HDR_H
+
+/*================================================================*/
+/* System Includes */
+
+/*================================================================*/
+/* Project Includes */
+
+#ifndef _WLAN_COMPAT_H
+#include <wlan/wlan_compat.h>
+#endif
+
+
+/*================================================================*/
+/* Constants */
+
+/*--- Sizes -----------------------------------------------*/
+#define WLAN_ADDR_LEN 6
+#define WLAN_CRC_LEN 4
+#define WLAN_BSSID_LEN 6
+#define WLAN_BSS_TS_LEN 8
+#define WLAN_HDR_A3_LEN 24
+#define WLAN_HDR_A4_LEN 30
+#define WLAN_SSID_MAXLEN 32
+#define WLAN_DATA_MAXLEN 2312
+#define WLAN_A3FR_MAXLEN (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN)
+#define WLAN_A4FR_MAXLEN (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN)
+#define WLAN_BEACON_FR_MAXLEN (WLAN_HDR_A3_LEN + 334)
+#define WLAN_ATIM_FR_MAXLEN (WLAN_HDR_A3_LEN + 0)
+#define WLAN_DISASSOC_FR_MAXLEN (WLAN_HDR_A3_LEN + 2)
+#define WLAN_ASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 48)
+#define WLAN_ASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16)
+#define WLAN_REASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 54)
+#define WLAN_REASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16)
+#define WLAN_PROBEREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 44)
+#define WLAN_PROBERESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 78)
+#define WLAN_AUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 261)
+#define WLAN_DEAUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 2)
+#define WLAN_WEP_NKEYS 4
+#define WLAN_WEP_MAXKEYLEN 13
+#define WLAN_CHALLENGE_IE_LEN 130
+#define WLAN_CHALLENGE_LEN 128
+#define WLAN_WEP_IV_LEN 4
+#define WLAN_WEP_ICV_LEN 4
+
+/*--- Frame Control Field -------------------------------------*/
+/* Frame Types */
+#define WLAN_FTYPE_MGMT 0x00
+#define WLAN_FTYPE_CTL 0x01
+#define WLAN_FTYPE_DATA 0x02
+
+/* Frame subtypes */
+/* Management */
+#define WLAN_FSTYPE_ASSOCREQ 0x00
+#define WLAN_FSTYPE_ASSOCRESP 0x01
+#define WLAN_FSTYPE_REASSOCREQ 0x02
+#define WLAN_FSTYPE_REASSOCRESP 0x03
+#define WLAN_FSTYPE_PROBEREQ 0x04
+#define WLAN_FSTYPE_PROBERESP 0x05
+#define WLAN_FSTYPE_BEACON 0x08
+#define WLAN_FSTYPE_ATIM 0x09
+#define WLAN_FSTYPE_DISASSOC 0x0a
+#define WLAN_FSTYPE_AUTHEN 0x0b
+#define WLAN_FSTYPE_DEAUTHEN 0x0c
+
+/* Control */
+#define WLAN_FSTYPE_BLOCKACKREQ 0x8
+#define WLAN_FSTYPE_BLOCKACK 0x9
+#define WLAN_FSTYPE_PSPOLL 0x0a
+#define WLAN_FSTYPE_RTS 0x0b
+#define WLAN_FSTYPE_CTS 0x0c
+#define WLAN_FSTYPE_ACK 0x0d
+#define WLAN_FSTYPE_CFEND 0x0e
+#define WLAN_FSTYPE_CFENDCFACK 0x0f
+
+/* Data */
+#define WLAN_FSTYPE_DATAONLY 0x00
+#define WLAN_FSTYPE_DATA_CFACK 0x01
+#define WLAN_FSTYPE_DATA_CFPOLL 0x02
+#define WLAN_FSTYPE_DATA_CFACK_CFPOLL 0x03
+#define WLAN_FSTYPE_NULL 0x04
+#define WLAN_FSTYPE_CFACK 0x05
+#define WLAN_FSTYPE_CFPOLL 0x06
+#define WLAN_FSTYPE_CFACK_CFPOLL 0x07
+
+
+/*================================================================*/
+/* Macros */
+
+/*--- FC Macros ----------------------------------------------*/
+/* Macros to get/set the bitfields of the Frame Control Field */
+/* GET_FC_??? - takes the host byte-order value of an FC */
+/* and retrieves the value of one of the */
+/* bitfields and moves that value so its lsb is */
+/* in bit 0. */
+/* SET_FC_??? - takes a host order value for one of the FC */
+/* bitfields and moves it to the proper bit */
+/* location for ORing into a host order FC. */
+/* To send the FC produced from SET_FC_???, */
+/* one must put the bytes in IEEE order. */
+/* e.g. */
+/* printf("the frame subtype is %x", */
+/* GET_FC_FTYPE( ieee2host( rx.fc ))) */
+/* */
+/* tx.fc = host2ieee( SET_FC_FTYPE(WLAN_FTYP_CTL) | */
+/* SET_FC_FSTYPE(WLAN_FSTYPE_RTS) ); */
+/*------------------------------------------------------------*/
+
+#define WLAN_GET_FC_PVER(n) (((UINT16)(n)) & (BIT0 | BIT1))
+#define WLAN_GET_FC_FTYPE(n) ((((UINT16)(n)) & (BIT2 | BIT3)) >> 2)
+#define WLAN_GET_FC_FSTYPE(n) ((((UINT16)(n)) & (BIT4|BIT5|BIT6|BIT7)) >> 4)
+#define WLAN_GET_FC_TODS(n) ((((UINT16)(n)) & (BIT8)) >> 8)
+#define WLAN_GET_FC_FROMDS(n) ((((UINT16)(n)) & (BIT9)) >> 9)
+#define WLAN_GET_FC_MOREFRAG(n) ((((UINT16)(n)) & (BIT10)) >> 10)
+#define WLAN_GET_FC_RETRY(n) ((((UINT16)(n)) & (BIT11)) >> 11)
+#define WLAN_GET_FC_PWRMGT(n) ((((UINT16)(n)) & (BIT12)) >> 12)
+#define WLAN_GET_FC_MOREDATA(n) ((((UINT16)(n)) & (BIT13)) >> 13)
+#define WLAN_GET_FC_ISWEP(n) ((((UINT16)(n)) & (BIT14)) >> 14)
+#define WLAN_GET_FC_ORDER(n) ((((UINT16)(n)) & (BIT15)) >> 15)
+
+#define WLAN_SET_FC_PVER(n) ((UINT16)(n))
+#define WLAN_SET_FC_FTYPE(n) (((UINT16)(n)) << 2)
+#define WLAN_SET_FC_FSTYPE(n) (((UINT16)(n)) << 4)
+#define WLAN_SET_FC_TODS(n) (((UINT16)(n)) << 8)
+#define WLAN_SET_FC_FROMDS(n) (((UINT16)(n)) << 9)
+#define WLAN_SET_FC_MOREFRAG(n) (((UINT16)(n)) << 10)
+#define WLAN_SET_FC_RETRY(n) (((UINT16)(n)) << 11)
+#define WLAN_SET_FC_PWRMGT(n) (((UINT16)(n)) << 12)
+#define WLAN_SET_FC_MOREDATA(n) (((UINT16)(n)) << 13)
+#define WLAN_SET_FC_ISWEP(n) (((UINT16)(n)) << 14)
+#define WLAN_SET_FC_ORDER(n) (((UINT16)(n)) << 15)
+
+/*--- Duration Macros ----------------------------------------*/
+/* Macros to get/set the bitfields of the Duration Field */
+/* - the duration value is only valid when bit15 is zero */
+/* - the firmware handles these values, so I'm not going */
+/* these macros right now. */
+/*------------------------------------------------------------*/
+
+/*--- Sequence Control Macros -------------------------------*/
+/* Macros to get/set the bitfields of the Sequence Control */
+/* Field. */
+/*------------------------------------------------------------*/
+#define WLAN_GET_SEQ_FRGNUM(n) (((UINT16)(n)) & (BIT0|BIT1|BIT2|BIT3))
+#define WLAN_GET_SEQ_SEQNUM(n) ((((UINT16)(n)) & (~(BIT0|BIT1|BIT2|BIT3))) >> 4)
+
+/*--- Data ptr macro -----------------------------------------*/
+/* Creates a UINT8* to the data portion of a frame */
+/* Assumes you're passing in a ptr to the beginning of the hdr*/
+/*------------------------------------------------------------*/
+#define WLAN_HDR_A3_DATAP(p) (((UINT8*)(p)) + WLAN_HDR_A3_LEN)
+#define WLAN_HDR_A4_DATAP(p) (((UINT8*)(p)) + WLAN_HDR_A4_LEN)
+
+#define DOT11_RATE5_ISBASIC_GET(r) (((UINT8)(r)) & BIT7)
+
+/*================================================================*/
+/* Types */
+
+/* BSS Timestamp */
+typedef UINT8 wlan_bss_ts_t[WLAN_BSS_TS_LEN];
+
+/* Generic 802.11 Header types */
+
+typedef struct p80211_hdr_a3
+{
+ UINT16 fc;
+ UINT16 dur;
+ UINT8 a1[WLAN_ADDR_LEN];
+ UINT8 a2[WLAN_ADDR_LEN];
+ UINT8 a3[WLAN_ADDR_LEN];
+ UINT16 seq;
+} __WLAN_ATTRIB_PACK__ p80211_hdr_a3_t;
+
+typedef struct p80211_hdr_a4
+{
+ UINT16 fc;
+ UINT16 dur;
+ UINT8 a1[WLAN_ADDR_LEN];
+ UINT8 a2[WLAN_ADDR_LEN];
+ UINT8 a3[WLAN_ADDR_LEN];
+ UINT16 seq;
+ UINT8 a4[WLAN_ADDR_LEN];
+} __WLAN_ATTRIB_PACK__ p80211_hdr_a4_t;
+
+typedef union p80211_hdr
+{
+ p80211_hdr_a3_t a3;
+ p80211_hdr_a4_t a4;
+} __WLAN_ATTRIB_PACK__ p80211_hdr_t;
+
+
+/*================================================================*/
+/* Extern Declarations */
+
+
+/*================================================================*/
+/* Function Declarations */
+
+/* Frame and header lenght macros */
+
+#define WLAN_CTL_FRAMELEN(fstype) (\
+ (fstype) == WLAN_FSTYPE_BLOCKACKREQ ? 24 : \
+ (fstype) == WLAN_FSTYPE_BLOCKACK ? 152 : \
+ (fstype) == WLAN_FSTYPE_PSPOLL ? 20 : \
+ (fstype) == WLAN_FSTYPE_RTS ? 20 : \
+ (fstype) == WLAN_FSTYPE_CTS ? 14 : \
+ (fstype) == WLAN_FSTYPE_ACK ? 14 : \
+ (fstype) == WLAN_FSTYPE_CFEND ? 20 : \
+ (fstype) == WLAN_FSTYPE_CFENDCFACK ? 20 : 4)
+
+#define WLAN_FCS_LEN 4
+
+/* ftcl in HOST order */
+inline static UINT16 p80211_headerlen(UINT16 fctl)
+{
+ UINT16 hdrlen = 0;
+
+ switch ( WLAN_GET_FC_FTYPE(fctl) ) {
+ case WLAN_FTYPE_MGMT:
+ hdrlen = WLAN_HDR_A3_LEN;
+ break;
+ case WLAN_FTYPE_DATA:
+ hdrlen = WLAN_HDR_A3_LEN;
+ if ( WLAN_GET_FC_TODS(fctl) && WLAN_GET_FC_FROMDS(fctl) ) {
+ hdrlen += WLAN_ADDR_LEN;
+ }
+ break;
+ case WLAN_FTYPE_CTL:
+ hdrlen = WLAN_CTL_FRAMELEN(WLAN_GET_FC_FSTYPE(fctl)) -
+ WLAN_FCS_LEN;
+ break;
+ default:
+ hdrlen = WLAN_HDR_A3_LEN;
+ }
+
+ return hdrlen;
+}
+
+#endif /* _P80211HDR_H */
diff --git a/gpxe/src/drivers/net/pcnet32.c b/gpxe/src/drivers/net/pcnet32.c
new file mode 100644
index 00000000..d8529da1
--- /dev/null
+++ b/gpxe/src/drivers/net/pcnet32.c
@@ -0,0 +1,1025 @@
+/**************************************************************************
+*
+* pcnet32.c -- Etherboot device driver for the AMD PCnet32
+* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* pcnet32.c: An AMD PCnet32 ethernet driver for linux:
+*
+* (C) 1996-1999 Thomas Bogendoerfer
+* See Linux Driver for full information
+*
+* The transmit and poll functions were written with reference to:
+* lance.c - LANCE NIC driver for Etherboot written by Ken Yap
+*
+* Linux Driver Version 1.27a, 10.02.2002
+*
+*
+* REVISION HISTORY:
+* ================
+* v1.0 08-06-2003 timlegge Initial port of Linux driver
+* v1.1 08-23-2003 timlegge Add multicast support
+* v1.2 01-17-2004 timlegge Initial driver output cleanup
+* v1.3 03-29-2004 timlegge More driver cleanup
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+#include "mii.h"
+
+/* void hex_dump(const char *data, const unsigned int len); */
+
+/* Etherboot Specific definations */
+#define drv_version "v1.3"
+#define drv_date "03-29-2004"
+
+static u32 ioaddr; /* Globally used for the card's io address */
+static struct nic_operations pcnet32_operations;
+
+#ifdef EDEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+/* End Etherboot Specific */
+
+static int cards_found = 0 /* __initdata */ ;
+
+#ifdef REMOVE
+/* FIXME: Remove these they are probably pointless */
+
+/*
+ * VLB I/O addresses
+ */
+static unsigned int pcnet32_portlist[] /*__initdata */ =
+{ 0x300, 0x320, 0x340, 0x360, 0 };
+
+static int pcnet32_debug = 1;
+static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */
+static int pcnet32vlb; /* check for VLB cards ? */
+
+static struct net_device *pcnet32_dev;
+
+static int max_interrupt_work = 80;
+static int rx_copybreak = 200;
+#endif
+#define PCNET32_PORT_AUI 0x00
+#define PCNET32_PORT_10BT 0x01
+#define PCNET32_PORT_GPSI 0x02
+#define PCNET32_PORT_MII 0x03
+
+#define PCNET32_PORT_PORTSEL 0x03
+#define PCNET32_PORT_ASEL 0x04
+#define PCNET32_PORT_100 0x40
+#define PCNET32_PORT_FD 0x80
+
+#define PCNET32_DMA_MASK 0xffffffff
+
+/*
+ * table to translate option values from tulip
+ * to internal options
+ */
+static unsigned char options_mapping[] = {
+ PCNET32_PORT_ASEL, /* 0 Auto-select */
+ PCNET32_PORT_AUI, /* 1 BNC/AUI */
+ PCNET32_PORT_AUI, /* 2 AUI/BNC */
+ PCNET32_PORT_ASEL, /* 3 not supported */
+ PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */
+ PCNET32_PORT_ASEL, /* 5 not supported */
+ PCNET32_PORT_ASEL, /* 6 not supported */
+ PCNET32_PORT_ASEL, /* 7 not supported */
+ PCNET32_PORT_ASEL, /* 8 not supported */
+ PCNET32_PORT_MII, /* 9 MII 10baseT */
+ PCNET32_PORT_MII | PCNET32_PORT_FD, /* 10 MII 10baseT-FD */
+ PCNET32_PORT_MII, /* 11 MII (autosel) */
+ PCNET32_PORT_10BT, /* 12 10BaseT */
+ PCNET32_PORT_MII | PCNET32_PORT_100, /* 13 MII 100BaseTx */
+ PCNET32_PORT_MII | PCNET32_PORT_100 | PCNET32_PORT_FD, /* 14 MII 100BaseTx-FD */
+ PCNET32_PORT_ASEL /* 15 not supported */
+};
+
+#define MAX_UNITS 8 /* More are supported, limit only on options */
+static int options[MAX_UNITS];
+static int full_duplex[MAX_UNITS];
+
+/*
+ * Theory of Operation
+ *
+ * This driver uses the same software structure as the normal lance
+ * driver. So look for a verbose description in lance.c. The differences
+ * to the normal lance driver is the use of the 32bit mode of PCnet32
+ * and PCnetPCI chips. Because these chips are 32bit chips, there is no
+ * 16MB limitation and we don't need bounce buffers.
+ */
+
+
+
+/*
+ * Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ * Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
+ * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4).
+ */
+#ifndef PCNET32_LOG_TX_BUFFERS
+#define PCNET32_LOG_TX_BUFFERS 1
+#define PCNET32_LOG_RX_BUFFERS 2
+#endif
+
+#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+/* FIXME: Fix this to allow multiple tx_ring descriptors */
+#define TX_RING_LEN_BITS 0x0000 /*PCNET32_LOG_TX_BUFFERS) << 12) */
+
+#define RX_RING_SIZE (1 << (PCNET32_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+#define RX_RING_LEN_BITS ((PCNET32_LOG_RX_BUFFERS) << 4)
+
+#define PKT_BUF_SZ 1544
+
+/* Offsets from base I/O address. */
+#define PCNET32_WIO_RDP 0x10
+#define PCNET32_WIO_RAP 0x12
+#define PCNET32_WIO_RESET 0x14
+#define PCNET32_WIO_BDP 0x16
+
+#define PCNET32_DWIO_RDP 0x10
+#define PCNET32_DWIO_RAP 0x14
+#define PCNET32_DWIO_RESET 0x18
+#define PCNET32_DWIO_BDP 0x1C
+
+#define PCNET32_TOTAL_SIZE 0x20
+
+/* The PCNET32 Rx and Tx ring descriptors. */
+struct pcnet32_rx_head {
+ u32 base;
+ s16 buf_length;
+ s16 status;
+ u32 msg_length;
+ u32 reserved;
+};
+
+struct pcnet32_tx_head {
+ u32 base;
+ s16 length;
+ s16 status;
+ u32 misc;
+ u32 reserved;
+};
+
+/* The PCNET32 32-Bit initialization block, described in databook. */
+struct pcnet32_init_block {
+ u16 mode;
+ u16 tlen_rlen;
+ u8 phys_addr[6];
+ u16 reserved;
+ u32 filter[2];
+ /* Receive and transmit ring base, along with extra bits. */
+ u32 rx_ring;
+ u32 tx_ring;
+};
+/* PCnet32 access functions */
+struct pcnet32_access {
+ u16(*read_csr) (unsigned long, int);
+ void (*write_csr) (unsigned long, int, u16);
+ u16(*read_bcr) (unsigned long, int);
+ void (*write_bcr) (unsigned long, int, u16);
+ u16(*read_rap) (unsigned long);
+ void (*write_rap) (unsigned long, u16);
+ void (*reset) (unsigned long);
+};
+
+/* Define the TX and RX Descriptors and Rings */
+struct {
+ struct pcnet32_tx_head tx_ring[TX_RING_SIZE]
+ __attribute__ ((aligned(16)));
+ struct pcnet32_rx_head rx_ring[RX_RING_SIZE]
+ __attribute__ ((aligned(16)));
+ unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE];
+ unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ];
+} pcnet32_bufs __shared;
+
+/* May need to be moved to mii.h */
+struct mii_if_info {
+ int phy_id;
+ int advertising;
+ unsigned int full_duplex:1; /* is full duplex? */
+};
+
+/*
+ * The first three fields of pcnet32_private are read by the ethernet device
+ * so we allocate the structure should be allocated by pci_alloc_consistent().
+ */
+#define MII_CNT 4
+struct pcnet32_private {
+ struct pcnet32_init_block init_block;
+ struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */
+ const char *name;
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ struct pcnet32_access a;
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ char tx_full;
+ int options;
+ int shared_irq:1, /* shared irq possible */
+ ltint:1, /* enable TxDone-intr inhibitor */
+ dxsuflo:1, /* disable transmit stop on uflo */
+ mii:1; /* mii port available */
+ struct mii_if_info mii_if;
+ unsigned char phys[MII_CNT];
+ struct net_device *next;
+ int full_duplex:1;
+} lpx;
+
+static struct pcnet32_private *lp;
+
+static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num);
+#if 0
+static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num,
+ int val);
+#endif
+enum pci_flags_bit {
+ PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
+ PCI_ADDR0 = 0x10 << 0, PCI_ADDR1 = 0x10 << 1, PCI_ADDR2 =
+ 0x10 << 2, PCI_ADDR3 = 0x10 << 3,
+};
+
+
+static u16 pcnet32_wio_read_csr(unsigned long addr, int index)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ return inw(addr + PCNET32_WIO_RDP);
+}
+
+static void pcnet32_wio_write_csr(unsigned long addr, int index, u16 val)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ outw(val, addr + PCNET32_WIO_RDP);
+}
+
+static u16 pcnet32_wio_read_bcr(unsigned long addr, int index)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ return inw(addr + PCNET32_WIO_BDP);
+}
+
+static void pcnet32_wio_write_bcr(unsigned long addr, int index, u16 val)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ outw(val, addr + PCNET32_WIO_BDP);
+}
+
+static u16 pcnet32_wio_read_rap(unsigned long addr)
+{
+ return inw(addr + PCNET32_WIO_RAP);
+}
+
+static void pcnet32_wio_write_rap(unsigned long addr, u16 val)
+{
+ outw(val, addr + PCNET32_WIO_RAP);
+}
+
+static void pcnet32_wio_reset(unsigned long addr)
+{
+ inw(addr + PCNET32_WIO_RESET);
+}
+
+static int pcnet32_wio_check(unsigned long addr)
+{
+ outw(88, addr + PCNET32_WIO_RAP);
+ return (inw(addr + PCNET32_WIO_RAP) == 88);
+}
+
+static struct pcnet32_access pcnet32_wio = {
+ read_csr:pcnet32_wio_read_csr,
+ write_csr:pcnet32_wio_write_csr,
+ read_bcr:pcnet32_wio_read_bcr,
+ write_bcr:pcnet32_wio_write_bcr,
+ read_rap:pcnet32_wio_read_rap,
+ write_rap:pcnet32_wio_write_rap,
+ reset:pcnet32_wio_reset
+};
+
+static u16 pcnet32_dwio_read_csr(unsigned long addr, int index)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ return (inl(addr + PCNET32_DWIO_RDP) & 0xffff);
+}
+
+static void pcnet32_dwio_write_csr(unsigned long addr, int index, u16 val)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ outl(val, addr + PCNET32_DWIO_RDP);
+}
+
+static u16 pcnet32_dwio_read_bcr(unsigned long addr, int index)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ return (inl(addr + PCNET32_DWIO_BDP) & 0xffff);
+}
+
+static void pcnet32_dwio_write_bcr(unsigned long addr, int index, u16 val)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ outl(val, addr + PCNET32_DWIO_BDP);
+}
+
+static u16 pcnet32_dwio_read_rap(unsigned long addr)
+{
+ return (inl(addr + PCNET32_DWIO_RAP) & 0xffff);
+}
+
+static void pcnet32_dwio_write_rap(unsigned long addr, u16 val)
+{
+ outl(val, addr + PCNET32_DWIO_RAP);
+}
+
+static void pcnet32_dwio_reset(unsigned long addr)
+{
+ inl(addr + PCNET32_DWIO_RESET);
+}
+
+static int pcnet32_dwio_check(unsigned long addr)
+{
+ outl(88, addr + PCNET32_DWIO_RAP);
+ return ((inl(addr + PCNET32_DWIO_RAP) & 0xffff) == 88);
+}
+
+static struct pcnet32_access pcnet32_dwio = {
+ read_csr:pcnet32_dwio_read_csr,
+ write_csr:pcnet32_dwio_write_csr,
+ read_bcr:pcnet32_dwio_read_bcr,
+ write_bcr:pcnet32_dwio_write_bcr,
+ read_rap:pcnet32_dwio_read_rap,
+ write_rap:pcnet32_dwio_write_rap,
+ reset:pcnet32_dwio_reset
+};
+
+
+/* Initialize the PCNET32 Rx and Tx rings. */
+static int pcnet32_init_ring(struct nic *nic)
+{
+ int i;
+
+ lp->tx_full = 0;
+ lp->cur_rx = lp->cur_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ pcnet32_bufs.rx_ring[i].base =
+ virt_to_le32desc(&pcnet32_bufs.rxb[i]);
+ pcnet32_bufs.rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
+ pcnet32_bufs.rx_ring[i].status = le16_to_cpu(0x8000);
+ }
+
+ /* The Tx buffer address is filled in as needed, but we do need to clear
+ the upper ownership bit. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ pcnet32_bufs.tx_ring[i].base = 0;
+ pcnet32_bufs.tx_ring[i].status = 0;
+ }
+
+
+ lp->init_block.tlen_rlen =
+ le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = nic->node_addr[i];
+ lp->init_block.rx_ring = virt_to_le32desc(&pcnet32_bufs.rx_ring[0]);
+ lp->init_block.tx_ring = virt_to_le32desc(&pcnet32_bufs.tx_ring[0]);
+ return 0;
+}
+
+/**************************************************************************
+RESET - Reset adapter
+***************************************************************************/
+static void pcnet32_reset(struct nic *nic)
+{
+ /* put the card in its initial state */
+ u16 val;
+ int i;
+
+ /* Reset the PCNET32 */
+ lp->a.reset(ioaddr);
+
+ /* switch pcnet32 to 32bit mode */
+ lp->a.write_bcr(ioaddr, 20, 2);
+
+ /* set/reset autoselect bit */
+ val = lp->a.read_bcr(ioaddr, 2) & ~2;
+ if (lp->options & PCNET32_PORT_ASEL)
+ val |= 2;
+ lp->a.write_bcr(ioaddr, 2, val);
+
+ /* handle full duplex setting */
+ if (lp->full_duplex) {
+ val = lp->a.read_bcr(ioaddr, 9) & ~3;
+ if (lp->options & PCNET32_PORT_FD) {
+ val |= 1;
+ if (lp->options ==
+ (PCNET32_PORT_FD | PCNET32_PORT_AUI))
+ val |= 2;
+ } else if (lp->options & PCNET32_PORT_ASEL) {
+ /* workaround of xSeries250, turn on for 79C975 only */
+ i = ((lp->a.
+ read_csr(ioaddr,
+ 88) | (lp->a.read_csr(ioaddr,
+ 89) << 16)) >>
+ 12) & 0xffff;
+ if (i == 0x2627)
+ val |= 3;
+ }
+ lp->a.write_bcr(ioaddr, 9, val);
+ }
+
+ /* set/reset GPSI bit in test register */
+ val = lp->a.read_csr(ioaddr, 124) & ~0x10;
+ if ((lp->options & PCNET32_PORT_PORTSEL) == PCNET32_PORT_GPSI)
+ val |= 0x10;
+ lp->a.write_csr(ioaddr, 124, val);
+
+ if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) {
+ val = lp->a.read_bcr(ioaddr, 32) & ~0x38; /* disable Auto Negotiation, set 10Mpbs, HD */
+ if (lp->options & PCNET32_PORT_FD)
+ val |= 0x10;
+ if (lp->options & PCNET32_PORT_100)
+ val |= 0x08;
+ lp->a.write_bcr(ioaddr, 32, val);
+ } else {
+ if (lp->options & PCNET32_PORT_ASEL) { /* enable auto negotiate, setup, disable fd */
+ val = lp->a.read_bcr(ioaddr, 32) & ~0x98;
+ val |= 0x20;
+ lp->a.write_bcr(ioaddr, 32, val);
+ }
+ }
+
+#ifdef DO_DXSUFLO
+ if (lp->dxsuflo) { /* Disable transmit stop on underflow */
+ val = lp->a.read_csr(ioaddr, 3);
+ val |= 0x40;
+ lp->a.write_csr(ioaddr, 3, val);
+ }
+#endif
+ if (1)
+ {
+ //disable interrupts
+ val = lp->a.read_csr(ioaddr, 3);
+ val = val
+ | (1 << 14) //BABLM intr disabled
+ | (1 << 12) //MISSM missed frame mask intr disabled
+ | (1 << 10) //RINTM receive intr disabled
+ | (1 << 9) //TINTM transmit intr disabled
+ | (1 << 8) //IDONM init done intr disabled
+ ;
+ lp->a.write_csr(ioaddr, 3, val);
+ }
+
+ if (lp->ltint) { /* Enable TxDone-intr inhibitor */
+ val = lp->a.read_csr(ioaddr, 5);
+ val |= (1 << 14);
+ lp->a.write_csr(ioaddr, 5, val);
+ }
+ lp->init_block.mode =
+ le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
+ lp->init_block.filter[0] = 0xffffffff;
+ lp->init_block.filter[1] = 0xffffffff;
+
+ pcnet32_init_ring(nic);
+
+
+ /* Re-initialize the PCNET32, and start it when done. */
+ lp->a.write_csr(ioaddr, 1,
+ (virt_to_bus(&lp->init_block)) & 0xffff);
+ lp->a.write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16);
+ lp->a.write_csr(ioaddr, 4, 0x0915);
+ lp->a.write_csr(ioaddr, 0, 0x0001);
+
+
+ i = 0;
+ while (i++ < 100)
+ if (lp->a.read_csr(ioaddr, 0) & 0x0100)
+ break;
+ /*
+ * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
+ * reports that doing so triggers a bug in the '974.
+ */
+ lp->a.write_csr(ioaddr, 0, 0x0042);
+
+ dprintf(("pcnet32 open, csr0 %hX.\n", lp->a.read_csr(ioaddr, 0)));
+
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int pcnet32_poll(struct nic *nic __unused, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+
+ signed char status;
+ int entry;
+
+ entry = lp->cur_rx & RX_RING_MOD_MASK;
+ status = (le16_to_cpu(pcnet32_bufs.rx_ring[entry].status) >> 8);
+
+ if (status < 0)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ if (status == 0x03) {
+ nic->packetlen =
+ (le32_to_cpu(pcnet32_bufs.rx_ring[entry].msg_length)
+ & 0xfff) - 4;
+ memcpy(nic->packet, &pcnet32_bufs.rxb[entry], nic->packetlen);
+
+ /* Andrew Boyd of QNX reports that some revs of the 79C765
+ * clear the buffer length */
+ pcnet32_bufs.rx_ring[entry].buf_length
+ = le16_to_cpu(-PKT_BUF_SZ);
+ /* prime for next receive */
+ pcnet32_bufs.rx_ring[entry].status |= le16_to_cpu(0x8000);
+ /* Switch to the next Rx ring buffer */
+ lp->cur_rx++;
+
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void pcnet32_transmit(struct nic *nic __unused, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ /* send the packet to destination */
+ unsigned long time;
+ u8 *ptxb;
+ u16 nstype;
+ u16 status;
+ int entry = 0; /*lp->cur_tx & TX_RING_MOD_MASK; */
+
+ status = 0x8300;
+ /* point to the current txb incase multiple tx_rings are used */
+ ptxb = pcnet32_bufs.txb + (lp->cur_tx * PKT_BUF_SZ);
+
+ /* copy the packet to ring buffer */
+ memcpy(ptxb, d, ETH_ALEN); /* dst */
+ memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+ nstype = htons((u16) t); /* type */
+ memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* type */
+ memcpy(ptxb + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ while (s < ETH_ZLEN) /* pad to min length */
+ ptxb[s++] = '\0';
+
+ pcnet32_bufs.tx_ring[entry].length = le16_to_cpu(-s);
+ pcnet32_bufs.tx_ring[entry].misc = 0x00000000;
+ pcnet32_bufs.tx_ring[entry].base = (u32) virt_to_le32desc(ptxb);
+
+ /* we set the top byte as the very last thing */
+ pcnet32_bufs.tx_ring[entry].status = le16_to_cpu(status);
+
+
+ /* Trigger an immediate send poll */
+ lp->a.write_csr(ioaddr, 0, 0x0048);
+
+ /* wait for transmit complete */
+ lp->cur_tx = 0; /* (lp->cur_tx + 1); */
+ time = currticks() + TICKS_PER_SEC; /* wait one second */
+ while (currticks() < time &&
+ ((short) le16_to_cpu(pcnet32_bufs.tx_ring[entry].status) < 0));
+
+ if ((short) le16_to_cpu(pcnet32_bufs.tx_ring[entry].status) < 0)
+ printf("PCNET32 timed out on transmit\n");
+
+ /* Stop pointing at the current txb
+ * otherwise the card continues to send the packet */
+ pcnet32_bufs.tx_ring[entry].base = 0;
+
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void pcnet32_disable ( struct nic *nic __unused ) {
+ /* Stop the PCNET32 here -- it ocassionally polls memory if we don't */
+ lp->a.write_csr(ioaddr, 0, 0x0004);
+
+ /*
+ * Switch back to 16-bit mode to avoid problems with dumb
+ * DOS packet driver after a warm reboot
+ */
+ lp->a.write_bcr(ioaddr, 20, 0);
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void pcnet32_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+You should omit the last argument struct pci_device * for a non-PCI NIC
+***************************************************************************/
+static int pcnet32_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ int i, media;
+ int fdx, mii, fset, dxsuflo, ltint;
+ int chip_version;
+ struct pcnet32_access *a = NULL;
+ char *chipname;
+ u8 promaddr[6];
+ int shared = 1;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ /* BASE is used throughout to address the card */
+ ioaddr = pci->ioaddr;
+ printf("pcnet32.c: Found %s, Vendor=0x%hX Device=0x%hX\n",
+ pci->driver_name, pci->vendor, pci->device);
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ /* reset the chip */
+ pcnet32_wio_reset(ioaddr);
+
+ /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */
+ if (pcnet32_wio_read_csr(ioaddr, 0) == 4
+ && pcnet32_wio_check(ioaddr)) {
+ a = &pcnet32_wio;
+ } else {
+ pcnet32_dwio_reset(ioaddr);
+ if (pcnet32_dwio_read_csr(ioaddr, 0) == 4
+ && pcnet32_dwio_check(ioaddr)) {
+ a = &pcnet32_dwio;
+ } else
+ return 0;
+ }
+
+ chip_version =
+ a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr, 89) << 16);
+
+ dprintf(("PCnet chip version is 0x%X\n", chip_version));
+ if ((chip_version & 0xfff) != 0x003)
+ return 0;
+
+ /* initialize variables */
+ fdx = mii = fset = dxsuflo = ltint = 0;
+ chip_version = (chip_version >> 12) & 0xffff;
+
+ switch (chip_version) {
+ case 0x2420:
+ chipname = "PCnet/PCI 79C970"; /* PCI */
+ break;
+ case 0x2430:
+ if (shared)
+ chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */
+ else
+ chipname = "PCnet/32 79C965"; /* 486/VL bus */
+ break;
+ case 0x2621:
+ chipname = "PCnet/PCI II 79C970A"; /* PCI */
+ fdx = 1;
+ break;
+ case 0x2623:
+ chipname = "PCnet/FAST 79C971"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ fset = 1;
+ ltint = 1;
+ break;
+ case 0x2624:
+ chipname = "PCnet/FAST+ 79C972"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ fset = 1;
+ break;
+ case 0x2625:
+ chipname = "PCnet/FAST III 79C973"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ break;
+ case 0x2626:
+ chipname = "PCnet/Home 79C978"; /* PCI */
+ fdx = 1;
+ /*
+ * This is based on specs published at www.amd.com. This section
+ * assumes that a card with a 79C978 wants to go into 1Mb HomePNA
+ * mode. The 79C978 can also go into standard ethernet, and there
+ * probably should be some sort of module option to select the
+ * mode by which the card should operate
+ */
+ /* switch to home wiring mode */
+ media = a->read_bcr(ioaddr, 49);
+
+ printf("media reset to %#x.\n", media);
+ a->write_bcr(ioaddr, 49, media);
+ break;
+ case 0x2627:
+ chipname = "PCnet/FAST III 79C975"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ break;
+ default:
+ chipname = "UNKNOWN";
+ printf("PCnet version %#x, no PCnet32 chip.\n",
+ chip_version);
+ return 0;
+ }
+
+ /*
+ * On selected chips turn on the BCR18:NOUFLO bit. This stops transmit
+ * starting until the packet is loaded. Strike one for reliability, lose
+ * one for latency - although on PCI this isnt a big loss. Older chips
+ * have FIFO's smaller than a packet, so you can't do this.
+ */
+
+ if (fset) {
+ a->write_bcr(ioaddr, 18,
+ (a->read_bcr(ioaddr, 18) | 0x0800));
+ a->write_csr(ioaddr, 80,
+ (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00);
+ dxsuflo = 1;
+ ltint = 1;
+ }
+
+ DBG ( "%s at %hX,", chipname, (unsigned int) ioaddr );
+
+ /* read PROM address */
+ for (i = 0; i < 6; i++)
+ promaddr[i] = inb(ioaddr + i);
+
+ /* Update the nic structure with the MAC Address */
+ for (i = 0; i < ETH_ALEN; i++) {
+ nic->node_addr[i] = promaddr[i];
+ }
+
+ /* Print out some hardware info */
+ DBG ( "%s: IO Addr 0x%hX, MAC Addr %s\n ", chipname, (unsigned int) ioaddr,
+ eth_ntoa ( nic->node_addr ) );
+
+ /* Set to pci bus master */
+ adjust_pci_device(pci);
+
+ /* point to private storage */
+ lp = &lpx;
+
+#if EBDEBUG
+ if (((chip_version + 1) & 0xfffe) == 0x2624) { /* Version 0x2623 or 0x2624 */
+ i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */
+ dprintf((" tx_start_pt(0x%hX):", i));
+ switch (i >> 10) {
+ case 0:
+ dprintf((" 20 bytes,"));
+ break;
+ case 1:
+ dprintf((" 64 bytes,"));
+ break;
+ case 2:
+ dprintf((" 128 bytes,"));
+ break;
+ case 3:
+ dprintf(("~220 bytes,"));
+ break;
+ }
+ i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */
+ dprintf((" BCR18(%hX):", i & 0xffff));
+ if (i & (1 << 5))
+ dprintf(("BurstWrEn "));
+ if (i & (1 << 6))
+ dprintf(("BurstRdEn "));
+ if (i & (1 << 7))
+ dprintf(("DWordIO "));
+ if (i & (1 << 11))
+ dprintf(("NoUFlow "));
+ i = a->read_bcr(ioaddr, 25);
+ dprintf((" SRAMSIZE=0x%hX,", i << 8));
+ i = a->read_bcr(ioaddr, 26);
+ dprintf((" SRAM_BND=0x%hX,", i << 8));
+ i = a->read_bcr(ioaddr, 27);
+ if (i & (1 << 14))
+ dprintf(("LowLatRx"));
+ }
+#endif
+ lp->name = chipname;
+ lp->shared_irq = shared;
+ lp->full_duplex = fdx;
+ lp->dxsuflo = dxsuflo;
+ lp->ltint = ltint;
+ lp->mii = mii;
+ /* FIXME: Fix Options for only one card */
+ if ((cards_found >= MAX_UNITS)
+ || ((unsigned int) options[cards_found] > sizeof(options_mapping)))
+ lp->options = PCNET32_PORT_ASEL;
+ else
+ lp->options = options_mapping[options[cards_found]];
+
+ if (fdx && !(lp->options & PCNET32_PORT_ASEL) &&
+ ((cards_found >= MAX_UNITS) || full_duplex[cards_found]))
+ lp->options |= PCNET32_PORT_FD;
+
+ if (!a) {
+ printf("No access methods\n");
+ return 0;
+ }
+
+ // lp->a = *a;
+ // Causes a loader:
+ // bin/blib.a(pcnet32.o)(.text+0x6b6): In function `pcnet32_probe':
+ // drivers/net/pcnet32.c:871: undefined reference to `memcpy'
+ // make: *** [bin/pcnet32.dsk.tmp] Error 1
+ // So we do:
+ memcpy ( &lp->a, a, sizeof ( lp->a ) );
+ // To explicity call memcpy.
+
+ /* detect special T1/E1 WAN card by checking for MAC address */
+ if (nic->node_addr[0] == 0x00 && nic->node_addr[1] == 0xe0
+ && nic->node_addr[2] == 0x75)
+ lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI;
+
+ lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */
+ lp->init_block.tlen_rlen =
+ le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = nic->node_addr[i];
+ lp->init_block.filter[0] = 0xffffffff;
+ lp->init_block.filter[1] = 0xffffffff;
+ lp->init_block.rx_ring = virt_to_bus(&pcnet32_bufs.rx_ring);
+ lp->init_block.tx_ring = virt_to_bus(&pcnet32_bufs.tx_ring);
+
+ /* switch pcnet32 to 32bit mode */
+ a->write_bcr(ioaddr, 20, 2);
+
+ a->write_csr(ioaddr, 1, (virt_to_bus(&lp->init_block)) & 0xffff);
+ a->write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16);
+
+ /*
+ * To auto-IRQ we enable the initialization-done and DMA error
+ * interrupts. For ISA boards we get a DMA error, but VLB and PCI
+ * boards will work.
+ */
+ /* Trigger an initialization just for the interrupt. */
+
+
+// a->write_csr(ioaddr, 0, 0x41);
+// mdelay(1);
+
+ cards_found++;
+
+ /* point to NIC specific routines */
+ pcnet32_reset(nic);
+ if (mii) {
+ int tmp;
+ int phy, phy_idx = 0;
+ u16 mii_lpa;
+ lp->phys[0] = 1; /* Default Setting */
+ for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
+ int mii_status = mdio_read(nic, phy, MII_BMSR);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ lp->phys[phy_idx++] = phy;
+ lp->mii_if.advertising =
+ mdio_read(nic, phy, MII_ADVERTISE);
+ if ((mii_status & 0x0040) == 0) {
+ tmp = phy;
+ dprintf (("MII PHY found at address %d, status "
+ "%hX advertising %hX\n", phy, mii_status,
+ lp->mii_if.advertising));
+ }
+ }
+ }
+ if (phy_idx == 0)
+ printf("No MII transceiver found!\n");
+ lp->mii_if.phy_id = lp->phys[0];
+
+ lp->mii_if.advertising =
+ mdio_read(nic, lp->phys[0], MII_ADVERTISE);
+
+ mii_lpa = mdio_read(nic, lp->phys[0], MII_LPA);
+ lp->mii_if.advertising &= mii_lpa;
+ if (lp->mii_if.advertising & ADVERTISE_100FULL)
+ printf("100Mbps Full-Duplex\n");
+ else if (lp->mii_if.advertising & ADVERTISE_100HALF)
+ printf("100Mbps Half-Duplex\n");
+ else if (lp->mii_if.advertising & ADVERTISE_10FULL)
+ printf("10Mbps Full-Duplex\n");
+ else if (lp->mii_if.advertising & ADVERTISE_10HALF)
+ printf("10Mbps Half-Duplex\n");
+ else
+ printf("\n");
+ } else {
+ /* The older chips are fixed 10Mbps, and some support full duplex,
+ * although not via autonegotiation, but only via configuration. */
+ if (fdx)
+ printf("10Mbps Full-Duplex\n");
+ else
+ printf("10Mbps Half-Duplex\n");
+ }
+
+ nic->nic_op = &pcnet32_operations;
+
+ return 1;
+}
+static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num)
+{
+ u16 val_out;
+ int phyaddr;
+
+ if (!lp->mii)
+ return 0;
+
+ phyaddr = lp->a.read_bcr(ioaddr, 33);
+
+ lp->a.write_bcr(ioaddr, 33,
+ ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+ val_out = lp->a.read_bcr(ioaddr, 34);
+ lp->a.write_bcr(ioaddr, 33, phyaddr);
+
+ return val_out;
+}
+
+#if 0
+static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num,
+ int val)
+{
+ int phyaddr;
+
+ if (!lp->mii)
+ return;
+
+ phyaddr = lp->a.read_bcr(ioaddr, 33);
+
+ lp->a.write_bcr(ioaddr, 33,
+ ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+ lp->a.write_bcr(ioaddr, 34, val);
+ lp->a.write_bcr(ioaddr, 33, phyaddr);
+}
+#endif
+
+static struct nic_operations pcnet32_operations = {
+ .connect = dummy_connect,
+ .poll = pcnet32_poll,
+ .transmit = pcnet32_transmit,
+ .irq = pcnet32_irq,
+
+};
+
+static struct pci_device_id pcnet32_nics[] = {
+ PCI_ROM(0x1022, 0x2000, "pcnet32", "AMD PCnet/PCI"),
+ PCI_ROM(0x1022, 0x2625, "pcnetfastiii", "AMD PCNet FAST III"),
+ PCI_ROM(0x1022, 0x2001, "amdhomepna", "AMD PCnet/HomePNA"),
+};
+
+PCI_DRIVER ( pcnet32_driver, pcnet32_nics, PCI_NO_CLASS );
+
+DRIVER ( "PCNET32/PCI", nic_driver, pci_driver, pcnet32_driver,
+ pcnet32_probe, pcnet32_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/pnic.c b/gpxe/src/drivers/net/pnic.c
new file mode 100644
index 00000000..b431ec52
--- /dev/null
+++ b/gpxe/src/drivers/net/pnic.c
@@ -0,0 +1,276 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Bochs Pseudo NIC driver for Etherboot
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ *
+ * See pnic_api.h for an explanation of the Bochs Pseudo NIC.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <io.h>
+#include <errno.h>
+#include <gpxe/pci.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+
+#include "pnic_api.h"
+
+struct pnic {
+ unsigned short ioaddr;
+};
+
+/*
+ * Utility functions: issue a PNIC command, retrieve result. Use
+ * pnic_command_quiet if you don't want failure codes to be
+ * automatically printed. Returns the PNIC status code.
+ *
+ * Set output_length to NULL only if you expect to receive exactly
+ * output_max_length bytes, otherwise it'll complain that you didn't
+ * get enough data (on the assumption that if you not interested in
+ * discovering the output length then you're expecting a fixed amount
+ * of data).
+ */
+
+static uint16_t pnic_command_quiet ( struct pnic *pnic, uint16_t command,
+ const void *input, uint16_t input_length,
+ void *output, uint16_t output_max_length,
+ uint16_t *output_length ) {
+ uint16_t status;
+ uint16_t _output_length;
+
+ if ( input != NULL ) {
+ /* Write input length */
+ outw ( input_length, pnic->ioaddr + PNIC_REG_LEN );
+ /* Write input data */
+ outsb ( pnic->ioaddr + PNIC_REG_DATA, input, input_length );
+ }
+ /* Write command */
+ outw ( command, pnic->ioaddr + PNIC_REG_CMD );
+ /* Retrieve status */
+ status = inw ( pnic->ioaddr + PNIC_REG_STAT );
+ /* Retrieve output length */
+ _output_length = inw ( pnic->ioaddr + PNIC_REG_LEN );
+ if ( output_length == NULL ) {
+ if ( _output_length != output_max_length ) {
+ printf ( "pnic_command %#hx: wrong data length "
+ "returned (expected %d, got %d)\n", command,
+ output_max_length, _output_length );
+ }
+ } else {
+ *output_length = _output_length;
+ }
+ if ( output != NULL ) {
+ if ( _output_length > output_max_length ) {
+ printf ( "pnic_command %#hx: output buffer too small "
+ "(have %d, need %d)\n", command,
+ output_max_length, _output_length );
+ _output_length = output_max_length;
+ }
+ /* Retrieve output data */
+ insb ( pnic->ioaddr + PNIC_REG_DATA, output, _output_length );
+ }
+ return status;
+}
+
+static uint16_t pnic_command ( struct pnic *pnic, uint16_t command,
+ const void *input, uint16_t input_length,
+ void *output, uint16_t output_max_length,
+ uint16_t *output_length ) {
+ uint16_t status = pnic_command_quiet ( pnic, command,
+ input, input_length,
+ output, output_max_length,
+ output_length );
+ if ( status == PNIC_STATUS_OK ) return status;
+ printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n",
+ command, input_length, status );
+ return status;
+}
+
+/* Check API version matches that of NIC */
+static int pnic_api_check ( uint16_t api_version ) {
+ if ( api_version != PNIC_API_VERSION ) {
+ printf ( "Warning: API version mismatch! "
+ "(NIC's is %d.%d, ours is %d.%d)\n",
+ api_version >> 8, api_version & 0xff,
+ PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff );
+ }
+ if ( api_version < PNIC_API_VERSION ) {
+ printf ( "** You may need to update your copy of Bochs **\n" );
+ }
+ return ( api_version == PNIC_API_VERSION );
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static void pnic_poll ( struct net_device *netdev ) {
+ struct pnic *pnic = netdev->priv;
+ struct io_buffer *iobuf;
+ uint16_t length;
+ uint16_t qlen;
+
+ /* Fetch all available packets */
+ while ( 1 ) {
+ if ( pnic_command ( pnic, PNIC_CMD_RECV_QLEN, NULL, 0,
+ &qlen, sizeof ( qlen ), NULL )
+ != PNIC_STATUS_OK )
+ return;
+ if ( qlen == 0 )
+ return;
+ iobuf = alloc_iob ( ETH_FRAME_LEN );
+ if ( ! iobuf ) {
+ DBG ( "could not allocate buffer\n" );
+ netdev_rx_err ( netdev, NULL, -ENOMEM );
+ return;
+ }
+ if ( pnic_command ( pnic, PNIC_CMD_RECV, NULL, 0,
+ iobuf->data, ETH_FRAME_LEN, &length )
+ != PNIC_STATUS_OK ) {
+ netdev_rx_err ( netdev, iobuf, -EIO );
+ return;
+ }
+ iob_put ( iobuf, length );
+ netdev_rx ( netdev, iobuf );
+ }
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static int pnic_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
+ struct pnic *pnic = netdev->priv;
+
+ /* Pad the packet */
+ iob_pad ( iobuf, ETH_ZLEN );
+
+ /* Send packet */
+ pnic_command ( pnic, PNIC_CMD_XMIT, iobuf->data, iob_len ( iobuf ),
+ NULL, 0, NULL );
+
+ netdev_tx_complete ( netdev, iobuf );
+ return 0;
+}
+
+/**************************************************************************
+OPEN - Open network device
+***************************************************************************/
+static int pnic_open ( struct net_device *netdev __unused ) {
+ /* Nothing to do */
+ return 0;
+}
+
+/**************************************************************************
+CLOSE - Close network device
+***************************************************************************/
+static void pnic_close ( struct net_device *netdev __unused ) {
+ /* Nothing to do */
+}
+
+/**************************************************************************
+IRQ - Enable/disable interrupts
+***************************************************************************/
+static void pnic_irq ( struct net_device *netdev, int enable ) {
+ struct pnic *pnic = netdev->priv;
+ uint8_t mask = ( enable ? 1 : 0 );
+
+ pnic_command ( pnic, PNIC_CMD_MASK_IRQ, &mask, sizeof ( mask ),
+ NULL, 0, NULL );
+}
+
+/**************************************************************************
+OPERATIONS TABLE
+***************************************************************************/
+static struct net_device_operations pnic_operations = {
+ .open = pnic_open,
+ .close = pnic_close,
+ .transmit = pnic_transmit,
+ .poll = pnic_poll,
+ .irq = pnic_irq,
+};
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void pnic_remove ( struct pci_device *pci ) {
+ struct net_device *netdev = pci_get_drvdata ( pci );
+ struct pnic *pnic = netdev->priv;
+
+ unregister_netdev ( netdev );
+ pnic_command ( pnic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int pnic_probe ( struct pci_device *pci,
+ const struct pci_device_id *id __unused ) {
+ struct net_device *netdev;
+ struct pnic *pnic;
+ uint16_t api_version;
+ uint16_t status;
+ int rc;
+
+ /* Allocate net device */
+ netdev = alloc_etherdev ( sizeof ( *pnic ) );
+ if ( ! netdev )
+ return -ENOMEM;
+ netdev_init ( netdev, &pnic_operations );
+ pnic = netdev->priv;
+ pci_set_drvdata ( pci, netdev );
+ netdev->dev = &pci->dev;
+ pnic->ioaddr = pci->ioaddr;
+
+ /* Fix up PCI device */
+ adjust_pci_device ( pci );
+
+ /* API version check */
+ status = pnic_command_quiet ( pnic, PNIC_CMD_API_VER, NULL, 0,
+ &api_version,
+ sizeof ( api_version ), NULL );
+ if ( status != PNIC_STATUS_OK ) {
+ printf ( "PNIC failed installation check, code %#hx\n",
+ status );
+ rc = -EIO;
+ goto err;
+ }
+ pnic_api_check ( api_version );
+
+ /* Get MAC address */
+ status = pnic_command ( pnic, PNIC_CMD_READ_MAC, NULL, 0,
+ netdev->ll_addr, ETH_ALEN, NULL );
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err;
+
+ return 0;
+
+ err:
+ /* Free net device */
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ return rc;
+}
+
+static struct pci_device_id pnic_nics[] = {
+/* genrules.pl doesn't let us use macros for PCI IDs...*/
+PCI_ROM ( 0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor" ),
+};
+
+struct pci_driver pnic_driver __pci_driver = {
+ .ids = pnic_nics,
+ .id_count = ( sizeof ( pnic_nics ) / sizeof ( pnic_nics[0] ) ),
+ .probe = pnic_probe,
+ .remove = pnic_remove,
+};
diff --git a/gpxe/src/drivers/net/pnic_api.h b/gpxe/src/drivers/net/pnic_api.h
new file mode 100644
index 00000000..6d117fa6
--- /dev/null
+++ b/gpxe/src/drivers/net/pnic_api.h
@@ -0,0 +1,59 @@
+/*
+ * Constants etc. for the Bochs/Etherboot pseudo-NIC
+ *
+ * This header file must be valid C and C++.
+ *
+ * Operation of the pseudo-NIC (PNIC) is pretty simple. To write a
+ * command plus data, first write the length of the data to
+ * PNIC_REG_LEN, then write the data a byte at a type to
+ * PNIC_REG_DATA, then write the command code to PNIC_REG_CMD. The
+ * status will be available from PNIC_REG_STAT. The length of any
+ * data returned will be in PNIC_REG_LEN and can be read a byte at a
+ * time from PNIC_REG_DATA.
+ */
+
+/*
+ * PCI parameters
+ */
+#define PNIC_PCI_VENDOR 0xfefe /* Hopefully these won't clash with */
+#define PNIC_PCI_DEVICE 0xefef /* any real PCI device IDs. */
+
+/*
+ * 'Hardware' register addresses, offset from io_base
+ */
+#define PNIC_REG_CMD 0x00 /* Command register, 2 bytes, write only */
+#define PNIC_REG_STAT 0x00 /* Status register, 2 bytes, read only */
+#define PNIC_REG_LEN 0x02 /* Length register, 2 bytes, read-write */
+#define PNIC_REG_DATA 0x04 /* Data port, 1 byte, read-write */
+/*
+ * PNIC_MAX_REG used in Bochs to claim i/o space
+ */
+#define PNIC_MAX_REG 0x04
+
+/*
+ * Command code definitions: write these into PNIC_REG_CMD
+ */
+#define PNIC_CMD_NOOP 0x0000
+#define PNIC_CMD_API_VER 0x0001
+#define PNIC_CMD_READ_MAC 0x0002
+#define PNIC_CMD_RESET 0x0003
+#define PNIC_CMD_XMIT 0x0004
+#define PNIC_CMD_RECV 0x0005
+#define PNIC_CMD_RECV_QLEN 0x0006
+#define PNIC_CMD_MASK_IRQ 0x0007
+#define PNIC_CMD_FORCE_IRQ 0x0008
+
+/*
+ * Status code definitions: read these from PNIC_REG_STAT
+ *
+ * We avoid using status codes that might be confused with
+ * randomly-read data (e.g. 0x0000, 0xffff etc.)
+ */
+#define PNIC_STATUS_OK 0x4f4b /* 'OK' */
+#define PNIC_STATUS_UNKNOWN_CMD 0x3f3f /* '??' */
+
+/*
+ * Other miscellaneous information
+ */
+
+#define PNIC_API_VERSION 0x0101 /* 1.1 */
diff --git a/gpxe/src/drivers/net/prism2.c b/gpxe/src/drivers/net/prism2.c
new file mode 100644
index 00000000..aaf4c968
--- /dev/null
+++ b/gpxe/src/drivers/net/prism2.c
@@ -0,0 +1,855 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Prism2 NIC driver for Etherboot
+
+Written by Michael Brown of Fen Systems Ltd
+$Id$
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#include <etherboot.h>
+#include <nic.h>
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+
+/*
+ * Hard-coded SSID
+ * Leave blank in order to connect to any available SSID
+ */
+
+static const char hardcoded_ssid[] = "";
+
+/*
+ * Maximum number of info packets to wait for on a join attempt.
+ * Some APs (including the Linksys WAP11) will send a "you are disconnected" packet
+ * before sending the "you are connected" packet, if the card has previously been
+ * attached to the AP.
+ *
+ * 2 is probably a sensible value, but YMMV.
+ */
+
+#define MAX_JOIN_INFO_COUNT 2
+
+/*
+ * Type of Prism2 interface to support
+ * If not already defined, select PLX
+ */
+#ifndef WLAN_HOSTIF
+#define WLAN_HOSTIF WLAN_PLX
+#endif
+
+/*
+ * Include wlan_compat, p80211 and hfa384x header files from Linux Prism2 driver
+ * We need to hack some defines in order to avoid compiling kernel-specific routines
+ */
+
+#define __LINUX_WLAN__
+#undef __KERNEL__
+#define __I386__
+#include "wlan_compat.h"
+#include "p80211hdr.h"
+#include "hfa384x.h"
+#define BAP_TIMEOUT ( 5000 )
+
+/*
+ * A few hacks to make the coding environment more Linux-like. This makes it somewhat
+ * quicker to convert code from the Linux Prism2 driver.
+ */
+#include <errno.h>
+#define __le16_to_cpu(x) (x)
+#define __le32_to_cpu(x) (x)
+#define __cpu_to_le16(x) (x)
+#define __cpu_to_le32(x) (x)
+
+#define hfa384x2host_16(n) (__le16_to_cpu((UINT16)(n)))
+#define hfa384x2host_32(n) (__le32_to_cpu((UINT32)(n)))
+#define host2hfa384x_16(n) (__cpu_to_le16((UINT16)(n)))
+#define host2hfa384x_32(n) (__cpu_to_le32((UINT32)(n)))
+
+/*
+ * PLX9052 PCI register offsets
+ * Taken from PLX9052 datasheet available from http://www.plxtech.com/download/9052/databook/9052db-20.pdf
+ */
+
+#define PLX_LOCAL_CONFIG_REGISTER_BASE ( PCI_BASE_ADDRESS_1 )
+#define PLX_LOCAL_ADDRESS_SPACE_0_BASE ( PCI_BASE_ADDRESS_2 )
+#define PLX_LOCAL_ADDRESS_SPACE_1_BASE ( PCI_BASE_ADDRESS_3 )
+#define PLX_LOCAL_ADDRESS_SPACE_2_BASE ( PCI_BASE_ADDRESS_4 )
+#define PLX_LOCAL_ADDRESS_SPACE_3_BASE ( PCI_BASE_ADDRESS_5 )
+
+#define PRISM2_PLX_ATTR_MEM_BASE ( PLX_LOCAL_ADDRESS_SPACE_0_BASE )
+#define PRISM2_PLX_IO_BASE ( PLX_LOCAL_ADDRESS_SPACE_1_BASE )
+
+#define PRISM2_PCI_MEM_BASE ( PCI_BASE_ADDRESS_0 )
+
+/*
+ * PCMCIA CIS types
+ * Taken from cistpl.h in pcmcia-cs
+ */
+
+#define CISTPL_VERS_1 ( 0x15 )
+#define CISTPL_END ( 0xff )
+
+#define CIS_STEP ( 2 )
+#define CISTPL_HEADER_LEN ( 2 * CIS_STEP )
+#define CISTPL_LEN_OFF ( 1 * CIS_STEP )
+#define CISTPL_VERS_1_STR_OFF ( 4 * CIS_STEP )
+
+/*
+ * Prism2 constants
+ * Taken from prism2sta.c in linux-wlan-ng
+ */
+
+#define COR_OFFSET ( 0x3e0 ) /* COR attribute offset of Prism2 PC card */
+#define COR_VALUE ( 0x41 ) /* Enable PC card with irq in level trigger (but interrupts disabled) */
+
+/* NIC specific static variables */
+
+/* The hfa384x_t structure is used extensively in the Linux driver but is ifdef'd out in our include since __KERNEL__ is not defined.
+ * This is a dummy version that contains only the fields we are interested in.
+ */
+
+typedef struct hfa384x
+{
+ UINT32 iobase;
+ UINT32 membase;
+ UINT16 lastcmd;
+ UINT16 status; /* in host order */
+ UINT16 resp0; /* in host order */
+ UINT16 resp1; /* in host order */
+ UINT16 resp2; /* in host order */
+ UINT8 bssid[WLAN_BSSID_LEN];
+} hfa384x_t;
+
+/* The global instance of the hardware (i.e. where we store iobase and membase, in the absence of anywhere better to put them */
+static hfa384x_t hw_global = {
+ 0, 0, 0, 0, 0, 0, 0, {0,0,0,0,0,0}
+};
+
+/*
+ * 802.11 headers in addition to those in hfa384x_tx_frame_t (LLC and SNAP)
+ * Taken from p80211conv.h
+ */
+
+typedef struct wlan_llc
+{
+ UINT8 dsap;
+ UINT8 ssap;
+ UINT8 ctl;
+} wlan_llc_t;
+
+static const wlan_llc_t wlan_llc_snap = { 0xaa, 0xaa, 0x03 }; /* LLC header indicating SNAP (?) */
+
+#define WLAN_IEEE_OUI_LEN 3
+typedef struct wlan_snap
+{
+ UINT8 oui[WLAN_IEEE_OUI_LEN];
+ UINT16 type;
+} wlan_snap_t;
+
+typedef struct wlan_80211hdr
+{
+ wlan_llc_t llc;
+ wlan_snap_t snap;
+} wlan_80211hdr_t;
+
+/*
+ * Function prototypes
+ */
+
+/*
+ * Hardware-level hfa384x functions
+ * These are based on the ones in hfa384x.h (which are ifdef'd out since __KERNEL__ is not defined).
+ * Basically, these functions are the result of hand-evaluating all the ifdefs and defines in the hfa384x.h versions.
+ */
+
+/* Retrieve the value of one of the MAC registers. */
+static inline UINT16 hfa384x_getreg( hfa384x_t *hw, UINT reg )
+{
+#if (WLAN_HOSTIF == WLAN_PLX)
+ return inw ( hw->iobase + reg );
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ return readw ( hw->membase + reg );
+#endif
+}
+
+/* Set the value of one of the MAC registers. */
+static inline void hfa384x_setreg( hfa384x_t *hw, UINT16 val, UINT reg )
+{
+#if (WLAN_HOSTIF == WLAN_PLX)
+ outw ( val, hw->iobase + reg );
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ writew ( val, hw->membase + reg );
+#endif
+ return;
+}
+
+/*
+ * Noswap versions
+ * Etherboot is i386 only, so swap and noswap are the same...
+ */
+static inline UINT16 hfa384x_getreg_noswap( hfa384x_t *hw, UINT reg )
+{
+ return hfa384x_getreg ( hw, reg );
+}
+static inline void hfa384x_setreg_noswap( hfa384x_t *hw, UINT16 val, UINT reg )
+{
+ hfa384x_setreg ( hw, val, reg );
+}
+
+/*
+ * Low-level hfa384x functions
+ * These are based on the ones in hfa384x.c, modified to work in the Etherboot environment.
+ */
+
+/*
+ * hfa384x_docmd_wait
+ *
+ * Waits for availability of the Command register, then
+ * issues the given command. Then polls the Evstat register
+ * waiting for command completion.
+ * Arguments:
+ * hw device structure
+ * cmd Command in host order
+ * parm0 Parameter0 in host order
+ * parm1 Parameter1 in host order
+ * parm2 Parameter2 in host order
+ * Returns:
+ * 0 success
+ * >0 command indicated error, Status and Resp0-2 are
+ * in hw structure.
+ */
+static int hfa384x_docmd_wait( hfa384x_t *hw, UINT16 cmd, UINT16 parm0, UINT16 parm1, UINT16 parm2)
+{
+ UINT16 reg = 0;
+ UINT16 counter = 0;
+
+ /* wait for the busy bit to clear */
+ counter = 0;
+ reg = hfa384x_getreg(hw, HFA384x_CMD);
+ while ( HFA384x_CMD_ISBUSY(reg) && (counter < 10) ) {
+ reg = hfa384x_getreg(hw, HFA384x_CMD);
+ counter++;
+ udelay(10);
+ }
+ if (HFA384x_CMD_ISBUSY(reg)) {
+ printf("hfa384x_cmd timeout(1), reg=0x%0hx.\n", reg);
+ return -ETIMEDOUT;
+ }
+
+ /* busy bit clear, write command */
+ hfa384x_setreg(hw, parm0, HFA384x_PARAM0);
+ hfa384x_setreg(hw, parm1, HFA384x_PARAM1);
+ hfa384x_setreg(hw, parm2, HFA384x_PARAM2);
+ hw->lastcmd = cmd;
+ hfa384x_setreg(hw, cmd, HFA384x_CMD);
+
+ /* Now wait for completion */
+ counter = 0;
+ reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+ /* Initialization is the problem. It takes about
+ 100ms. "normal" commands are typically is about
+ 200-400 us (I've never seen less than 200). Longer
+ is better so that we're not hammering the bus. */
+ while ( !HFA384x_EVSTAT_ISCMD(reg) && (counter < 5000)) {
+ reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+ counter++;
+ udelay(200);
+ }
+ if ( ! HFA384x_EVSTAT_ISCMD(reg) ) {
+ printf("hfa384x_cmd timeout(2), reg=0x%0hx.\n", reg);
+ return -ETIMEDOUT;
+ }
+
+ /* Read status and response */
+ hw->status = hfa384x_getreg(hw, HFA384x_STATUS);
+ hw->resp0 = hfa384x_getreg(hw, HFA384x_RESP0);
+ hw->resp1 = hfa384x_getreg(hw, HFA384x_RESP1);
+ hw->resp2 = hfa384x_getreg(hw, HFA384x_RESP2);
+ hfa384x_setreg(hw, HFA384x_EVACK_CMD, HFA384x_EVACK);
+ return HFA384x_STATUS_RESULT_GET(hw->status);
+}
+
+/*
+ * Prepare BAP for access. Assigns FID and RID, sets offset register
+ * and waits for BAP to become available.
+ *
+ * Arguments:
+ * hw device structure
+ * id FID or RID, destined for the select register (host order)
+ * offset An _even_ offset into the buffer for the given FID/RID.
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_prepare_bap(hfa384x_t *hw, UINT16 id, UINT16 offset)
+{
+ int result = 0;
+ UINT16 reg;
+ UINT16 i;
+
+ /* Validate offset, buf, and len */
+ if ( (offset > HFA384x_BAP_OFFSET_MAX) || (offset % 2) ) {
+ result = -EINVAL;
+ } else {
+ /* Write fid/rid and offset */
+ hfa384x_setreg(hw, id, HFA384x_SELECT0);
+ udelay(10);
+ hfa384x_setreg(hw, offset, HFA384x_OFFSET0);
+ /* Wait for offset[busy] to clear (see BAP_TIMEOUT) */
+ i = 0;
+ do {
+ reg = hfa384x_getreg(hw, HFA384x_OFFSET0);
+ if ( i > 0 ) udelay(2);
+ i++;
+ } while ( i < BAP_TIMEOUT && HFA384x_OFFSET_ISBUSY(reg));
+ if ( i >= BAP_TIMEOUT ) {
+ /* failure */
+ result = reg;
+ } else if ( HFA384x_OFFSET_ISERR(reg) ){
+ /* failure */
+ result = reg;
+ }
+ }
+ return result;
+}
+
+/*
+ * Copy data from BAP to memory.
+ *
+ * Arguments:
+ * hw device structure
+ * id FID or RID, destined for the select register (host order)
+ * offset An _even_ offset into the buffer for the given FID/RID.
+ * buf ptr to array of bytes
+ * len length of data to transfer in bytes
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_copy_from_bap(hfa384x_t *hw, UINT16 id, UINT16 offset,
+ void *buf, UINT len)
+{
+ int result = 0;
+ UINT8 *d = (UINT8*)buf;
+ UINT16 i;
+ UINT16 reg = 0;
+
+ /* Prepare BAP */
+ result = hfa384x_prepare_bap ( hw, id, offset );
+ if ( result == 0 ) {
+ /* Read even(len) buf contents from data reg */
+ for ( i = 0; i < (len & 0xfffe); i+=2 ) {
+ *(UINT16*)(&(d[i])) = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
+ }
+ /* If len odd, handle last byte */
+ if ( len % 2 ){
+ reg = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
+ d[len-1] = ((UINT8*)(&reg))[0];
+ }
+ }
+ if (result) {
+ printf ( "copy_from_bap(%#hx, %#hx, %d) failed, result=%#hx\n", id, offset, len, result);
+ }
+ return result;
+}
+
+/*
+ * Copy data from memory to BAP.
+ *
+ * Arguments:
+ * hw device structure
+ * id FID or RID, destined for the select register (host order)
+ * offset An _even_ offset into the buffer for the given FID/RID.
+ * buf ptr to array of bytes
+ * len length of data to transfer in bytes
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_copy_to_bap(hfa384x_t *hw, UINT16 id, UINT16 offset,
+ void *buf, UINT len)
+{
+ int result = 0;
+ UINT8 *d = (UINT8*)buf;
+ UINT16 i;
+ UINT16 savereg;
+
+ /* Prepare BAP */
+ result = hfa384x_prepare_bap ( hw, id, offset );
+ if ( result == 0 ) {
+ /* Write even(len) buf contents to data reg */
+ for ( i = 0; i < (len & 0xfffe); i+=2 ) {
+ hfa384x_setreg_noswap(hw, *(UINT16*)(&(d[i])), HFA384x_DATA0);
+ }
+ /* If len odd, handle last byte */
+ if ( len % 2 ){
+ savereg = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
+ result = hfa384x_prepare_bap ( hw, id, offset + (len & 0xfffe) );
+ if ( result == 0 ) {
+ ((UINT8*)(&savereg))[0] = d[len-1];
+ hfa384x_setreg_noswap(hw, savereg, HFA384x_DATA0);
+ }
+ }
+ }
+ if (result) {
+ printf ( "copy_to_bap(%#hx, %#hx, %d) failed, result=%#hx\n", id, offset, len, result);
+ }
+ return result;
+}
+
+/*
+ * Request a given record to be copied to/from the record buffer.
+ *
+ * Arguments:
+ * hw device structure
+ * write [0|1] copy the record buffer to the given
+ * configuration record. (host order)
+ * rid RID of the record to read/write. (host order)
+ *
+ * Returns:
+ * 0 success
+ */
+static inline int hfa384x_cmd_access(hfa384x_t *hw, UINT16 write, UINT16 rid)
+{
+ return hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ACCESS) | HFA384x_CMD_WRITE_SET(write), rid, 0, 0);
+}
+
+/*
+ * Performs the sequence necessary to read a config/info item.
+ *
+ * Arguments:
+ * hw device structure
+ * rid config/info record id (host order)
+ * buf host side record buffer. Upon return it will
+ * contain the body portion of the record (minus the
+ * RID and len).
+ * len buffer length (in bytes, should match record length)
+ *
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_drvr_getconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len)
+{
+ int result = 0;
+ hfa384x_rec_t rec;
+
+ /* Request read of RID */
+ result = hfa384x_cmd_access( hw, 0, rid);
+ if ( result ) {
+ printf("Call to hfa384x_cmd_access failed\n");
+ return -1;
+ }
+ /* Copy out record length */
+ result = hfa384x_copy_from_bap( hw, rid, 0, &rec, sizeof(rec));
+ if ( result ) {
+ return -1;
+ }
+ /* Validate the record length */
+ if ( ((hfa384x2host_16(rec.reclen)-1)*2) != len ) { /* note body len calculation in bytes */
+ printf ( "RID len mismatch, rid=%#hx hlen=%d fwlen=%d\n", rid, len, (hfa384x2host_16(rec.reclen)-1)*2);
+ return -1;
+ }
+ /* Copy out record data */
+ result = hfa384x_copy_from_bap( hw, rid, sizeof(rec), buf, len);
+ return result;
+}
+
+/*
+ * Performs the sequence necessary to read a 16/32 bit config/info item
+ * and convert it to host order.
+ *
+ * Arguments:
+ * hw device structure
+ * rid config/info record id (in host order)
+ * val ptr to 16/32 bit buffer to receive value (in host order)
+ *
+ * Returns:
+ * 0 success
+ */
+#if 0 /* Not actually used anywhere */
+static int hfa384x_drvr_getconfig16(hfa384x_t *hw, UINT16 rid, void *val)
+{
+ int result = 0;
+ result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT16));
+ if ( result == 0 ) {
+ *((UINT16*)val) = hfa384x2host_16(*((UINT16*)val));
+ }
+ return result;
+}
+#endif
+#if 0 /* Not actually used anywhere */
+static int hfa384x_drvr_getconfig32(hfa384x_t *hw, UINT16 rid, void *val)
+{
+ int result = 0;
+ result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT32));
+ if ( result == 0 ) {
+ *((UINT32*)val) = hfa384x2host_32(*((UINT32*)val));
+ }
+ return result;
+}
+#endif
+
+/*
+ * Performs the sequence necessary to write a config/info item.
+ *
+ * Arguments:
+ * hw device structure
+ * rid config/info record id (in host order)
+ * buf host side record buffer
+ * len buffer length (in bytes)
+ *
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_drvr_setconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len)
+{
+ int result = 0;
+ hfa384x_rec_t rec;
+
+ rec.rid = host2hfa384x_16(rid);
+ rec.reclen = host2hfa384x_16((len/2) + 1); /* note conversion to words, +1 for rid field */
+ /* write the record header */
+ result = hfa384x_copy_to_bap( hw, rid, 0, &rec, sizeof(rec));
+ if ( result ) {
+ printf("Failure writing record header\n");
+ return -1;
+ }
+ /* write the record data (if there is any) */
+ if ( len > 0 ) {
+ result = hfa384x_copy_to_bap( hw, rid, sizeof(rec), buf, len);
+ if ( result ) {
+ printf("Failure writing record data\n");
+ return -1;
+ }
+ }
+ /* Trigger setting of record */
+ result = hfa384x_cmd_access( hw, 1, rid);
+ return result;
+}
+
+/*
+ * Performs the sequence necessary to write a 16/32 bit config/info item.
+ *
+ * Arguments:
+ * hw device structure
+ * rid config/info record id (in host order)
+ * val 16/32 bit value to store (in host order)
+ *
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_drvr_setconfig16(hfa384x_t *hw, UINT16 rid, UINT16 *val)
+{
+ UINT16 value;
+ value = host2hfa384x_16(*val);
+ return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(UINT16));
+}
+#if 0 /* Not actually used anywhere */
+static int hfa384x_drvr_setconfig32(hfa384x_t *hw, UINT16 rid, UINT32 *val)
+{
+ UINT32 value;
+ value = host2hfa384x_32(*val);
+ return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(UINT32));
+}
+#endif
+
+/*
+ * Wait for an event, with specified checking interval and timeout.
+ * Automatically acknolwedges events.
+ *
+ * Arguments:
+ * hw device structure
+ * event_mask EVSTAT register mask of events to wait for
+ * event_ack EVACK register set of events to be acknowledged if they happen (can be
+ * used to acknowledge "ignorable" events in addition to the "main" event)
+ * wait Time (in us) to wait between each poll of the register
+ * timeout Maximum number of polls before timing out
+ * descr Descriptive text string of what is being waited for
+ * (will be printed out if a timeout happens)
+ *
+ * Returns:
+ * value of EVSTAT register, or 0 on failure
+ */
+static int hfa384x_wait_for_event(hfa384x_t *hw, UINT16 event_mask, UINT16 event_ack, int wait, int timeout, const char *descr)
+{
+ UINT16 reg;
+ int count = 0;
+
+ do {
+ reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+ if ( count > 0 ) udelay(wait);
+ count++;
+ } while ( !(reg & event_mask) && count < timeout);
+ if ( count >= timeout ) {
+ printf("hfa384x: Timed out waiting for %s\n", descr);
+ return 0; /* Return failure */
+ }
+ /* Acknowledge all events that we were waiting on */
+ hfa384x_setreg(hw, reg & ( event_mask | event_ack ), HFA384x_EVACK);
+ return reg;
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int prism2_poll(struct nic *nic, int retrieve)
+{
+ UINT16 reg;
+ UINT16 rxfid;
+ UINT16 result;
+ hfa384x_rx_frame_t rxdesc;
+ hfa384x_t *hw = &hw_global;
+
+ /* Check for received packet */
+ reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+ if ( ! HFA384x_EVSTAT_ISRX(reg) ) {
+ /* No packet received - return 0 */
+ return 0;
+ }
+
+ if ( ! retrieve ) return 1;
+
+ /* Acknowledge RX event */
+ hfa384x_setreg(hw, HFA384x_EVACK_RX_SET(1), HFA384x_EVACK);
+ /* Get RX FID */
+ rxfid = hfa384x_getreg(hw, HFA384x_RXFID);
+ /* Get the descriptor (including headers) */
+ result = hfa384x_copy_from_bap(hw, rxfid, 0, &rxdesc, sizeof(rxdesc));
+ if ( result ) {
+ return 0; /* fail */
+ }
+ /* Byte order convert once up front. */
+ rxdesc.status = hfa384x2host_16(rxdesc.status);
+ rxdesc.time = hfa384x2host_32(rxdesc.time);
+ rxdesc.data_len = hfa384x2host_16(rxdesc.data_len);
+
+ /* Fill in nic->packetlen */
+ nic->packetlen = rxdesc.data_len;
+ if ( nic->packetlen > 0 ) {
+ /* Fill in nic->packet */
+ /*
+ * NOTE: Packets as received have an 8-byte header (LLC+SNAP(?)) terminating with the packet type.
+ * Etherboot expects a 14-byte header terminating with the packet type (it ignores the rest of the
+ * header), so we use a quick hack to achieve this.
+ */
+ result = hfa384x_copy_from_bap(hw, rxfid, HFA384x_RX_DATA_OFF,
+ nic->packet + ETH_HLEN - sizeof(wlan_80211hdr_t), nic->packetlen);
+ if ( result ) {
+ return 0; /* fail */
+ }
+ }
+ return 1; /* Packet successfully received */
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void prism2_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ hfa384x_t *hw = &hw_global;
+ hfa384x_tx_frame_t txdesc;
+ wlan_80211hdr_t p80211hdr = { wlan_llc_snap, {{0,0,0},0} };
+ UINT16 fid;
+ UINT16 status;
+ int result;
+
+ // Request FID allocation
+ result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ALLOC), HFA384x_DRVR_TXBUF_MAX, 0, 0);
+ if (result != 0) {
+ printf("hfa384x: Tx FID allocate command failed: Aborting transmit..\n");
+ return;
+ }
+ if ( !hfa384x_wait_for_event(hw, HFA384x_EVSTAT_ALLOC, HFA384x_EVACK_INFO, 10, 50, "Tx FID to be allocated\n" ) ) return;
+ fid = hfa384x_getreg(hw, HFA384x_ALLOCFID);
+
+ /* Build Tx frame structure */
+ memset(&txdesc, 0, sizeof(txdesc));
+ txdesc.tx_control = host2hfa384x_16( HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) |
+ HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(1) );
+ txdesc.frame_control = host2ieee16( WLAN_SET_FC_FTYPE(WLAN_FTYPE_DATA) |
+ WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_DATAONLY) |
+ WLAN_SET_FC_TODS(1) );
+ memcpy(txdesc.address1, hw->bssid, WLAN_ADDR_LEN);
+ memcpy(txdesc.address2, nic->node_addr, WLAN_ADDR_LEN);
+ memcpy(txdesc.address3, d, WLAN_ADDR_LEN);
+ txdesc.data_len = host2hfa384x_16( sizeof(txdesc) + sizeof(p80211hdr) + s );
+ /* Set up SNAP header */
+ /* Let OUI default to RFC1042 (0x000000) */
+ p80211hdr.snap.type = htons(t);
+
+ /* Copy txdesc, p80211hdr and payload parts to FID */
+ result = hfa384x_copy_to_bap(hw, fid, 0, &txdesc, sizeof(txdesc));
+ if ( result ) return; /* fail */
+ result = hfa384x_copy_to_bap( hw, fid, sizeof(txdesc), &p80211hdr, sizeof(p80211hdr) );
+ if ( result ) return; /* fail */
+ result = hfa384x_copy_to_bap( hw, fid, sizeof(txdesc) + sizeof(p80211hdr), (UINT8*)p, s );
+ if ( result ) return; /* fail */
+
+ /* Issue Tx command */
+ result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_TX), fid, 0, 0);
+ if ( result != 0 ) {
+ printf("hfa384x: Transmit failed with result %#hx.\n", result);
+ return;
+ }
+
+ /* Wait for transmit completion (or exception) */
+ result = hfa384x_wait_for_event(hw, HFA384x_EVSTAT_TXEXC | HFA384x_EVSTAT_TX, HFA384x_EVACK_INFO,
+ 200, 500, "Tx to complete\n" );
+ if ( !result ) return; /* timeout failure */
+ if ( HFA384x_EVSTAT_ISTXEXC(result) ) {
+ fid = hfa384x_getreg(hw, HFA384x_TXCOMPLFID);
+ printf ( "Tx exception occurred with fid %#hx\n", fid );
+ result = hfa384x_copy_from_bap(hw, fid, 0, &status, sizeof(status));
+ if ( result ) return; /* fail */
+ printf("hfa384x: Tx error occurred (status %#hx):\n", status);
+ if ( HFA384x_TXSTATUS_ISACKERR(status) ) { printf(" ...acknowledgement error\n"); }
+ if ( HFA384x_TXSTATUS_ISFORMERR(status) ) { printf(" ...format error\n"); }
+ if ( HFA384x_TXSTATUS_ISDISCON(status) ) { printf(" ...disconnected error\n"); }
+ if ( HFA384x_TXSTATUS_ISAGEDERR(status) ) { printf(" ...AGED error\n"); }
+ if ( HFA384x_TXSTATUS_ISRETRYERR(status) ) { printf(" ...retry error\n"); }
+ return; /* fail */
+ }
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void prism2_disable ( struct nic *nic __unused ) {
+ /* put the card in its initial state */
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void prism2_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+Operations table
+***************************************************************************/
+static struct nic_operations prism2_operations = {
+ .connect = dummy_connect,
+ .poll = prism2_poll,
+ .transmit = prism2_transmit,
+ .irq = prism2_irq,
+};
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+You should omit the last argument struct pci_device * for a non-PCI NIC
+***************************************************************************/
+static int prism2_probe ( struct nic *nic, hfa384x_t *hw ) {
+ int result;
+ UINT16 tmp16 = 0;
+ UINT16 infofid;
+ hfa384x_InfFrame_t inf;
+ char ssid[HFA384x_RID_CNFDESIREDSSID_LEN];
+ int info_count = 0;
+
+ nic->irqno = 0;
+
+ /* Initialize card */
+ result = hfa384x_docmd_wait(hw, HFA384x_CMDCODE_INIT, 0,0,0); /* Send initialize command */
+ if ( result ) printf ( "Initialize command returned %#hx\n", result );
+ hfa384x_setreg(hw, 0, HFA384x_INTEN); /* Disable interrupts */
+ hfa384x_setreg(hw, 0xffff, HFA384x_EVACK); /* Acknowledge any spurious events */
+
+ DBG ( "MAC address %s\n", eth_ntoa ( nic->node_addr ) );
+
+ /* Retrieve MAC address (and fill out nic->node_addr) */
+ hfa384x_drvr_getconfig ( hw, HFA384x_RID_CNFOWNMACADDR, nic->node_addr, HFA384x_RID_CNFOWNMACADDR_LEN );
+
+ /* Prepare card for autojoin */
+ /* This procedure is reverse-engineered from a register-level trace of the Linux driver's join process */
+ tmp16 = WLAN_DATA_MAXLEN; /* Set maximum data length */
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFMAXDATALEN, &tmp16);
+ if ( result ) printf ( "Set Max Data Length command returned %#hx\n", result );
+ tmp16 = 0x000f; /* Set transmit rate(?) */
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_TXRATECNTL, &tmp16);
+ if ( result ) printf ( "Set Transmit Rate command returned %#hx\n", result );
+ tmp16 = HFA384x_CNFAUTHENTICATION_OPENSYSTEM; /* Set authentication type to OpenSystem */
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFAUTHENTICATION, &tmp16);
+ if ( result ) printf ( "Set Authentication Type command returned %#hx\n", result );
+ /* Set SSID */
+ memset(ssid, 0, HFA384x_RID_CNFDESIREDSSID_LEN);
+ for ( tmp16=0; tmp16<sizeof(hardcoded_ssid); tmp16++ ) { ssid[2+tmp16] = hardcoded_ssid[tmp16]; }
+ ssid[0] = sizeof(hardcoded_ssid) - 1; /* Ignore terminating zero */
+ result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFDESIREDSSID, ssid, HFA384x_RID_CNFDESIREDSSID_LEN); /* Set the SSID */
+ if ( result ) printf ( "Set SSID command returned %#hx\n", result );
+ tmp16 = 1; /* Set port type to ESS port */
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFPORTTYPE, &tmp16);
+ if ( result ) printf ( "Set port type command returned %#hx\n", result );
+ /* Enable card */
+ result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ENABLE) | HFA384x_CMD_MACPORT_SET(0), 0,0,0);
+ if ( result ) printf ( "Enable command returned %#hx\n", result );
+
+ do {
+ /* Increment info_count, abort if too many attempts.
+ * See comment next to definition of MAX_JOIN_INFO_COUNT for explanation.
+ */
+ info_count++;
+ if ( info_count > MAX_JOIN_INFO_COUNT ) {
+ printf ( "Too many failed attempts - aborting\n" );
+ return 0;
+ }
+
+ /* Wait for info frame to indicate link status */
+ if ( sizeof(hardcoded_ssid) == 1 ) {
+ /* Empty SSID => join to any SSID */
+ printf ( "Attempting to autojoin to any available access point (attempt %d)...", info_count );
+ } else {
+ printf ( "Attempting to autojoin to SSID %s (attempt %d)...", &ssid[2], info_count );
+ }
+
+ if ( !hfa384x_wait_for_event(hw, HFA384x_EVSTAT_INFO, 0, 1000, 2000, "Info event" ) ) return 0;
+ printf("done\n");
+ infofid = hfa384x_getreg(hw, HFA384x_INFOFID);
+ /* Retrieve the length */
+ result = hfa384x_copy_from_bap( hw, infofid, 0, &inf.framelen, sizeof(UINT16));
+ if ( result ) return 0; /* fail */
+ inf.framelen = hfa384x2host_16(inf.framelen);
+ /* Retrieve the rest */
+ result = hfa384x_copy_from_bap( hw, infofid, sizeof(UINT16),
+ &(inf.infotype), inf.framelen * sizeof(UINT16));
+ if ( result ) return 0; /* fail */
+ if ( inf.infotype != HFA384x_IT_LINKSTATUS ) {
+ /* Not a Link Status info frame: die */
+ printf ( "Unexpected info frame type %#hx (not LinkStatus type)\n", inf.infotype );
+ return 0;
+ }
+ inf.info.linkstatus.linkstatus = hfa384x2host_16(inf.info.linkstatus.linkstatus);
+ if ( inf.info.linkstatus.linkstatus != HFA384x_LINK_CONNECTED ) {
+ /* Link not connected - retry */
+ printf ( "Link not connected (status %#hx)\n", inf.info.linkstatus.linkstatus );
+ }
+ } while ( inf.info.linkstatus.linkstatus != HFA384x_LINK_CONNECTED );
+
+ /* Retrieve BSSID and print Connected message */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CURRENTBSSID, hw->bssid, WLAN_BSSID_LEN);
+
+ DBG ( "Link connected (BSSID %s - ", eth_ntoa ( hw->bssid ) );
+ DBG ( " MAC address %s)\n", eth_ntoa (nic->node_addr ) );
+
+ /* point to NIC specific routines */
+ nic->nic_op = &prism2_operations;
+ return 1;
+}
+
diff --git a/gpxe/src/drivers/net/prism2_pci.c b/gpxe/src/drivers/net/prism2_pci.c
new file mode 100644
index 00000000..63cc22e3
--- /dev/null
+++ b/gpxe/src/drivers/net/prism2_pci.c
@@ -0,0 +1,59 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Prism2 NIC driver for Etherboot
+Wrapper for prism2_pci
+
+Written by Michael Brown of Fen Systems Ltd
+$Id$
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#include <gpxe/pci.h>
+#include <nic.h>
+
+#define WLAN_HOSTIF WLAN_PCI
+#include "prism2.c"
+
+static int prism2_pci_probe ( struct nic *nic, struct pci_device *pci ) {
+ hfa384x_t *hw = &hw_global;
+ uint32_t membase = 0; /* Prism2.5 Memory Base */
+
+ pci_read_config_dword( pci, PRISM2_PCI_MEM_BASE, &membase);
+ membase &= PCI_BASE_ADDRESS_MEM_MASK;
+ hw->membase = (uint32_t) phys_to_virt(membase);
+ printf ( "Prism2.5 has registers at %#lx\n", hw->membase );
+
+ nic->ioaddr = hw->membase;
+ nic->irqno = 0;
+
+ return prism2_probe ( nic, hw );
+}
+
+static void prism2_pci_disable ( struct nic *nic ) {
+ prism2_disable ( nic );
+}
+
+static struct pci_device_id prism2_pci_nics[] = {
+PCI_ROM(0x1260, 0x3873, "prism2_pci", "Harris Semiconductor Prism2.5 clone"),
+PCI_ROM(0x1260, 0x3873, "hwp01170", "ActionTec HWP01170"),
+PCI_ROM(0x1260, 0x3873, "dwl520", "DLink DWL-520"),
+};
+
+PCI_DRIVER ( prism2_pci_driver, prism2_pci_nics, PCI_NO_CLASS );
+
+DRIVER ( "Prism2/PCI", nic_driver, pci_driver, prism2_pci_driver,
+ prism2_pci_probe, prism2_pci_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/prism2_plx.c b/gpxe/src/drivers/net/prism2_plx.c
new file mode 100644
index 00000000..150bbe69
--- /dev/null
+++ b/gpxe/src/drivers/net/prism2_plx.c
@@ -0,0 +1,121 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Prism2 NIC driver for Etherboot
+Wrapper for prism2_plx
+
+Written by Michael Brown of Fen Systems Ltd
+$Id$
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#include <gpxe/pci.h>
+#include <nic.h>
+
+#define WLAN_HOSTIF WLAN_PLX
+#include "prism2.c"
+
+/*
+ * Find PLX card. Prints out information strings from PCMCIA CIS as visual
+ * confirmation of presence of card.
+ *
+ * Arguments:
+ * hw device structure to be filled in
+ * p PCI device structure
+ *
+ * Returns:
+ * 1 Success
+ */
+static int prism2_find_plx ( hfa384x_t *hw, struct pci_device *p )
+{
+ int found = 0;
+ uint32_t plx_lcr = 0; /* PLX9052 Local Configuration Register Base (I/O) */
+ uint32_t attr_mem = 0; /* Prism2 Attribute Memory Base */
+ uint32_t iobase = 0; /* Prism2 I/O Base */
+ unsigned char *cis_tpl = NULL;
+ unsigned char *cis_string;
+
+ /* Obtain all memory and IO base addresses */
+ pci_read_config_dword( p, PLX_LOCAL_CONFIG_REGISTER_BASE, &plx_lcr);
+ plx_lcr &= PCI_BASE_ADDRESS_IO_MASK;
+ pci_read_config_dword( p, PRISM2_PLX_ATTR_MEM_BASE, &attr_mem);
+ pci_read_config_dword( p, PRISM2_PLX_IO_BASE, &iobase);
+ iobase &= PCI_BASE_ADDRESS_IO_MASK;
+
+ /* Fill out hw structure */
+ hw->membase = attr_mem;
+ hw->iobase = iobase;
+ printf ( "PLX9052 has local config registers at %#lx\n", plx_lcr );
+ printf ( "Prism2 has attribute memory at %#lx and I/O base at %#lx\n", attr_mem, iobase );
+
+ /* Search for CIS strings */
+ printf ( "Searching for PCMCIA card...\n" );
+ cis_tpl = bus_to_virt(attr_mem);
+ while ( *cis_tpl != CISTPL_END ) {
+ if ( *cis_tpl == CISTPL_VERS_1 ) {
+ /* CISTPL_VERS_1 contains some nice text strings */
+ printf ( "...found " );
+ found = 1;
+ cis_string = cis_tpl + CISTPL_VERS_1_STR_OFF;
+ while ( ! ( ( *cis_string == 0 ) && ( *(cis_string+CIS_STEP) == 0 ) ) ) {
+ printf ( "%c", *cis_string == 0 ? ' ' : *cis_string );
+ cis_string += CIS_STEP;
+ }
+ printf ( "\n" );
+ }
+ /* printf ( "CIS tuple type %#hhx, length %#hhx\n", *cis_tpl, *(cis_tpl+CISTPL_LEN_OFF) ); */
+ cis_tpl += CISTPL_HEADER_LEN + CIS_STEP * ( *(cis_tpl+CISTPL_LEN_OFF) );
+ }
+ if ( found == 0 ) {
+ printf ( "...nothing found\n" );
+ }
+ ((unsigned char *)bus_to_virt(attr_mem))[COR_OFFSET] = COR_VALUE; /* Write COR to enable PC card */
+ return found;
+}
+
+static int prism2_plx_probe ( struct nic *nic, struct pci_device *pci ) {
+ hfa384x_t *hw = &hw_global;
+
+ /* Find and intialise PLX Prism2 card */
+ if ( ! prism2_find_plx ( hw, pci ) ) return 0;
+ nic->ioaddr = hw->iobase;
+ nic->irqno = 0;
+ return prism2_probe ( nic, hw );
+}
+
+static void prism2_plx_disable ( struct nic *nic ) {
+ prism2_disable ( nic );
+}
+
+static struct pci_device_id prism2_plx_nics[] = {
+PCI_ROM(0x1385, 0x4100, "ma301", "Netgear MA301"),
+PCI_ROM(0x10b7, 0x7770, "3c-airconnect", "3Com AirConnect"),
+PCI_ROM(0x111a, 0x1023, "ss1023", "Siemens SpeedStream SS1023"),
+PCI_ROM(0x15e8, 0x0130, "correga", "Correga"),
+PCI_ROM(0x1638, 0x1100, "smc2602w", "SMC EZConnect SMC2602W"), /* or Eumitcom PCI WL11000, Addtron AWA-100 */
+PCI_ROM(0x16ab, 0x1100, "gl24110p", "Global Sun Tech GL24110P"),
+PCI_ROM(0x16ab, 0x1101, "16ab-1101", "Unknown"),
+PCI_ROM(0x16ab, 0x1102, "wdt11", "Linksys WDT11"),
+PCI_ROM(0x16ec, 0x3685, "usr2415", "USR 2415"),
+PCI_ROM(0xec80, 0xec00, "f5d6000", "Belkin F5D6000"),
+PCI_ROM(0x126c, 0x8030, "emobility", "Nortel emobility"),
+};
+
+PCI_DRIVER ( prism2_plx_driver, prism2_plx_nics, PCI_NO_CLASS );
+
+
+DRIVER ( "Prism2/PLX", nic_driver, pci_driver, prism2_plx_driver,
+ prism2_plx_probe, prism2_plx_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/r8169.c b/gpxe/src/drivers/net/r8169.c
new file mode 100644
index 00000000..ab9a30f8
--- /dev/null
+++ b/gpxe/src/drivers/net/r8169.c
@@ -0,0 +1,1184 @@
+/**************************************************************************
+* r8169.c: Etherboot device driver for the RealTek RTL-8169 Gigabit
+* Written 2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* r8169.c: A RealTek RTL-8169 Gigabit Ethernet driver
+* for Linux kernel 2.4.x.
+*
+* Written 2002 ShuChen <shuchen@realtek.com.tw>
+* See Linux Driver for full information
+*
+* Linux Driver Versions:
+* 1.27a, 10.02.2002
+* RTL8169_VERSION "2.2" <2004/08/09>
+*
+* Thanks to:
+* Jean Chen of RealTek Semiconductor Corp. for
+* providing the evaluation NIC used to develop
+* this driver. RealTek's support for Etherboot
+* is appreciated.
+*
+* REVISION HISTORY:
+* ================
+*
+* v1.0 11-26-2003 timlegge Initial port of Linux driver
+* v1.5 01-17-2004 timlegge Initial driver output cleanup
+* v1.6 03-27-2004 timlegge Additional Cleanup
+* v1.7 11-22-2005 timlegge Update to RealTek Driver Version 2.2
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/malloc.h>
+
+#define drv_version "v1.6"
+#define drv_date "03-27-2004"
+
+#define HZ 1000
+
+static u32 ioaddr;
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#undef RTL8169_DEBUG
+#undef RTL8169_JUMBO_FRAME_SUPPORT
+#undef RTL8169_HW_FLOW_CONTROL_SUPPORT
+
+
+#undef RTL8169_IOCTL_SUPPORT
+#undef RTL8169_DYNAMIC_CONTROL
+#define RTL8169_USE_IO
+
+
+/* media options
+ _10_Half = 0x01,
+ _10_Full = 0x02,
+ _100_Half = 0x04,
+ _100_Full = 0x08,
+ _1000_Full = 0x10,
+*/
+static int media = -1;
+
+#if 0
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
+#endif
+
+#if 0
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+ The RTL chips use a 64 element hash table based on the Ethernet CRC. */
+static int multicast_filter_limit = 32;
+#endif
+
+/* MAC address length*/
+#define MAC_ADDR_LEN 6
+
+/* max supported gigabit ethernet frame size -- must be at least (dev->mtu+14+4).*/
+#define MAX_ETH_FRAME_SIZE 1536
+
+#define TX_FIFO_THRESH 256 /* In bytes */
+
+#define RX_FIFO_THRESH 7 /* 7 means NO threshold, Rx buffer level before first PCI xfer. */
+#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */
+#define TX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */
+#define ETTh 0x3F /* 0x3F means NO threshold */
+
+#define EarlyTxThld 0x3F /* 0x3F means NO early transmit */
+#define RxPacketMaxSize 0x0800 /* Maximum size supported is 16K-1 */
+#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */
+
+#define NUM_TX_DESC 1 /* Number of Tx descriptor registers */
+#define NUM_RX_DESC 4 /* Number of Rx descriptor registers */
+#define RX_BUF_SIZE 1536 /* Rx Buffer size */
+
+#define RTL_MIN_IO_SIZE 0x80
+#define TX_TIMEOUT (6*HZ)
+
+#define RTL8169_TIMER_EXPIRE_TIME 100 //100
+
+#define ETH_HDR_LEN 14
+#define DEFAULT_MTU 1500
+#define DEFAULT_RX_BUF_LEN 1536
+
+
+#ifdef RTL8169_JUMBO_FRAME_SUPPORT
+#define MAX_JUMBO_FRAME_MTU ( 10000 )
+#define MAX_RX_SKBDATA_SIZE ( MAX_JUMBO_FRAME_MTU + ETH_HDR_LEN )
+#else
+#define MAX_RX_SKBDATA_SIZE 1600
+#endif //end #ifdef RTL8169_JUMBO_FRAME_SUPPORT
+
+#ifdef RTL8169_USE_IO
+#define RTL_W8(reg, val8) outb ((val8), ioaddr + (reg))
+#define RTL_W16(reg, val16) outw ((val16), ioaddr + (reg))
+#define RTL_W32(reg, val32) outl ((val32), ioaddr + (reg))
+#define RTL_R8(reg) inb (ioaddr + (reg))
+#define RTL_R16(reg) inw (ioaddr + (reg))
+#define RTL_R32(reg) ((unsigned long) inl (ioaddr + (reg)))
+#else
+/* write/read MMIO register */
+#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg))
+#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg))
+#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg))
+#define RTL_R8(reg) readb (ioaddr + (reg))
+#define RTL_R16(reg) readw (ioaddr + (reg))
+#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg)))
+#endif
+
+#define MCFG_METHOD_1 0x01
+#define MCFG_METHOD_2 0x02
+#define MCFG_METHOD_3 0x03
+#define MCFG_METHOD_4 0x04
+
+#define PCFG_METHOD_1 0x01 //PHY Reg 0x03 bit0-3 == 0x0000
+#define PCFG_METHOD_2 0x02 //PHY Reg 0x03 bit0-3 == 0x0001
+#define PCFG_METHOD_3 0x03 //PHY Reg 0x03 bit0-3 == 0x0002
+
+static struct {
+ const char *name;
+ u8 mcfg; /* depend on RTL8169 docs */
+ u32 RxConfigMask; /* should clear the bits supported by this chip */
+} rtl_chip_info[] = {
+ {
+ "RTL-8169", MCFG_METHOD_1, 0xff7e1880,}, {
+ "RTL8169s/8110s", MCFG_METHOD_2, 0xff7e1880}, {
+"RTL8169s/8110s", MCFG_METHOD_3, 0xff7e1880},};
+
+enum RTL8169_registers {
+ MAC0 = 0x0, /* Ethernet hardware address. */
+ MAR0 = 0x8, /* Multicast filter. */
+ TxDescStartAddr = 0x20,
+ TxHDescStartAddr = 0x28,
+ FLASH = 0x30,
+ ERSR = 0x36,
+ ChipCmd = 0x37,
+ TxPoll = 0x38,
+ IntrMask = 0x3C,
+ IntrStatus = 0x3E,
+ TxConfig = 0x40,
+ RxConfig = 0x44,
+ RxMissed = 0x4C,
+ Cfg9346 = 0x50,
+ Config0 = 0x51,
+ Config1 = 0x52,
+ Config2 = 0x53,
+ Config3 = 0x54,
+ Config4 = 0x55,
+ Config5 = 0x56,
+ MultiIntr = 0x5C,
+ PHYAR = 0x60,
+ TBICSR = 0x64,
+ TBI_ANAR = 0x68,
+ TBI_LPAR = 0x6A,
+ PHYstatus = 0x6C,
+ RxMaxSize = 0xDA,
+ CPlusCmd = 0xE0,
+ RxDescStartAddr = 0xE4,
+ ETThReg = 0xEC,
+ FuncEvent = 0xF0,
+ FuncEventMask = 0xF4,
+ FuncPresetState = 0xF8,
+ FuncForceEvent = 0xFC,
+};
+
+enum RTL8169_register_content {
+ /*InterruptStatusBits */
+ SYSErr = 0x8000,
+ PCSTimeout = 0x4000,
+ SWInt = 0x0100,
+ TxDescUnavail = 0x80,
+ RxFIFOOver = 0x40,
+ LinkChg = 0x20,
+ RxOverflow = 0x10,
+ TxErr = 0x08,
+ TxOK = 0x04,
+ RxErr = 0x02,
+ RxOK = 0x01,
+
+ /*RxStatusDesc */
+ RxRES = 0x00200000,
+ RxCRC = 0x00080000,
+ RxRUNT = 0x00100000,
+ RxRWT = 0x00400000,
+
+ /*ChipCmdBits */
+ CmdReset = 0x10,
+ CmdRxEnb = 0x08,
+ CmdTxEnb = 0x04,
+ RxBufEmpty = 0x01,
+
+ /*Cfg9346Bits */
+ Cfg9346_Lock = 0x00,
+ Cfg9346_Unlock = 0xC0,
+
+ /*rx_mode_bits */
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+ AcceptBroadcast = 0x08,
+ AcceptMulticast = 0x04,
+ AcceptMyPhys = 0x02,
+ AcceptAllPhys = 0x01,
+
+ /*RxConfigBits */
+ RxCfgFIFOShift = 13,
+ RxCfgDMAShift = 8,
+
+ /*TxConfigBits */
+ TxInterFrameGapShift = 24,
+ TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */
+
+ /*rtl8169_PHYstatus */
+ TBI_Enable = 0x80,
+ TxFlowCtrl = 0x40,
+ RxFlowCtrl = 0x20,
+ _1000bpsF = 0x10,
+ _100bps = 0x08,
+ _10bps = 0x04,
+ LinkStatus = 0x02,
+ FullDup = 0x01,
+
+ /*GIGABIT_PHY_registers */
+ PHY_CTRL_REG = 0,
+ PHY_STAT_REG = 1,
+ PHY_AUTO_NEGO_REG = 4,
+ PHY_1000_CTRL_REG = 9,
+
+ /*GIGABIT_PHY_REG_BIT */
+ PHY_Restart_Auto_Nego = 0x0200,
+ PHY_Enable_Auto_Nego = 0x1000,
+
+ /* PHY_STAT_REG = 1; */
+ PHY_Auto_Neco_Comp = 0x0020,
+
+ /* PHY_AUTO_NEGO_REG = 4; */
+ PHY_Cap_10_Half = 0x0020,
+ PHY_Cap_10_Full = 0x0040,
+ PHY_Cap_100_Half = 0x0080,
+ PHY_Cap_100_Full = 0x0100,
+
+ /* PHY_1000_CTRL_REG = 9; */
+ PHY_Cap_1000_Full = 0x0200,
+ PHY_Cap_1000_Half = 0x0100,
+
+ PHY_Cap_PAUSE = 0x0400,
+ PHY_Cap_ASYM_PAUSE = 0x0800,
+
+ PHY_Cap_Null = 0x0,
+
+ /*_MediaType*/
+ _10_Half = 0x01,
+ _10_Full = 0x02,
+ _100_Half = 0x04,
+ _100_Full = 0x08,
+ _1000_Full = 0x10,
+
+ /*_TBICSRBit*/
+ TBILinkOK = 0x02000000,
+};
+
+enum _DescStatusBit {
+ OWNbit = 0x80000000,
+ EORbit = 0x40000000,
+ FSbit = 0x20000000,
+ LSbit = 0x10000000,
+};
+
+struct TxDesc {
+ u32 status;
+ u32 vlan_tag;
+ u32 buf_addr;
+ u32 buf_Haddr;
+};
+
+struct RxDesc {
+ u32 status;
+ u32 vlan_tag;
+ u32 buf_addr;
+ u32 buf_Haddr;
+};
+
+/* The descriptors for this card are required to be aligned on 256
+ * byte boundaries. As the align attribute does not do more than 16
+ * bytes of alignment it requires some extra steps. Add 256 to the
+ * size of the array and the init_ring adjusts the alignment.
+ *
+ * UPDATE: This is no longer true; we can request arbitrary alignment.
+ */
+
+/* Define the TX and RX Descriptors and Buffers */
+#define __align_256 __attribute__ (( aligned ( 256 ) ))
+struct {
+ struct TxDesc tx_ring[NUM_TX_DESC] __align_256;
+ unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE];
+ struct RxDesc rx_ring[NUM_RX_DESC] __align_256;
+ unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE];
+} *r8169_bufs;
+#define tx_ring r8169_bufs->tx_ring
+#define rx_ring r8169_bufs->rx_ring
+#define txb r8169_bufs->txb
+#define rxb r8169_bufs->rxb
+
+static struct rtl8169_private {
+ void *mmio_addr; /* memory map physical address */
+ int chipset;
+ int pcfg;
+ int mcfg;
+ unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
+ unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
+ struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */
+ struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */
+ unsigned char *RxBufferRing[NUM_RX_DESC]; /* Index of Rx Buffer array */
+ unsigned char *Tx_skbuff[NUM_TX_DESC];
+} tpx;
+
+static struct rtl8169_private *tpc;
+
+static const u16 rtl8169_intr_mask =
+ LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK;
+static const unsigned int rtl8169_rx_config =
+ (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift) |
+ 0x0000000E;
+
+static void rtl8169_hw_PHY_config(struct nic *nic __unused);
+//static void rtl8169_hw_PHY_reset(struct net_device *dev);
+
+#define RTL8169_WRITE_GMII_REG_BIT( ioaddr, reg, bitnum, bitval )\
+{ \
+ int val; \
+ if( bitval == 1 ){ val = ( RTL8169_READ_GMII_REG( ioaddr, reg ) | (bitval<<bitnum) ) & 0xffff ; } \
+ else{ val = ( RTL8169_READ_GMII_REG( ioaddr, reg ) & (~(0x0001<<bitnum)) ) & 0xffff ; } \
+ RTL8169_WRITE_GMII_REG( ioaddr, reg, val ); \
+ }
+
+//=================================================================
+// PHYAR
+// bit Symbol
+// 31 Flag
+// 30-21 reserved
+// 20-16 5-bit GMII/MII register address
+// 15-0 16-bit GMII/MII register data
+//=================================================================
+static void RTL8169_WRITE_GMII_REG(unsigned long ioaddr, int RegAddr, int value)
+{
+ int i;
+
+ RTL_W32(PHYAR, 0x80000000 | (RegAddr & 0xFF) << 16 | value);
+ udelay(1000);
+
+ for (i = 2000; i > 0; i--) {
+ // Check if the RTL8169 has completed writing to the specified MII register
+ if (!(RTL_R32(PHYAR) & 0x80000000)) {
+ break;
+ } else {
+ udelay(100);
+ } // end of if( ! (RTL_R32(PHYAR)&0x80000000) )
+ } // end of for() loop
+}
+
+//=================================================================
+static int RTL8169_READ_GMII_REG(unsigned long ioaddr, int RegAddr)
+{
+ int i, value = -1;
+
+ RTL_W32(PHYAR, 0x0 | (RegAddr & 0xFF) << 16);
+ udelay(1000);
+
+ for (i = 2000; i > 0; i--) {
+ // Check if the RTL8169 has completed retrieving data from the specified MII register
+ if (RTL_R32(PHYAR) & 0x80000000) {
+ value = (int) (RTL_R32(PHYAR) & 0xFFFF);
+ break;
+ } else {
+ udelay(100);
+ } // end of if( RTL_R32(PHYAR) & 0x80000000 )
+ } // end of for() loop
+ return value;
+}
+
+
+#if 0
+static void mdio_write(int RegAddr, int value)
+{
+ int i;
+
+ RTL_W32(PHYAR, 0x80000000 | (RegAddr & 0xFF) << 16 | value);
+ udelay(1000);
+
+ for (i = 2000; i > 0; i--) {
+ /* Check if the RTL8169 has completed writing to the specified MII register */
+ if (!(RTL_R32(PHYAR) & 0x80000000)) {
+ break;
+ } else {
+ udelay(100);
+ }
+ }
+}
+
+static int mdio_read(int RegAddr)
+{
+ int i, value = -1;
+
+ RTL_W32(PHYAR, 0x0 | (RegAddr & 0xFF) << 16);
+ udelay(1000);
+
+ for (i = 2000; i > 0; i--) {
+ /* Check if the RTL8169 has completed retrieving data from the specified MII register */
+ if (RTL_R32(PHYAR) & 0x80000000) {
+ value = (int) (RTL_R32(PHYAR) & 0xFFFF);
+ break;
+ } else {
+ udelay(100);
+ }
+ }
+ return value;
+}
+#endif
+
+#define IORESOURCE_MEM 0x00000200
+
+static int rtl8169_init_board(struct pci_device *pdev)
+{
+ int i;
+// unsigned long mmio_end, mmio_flags
+ unsigned long mmio_start, mmio_len;
+
+ adjust_pci_device(pdev);
+
+ mmio_start = pci_bar_start(pdev, PCI_BASE_ADDRESS_1);
+// mmio_end = pci_resource_end (pdev, 1);
+// mmio_flags = pci_resource_flags (pdev, PCI_BASE_ADDRESS_1);
+ mmio_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_1);
+
+ // make sure PCI base addr 1 is MMIO
+// if (!(mmio_flags & IORESOURCE_MEM)) {
+// printf ("region #1 not an MMIO resource, aborting\n");
+// return 0;
+// }
+
+ // check for weird/broken PCI region reporting
+ if (mmio_len < RTL_MIN_IO_SIZE) {
+ printf("Invalid PCI region size(s), aborting\n");
+ return 0;
+ }
+#ifdef RTL8169_USE_IO
+ ioaddr = pci_bar_start(pdev, PCI_BASE_ADDRESS_0);
+#else
+ // ioremap MMIO region
+ ioaddr = (unsigned long) ioremap(mmio_start, mmio_len);
+ if (ioaddr == 0) {
+ printk("cannot remap MMIO, aborting\n");
+ return 0;
+ }
+#endif
+
+ tpc->mmio_addr = &ioaddr;
+ /* Soft reset the chip. */
+ RTL_W8(ChipCmd, CmdReset);
+
+ /* Check that the chip has finished the reset. */
+ for (i = 1000; i > 0; i--)
+ if ((RTL_R8(ChipCmd) & CmdReset) == 0)
+ break;
+ else
+ udelay(10);
+ // identify config method
+ {
+ unsigned long val32 = (RTL_R32(TxConfig) & 0x7c800000);
+ if (val32 == (0x1 << 28)) {
+ tpc->mcfg = MCFG_METHOD_4;
+ } else if (val32 == (0x1 << 26)) {
+ tpc->mcfg = MCFG_METHOD_3;
+ } else if (val32 == (0x1 << 23)) {
+ tpc->mcfg = MCFG_METHOD_2;
+ } else if (val32 == 0x00000000) {
+ tpc->mcfg = MCFG_METHOD_1;
+ } else {
+ tpc->mcfg = MCFG_METHOD_1;
+ }
+ }
+ {
+ unsigned char val8 =
+ (unsigned char) (RTL8169_READ_GMII_REG(ioaddr, 3) &
+ 0x000f);
+ if (val8 == 0x00) {
+ tpc->pcfg = PCFG_METHOD_1;
+ } else if (val8 == 0x01) {
+ tpc->pcfg = PCFG_METHOD_2;
+ } else if (val8 == 0x02) {
+ tpc->pcfg = PCFG_METHOD_3;
+ } else {
+ tpc->pcfg = PCFG_METHOD_3;
+ }
+ }
+
+ /* identify chip attached to board */
+
+ for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--)
+ if (tpc->mcfg == rtl_chip_info[i].mcfg) {
+ tpc->chipset = i;
+ goto match;
+ }
+ /* if unknown chip, assume array element #0, original RTL-8169 in this case */
+ DBG ( "PCI device: unknown chip version, assuming RTL-8169\n" );
+ DBG ( "PCI device: TxConfig = %#lX\n", ( unsigned long ) RTL_R32 ( TxConfig ) );
+
+ tpc->chipset = 0;
+ return 1;
+
+ match:
+ return 0;
+
+}
+
+/**************************************************************************
+IRQ - Wait for a frame
+***************************************************************************/
+static void r8169_irq(struct nic *nic __unused, irq_action_t action)
+{
+ int intr_status = 0;
+ int interested = RxOverflow | RxFIFOOver | RxErr | RxOK;
+
+ switch (action) {
+ case DISABLE:
+ case ENABLE:
+ intr_status = RTL_R16(IntrStatus);
+ /* h/w no longer present (hotplug?) or major error,
+ bail */
+ if (intr_status == 0xFFFF)
+ break;
+
+ intr_status = intr_status & ~interested;
+ if (action == ENABLE)
+ intr_status = intr_status | interested;
+ RTL_W16(IntrMask, intr_status);
+ break;
+ case FORCE:
+ RTL_W8(TxPoll, (RTL_R8(TxPoll) | 0x01));
+ break;
+ }
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int r8169_poll(struct nic *nic, int retreive)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+ int cur_rx;
+ unsigned int intr_status = 0;
+ cur_rx = tpc->cur_rx;
+ if ((tpc->RxDescArray[cur_rx].status & OWNbit) == 0) {
+ /* There is a packet ready */
+ if (!retreive)
+ return 1;
+ intr_status = RTL_R16(IntrStatus);
+ /* h/w no longer present (hotplug?) or major error,
+ bail */
+ if (intr_status == 0xFFFF)
+ return 0;
+ RTL_W16(IntrStatus, intr_status &
+ ~(RxFIFOOver | RxOverflow | RxOK));
+
+ if (!(tpc->RxDescArray[cur_rx].status & RxRES)) {
+ nic->packetlen = (int) (tpc->RxDescArray[cur_rx].
+ status & 0x00001FFF) - 4;
+ memcpy(nic->packet, tpc->RxBufferRing[cur_rx],
+ nic->packetlen);
+ if (cur_rx == NUM_RX_DESC - 1)
+ tpc->RxDescArray[cur_rx].status =
+ (OWNbit | EORbit) + RX_BUF_SIZE;
+ else
+ tpc->RxDescArray[cur_rx].status =
+ OWNbit + RX_BUF_SIZE;
+ tpc->RxDescArray[cur_rx].buf_addr =
+ virt_to_bus(tpc->RxBufferRing[cur_rx]);
+ } else
+ printf("Error Rx");
+ /* FIXME: shouldn't I reset the status on an error */
+ cur_rx = (cur_rx + 1) % NUM_RX_DESC;
+ tpc->cur_rx = cur_rx;
+ RTL_W16(IntrStatus, intr_status &
+ (RxFIFOOver | RxOverflow | RxOK));
+
+ return 1;
+
+ }
+ tpc->cur_rx = cur_rx;
+ /* FIXME: There is no reason to do this as cur_rx did not change */
+
+ return (0); /* initially as this is called to flush the input */
+
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void r8169_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ /* send the packet to destination */
+
+ u16 nstype;
+ u32 to;
+ u8 *ptxb;
+ int entry = tpc->cur_tx % NUM_TX_DESC;
+
+ /* point to the current txb incase multiple tx_rings are used */
+ ptxb = tpc->Tx_skbuff[entry * MAX_ETH_FRAME_SIZE];
+ memcpy(ptxb, d, ETH_ALEN);
+ memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) t);
+ memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2);
+ memcpy(ptxb + ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ s &= 0x0FFF;
+ while (s < ETH_ZLEN)
+ ptxb[s++] = '\0';
+
+ tpc->TxDescArray[entry].buf_addr = virt_to_bus(ptxb);
+ if (entry != (NUM_TX_DESC - 1))
+ tpc->TxDescArray[entry].status =
+ (OWNbit | FSbit | LSbit) | ((s > ETH_ZLEN) ? s :
+ ETH_ZLEN);
+ else
+ tpc->TxDescArray[entry].status =
+ (OWNbit | EORbit | FSbit | LSbit) | ((s > ETH_ZLEN) ? s
+ : ETH_ZLEN);
+ RTL_W8(TxPoll, 0x40); /* set polling bit */
+
+ tpc->cur_tx++;
+ to = currticks() + TX_TIMEOUT;
+ while ((tpc->TxDescArray[entry].status & OWNbit) && (currticks() < to)); /* wait */
+
+ if (currticks() >= to) {
+ printf("TX Time Out");
+ }
+}
+
+static void rtl8169_set_rx_mode(struct nic *nic __unused)
+{
+ u32 mc_filter[2]; /* Multicast hash filter */
+ int rx_mode;
+ u32 tmp = 0;
+
+ /* IFF_ALLMULTI */
+ /* Too many to filter perfectly -- accept all multicasts. */
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+
+ tmp =
+ rtl8169_rx_config | rx_mode | (RTL_R32(RxConfig) &
+ rtl_chip_info[tpc->chipset].
+ RxConfigMask);
+
+ RTL_W32(RxConfig, tmp);
+ RTL_W32(MAR0 + 0, mc_filter[0]);
+ RTL_W32(MAR0 + 4, mc_filter[1]);
+}
+static void rtl8169_hw_start(struct nic *nic)
+{
+ u32 i;
+
+ /* Soft reset the chip. */
+ RTL_W8(ChipCmd, CmdReset);
+
+ /* Check that the chip has finished the reset. */
+ for (i = 1000; i > 0; i--) {
+ if ((RTL_R8(ChipCmd) & CmdReset) == 0)
+ break;
+ else
+ udelay(10);
+ }
+
+ RTL_W8(Cfg9346, Cfg9346_Unlock);
+ RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+ RTL_W8(ETThReg, ETTh);
+
+ /* For gigabit rtl8169 */
+ RTL_W16(RxMaxSize, RxPacketMaxSize);
+
+ /* Set Rx Config register */
+ i = rtl8169_rx_config | (RTL_R32(RxConfig) &
+ rtl_chip_info[tpc->chipset].RxConfigMask);
+ RTL_W32(RxConfig, i);
+
+ /* Set DMA burst size and Interframe Gap Time */
+ RTL_W32(TxConfig,
+ (TX_DMA_BURST << TxDMAShift) | (InterFrameGap <<
+ TxInterFrameGapShift));
+
+
+ RTL_W16(CPlusCmd, RTL_R16(CPlusCmd));
+
+ if (tpc->mcfg == MCFG_METHOD_2 || tpc->mcfg == MCFG_METHOD_3) {
+ RTL_W16(CPlusCmd,
+ (RTL_R16(CPlusCmd) | (1 << 14) | (1 << 3)));
+ DBG
+ ("Set MAC Reg C+CR Offset 0xE0: bit-3 and bit-14\n");
+ } else {
+ RTL_W16(CPlusCmd, (RTL_R16(CPlusCmd) | (1 << 3)));
+ DBG("Set MAC Reg C+CR Offset 0xE0: bit-3.\n");
+ }
+
+ {
+ //RTL_W16(0xE2, 0x1517);
+ //RTL_W16(0xE2, 0x152a);
+ //RTL_W16(0xE2, 0x282a);
+ RTL_W16(0xE2, 0x0000);
+ }
+
+
+
+ tpc->cur_rx = 0;
+
+ RTL_W32(TxDescStartAddr, virt_to_le32desc(tpc->TxDescArray));
+ RTL_W32(RxDescStartAddr, virt_to_le32desc(tpc->RxDescArray));
+ RTL_W8(Cfg9346, Cfg9346_Lock);
+ udelay(10);
+
+ RTL_W32(RxMissed, 0);
+
+ rtl8169_set_rx_mode(nic);
+
+ /* no early-rx interrupts */
+ RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
+
+ RTL_W16(IntrMask, rtl8169_intr_mask);
+
+}
+
+static void rtl8169_init_ring(struct nic *nic __unused)
+{
+ int i;
+
+ tpc->cur_rx = 0;
+ tpc->cur_tx = 0;
+ memset(tpc->TxDescArray, 0x0, NUM_TX_DESC * sizeof(struct TxDesc));
+ memset(tpc->RxDescArray, 0x0, NUM_RX_DESC * sizeof(struct RxDesc));
+
+ for (i = 0; i < NUM_TX_DESC; i++) {
+ tpc->Tx_skbuff[i] = &txb[i];
+ }
+
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ if (i == (NUM_RX_DESC - 1))
+ tpc->RxDescArray[i].status =
+ (OWNbit | EORbit) | RX_BUF_SIZE;
+ else
+ tpc->RxDescArray[i].status = OWNbit | RX_BUF_SIZE;
+
+ tpc->RxBufferRing[i] = &rxb[i * RX_BUF_SIZE];
+ tpc->RxDescArray[i].buf_addr =
+ virt_to_bus(tpc->RxBufferRing[i]);
+ }
+}
+
+/**************************************************************************
+RESET - Finish setting up the ethernet interface
+***************************************************************************/
+static void r8169_reset(struct nic *nic)
+{
+ int i;
+
+ tpc->TxDescArray = tx_ring;
+ tpc->RxDescArray = rx_ring;
+
+ rtl8169_init_ring(nic);
+ rtl8169_hw_start(nic);
+ /* Construct a perfect filter frame with the mac address as first match
+ * and broadcast for all others */
+ for (i = 0; i < 192; i++)
+ txb[i] = 0xFF;
+
+ txb[0] = nic->node_addr[0];
+ txb[1] = nic->node_addr[1];
+ txb[2] = nic->node_addr[2];
+ txb[3] = nic->node_addr[3];
+ txb[4] = nic->node_addr[4];
+ txb[5] = nic->node_addr[5];
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void r8169_disable ( struct nic *nic __unused ) {
+ int i;
+ /* Stop the chip's Tx and Rx DMA processes. */
+ RTL_W8(ChipCmd, 0x00);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16(IntrMask, 0x0000);
+
+ RTL_W32(RxMissed, 0);
+
+ tpc->TxDescArray = NULL;
+ tpc->RxDescArray = NULL;
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ tpc->RxBufferRing[i] = NULL;
+ }
+}
+
+static struct nic_operations r8169_operations = {
+ .connect = dummy_connect,
+ .poll = r8169_poll,
+ .transmit = r8169_transmit,
+ .irq = r8169_irq,
+
+};
+
+static struct pci_device_id r8169_nics[] = {
+ PCI_ROM(0x10ec, 0x8169, "r8169", "RealTek RTL8169 Gigabit Ethernet"),
+ PCI_ROM(0x16ec, 0x0116, "usr-r8169", "US Robotics RTL8169 Gigabit Ethernet"),
+ PCI_ROM(0x1186, 0x4300, "dlink-r8169", "D-Link RTL8169 Gigabit Ethernet"),
+ PCI_ROM(0x1737, 0x1032, "linksys-r8169", "Linksys RTL8169 Gigabit Ethernet"),
+};
+
+PCI_DRIVER ( r8169_driver, r8169_nics, PCI_NO_CLASS );
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+#define board_found 1
+#define valid_link 0
+static int r8169_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ static int board_idx = -1;
+ static int printed_version = 0;
+ int i, rc;
+ int option = -1, Cap10_100 = 0, Cap1000 = 0;
+
+ printf ( "r8169.c: Found %s, Vendor=%hX Device=%hX\n",
+ pci->driver_name, pci->vendor, pci->device );
+
+ board_idx++;
+
+ printed_version = 1;
+
+ /* Quick and very dirty hack to get r8169 driver working
+ * again, pre-rewrite
+ */
+ if ( ! r8169_bufs )
+ r8169_bufs = malloc_dma ( sizeof ( *r8169_bufs ), 256 );
+ if ( ! r8169_bufs )
+ return 0;
+ memset ( r8169_bufs, 0, sizeof ( *r8169_bufs ) );
+
+ /* point to private storage */
+ tpc = &tpx;
+
+ rc = rtl8169_init_board(pci); /* Return code is meaningless */
+
+ /* Get MAC address. FIXME: read EEPROM */
+ for (i = 0; i < MAC_ADDR_LEN; i++)
+ nic->node_addr[i] = RTL_R8(MAC0 + i);
+
+ DBG ( "%s: Identified chip type is '%s'.\n", pci->driver_name,
+ rtl_chip_info[tpc->chipset].name );
+
+ /* Print out some hardware info */
+ DBG ( "%s: %s at IOAddr %#hX, ", pci->driver_name, eth_ntoa ( nic->node_addr ),
+ (unsigned int) ioaddr );
+
+ /* Config PHY */
+ rtl8169_hw_PHY_config(nic);
+
+ DBG("Set MAC Reg C+CR Offset 0x82h = 0x01h\n");
+ RTL_W8(0x82, 0x01);
+
+ if (tpc->mcfg < MCFG_METHOD_3) {
+ DBG("Set PCI Latency=0x40\n");
+ pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0x40);
+ }
+
+ if (tpc->mcfg == MCFG_METHOD_2) {
+ DBG("Set MAC Reg C+CR Offset 0x82h = 0x01h\n");
+ RTL_W8(0x82, 0x01);
+ DBG("Set PHY Reg 0x0bh = 0x00h\n");
+ RTL8169_WRITE_GMII_REG(ioaddr, 0x0b, 0x0000); //w 0x0b 15 0 0
+ }
+
+ /* if TBI is not endbled */
+ if (!(RTL_R8(PHYstatus) & TBI_Enable)) {
+ int val = RTL8169_READ_GMII_REG(ioaddr, PHY_AUTO_NEGO_REG);
+
+#ifdef RTL8169_HW_FLOW_CONTROL_SUPPORT
+ val |= PHY_Cap_PAUSE | PHY_Cap_ASYM_PAUSE;
+#endif //end #define RTL8169_HW_FLOW_CONTROL_SUPPORT
+
+ option = media;
+ /* Force RTL8169 in 10/100/1000 Full/Half mode. */
+ if (option > 0) {
+ printf(" Force-mode Enabled.\n");
+ Cap10_100 = 0, Cap1000 = 0;
+ switch (option) {
+ case _10_Half:
+ Cap10_100 = PHY_Cap_10_Half;
+ Cap1000 = PHY_Cap_Null;
+ break;
+ case _10_Full:
+ Cap10_100 = PHY_Cap_10_Full;
+ Cap1000 = PHY_Cap_Null;
+ break;
+ case _100_Half:
+ Cap10_100 = PHY_Cap_100_Half;
+ Cap1000 = PHY_Cap_Null;
+ break;
+ case _100_Full:
+ Cap10_100 = PHY_Cap_100_Full;
+ Cap1000 = PHY_Cap_Null;
+ break;
+ case _1000_Full:
+ Cap10_100 = PHY_Cap_Null;
+ Cap1000 = PHY_Cap_1000_Full;
+ break;
+ default:
+ break;
+ }
+ RTL8169_WRITE_GMII_REG(ioaddr, PHY_AUTO_NEGO_REG, Cap10_100 | (val & 0xC1F)); //leave PHY_AUTO_NEGO_REG bit4:0 unchanged
+ RTL8169_WRITE_GMII_REG(ioaddr, PHY_1000_CTRL_REG,
+ Cap1000);
+ } else {
+ DBG ( "%s: Auto-negotiation Enabled.\n", pci->driver_name );
+
+ // enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged
+ RTL8169_WRITE_GMII_REG(ioaddr, PHY_AUTO_NEGO_REG,
+ PHY_Cap_10_Half |
+ PHY_Cap_10_Full |
+ PHY_Cap_100_Half |
+ PHY_Cap_100_Full | (val &
+ 0xC1F));
+
+ // enable 1000 Full Mode
+// RTL8169_WRITE_GMII_REG( ioaddr, PHY_1000_CTRL_REG, PHY_Cap_1000_Full );
+ RTL8169_WRITE_GMII_REG(ioaddr, PHY_1000_CTRL_REG, PHY_Cap_1000_Full | PHY_Cap_1000_Half); //rtl8168
+
+ } // end of if( option > 0 )
+
+ // Enable auto-negotiation and restart auto-nigotiation
+ RTL8169_WRITE_GMII_REG(ioaddr, PHY_CTRL_REG,
+ PHY_Enable_Auto_Nego |
+ PHY_Restart_Auto_Nego);
+ udelay(100);
+
+ // wait for auto-negotiation process
+ for (i = 10000; i > 0; i--) {
+ //check if auto-negotiation complete
+ if (RTL8169_READ_GMII_REG(ioaddr, PHY_STAT_REG) &
+ PHY_Auto_Neco_Comp) {
+ udelay(100);
+ option = RTL_R8(PHYstatus);
+ if (option & _1000bpsF) {
+ printf
+ ("1000Mbps Full-duplex operation.\n");
+ } else {
+ printf
+ ("%sMbps %s-duplex operation.\n",
+ (option & _100bps) ? "100" :
+ "10",
+ (option & FullDup) ? "Full" :
+ "Half");
+ }
+ break;
+ } else {
+ udelay(100);
+ } // end of if( RTL8169_READ_GMII_REG(ioaddr, 1) & 0x20 )
+ } // end for-loop to wait for auto-negotiation process
+
+
+ } else {
+ udelay(100);
+ printf
+ ("%s: 1000Mbps Full-duplex operation, TBI Link %s!\n",
+ pci->driver_name,
+ (RTL_R32(TBICSR) & TBILinkOK) ? "OK" : "Failed");
+
+ }
+
+ r8169_reset(nic);
+
+ /* point to NIC specific routines */
+ nic->nic_op = &r8169_operations;
+
+ nic->irqno = pci->irq;
+ nic->ioaddr = ioaddr;
+
+ return 1;
+}
+
+//======================================================================================================
+/*
+static void rtl8169_hw_PHY_reset(struct nic *nic __unused)
+{
+ int val, phy_reset_expiretime = 50;
+ struct rtl8169_private *priv = dev->priv;
+ unsigned long ioaddr = priv->ioaddr;
+
+ DBG("%s: Reset RTL8169s PHY\n", dev->name);
+
+ val = ( RTL8169_READ_GMII_REG( ioaddr, 0 ) | 0x8000 ) & 0xffff;
+ RTL8169_WRITE_GMII_REG( ioaddr, 0, val );
+
+ do //waiting for phy reset
+ {
+ if( RTL8169_READ_GMII_REG( ioaddr, 0 ) & 0x8000 ){
+ phy_reset_expiretime --;
+ udelay(100);
+ }
+ else{
+ break;
+ }
+ }while( phy_reset_expiretime >= 0 );
+
+ assert( phy_reset_expiretime > 0 );
+}
+
+*/
+
+//======================================================================================================
+static void rtl8169_hw_PHY_config(struct nic *nic __unused)
+{
+
+ DBG("priv->mcfg=%d, priv->pcfg=%d\n", tpc->mcfg, tpc->pcfg);
+
+ if (tpc->mcfg == MCFG_METHOD_4) {
+/*
+ RTL8169_WRITE_GMII_REG( (unsigned long)ioaddr, 0x1F, 0x0001 );
+ RTL8169_WRITE_GMII_REG( (unsigned long)ioaddr, 0x1b, 0x841e );
+ RTL8169_WRITE_GMII_REG( (unsigned long)ioaddr, 0x0e, 0x7bfb );
+ RTL8169_WRITE_GMII_REG( (unsigned long)ioaddr, 0x09, 0x273a );
+*/
+
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x1F,
+ 0x0002);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x01,
+ 0x90D0);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x1F,
+ 0x0000);
+ } else if ((tpc->mcfg == MCFG_METHOD_2)
+ || (tpc->mcfg == MCFG_METHOD_3)) {
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x1F,
+ 0x0001);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x15,
+ 0x1000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x18,
+ 0x65C7);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0x0000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x03,
+ 0x00A1);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x02,
+ 0x0008);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x01,
+ 0x1020);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x00,
+ 0x1000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0x0800);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0x0000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0x7000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x03,
+ 0xFF41);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x02,
+ 0xDE60);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x01,
+ 0x0140);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x00,
+ 0x0077);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0x7800);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0x7000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0xA000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x03,
+ 0xDF01);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x02,
+ 0xDF20);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x01,
+ 0xFF95);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x00,
+ 0xFA00);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0xA800);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0xA000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0xB000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x03,
+ 0xFF41);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x02,
+ 0xDE20);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x01,
+ 0x0140);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x00,
+ 0x00BB);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0xB800);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0xB000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0xF000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x03,
+ 0xDF01);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x02,
+ 0xDF20);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x01,
+ 0xFF95);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x00,
+ 0xBF00);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0xF800);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0xF000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x04,
+ 0x0000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x1F,
+ 0x0000);
+ RTL8169_WRITE_GMII_REG((unsigned long) ioaddr, 0x0B,
+ 0x0000);
+ } else {
+ DBG("tpc->mcfg=%d. Discard hw PHY config.\n",
+ tpc->mcfg);
+ }
+}
+
+DRIVER ( "r8169/PCI", nic_driver, pci_driver, r8169_driver,
+ r8169_probe, r8169_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/rtl8139.c b/gpxe/src/drivers/net/rtl8139.c
new file mode 100644
index 00000000..c432884c
--- /dev/null
+++ b/gpxe/src/drivers/net/rtl8139.c
@@ -0,0 +1,583 @@
+/* rtl8139.c - etherboot driver for the Realtek 8139 chipset
+
+ ported from the linux driver written by Donald Becker
+ by Rainer Bawidamann (Rainer.Bawidamann@informatik.uni-ulm.de) 1999
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ changes to the original driver:
+ - removed support for interrupts, switching to polling mode (yuck!)
+ - removed support for the 8129 chip (external MII)
+
+*/
+
+/*********************************************************************/
+/* Revision History */
+/*********************************************************************/
+
+/*
+ 27 May 2006 mcb30@users.sourceforge.net (Michael Brown)
+ Rewrote to use the new net driver API, the updated PCI API, and
+ the generic three-wire serial device support for EEPROM access.
+
+ 28 Dec 2002 ken_yap@users.sourceforge.net (Ken Yap)
+ Put in virt_to_bus calls to allow Etherboot relocation.
+
+ 06 Apr 2001 ken_yap@users.sourceforge.net (Ken Yap)
+ Following email from Hyun-Joon Cha, added a disable routine, otherwise
+ NIC remains live and can crash the kernel later.
+
+ 4 Feb 2000 espenlaub@informatik.uni-ulm.de (Klaus Espenlaub)
+ Shuffled things around, removed the leftovers from the 8129 support
+ that was in the Linux driver and added a bit more 8139 definitions.
+ Moved the 8K receive buffer to a fixed, available address outside the
+ 0x98000-0x9ffff range. This is a bit of a hack, but currently the only
+ way to make room for the Etherboot features that need substantial amounts
+ of code like the ANSI console support. Currently the buffer is just below
+ 0x10000, so this even conforms to the tagged boot image specification,
+ which reserves the ranges 0x00000-0x10000 and 0x98000-0xA0000. My
+ interpretation of this "reserved" is that Etherboot may do whatever it
+ likes, as long as its environment is kept intact (like the BIOS
+ variables). Hopefully fixed rtl_poll() once and for all. The symptoms
+ were that if Etherboot was left at the boot menu for several minutes, the
+ first eth_poll failed. Seems like I am the only person who does this.
+ First of all I fixed the debugging code and then set out for a long bug
+ hunting session. It took me about a week full time work - poking around
+ various places in the driver, reading Don Becker's and Jeff Garzik's Linux
+ driver and even the FreeBSD driver (what a piece of crap!) - and
+ eventually spotted the nasty thing: the transmit routine was acknowledging
+ each and every interrupt pending, including the RxOverrun and RxFIFIOver
+ interrupts. This confused the RTL8139 thoroughly. It destroyed the
+ Rx ring contents by dumping the 2K FIFO contents right where we wanted to
+ get the next packet. Oh well, what fun.
+
+ 18 Jan 2000 mdc@etherboot.org (Marty Connor)
+ Drastically simplified error handling. Basically, if any error
+ in transmission or reception occurs, the card is reset.
+ Also, pointed all transmit descriptors to the same buffer to
+ save buffer space. This should decrease driver size and avoid
+ corruption because of exceeding 32K during runtime.
+
+ 28 Jul 1999 (Matthias Meixner - meixner@rbg.informatik.tu-darmstadt.de)
+ rtl_poll was quite broken: it used the RxOK interrupt flag instead
+ of the RxBufferEmpty flag which often resulted in very bad
+ transmission performace - below 1kBytes/s.
+
+*/
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <io.h>
+#include <errno.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <gpxe/pci.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/spi_bit.h>
+#include <gpxe/threewire.h>
+#include <gpxe/nvo.h>
+
+#define TX_RING_SIZE 4
+
+struct rtl8139_tx {
+ unsigned int next;
+ struct io_buffer *iobuf[TX_RING_SIZE];
+};
+
+struct rtl8139_rx {
+ void *ring;
+ unsigned int offset;
+};
+
+struct rtl8139_nic {
+ unsigned short ioaddr;
+ struct rtl8139_tx tx;
+ struct rtl8139_rx rx;
+ struct spi_bit_basher spibit;
+ struct spi_device eeprom;
+ struct nvo_block nvo;
+};
+
+/* Tuning Parameters */
+#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
+#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */
+#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */
+#define TX_DMA_BURST 4 /* Calculate as 16<<val. */
+#define TX_IPG 3 /* This is the only valid value */
+#define RX_BUF_LEN_IDX 0 /* 0, 1, 2 is allowed - 8,16,32K rx buffer */
+#define RX_BUF_LEN ( (8192 << RX_BUF_LEN_IDX) )
+#define RX_BUF_PAD 4
+
+/* Symbolic offsets to registers. */
+enum RTL8139_registers {
+ MAC0=0, /* Ethernet hardware address. */
+ MAR0=8, /* Multicast filter. */
+ TxStatus0=0x10, /* Transmit status (four 32bit registers). */
+ TxAddr0=0x20, /* Tx descriptors (also four 32bit). */
+ RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36,
+ ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A,
+ IntrMask=0x3C, IntrStatus=0x3E,
+ TxConfig=0x40, RxConfig=0x44,
+ Timer=0x48, /* general-purpose counter. */
+ RxMissed=0x4C, /* 24 bits valid, write clears. */
+ Cfg9346=0x50, Config0=0x51, Config1=0x52,
+ TimerIntrReg=0x54, /* intr if gp counter reaches this value */
+ MediaStatus=0x58,
+ Config3=0x59,
+ MultiIntr=0x5C,
+ RevisionID=0x5E, /* revision of the RTL8139 chip */
+ TxSummary=0x60,
+ MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68,
+ NWayExpansion=0x6A,
+ DisconnectCnt=0x6C, FalseCarrierCnt=0x6E,
+ NWayTestReg=0x70,
+ RxCnt=0x72, /* packet received counter */
+ CSCR=0x74, /* chip status and configuration register */
+ PhyParm1=0x78,TwisterParm=0x7c,PhyParm2=0x80, /* undocumented */
+ /* from 0x84 onwards are a number of power management/wakeup frame
+ * definitions we will probably never need to know about. */
+};
+
+enum RxEarlyStatusBits {
+ ERGood=0x08, ERBad=0x04, EROVW=0x02, EROK=0x01
+};
+
+enum ChipCmdBits {
+ CmdReset=0x10, CmdRxEnb=0x08, CmdTxEnb=0x04, RxBufEmpty=0x01, };
+
+enum IntrMaskBits {
+ SERR=0x8000, TimeOut=0x4000, LenChg=0x2000,
+ FOVW=0x40, PUN_LinkChg=0x20, RXOVW=0x10,
+ TER=0x08, TOK=0x04, RER=0x02, ROK=0x01
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatusBits {
+ PCIErr=0x8000, PCSTimeout=0x4000, CableLenChange= 0x2000,
+ RxFIFOOver=0x40, RxUnderrun=0x20, RxOverflow=0x10,
+ TxErr=0x08, TxOK=0x04, RxErr=0x02, RxOK=0x01,
+};
+enum TxStatusBits {
+ TxHostOwns=0x2000, TxUnderrun=0x4000, TxStatOK=0x8000,
+ TxOutOfWindow=0x20000000, TxAborted=0x40000000,
+ TxCarrierLost=0x80000000,
+};
+enum RxStatusBits {
+ RxMulticast=0x8000, RxPhysical=0x4000, RxBroadcast=0x2000,
+ RxBadSymbol=0x0020, RxRunt=0x0010, RxTooLong=0x0008, RxCRCErr=0x0004,
+ RxBadAlign=0x0002, RxStatusOK=0x0001,
+};
+
+enum MediaStatusBits {
+ MSRTxFlowEnable=0x80, MSRRxFlowEnable=0x40, MSRSpeed10=0x08,
+ MSRLinkFail=0x04, MSRRxPauseFlag=0x02, MSRTxPauseFlag=0x01,
+};
+
+enum MIIBMCRBits {
+ BMCRReset=0x8000, BMCRSpeed100=0x2000, BMCRNWayEnable=0x1000,
+ BMCRRestartNWay=0x0200, BMCRDuplex=0x0100,
+};
+
+enum CSCRBits {
+ CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800,
+ CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0,
+ CSCR_LinkDownCmd=0x0f3c0,
+};
+
+enum RxConfigBits {
+ RxCfgWrap=0x80,
+ Eeprom9356=0x40,
+ AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08,
+ AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01,
+};
+
+enum Config1Bits {
+ VPDEnable=0x02,
+};
+
+/* EEPROM access */
+#define EE_M1 0x80 /* Mode select bit 1 */
+#define EE_M0 0x40 /* Mode select bit 0 */
+#define EE_CS 0x08 /* EEPROM chip select */
+#define EE_SK 0x04 /* EEPROM shift clock */
+#define EE_DI 0x02 /* Data in */
+#define EE_DO 0x01 /* Data out */
+
+/* Offsets within EEPROM (these are word offsets) */
+#define EE_MAC 7
+
+static const uint8_t rtl_ee_bits[] = {
+ [SPI_BIT_SCLK] = EE_SK,
+ [SPI_BIT_MOSI] = EE_DI,
+ [SPI_BIT_MISO] = EE_DO,
+ [SPI_BIT_SS(0)] = ( EE_CS | EE_M1 ),
+};
+
+static int rtl_spi_read_bit ( struct bit_basher *basher,
+ unsigned int bit_id ) {
+ struct rtl8139_nic *rtl = container_of ( basher, struct rtl8139_nic,
+ spibit.basher );
+ uint8_t mask = rtl_ee_bits[bit_id];
+ uint8_t eereg;
+
+ eereg = inb ( rtl->ioaddr + Cfg9346 );
+ return ( eereg & mask );
+}
+
+static void rtl_spi_write_bit ( struct bit_basher *basher,
+ unsigned int bit_id, unsigned long data ) {
+ struct rtl8139_nic *rtl = container_of ( basher, struct rtl8139_nic,
+ spibit.basher );
+ uint8_t mask = rtl_ee_bits[bit_id];
+ uint8_t eereg;
+
+ eereg = inb ( rtl->ioaddr + Cfg9346 );
+ eereg &= ~mask;
+ eereg |= ( data & mask );
+ outb ( eereg, rtl->ioaddr + Cfg9346 );
+}
+
+static struct bit_basher_operations rtl_basher_ops = {
+ .read = rtl_spi_read_bit,
+ .write = rtl_spi_write_bit,
+};
+
+/** Portion of EEPROM available for non-volatile stored options
+ *
+ * We use offset 0x40 (i.e. address 0x20), length 0x40. This block is
+ * marked as VPD in the rtl8139 datasheets, so we use it only if we
+ * detect that the card is not supporting VPD.
+ */
+static struct nvo_fragment rtl_nvo_fragments[] = {
+ { 0x20, 0x40 },
+ { 0, 0 }
+};
+
+/**
+ * Set up for EEPROM access
+ *
+ * @v netdev Net device
+ */
+static void rtl_init_eeprom ( struct net_device *netdev ) {
+ struct rtl8139_nic *rtl = netdev->priv;
+ int ee9356;
+ int vpd;
+
+ /* Initialise three-wire bus */
+ rtl->spibit.basher.op = &rtl_basher_ops;
+ rtl->spibit.bus.mode = SPI_MODE_THREEWIRE;
+ init_spi_bit_basher ( &rtl->spibit );
+
+ /* Detect EEPROM type and initialise three-wire device */
+ ee9356 = ( inw ( rtl->ioaddr + RxConfig ) & Eeprom9356 );
+ if ( ee9356 ) {
+ DBG ( "EEPROM is an AT93C56\n" );
+ init_at93c56 ( &rtl->eeprom, 16 );
+ } else {
+ DBG ( "EEPROM is an AT93C46\n" );
+ init_at93c46 ( &rtl->eeprom, 16 );
+ }
+ rtl->eeprom.bus = &rtl->spibit.bus;
+
+ /* Initialise space for non-volatile options, if available */
+ vpd = ( inw ( rtl->ioaddr + Config1 ) & VPDEnable );
+ if ( vpd ) {
+ DBG ( "EEPROM in use for VPD; cannot use for options\n" );
+ } else {
+ nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, rtl_nvo_fragments,
+ &netdev->refcnt );
+ }
+}
+
+/**
+ * Reset NIC
+ *
+ * @v netdev Net device
+ *
+ * Issues a hardware reset and waits for the reset to complete.
+ */
+static void rtl_reset ( struct net_device *netdev ) {
+ struct rtl8139_nic *rtl = netdev->priv;
+
+ /* Reset chip */
+ outb ( CmdReset, rtl->ioaddr + ChipCmd );
+ mdelay ( 10 );
+ memset ( &rtl->tx, 0, sizeof ( rtl->tx ) );
+ rtl->rx.offset = 0;
+}
+
+/**
+ * Open NIC
+ *
+ * @v netdev Net device
+ * @ret rc Return status code
+ */
+static int rtl_open ( struct net_device *netdev ) {
+ struct rtl8139_nic *rtl = netdev->priv;
+ int i;
+
+ /* Program the MAC address */
+ for ( i = 0 ; i < ETH_ALEN ; i++ )
+ outb ( netdev->ll_addr[i], rtl->ioaddr + MAC0 + i );
+
+ /* Set up RX ring */
+ rtl->rx.ring = malloc ( RX_BUF_LEN + RX_BUF_PAD );
+ if ( ! rtl->rx.ring )
+ return -ENOMEM;
+ outl ( virt_to_bus ( rtl->rx.ring ), rtl->ioaddr + RxBuf );
+ DBG ( "RX ring at %lx\n", virt_to_bus ( rtl->rx.ring ) );
+
+ /* Enable TX and RX */
+ outb ( ( CmdRxEnb | CmdTxEnb ), rtl->ioaddr + ChipCmd );
+ outl ( ( ( RX_FIFO_THRESH << 13 ) | ( RX_BUF_LEN_IDX << 11 ) |
+ ( RX_DMA_BURST << 8 ) | AcceptBroadcast | AcceptMulticast |
+ AcceptMyPhys ), rtl->ioaddr + RxConfig );
+ outl ( 0xffffffffUL, rtl->ioaddr + MAR0 + 0 );
+ outl ( 0xffffffffUL, rtl->ioaddr + MAR0 + 4 );
+ outl ( ( ( TX_DMA_BURST << 8 ) | ( TX_IPG << 24 ) ),
+ rtl->ioaddr + TxConfig );
+
+ return 0;
+}
+
+/**
+ * Close NIC
+ *
+ * @v netdev Net device
+ */
+static void rtl_close ( struct net_device *netdev ) {
+ struct rtl8139_nic *rtl = netdev->priv;
+
+ /* Reset the hardware to disable everything in one go */
+ rtl_reset ( netdev );
+
+ /* Free RX ring */
+ free ( rtl->rx.ring );
+ rtl->rx.ring = NULL;
+}
+
+/**
+ * Transmit packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int rtl_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf ) {
+ struct rtl8139_nic *rtl = netdev->priv;
+
+ /* Check for space in TX ring */
+ if ( rtl->tx.iobuf[rtl->tx.next] != NULL ) {
+ DBG ( "TX overflow\n" );
+ return -ENOBUFS;
+ }
+
+ /* Pad and align packet */
+ iob_pad ( iobuf, ETH_ZLEN );
+
+ /* Add to TX ring */
+ DBG ( "TX id %d at %lx+%zx\n", rtl->tx.next,
+ virt_to_bus ( iobuf->data ), iob_len ( iobuf ) );
+ rtl->tx.iobuf[rtl->tx.next] = iobuf;
+ outl ( virt_to_bus ( iobuf->data ),
+ rtl->ioaddr + TxAddr0 + 4 * rtl->tx.next );
+ outl ( ( ( ( TX_FIFO_THRESH & 0x7e0 ) << 11 ) | iob_len ( iobuf ) ),
+ rtl->ioaddr + TxStatus0 + 4 * rtl->tx.next );
+ rtl->tx.next = ( rtl->tx.next + 1 ) % TX_RING_SIZE;
+
+ return 0;
+}
+
+/**
+ * Poll for received packets
+ *
+ * @v netdev Network device
+ */
+static void rtl_poll ( struct net_device *netdev ) {
+ struct rtl8139_nic *rtl = netdev->priv;
+ unsigned int status;
+ unsigned int tsad;
+ unsigned int rx_status;
+ unsigned int rx_len;
+ struct io_buffer *rx_iob;
+ int wrapped_len;
+ int i;
+
+ /* Acknowledge interrupts */
+ status = inw ( rtl->ioaddr + IntrStatus );
+ if ( ! status )
+ return;
+ outw ( status, rtl->ioaddr + IntrStatus );
+
+ /* Handle TX completions */
+ tsad = inw ( rtl->ioaddr + TxSummary );
+ for ( i = 0 ; i < TX_RING_SIZE ; i++ ) {
+ if ( ( rtl->tx.iobuf[i] != NULL ) && ( tsad & ( 1 << i ) ) ) {
+ DBG ( "TX id %d complete\n", i );
+ netdev_tx_complete ( netdev, rtl->tx.iobuf[i] );
+ rtl->tx.iobuf[i] = NULL;
+ }
+ }
+
+ /* Handle received packets */
+ while ( ! ( inw ( rtl->ioaddr + ChipCmd ) & RxBufEmpty ) ) {
+ rx_status = * ( ( uint16_t * )
+ ( rtl->rx.ring + rtl->rx.offset ) );
+ rx_len = * ( ( uint16_t * )
+ ( rtl->rx.ring + rtl->rx.offset + 2 ) );
+ if ( rx_status & RxOK ) {
+ DBG ( "RX packet at offset %x+%x\n", rtl->rx.offset,
+ rx_len );
+
+ rx_iob = alloc_iob ( rx_len );
+ if ( ! rx_iob ) {
+ netdev_rx_err ( netdev, NULL, -ENOMEM );
+ /* Leave packet for next call to poll() */
+ break;
+ }
+
+ wrapped_len = ( ( rtl->rx.offset + 4 + rx_len )
+ - RX_BUF_LEN );
+ if ( wrapped_len < 0 )
+ wrapped_len = 0;
+
+ memcpy ( iob_put ( rx_iob, rx_len - wrapped_len ),
+ rtl->rx.ring + rtl->rx.offset + 4,
+ rx_len - wrapped_len );
+ memcpy ( iob_put ( rx_iob, wrapped_len ),
+ rtl->rx.ring, wrapped_len );
+
+ netdev_rx ( netdev, rx_iob );
+ } else {
+ DBG ( "RX bad packet (status %#04x len %d)\n",
+ rx_status, rx_len );
+ netdev_rx_err ( netdev, NULL, -EINVAL );
+ }
+ rtl->rx.offset = ( ( ( rtl->rx.offset + 4 + rx_len + 3 ) & ~3 )
+ % RX_BUF_LEN );
+ outw ( rtl->rx.offset - 16, rtl->ioaddr + RxBufPtr );
+ }
+}
+
+/**
+ * Enable/disable interrupts
+ *
+ * @v netdev Network device
+ * @v enable Interrupts should be enabled
+ */
+static void rtl_irq ( struct net_device *netdev, int enable ) {
+ struct rtl8139_nic *rtl = netdev->priv;
+
+ outw ( ( enable ? ( ROK | RER | TOK | TER ) : 0 ),
+ rtl->ioaddr + IntrMask );
+}
+
+/** RTL8139 net device operations */
+static struct net_device_operations rtl_operations = {
+ .open = rtl_open,
+ .close = rtl_close,
+ .transmit = rtl_transmit,
+ .poll = rtl_poll,
+ .irq = rtl_irq,
+};
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @v id PCI ID
+ * @ret rc Return status code
+ */
+static int rtl_probe ( struct pci_device *pci,
+ const struct pci_device_id *id __unused ) {
+ struct net_device *netdev;
+ struct rtl8139_nic *rtl;
+ int rc;
+
+ /* Allocate net device */
+ netdev = alloc_etherdev ( sizeof ( *rtl ) );
+ if ( ! netdev )
+ return -ENOMEM;
+ netdev_init ( netdev, &rtl_operations );
+ rtl = netdev->priv;
+ pci_set_drvdata ( pci, netdev );
+ netdev->dev = &pci->dev;
+ memset ( rtl, 0, sizeof ( *rtl ) );
+ rtl->ioaddr = pci->ioaddr;
+
+ /* Fix up PCI device */
+ adjust_pci_device ( pci );
+
+ /* Reset the NIC, set up EEPROM access and read MAC address */
+ rtl_reset ( netdev );
+ rtl_init_eeprom ( netdev );
+ nvs_read ( &rtl->eeprom.nvs, EE_MAC, netdev->ll_addr, ETH_ALEN );
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register_netdev;
+
+ /* Register non-volatile storage */
+ if ( rtl->nvo.nvs ) {
+ if ( ( rc = register_nvo ( &rtl->nvo,
+ netdev_settings ( netdev ) ) ) != 0)
+ goto err_register_nvo;
+ }
+
+ return 0;
+
+ err_register_nvo:
+ unregister_netdev ( netdev );
+ err_register_netdev:
+ rtl_reset ( netdev );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void rtl_remove ( struct pci_device *pci ) {
+ struct net_device *netdev = pci_get_drvdata ( pci );
+ struct rtl8139_nic *rtl = netdev->priv;
+
+ if ( rtl->nvo.nvs )
+ unregister_nvo ( &rtl->nvo );
+ unregister_netdev ( netdev );
+ rtl_reset ( netdev );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+static struct pci_device_id rtl8139_nics[] = {
+PCI_ROM(0x10ec, 0x8129, "rtl8129", "Realtek 8129"),
+PCI_ROM(0x10ec, 0x8139, "rtl8139", "Realtek 8139"),
+PCI_ROM(0x10ec, 0x8138, "rtl8139b", "Realtek 8139B"),
+PCI_ROM(0x1186, 0x1300, "dfe538", "DFE530TX+/DFE538TX"),
+PCI_ROM(0x1113, 0x1211, "smc1211-1", "SMC EZ10/100"),
+PCI_ROM(0x1112, 0x1211, "smc1211", "SMC EZ10/100"),
+PCI_ROM(0x1500, 0x1360, "delta8139", "Delta Electronics 8139"),
+PCI_ROM(0x4033, 0x1360, "addtron8139", "Addtron Technology 8139"),
+PCI_ROM(0x1186, 0x1340, "dfe690txd", "D-Link DFE690TXD"),
+PCI_ROM(0x13d1, 0xab06, "fe2000vx", "AboCom FE2000VX"),
+PCI_ROM(0x1259, 0xa117, "allied8139", "Allied Telesyn 8139"),
+PCI_ROM(0x14ea, 0xab06, "fnw3603tx", "Planex FNW-3603-TX"),
+PCI_ROM(0x14ea, 0xab07, "fnw3800tx", "Planex FNW-3800-TX"),
+PCI_ROM(0xffff, 0x8139, "clone-rtl8139", "Cloned 8139"),
+};
+
+struct pci_driver rtl8139_driver __pci_driver = {
+ .ids = rtl8139_nics,
+ .id_count = ( sizeof ( rtl8139_nics ) / sizeof ( rtl8139_nics[0] ) ),
+ .probe = rtl_probe,
+ .remove = rtl_remove,
+};
diff --git a/gpxe/src/drivers/net/sis900.c b/gpxe/src/drivers/net/sis900.c
new file mode 100644
index 00000000..58526506
--- /dev/null
+++ b/gpxe/src/drivers/net/sis900.c
@@ -0,0 +1,1296 @@
+/* -*- Mode:C; c-basic-offset:4; -*- */
+
+/*
+ sis900.c: An SiS 900/7016 PCI Fast Ethernet driver for Etherboot
+ Copyright (C) 2001 Entity Cyber, Inc.
+
+ Revision: 1.0 March 1, 2001
+
+ Author: Marty Connor (mdc@etherboot.org)
+
+ Adapted from a Linux driver which was written by Donald Becker
+ and modified by Ollie Lho and Chin-Shan Li of SiS Corporation.
+ Rewritten for Etherboot by Marty Connor.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ References:
+ SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support,
+ preliminary Rev. 1.0 Jan. 14, 1998
+ SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support,
+ preliminary Rev. 1.0 Nov. 10, 1998
+ SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
+ preliminary Rev. 1.0 Jan. 18, 1998
+ http://www.sis.com.tw/support/databook.htm */
+
+/* Revision History */
+
+/*
+ 07 Dec 2003 timlegge - Enabled Multicast Support
+ 06 Dec 2003 timlegge - Fixed relocation issue in 5.2
+ 04 Jan 2002 Chien-Yu Chen, Doug Ambrisko, Marty Connor Patch to Etherboot 5.0.5
+ Added support for the SiS 630ET plus various bug fixes from linux kernel
+ source 2.4.17.
+ 01 March 2001 mdc 1.0
+ Initial Release. Tested with PCI based sis900 card and ThinkNIC
+ computer.
+ 20 March 2001 P.Koegel
+ added support for sis630e and PHY ICS1893 and RTL8201
+ Testet with SIS730S chipset + ICS1893
+*/
+
+
+/* Includes */
+
+#include "etherboot.h"
+#include <gpxe/pci.h>
+#include "nic.h"
+
+#include "sis900.h"
+
+/* Globals */
+
+static struct nic_operations sis900_operations;
+
+static int sis900_debug = 0;
+
+static unsigned short vendor, dev_id;
+static unsigned long ioaddr;
+static u8 pci_revision;
+
+static unsigned int cur_phy;
+
+static unsigned int cur_rx;
+
+struct {
+ BufferDesc txd;
+ BufferDesc rxd[NUM_RX_DESC];
+ unsigned char txb[TX_BUF_SIZE];
+ unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE];
+} sis900_bufs __shared;
+#define txd sis900_bufs.txd
+#define rxd sis900_bufs.rxd
+#define txb sis900_bufs.txb
+#define rxb sis900_bufs.rxb
+
+#if 0
+static struct mac_chip_info {
+ const char *name;
+ u16 vendor_id, device_id, flags;
+ int io_size;
+} mac_chip_table[] = {
+ { "SiS 900 PCI Fast Ethernet", PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS900,
+ PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE},
+ { "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS7016,
+ PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE},
+ {0,0,0,0,0} /* 0 terminated list. */
+};
+#endif
+
+static void sis900_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
+static void amd79c901_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
+static void ics1893_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
+static void rtl8201_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
+static void vt6103_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
+
+static struct mii_chip_info {
+ const char * name;
+ u16 phy_id0;
+ u16 phy_id1;
+ void (*read_mode) (struct nic *nic, int phy_addr, int *speed, int *duplex);
+} mii_chip_table[] = {
+ {"SiS 900 Internal MII PHY", 0x001d, 0x8000, sis900_read_mode},
+ {"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode},
+ {"SiS 900 on Foxconn 661 7MI", 0x0143, 0xBC70, sis900_read_mode},
+ {"AMD 79C901 10BASE-T PHY", 0x0000, 0x6B70, amd79c901_read_mode},
+ {"AMD 79C901 HomePNA PHY", 0x0000, 0x6B90, amd79c901_read_mode},
+ {"ICS 1893 Integrated PHYceiver" , 0x0015, 0xf440,ics1893_read_mode},
+// {"NS 83851 PHY",0x2000, 0x5C20, MIX },
+ {"RTL 8201 10/100Mbps Phyceiver" , 0x0000, 0x8200,rtl8201_read_mode},
+ {"VIA 6103 10/100Mbps Phyceiver", 0x0101, 0x8f20,vt6103_read_mode},
+ {0,0,0,0}
+};
+
+static struct mii_phy {
+ struct mii_phy * next;
+ struct mii_chip_info * chip_info;
+ int phy_addr;
+ u16 status;
+} mii;
+
+
+
+#if 0
+// PCI to ISA bridge for SIS640E access
+static struct pci_device_id pci_isa_bridge_list[] = {
+ { .vendor = 0x1039, .device = 0x0008,
+ .name = "SIS 85C503/5513 PCI to ISA bridge"},
+};
+
+PCI_DRIVER( sis_bridge_pci_driver, pci_isa_bridge_list, PCI_NO_CLASS );
+
+static struct device_driver sis_bridge_driver = {
+ .name = "SIS ISA bridge",
+ .bus_driver = &pci_driver,
+ .bus_driver_info = ( struct bus_driver_info * ) &sis_bridge_pci_driver,
+};
+#endif
+
+/* Function Prototypes */
+
+static int sis900_probe(struct nic *nic,struct pci_device *pci);
+
+static u16 sis900_read_eeprom(int location);
+static void sis900_mdio_reset(long mdio_addr);
+static void sis900_mdio_idle(long mdio_addr);
+static u16 sis900_mdio_read(int phy_id, int location);
+#if 0
+static void sis900_mdio_write(int phy_id, int location, int val);
+#endif
+static void sis900_init(struct nic *nic);
+
+static void sis900_reset(struct nic *nic);
+
+static void sis900_init_rxfilter(struct nic *nic);
+static void sis900_init_txd(struct nic *nic);
+static void sis900_init_rxd(struct nic *nic);
+static void sis900_set_rx_mode(struct nic *nic);
+static void sis900_check_mode(struct nic *nic);
+
+static void sis900_transmit(struct nic *nic, const char *d,
+ unsigned int t, unsigned int s, const char *p);
+static int sis900_poll(struct nic *nic, int retrieve);
+
+static void sis900_disable(struct nic *nic);
+
+static void sis900_irq(struct nic *nic, irq_action_t action);
+
+/**
+ * sis900_get_mac_addr: - Get MAC address for stand alone SiS900 model
+ * @pci_dev: the sis900 pci device
+ * @net_dev: the net device to get address for
+ *
+ * Older SiS900 and friends, use EEPROM to store MAC address.
+ * MAC address is read from read_eeprom() into @net_dev->dev_addr.
+ */
+
+static int sis900_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
+{
+ u16 signature;
+ int i;
+
+ /* check to see if we have sane EEPROM */
+ signature = (u16) sis900_read_eeprom( EEPROMSignature);
+ if (signature == 0xffff || signature == 0x0000) {
+ printf ("sis900_probe: Error EERPOM read %hX\n", signature);
+ return 0;
+ }
+
+ /* get MAC address from EEPROM */
+ for (i = 0; i < 3; i++)
+ ((u16 *)(nic->node_addr))[i] = sis900_read_eeprom(i+EEPROMMACAddr);
+ return 1;
+}
+
+/**
+ * sis96x_get_mac_addr: - Get MAC address for SiS962 or SiS963 model
+ * @pci_dev: the sis900 pci device
+ * @net_dev: the net device to get address for
+ *
+ * SiS962 or SiS963 model, use EEPROM to store MAC address. And EEPROM
+ * is shared by
+ * LAN and 1394. When access EEPROM, send EEREQ signal to hardware first
+ * and wait for EEGNT. If EEGNT is ON, EEPROM is permitted to be access
+ * by LAN, otherwise is not. After MAC address is read from EEPROM, send
+ * EEDONE signal to refuse EEPROM access by LAN.
+ * The EEPROM map of SiS962 or SiS963 is different to SiS900.
+ * The signature field in SiS962 or SiS963 spec is meaningless.
+ * MAC address is read into @net_dev->dev_addr.
+ */
+
+static int sis96x_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
+{
+/* long ioaddr = net_dev->base_addr; */
+ long ee_addr = ioaddr + mear;
+ u32 waittime = 0;
+ int i;
+
+ printf("Alternate function\n");
+
+ outl(EEREQ, ee_addr);
+ while(waittime < 2000) {
+ if(inl(ee_addr) & EEGNT) {
+
+ /* get MAC address from EEPROM */
+ for (i = 0; i < 3; i++)
+ ((u16 *)(nic->node_addr))[i] = sis900_read_eeprom(i+EEPROMMACAddr);
+
+ outl(EEDONE, ee_addr);
+ return 1;
+ } else {
+ udelay(1);
+ waittime ++;
+ }
+ }
+ outl(EEDONE, ee_addr);
+ return 0;
+}
+
+/**
+ * sis630e_get_mac_addr: - Get MAC address for SiS630E model
+ * @pci_dev: the sis900 pci device
+ * @net_dev: the net device to get address for
+ *
+ * SiS630E model, use APC CMOS RAM to store MAC address.
+ * APC CMOS RAM is accessed through ISA bridge.
+ * MAC address is read into @net_dev->dev_addr.
+ */
+
+static int sis630e_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
+{
+#if 0
+ u8 reg;
+ int i;
+ struct bus_loc bus_loc;
+ union {
+ struct bus_dev bus_dev;
+ struct pci_device isa_bridge;
+ } u;
+
+ /* find PCI to ISA bridge */
+ memset(&bus_loc, 0, sizeof(bus_loc));
+ if ( ! find_by_driver ( &bus_loc, &u.bus_dev, &sis_bridge_driver, 0 ) )
+ return 0;
+
+ pci_read_config_byte(&u.isa_bridge, 0x48, &reg);
+ pci_write_config_byte(&u.isa_bridge, 0x48, reg | 0x40);
+
+ for (i = 0; i < ETH_ALEN; i++)
+ {
+ outb(0x09 + i, 0x70);
+ ((u8 *)(nic->node_addr))[i] = inb(0x71);
+ }
+ pci_write_config_byte(&u.isa_bridge, 0x48, reg & ~0x40);
+
+ return 1;
+#endif
+
+ /* Does not work with current bus/device model */
+ memset ( nic->node_addr, 0, sizeof ( nic->node_addr ) );
+ return 0;
+}
+
+/**
+ * sis630e_get_mac_addr: - Get MAC address for SiS630E model
+ * @pci_dev: the sis900 pci device
+ * @net_dev: the net device to get address for
+ *
+ * SiS630E model, use APC CMOS RAM to store MAC address.
+ * APC CMOS RAM is accessed through ISA bridge.
+ * MAC address is read into @net_dev->dev_addr.
+ */
+
+static int sis635_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
+{
+ u32 rfcrSave;
+ u32 i;
+
+
+ rfcrSave = inl(rfcr + ioaddr);
+
+ outl(rfcrSave | RELOAD, ioaddr + cr);
+ outl(0, ioaddr + cr);
+
+ /* disable packet filtering before setting filter */
+ outl(rfcrSave & ~RFEN, rfcr + ioaddr);
+
+ /* load MAC addr to filter data register */
+ for (i = 0 ; i < 3 ; i++) {
+ outl((i << RFADDR_shift), ioaddr + rfcr);
+ *( ((u16 *)nic->node_addr) + i) = inw(ioaddr + rfdr);
+ }
+
+ /* enable packet filitering */
+ outl(rfcrSave | RFEN, rfcr + ioaddr);
+
+ return 1;
+}
+
+/*
+ * Function: sis900_probe
+ *
+ * Description: initializes initializes the NIC, retrieves the
+ * MAC address of the card, and sets up some globals required by
+ * other routines.
+ *
+ * Side effects:
+ * leaves the ioaddress of the sis900 chip in the variable ioaddr.
+ * leaves the sis900 initialized, and ready to recieve packets.
+ *
+ * Returns: struct nic *: pointer to NIC data structure
+ */
+
+static int sis900_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ int i;
+ int found=0;
+ int phy_addr;
+ u8 revision;
+ int ret;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr;
+
+ ioaddr = pci->ioaddr;
+ vendor = pci->vendor;
+ dev_id = pci->device;
+
+ /* wakeup chip */
+ pci_write_config_dword(pci, 0x40, 0x00000000);
+
+ adjust_pci_device(pci);
+
+ /* get MAC address */
+ ret = 0;
+ pci_read_config_byte(pci, PCI_REVISION, &revision);
+
+ /* save for use later in sis900_reset() */
+ pci_revision = revision;
+
+ if (revision == SIS630E_900_REV)
+ ret = sis630e_get_mac_addr(pci, nic);
+ else if ((revision > 0x81) && (revision <= 0x90))
+ ret = sis635_get_mac_addr(pci, nic);
+ else if (revision == SIS96x_900_REV)
+ ret = sis96x_get_mac_addr(pci, nic);
+ else
+ ret = sis900_get_mac_addr(pci, nic);
+
+ if (ret == 0)
+ {
+ printf ("sis900_probe: Error MAC address not found\n");
+ return 0;
+ }
+
+ /* 630ET : set the mii access mode as software-mode */
+ if (revision == SIS630ET_900_REV)
+ outl(ACCESSMODE | inl(ioaddr + cr), ioaddr + cr);
+
+ DBG( "sis900_probe: Vendor:%#hX Device:%#hX\n", vendor, dev_id );
+
+ /* probe for mii transceiver */
+ /* search for total of 32 possible mii phy addresses */
+
+ found = 0;
+ for (phy_addr = 0; phy_addr < 32; phy_addr++) {
+ u16 mii_status;
+ u16 phy_id0, phy_id1;
+
+ mii_status = sis900_mdio_read(phy_addr, MII_STATUS);
+ if (mii_status == 0xffff || mii_status == 0x0000)
+ /* the mii is not accessable, try next one */
+ continue;
+
+ phy_id0 = sis900_mdio_read(phy_addr, MII_PHY_ID0);
+ phy_id1 = sis900_mdio_read(phy_addr, MII_PHY_ID1);
+
+ /* search our mii table for the current mii */
+ for (i = 0; mii_chip_table[i].phy_id1; i++) {
+
+ if ((phy_id0 == mii_chip_table[i].phy_id0) &&
+ ((phy_id1 & 0xFFF0) == mii_chip_table[i].phy_id1)){
+
+ printf("sis900_probe: %s transceiver found at address %d.\n",
+ mii_chip_table[i].name, phy_addr);
+
+ mii.chip_info = &mii_chip_table[i];
+ mii.phy_addr = phy_addr;
+ mii.status = sis900_mdio_read(phy_addr, MII_STATUS);
+ mii.next = NULL;
+
+ found=1;
+ break;
+ }
+ }
+ }
+
+ if (found == 0) {
+ printf("sis900_probe: No MII transceivers found!\n");
+ return 0;
+ }
+
+ /* Arbitrarily select the last PHY found as current PHY */
+ cur_phy = mii.phy_addr;
+ printf("sis900_probe: Using %s as default\n", mii.chip_info->name);
+
+ /* initialize device */
+ sis900_init(nic);
+ nic->nic_op = &sis900_operations;
+
+ return 1;
+}
+
+
+
+
+/*
+ * EEPROM Routines: These functions read and write to EEPROM for
+ * retrieving the MAC address and other configuration information about
+ * the card.
+ */
+
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay() inl(ee_addr)
+
+
+/* Function: sis900_read_eeprom
+ *
+ * Description: reads and returns a given location from EEPROM
+ *
+ * Arguments: int location: requested EEPROM location
+ *
+ * Returns: u16: contents of requested EEPROM location
+ *
+ */
+
+/* Read Serial EEPROM through EEPROM Access Register, Note that location is
+ in word (16 bits) unit */
+static u16 sis900_read_eeprom(int location)
+{
+ int i;
+ u16 retval = 0;
+ long ee_addr = ioaddr + mear;
+ u32 read_cmd = location | EEread;
+
+ outl(0, ee_addr);
+ eeprom_delay();
+ outl(EECS, ee_addr);
+ eeprom_delay();
+
+ /* Shift the read command (9) bits out. */
+ for (i = 8; i >= 0; i--) {
+ u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS;
+ outl(dataval, ee_addr);
+ eeprom_delay();
+ outl(dataval | EECLK, ee_addr);
+ eeprom_delay();
+ }
+ outl(EECS, ee_addr);
+ eeprom_delay();
+
+ /* read the 16-bits data in */
+ for (i = 16; i > 0; i--) {
+ outl(EECS, ee_addr);
+ eeprom_delay();
+ outl(EECS | EECLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0);
+ eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ outl(0, ee_addr);
+ eeprom_delay();
+// outl(EECLK, ee_addr);
+
+ return (retval);
+}
+
+#define sis900_mdio_delay() inl(mdio_addr)
+
+
+/*
+ Read and write the MII management registers using software-generated
+ serial MDIO protocol. Note that the command bits and data bits are
+ send out seperately
+*/
+
+static void sis900_mdio_idle(long mdio_addr)
+{
+ outl(MDIO | MDDIR, mdio_addr);
+ sis900_mdio_delay();
+ outl(MDIO | MDDIR | MDC, mdio_addr);
+}
+
+/* Syncronize the MII management interface by shifting 32 one bits out. */
+static void sis900_mdio_reset(long mdio_addr)
+{
+ int i;
+
+ for (i = 31; i >= 0; i--) {
+ outl(MDDIR | MDIO, mdio_addr);
+ sis900_mdio_delay();
+ outl(MDDIR | MDIO | MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+ return;
+}
+
+static u16 sis900_mdio_read(int phy_id, int location)
+{
+ long mdio_addr = ioaddr + mear;
+ int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
+ u16 retval = 0;
+ int i;
+
+ sis900_mdio_reset(mdio_addr);
+ sis900_mdio_idle(mdio_addr);
+
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
+ outl(dataval, mdio_addr);
+ sis900_mdio_delay();
+ outl(dataval | MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+
+ /* Read the 16 data bits. */
+ for (i = 16; i > 0; i--) {
+ outl(0, mdio_addr);
+ sis900_mdio_delay();
+ retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0);
+ outl(MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+ outl(0x00, mdio_addr);
+ return retval;
+}
+
+#if 0
+static void sis900_mdio_write(int phy_id, int location, int value)
+{
+ long mdio_addr = ioaddr + mear;
+ int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
+ int i;
+
+ sis900_mdio_reset(mdio_addr);
+ sis900_mdio_idle(mdio_addr);
+
+ /* Shift the command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
+ outb(dataval, mdio_addr);
+ sis900_mdio_delay();
+ outb(dataval | MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+ sis900_mdio_delay();
+
+ /* Shift the value bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR;
+ outl(dataval, mdio_addr);
+ sis900_mdio_delay();
+ outl(dataval | MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+ sis900_mdio_delay();
+
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ outb(0, mdio_addr);
+ sis900_mdio_delay();
+ outb(MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+ outl(0x00, mdio_addr);
+ return;
+}
+#endif
+
+
+/* Function: sis900_init
+ *
+ * Description: resets the ethernet controller chip and various
+ * data structures required for sending and receiving packets.
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * returns: void.
+ */
+
+static void
+sis900_init(struct nic *nic)
+{
+ /* Soft reset the chip. */
+ sis900_reset(nic);
+
+ sis900_init_rxfilter(nic);
+
+ sis900_init_txd(nic);
+ sis900_init_rxd(nic);
+
+ sis900_set_rx_mode(nic);
+
+ sis900_check_mode(nic);
+
+ outl(RxENA| inl(ioaddr + cr), ioaddr + cr);
+}
+
+
+/*
+ * Function: sis900_reset
+ *
+ * Description: disables interrupts and soft resets the controller chip
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_reset(struct nic *nic __unused)
+{
+ int i = 0;
+ u32 status = TxRCMP | RxRCMP;
+
+ outl(0, ioaddr + ier);
+ outl(0, ioaddr + imr);
+ outl(0, ioaddr + rfcr);
+
+ outl(RxRESET | TxRESET | RESET | inl(ioaddr + cr), ioaddr + cr);
+
+ /* Check that the chip has finished the reset. */
+ while (status && (i++ < 1000)) {
+ status ^= (inl(isr + ioaddr) & status);
+ }
+
+ if( (pci_revision >= SIS635A_900_REV) || (pci_revision == SIS900B_900_REV) )
+ outl(PESEL | RND_CNT, ioaddr + cfg);
+ else
+ outl(PESEL, ioaddr + cfg);
+}
+
+
+/* Function: sis_init_rxfilter
+ *
+ * Description: sets receive filter address to our MAC address
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * returns: void.
+ */
+
+static void
+sis900_init_rxfilter(struct nic *nic)
+{
+ u32 rfcrSave;
+ int i;
+
+ rfcrSave = inl(rfcr + ioaddr);
+
+ /* disable packet filtering before setting filter */
+ outl(rfcrSave & ~RFEN, rfcr + ioaddr);
+
+ /* load MAC addr to filter data register */
+ for (i = 0 ; i < 3 ; i++) {
+ u32 w;
+
+ w = (u32) *((u16 *)(nic->node_addr)+i);
+ outl((i << RFADDR_shift), ioaddr + rfcr);
+ outl(w, ioaddr + rfdr);
+
+ if (sis900_debug > 0)
+ printf("sis900_init_rxfilter: Receive Filter Addrss[%d]=%lX\n",
+ i, inl(ioaddr + rfdr));
+ }
+
+ /* enable packet filitering */
+ outl(rfcrSave | RFEN, rfcr + ioaddr);
+}
+
+
+/*
+ * Function: sis_init_txd
+ *
+ * Description: initializes the Tx descriptor
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * returns: void.
+ */
+
+static void
+sis900_init_txd(struct nic *nic __unused)
+{
+ txd.link = (u32) 0;
+ txd.cmdsts = (u32) 0;
+ txd.bufptr = virt_to_bus(&txb[0]);
+
+ /* load Transmit Descriptor Register */
+ outl(virt_to_bus(&txd), ioaddr + txdp);
+ if (sis900_debug > 0)
+ printf("sis900_init_txd: TX descriptor register loaded with: %lX\n",
+ inl(ioaddr + txdp));
+}
+
+
+/* Function: sis_init_rxd
+ *
+ * Description: initializes the Rx descriptor ring
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_init_rxd(struct nic *nic __unused)
+{
+ int i;
+
+ cur_rx = 0;
+
+ /* init RX descriptor */
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ rxd[i].link = virt_to_bus((i+1 < NUM_RX_DESC) ? &rxd[i+1] : &rxd[0]);
+ rxd[i].cmdsts = (u32) RX_BUF_SIZE;
+ rxd[i].bufptr = virt_to_bus(&rxb[i*RX_BUF_SIZE]);
+ if (sis900_debug > 0)
+ printf("sis900_init_rxd: rxd[%d]=%p link=%X cmdsts=%X bufptr=%X\n",
+ i, &rxd[i], (unsigned int) rxd[i].link, (unsigned int) rxd[i].cmdsts,
+ (unsigned int) rxd[i].bufptr);
+ }
+
+ /* load Receive Descriptor Register */
+ outl(virt_to_bus(&rxd[0]), ioaddr + rxdp);
+
+ if (sis900_debug > 0)
+ printf("sis900_init_rxd: RX descriptor register loaded with: %lX\n",
+ inl(ioaddr + rxdp));
+
+}
+
+
+/* Function: sis_init_rxd
+ *
+ * Description:
+ * sets the receive mode to accept all broadcast packets and packets
+ * with our MAC address, and reject all multicast packets.
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void sis900_set_rx_mode(struct nic *nic __unused)
+{
+ int i, table_entries;
+ u32 rx_mode;
+ u16 mc_filter[16] = {0}; /* 256/128 bits multicast hash table */
+
+ if((pci_revision == SIS635A_900_REV) || (pci_revision == SIS900B_900_REV))
+ table_entries = 16;
+ else
+ table_entries = 8;
+
+ /* accept all multicast packet */
+ rx_mode = RFAAB | RFAAM;
+ for (i = 0; i < table_entries; i++)
+ mc_filter[i] = 0xffff;
+
+ /* update Multicast Hash Table in Receive Filter */
+ for (i = 0; i < table_entries; i++) {
+ /* why plus 0x04? That makes the correct value for hash table. */
+ outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
+ outl(mc_filter[i], ioaddr + rfdr);
+ }
+
+ /* Accept Broadcast and multicast packets, destination addresses that match
+ our MAC address */
+ outl(RFEN | rx_mode, ioaddr + rfcr);
+
+ return;
+}
+
+
+/* Function: sis900_check_mode
+ *
+ * Description: checks the state of transmit and receive
+ * parameters on the NIC, and updates NIC registers to match
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_check_mode(struct nic *nic)
+{
+ int speed, duplex;
+ u32 tx_flags = 0, rx_flags = 0;
+
+ mii.chip_info->read_mode(nic, cur_phy, &speed, &duplex);
+
+ if( inl(ioaddr + cfg) & EDB_MASTER_EN ) {
+ tx_flags = TxATP | (DMA_BURST_64 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
+ rx_flags = DMA_BURST_64 << RxMXDMA_shift;
+ }
+ else {
+ tx_flags = TxATP | (DMA_BURST_512 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
+ rx_flags = DMA_BURST_512 << RxMXDMA_shift;
+ }
+
+ if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS) {
+ rx_flags |= (RxDRNT_10 << RxDRNT_shift);
+ tx_flags |= (TxDRNT_10 << TxDRNT_shift);
+ }
+ else {
+ rx_flags |= (RxDRNT_100 << RxDRNT_shift);
+ tx_flags |= (TxDRNT_100 << TxDRNT_shift);
+ }
+
+ if (duplex == FDX_CAPABLE_FULL_SELECTED) {
+ tx_flags |= (TxCSI | TxHBI);
+ rx_flags |= RxATX;
+ }
+
+ outl (tx_flags, ioaddr + txcfg);
+ outl (rx_flags, ioaddr + rxcfg);
+}
+
+
+/* Function: sis900_read_mode
+ *
+ * Description: retrieves and displays speed and duplex
+ * parameters from the NIC
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex)
+{
+ int i = 0;
+ u32 status;
+ u16 phy_id0, phy_id1;
+
+ /* STSOUT register is Latched on Transition, read operation updates it */
+ do {
+ status = sis900_mdio_read(phy_addr, MII_STSOUT);
+ } while (i++ < 2);
+
+ *speed = HW_SPEED_10_MBPS;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+
+ if (status & (MII_NWAY_TX | MII_NWAY_TX_FDX))
+ *speed = HW_SPEED_100_MBPS;
+ if (status & ( MII_NWAY_TX_FDX | MII_NWAY_T_FDX))
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+
+ /* Workaround for Realtek RTL8201 PHY issue */
+ phy_id0 = sis900_mdio_read(phy_addr, MII_PHY_ID0);
+ phy_id1 = sis900_mdio_read(phy_addr, MII_PHY_ID1);
+ if((phy_id0 == 0x0000) && ((phy_id1 & 0xFFF0) == 0x8200)){
+ if(sis900_mdio_read(phy_addr, MII_CONTROL) & MII_CNTL_FDX)
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ if(sis900_mdio_read(phy_addr, 0x0019) & 0x01)
+ *speed = HW_SPEED_100_MBPS;
+ }
+
+ if (status & MII_STSOUT_LINK_FAIL)
+ printf("sis900_read_mode: Media Link Off\n");
+ else
+ printf("sis900_read_mode: Media Link On %s %s-duplex \n",
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+}
+
+
+/* Function: amd79c901_read_mode
+ *
+ * Description: retrieves and displays speed and duplex
+ * parameters from the NIC
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+amd79c901_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex)
+{
+ int i;
+ u16 status;
+
+ for (i = 0; i < 2; i++)
+ status = sis900_mdio_read(phy_addr, MII_STATUS);
+
+ if (status & MII_STAT_CAN_AUTO) {
+ /* 10BASE-T PHY */
+ for (i = 0; i < 2; i++)
+ status = sis900_mdio_read(phy_addr, MII_STATUS_SUMMARY);
+ if (status & MII_STSSUM_SPD)
+ *speed = HW_SPEED_100_MBPS;
+ else
+ *speed = HW_SPEED_10_MBPS;
+ if (status & MII_STSSUM_DPLX)
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ else
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+
+ if (status & MII_STSSUM_LINK)
+ printf("amd79c901_read_mode: Media Link On %s %s-duplex \n",
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+ else
+ printf("amd79c901_read_mode: Media Link Off\n");
+ }
+ else {
+ /* HomePNA */
+ *speed = HW_SPEED_HOME;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+ if (status & MII_STAT_LINK)
+ printf("amd79c901_read_mode:Media Link On 1mbps half-duplex \n");
+ else
+ printf("amd79c901_read_mode: Media Link Off\n");
+ }
+}
+
+
+/**
+ * ics1893_read_mode: - read media mode for ICS1893 PHY
+ * @net_dev: the net device to read mode for
+ * @phy_addr: mii phy address
+ * @speed: the transmit speed to be determined
+ * @duplex: the duplex mode to be determined
+ *
+ * ICS1893 PHY use Quick Poll Detailed Status register
+ * to determine the speed and duplex mode for sis900
+ */
+
+static void ics1893_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex)
+{
+ int i = 0;
+ u32 status;
+
+ /* MII_QPDSTS is Latched, read twice in succession will reflect the current state */
+ for (i = 0; i < 2; i++)
+ status = sis900_mdio_read(phy_addr, MII_QPDSTS);
+
+ if (status & MII_STSICS_SPD)
+ *speed = HW_SPEED_100_MBPS;
+ else
+ *speed = HW_SPEED_10_MBPS;
+
+ if (status & MII_STSICS_DPLX)
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ else
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+
+ if (status & MII_STSICS_LINKSTS)
+ printf("ics1893_read_mode: Media Link On %s %s-duplex \n",
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+ else
+ printf("ics1893_read_mode: Media Link Off\n");
+}
+
+/**
+ * rtl8201_read_mode: - read media mode for rtl8201 phy
+ * @nic: the net device to read mode for
+ * @phy_addr: mii phy address
+ * @speed: the transmit speed to be determined
+ * @duplex: the duplex mode to be determined
+ *
+ * read MII_STATUS register from rtl8201 phy
+ * to determine the speed and duplex mode for sis900
+ */
+
+static void rtl8201_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex)
+{
+ u32 status;
+
+ status = sis900_mdio_read(phy_addr, MII_STATUS);
+
+ if (status & MII_STAT_CAN_TX_FDX) {
+ *speed = HW_SPEED_100_MBPS;
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_TX) {
+ *speed = HW_SPEED_100_MBPS;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_T_FDX) {
+ *speed = HW_SPEED_10_MBPS;
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_T) {
+ *speed = HW_SPEED_10_MBPS;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+ }
+
+ if (status & MII_STAT_LINK)
+ printf("rtl8201_read_mode: Media Link On %s %s-duplex \n",
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+ else
+ printf("rtl8201_read_config_mode: Media Link Off\n");
+}
+
+/**
+ * vt6103_read_mode: - read media mode for vt6103 phy
+ * @nic: the net device to read mode for
+ * @phy_addr: mii phy address
+ * @speed: the transmit speed to be determined
+ * @duplex: the duplex mode to be determined
+ *
+ * read MII_STATUS register from rtl8201 phy
+ * to determine the speed and duplex mode for sis900
+ */
+
+static void vt6103_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex)
+{
+ u32 status;
+
+ status = sis900_mdio_read(phy_addr, MII_STATUS);
+
+ if (status & MII_STAT_CAN_TX_FDX) {
+ *speed = HW_SPEED_100_MBPS;
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_TX) {
+ *speed = HW_SPEED_100_MBPS;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_T_FDX) {
+ *speed = HW_SPEED_10_MBPS;
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_T) {
+ *speed = HW_SPEED_10_MBPS;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+ }
+
+ if (status & MII_STAT_LINK)
+ printf("vt6103_read_mode: Media Link On %s %s-duplex \n",
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+ else
+ printf("vt6103_read_config_mode: Media Link Off\n");
+}
+
+/* Function: sis900_transmit
+ *
+ * Description: transmits a packet and waits for completion or timeout.
+ *
+ * Arguments: char d[6]: destination ethernet address.
+ * unsigned short t: ethernet protocol type.
+ * unsigned short s: size of the data-part of the packet.
+ * char *p: the data for the packet.
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_transmit(struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ u32 to, nstype;
+ volatile u32 tx_status;
+
+ /* Stop the transmitter */
+ outl(TxDIS | inl(ioaddr + cr), ioaddr + cr);
+
+ /* load Transmit Descriptor Register */
+ outl(virt_to_bus(&txd), ioaddr + txdp);
+ if (sis900_debug > 1)
+ printf("sis900_transmit: TX descriptor register loaded with: %lX\n",
+ inl(ioaddr + txdp));
+
+ memcpy(txb, d, ETH_ALEN);
+ memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons(t);
+ memcpy(txb + 2 * ETH_ALEN, (char*)&nstype, 2);
+ memcpy(txb + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ s &= DSIZE;
+
+ if (sis900_debug > 1)
+ printf("sis900_transmit: sending %d bytes ethtype %hX\n", (int) s, t);
+
+ /* pad to minimum packet size */
+ while (s < ETH_ZLEN)
+ txb[s++] = '\0';
+
+ /* set the transmit buffer descriptor and enable Transmit State Machine */
+ txd.bufptr = virt_to_bus(&txb[0]);
+ txd.cmdsts = (u32) OWN | s;
+
+ /* restart the transmitter */
+ outl(TxENA | inl(ioaddr + cr), ioaddr + cr);
+
+ if (sis900_debug > 1)
+ printf("sis900_transmit: Queued Tx packet size %d.\n", (int) s);
+
+ to = currticks() + TX_TIMEOUT;
+
+ while (((tx_status=txd.cmdsts) & OWN) && (currticks() < to))
+ /* wait */ ;
+
+ if (currticks() >= to) {
+ printf("sis900_transmit: TX Timeout! Tx status %X.\n",
+ (unsigned int) tx_status);
+ }
+
+ if (tx_status & (ABORT | UNDERRUN | OWCOLL)) {
+ /* packet unsuccessfully transmited */
+ printf("sis900_transmit: Transmit error, Tx status %X.\n",
+ (unsigned int) tx_status);
+ }
+ /* Disable interrupts by clearing the interrupt mask. */
+ outl(0, ioaddr + imr);
+}
+
+
+/* Function: sis900_poll
+ *
+ * Description: checks for a received packet and returns it if found.
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: 1 if a packet was recieved.
+ * 0 if no pacet was recieved.
+ *
+ * Side effects:
+ * Returns (copies) the packet to the array nic->packet.
+ * Returns the length of the packet in nic->packetlen.
+ */
+
+static int
+sis900_poll(struct nic *nic, int retrieve)
+{
+ u32 rx_status = rxd[cur_rx].cmdsts;
+ int retstat = 0;
+
+ if (sis900_debug > 2)
+ printf("sis900_poll: cur_rx:%d, status:%X\n", cur_rx,
+ (unsigned int) rx_status);
+
+ if (!(rx_status & OWN))
+ return retstat;
+
+ if (sis900_debug > 1)
+ printf("sis900_poll: got a packet: cur_rx:%d, status:%X\n",
+ cur_rx, (unsigned int) rx_status);
+
+ if ( ! retrieve ) return 1;
+
+ nic->packetlen = (rx_status & DSIZE) - CRC_SIZE;
+
+ if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {
+ /* corrupted packet received */
+ printf("sis900_poll: Corrupted packet received, buffer status = %X\n",
+ (unsigned int) rx_status);
+ retstat = 0;
+ } else {
+ /* give packet to higher level routine */
+ memcpy(nic->packet, (rxb + cur_rx*RX_BUF_SIZE), nic->packetlen);
+ retstat = 1;
+ }
+
+ /* return the descriptor and buffer to receive ring */
+ rxd[cur_rx].cmdsts = RX_BUF_SIZE;
+ rxd[cur_rx].bufptr = virt_to_bus(&rxb[cur_rx*RX_BUF_SIZE]);
+
+ if (++cur_rx == NUM_RX_DESC)
+ cur_rx = 0;
+
+ /* re-enable the potentially idle receive state machine */
+ outl(RxENA | inl(ioaddr + cr), ioaddr + cr);
+
+ return retstat;
+
+}
+
+
+/* Function: sis900_disable
+ *
+ * Description: Turns off interrupts and stops Tx and Rx engines
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_disable ( struct nic *nic ) {
+
+ sis900_init(nic);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ outl(0, ioaddr + imr);
+ outl(0, ioaddr + ier);
+
+ /* Stop the chip's Tx and Rx Status Machine */
+ outl(RxDIS | TxDIS | inl(ioaddr + cr), ioaddr + cr);
+}
+
+
+/* Function: sis900_irq
+ *
+ * Description: Enable, Disable, or Force, interrupts
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ * irq_action_t action: Requested action
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct nic_operations sis900_operations = {
+ .connect = dummy_connect,
+ .poll = sis900_poll,
+ .transmit = sis900_transmit,
+ .irq = sis900_irq,
+};
+
+static struct pci_device_id sis900_nics[] = {
+PCI_ROM(0x1039, 0x0900, "sis900", "SIS900"),
+PCI_ROM(0x1039, 0x7016, "sis7016", "SIS7016"),
+};
+
+PCI_DRIVER ( sis900_driver, sis900_nics, PCI_NO_CLASS );
+
+DRIVER ( "SIS900", nic_driver, pci_driver, sis900_driver,
+ sis900_probe, sis900_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/sis900.h b/gpxe/src/drivers/net/sis900.h
new file mode 100644
index 00000000..e88e111d
--- /dev/null
+++ b/gpxe/src/drivers/net/sis900.h
@@ -0,0 +1,373 @@
+/* -*- Mode:C; c-basic-offset:4; -*- */
+
+/* Definitions for SiS ethernet controllers including 7014/7016 and 900
+ * References:
+ * SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support,
+ * preliminary Rev. 1.0 Jan. 14, 1998
+ * SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support,
+ * preliminary Rev. 1.0 Nov. 10, 1998
+ * SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
+ * preliminary Rev. 1.0 Jan. 18, 1998
+ * http://www.sis.com.tw/support/databook.htm
+ */
+
+/* MAC operationl registers of SiS 7016 and SiS 900 ethernet controller */
+/* The I/O extent, SiS 900 needs 256 bytes of io address */
+#define SIS900_TOTAL_SIZE 0x100
+
+/* Symbolic offsets to registers. */
+enum sis900_registers {
+ cr=0x0, /* Command Register */
+ cfg=0x4, /* Configuration Register */
+ mear=0x8, /* EEPROM Access Register */
+ ptscr=0xc, /* PCI Test Control Register */
+ isr=0x10, /* Interrupt Status Register */
+ imr=0x14, /* Interrupt Mask Register */
+ ier=0x18, /* Interrupt Enable Register */
+ epar=0x18, /* Enhanced PHY Access Register */
+ txdp=0x20, /* Transmit Descriptor Pointer Register */
+ txcfg=0x24, /* Transmit Configuration Register */
+ rxdp=0x30, /* Receive Descriptor Pointer Register */
+ rxcfg=0x34, /* Receive Configuration Register */
+ flctrl=0x38, /* Flow Control Register */
+ rxlen=0x3c, /* Receive Packet Length Register */
+ rfcr=0x48, /* Receive Filter Control Register */
+ rfdr=0x4C, /* Receive Filter Data Register */
+ pmctrl=0xB0, /* Power Management Control Register */
+ pmer=0xB4 /* Power Management Wake-up Event Register */
+};
+
+/* Symbolic names for bits in various registers */
+enum sis900_command_register_bits {
+ RELOAD = 0x00000400,
+ ACCESSMODE = 0x00000200,
+ RESET = 0x00000100,
+ SWI = 0x00000080,
+ RxRESET = 0x00000020,
+ TxRESET = 0x00000010,
+ RxDIS = 0x00000008,
+ RxENA = 0x00000004,
+ TxDIS = 0x00000002,
+ TxENA = 0x00000001
+};
+
+enum sis900_configuration_register_bits {
+ DESCRFMT = 0x00000100, /* 7016 specific */
+ REQALG = 0x00000080,
+ SB = 0x00000040,
+ POW = 0x00000020,
+ EXD = 0x00000010,
+ PESEL = 0x00000008,
+ LPM = 0x00000004,
+ BEM = 0x00000001,
+ RND_CNT = 0x00000400,
+ FAIR_BACKOFF = 0x00000200,
+ EDB_MASTER_EN = 0x00002000
+};
+
+enum sis900_eeprom_access_reigster_bits {
+ MDC = 0x00000040,
+ MDDIR = 0x00000020,
+ MDIO = 0x00000010, /* 7016 specific */
+ EECS = 0x00000008,
+ EECLK = 0x00000004,
+ EEDO = 0x00000002,
+ EEDI = 0x00000001
+};
+
+enum sis900_interrupt_register_bits {
+ WKEVT = 0x10000000,
+ TxPAUSEEND = 0x08000000,
+ TxPAUSE = 0x04000000,
+ TxRCMP = 0x02000000,
+ RxRCMP = 0x01000000,
+ DPERR = 0x00800000,
+ SSERR = 0x00400000,
+ RMABT = 0x00200000,
+ RTABT = 0x00100000,
+ RxSOVR = 0x00010000,
+ HIBERR = 0x00008000,
+ SWINT = 0x00001000,
+ MIBINT = 0x00000800,
+ TxURN = 0x00000400,
+ TxIDLE = 0x00000200,
+ TxERR = 0x00000100,
+ TxDESC = 0x00000080,
+ TxOK = 0x00000040,
+ RxORN = 0x00000020,
+ RxIDLE = 0x00000010,
+ RxEARLY = 0x00000008,
+ RxERR = 0x00000004,
+ RxDESC = 0x00000002,
+ RxOK = 0x00000001
+};
+
+enum sis900_interrupt_enable_reigster_bits {
+ IE = 0x00000001
+};
+
+/* maximum dma burst fro transmission and receive*/
+#define MAX_DMA_RANGE 7 /* actually 0 means MAXIMUM !! */
+#define TxMXDMA_shift 20
+#define RxMXDMA_shift 20
+#define TX_DMA_BURST 0
+#define RX_DMA_BURST 0
+
+enum sis900_tx_rx_dma{
+ DMA_BURST_512 = 0, DMA_BURST_64 = 5
+};
+
+/* transmit FIFO threshholds */
+#define TX_FILL_THRESH 16 /* 1/4 FIFO size */
+#define TxFILLT_shift 8
+#define TxDRNT_shift 0
+#define TxDRNT_100 48 /* 3/4 FIFO size */
+#define TxDRNT_10 16 /* 1/2 FIFO size */
+
+enum sis900_transmit_config_register_bits {
+ TxCSI = 0x80000000,
+ TxHBI = 0x40000000,
+ TxMLB = 0x20000000,
+ TxATP = 0x10000000,
+ TxIFG = 0x0C000000,
+ TxFILLT = 0x00003F00,
+ TxDRNT = 0x0000003F
+};
+
+/* recevie FIFO thresholds */
+#define RxDRNT_shift 1
+#define RxDRNT_100 16 /* 1/2 FIFO size */
+#define RxDRNT_10 24 /* 3/4 FIFO size */
+
+enum sis900_reveive_config_register_bits {
+ RxAEP = 0x80000000,
+ RxARP = 0x40000000,
+ RxATX = 0x10000000,
+ RxAJAB = 0x08000000,
+ RxDRNT = 0x0000007F
+};
+
+#define RFAA_shift 28
+#define RFADDR_shift 16
+
+enum sis900_receive_filter_control_register_bits {
+ RFEN = 0x80000000,
+ RFAAB = 0x40000000,
+ RFAAM = 0x20000000,
+ RFAAP = 0x10000000,
+ RFPromiscuous = (RFAAB|RFAAM|RFAAP)
+};
+
+enum sis900_reveive_filter_data_mask {
+ RFDAT = 0x0000FFFF
+};
+
+/* EEPROM Addresses */
+enum sis900_eeprom_address {
+ EEPROMSignature = 0x00,
+ EEPROMVendorID = 0x02,
+ EEPROMDeviceID = 0x03,
+ EEPROMMACAddr = 0x08,
+ EEPROMChecksum = 0x0b
+};
+
+/* The EEPROM commands include the alway-set leading bit. Refer to NM93Cxx datasheet */
+enum sis900_eeprom_command {
+ EEread = 0x0180,
+ EEwrite = 0x0140,
+ EEerase = 0x01C0,
+ EEwriteEnable = 0x0130,
+ EEwriteDisable = 0x0100,
+ EEeraseAll = 0x0120,
+ EEwriteAll = 0x0110,
+ EEaddrMask = 0x013F,
+ EEcmdShift = 16
+};
+/* For SiS962 or SiS963, request the eeprom software access */
+enum sis96x_eeprom_command {
+ EEREQ = 0x00000400, EEDONE = 0x00000200, EEGNT = 0x00000100
+};
+
+/* Manamgement Data I/O (mdio) frame */
+#define MIIread 0x6000
+#define MIIwrite 0x5002
+#define MIIpmdShift 7
+#define MIIregShift 2
+#define MIIcmdLen 16
+#define MIIcmdShift 16
+
+/* Buffer Descriptor Status*/
+enum sis900_buffer_status {
+ OWN = 0x80000000,
+ MORE = 0x40000000,
+ INTR = 0x20000000,
+ SUPCRC = 0x10000000,
+ INCCRC = 0x10000000,
+ OK = 0x08000000,
+ DSIZE = 0x00000FFF
+};
+
+/* Status for TX Buffers */
+enum sis900_tx_buffer_status {
+ ABORT = 0x04000000,
+ UNDERRUN = 0x02000000,
+ NOCARRIER = 0x01000000,
+ DEFERD = 0x00800000,
+ EXCDEFER = 0x00400000,
+ OWCOLL = 0x00200000,
+ EXCCOLL = 0x00100000,
+ COLCNT = 0x000F0000
+};
+
+enum sis900_rx_bufer_status {
+ OVERRUN = 0x02000000,
+ DEST = 0x00800000,
+ BCAST = 0x01800000,
+ MCAST = 0x01000000,
+ UNIMATCH = 0x00800000,
+ TOOLONG = 0x00400000,
+ RUNT = 0x00200000,
+ RXISERR = 0x00100000,
+ CRCERR = 0x00080000,
+ FAERR = 0x00040000,
+ LOOPBK = 0x00020000,
+ RXCOL = 0x00010000
+};
+
+/* MII register offsets */
+enum mii_registers {
+ MII_CONTROL = 0x0000,
+ MII_STATUS = 0x0001,
+ MII_PHY_ID0 = 0x0002,
+ MII_PHY_ID1 = 0x0003,
+ MII_ANADV = 0x0004,
+ MII_ANLPAR = 0x0005,
+ MII_ANEXT = 0x0006
+};
+
+/* mii registers specific to SiS 900 */
+enum sis_mii_registers {
+ MII_CONFIG1 = 0x0010,
+ MII_CONFIG2 = 0x0011,
+ MII_STSOUT = 0x0012,
+ MII_MASK = 0x0013,
+ MII_RESV = 0x0014
+};
+
+/* mii registers specific to AMD 79C901 */
+enum amd_mii_registers {
+ MII_STATUS_SUMMARY = 0x0018
+};
+
+/* mii registers specific to ICS 1893 */
+enum ics_mii_registers {
+ MII_EXTCTRL = 0x0010, MII_QPDSTS = 0x0011, MII_10BTOP = 0x0012,
+ MII_EXTCTRL2 = 0x0013
+};
+
+
+
+/* MII Control register bit definitions. */
+enum mii_control_register_bits {
+ MII_CNTL_FDX = 0x0100,
+ MII_CNTL_RST_AUTO = 0x0200,
+ MII_CNTL_ISOLATE = 0x0400,
+ MII_CNTL_PWRDWN = 0x0800,
+ MII_CNTL_AUTO = 0x1000,
+ MII_CNTL_SPEED = 0x2000,
+ MII_CNTL_LPBK = 0x4000,
+ MII_CNTL_RESET = 0x8000
+};
+
+/* MII Status register bit */
+enum mii_status_register_bits {
+ MII_STAT_EXT = 0x0001,
+ MII_STAT_JAB = 0x0002,
+ MII_STAT_LINK = 0x0004,
+ MII_STAT_CAN_AUTO = 0x0008,
+ MII_STAT_FAULT = 0x0010,
+ MII_STAT_AUTO_DONE = 0x0020,
+ MII_STAT_CAN_T = 0x0800,
+ MII_STAT_CAN_T_FDX = 0x1000,
+ MII_STAT_CAN_TX = 0x2000,
+ MII_STAT_CAN_TX_FDX = 0x4000,
+ MII_STAT_CAN_T4 = 0x8000
+};
+
+#define MII_ID1_OUI_LO 0xFC00 /* low bits of OUI mask */
+#define MII_ID1_MODEL 0x03F0 /* model number */
+#define MII_ID1_REV 0x000F /* model number */
+
+/* MII NWAY Register Bits ...
+ valid for the ANAR (Auto-Negotiation Advertisement) and
+ ANLPAR (Auto-Negotiation Link Partner) registers */
+enum mii_nway_register_bits {
+ MII_NWAY_NODE_SEL = 0x001f,
+ MII_NWAY_CSMA_CD = 0x0001,
+ MII_NWAY_T = 0x0020,
+ MII_NWAY_T_FDX = 0x0040,
+ MII_NWAY_TX = 0x0080,
+ MII_NWAY_TX_FDX = 0x0100,
+ MII_NWAY_T4 = 0x0200,
+ MII_NWAY_PAUSE = 0x0400,
+ MII_NWAY_RF = 0x2000,
+ MII_NWAY_ACK = 0x4000,
+ MII_NWAY_NP = 0x8000
+};
+
+enum mii_stsout_register_bits {
+ MII_STSOUT_LINK_FAIL = 0x4000,
+ MII_STSOUT_SPD = 0x0080,
+ MII_STSOUT_DPLX = 0x0040
+};
+
+enum mii_stsics_register_bits {
+ MII_STSICS_SPD = 0x8000, MII_STSICS_DPLX = 0x4000,
+ MII_STSICS_LINKSTS = 0x0001
+};
+
+enum mii_stssum_register_bits {
+ MII_STSSUM_LINK = 0x0008,
+ MII_STSSUM_DPLX = 0x0004,
+ MII_STSSUM_AUTO = 0x0002,
+ MII_STSSUM_SPD = 0x0001
+};
+
+enum sis900_revision_id {
+ SIS630A_900_REV = 0x80, SIS630E_900_REV = 0x81,
+ SIS630S_900_REV = 0x82, SIS630EA1_900_REV = 0x83,
+ SIS630ET_900_REV = 0x84, SIS635A_900_REV = 0x90,
+ SIS96x_900_REV = 0X91, SIS900B_900_REV = 0x03
+};
+
+enum sis630_revision_id {
+ SIS630A0 = 0x00, SIS630A1 = 0x01,
+ SIS630B0 = 0x10, SIS630B1 = 0x11
+};
+
+#define FDX_CAPABLE_DUPLEX_UNKNOWN 0
+#define FDX_CAPABLE_HALF_SELECTED 1
+#define FDX_CAPABLE_FULL_SELECTED 2
+
+#define HW_SPEED_UNCONFIG 0
+#define HW_SPEED_HOME 1
+#define HW_SPEED_10_MBPS 10
+#define HW_SPEED_100_MBPS 100
+#define HW_SPEED_DEFAULT (HW_SPEED_100_MBPS)
+
+#define CRC_SIZE 4
+#define MAC_HEADER_SIZE 14
+
+#define TX_BUF_SIZE 1536
+#define RX_BUF_SIZE 1536
+
+#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */
+
+/* Time in ticks before concluding the transmitter is hung. */
+#define TX_TIMEOUT (4*TICKS_PER_SEC)
+
+typedef struct _BufferDesc {
+ u32 link;
+ volatile u32 cmdsts;
+ u32 bufptr;
+} BufferDesc;
diff --git a/gpxe/src/drivers/net/smc9000.c b/gpxe/src/drivers/net/smc9000.c
new file mode 100644
index 00000000..31a1e1b1
--- /dev/null
+++ b/gpxe/src/drivers/net/smc9000.c
@@ -0,0 +1,952 @@
+#ifdef ALLMULTI
+#error multicast support is not yet implemented
+#endif
+ /*------------------------------------------------------------------------
+ * smc9000.c
+ * This is a Etherboot driver for SMC's 9000 series of Ethernet cards.
+ *
+ * Copyright (C) 1998 Daniel Engström <daniel.engstrom@riksnett.no>
+ * Based on the Linux SMC9000 driver, smc9194.c by Eric Stahlman
+ * Copyright (C) 1996 by Erik Stahlman <eric@vt.edu>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * "Features" of the SMC chip:
+ * 4608 byte packet memory. ( for the 91C92/4. Others have more )
+ * EEPROM for configuration
+ * AUI/TP selection
+ *
+ * Authors
+ * Erik Stahlman <erik@vt.edu>
+ * Daniel Engström <daniel.engstrom@riksnett.no>
+ *
+ * History
+ * 98-09-25 Daniel Engström Etherboot driver crated from Eric's
+ * Linux driver.
+ *
+ *---------------------------------------------------------------------------*/
+#define LINUX_OUT_MACROS 1
+#define SMC9000_DEBUG 0
+
+#if SMC9000_DEBUG > 1
+#define PRINTK2 printf
+#else
+#define PRINTK2(args...)
+#endif
+
+#include <gpxe/ethernet.h>
+#include <errno.h>
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/isa.h>
+#include "smc9000.h"
+
+# define _outb outb
+# define _outw outw
+
+static const char smc9000_version[] = "Version 0.99 98-09-30";
+static const char *interfaces[ 2 ] = { "TP", "AUI" };
+static const char *chip_ids[ 15 ] = {
+ NULL, NULL, NULL,
+ /* 3 */ "SMC91C90/91C92",
+ /* 4 */ "SMC91C94",
+ /* 5 */ "SMC91C95",
+ NULL,
+ /* 7 */ "SMC91C100",
+ /* 8 */ "SMC91C100FD",
+ /* 9 */ "SMC91C11xFD",
+ NULL, NULL,
+ NULL, NULL, NULL
+};
+static const char smc91c96_id[] = "SMC91C96";
+
+/*------------------------------------------------------------
+ . Reads a register from the MII Management serial interface
+ .-------------------------------------------------------------*/
+static word smc_read_phy_register(int ioaddr, byte phyaddr, byte phyreg)
+{
+ int oldBank;
+ unsigned int i;
+ byte mask;
+ word mii_reg;
+ byte bits[64];
+ int clk_idx = 0;
+ int input_idx;
+ word phydata;
+
+ // 32 consecutive ones on MDO to establish sync
+ for (i = 0; i < 32; ++i)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+ // Start code <01>
+ bits[clk_idx++] = MII_MDOE;
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+ // Read command <10>
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ bits[clk_idx++] = MII_MDOE;
+
+ // Output the PHY address, msb first
+ mask = (byte)0x10;
+ for (i = 0; i < 5; ++i)
+ {
+ if (phyaddr & mask)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ else
+ bits[clk_idx++] = MII_MDOE;
+
+ // Shift to next lowest bit
+ mask >>= 1;
+ }
+
+ // Output the phy register number, msb first
+ mask = (byte)0x10;
+ for (i = 0; i < 5; ++i)
+ {
+ if (phyreg & mask)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ else
+ bits[clk_idx++] = MII_MDOE;
+
+ // Shift to next lowest bit
+ mask >>= 1;
+ }
+
+ // Tristate and turnaround (2 bit times)
+ bits[clk_idx++] = 0;
+ //bits[clk_idx++] = 0;
+
+ // Input starts at this bit time
+ input_idx = clk_idx;
+
+ // Will input 16 bits
+ for (i = 0; i < 16; ++i)
+ bits[clk_idx++] = 0;
+
+ // Final clock bit
+ bits[clk_idx++] = 0;
+
+ // Save the current bank
+ oldBank = inw( ioaddr+BANK_SELECT );
+
+ // Select bank 3
+ SMC_SELECT_BANK(ioaddr, 3);
+
+ // Get the current MII register value
+ mii_reg = inw( ioaddr+MII_REG );
+
+ // Turn off all MII Interface bits
+ mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);
+
+ // Clock all 64 cycles
+ for (i = 0; i < sizeof(bits); ++i)
+ {
+ // Clock Low - output data
+ outw( mii_reg | bits[i], ioaddr+MII_REG );
+ udelay(50);
+
+
+ // Clock Hi - input data
+ outw( mii_reg | bits[i] | MII_MCLK, ioaddr+MII_REG );
+ udelay(50);
+ bits[i] |= inw( ioaddr+MII_REG ) & MII_MDI;
+ }
+
+ // Return to idle state
+ // Set clock to low, data to low, and output tristated
+ outw( mii_reg, ioaddr+MII_REG );
+ udelay(50);
+
+ // Restore original bank select
+ SMC_SELECT_BANK(ioaddr, oldBank);
+
+ // Recover input data
+ phydata = 0;
+ for (i = 0; i < 16; ++i)
+ {
+ phydata <<= 1;
+
+ if (bits[input_idx++] & MII_MDI)
+ phydata |= 0x0001;
+ }
+
+#if (SMC_DEBUG > 2 )
+ printf("smc_read_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n",
+ phyaddr, phyreg, phydata);
+#endif
+
+ return(phydata);
+}
+
+
+/*------------------------------------------------------------
+ . Writes a register to the MII Management serial interface
+ .-------------------------------------------------------------*/
+static void smc_write_phy_register(int ioaddr,
+ byte phyaddr, byte phyreg, word phydata)
+{
+ int oldBank;
+ unsigned int i;
+ word mask;
+ word mii_reg;
+ byte bits[65];
+ int clk_idx = 0;
+
+ // 32 consecutive ones on MDO to establish sync
+ for (i = 0; i < 32; ++i)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+ // Start code <01>
+ bits[clk_idx++] = MII_MDOE;
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+ // Write command <01>
+ bits[clk_idx++] = MII_MDOE;
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+ // Output the PHY address, msb first
+ mask = (byte)0x10;
+ for (i = 0; i < 5; ++i)
+ {
+ if (phyaddr & mask)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ else
+ bits[clk_idx++] = MII_MDOE;
+
+ // Shift to next lowest bit
+ mask >>= 1;
+ }
+
+ // Output the phy register number, msb first
+ mask = (byte)0x10;
+ for (i = 0; i < 5; ++i)
+ {
+ if (phyreg & mask)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ else
+ bits[clk_idx++] = MII_MDOE;
+
+ // Shift to next lowest bit
+ mask >>= 1;
+ }
+
+ // Tristate and turnaround (2 bit times)
+ bits[clk_idx++] = 0;
+ bits[clk_idx++] = 0;
+
+ // Write out 16 bits of data, msb first
+ mask = 0x8000;
+ for (i = 0; i < 16; ++i)
+ {
+ if (phydata & mask)
+ bits[clk_idx++] = MII_MDOE | MII_MDO;
+ else
+ bits[clk_idx++] = MII_MDOE;
+
+ // Shift to next lowest bit
+ mask >>= 1;
+ }
+
+ // Final clock bit (tristate)
+ bits[clk_idx++] = 0;
+
+ // Save the current bank
+ oldBank = inw( ioaddr+BANK_SELECT );
+
+ // Select bank 3
+ SMC_SELECT_BANK(ioaddr, 3);
+
+ // Get the current MII register value
+ mii_reg = inw( ioaddr+MII_REG );
+
+ // Turn off all MII Interface bits
+ mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);
+
+ // Clock all cycles
+ for (i = 0; i < sizeof(bits); ++i)
+ {
+ // Clock Low - output data
+ outw( mii_reg | bits[i], ioaddr+MII_REG );
+ udelay(50);
+
+
+ // Clock Hi - input data
+ outw( mii_reg | bits[i] | MII_MCLK, ioaddr+MII_REG );
+ udelay(50);
+ bits[i] |= inw( ioaddr+MII_REG ) & MII_MDI;
+ }
+
+ // Return to idle state
+ // Set clock to low, data to low, and output tristated
+ outw( mii_reg, ioaddr+MII_REG );
+ udelay(50);
+
+ // Restore original bank select
+ SMC_SELECT_BANK(ioaddr, oldBank);
+
+#if (SMC_DEBUG > 2 )
+ printf("smc_write_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n",
+ phyaddr, phyreg, phydata);
+#endif
+}
+
+
+/*------------------------------------------------------------
+ . Finds and reports the PHY address
+ .-------------------------------------------------------------*/
+static int smc_detect_phy(int ioaddr, byte *pphyaddr)
+{
+ word phy_id1;
+ word phy_id2;
+ int phyaddr;
+ int found = 0;
+
+ // Scan all 32 PHY addresses if necessary
+ for (phyaddr = 0; phyaddr < 32; ++phyaddr)
+ {
+ // Read the PHY identifiers
+ phy_id1 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID1_REG);
+ phy_id2 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID2_REG);
+
+ // Make sure it is a valid identifier
+ if ((phy_id2 > 0x0000) && (phy_id2 < 0xffff) &&
+ (phy_id1 > 0x0000) && (phy_id1 < 0xffff))
+ {
+ if ((phy_id1 != 0x8000) && (phy_id2 != 0x8000))
+ {
+ // Save the PHY's address
+ *pphyaddr = phyaddr;
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ printf("No PHY found\n");
+ return(0);
+ }
+
+ // Set the PHY type
+ if ( (phy_id1 == 0x0016) && ((phy_id2 & 0xFFF0) == 0xF840 ) )
+ {
+ printf("PHY=LAN83C183 (LAN91C111 Internal)\n");
+ }
+
+ if ( (phy_id1 == 0x0282) && ((phy_id2 & 0xFFF0) == 0x1C50) )
+ {
+ printf("PHY=LAN83C180\n");
+ }
+
+ return(1);
+}
+
+/*------------------------------------------------------------
+ . Configures the specified PHY using Autonegotiation. Calls
+ . smc_phy_fixed() if the user has requested a certain config.
+ .-------------------------------------------------------------*/
+static void smc_phy_configure(int ioaddr)
+{
+ int timeout;
+ byte phyaddr;
+ word my_phy_caps; // My PHY capabilities
+ word my_ad_caps; // My Advertised capabilities
+ word status;
+ int failed = 0;
+ int rpc_cur_mode = RPC_DEFAULT;
+ int lastPhy18;
+
+ // Find the address and type of our phy
+ if (!smc_detect_phy(ioaddr, &phyaddr))
+ {
+ return;
+ }
+
+ // Reset the PHY, setting all other bits to zero
+ smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, PHY_CNTL_RST);
+
+ // Wait for the reset to complete, or time out
+ timeout = 6; // Wait up to 3 seconds
+ while (timeout--)
+ {
+ if (!(smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG)
+ & PHY_CNTL_RST))
+ {
+ // reset complete
+ break;
+ }
+
+ mdelay(500); // wait 500 millisecs
+ }
+
+ if (timeout < 1)
+ {
+ PRINTK2("PHY reset timed out\n");
+ return;
+ }
+
+ // Read PHY Register 18, Status Output
+ lastPhy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG);
+
+ // Enable PHY Interrupts (for register 18)
+ // Interrupts listed here are disabled
+ smc_write_phy_register(ioaddr, phyaddr, PHY_MASK_REG,
+ PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD |
+ PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB |
+ PHY_INT_SPDDET | PHY_INT_DPLXDET);
+
+ /* Configure the Receive/Phy Control register */
+ SMC_SELECT_BANK(ioaddr, 0);
+ outw( rpc_cur_mode, ioaddr + RPC_REG );
+
+ // Copy our capabilities from PHY_STAT_REG to PHY_AD_REG
+ my_phy_caps = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG);
+ my_ad_caps = PHY_AD_CSMA; // I am CSMA capable
+
+ if (my_phy_caps & PHY_STAT_CAP_T4)
+ my_ad_caps |= PHY_AD_T4;
+
+ if (my_phy_caps & PHY_STAT_CAP_TXF)
+ my_ad_caps |= PHY_AD_TX_FDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TXH)
+ my_ad_caps |= PHY_AD_TX_HDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TF)
+ my_ad_caps |= PHY_AD_10_FDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TH)
+ my_ad_caps |= PHY_AD_10_HDX;
+
+ // Update our Auto-Neg Advertisement Register
+ smc_write_phy_register(ioaddr, phyaddr, PHY_AD_REG, my_ad_caps);
+
+ PRINTK2("phy caps=%x\n", my_phy_caps);
+ PRINTK2("phy advertised caps=%x\n", my_ad_caps);
+
+ // Restart auto-negotiation process in order to advertise my caps
+ smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG,
+ PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST );
+
+ // Wait for the auto-negotiation to complete. This may take from
+ // 2 to 3 seconds.
+ // Wait for the reset to complete, or time out
+ timeout = 20; // Wait up to 10 seconds
+ while (timeout--)
+ {
+ status = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG);
+ if (status & PHY_STAT_ANEG_ACK)
+ {
+ // auto-negotiate complete
+ break;
+ }
+
+ mdelay(500); // wait 500 millisecs
+
+ // Restart auto-negotiation if remote fault
+ if (status & PHY_STAT_REM_FLT)
+ {
+ PRINTK2("PHY remote fault detected\n");
+
+ // Restart auto-negotiation
+ PRINTK2("PHY restarting auto-negotiation\n");
+ smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG,
+ PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST |
+ PHY_CNTL_SPEED | PHY_CNTL_DPLX);
+ }
+ }
+
+ if (timeout < 1)
+ {
+ PRINTK2("PHY auto-negotiate timed out\n");
+ failed = 1;
+ }
+
+ // Fail if we detected an auto-negotiate remote fault
+ if (status & PHY_STAT_REM_FLT)
+ {
+ PRINTK2("PHY remote fault detected\n");
+ failed = 1;
+ }
+
+ // Set our sysctl parameters to match auto-negotiation results
+ if ( lastPhy18 & PHY_INT_SPDDET )
+ {
+ PRINTK2("PHY 100BaseT\n");
+ rpc_cur_mode |= RPC_SPEED;
+ }
+ else
+ {
+ PRINTK2("PHY 10BaseT\n");
+ rpc_cur_mode &= ~RPC_SPEED;
+ }
+
+ if ( lastPhy18 & PHY_INT_DPLXDET )
+ {
+ PRINTK2("PHY Full Duplex\n");
+ rpc_cur_mode |= RPC_DPLX;
+ }
+ else
+ {
+ PRINTK2("PHY Half Duplex\n");
+ rpc_cur_mode &= ~RPC_DPLX;
+ }
+
+ // Re-Configure the Receive/Phy Control register
+ outw( rpc_cur_mode, ioaddr + RPC_REG );
+}
+
+/*
+ * Function: smc_reset( int ioaddr )
+ * Purpose:
+ * This sets the SMC91xx chip to its normal state, hopefully from whatever
+ * mess that any other DOS driver has put it in.
+ *
+ * Maybe I should reset more registers to defaults in here? SOFTRESET should
+ * do that for me.
+ *
+ * Method:
+ * 1. send a SOFT RESET
+ * 2. wait for it to finish
+ * 3. reset the memory management unit
+ * 4. clear all interrupts
+ *
+*/
+static void smc_reset(int ioaddr)
+{
+ /* This resets the registers mostly to defaults, but doesn't
+ * affect EEPROM. That seems unnecessary */
+ SMC_SELECT_BANK(ioaddr, 0);
+ _outw( RCR_SOFTRESET, ioaddr + RCR );
+
+ /* this should pause enough for the chip to be happy */
+ SMC_DELAY(ioaddr);
+
+ /* Set the transmit and receive configuration registers to
+ * default values */
+ _outw(RCR_CLEAR, ioaddr + RCR);
+ _outw(TCR_CLEAR, ioaddr + TCR);
+
+ /* Reset the MMU */
+ SMC_SELECT_BANK(ioaddr, 2);
+ _outw( MC_RESET, ioaddr + MMU_CMD );
+
+ /* Note: It doesn't seem that waiting for the MMU busy is needed here,
+ * but this is a place where future chipsets _COULD_ break. Be wary
+ * of issuing another MMU command right after this */
+ _outb(0, ioaddr + INT_MASK);
+}
+
+
+/*----------------------------------------------------------------------
+ * Function: smc9000_probe_addr( int ioaddr )
+ *
+ * Purpose:
+ * Tests to see if a given ioaddr points to an SMC9xxx chip.
+ * Returns a 1 on success
+ *
+ * Algorithm:
+ * (1) see if the high byte of BANK_SELECT is 0x33
+ * (2) compare the ioaddr with the base register's address
+ * (3) see if I recognize the chip ID in the appropriate register
+ *
+ * ---------------------------------------------------------------------
+ */
+static int smc9000_probe_addr( isa_probe_addr_t ioaddr )
+{
+ word bank;
+ word revision_register;
+ word base_address_register;
+
+ /* First, see if the high byte is 0x33 */
+ bank = inw(ioaddr + BANK_SELECT);
+ if ((bank & 0xFF00) != 0x3300) {
+ return 0;
+ }
+ /* The above MIGHT indicate a device, but I need to write to further
+ * test this. */
+ _outw(0x0, ioaddr + BANK_SELECT);
+ bank = inw(ioaddr + BANK_SELECT);
+ if ((bank & 0xFF00) != 0x3300) {
+ return 0;
+ }
+
+ /* well, we've already written once, so hopefully another time won't
+ * hurt. This time, I need to switch the bank register to bank 1,
+ * so I can access the base address register */
+ SMC_SELECT_BANK(ioaddr, 1);
+ base_address_register = inw(ioaddr + BASE);
+
+ if (ioaddr != (base_address_register >> 3 & 0x3E0)) {
+ DBG("SMC9000: IOADDR %hX doesn't match configuration (%hX)."
+ "Probably not a SMC chip\n",
+ ioaddr, base_address_register >> 3 & 0x3E0);
+ /* well, the base address register didn't match. Must not have
+ * been a SMC chip after all. */
+ return 0;
+ }
+
+
+ /* check if the revision register is something that I recognize.
+ * These might need to be added to later, as future revisions
+ * could be added. */
+ SMC_SELECT_BANK(ioaddr, 3);
+ revision_register = inw(ioaddr + REVISION);
+ if (!chip_ids[(revision_register >> 4) & 0xF]) {
+ /* I don't recognize this chip, so... */
+ DBG( "SMC9000: IO %hX: Unrecognized revision register:"
+ " %hX, Contact author.\n", ioaddr, revision_register );
+ return 0;
+ }
+
+ /* at this point I'll assume that the chip is an SMC9xxx.
+ * It might be prudent to check a listing of MAC addresses
+ * against the hardware address, or do some other tests. */
+ return 1;
+}
+
+
+/**************************************************************************
+ * ETH_TRANSMIT - Transmit a frame
+ ***************************************************************************/
+static void smc9000_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ word length; /* real, length incl. header */
+ word numPages;
+ unsigned long time_out;
+ byte packet_no;
+ word status;
+ int i;
+
+ /* We dont pad here since we can have the hardware doing it for us */
+ length = (s + ETH_HLEN + 1)&~1;
+
+ /* convert to MMU pages */
+ numPages = length / 256;
+
+ if (numPages > 7 ) {
+ DBG("SMC9000: Far too big packet error. \n");
+ return;
+ }
+
+ /* dont try more than, say 30 times */
+ for (i=0;i<30;i++) {
+ /* now, try to allocate the memory */
+ SMC_SELECT_BANK(nic->ioaddr, 2);
+ _outw(MC_ALLOC | numPages, nic->ioaddr + MMU_CMD);
+
+ status = 0;
+ /* wait for the memory allocation to finnish */
+ for (time_out = currticks() + 5*TICKS_PER_SEC; currticks() < time_out; ) {
+ status = inb(nic->ioaddr + INTERRUPT);
+ if ( status & IM_ALLOC_INT ) {
+ /* acknowledge the interrupt */
+ _outb(IM_ALLOC_INT, nic->ioaddr + INTERRUPT);
+ break;
+ }
+ }
+
+ if ((status & IM_ALLOC_INT) != 0 ) {
+ /* We've got the memory */
+ break;
+ } else {
+ printf("SMC9000: Memory allocation timed out, resetting MMU.\n");
+ _outw(MC_RESET, nic->ioaddr + MMU_CMD);
+ }
+ }
+
+ /* If I get here, I _know_ there is a packet slot waiting for me */
+ packet_no = inb(nic->ioaddr + PNR_ARR + 1);
+ if (packet_no & 0x80) {
+ /* or isn't there? BAD CHIP! */
+ printf("SMC9000: Memory allocation failed. \n");
+ return;
+ }
+
+ /* we have a packet address, so tell the card to use it */
+ _outb(packet_no, nic->ioaddr + PNR_ARR);
+
+ /* point to the beginning of the packet */
+ _outw(PTR_AUTOINC, nic->ioaddr + POINTER);
+
+#if SMC9000_DEBUG > 2
+ printf("Trying to xmit packet of length %hX\n", length );
+#endif
+
+ /* send the packet length ( +6 for status, length and ctl byte )
+ * and the status word ( set to zeros ) */
+ _outw(0, nic->ioaddr + DATA_1 );
+
+ /* send the packet length ( +6 for status words, length, and ctl) */
+ _outb((length+6) & 0xFF, nic->ioaddr + DATA_1);
+ _outb((length+6) >> 8 , nic->ioaddr + DATA_1);
+
+ /* Write the contents of the packet */
+
+ /* The ethernet header first... */
+ outsw(nic->ioaddr + DATA_1, d, ETH_ALEN >> 1);
+ outsw(nic->ioaddr + DATA_1, nic->node_addr, ETH_ALEN >> 1);
+ _outw(htons(t), nic->ioaddr + DATA_1);
+
+ /* ... the data ... */
+ outsw(nic->ioaddr + DATA_1 , p, s >> 1);
+
+ /* ... and the last byte, if there is one. */
+ if ((s & 1) == 0) {
+ _outw(0, nic->ioaddr + DATA_1);
+ } else {
+ _outb(p[s-1], nic->ioaddr + DATA_1);
+ _outb(0x20, nic->ioaddr + DATA_1);
+ }
+
+ /* and let the chipset deal with it */
+ _outw(MC_ENQUEUE , nic->ioaddr + MMU_CMD);
+
+ status = 0; time_out = currticks() + 5*TICKS_PER_SEC;
+ do {
+ status = inb(nic->ioaddr + INTERRUPT);
+
+ if ((status & IM_TX_INT ) != 0) {
+ word tx_status;
+
+ /* ack interrupt */
+ _outb(IM_TX_INT, nic->ioaddr + INTERRUPT);
+
+ packet_no = inw(nic->ioaddr + FIFO_PORTS);
+ packet_no &= 0x7F;
+
+ /* select this as the packet to read from */
+ _outb( packet_no, nic->ioaddr + PNR_ARR );
+
+ /* read the first word from this packet */
+ _outw( PTR_AUTOINC | PTR_READ, nic->ioaddr + POINTER );
+
+ tx_status = inw( nic->ioaddr + DATA_1 );
+
+ if (0 == (tx_status & TS_SUCCESS)) {
+ DBG("SMC9000: TX FAIL STATUS: %hX \n", tx_status);
+ /* re-enable transmit */
+ SMC_SELECT_BANK(nic->ioaddr, 0);
+ _outw(inw(nic->ioaddr + TCR ) | TCR_ENABLE, nic->ioaddr + TCR );
+ }
+
+ /* kill the packet */
+ SMC_SELECT_BANK(nic->ioaddr, 2);
+ _outw(MC_FREEPKT, nic->ioaddr + MMU_CMD);
+
+ return;
+ }
+ }while(currticks() < time_out);
+
+ printf("SMC9000: TX timed out, resetting board\n");
+ smc_reset(nic->ioaddr);
+ return;
+}
+
+/**************************************************************************
+ * ETH_POLL - Wait for a frame
+ ***************************************************************************/
+static int smc9000_poll(struct nic *nic, int retrieve)
+{
+ SMC_SELECT_BANK(nic->ioaddr, 2);
+ if (inw(nic->ioaddr + FIFO_PORTS) & FP_RXEMPTY)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ /* start reading from the start of the packet */
+ _outw(PTR_READ | PTR_RCV | PTR_AUTOINC, nic->ioaddr + POINTER);
+
+ /* First read the status and check that we're ok */
+ if (!(inw(nic->ioaddr + DATA_1) & RS_ERRORS)) {
+ /* Next: read the packet length and mask off the top bits */
+ nic->packetlen = (inw(nic->ioaddr + DATA_1) & 0x07ff);
+
+ /* the packet length includes the 3 extra words */
+ nic->packetlen -= 6;
+#if SMC9000_DEBUG > 2
+ printf(" Reading %d words (and %d byte(s))\n",
+ (nic->packetlen >> 1), nic->packetlen & 1);
+#endif
+ /* read the packet (and the last "extra" word) */
+ insw(nic->ioaddr + DATA_1, nic->packet, (nic->packetlen+2) >> 1);
+ /* is there an odd last byte ? */
+ if (nic->packet[nic->packetlen+1] & 0x20)
+ nic->packetlen++;
+
+ /* error or good, tell the card to get rid of this packet */
+ _outw(MC_RELEASE, nic->ioaddr + MMU_CMD);
+ return 1;
+ }
+
+ printf("SMC9000: RX error\n");
+ /* error or good, tell the card to get rid of this packet */
+ _outw(MC_RELEASE, nic->ioaddr + MMU_CMD);
+ return 0;
+}
+
+static void smc9000_disable ( struct nic *nic, struct isa_device *isa __unused ) {
+
+ smc_reset(nic->ioaddr);
+
+ /* no more interrupts for me */
+ SMC_SELECT_BANK(nic->ioaddr, 2);
+ _outb( 0, nic->ioaddr + INT_MASK);
+
+ /* and tell the card to stay away from that nasty outside world */
+ SMC_SELECT_BANK(nic->ioaddr, 0);
+ _outb( RCR_CLEAR, nic->ioaddr + RCR );
+ _outb( TCR_CLEAR, nic->ioaddr + TCR );
+}
+
+static void smc9000_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct nic_operations smc9000_operations = {
+ .connect = dummy_connect,
+ .poll = smc9000_poll,
+ .transmit = smc9000_transmit,
+ .irq = smc9000_irq,
+
+};
+
+/**************************************************************************
+ * ETH_PROBE - Look for an adapter
+ ***************************************************************************/
+
+static int smc9000_probe ( struct nic *nic, struct isa_device *isa ) {
+
+ unsigned short revision;
+ int memory;
+ int media;
+ const char * version_string;
+ const char * if_string;
+ int i;
+
+ nic->irqno = 0;
+ nic->ioaddr = isa->ioaddr;
+
+ /*
+ * Get the MAC address ( bank 1, regs 4 - 9 )
+ */
+ SMC_SELECT_BANK(nic->ioaddr, 1);
+ for ( i = 0; i < 6; i += 2 ) {
+ word address;
+
+ address = inw(nic->ioaddr + ADDR0 + i);
+ nic->node_addr[i+1] = address >> 8;
+ nic->node_addr[i] = address & 0xFF;
+ }
+
+ /* get the memory information */
+ SMC_SELECT_BANK(nic->ioaddr, 0);
+ memory = ( inw(nic->ioaddr + MCR) >> 9 ) & 0x7; /* multiplier */
+ memory *= 256 * (inw(nic->ioaddr + MIR) & 0xFF);
+
+ /*
+ * Now, I want to find out more about the chip. This is sort of
+ * redundant, but it's cleaner to have it in both, rather than having
+ * one VERY long probe procedure.
+ */
+ SMC_SELECT_BANK(nic->ioaddr, 3);
+ revision = inw(nic->ioaddr + REVISION);
+ version_string = chip_ids[(revision >> 4) & 0xF];
+
+ if (((revision & 0xF0) >> 4 == CHIP_9196) &&
+ ((revision & 0x0F) >= REV_9196)) {
+ /* This is a 91c96. 'c96 has the same chip id as 'c94 (4) but
+ * a revision starting at 6 */
+ version_string = smc91c96_id;
+ }
+
+ if ( !version_string ) {
+ /* I shouldn't get here because this call was done before.... */
+ return 0;
+ }
+
+ /* is it using AUI or 10BaseT ? */
+ SMC_SELECT_BANK(nic->ioaddr, 1);
+ if (inw(nic->ioaddr + CONFIG) & CFG_AUI_SELECT)
+ media = 2;
+ else
+ media = 1;
+
+ if_string = interfaces[media - 1];
+
+ /* now, reset the chip, and put it into a known state */
+ smc_reset(nic->ioaddr);
+
+ printf("SMC9000 %s\n", smc9000_version);
+ DBG("Copyright (C) 1998 Daniel Engstr\x94m\n");
+ DBG("Copyright (C) 1996 Eric Stahlman\n");
+
+ printf("%s rev:%d I/O port:%hX Interface:%s RAM:%d bytes \n",
+ version_string, revision & 0xF,
+ nic->ioaddr, if_string, memory );
+
+ DBG ( "Ethernet MAC address: %s\n", eth_ntoa ( nic->node_addr ) );
+
+ SMC_SELECT_BANK(nic->ioaddr, 0);
+
+ /* see the header file for options in TCR/RCR NORMAL*/
+ _outw(TCR_NORMAL, nic->ioaddr + TCR);
+ _outw(RCR_NORMAL, nic->ioaddr + RCR);
+
+ /* Select which interface to use */
+ SMC_SELECT_BANK(nic->ioaddr, 1);
+ if ( media == 1 ) {
+ _outw( inw( nic->ioaddr + CONFIG ) & ~CFG_AUI_SELECT,
+ nic->ioaddr + CONFIG );
+ }
+ else if ( media == 2 ) {
+ _outw( inw( nic->ioaddr + CONFIG ) | CFG_AUI_SELECT,
+ nic->ioaddr + CONFIG );
+ }
+
+ smc_phy_configure(nic->ioaddr);
+
+ nic->nic_op = &smc9000_operations;
+ return 1;
+}
+
+/*
+ * The SMC9000 can be at any of the following port addresses. To
+ * change for a slightly different card, you can add it to the array.
+ *
+ */
+static isa_probe_addr_t smc9000_probe_addrs[] = {
+ 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,
+ 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0,
+};
+
+ISA_DRIVER ( smc9000_driver, smc9000_probe_addrs, smc9000_probe_addr,
+ GENERIC_ISAPNP_VENDOR, 0x8228 );
+
+DRIVER ( "SMC9000", nic_driver, isa_driver, smc9000_driver,
+ smc9000_probe, smc9000_disable );
+
+ISA_ROM ( "smc9000", "SMC9000" );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/smc9000.h b/gpxe/src/drivers/net/smc9000.h
new file mode 100644
index 00000000..318b7798
--- /dev/null
+++ b/gpxe/src/drivers/net/smc9000.h
@@ -0,0 +1,433 @@
+/*------------------------------------------------------------------------
+ * smc9000.h
+ *
+ * Copyright (C) 1998 by Daniel Engström
+ * Copyright (C) 1996 by Erik Stahlman
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * This file contains register information and access macros for
+ * the SMC91xxx chipset.
+ *
+ * Information contained in this file was obtained from the SMC91C94
+ * manual from SMC. To get a copy, if you really want one, you can find
+ * information under www.smsc.com in the components division.
+ * ( this thanks to advice from Donald Becker ).
+ *
+ * Authors
+ * Daniel Engström <daniel.engstrom@riksnett.no>
+ * Erik Stahlman <erik@vt.edu>
+ *
+ * History
+ * 96-01-06 Erik Stahlman moved definitions here from main .c
+ * file
+ * 96-01-19 Erik Stahlman polished this up some, and added
+ * better error handling
+ * 98-09-25 Daniel Engström adjusted for Etherboot
+ * 98-09-27 Daniel Engström moved some static strings back to the
+ * main .c file
+ * --------------------------------------------------------------------------*/
+#ifndef _SMC9000_H_
+# define _SMC9000_H_
+
+/* I want some simple types */
+typedef unsigned char byte;
+typedef unsigned short word;
+typedef unsigned long int dword;
+
+/*---------------------------------------------------------------
+ *
+ * A description of the SMC registers is probably in order here,
+ * although for details, the SMC datasheet is invaluable.
+ *
+ * Basically, the chip has 4 banks of registers ( 0 to 3 ), which
+ * are accessed by writing a number into the BANK_SELECT register
+ * ( I also use a SMC_SELECT_BANK macro for this ).
+ *
+ * The banks are configured so that for most purposes, bank 2 is all
+ * that is needed for simple run time tasks.
+ * ----------------------------------------------------------------------*/
+
+/*
+ * Bank Select Register:
+ *
+ * yyyy yyyy 0000 00xx
+ * xx = bank number
+ * yyyy yyyy = 0x33, for identification purposes.
+ */
+#define BANK_SELECT 14
+
+/* BANK 0 */
+
+#define TCR 0 /* transmit control register */
+#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */
+#define TCR_FDUPLX 0x0800 /* receive packets sent out */
+#define TCR_STP_SQET 0x1000 /* stop transmitting if Signal quality error */
+#define TCR_MON_CNS 0x0400 /* monitors the carrier status */
+#define TCR_PAD_ENABLE 0x0080 /* pads short packets to 64 bytes */
+
+#define TCR_CLEAR 0 /* do NOTHING */
+/* the normal settings for the TCR register : */
+#define TCR_NORMAL (TCR_ENABLE | TCR_PAD_ENABLE)
+
+
+#define EPH_STATUS 2
+#define ES_LINK_OK 0x4000 /* is the link integrity ok ? */
+
+#define RCR 4
+#define RCR_SOFTRESET 0x8000 /* resets the chip */
+#define RCR_STRIP_CRC 0x200 /* strips CRC */
+#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */
+#define RCR_ALMUL 0x4 /* receive all multicast packets */
+#define RCR_PROMISC 0x2 /* enable promiscuous mode */
+
+/* the normal settings for the RCR register : */
+#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE)
+#define RCR_CLEAR 0x0 /* set it to a base state */
+
+#define COUNTER 6
+#define MIR 8
+#define MCR 10
+/* 12 is reserved */
+
+// Receive/Phy Control Register
+/* BANK 0 */
+#define RPC_REG 0x000A
+#define RPC_SPEED 0x2000 // When 1 PHY is in 100Mbps mode.
+#define RPC_DPLX 0x1000 // When 1 PHY is in Full-Duplex Mode
+#define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode
+#define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb
+#define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb
+#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect
+#define RPC_LED_RES (0x01) // LED = Reserved
+#define RPC_LED_10 (0x02) // LED = 10Mbps link detect
+#define RPC_LED_FD (0x03) // LED = Full Duplex Mode
+#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred
+#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect
+#define RPC_LED_TX (0x06) // LED = TX packet occurred
+#define RPC_LED_RX (0x07) // LED = RX packet occurred
+#define RPC_DEFAULT (RPC_ANEG | (RPC_LED_100 << RPC_LSXA_SHFT) | (RPC_LED_FD << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX)
+
+// Receive/Phy Control Register
+/* BANK 0 */
+#define RPC_REG 0x000A
+#define RPC_SPEED 0x2000 // When 1 PHY is in 100Mbps mode.
+#define RPC_DPLX 0x1000 // When 1 PHY is in Full-Duplex Mode
+#define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode
+#define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb
+#define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb
+#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect
+#define RPC_LED_RES (0x01) // LED = Reserved
+#define RPC_LED_10 (0x02) // LED = 10Mbps link detect
+#define RPC_LED_FD (0x03) // LED = Full Duplex Mode
+#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred
+#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect
+#define RPC_LED_TX (0x06) // LED = TX packet occurred
+#define RPC_LED_RX (0x07) // LED = RX packet occurred
+#define RPC_DEFAULT (RPC_ANEG | (RPC_LED_100 << RPC_LSXA_SHFT) | (RPC_LED_FD << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX)
+
+/* BANK 1 */
+#define CONFIG 0
+#define CFG_AUI_SELECT 0x100
+#define BASE 2
+#define ADDR0 4
+#define ADDR1 6
+#define ADDR2 8
+#define GENERAL 10
+#define CONTROL 12
+#define CTL_POWERDOWN 0x2000
+#define CTL_LE_ENABLE 0x80
+#define CTL_CR_ENABLE 0x40
+#define CTL_TE_ENABLE 0x0020
+#define CTL_AUTO_RELEASE 0x0800
+#define CTL_EPROM_ACCESS 0x0003 /* high if Eprom is being read */
+
+/* BANK 2 */
+#define MMU_CMD 0
+#define MC_BUSY 1 /* only readable bit in the register */
+#define MC_NOP 0
+#define MC_ALLOC 0x20 /* or with number of 256 byte packets */
+#define MC_RESET 0x40
+#define MC_REMOVE 0x60 /* remove the current rx packet */
+#define MC_RELEASE 0x80 /* remove and release the current rx packet */
+#define MC_FREEPKT 0xA0 /* Release packet in PNR register */
+#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */
+
+#define PNR_ARR 2
+#define FIFO_PORTS 4
+
+#define FP_RXEMPTY 0x8000
+#define FP_TXEMPTY 0x80
+
+#define POINTER 6
+#define PTR_READ 0x2000
+#define PTR_RCV 0x8000
+#define PTR_AUTOINC 0x4000
+#define PTR_AUTO_INC 0x0040
+
+#define DATA_1 8
+#define DATA_2 10
+#define INTERRUPT 12
+
+#define INT_MASK 13
+#define IM_RCV_INT 0x1
+#define IM_TX_INT 0x2
+#define IM_TX_EMPTY_INT 0x4
+#define IM_ALLOC_INT 0x8
+#define IM_RX_OVRN_INT 0x10
+#define IM_EPH_INT 0x20
+#define IM_ERCV_INT 0x40 /* not on SMC9192 */
+
+/* BANK 3 */
+#define MULTICAST1 0
+#define MULTICAST2 2
+#define MULTICAST3 4
+#define MULTICAST4 6
+#define MGMT 8
+#define REVISION 10 /* ( hi: chip id low: rev # ) */
+
+// Management Interface Register (MII)
+#define MII_REG 0x0008
+#define MII_MSK_CRS100 0x4000 // Disables CRS100 detection during tx half dup
+#define MII_MDOE 0x0008 // MII Output Enable
+#define MII_MCLK 0x0004 // MII Clock, pin MDCLK
+#define MII_MDI 0x0002 // MII Input, pin MDI
+#define MII_MDO 0x0001 // MII Output, pin MDO
+
+/* this is NOT on SMC9192 */
+#define ERCV 12
+
+/* Note that 9194 and 9196 have the smame chip id,
+ * the 9196 will have revisions starting at 6 */
+#define CHIP_9190 3
+#define CHIP_9194 4
+#define CHIP_9195 5
+#define CHIP_9196 4
+#define CHIP_91100 7
+#define CHIP_91100FD 8
+
+#define REV_9196 6
+
+/*
+ * Transmit status bits
+ */
+#define TS_SUCCESS 0x0001
+#define TS_LOSTCAR 0x0400
+#define TS_LATCOL 0x0200
+#define TS_16COL 0x0010
+
+/*
+ * Receive status bits
+ */
+#define RS_ALGNERR 0x8000
+#define RS_BADCRC 0x2000
+#define RS_ODDFRAME 0x1000
+#define RS_TOOLONG 0x0800
+#define RS_TOOSHORT 0x0400
+#define RS_MULTICAST 0x0001
+#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+// Management Interface Register (MII)
+#define MII_REG 0x0008
+#define MII_MSK_CRS100 0x4000 // Disables CRS100 detection during tx half dup
+#define MII_MDOE 0x0008 // MII Output Enable
+#define MII_MCLK 0x0004 // MII Clock, pin MDCLK
+#define MII_MDI 0x0002 // MII Input, pin MDI
+#define MII_MDO 0x0001 // MII Output, pin MDO
+
+// PHY Register Addresses (LAN91C111 Internal PHY)
+
+// PHY Control Register
+#define PHY_CNTL_REG 0x00
+#define PHY_CNTL_RST 0x8000 // 1=PHY Reset
+#define PHY_CNTL_LPBK 0x4000 // 1=PHY Loopback
+#define PHY_CNTL_SPEED 0x2000 // 1=100Mbps, 0=10Mpbs
+#define PHY_CNTL_ANEG_EN 0x1000 // 1=Enable Auto negotiation
+#define PHY_CNTL_PDN 0x0800 // 1=PHY Power Down mode
+#define PHY_CNTL_MII_DIS 0x0400 // 1=MII 4 bit interface disabled
+#define PHY_CNTL_ANEG_RST 0x0200 // 1=Reset Auto negotiate
+#define PHY_CNTL_DPLX 0x0100 // 1=Full Duplex, 0=Half Duplex
+#define PHY_CNTL_COLTST 0x0080 // 1= MII Colision Test
+
+// PHY Status Register
+#define PHY_STAT_REG 0x01
+#define PHY_STAT_CAP_T4 0x8000 // 1=100Base-T4 capable
+#define PHY_STAT_CAP_TXF 0x4000 // 1=100Base-X full duplex capable
+#define PHY_STAT_CAP_TXH 0x2000 // 1=100Base-X half duplex capable
+#define PHY_STAT_CAP_TF 0x1000 // 1=10Mbps full duplex capable
+#define PHY_STAT_CAP_TH 0x0800 // 1=10Mbps half duplex capable
+#define PHY_STAT_CAP_SUPR 0x0040 // 1=recv mgmt frames with not preamble
+#define PHY_STAT_ANEG_ACK 0x0020 // 1=ANEG has completed
+#define PHY_STAT_REM_FLT 0x0010 // 1=Remote Fault detected
+#define PHY_STAT_CAP_ANEG 0x0008 // 1=Auto negotiate capable
+#define PHY_STAT_LINK 0x0004 // 1=valid link
+#define PHY_STAT_JAB 0x0002 // 1=10Mbps jabber condition
+#define PHY_STAT_EXREG 0x0001 // 1=extended registers implemented
+
+// PHY Identifier Registers
+#define PHY_ID1_REG 0x02 // PHY Identifier 1
+#define PHY_ID2_REG 0x03 // PHY Identifier 2
+
+// PHY Auto-Negotiation Advertisement Register
+#define PHY_AD_REG 0x04
+#define PHY_AD_NP 0x8000 // 1=PHY requests exchange of Next Page
+#define PHY_AD_ACK 0x4000 // 1=got link code word from remote
+#define PHY_AD_RF 0x2000 // 1=advertise remote fault
+#define PHY_AD_T4 0x0200 // 1=PHY is capable of 100Base-T4
+#define PHY_AD_TX_FDX 0x0100 // 1=PHY is capable of 100Base-TX FDPLX
+#define PHY_AD_TX_HDX 0x0080 // 1=PHY is capable of 100Base-TX HDPLX
+#define PHY_AD_10_FDX 0x0040 // 1=PHY is capable of 10Base-T FDPLX
+#define PHY_AD_10_HDX 0x0020 // 1=PHY is capable of 10Base-T HDPLX
+#define PHY_AD_CSMA 0x0001 // 1=PHY is capable of 802.3 CMSA
+
+// PHY Auto-negotiation Remote End Capability Register
+#define PHY_RMT_REG 0x05
+// Uses same bit definitions as PHY_AD_REG
+
+// PHY Configuration Register 1
+#define PHY_CFG1_REG 0x10
+#define PHY_CFG1_LNKDIS 0x8000 // 1=Rx Link Detect Function disabled
+#define PHY_CFG1_XMTDIS 0x4000 // 1=TP Transmitter Disabled
+#define PHY_CFG1_XMTPDN 0x2000 // 1=TP Transmitter Powered Down
+#define PHY_CFG1_BYPSCR 0x0400 // 1=Bypass scrambler/descrambler
+#define PHY_CFG1_UNSCDS 0x0200 // 1=Unscramble Idle Reception Disable
+#define PHY_CFG1_EQLZR 0x0100 // 1=Rx Equalizer Disabled
+#define PHY_CFG1_CABLE 0x0080 // 1=STP(150ohm), 0=UTP(100ohm)
+#define PHY_CFG1_RLVL0 0x0040 // 1=Rx Squelch level reduced by 4.5db
+#define PHY_CFG1_TLVL_SHIFT 2 // Transmit Output Level Adjust
+#define PHY_CFG1_TLVL_MASK 0x003C
+#define PHY_CFG1_TRF_MASK 0x0003 // Transmitter Rise/Fall time
+
+
+// PHY Configuration Register 2
+#define PHY_CFG2_REG 0x11
+#define PHY_CFG2_APOLDIS 0x0020 // 1=Auto Polarity Correction disabled
+#define PHY_CFG2_JABDIS 0x0010 // 1=Jabber disabled
+#define PHY_CFG2_MREG 0x0008 // 1=Multiple register access (MII mgt)
+#define PHY_CFG2_INTMDIO 0x0004 // 1=Interrupt signaled with MDIO pulseo
+
+// PHY Status Output (and Interrupt status) Register
+#define PHY_INT_REG 0x12 // Status Output (Interrupt Status)
+#define PHY_INT_INT 0x8000 // 1=bits have changed since last read
+#define PHY_INT_LNKFAIL 0x4000 // 1=Link Not detected
+#define PHY_INT_LOSSSYNC 0x2000 // 1=Descrambler has lost sync
+#define PHY_INT_CWRD 0x1000 // 1=Invalid 4B5B code detected on rx
+#define PHY_INT_SSD 0x0800 // 1=No Start Of Stream detected on rx
+#define PHY_INT_ESD 0x0400 // 1=No End Of Stream detected on rx
+#define PHY_INT_RPOL 0x0200 // 1=Reverse Polarity detected
+#define PHY_INT_JAB 0x0100 // 1=Jabber detected
+#define PHY_INT_SPDDET 0x0080 // 1=100Base-TX mode, 0=10Base-T mode
+#define PHY_INT_DPLXDET 0x0040 // 1=Device in Full Duplex
+
+// PHY Interrupt/Status Mask Register
+#define PHY_MASK_REG 0x13 // Interrupt Mask
+// Uses the same bit definitions as PHY_INT_REG
+
+
+// PHY Register Addresses (LAN91C111 Internal PHY)
+
+// PHY Control Register
+#define PHY_CNTL_REG 0x00
+#define PHY_CNTL_RST 0x8000 // 1=PHY Reset
+#define PHY_CNTL_LPBK 0x4000 // 1=PHY Loopback
+#define PHY_CNTL_SPEED 0x2000 // 1=100Mbps, 0=10Mpbs
+#define PHY_CNTL_ANEG_EN 0x1000 // 1=Enable Auto negotiation
+#define PHY_CNTL_PDN 0x0800 // 1=PHY Power Down mode
+#define PHY_CNTL_MII_DIS 0x0400 // 1=MII 4 bit interface disabled
+#define PHY_CNTL_ANEG_RST 0x0200 // 1=Reset Auto negotiate
+#define PHY_CNTL_DPLX 0x0100 // 1=Full Duplex, 0=Half Duplex
+#define PHY_CNTL_COLTST 0x0080 // 1= MII Colision Test
+
+// PHY Status Register
+#define PHY_STAT_REG 0x01
+#define PHY_STAT_CAP_T4 0x8000 // 1=100Base-T4 capable
+#define PHY_STAT_CAP_TXF 0x4000 // 1=100Base-X full duplex capable
+#define PHY_STAT_CAP_TXH 0x2000 // 1=100Base-X half duplex capable
+#define PHY_STAT_CAP_TF 0x1000 // 1=10Mbps full duplex capable
+#define PHY_STAT_CAP_TH 0x0800 // 1=10Mbps half duplex capable
+#define PHY_STAT_CAP_SUPR 0x0040 // 1=recv mgmt frames with not preamble
+#define PHY_STAT_ANEG_ACK 0x0020 // 1=ANEG has completed
+#define PHY_STAT_REM_FLT 0x0010 // 1=Remote Fault detected
+#define PHY_STAT_CAP_ANEG 0x0008 // 1=Auto negotiate capable
+#define PHY_STAT_LINK 0x0004 // 1=valid link
+#define PHY_STAT_JAB 0x0002 // 1=10Mbps jabber condition
+#define PHY_STAT_EXREG 0x0001 // 1=extended registers implemented
+
+// PHY Identifier Registers
+#define PHY_ID1_REG 0x02 // PHY Identifier 1
+#define PHY_ID2_REG 0x03 // PHY Identifier 2
+
+// PHY Auto-Negotiation Advertisement Register
+#define PHY_AD_REG 0x04
+#define PHY_AD_NP 0x8000 // 1=PHY requests exchange of Next Page
+#define PHY_AD_ACK 0x4000 // 1=got link code word from remote
+#define PHY_AD_RF 0x2000 // 1=advertise remote fault
+#define PHY_AD_T4 0x0200 // 1=PHY is capable of 100Base-T4
+#define PHY_AD_TX_FDX 0x0100 // 1=PHY is capable of 100Base-TX FDPLX
+#define PHY_AD_TX_HDX 0x0080 // 1=PHY is capable of 100Base-TX HDPLX
+#define PHY_AD_10_FDX 0x0040 // 1=PHY is capable of 10Base-T FDPLX
+#define PHY_AD_10_HDX 0x0020 // 1=PHY is capable of 10Base-T HDPLX
+#define PHY_AD_CSMA 0x0001 // 1=PHY is capable of 802.3 CMSA
+
+// PHY Auto-negotiation Remote End Capability Register
+#define PHY_RMT_REG 0x05
+// Uses same bit definitions as PHY_AD_REG
+
+// PHY Configuration Register 1
+#define PHY_CFG1_REG 0x10
+#define PHY_CFG1_LNKDIS 0x8000 // 1=Rx Link Detect Function disabled
+#define PHY_CFG1_XMTDIS 0x4000 // 1=TP Transmitter Disabled
+#define PHY_CFG1_XMTPDN 0x2000 // 1=TP Transmitter Powered Down
+#define PHY_CFG1_BYPSCR 0x0400 // 1=Bypass scrambler/descrambler
+#define PHY_CFG1_UNSCDS 0x0200 // 1=Unscramble Idle Reception Disable
+#define PHY_CFG1_EQLZR 0x0100 // 1=Rx Equalizer Disabled
+#define PHY_CFG1_CABLE 0x0080 // 1=STP(150ohm), 0=UTP(100ohm)
+#define PHY_CFG1_RLVL0 0x0040 // 1=Rx Squelch level reduced by 4.5db
+#define PHY_CFG1_TLVL_SHIFT 2 // Transmit Output Level Adjust
+#define PHY_CFG1_TLVL_MASK 0x003C
+#define PHY_CFG1_TRF_MASK 0x0003 // Transmitter Rise/Fall time
+
+
+// PHY Configuration Register 2
+#define PHY_CFG2_REG 0x11
+#define PHY_CFG2_APOLDIS 0x0020 // 1=Auto Polarity Correction disabled
+#define PHY_CFG2_JABDIS 0x0010 // 1=Jabber disabled
+#define PHY_CFG2_MREG 0x0008 // 1=Multiple register access (MII mgt)
+#define PHY_CFG2_INTMDIO 0x0004 // 1=Interrupt signaled with MDIO pulseo
+
+// PHY Status Output (and Interrupt status) Register
+#define PHY_INT_REG 0x12 // Status Output (Interrupt Status)
+#define PHY_INT_INT 0x8000 // 1=bits have changed since last read
+#define PHY_INT_LNKFAIL 0x4000 // 1=Link Not detected
+#define PHY_INT_LOSSSYNC 0x2000 // 1=Descrambler has lost sync
+#define PHY_INT_CWRD 0x1000 // 1=Invalid 4B5B code detected on rx
+#define PHY_INT_SSD 0x0800 // 1=No Start Of Stream detected on rx
+#define PHY_INT_ESD 0x0400 // 1=No End Of Stream detected on rx
+#define PHY_INT_RPOL 0x0200 // 1=Reverse Polarity detected
+#define PHY_INT_JAB 0x0100 // 1=Jabber detected
+#define PHY_INT_SPDDET 0x0080 // 1=100Base-TX mode, 0=10Base-T mode
+#define PHY_INT_DPLXDET 0x0040 // 1=Device in Full Duplex
+
+// PHY Interrupt/Status Mask Register
+#define PHY_MASK_REG 0x13 // Interrupt Mask
+// Uses the same bit definitions as PHY_INT_REG
+
+
+/*-------------------------------------------------------------------------
+ * I define some macros to make it easier to do somewhat common
+ * or slightly complicated, repeated tasks.
+ --------------------------------------------------------------------------*/
+
+/* select a register bank, 0 to 3 */
+
+#define SMC_SELECT_BANK(x, y) { _outw( y, x + BANK_SELECT ); }
+
+/* define a small delay for the reset */
+#define SMC_DELAY(x) { inw( x + RCR );\
+ inw( x + RCR );\
+ inw( x + RCR ); }
+
+
+#endif /* _SMC_9000_H_ */
+
diff --git a/gpxe/src/drivers/net/sundance.c b/gpxe/src/drivers/net/sundance.c
new file mode 100644
index 00000000..3b6f5a88
--- /dev/null
+++ b/gpxe/src/drivers/net/sundance.c
@@ -0,0 +1,885 @@
+/**************************************************************************
+*
+* sundance.c -- Etherboot device driver for the Sundance ST201 "Alta".
+* Written 2002-2002 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* sundance.c: A Linux device driver for the Sundance ST201 "Alta"
+* Written 1999-2002 by Donald Becker
+*
+* tulip.c: Tulip and Clone Etherboot Driver
+* By Marty Conner
+* Copyright (C) 2001 Entity Cyber, Inc.
+*
+* Linux Driver Version LK1.09a, 10-Jul-2003 (2.4.25)
+*
+* REVISION HISTORY:
+* ================
+* v1.1 01-01-2003 timlegge Initial implementation
+* v1.7 04-10-2003 timlegge Transfers Linux Kernel (30 sec)
+* v1.8 04-13-2003 timlegge Fix multiple transmission bug
+* v1.9 08-19-2003 timlegge Support Multicast
+* v1.10 01-17-2004 timlegge Initial driver output cleanup
+* v1.11 03-21-2004 timlegge Remove unused variables
+* v1.12 03-21-2004 timlegge Remove excess MII defines
+* v1.13 03-24-2004 timlegge Update to Linux 2.4.25 driver
+*
+****************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include <gpxe/pci.h>
+#include "mii.h"
+
+#define drv_version "v1.12"
+#define drv_date "2004-03-21"
+
+#define HZ 100
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+/* May need to be moved to mii.h */
+struct mii_if_info {
+ int phy_id;
+ int advertising;
+ unsigned int full_duplex:1; /* is full duplex? */
+};
+
+/* Set the mtu */
+static int mtu = 1514;
+
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
+ The sundance uses a 64 element hash table based on the Ethernet CRC. */
+// static int multicast_filter_limit = 32;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ Setting to > 1518 effectively disables this feature.
+ This chip can receive into any byte alignment buffers, so word-oriented
+ archs do not need a copy-align of the IP header. */
+static int rx_copybreak = 0;
+static int flowctrl = 1;
+
+/* Allow forcing the media type */
+/* media[] specifies the media type the NIC operates at.
+ autosense Autosensing active media.
+ 10mbps_hd 10Mbps half duplex.
+ 10mbps_fd 10Mbps full duplex.
+ 100mbps_hd 100Mbps half duplex.
+ 100mbps_fd 100Mbps full duplex.
+*/
+static char media[] = "autosense";
+
+/* Operational parameters that are set at compile time. */
+
+/* As Etherboot uses a Polling driver we can keep the number of rings
+to the minimum number required. In general that is 1 transmit and 4 receive receive rings. However some cards require that
+there be a minimum of 2 rings */
+#define TX_RING_SIZE 2
+#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */
+#define RX_RING_SIZE 4
+
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIME_OUT (4*HZ)
+#define PKT_BUF_SZ 1536
+
+/* Offsets to the device registers.
+ Unlike software-only systems, device drivers interact with complex hardware.
+ It's not useful to define symbolic names for every register bit in the
+ device. The name can only partially document the semantics and make
+ the driver longer and more difficult to read.
+ In general, only the important configuration values or bits changed
+ multiple times should be defined symbolically.
+*/
+enum alta_offsets {
+ DMACtrl = 0x00,
+ TxListPtr = 0x04,
+ TxDMABurstThresh = 0x08,
+ TxDMAUrgentThresh = 0x09,
+ TxDMAPollPeriod = 0x0a,
+ RxDMAStatus = 0x0c,
+ RxListPtr = 0x10,
+ DebugCtrl0 = 0x1a,
+ DebugCtrl1 = 0x1c,
+ RxDMABurstThresh = 0x14,
+ RxDMAUrgentThresh = 0x15,
+ RxDMAPollPeriod = 0x16,
+ LEDCtrl = 0x1a,
+ ASICCtrl = 0x30,
+ EEData = 0x34,
+ EECtrl = 0x36,
+ TxStartThresh = 0x3c,
+ RxEarlyThresh = 0x3e,
+ FlashAddr = 0x40,
+ FlashData = 0x44,
+ TxStatus = 0x46,
+ TxFrameId = 0x47,
+ DownCounter = 0x18,
+ IntrClear = 0x4a,
+ IntrEnable = 0x4c,
+ IntrStatus = 0x4e,
+ MACCtrl0 = 0x50,
+ MACCtrl1 = 0x52,
+ StationAddr = 0x54,
+ MaxFrameSize = 0x5A,
+ RxMode = 0x5c,
+ MIICtrl = 0x5e,
+ MulticastFilter0 = 0x60,
+ MulticastFilter1 = 0x64,
+ RxOctetsLow = 0x68,
+ RxOctetsHigh = 0x6a,
+ TxOctetsLow = 0x6c,
+ TxOctetsHigh = 0x6e,
+ TxFramesOK = 0x70,
+ RxFramesOK = 0x72,
+ StatsCarrierError = 0x74,
+ StatsLateColl = 0x75,
+ StatsMultiColl = 0x76,
+ StatsOneColl = 0x77,
+ StatsTxDefer = 0x78,
+ RxMissed = 0x79,
+ StatsTxXSDefer = 0x7a,
+ StatsTxAbort = 0x7b,
+ StatsBcastTx = 0x7c,
+ StatsBcastRx = 0x7d,
+ StatsMcastTx = 0x7e,
+ StatsMcastRx = 0x7f,
+ /* Aliased and bogus values! */
+ RxStatus = 0x0c,
+};
+enum ASICCtrl_HiWord_bit {
+ GlobalReset = 0x0001,
+ RxReset = 0x0002,
+ TxReset = 0x0004,
+ DMAReset = 0x0008,
+ FIFOReset = 0x0010,
+ NetworkReset = 0x0020,
+ HostReset = 0x0040,
+ ResetBusy = 0x0400,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum intr_status_bits {
+ IntrSummary = 0x0001, IntrPCIErr = 0x0002, IntrMACCtrl = 0x0008,
+ IntrTxDone = 0x0004, IntrRxDone = 0x0010, IntrRxStart = 0x0020,
+ IntrDrvRqst = 0x0040,
+ StatsMax = 0x0080, LinkChange = 0x0100,
+ IntrTxDMADone = 0x0200, IntrRxDMADone = 0x0400,
+};
+
+/* Bits in the RxMode register. */
+enum rx_mode_bits {
+ AcceptAllIPMulti = 0x20, AcceptMultiHash = 0x10, AcceptAll = 0x08,
+ AcceptBroadcast = 0x04, AcceptMulticast = 0x02, AcceptMyPhys =
+ 0x01,
+};
+/* Bits in MACCtrl. */
+enum mac_ctrl0_bits {
+ EnbFullDuplex = 0x20, EnbRcvLargeFrame = 0x40,
+ EnbFlowCtrl = 0x100, EnbPassRxCRC = 0x200,
+};
+enum mac_ctrl1_bits {
+ StatsEnable = 0x0020, StatsDisable = 0x0040, StatsEnabled = 0x0080,
+ TxEnable = 0x0100, TxDisable = 0x0200, TxEnabled = 0x0400,
+ RxEnable = 0x0800, RxDisable = 0x1000, RxEnabled = 0x2000,
+};
+
+/* The Rx and Tx buffer descriptors.
+ Using only 32 bit fields simplifies software endian correction.
+ This structure must be aligned, and should avoid spanning cache lines.
+*/
+struct netdev_desc {
+ u32 next_desc;
+ u32 status;
+ u32 addr;
+ u32 length;
+};
+
+/* Bits in netdev_desc.status */
+enum desc_status_bits {
+ DescOwn = 0x8000,
+ DescEndPacket = 0x4000,
+ DescEndRing = 0x2000,
+ LastFrag = 0x80000000,
+ DescIntrOnTx = 0x8000,
+ DescIntrOnDMADone = 0x80000000,
+ DisableAlign = 0x00000001,
+};
+
+/**********************************************
+* Descriptor Ring and Buffer defination
+***********************************************/
+/* Define the TX Descriptor */
+static struct netdev_desc tx_ring[TX_RING_SIZE];
+
+/* Define the RX Descriptor */
+static struct netdev_desc rx_ring[RX_RING_SIZE];
+
+/* Create a static buffer of size PKT_BUF_SZ for each RX and TX descriptor.
+ All descriptors point to a part of this buffer */
+struct {
+ unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE];
+ unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ];
+} rx_tx_buf __shared;
+#define rxb rx_tx_buf.rxb
+#define txb rx_tx_buf.txb
+
+/* FIXME: Move BASE to the private structure */
+static u32 BASE;
+#define EEPROM_SIZE 128
+
+enum pci_id_flags_bits {
+ PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
+ PCI_ADDR0 = 0 << 4, PCI_ADDR1 = 1 << 4, PCI_ADDR2 =
+ 2 << 4, PCI_ADDR3 = 3 << 4,
+};
+
+enum chip_capability_flags { CanHaveMII = 1, KendinPktDropBug = 2, };
+#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0)
+
+#define MII_CNT 4
+static struct sundance_private {
+ const char *nic_name;
+ /* Frequently used values */
+
+ unsigned int cur_rx; /* Producer/consumer ring indicies */
+ unsigned int mtu;
+
+ /* These values keep track of the tranceiver/media in use */
+ unsigned int flowctrl:1;
+ unsigned int an_enable:1;
+
+ unsigned int speed;
+
+ /* MII tranceiver section */
+ struct mii_if_info mii_if;
+ int mii_preamble_required;
+ unsigned char phys[MII_CNT];
+ unsigned char pci_rev_id;
+} sdx;
+
+static struct sundance_private *sdc;
+
+/* Station Address location within the EEPROM */
+#define EEPROM_SA_OFFSET 0x10
+#define DEFAULT_INTR (IntrRxDMADone | IntrPCIErr | \
+ IntrDrvRqst | IntrTxDone | StatsMax | \
+ LinkChange)
+
+static int eeprom_read(long ioaddr, int location);
+static int mdio_read(struct nic *nic, int phy_id, unsigned int location);
+static void mdio_write(struct nic *nic, int phy_id, unsigned int location,
+ int value);
+static void set_rx_mode(struct nic *nic);
+
+static void check_duplex(struct nic *nic)
+{
+ int mii_lpa = mdio_read(nic, sdc->phys[0], MII_LPA);
+ int negotiated = mii_lpa & sdc->mii_if.advertising;
+ int duplex;
+
+ /* Force media */
+ if (!sdc->an_enable || mii_lpa == 0xffff) {
+ if (sdc->mii_if.full_duplex)
+ outw(inw(BASE + MACCtrl0) | EnbFullDuplex,
+ BASE + MACCtrl0);
+ return;
+ }
+
+ /* Autonegotiation */
+ duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+ if (sdc->mii_if.full_duplex != duplex) {
+ sdc->mii_if.full_duplex = duplex;
+ DBG ("%s: Setting %s-duplex based on MII #%d "
+ "negotiated capability %4.4x.\n", sdc->nic_name,
+ duplex ? "full" : "half", sdc->phys[0],
+ negotiated );
+ outw(inw(BASE + MACCtrl0) | duplex ? 0x20 : 0,
+ BASE + MACCtrl0);
+ }
+}
+
+
+/**************************************************************************
+ * init_ring - setup the tx and rx descriptors
+ *************************************************************************/
+static void init_ring(struct nic *nic __unused)
+{
+ int i;
+
+ sdc->cur_rx = 0;
+
+ /* Initialize all the Rx descriptors */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_ring[i].next_desc = virt_to_le32desc(&rx_ring[i + 1]);
+ rx_ring[i].status = 0;
+ rx_ring[i].length = 0;
+ rx_ring[i].addr = 0;
+ }
+
+ /* Mark the last entry as wrapping the ring */
+ rx_ring[i - 1].next_desc = virt_to_le32desc(&rx_ring[0]);
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_ring[i].addr = virt_to_le32desc(&rxb[i * PKT_BUF_SZ]);
+ rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LastFrag);
+ }
+
+ /* We only use one transmit buffer, but two
+ * descriptors so transmit engines have somewhere
+ * to point should they feel the need */
+ tx_ring[0].status = 0x00000000;
+ tx_ring[0].addr = virt_to_bus(&txb[0]);
+ tx_ring[0].next_desc = 0; /* virt_to_bus(&tx_ring[1]); */
+
+ /* This descriptor is never used */
+ tx_ring[1].status = 0x00000000;
+ tx_ring[1].addr = 0; /*virt_to_bus(&txb[0]); */
+ tx_ring[1].next_desc = 0;
+
+ /* Mark the last entry as wrapping the ring,
+ * though this should never happen */
+ tx_ring[1].length = cpu_to_le32(LastFrag | PKT_BUF_SZ);
+}
+
+/**************************************************************************
+ * RESET - Reset Adapter
+ * ***********************************************************************/
+static void sundance_reset(struct nic *nic)
+{
+ int i;
+
+ init_ring(nic);
+
+ outl(virt_to_le32desc(&rx_ring[0]), BASE + RxListPtr);
+ /* The Tx List Pointer is written as packets are queued */
+
+ /* Initialize other registers. */
+ /* __set_mac_addr(dev); */
+ {
+ u16 addr16;
+
+ addr16 = (nic->node_addr[0] | (nic->node_addr[1] << 8));
+ outw(addr16, BASE + StationAddr);
+ addr16 = (nic->node_addr[2] | (nic->node_addr[3] << 8));
+ outw(addr16, BASE + StationAddr + 2);
+ addr16 = (nic->node_addr[4] | (nic->node_addr[5] << 8));
+ outw(addr16, BASE + StationAddr + 4);
+ }
+
+ outw(sdc->mtu + 14, BASE + MaxFrameSize);
+ if (sdc->mtu > 2047) /* this will never happen with default options */
+ outl(inl(BASE + ASICCtrl) | 0x0c, BASE + ASICCtrl);
+
+ set_rx_mode(nic);
+
+ outw(0, BASE + DownCounter);
+ /* Set the chip to poll every N*30nsec */
+ outb(100, BASE + RxDMAPollPeriod);
+
+ /* Fix DFE-580TX packet drop issue */
+ if (sdc->pci_rev_id >= 0x14)
+ writeb(0x01, BASE + DebugCtrl1);
+
+ outw(RxEnable | TxEnable, BASE + MACCtrl1);
+
+ /* Construct a perfect filter frame with the mac address as first match
+ * and broadcast for all others */
+ for (i = 0; i < 192; i++)
+ txb[i] = 0xFF;
+
+ txb[0] = nic->node_addr[0];
+ txb[1] = nic->node_addr[1];
+ txb[2] = nic->node_addr[2];
+ txb[3] = nic->node_addr[3];
+ txb[4] = nic->node_addr[4];
+ txb[5] = nic->node_addr[5];
+
+ DBG ( "%s: Done sundance_reset, status: Rx %hX Tx %hX "
+ "MAC Control %hX, %hX %hX\n",
+ sdc->nic_name, (int) inl(BASE + RxStatus),
+ (int) inw(BASE + TxStatus), (int) inl(BASE + MACCtrl0),
+ (int) inw(BASE + MACCtrl1), (int) inw(BASE + MACCtrl0) );
+}
+
+/**************************************************************************
+IRQ - Wait for a frame
+***************************************************************************/
+static void sundance_irq ( struct nic *nic, irq_action_t action ) {
+ unsigned int intr_status;
+
+ switch ( action ) {
+ case DISABLE :
+ case ENABLE :
+ intr_status = inw(nic->ioaddr + IntrStatus);
+ intr_status = intr_status & ~DEFAULT_INTR;
+ if ( action == ENABLE )
+ intr_status = intr_status | DEFAULT_INTR;
+ outw(intr_status, nic->ioaddr + IntrEnable);
+ break;
+ case FORCE :
+ outw(0x0200, BASE + ASICCtrl);
+ break;
+ }
+}
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int sundance_poll(struct nic *nic, int retreive)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+ int entry = sdc->cur_rx % RX_RING_SIZE;
+ u32 frame_status = le32_to_cpu(rx_ring[entry].status);
+ int intr_status;
+ int pkt_len = 0;
+
+ if (!(frame_status & DescOwn))
+ return 0;
+
+ /* There is a packet ready */
+ if(!retreive)
+ return 1;
+
+ intr_status = inw(nic->ioaddr + IntrStatus);
+ outw(intr_status, nic->ioaddr + IntrStatus);
+
+ pkt_len = frame_status & 0x1fff;
+
+ if (frame_status & 0x001f4000) {
+ DBG ( "Polling frame_status error\n" ); /* Do we really care about this */
+ } else {
+ if (pkt_len < rx_copybreak) {
+ /* FIXME: What should happen Will this ever occur */
+ printf("Poll Error: pkt_len < rx_copybreak");
+ } else {
+ nic->packetlen = pkt_len;
+ memcpy(nic->packet, rxb +
+ (sdc->cur_rx * PKT_BUF_SZ), nic->packetlen);
+
+ }
+ }
+ rx_ring[entry].length = cpu_to_le32(PKT_BUF_SZ | LastFrag);
+ rx_ring[entry].status = 0;
+ entry++;
+ sdc->cur_rx = entry % RX_RING_SIZE;
+ outw(DEFAULT_INTR & ~(IntrRxDone|IntrRxDMADone),
+ nic->ioaddr + IntrStatus);
+ return 1;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void sundance_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ u16 nstype;
+ u32 to;
+
+ /* Disable the Tx */
+ outw(TxDisable, BASE + MACCtrl1);
+
+ memcpy(txb, d, ETH_ALEN);
+ memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) t);
+ memcpy(txb + 2 * ETH_ALEN, (u8 *) & nstype, 2);
+ memcpy(txb + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ s &= 0x0FFF;
+ while (s < ETH_ZLEN)
+ txb[s++] = '\0';
+
+ /* Setup the transmit descriptor */
+ tx_ring[0].length = cpu_to_le32(s | LastFrag);
+ tx_ring[0].status = cpu_to_le32(0x00000001);
+
+ /* Point to transmit descriptor */
+ outl(virt_to_le32desc(&tx_ring[0]), BASE + TxListPtr);
+
+ /* Enable Tx */
+ outw(TxEnable, BASE + MACCtrl1);
+ /* Trigger an immediate send */
+ outw(0, BASE + TxStatus);
+
+ to = currticks() + TX_TIME_OUT;
+ while (!(tx_ring[0].status & 0x00010000) && (currticks() < to)); /* wait */
+
+ if (currticks() >= to) {
+ printf("TX Time Out");
+ }
+ /* Disable Tx */
+ outw(TxDisable, BASE + MACCtrl1);
+
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void sundance_disable ( struct nic *nic __unused ) {
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+ outw(0x0000, BASE + IntrEnable);
+ /* Stop the Chipchips Tx and Rx Status */
+ outw(TxDisable | RxDisable | StatsDisable, BASE + MACCtrl1);
+}
+
+static struct nic_operations sundance_operations = {
+ .connect = dummy_connect,
+ .poll = sundance_poll,
+ .transmit = sundance_transmit,
+ .irq = sundance_irq,
+
+};
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int sundance_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ u8 ee_data[EEPROM_SIZE];
+ u16 mii_ctl;
+ int i;
+ int speed;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ /* BASE is used throughout to address the card */
+ BASE = pci->ioaddr;
+ printf(" sundance.c: Found %s Vendor=0x%hX Device=0x%hX\n",
+ pci->driver_name, pci->vendor, pci->device);
+
+ /* Get the MAC Address by reading the EEPROM */
+ for (i = 0; i < 3; i++) {
+ ((u16 *) ee_data)[i] =
+ le16_to_cpu(eeprom_read(BASE, i + EEPROM_SA_OFFSET));
+ }
+ /* Update the nic structure with the MAC Address */
+ for (i = 0; i < ETH_ALEN; i++) {
+ nic->node_addr[i] = ee_data[i];
+ }
+
+ /* Set the card as PCI Bus Master */
+ adjust_pci_device(pci);
+
+// sdc->mii_if.dev = pci;
+// sdc->mii_if.phy_id_mask = 0x1f;
+// sdc->mii_if.reg_num_mask = 0x1f;
+
+ /* point to private storage */
+ sdc = &sdx;
+
+ sdc->nic_name = pci->driver_name;
+ sdc->mtu = mtu;
+
+ pci_read_config_byte(pci, PCI_REVISION_ID, &sdc->pci_rev_id);
+
+ DBG ( "Device revision id: %hx\n", sdc->pci_rev_id );
+
+ /* Print out some hardware info */
+ DBG ( "%s: %s at ioaddr %hX, ", pci->driver_name, nic->node_addr, (unsigned int) BASE);
+
+ sdc->mii_preamble_required = 0;
+ if (1) {
+ int phy, phy_idx = 0;
+ sdc->phys[0] = 1; /* Default Setting */
+ sdc->mii_preamble_required++;
+ for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
+ int mii_status = mdio_read(nic, phy, MII_BMSR);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ sdc->phys[phy_idx++] = phy;
+ sdc->mii_if.advertising =
+ mdio_read(nic, phy, MII_ADVERTISE);
+ if ((mii_status & 0x0040) == 0)
+ sdc->mii_preamble_required++;
+ DBG
+ ( "%s: MII PHY found at address %d, status " "%hX advertising %hX\n", sdc->nic_name, phy, mii_status, sdc->mii_if.advertising );
+ }
+ }
+ sdc->mii_preamble_required--;
+ if (phy_idx == 0)
+ printf("%s: No MII transceiver found!\n",
+ sdc->nic_name);
+ sdc->mii_if.phy_id = sdc->phys[0];
+ }
+
+ /* Parse override configuration */
+ sdc->an_enable = 1;
+ if (strcasecmp(media, "autosense") != 0) {
+ sdc->an_enable = 0;
+ if (strcasecmp(media, "100mbps_fd") == 0 ||
+ strcasecmp(media, "4") == 0) {
+ sdc->speed = 100;
+ sdc->mii_if.full_duplex = 1;
+ } else if (strcasecmp(media, "100mbps_hd") == 0
+ || strcasecmp(media, "3") == 0) {
+ sdc->speed = 100;
+ sdc->mii_if.full_duplex = 0;
+ } else if (strcasecmp(media, "10mbps_fd") == 0 ||
+ strcasecmp(media, "2") == 0) {
+ sdc->speed = 10;
+ sdc->mii_if.full_duplex = 1;
+ } else if (strcasecmp(media, "10mbps_hd") == 0 ||
+ strcasecmp(media, "1") == 0) {
+ sdc->speed = 10;
+ sdc->mii_if.full_duplex = 0;
+ } else {
+ sdc->an_enable = 1;
+ }
+ }
+ if (flowctrl == 1)
+ sdc->flowctrl = 1;
+
+ /* Fibre PHY? */
+ if (inl(BASE + ASICCtrl) & 0x80) {
+ /* Default 100Mbps Full */
+ if (sdc->an_enable) {
+ sdc->speed = 100;
+ sdc->mii_if.full_duplex = 1;
+ sdc->an_enable = 0;
+ }
+ }
+
+ /* The Linux driver uses flow control and resets the link here. This means the
+ mii section from above would need to be re done I believe. Since it serves
+ no real purpose leave it out. */
+
+ /* Force media type */
+ if (!sdc->an_enable) {
+ mii_ctl = 0;
+ mii_ctl |= (sdc->speed == 100) ? BMCR_SPEED100 : 0;
+ mii_ctl |= (sdc->mii_if.full_duplex) ? BMCR_FULLDPLX : 0;
+ mdio_write(nic, sdc->phys[0], MII_BMCR, mii_ctl);
+ printf("Override speed=%d, %s duplex\n",
+ sdc->speed,
+ sdc->mii_if.full_duplex ? "Full" : "Half");
+ }
+
+ /* Reset the chip to erase previous misconfiguration */
+ DBG ( "ASIC Control is %#lx\n", inl(BASE + ASICCtrl) );
+ outw(0x007f, BASE + ASICCtrl + 2);
+ DBG ( "ASIC Control is now %#lx.\n", inl(BASE + ASICCtrl) );
+
+ sundance_reset(nic);
+ if (sdc->an_enable) {
+ u16 mii_advertise, mii_lpa;
+ mii_advertise =
+ mdio_read(nic, sdc->phys[0], MII_ADVERTISE);
+ mii_lpa = mdio_read(nic, sdc->phys[0], MII_LPA);
+ mii_advertise &= mii_lpa;
+ if (mii_advertise & ADVERTISE_100FULL)
+ sdc->speed = 100;
+ else if (mii_advertise & ADVERTISE_100HALF)
+ sdc->speed = 100;
+ else if (mii_advertise & ADVERTISE_10FULL)
+ sdc->speed = 10;
+ else if (mii_advertise & ADVERTISE_10HALF)
+ sdc->speed = 10;
+ } else {
+ mii_ctl = mdio_read(nic, sdc->phys[0], MII_BMCR);
+ speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10;
+ sdc->speed = speed;
+ printf("%s: Link changed: %dMbps ,", sdc->nic_name, speed);
+ printf("%s duplex.\n", (mii_ctl & BMCR_FULLDPLX) ?
+ "full" : "half");
+ }
+ check_duplex(nic);
+ if (sdc->flowctrl && sdc->mii_if.full_duplex) {
+ outw(inw(BASE + MulticastFilter1 + 2) | 0x0200,
+ BASE + MulticastFilter1 + 2);
+ outw(inw(BASE + MACCtrl0) | EnbFlowCtrl, BASE + MACCtrl0);
+ }
+ printf("%dMbps, %s-Duplex\n", sdc->speed,
+ sdc->mii_if.full_duplex ? "Full" : "Half");
+
+ /* point to NIC specific routines */
+ nic->nic_op = &sundance_operations;
+
+ nic->irqno = pci->irq;
+ nic->ioaddr = BASE;
+
+ return 1;
+}
+
+
+/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */
+static int eeprom_read(long ioaddr, int location)
+{
+ int boguscnt = 10000; /* Typical 1900 ticks */
+ outw(0x0200 | (location & 0xff), ioaddr + EECtrl);
+ do {
+ if (!(inw(ioaddr + EECtrl) & 0x8000)) {
+ return inw(ioaddr + EEData);
+ }
+ }
+ while (--boguscnt > 0);
+ return 0;
+}
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details.
+
+ The maximum data clock rate is 2.5 Mhz.
+ The timing is decoupled from the processor clock by flushing the write
+ from the CPU write buffer with a following read, and using PCI
+ transaction time. */
+
+#define mdio_in(mdio_addr) inb(mdio_addr)
+#define mdio_out(value, mdio_addr) outb(value, mdio_addr)
+#define mdio_delay(mdio_addr) inb(mdio_addr)
+
+enum mii_reg_bits {
+ MDIO_ShiftClk = 0x0001, MDIO_Data = 0x0002, MDIO_EnbOutput =
+ 0x0004,
+};
+#define MDIO_EnbIn (0)
+#define MDIO_WRITE0 (MDIO_EnbOutput)
+#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput)
+
+/* Generate the preamble required for initial synchronization and
+ a few older transceivers. */
+static void mdio_sync(long mdio_addr)
+{
+ int bits = 32;
+
+ /* Establish sync by sending at least 32 logic ones. */
+ while (--bits >= 0) {
+ mdio_out(MDIO_WRITE1, mdio_addr);
+ mdio_delay(mdio_addr);
+ mdio_out(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+}
+
+static int
+mdio_read(struct nic *nic __unused, int phy_id, unsigned int location)
+{
+ long mdio_addr = BASE + MIICtrl;
+ int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int i, retval = 0;
+
+ if (sdc->mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval =
+ (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+ mdio_out(dataval, mdio_addr);
+ mdio_delay(mdio_addr);
+ mdio_out(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ mdio_out(MDIO_EnbIn, mdio_addr);
+ mdio_delay(mdio_addr);
+ retval = (retval << 1) | ((mdio_in(mdio_addr) & MDIO_Data)
+ ? 1 : 0);
+ mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ return (retval >> 1) & 0xffff;
+}
+
+static void
+mdio_write(struct nic *nic __unused, int phy_id,
+ unsigned int location, int value)
+{
+ long mdio_addr = BASE + MIICtrl;
+ int mii_cmd =
+ (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
+ int i;
+
+ if (sdc->mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval =
+ (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+ mdio_out(dataval, mdio_addr);
+ mdio_delay(mdio_addr);
+ mdio_out(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ mdio_out(MDIO_EnbIn, mdio_addr);
+ mdio_delay(mdio_addr);
+ mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ return;
+}
+
+static void set_rx_mode(struct nic *nic __unused)
+{
+ int i;
+ u16 mc_filter[4]; /* Multicast hash filter */
+ u32 rx_mode;
+
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+
+ if (sdc->mii_if.full_duplex && sdc->flowctrl)
+ mc_filter[3] |= 0x0200;
+ for (i = 0; i < 4; i++)
+ outw(mc_filter[i], BASE + MulticastFilter0 + i * 2);
+ outb(rx_mode, BASE + RxMode);
+ return;
+}
+
+static struct pci_device_id sundance_nics[] = {
+ PCI_ROM(0x13f0, 0x0201, "sundance", "ST201 Sundance 'Alta' based Adaptor"),
+ PCI_ROM(0x1186, 0x1002, "dfe530txs", "D-Link DFE530TXS (Sundance ST201 Alta)"),
+};
+
+PCI_DRIVER ( sundance_driver, sundance_nics, PCI_NO_CLASS );
+
+DRIVER ( "SUNDANCE/PCI", nic_driver, pci_driver, sundance_driver,
+ sundance_probe, sundance_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/tg3.c b/gpxe/src/drivers/net/tg3.c
new file mode 100644
index 00000000..2aa072b6
--- /dev/null
+++ b/gpxe/src/drivers/net/tg3.c
@@ -0,0 +1,3400 @@
+/* $Id$
+ * tg3.c: Broadcom Tigon3 ethernet driver.
+ *
+ * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)
+ * Copyright (C) 2001, 2002 Jeff Garzik (jgarzik@mandrakesoft.com)
+ * Copyright (C) 2003 Eric Biederman (ebiederman@lnxi.com) [etherboot port]
+ */
+
+/* 11-13-2003 timlegge Fix Issue with NetGear GA302T
+ * 11-18-2003 ebiederm Generalize NetGear Fix to what the code was supposed to be.
+ * 01-06-2005 Alf (Frederic Olivie) Add Dell bcm 5751 (0x1677) support
+ * 04-15-2005 Martin Vogt Add Fujitsu Siemens Computer (FSC) 0x1734 bcm 5751 0x105d support
+ */
+
+#include "etherboot.h"
+#include "nic.h"
+#include <errno.h>
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+#include "string.h"
+#include "tg3.h"
+
+#define SUPPORT_COPPER_PHY 1
+#define SUPPORT_FIBER_PHY 1
+#define SUPPORT_LINK_REPORT 1
+#define SUPPORT_PARTNO_STR 1
+#define SUPPORT_PHY_STR 1
+
+static struct tg3 tg3;
+
+/* These numbers seem to be hard coded in the NIC firmware somehow.
+ * You can't change the ring sizes, but you can change where you place
+ * them in the NIC onboard memory.
+ */
+#define TG3_RX_RING_SIZE 512
+#define TG3_DEF_RX_RING_PENDING 20 /* RX_RING_PENDING seems to be o.k. at 20 and 200 */
+#define TG3_RX_RCB_RING_SIZE 1024
+
+/* (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 ? \
+ 512 : 1024) */
+#define TG3_TX_RING_SIZE 512
+#define TG3_DEF_TX_RING_PENDING (TG3_TX_RING_SIZE - 1)
+
+#define TG3_RX_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * TG3_RX_RING_SIZE)
+#define TG3_RX_RCB_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * TG3_RX_RCB_RING_SIZE)
+
+#define TG3_TX_RING_BYTES (sizeof(struct tg3_tx_buffer_desc) * TG3_TX_RING_SIZE)
+#define NEXT_TX(N) (((N) + 1) & (TG3_TX_RING_SIZE - 1))
+#define PREV_TX(N) (((N) - 1) & (TG3_TX_RING_SIZE - 1))
+
+#define RX_PKT_BUF_SZ (1536 + 2 + 64)
+
+struct eth_frame {
+ uint8_t dst_addr[ETH_ALEN];
+ uint8_t src_addr[ETH_ALEN];
+ uint16_t type;
+ uint8_t data [ETH_FRAME_LEN - ETH_HLEN];
+};
+
+struct bss {
+ struct tg3_rx_buffer_desc rx_std[TG3_RX_RING_SIZE];
+ struct tg3_rx_buffer_desc rx_rcb[TG3_RX_RCB_RING_SIZE];
+ struct tg3_tx_buffer_desc tx_ring[TG3_TX_RING_SIZE];
+ struct tg3_hw_status hw_status;
+ struct tg3_hw_stats hw_stats;
+ unsigned char rx_bufs[TG3_DEF_RX_RING_PENDING][RX_PKT_BUF_SZ];
+ struct eth_frame tx_frame[2];
+} tg3_bss __shared;
+
+/**
+ * pci_save_state - save the PCI configuration space of a device before suspending
+ * @dev: - PCI device that we're dealing with
+ * @buffer: - buffer to hold config space context
+ *
+ * @buffer must be large enough to hold the entire PCI 2.2 config space
+ * (>= 64 bytes).
+ */
+static int pci_save_state(struct pci_device *dev, uint32_t *buffer)
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ pci_read_config_dword(dev, i * 4,&buffer[i]);
+ return 0;
+}
+
+/**
+ * pci_restore_state - Restore the saved state of a PCI device
+ * @dev: - PCI device that we're dealing with
+ * @buffer: - saved PCI config space
+ *
+ */
+static int pci_restore_state(struct pci_device *dev, uint32_t *buffer)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ pci_write_config_dword(dev,i * 4, buffer[i]);
+ return 0;
+}
+
+static void tg3_write_indirect_reg32(uint32_t off, uint32_t val)
+{
+ pci_write_config_dword(tg3.pdev, TG3PCI_REG_BASE_ADDR, off);
+ pci_write_config_dword(tg3.pdev, TG3PCI_REG_DATA, val);
+}
+
+#define tw32(reg,val) tg3_write_indirect_reg32((reg),(val))
+#define tw32_mailbox(reg, val) writel(((val) & 0xffffffff), tg3.regs + (reg))
+#define tw16(reg,val) writew(((val) & 0xffff), tg3.regs + (reg))
+#define tw8(reg,val) writeb(((val) & 0xff), tg3.regs + (reg))
+#define tr32(reg) readl(tg3.regs + (reg))
+#define tr16(reg) readw(tg3.regs + (reg))
+#define tr8(reg) readb(tg3.regs + (reg))
+
+static void tw32_carefully(uint32_t reg, uint32_t val)
+{
+ tw32(reg, val);
+ tr32(reg);
+ udelay(100);
+}
+
+static void tw32_mailbox2(uint32_t reg, uint32_t val)
+{
+ tw32_mailbox(reg, val);
+ tr32(reg);
+}
+
+static void tg3_write_mem(uint32_t off, uint32_t val)
+{
+ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
+ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_DATA, val);
+
+ /* Always leave this as zero. */
+ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
+}
+
+static void tg3_read_mem(uint32_t off, uint32_t *val)
+{
+ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
+ pci_read_config_dword(tg3.pdev, TG3PCI_MEM_WIN_DATA, val);
+
+ /* Always leave this as zero. */
+ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
+}
+
+static void tg3_disable_ints(struct tg3 *tp)
+{
+ tw32(TG3PCI_MISC_HOST_CTRL,
+ (tp->misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT));
+ tw32_mailbox2(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001);
+}
+
+static void tg3_switch_clocks(struct tg3 *tp)
+{
+ uint32_t orig_clock_ctrl, clock_ctrl;
+
+ clock_ctrl = tr32(TG3PCI_CLOCK_CTRL);
+
+ orig_clock_ctrl = clock_ctrl;
+ clock_ctrl &= (CLOCK_CTRL_FORCE_CLKRUN | CLOCK_CTRL_CLKRUN_OENABLE | 0x1f);
+ tp->pci_clock_ctrl = clock_ctrl;
+
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) &&
+ (!((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750)
+ && (tp->tg3_flags & TG3_FLAG_ENABLE_ASF))) &&
+ (orig_clock_ctrl & CLOCK_CTRL_44MHZ_CORE)!=0) {
+ tw32_carefully(TG3PCI_CLOCK_CTRL,
+ clock_ctrl | (CLOCK_CTRL_44MHZ_CORE | CLOCK_CTRL_ALTCLK));
+ tw32_carefully(TG3PCI_CLOCK_CTRL,
+ clock_ctrl | (CLOCK_CTRL_ALTCLK));
+ }
+ tw32_carefully(TG3PCI_CLOCK_CTRL, clock_ctrl);
+}
+
+#define PHY_BUSY_LOOPS 5000
+
+static int tg3_readphy(struct tg3 *tp, int reg, uint32_t *val)
+{
+ uint32_t frame_val;
+ int loops, ret;
+
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL);
+
+ *val = 0xffffffff;
+
+ frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) &
+ MI_COM_PHY_ADDR_MASK);
+ frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
+ MI_COM_REG_ADDR_MASK);
+ frame_val |= (MI_COM_CMD_READ | MI_COM_START);
+
+ tw32_carefully(MAC_MI_COM, frame_val);
+
+ loops = PHY_BUSY_LOOPS;
+ while (loops-- > 0) {
+ udelay(10);
+ frame_val = tr32(MAC_MI_COM);
+
+ if ((frame_val & MI_COM_BUSY) == 0) {
+ udelay(5);
+ frame_val = tr32(MAC_MI_COM);
+ break;
+ }
+ }
+
+ ret = -EBUSY;
+ if (loops > 0) {
+ *val = frame_val & MI_COM_DATA_MASK;
+ ret = 0;
+ }
+
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+
+ return ret;
+}
+
+static int tg3_writephy(struct tg3 *tp, int reg, uint32_t val)
+{
+ uint32_t frame_val;
+ int loops, ret;
+
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL);
+
+ frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) &
+ MI_COM_PHY_ADDR_MASK);
+ frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
+ MI_COM_REG_ADDR_MASK);
+ frame_val |= (val & MI_COM_DATA_MASK);
+ frame_val |= (MI_COM_CMD_WRITE | MI_COM_START);
+
+ tw32_carefully(MAC_MI_COM, frame_val);
+
+ loops = PHY_BUSY_LOOPS;
+ while (loops-- > 0) {
+ udelay(10);
+ frame_val = tr32(MAC_MI_COM);
+ if ((frame_val & MI_COM_BUSY) == 0) {
+ udelay(5);
+ frame_val = tr32(MAC_MI_COM);
+ break;
+ }
+ }
+
+ ret = -EBUSY;
+ if (loops > 0)
+ ret = 0;
+
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+
+ return ret;
+}
+
+static int tg3_writedsp(struct tg3 *tp, uint16_t addr, uint16_t val)
+{
+ int err;
+ err = tg3_writephy(tp, MII_TG3_DSP_ADDRESS, addr);
+ err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, val);
+ return err;
+}
+
+
+static void tg3_phy_set_wirespeed(struct tg3 *tp)
+{
+ uint32_t val;
+
+ if (tp->tg3_flags2 & TG3_FLG2_NO_ETH_WIRE_SPEED)
+ return;
+
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x7007);
+ tg3_readphy(tp, MII_TG3_AUX_CTRL, &val);
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, (val | (1 << 15) | (1 << 4)));
+}
+
+static int tg3_bmcr_reset(struct tg3 *tp)
+{
+ uint32_t phy_control;
+ int limit, err;
+
+ /* OK, reset it, and poll the BMCR_RESET bit until it
+ * clears or we time out.
+ */
+ phy_control = BMCR_RESET;
+ err = tg3_writephy(tp, MII_BMCR, phy_control);
+ if (err != 0)
+ return -EBUSY;
+
+ limit = 5000;
+ while (limit--) {
+ err = tg3_readphy(tp, MII_BMCR, &phy_control);
+ if (err != 0)
+ return -EBUSY;
+
+ if ((phy_control & BMCR_RESET) == 0) {
+ udelay(40);
+ break;
+ }
+ udelay(10);
+ }
+ if (limit <= 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int tg3_wait_macro_done(struct tg3 *tp)
+{
+ int limit = 100;
+
+ while (limit--) {
+ uint32_t tmp32;
+
+ tg3_readphy(tp, 0x16, &tmp32);
+ if ((tmp32 & 0x1000) == 0)
+ break;
+ }
+ if (limit <= 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int tg3_phy_write_and_check_testpat(struct tg3 *tp, int *resetp)
+{
+ static const uint32_t test_pat[4][6] = {
+ { 0x00005555, 0x00000005, 0x00002aaa, 0x0000000a, 0x00003456, 0x00000003 },
+ { 0x00002aaa, 0x0000000a, 0x00003333, 0x00000003, 0x0000789a, 0x00000005 },
+ { 0x00005a5a, 0x00000005, 0x00002a6a, 0x0000000a, 0x00001bcd, 0x00000003 },
+ { 0x00002a5a, 0x0000000a, 0x000033c3, 0x00000003, 0x00002ef1, 0x00000005 }
+ };
+ int chan;
+
+ for (chan = 0; chan < 4; chan++) {
+ int i;
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
+ (chan * 0x2000) | 0x0200);
+ tg3_writephy(tp, 0x16, 0x0002);
+
+ for (i = 0; i < 6; i++)
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT,
+ test_pat[chan][i]);
+
+ tg3_writephy(tp, 0x16, 0x0202);
+ if (tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
+ (chan * 0x2000) | 0x0200);
+ tg3_writephy(tp, 0x16, 0x0082);
+ if (tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+
+ tg3_writephy(tp, 0x16, 0x0802);
+ if (tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+
+ for (i = 0; i < 6; i += 2) {
+ uint32_t low, high;
+
+ tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &low);
+ tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &high);
+ if (tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+ low &= 0x7fff;
+ high &= 0x000f;
+ if (low != test_pat[chan][i] ||
+ high != test_pat[chan][i+1]) {
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000b);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4001);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4005);
+
+ return -EBUSY;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int tg3_phy_reset_chanpat(struct tg3 *tp)
+{
+ int chan;
+
+ for (chan = 0; chan < 4; chan++) {
+ int i;
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
+ (chan * 0x2000) | 0x0200);
+ tg3_writephy(tp, 0x16, 0x0002);
+ for (i = 0; i < 6; i++)
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x000);
+ tg3_writephy(tp, 0x16, 0x0202);
+ if (tg3_wait_macro_done(tp))
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int tg3_phy_reset_5703_4_5(struct tg3 *tp)
+{
+ uint32_t reg32, phy9_orig;
+ int retries, do_phy_reset, err;
+
+ retries = 10;
+ do_phy_reset = 1;
+ do {
+ if (do_phy_reset) {
+ err = tg3_bmcr_reset(tp);
+ if (err)
+ return err;
+ do_phy_reset = 0;
+ }
+
+ /* Disable transmitter and interrupt. */
+ tg3_readphy(tp, MII_TG3_EXT_CTRL, &reg32);
+ reg32 |= 0x3000;
+ tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32);
+
+ /* Set full-duplex, 1000 mbps. */
+ tg3_writephy(tp, MII_BMCR,
+ BMCR_FULLDPLX | TG3_BMCR_SPEED1000);
+
+ /* Set to master mode. */
+ tg3_readphy(tp, MII_TG3_CTRL, &phy9_orig);
+ tg3_writephy(tp, MII_TG3_CTRL,
+ (MII_TG3_CTRL_AS_MASTER |
+ MII_TG3_CTRL_ENABLE_AS_MASTER));
+
+ /* Enable SM_DSP_CLOCK and 6dB. */
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00);
+
+ /* Block the PHY control access. */
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8005);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0800);
+
+ err = tg3_phy_write_and_check_testpat(tp, &do_phy_reset);
+ if (!err)
+ break;
+ } while (--retries);
+
+ err = tg3_phy_reset_chanpat(tp);
+ if (err)
+ return err;
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8005);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0000);
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8200);
+ tg3_writephy(tp, 0x16, 0x0000);
+
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400);
+
+ tg3_writephy(tp, MII_TG3_CTRL, phy9_orig);
+
+ tg3_readphy(tp, MII_TG3_EXT_CTRL, &reg32);
+ reg32 &= ~0x3000;
+ tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32);
+
+ return err;
+}
+
+/* This will reset the tigon3 PHY if there is no valid
+ * link.
+ */
+static int tg3_phy_reset(struct tg3 *tp)
+{
+ uint32_t phy_status;
+ int err;
+
+ err = tg3_readphy(tp, MII_BMSR, &phy_status);
+ err |= tg3_readphy(tp, MII_BMSR, &phy_status);
+ if (err != 0)
+ return -EBUSY;
+
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) {
+ err = tg3_phy_reset_5703_4_5(tp);
+ if (err)
+ return err;
+ goto out;
+ }
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) {
+ // Taken from Broadcom's source code
+ tg3_writephy(tp, 0x18, 0x0c00);
+ tg3_writephy(tp, 0x17, 0x000a);
+ tg3_writephy(tp, 0x15, 0x310b);
+ tg3_writephy(tp, 0x17, 0x201f);
+ tg3_writephy(tp, 0x15, 0x9506);
+ tg3_writephy(tp, 0x17, 0x401f);
+ tg3_writephy(tp, 0x15, 0x14e2);
+ tg3_writephy(tp, 0x18, 0x0400);
+ }
+ err = tg3_bmcr_reset(tp);
+ if (err)
+ return err;
+ out:
+ tg3_phy_set_wirespeed(tp);
+ return 0;
+}
+
+static void tg3_set_power_state_0(struct tg3 *tp)
+{
+ uint16_t power_control;
+ int pm = tp->pm_cap;
+
+ /* Make sure register accesses (indirect or otherwise)
+ * will function correctly.
+ */
+ pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, tp->misc_host_ctrl);
+
+ pci_read_config_word(tp->pdev, pm + PCI_PM_CTRL, &power_control);
+
+ power_control |= PCI_PM_CTRL_PME_STATUS;
+ power_control &= ~(PCI_PM_CTRL_STATE_MASK);
+ power_control |= 0;
+ pci_write_config_word(tp->pdev, pm + PCI_PM_CTRL, power_control);
+
+ tw32_carefully(GRC_LOCAL_CTRL, tp->grc_local_ctrl);
+
+ return;
+}
+
+
+#if SUPPORT_LINK_REPORT
+static void tg3_link_report(struct tg3 *tp)
+{
+ if (!tp->carrier_ok) {
+ printf("Link is down.\n");
+ } else {
+ printf("Link is up at %d Mbps, %s duplex. %s %s %s\n",
+ (tp->link_config.active_speed == SPEED_1000 ?
+ 1000 :
+ (tp->link_config.active_speed == SPEED_100 ?
+ 100 : 10)),
+ (tp->link_config.active_duplex == DUPLEX_FULL ?
+ "full" : "half"),
+ (tp->tg3_flags & TG3_FLAG_TX_PAUSE) ? "TX" : "",
+ (tp->tg3_flags & TG3_FLAG_RX_PAUSE) ? "RX" : "",
+ (tp->tg3_flags & (TG3_FLAG_TX_PAUSE |TG3_FLAG_RX_PAUSE)) ? "flow control" : "");
+ }
+}
+#else
+#define tg3_link_report(tp)
+#endif
+
+static void tg3_setup_flow_control(struct tg3 *tp, uint32_t local_adv, uint32_t remote_adv)
+{
+ uint32_t new_tg3_flags = 0;
+
+ if (local_adv & ADVERTISE_PAUSE_CAP) {
+ if (local_adv & ADVERTISE_PAUSE_ASYM) {
+ if (remote_adv & LPA_PAUSE_CAP)
+ new_tg3_flags |=
+ (TG3_FLAG_RX_PAUSE |
+ TG3_FLAG_TX_PAUSE);
+ else if (remote_adv & LPA_PAUSE_ASYM)
+ new_tg3_flags |=
+ (TG3_FLAG_RX_PAUSE);
+ } else {
+ if (remote_adv & LPA_PAUSE_CAP)
+ new_tg3_flags |=
+ (TG3_FLAG_RX_PAUSE |
+ TG3_FLAG_TX_PAUSE);
+ }
+ } else if (local_adv & ADVERTISE_PAUSE_ASYM) {
+ if ((remote_adv & LPA_PAUSE_CAP) &&
+ (remote_adv & LPA_PAUSE_ASYM))
+ new_tg3_flags |= TG3_FLAG_TX_PAUSE;
+ }
+
+ tp->tg3_flags &= ~(TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE);
+ tp->tg3_flags |= new_tg3_flags;
+
+ if (new_tg3_flags & TG3_FLAG_RX_PAUSE)
+ tp->rx_mode |= RX_MODE_FLOW_CTRL_ENABLE;
+ else
+ tp->rx_mode &= ~RX_MODE_FLOW_CTRL_ENABLE;
+
+ if (new_tg3_flags & TG3_FLAG_TX_PAUSE)
+ tp->tx_mode |= TX_MODE_FLOW_CTRL_ENABLE;
+ else
+ tp->tx_mode &= ~TX_MODE_FLOW_CTRL_ENABLE;
+}
+
+#if SUPPORT_COPPER_PHY
+static void tg3_aux_stat_to_speed_duplex(
+ struct tg3 *tp __unused, uint32_t val, uint8_t *speed, uint8_t *duplex)
+{
+ static const uint8_t map[] = {
+ [0] = (SPEED_INVALID << 2) | DUPLEX_INVALID,
+ [MII_TG3_AUX_STAT_10HALF >> 8] = (SPEED_10 << 2) | DUPLEX_HALF,
+ [MII_TG3_AUX_STAT_10FULL >> 8] = (SPEED_10 << 2) | DUPLEX_FULL,
+ [MII_TG3_AUX_STAT_100HALF >> 8] = (SPEED_100 << 2) | DUPLEX_HALF,
+ [MII_TG3_AUX_STAT_100_4 >> 8] = (SPEED_INVALID << 2) | DUPLEX_INVALID,
+ [MII_TG3_AUX_STAT_100FULL >> 8] = (SPEED_100 << 2) | DUPLEX_FULL,
+ [MII_TG3_AUX_STAT_1000HALF >> 8] = (SPEED_1000 << 2) | DUPLEX_HALF,
+ [MII_TG3_AUX_STAT_1000FULL >> 8] = (SPEED_1000 << 2) | DUPLEX_FULL,
+ };
+ uint8_t result;
+ result = map[(val & MII_TG3_AUX_STAT_SPDMASK) >> 8];
+ *speed = result >> 2;
+ *duplex = result & 3;
+}
+
+static int tg3_phy_copper_begin(struct tg3 *tp)
+{
+ uint32_t new_adv;
+
+ tp->link_config.advertising =
+ (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg | ADVERTISED_MII);
+
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) {
+ tp->link_config.advertising &=
+ ~(ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full);
+ }
+
+ new_adv = (ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+ if (tp->link_config.advertising & ADVERTISED_10baseT_Half) {
+ new_adv |= ADVERTISE_10HALF;
+ }
+ if (tp->link_config.advertising & ADVERTISED_10baseT_Full) {
+ new_adv |= ADVERTISE_10FULL;
+ }
+ if (tp->link_config.advertising & ADVERTISED_100baseT_Half) {
+ new_adv |= ADVERTISE_100HALF;
+ }
+ if (tp->link_config.advertising & ADVERTISED_100baseT_Full) {
+ new_adv |= ADVERTISE_100FULL;
+ }
+ tg3_writephy(tp, MII_ADVERTISE, new_adv);
+
+ if (tp->link_config.advertising &
+ (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) {
+ new_adv = 0;
+ if (tp->link_config.advertising & ADVERTISED_1000baseT_Half) {
+ new_adv |= MII_TG3_CTRL_ADV_1000_HALF;
+ }
+ if (tp->link_config.advertising & ADVERTISED_1000baseT_Full) {
+ new_adv |= MII_TG3_CTRL_ADV_1000_FULL;
+ }
+ if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY) &&
+ (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 ||
+ tp->pci_chip_rev_id == CHIPREV_ID_5701_B0)) {
+ new_adv |= (MII_TG3_CTRL_AS_MASTER |
+ MII_TG3_CTRL_ENABLE_AS_MASTER);
+ }
+ tg3_writephy(tp, MII_TG3_CTRL, new_adv);
+ } else {
+ tg3_writephy(tp, MII_TG3_CTRL, 0);
+ }
+
+ tg3_writephy(tp, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+
+ return 0;
+}
+
+static int tg3_init_5401phy_dsp(struct tg3 *tp)
+{
+ int err;
+
+ /* Turn off tap power management. */
+ err = tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c20);
+
+ err |= tg3_writedsp(tp, 0x0012, 0x1804);
+ err |= tg3_writedsp(tp, 0x0013, 0x1204);
+ err |= tg3_writedsp(tp, 0x8006, 0x0132);
+ err |= tg3_writedsp(tp, 0x8006, 0x0232);
+ err |= tg3_writedsp(tp, 0x201f, 0x0a20);
+
+ udelay(40);
+
+ return err;
+}
+
+static int tg3_setup_copper_phy(struct tg3 *tp)
+{
+ int current_link_up;
+ uint32_t bmsr, dummy;
+ int i, err;
+
+ tw32_carefully(MAC_STATUS,
+ (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED
+ | MAC_STATUS_LNKSTATE_CHANGED));
+
+ tp->mi_mode = MAC_MI_MODE_BASE;
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x02);
+
+ /* Some third-party PHYs need to be reset on link going
+ * down.
+ */
+ if ( ( (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) ||
+ (tp->pci_chip_rev_id == CHIPREV_ID_5705_A0)) &&
+ (tp->carrier_ok)) {
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ if (!(bmsr & BMSR_LSTATUS))
+ tg3_phy_reset(tp);
+ }
+
+ if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) {
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+
+ if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE))
+ bmsr = 0;
+
+ if (!(bmsr & BMSR_LSTATUS)) {
+ err = tg3_init_5401phy_dsp(tp);
+ if (err)
+ return err;
+
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ for (i = 0; i < 1000; i++) {
+ udelay(10);
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ if (bmsr & BMSR_LSTATUS) {
+ udelay(40);
+ break;
+ }
+ }
+
+ if ((tp->phy_id & PHY_ID_REV_MASK) == PHY_REV_BCM5401_B0 &&
+ !(bmsr & BMSR_LSTATUS) &&
+ tp->link_config.active_speed == SPEED_1000) {
+ err = tg3_phy_reset(tp);
+ if (!err)
+ err = tg3_init_5401phy_dsp(tp);
+ if (err)
+ return err;
+ }
+ }
+ } else if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 ||
+ tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) {
+ /* 5701 {A0,B0} CRC bug workaround */
+ tg3_writephy(tp, 0x15, 0x0a75);
+ tg3_writephy(tp, 0x1c, 0x8c68);
+ tg3_writephy(tp, 0x1c, 0x8d68);
+ tg3_writephy(tp, 0x1c, 0x8c68);
+ }
+
+ /* Clear pending interrupts... */
+ tg3_readphy(tp, MII_TG3_ISTAT, &dummy);
+ tg3_readphy(tp, MII_TG3_ISTAT, &dummy);
+
+ tg3_writephy(tp, MII_TG3_IMASK, ~0);
+
+ if (tp->led_mode == led_mode_three_link)
+ tg3_writephy(tp, MII_TG3_EXT_CTRL,
+ MII_TG3_EXT_CTRL_LNK3_LED_MODE);
+ else
+ tg3_writephy(tp, MII_TG3_EXT_CTRL, 0);
+
+ current_link_up = 0;
+
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+
+ if (bmsr & BMSR_LSTATUS) {
+ uint32_t aux_stat, bmcr;
+
+ tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat);
+ for (i = 0; i < 2000; i++) {
+ udelay(10);
+ tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat);
+ if (aux_stat)
+ break;
+ }
+
+ tg3_aux_stat_to_speed_duplex(tp, aux_stat,
+ &tp->link_config.active_speed,
+ &tp->link_config.active_duplex);
+ tg3_readphy(tp, MII_BMCR, &bmcr);
+ tg3_readphy(tp, MII_BMCR, &bmcr);
+ if (bmcr & BMCR_ANENABLE) {
+ uint32_t gig_ctrl;
+
+ current_link_up = 1;
+
+ /* Force autoneg restart if we are exiting
+ * low power mode.
+ */
+ tg3_readphy(tp, MII_TG3_CTRL, &gig_ctrl);
+ if (!(gig_ctrl & (MII_TG3_CTRL_ADV_1000_HALF |
+ MII_TG3_CTRL_ADV_1000_FULL))) {
+ current_link_up = 0;
+ }
+ } else {
+ current_link_up = 0;
+ }
+ }
+
+ if (current_link_up == 1 &&
+ (tp->link_config.active_duplex == DUPLEX_FULL)) {
+ uint32_t local_adv, remote_adv;
+
+ tg3_readphy(tp, MII_ADVERTISE, &local_adv);
+ local_adv &= (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+
+ tg3_readphy(tp, MII_LPA, &remote_adv);
+ remote_adv &= (LPA_PAUSE_CAP | LPA_PAUSE_ASYM);
+
+ /* If we are not advertising full pause capability,
+ * something is wrong. Bring the link down and reconfigure.
+ */
+ if (local_adv != ADVERTISE_PAUSE_CAP) {
+ current_link_up = 0;
+ } else {
+ tg3_setup_flow_control(tp, local_adv, remote_adv);
+ }
+ }
+
+ if (current_link_up == 0) {
+ uint32_t tmp;
+
+ tg3_phy_copper_begin(tp);
+
+ tg3_readphy(tp, MII_BMSR, &tmp);
+ tg3_readphy(tp, MII_BMSR, &tmp);
+ if (tmp & BMSR_LSTATUS)
+ current_link_up = 1;
+ }
+
+ tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK;
+ if (current_link_up == 1) {
+ if (tp->link_config.active_speed == SPEED_100 ||
+ tp->link_config.active_speed == SPEED_10)
+ tp->mac_mode |= MAC_MODE_PORT_MODE_MII;
+ else
+ tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
+ } else
+ tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
+
+ tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX;
+ if (tp->link_config.active_duplex == DUPLEX_HALF)
+ tp->mac_mode |= MAC_MODE_HALF_DUPLEX;
+
+ tp->mac_mode &= ~MAC_MODE_LINK_POLARITY;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) {
+ if ((tp->led_mode == led_mode_link10) ||
+ (current_link_up == 1 &&
+ tp->link_config.active_speed == SPEED_10))
+ tp->mac_mode |= MAC_MODE_LINK_POLARITY;
+ } else {
+ if (current_link_up == 1)
+ tp->mac_mode |= MAC_MODE_LINK_POLARITY;
+ tw32(MAC_LED_CTRL, LED_CTRL_PHY_MODE_1);
+ }
+
+ /* ??? Without this setting Netgear GA302T PHY does not
+ * ??? send/receive packets...
+ * With this other PHYs cannot bring up the link
+ */
+ if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5411 &&
+ tp->pci_chip_rev_id == CHIPREV_ID_5700_ALTIMA) {
+ tp->mi_mode |= MAC_MI_MODE_AUTO_POLL;
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+ }
+
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ /* Link change polled. */
+ tw32_carefully(MAC_EVENT, 0);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 &&
+ current_link_up == 1 &&
+ tp->link_config.active_speed == SPEED_1000 &&
+ ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ||
+ (tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED))) {
+ udelay(120);
+ tw32_carefully(MAC_STATUS,
+ (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED));
+ tg3_write_mem(
+ NIC_SRAM_FIRMWARE_MBOX,
+ NIC_SRAM_FIRMWARE_MBOX_MAGIC2);
+ }
+
+ if (current_link_up != tp->carrier_ok) {
+ tp->carrier_ok = current_link_up;
+ tg3_link_report(tp);
+ }
+
+ return 0;
+}
+#else
+#define tg3_setup_copper_phy(TP) (-EINVAL)
+#endif /* SUPPORT_COPPER_PHY */
+
+#if SUPPORT_FIBER_PHY
+struct tg3_fiber_aneginfo {
+ int state;
+#define ANEG_STATE_UNKNOWN 0
+#define ANEG_STATE_AN_ENABLE 1
+#define ANEG_STATE_RESTART_INIT 2
+#define ANEG_STATE_RESTART 3
+#define ANEG_STATE_DISABLE_LINK_OK 4
+#define ANEG_STATE_ABILITY_DETECT_INIT 5
+#define ANEG_STATE_ABILITY_DETECT 6
+#define ANEG_STATE_ACK_DETECT_INIT 7
+#define ANEG_STATE_ACK_DETECT 8
+#define ANEG_STATE_COMPLETE_ACK_INIT 9
+#define ANEG_STATE_COMPLETE_ACK 10
+#define ANEG_STATE_IDLE_DETECT_INIT 11
+#define ANEG_STATE_IDLE_DETECT 12
+#define ANEG_STATE_LINK_OK 13
+#define ANEG_STATE_NEXT_PAGE_WAIT_INIT 14
+#define ANEG_STATE_NEXT_PAGE_WAIT 15
+
+ uint32_t flags;
+#define MR_AN_ENABLE 0x00000001
+#define MR_RESTART_AN 0x00000002
+#define MR_AN_COMPLETE 0x00000004
+#define MR_PAGE_RX 0x00000008
+#define MR_NP_LOADED 0x00000010
+#define MR_TOGGLE_TX 0x00000020
+#define MR_LP_ADV_FULL_DUPLEX 0x00000040
+#define MR_LP_ADV_HALF_DUPLEX 0x00000080
+#define MR_LP_ADV_SYM_PAUSE 0x00000100
+#define MR_LP_ADV_ASYM_PAUSE 0x00000200
+#define MR_LP_ADV_REMOTE_FAULT1 0x00000400
+#define MR_LP_ADV_REMOTE_FAULT2 0x00000800
+#define MR_LP_ADV_NEXT_PAGE 0x00001000
+#define MR_TOGGLE_RX 0x00002000
+#define MR_NP_RX 0x00004000
+
+#define MR_LINK_OK 0x80000000
+
+ unsigned long link_time, cur_time;
+
+ uint32_t ability_match_cfg;
+ int ability_match_count;
+
+ char ability_match, idle_match, ack_match;
+
+ uint32_t txconfig, rxconfig;
+#define ANEG_CFG_NP 0x00000080
+#define ANEG_CFG_ACK 0x00000040
+#define ANEG_CFG_RF2 0x00000020
+#define ANEG_CFG_RF1 0x00000010
+#define ANEG_CFG_PS2 0x00000001
+#define ANEG_CFG_PS1 0x00008000
+#define ANEG_CFG_HD 0x00004000
+#define ANEG_CFG_FD 0x00002000
+#define ANEG_CFG_INVAL 0x00001f06
+
+};
+#define ANEG_OK 0
+#define ANEG_DONE 1
+#define ANEG_TIMER_ENAB 2
+#define ANEG_FAILED -1
+
+#define ANEG_STATE_SETTLE_TIME 10000
+
+static int tg3_fiber_aneg_smachine(struct tg3 *tp,
+ struct tg3_fiber_aneginfo *ap)
+{
+ unsigned long delta;
+ uint32_t rx_cfg_reg;
+ int ret;
+
+ if (ap->state == ANEG_STATE_UNKNOWN) {
+ ap->rxconfig = 0;
+ ap->link_time = 0;
+ ap->cur_time = 0;
+ ap->ability_match_cfg = 0;
+ ap->ability_match_count = 0;
+ ap->ability_match = 0;
+ ap->idle_match = 0;
+ ap->ack_match = 0;
+ }
+ ap->cur_time++;
+
+ if (tr32(MAC_STATUS) & MAC_STATUS_RCVD_CFG) {
+ rx_cfg_reg = tr32(MAC_RX_AUTO_NEG);
+
+ if (rx_cfg_reg != ap->ability_match_cfg) {
+ ap->ability_match_cfg = rx_cfg_reg;
+ ap->ability_match = 0;
+ ap->ability_match_count = 0;
+ } else {
+ if (++ap->ability_match_count > 1) {
+ ap->ability_match = 1;
+ ap->ability_match_cfg = rx_cfg_reg;
+ }
+ }
+ if (rx_cfg_reg & ANEG_CFG_ACK)
+ ap->ack_match = 1;
+ else
+ ap->ack_match = 0;
+
+ ap->idle_match = 0;
+ } else {
+ ap->idle_match = 1;
+ ap->ability_match_cfg = 0;
+ ap->ability_match_count = 0;
+ ap->ability_match = 0;
+ ap->ack_match = 0;
+
+ rx_cfg_reg = 0;
+ }
+
+ ap->rxconfig = rx_cfg_reg;
+ ret = ANEG_OK;
+
+ switch(ap->state) {
+ case ANEG_STATE_UNKNOWN:
+ if (ap->flags & (MR_AN_ENABLE | MR_RESTART_AN))
+ ap->state = ANEG_STATE_AN_ENABLE;
+
+ /* fallthru */
+ case ANEG_STATE_AN_ENABLE:
+ ap->flags &= ~(MR_AN_COMPLETE | MR_PAGE_RX);
+ if (ap->flags & MR_AN_ENABLE) {
+ ap->link_time = 0;
+ ap->cur_time = 0;
+ ap->ability_match_cfg = 0;
+ ap->ability_match_count = 0;
+ ap->ability_match = 0;
+ ap->idle_match = 0;
+ ap->ack_match = 0;
+
+ ap->state = ANEG_STATE_RESTART_INIT;
+ } else {
+ ap->state = ANEG_STATE_DISABLE_LINK_OK;
+ }
+ break;
+
+ case ANEG_STATE_RESTART_INIT:
+ ap->link_time = ap->cur_time;
+ ap->flags &= ~(MR_NP_LOADED);
+ ap->txconfig = 0;
+ tw32(MAC_TX_AUTO_NEG, 0);
+ tp->mac_mode |= MAC_MODE_SEND_CONFIGS;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ ret = ANEG_TIMER_ENAB;
+ ap->state = ANEG_STATE_RESTART;
+
+ /* fallthru */
+ case ANEG_STATE_RESTART:
+ delta = ap->cur_time - ap->link_time;
+ if (delta > ANEG_STATE_SETTLE_TIME) {
+ ap->state = ANEG_STATE_ABILITY_DETECT_INIT;
+ } else {
+ ret = ANEG_TIMER_ENAB;
+ }
+ break;
+
+ case ANEG_STATE_DISABLE_LINK_OK:
+ ret = ANEG_DONE;
+ break;
+
+ case ANEG_STATE_ABILITY_DETECT_INIT:
+ ap->flags &= ~(MR_TOGGLE_TX);
+ ap->txconfig = (ANEG_CFG_FD | ANEG_CFG_PS1);
+ tw32(MAC_TX_AUTO_NEG, ap->txconfig);
+ tp->mac_mode |= MAC_MODE_SEND_CONFIGS;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ ap->state = ANEG_STATE_ABILITY_DETECT;
+ break;
+
+ case ANEG_STATE_ABILITY_DETECT:
+ if (ap->ability_match != 0 && ap->rxconfig != 0) {
+ ap->state = ANEG_STATE_ACK_DETECT_INIT;
+ }
+ break;
+
+ case ANEG_STATE_ACK_DETECT_INIT:
+ ap->txconfig |= ANEG_CFG_ACK;
+ tw32(MAC_TX_AUTO_NEG, ap->txconfig);
+ tp->mac_mode |= MAC_MODE_SEND_CONFIGS;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ ap->state = ANEG_STATE_ACK_DETECT;
+
+ /* fallthru */
+ case ANEG_STATE_ACK_DETECT:
+ if (ap->ack_match != 0) {
+ if ((ap->rxconfig & ~ANEG_CFG_ACK) ==
+ (ap->ability_match_cfg & ~ANEG_CFG_ACK)) {
+ ap->state = ANEG_STATE_COMPLETE_ACK_INIT;
+ } else {
+ ap->state = ANEG_STATE_AN_ENABLE;
+ }
+ } else if (ap->ability_match != 0 &&
+ ap->rxconfig == 0) {
+ ap->state = ANEG_STATE_AN_ENABLE;
+ }
+ break;
+
+ case ANEG_STATE_COMPLETE_ACK_INIT:
+ if (ap->rxconfig & ANEG_CFG_INVAL) {
+ ret = ANEG_FAILED;
+ break;
+ }
+ ap->flags &= ~(MR_LP_ADV_FULL_DUPLEX |
+ MR_LP_ADV_HALF_DUPLEX |
+ MR_LP_ADV_SYM_PAUSE |
+ MR_LP_ADV_ASYM_PAUSE |
+ MR_LP_ADV_REMOTE_FAULT1 |
+ MR_LP_ADV_REMOTE_FAULT2 |
+ MR_LP_ADV_NEXT_PAGE |
+ MR_TOGGLE_RX |
+ MR_NP_RX);
+ if (ap->rxconfig & ANEG_CFG_FD)
+ ap->flags |= MR_LP_ADV_FULL_DUPLEX;
+ if (ap->rxconfig & ANEG_CFG_HD)
+ ap->flags |= MR_LP_ADV_HALF_DUPLEX;
+ if (ap->rxconfig & ANEG_CFG_PS1)
+ ap->flags |= MR_LP_ADV_SYM_PAUSE;
+ if (ap->rxconfig & ANEG_CFG_PS2)
+ ap->flags |= MR_LP_ADV_ASYM_PAUSE;
+ if (ap->rxconfig & ANEG_CFG_RF1)
+ ap->flags |= MR_LP_ADV_REMOTE_FAULT1;
+ if (ap->rxconfig & ANEG_CFG_RF2)
+ ap->flags |= MR_LP_ADV_REMOTE_FAULT2;
+ if (ap->rxconfig & ANEG_CFG_NP)
+ ap->flags |= MR_LP_ADV_NEXT_PAGE;
+
+ ap->link_time = ap->cur_time;
+
+ ap->flags ^= (MR_TOGGLE_TX);
+ if (ap->rxconfig & 0x0008)
+ ap->flags |= MR_TOGGLE_RX;
+ if (ap->rxconfig & ANEG_CFG_NP)
+ ap->flags |= MR_NP_RX;
+ ap->flags |= MR_PAGE_RX;
+
+ ap->state = ANEG_STATE_COMPLETE_ACK;
+ ret = ANEG_TIMER_ENAB;
+ break;
+
+ case ANEG_STATE_COMPLETE_ACK:
+ if (ap->ability_match != 0 &&
+ ap->rxconfig == 0) {
+ ap->state = ANEG_STATE_AN_ENABLE;
+ break;
+ }
+ delta = ap->cur_time - ap->link_time;
+ if (delta > ANEG_STATE_SETTLE_TIME) {
+ if (!(ap->flags & (MR_LP_ADV_NEXT_PAGE))) {
+ ap->state = ANEG_STATE_IDLE_DETECT_INIT;
+ } else {
+ if ((ap->txconfig & ANEG_CFG_NP) == 0 &&
+ !(ap->flags & MR_NP_RX)) {
+ ap->state = ANEG_STATE_IDLE_DETECT_INIT;
+ } else {
+ ret = ANEG_FAILED;
+ }
+ }
+ }
+ break;
+
+ case ANEG_STATE_IDLE_DETECT_INIT:
+ ap->link_time = ap->cur_time;
+ tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ ap->state = ANEG_STATE_IDLE_DETECT;
+ ret = ANEG_TIMER_ENAB;
+ break;
+
+ case ANEG_STATE_IDLE_DETECT:
+ if (ap->ability_match != 0 &&
+ ap->rxconfig == 0) {
+ ap->state = ANEG_STATE_AN_ENABLE;
+ break;
+ }
+ delta = ap->cur_time - ap->link_time;
+ if (delta > ANEG_STATE_SETTLE_TIME) {
+ /* XXX another gem from the Broadcom driver :( */
+ ap->state = ANEG_STATE_LINK_OK;
+ }
+ break;
+
+ case ANEG_STATE_LINK_OK:
+ ap->flags |= (MR_AN_COMPLETE | MR_LINK_OK);
+ ret = ANEG_DONE;
+ break;
+
+ case ANEG_STATE_NEXT_PAGE_WAIT_INIT:
+ /* ??? unimplemented */
+ break;
+
+ case ANEG_STATE_NEXT_PAGE_WAIT:
+ /* ??? unimplemented */
+ break;
+
+ default:
+ ret = ANEG_FAILED;
+ break;
+ };
+
+ return ret;
+}
+
+static int tg3_setup_fiber_phy(struct tg3 *tp)
+{
+ uint32_t orig_pause_cfg;
+ uint16_t orig_active_speed;
+ uint8_t orig_active_duplex;
+ int current_link_up;
+ int i;
+
+ orig_pause_cfg =
+ (tp->tg3_flags & (TG3_FLAG_RX_PAUSE |
+ TG3_FLAG_TX_PAUSE));
+ orig_active_speed = tp->link_config.active_speed;
+ orig_active_duplex = tp->link_config.active_duplex;
+
+ tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX);
+ tp->mac_mode |= MAC_MODE_PORT_MODE_TBI;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ /* Reset when initting first time or we have a link. */
+ if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) ||
+ (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED)) {
+ /* Set PLL lock range. */
+ tg3_writephy(tp, 0x16, 0x8007);
+
+ /* SW reset */
+ tg3_writephy(tp, MII_BMCR, BMCR_RESET);
+
+ /* Wait for reset to complete. */
+ mdelay(5);
+
+ /* Config mode; select PMA/Ch 1 regs. */
+ tg3_writephy(tp, 0x10, 0x8411);
+
+ /* Enable auto-lock and comdet, select txclk for tx. */
+ tg3_writephy(tp, 0x11, 0x0a10);
+
+ tg3_writephy(tp, 0x18, 0x00a0);
+ tg3_writephy(tp, 0x16, 0x41ff);
+
+ /* Assert and deassert POR. */
+ tg3_writephy(tp, 0x13, 0x0400);
+ udelay(40);
+ tg3_writephy(tp, 0x13, 0x0000);
+
+ tg3_writephy(tp, 0x11, 0x0a50);
+ udelay(40);
+ tg3_writephy(tp, 0x11, 0x0a10);
+
+ /* Wait for signal to stabilize */
+ mdelay(150);
+
+ /* Deselect the channel register so we can read the PHYID
+ * later.
+ */
+ tg3_writephy(tp, 0x10, 0x8011);
+ }
+
+ /* Disable link change interrupt. */
+ tw32_carefully(MAC_EVENT, 0);
+
+ current_link_up = 0;
+ if (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) {
+ if (!(tp->tg3_flags & TG3_FLAG_GOT_SERDES_FLOWCTL)) {
+ struct tg3_fiber_aneginfo aninfo;
+ int status = ANEG_FAILED;
+ unsigned int tick;
+ uint32_t tmp;
+
+ memset(&aninfo, 0, sizeof(aninfo));
+ aninfo.flags |= (MR_AN_ENABLE);
+
+ tw32(MAC_TX_AUTO_NEG, 0);
+
+ tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK;
+ tw32_carefully(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII);
+
+ tw32_carefully(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS);
+
+ aninfo.state = ANEG_STATE_UNKNOWN;
+ aninfo.cur_time = 0;
+ tick = 0;
+ while (++tick < 195000) {
+ status = tg3_fiber_aneg_smachine(tp, &aninfo);
+ if (status == ANEG_DONE ||
+ status == ANEG_FAILED)
+ break;
+
+ udelay(1);
+ }
+
+ tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ if (status == ANEG_DONE &&
+ (aninfo.flags &
+ (MR_AN_COMPLETE | MR_LINK_OK |
+ MR_LP_ADV_FULL_DUPLEX))) {
+ uint32_t local_adv, remote_adv;
+
+ local_adv = ADVERTISE_PAUSE_CAP;
+ remote_adv = 0;
+ if (aninfo.flags & MR_LP_ADV_SYM_PAUSE)
+ remote_adv |= LPA_PAUSE_CAP;
+ if (aninfo.flags & MR_LP_ADV_ASYM_PAUSE)
+ remote_adv |= LPA_PAUSE_ASYM;
+
+ tg3_setup_flow_control(tp, local_adv, remote_adv);
+
+ tp->tg3_flags |=
+ TG3_FLAG_GOT_SERDES_FLOWCTL;
+ current_link_up = 1;
+ }
+ for (i = 0; i < 60; i++) {
+ udelay(20);
+ tw32_carefully(MAC_STATUS,
+ (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED));
+ if ((tr32(MAC_STATUS) &
+ (MAC_STATUS_SYNC_CHANGED |
+ MAC_STATUS_CFG_CHANGED)) == 0)
+ break;
+ }
+ if (current_link_up == 0 &&
+ (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED)) {
+ current_link_up = 1;
+ }
+ } else {
+ /* Forcing 1000FD link up. */
+ current_link_up = 1;
+ }
+ }
+
+ tp->mac_mode &= ~MAC_MODE_LINK_POLARITY;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ tp->hw_status->status =
+ (SD_STATUS_UPDATED |
+ (tp->hw_status->status & ~SD_STATUS_LINK_CHG));
+
+ for (i = 0; i < 100; i++) {
+ udelay(20);
+ tw32_carefully(MAC_STATUS,
+ (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED));
+ if ((tr32(MAC_STATUS) &
+ (MAC_STATUS_SYNC_CHANGED |
+ MAC_STATUS_CFG_CHANGED)) == 0)
+ break;
+ }
+
+ if ((tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) == 0)
+ current_link_up = 0;
+
+ if (current_link_up == 1) {
+ tp->link_config.active_speed = SPEED_1000;
+ tp->link_config.active_duplex = DUPLEX_FULL;
+ } else {
+ tp->link_config.active_speed = SPEED_INVALID;
+ tp->link_config.active_duplex = DUPLEX_INVALID;
+ }
+
+ if (current_link_up != tp->carrier_ok) {
+ tp->carrier_ok = current_link_up;
+ tg3_link_report(tp);
+ } else {
+ uint32_t now_pause_cfg =
+ tp->tg3_flags & (TG3_FLAG_RX_PAUSE |
+ TG3_FLAG_TX_PAUSE);
+ if (orig_pause_cfg != now_pause_cfg ||
+ orig_active_speed != tp->link_config.active_speed ||
+ orig_active_duplex != tp->link_config.active_duplex)
+ tg3_link_report(tp);
+ }
+
+ if ((tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) == 0) {
+ tw32_carefully(MAC_MODE, tp->mac_mode | MAC_MODE_LINK_POLARITY);
+ if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) {
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+ }
+ }
+
+ return 0;
+}
+#else
+#define tg3_setup_fiber_phy(TP) (-EINVAL)
+#endif /* SUPPORT_FIBER_PHY */
+
+static int tg3_setup_phy(struct tg3 *tp)
+{
+ int err;
+
+ if (tp->phy_id == PHY_ID_SERDES) {
+ err = tg3_setup_fiber_phy(tp);
+ } else {
+ err = tg3_setup_copper_phy(tp);
+ }
+
+ if (tp->link_config.active_speed == SPEED_1000 &&
+ tp->link_config.active_duplex == DUPLEX_HALF)
+ tw32(MAC_TX_LENGTHS,
+ ((2 << TX_LENGTHS_IPG_CRS_SHIFT) |
+ (6 << TX_LENGTHS_IPG_SHIFT) |
+ (0xff << TX_LENGTHS_SLOT_TIME_SHIFT)));
+ else
+ tw32(MAC_TX_LENGTHS,
+ ((2 << TX_LENGTHS_IPG_CRS_SHIFT) |
+ (6 << TX_LENGTHS_IPG_SHIFT) |
+ (32 << TX_LENGTHS_SLOT_TIME_SHIFT)));
+
+ return err;
+}
+
+
+#define MAX_WAIT_CNT 1000
+
+/* To stop a block, clear the enable bit and poll till it
+ * clears.
+ */
+static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, uint32_t enable_bit)
+{
+ unsigned int i;
+ uint32_t val;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
+ switch(ofs) {
+ case RCVLSC_MODE:
+ case DMAC_MODE:
+ case MBFREE_MODE:
+ case BUFMGR_MODE:
+ case MEMARB_MODE:
+ /* We can't enable/disable these bits of the
+ * 5705, just say success.
+ */
+ return 0;
+ default:
+ break;
+ }
+ }
+ val = tr32(ofs);
+ val &= ~enable_bit;
+ tw32(ofs, val);
+ tr32(ofs);
+
+ for (i = 0; i < MAX_WAIT_CNT; i++) {
+ udelay(100);
+ val = tr32(ofs);
+ if ((val & enable_bit) == 0)
+ break;
+ }
+
+ if (i == MAX_WAIT_CNT) {
+ printf( "tg3_stop_block timed out, ofs=%#lx enable_bit=%3lx\n",
+ ofs, enable_bit );
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int tg3_abort_hw(struct tg3 *tp)
+{
+ int i, err;
+
+ tg3_disable_ints(tp);
+
+ tp->rx_mode &= ~RX_MODE_ENABLE;
+ tw32_carefully(MAC_RX_MODE, tp->rx_mode);
+
+ err = tg3_stop_block(tp, RCVBDI_MODE, RCVBDI_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RCVLPC_MODE, RCVLPC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RCVLSC_MODE, RCVLSC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RCVDBDI_MODE, RCVDBDI_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RCVDCC_MODE, RCVDCC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RCVCC_MODE, RCVCC_MODE_ENABLE);
+
+ err |= tg3_stop_block(tp, SNDBDS_MODE, SNDBDS_MODE_ENABLE);
+ err |= tg3_stop_block(tp, SNDBDI_MODE, SNDBDI_MODE_ENABLE);
+ err |= tg3_stop_block(tp, SNDDATAI_MODE, SNDDATAI_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RDMAC_MODE, RDMAC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, SNDDATAC_MODE, SNDDATAC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, SNDBDC_MODE, SNDBDC_MODE_ENABLE);
+ if (err)
+ goto out;
+
+ tp->mac_mode &= ~MAC_MODE_TDE_ENABLE;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ tp->tx_mode &= ~TX_MODE_ENABLE;
+ tw32_carefully(MAC_TX_MODE, tp->tx_mode);
+
+ for (i = 0; i < MAX_WAIT_CNT; i++) {
+ udelay(100);
+ if (!(tr32(MAC_TX_MODE) & TX_MODE_ENABLE))
+ break;
+ }
+ if (i >= MAX_WAIT_CNT) {
+ printf("tg3_abort_hw timed out TX_MODE_ENABLE will not clear MAC_TX_MODE=%x\n",
+ (unsigned int) tr32(MAC_TX_MODE));
+ return -ENODEV;
+ }
+
+ err = tg3_stop_block(tp, HOSTCC_MODE, HOSTCC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, WDMAC_MODE, WDMAC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, MBFREE_MODE, MBFREE_MODE_ENABLE);
+
+ tw32(FTQ_RESET, 0xffffffff);
+ tw32(FTQ_RESET, 0x00000000);
+
+ err |= tg3_stop_block(tp, BUFMGR_MODE, BUFMGR_MODE_ENABLE);
+ err |= tg3_stop_block(tp, MEMARB_MODE, MEMARB_MODE_ENABLE);
+ if (err)
+ goto out;
+
+ memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE);
+
+out:
+ return err;
+}
+
+static void tg3_chip_reset(struct tg3 *tp)
+{
+ uint32_t val;
+
+ if (!(tp->tg3_flags2 & TG3_FLG2_SUN_5704)) {
+ /* Force NVRAM to settle.
+ * This deals with a chip bug which can result in EEPROM
+ * corruption.
+ */
+ if (tp->tg3_flags & TG3_FLAG_NVRAM) {
+ int i;
+
+ tw32(NVRAM_SWARB, SWARB_REQ_SET1);
+ for (i = 0; i < 100000; i++) {
+ if (tr32(NVRAM_SWARB) & SWARB_GNT1)
+ break;
+ udelay(10);
+ }
+ }
+ }
+ /* In Etherboot we don't need to worry about the 5701
+ * REG_WRITE_BUG because we do all register writes indirectly.
+ */
+
+ // Alf: here patched
+ /* do the reset */
+ val = GRC_MISC_CFG_CORECLK_RESET;
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)
+ || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750)) {
+ val |= GRC_MISC_CFG_KEEP_GPHY_POWER;
+ }
+
+ // Alf : Please VALIDATE THIS.
+ // It is necessary in my case (5751) to prevent a reboot, but
+ // I have no idea about a side effect on any other version.
+ // It appears to be what's done in tigon3.c from Broadcom
+ if (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) {
+ tw32(GRC_MISC_CFG, 0x20000000) ;
+ val |= 0x20000000 ;
+ }
+
+ tw32(GRC_MISC_CFG, val);
+
+ /* Flush PCI posted writes. The normal MMIO registers
+ * are inaccessible at this time so this is the only
+ * way to make this reliably. I tried to use indirect
+ * register read/write but this upset some 5701 variants.
+ */
+ pci_read_config_dword(tp->pdev, PCI_COMMAND, &val);
+
+ udelay(120);
+
+ /* Re-enable indirect register accesses. */
+ pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
+ tp->misc_host_ctrl);
+
+ /* Set MAX PCI retry to zero. */
+ val = (PCISTATE_ROM_ENABLE | PCISTATE_ROM_RETRY_ENABLE);
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0 &&
+ (tp->tg3_flags & TG3_FLAG_PCIX_MODE))
+ val |= PCISTATE_RETRY_SAME_DMA;
+ pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE, val);
+
+ pci_restore_state(tp->pdev, tp->pci_cfg_state);
+
+ /* Make sure PCI-X relaxed ordering bit is clear. */
+ pci_read_config_dword(tp->pdev, TG3PCI_X_CAPS, &val);
+ val &= ~PCIX_CAPS_RELAXED_ORDERING;
+ pci_write_config_dword(tp->pdev, TG3PCI_X_CAPS, val);
+
+ tw32(MEMARB_MODE, MEMARB_MODE_ENABLE);
+
+ if (((tp->nic_sram_data_cfg & NIC_SRAM_DATA_CFG_MINI_PCI) != 0) &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) {
+ tp->pci_clock_ctrl |=
+ (CLOCK_CTRL_FORCE_CLKRUN | CLOCK_CTRL_CLKRUN_OENABLE);
+ tw32(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl);
+ }
+
+ tw32(TG3PCI_MISC_HOST_CTRL, tp->misc_host_ctrl);
+}
+
+static void tg3_stop_fw(struct tg3 *tp)
+{
+ if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) {
+ uint32_t val;
+ int i;
+
+ tg3_write_mem(NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_PAUSE_FW);
+ val = tr32(GRC_RX_CPU_EVENT);
+ val |= (1 << 14);
+ tw32(GRC_RX_CPU_EVENT, val);
+
+ /* Wait for RX cpu to ACK the event. */
+ for (i = 0; i < 100; i++) {
+ if (!(tr32(GRC_RX_CPU_EVENT) & (1 << 14)))
+ break;
+ udelay(1);
+ }
+ }
+}
+
+static int tg3_restart_fw(struct tg3 *tp, uint32_t state)
+{
+ uint32_t val;
+ int i;
+
+ tg3_write_mem(NIC_SRAM_FIRMWARE_MBOX,
+ NIC_SRAM_FIRMWARE_MBOX_MAGIC1);
+ /* Wait for firmware initialization to complete. */
+ for (i = 0; i < 100000; i++) {
+ tg3_read_mem(NIC_SRAM_FIRMWARE_MBOX, &val);
+ if (val == (uint32_t) ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1)
+ break;
+ udelay(10);
+ }
+ if (i >= 100000 &&
+ !(tp->tg3_flags2 & TG3_FLG2_SUN_5704)) {
+ printf ( "Firmware will not restart magic=%#lx\n",
+ val );
+ return -ENODEV;
+ }
+ if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
+ state = DRV_STATE_SUSPEND;
+ }
+
+ if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) &&
+ (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0)) {
+ // Enable PCIE bug fix
+ tg3_read_mem(0x7c00, &val);
+ tg3_write_mem(0x7c00, val | 0x02000000);
+ }
+ tg3_write_mem(NIC_SRAM_FW_DRV_STATE_MBOX, state);
+ return 0;
+}
+
+static int tg3_halt(struct tg3 *tp)
+{
+ tg3_stop_fw(tp);
+ tg3_abort_hw(tp);
+ tg3_chip_reset(tp);
+ return tg3_restart_fw(tp, DRV_STATE_UNLOAD);
+}
+
+static void __tg3_set_mac_addr(struct tg3 *tp)
+{
+ uint32_t addr_high, addr_low;
+ int i;
+
+ addr_high = ((tp->nic->node_addr[0] << 8) |
+ tp->nic->node_addr[1]);
+ addr_low = ((tp->nic->node_addr[2] << 24) |
+ (tp->nic->node_addr[3] << 16) |
+ (tp->nic->node_addr[4] << 8) |
+ (tp->nic->node_addr[5] << 0));
+ for (i = 0; i < 4; i++) {
+ tw32(MAC_ADDR_0_HIGH + (i * 8), addr_high);
+ tw32(MAC_ADDR_0_LOW + (i * 8), addr_low);
+ }
+
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700) &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705)) {
+ for(i = 0; i < 12; i++) {
+ tw32(MAC_EXTADDR_0_HIGH + (i * 8), addr_high);
+ tw32(MAC_EXTADDR_0_LOW + (i * 8), addr_low);
+ }
+ }
+ addr_high = (tp->nic->node_addr[0] +
+ tp->nic->node_addr[1] +
+ tp->nic->node_addr[2] +
+ tp->nic->node_addr[3] +
+ tp->nic->node_addr[4] +
+ tp->nic->node_addr[5]) &
+ TX_BACKOFF_SEED_MASK;
+ tw32(MAC_TX_BACKOFF_SEED, addr_high);
+}
+
+static void tg3_set_bdinfo(struct tg3 *tp, uint32_t bdinfo_addr,
+ dma_addr_t mapping, uint32_t maxlen_flags,
+ uint32_t nic_addr)
+{
+ tg3_write_mem((bdinfo_addr +
+ TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH),
+ ((uint64_t) mapping >> 32));
+ tg3_write_mem((bdinfo_addr +
+ TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW),
+ ((uint64_t) mapping & 0xffffffff));
+ tg3_write_mem((bdinfo_addr +
+ TG3_BDINFO_MAXLEN_FLAGS),
+ maxlen_flags);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ tg3_write_mem((bdinfo_addr + TG3_BDINFO_NIC_ADDR), nic_addr);
+ }
+}
+
+
+static void tg3_init_rings(struct tg3 *tp)
+{
+ unsigned i;
+
+ /* Zero out the tg3 variables */
+ memset(&tg3_bss, 0, sizeof(tg3_bss));
+ tp->rx_std = &tg3_bss.rx_std[0];
+ tp->rx_rcb = &tg3_bss.rx_rcb[0];
+ tp->tx_ring = &tg3_bss.tx_ring[0];
+ tp->hw_status = &tg3_bss.hw_status;
+ tp->hw_stats = &tg3_bss.hw_stats;
+ tp->mac_mode = 0;
+
+
+ /* Initialize tx/rx rings for packet processing.
+ *
+ * The chip has been shut down and the driver detached from
+ * the networking, so no interrupts or new tx packets will
+ * end up in the driver.
+ */
+
+ /* Initialize invariants of the rings, we only set this
+ * stuff once. This works because the card does not
+ * write into the rx buffer posting rings.
+ */
+ for (i = 0; i < TG3_RX_RING_SIZE; i++) {
+ struct tg3_rx_buffer_desc *rxd;
+
+ rxd = &tp->rx_std[i];
+ rxd->idx_len = (RX_PKT_BUF_SZ - 2 - 64) << RXD_LEN_SHIFT;
+ rxd->type_flags = (RXD_FLAG_END << RXD_FLAGS_SHIFT);
+ rxd->opaque = (RXD_OPAQUE_RING_STD | (i << RXD_OPAQUE_INDEX_SHIFT));
+
+ /* Note where the receive buffer for the ring is placed */
+ rxd->addr_hi = 0;
+ rxd->addr_lo = virt_to_bus(
+ &tg3_bss.rx_bufs[i%TG3_DEF_RX_RING_PENDING][2]);
+ }
+}
+
+#define TG3_WRITE_SETTINGS(TABLE) \
+do { \
+ const uint32_t *_table, *_end; \
+ _table = TABLE; \
+ _end = _table + sizeof(TABLE)/sizeof(TABLE[0]); \
+ for(; _table < _end; _table += 2) { \
+ tw32(_table[0], _table[1]); \
+ } \
+} while(0)
+
+
+/* initialize/reset the tg3 */
+static int tg3_setup_hw(struct tg3 *tp)
+{
+ uint32_t val, rdmac_mode;
+ int i, err, limit;
+
+ /* Simply don't support setups with extremly buggy firmware in etherboot */
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) {
+ printf("Error 5701_A0 firmware bug detected\n");
+ return -EINVAL;
+ }
+
+ tg3_disable_ints(tp);
+
+ /* Originally this was all in tg3_init_hw */
+
+ /* Force the chip into D0. */
+ tg3_set_power_state_0(tp);
+
+ tg3_switch_clocks(tp);
+
+ tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0);
+
+ // This should go somewhere else
+#define T3_PCIE_CAPABILITY_ID_REG 0xD0
+#define T3_PCIE_CAPABILITY_ID 0x10
+#define T3_PCIE_CAPABILITY_REG 0xD2
+
+ /* Originally this was all in tg3_reset_hw */
+
+ tg3_stop_fw(tp);
+
+ /* No need to call tg3_abort_hw here, it is called before tg3_setup_hw. */
+
+ tg3_chip_reset(tp);
+
+ tw32(GRC_MODE, tp->grc_mode); /* Redundant? */
+
+ err = tg3_restart_fw(tp, DRV_STATE_START);
+ if (err)
+ return err;
+
+ if (tp->phy_id == PHY_ID_SERDES) {
+ tp->mac_mode = MAC_MODE_PORT_MODE_TBI;
+ }
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+
+ /* This works around an issue with Athlon chipsets on
+ * B3 tigon3 silicon. This bit has no effect on any
+ * other revision.
+ * Alf: Except 5750 ! (which reboots)
+ */
+
+ if (!(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS))
+ tp->pci_clock_ctrl |= CLOCK_CTRL_DELAY_PCI_GRANT;
+ tw32_carefully(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl);
+
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0 &&
+ (tp->tg3_flags & TG3_FLAG_PCIX_MODE)) {
+ val = tr32(TG3PCI_PCISTATE);
+ val |= PCISTATE_RETRY_SAME_DMA;
+ tw32(TG3PCI_PCISTATE, val);
+ }
+
+ /* Descriptor ring init may make accesses to the
+ * NIC SRAM area to setup the TX descriptors, so we
+ * can only do this after the hardware has been
+ * successfully reset.
+ */
+ tg3_init_rings(tp);
+
+ /* Clear statistics/status block in chip */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ for (i = NIC_SRAM_STATS_BLK;
+ i < NIC_SRAM_STATUS_BLK + TG3_HW_STATUS_SIZE;
+ i += sizeof(uint32_t)) {
+ tg3_write_mem(i, 0);
+ udelay(40);
+ }
+ }
+
+ /* This value is determined during the probe time DMA
+ * engine test, tg3_setup_dma.
+ */
+ tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl);
+
+ tp->grc_mode &= ~(GRC_MODE_HOST_SENDBDS |
+ GRC_MODE_4X_NIC_SEND_RINGS |
+ GRC_MODE_NO_TX_PHDR_CSUM |
+ GRC_MODE_NO_RX_PHDR_CSUM);
+ tp->grc_mode |= GRC_MODE_HOST_SENDBDS;
+ tp->grc_mode |= GRC_MODE_NO_TX_PHDR_CSUM;
+ tp->grc_mode |= GRC_MODE_NO_RX_PHDR_CSUM;
+
+ tw32(GRC_MODE,
+ tp->grc_mode |
+ (GRC_MODE_IRQ_ON_MAC_ATTN | GRC_MODE_HOST_STACKUP));
+
+ /* Setup the timer prescalar register. Clock is always 66Mhz. */
+ tw32(GRC_MISC_CFG,
+ (65 << GRC_MISC_CFG_PRESCALAR_SHIFT));
+
+ /* Initialize MBUF/DESC pool. */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ tw32(BUFMGR_MB_POOL_ADDR, NIC_SRAM_MBUF_POOL_BASE);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704)
+ tw32(BUFMGR_MB_POOL_SIZE, NIC_SRAM_MBUF_POOL_SIZE64);
+ else
+ tw32(BUFMGR_MB_POOL_SIZE, NIC_SRAM_MBUF_POOL_SIZE96);
+ tw32(BUFMGR_DMA_DESC_POOL_ADDR, NIC_SRAM_DMA_DESC_POOL_BASE);
+ tw32(BUFMGR_DMA_DESC_POOL_SIZE, NIC_SRAM_DMA_DESC_POOL_SIZE);
+ }
+ if (!(tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE)) {
+ tw32(BUFMGR_MB_RDMA_LOW_WATER,
+ tp->bufmgr_config.mbuf_read_dma_low_water);
+ tw32(BUFMGR_MB_MACRX_LOW_WATER,
+ tp->bufmgr_config.mbuf_mac_rx_low_water);
+ tw32(BUFMGR_MB_HIGH_WATER,
+ tp->bufmgr_config.mbuf_high_water);
+ } else {
+ tw32(BUFMGR_MB_RDMA_LOW_WATER,
+ tp->bufmgr_config.mbuf_read_dma_low_water_jumbo);
+ tw32(BUFMGR_MB_MACRX_LOW_WATER,
+ tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo);
+ tw32(BUFMGR_MB_HIGH_WATER,
+ tp->bufmgr_config.mbuf_high_water_jumbo);
+ }
+ tw32(BUFMGR_DMA_LOW_WATER,
+ tp->bufmgr_config.dma_low_water);
+ tw32(BUFMGR_DMA_HIGH_WATER,
+ tp->bufmgr_config.dma_high_water);
+
+ tw32(BUFMGR_MODE, BUFMGR_MODE_ENABLE | BUFMGR_MODE_ATTN_ENABLE);
+ for (i = 0; i < 2000; i++) {
+ if (tr32(BUFMGR_MODE) & BUFMGR_MODE_ENABLE)
+ break;
+ udelay(10);
+ }
+ if (i >= 2000) {
+ printf("tg3_setup_hw cannot enable BUFMGR\n");
+ return -ENODEV;
+ }
+
+ tw32(FTQ_RESET, 0xffffffff);
+ tw32(FTQ_RESET, 0x00000000);
+ for (i = 0; i < 2000; i++) {
+ if (tr32(FTQ_RESET) == 0x00000000)
+ break;
+ udelay(10);
+ }
+ if (i >= 2000) {
+ printf("tg3_setup_hw cannot reset FTQ\n");
+ return -ENODEV;
+ }
+
+ /* Initialize TG3_BDINFO's at:
+ * RCVDBDI_STD_BD: standard eth size rx ring
+ * RCVDBDI_JUMBO_BD: jumbo frame rx ring
+ * RCVDBDI_MINI_BD: small frame rx ring (??? does not work)
+ *
+ * like so:
+ * TG3_BDINFO_HOST_ADDR: high/low parts of DMA address of ring
+ * TG3_BDINFO_MAXLEN_FLAGS: (rx max buffer size << 16) |
+ * ring attribute flags
+ * TG3_BDINFO_NIC_ADDR: location of descriptors in nic SRAM
+ *
+ * Standard receive ring @ NIC_SRAM_RX_BUFFER_DESC, 512 entries.
+ * Jumbo receive ring @ NIC_SRAM_RX_JUMBO_BUFFER_DESC, 256 entries.
+ *
+ * ??? No space allocated for mini receive ring? :(
+ *
+ * The size of each ring is fixed in the firmware, but the location is
+ * configurable.
+ */
+ {
+ static const uint32_t table_all[] = {
+ /* Setup replenish thresholds. */
+ RCVBDI_STD_THRESH, TG3_DEF_RX_RING_PENDING / 8,
+
+ /* Etherboot lives below 4GB */
+ RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH, 0,
+ RCVDBDI_STD_BD + TG3_BDINFO_NIC_ADDR, NIC_SRAM_RX_BUFFER_DESC,
+ };
+ static const uint32_t table_not_5705[] = {
+ /* Buffer maximum length */
+ RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS, RX_STD_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT,
+
+ /* Disable the mini frame rx ring */
+ RCVDBDI_MINI_BD + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED,
+
+ /* Disable the jumbo frame rx ring */
+ RCVBDI_JUMBO_THRESH, 0,
+ RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED,
+
+
+ };
+ TG3_WRITE_SETTINGS(table_all);
+ tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW,
+ virt_to_bus(tp->rx_std));
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
+ tw32(RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS,
+ RX_STD_MAX_SIZE_5705 << BDINFO_FLAGS_MAXLEN_SHIFT);
+ } else {
+ TG3_WRITE_SETTINGS(table_not_5705);
+ }
+ }
+
+
+ /* There is only one send ring on 5705, no need to explicitly
+ * disable the others.
+ */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ /* Clear out send RCB ring in SRAM. */
+ for (i = NIC_SRAM_SEND_RCB; i < NIC_SRAM_RCV_RET_RCB; i += TG3_BDINFO_SIZE)
+ tg3_write_mem(i + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED);
+ }
+
+ tp->tx_prod = 0;
+ tw32_mailbox(MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW, 0);
+ tw32_mailbox2(MAILBOX_SNDNIC_PROD_IDX_0 + TG3_64BIT_REG_LOW, 0);
+
+ tg3_set_bdinfo(tp,
+ NIC_SRAM_SEND_RCB,
+ virt_to_bus(tp->tx_ring),
+ (TG3_TX_RING_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT),
+ NIC_SRAM_TX_BUFFER_DESC);
+
+ /* There is only one receive return ring on 5705, no need to explicitly
+ * disable the others.
+ */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ for (i = NIC_SRAM_RCV_RET_RCB; i < NIC_SRAM_STATS_BLK; i += TG3_BDINFO_SIZE) {
+ tg3_write_mem(i + TG3_BDINFO_MAXLEN_FLAGS,
+ BDINFO_FLAGS_DISABLED);
+ }
+ }
+
+ tp->rx_rcb_ptr = 0;
+ tw32_mailbox2(MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW, 0);
+
+ tg3_set_bdinfo(tp,
+ NIC_SRAM_RCV_RET_RCB,
+ virt_to_bus(tp->rx_rcb),
+ (TG3_RX_RCB_RING_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT),
+ 0);
+
+ tp->rx_std_ptr = TG3_DEF_RX_RING_PENDING;
+ tw32_mailbox2(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW,
+ tp->rx_std_ptr);
+
+ tw32_mailbox2(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW, 0);
+
+ /* Initialize MAC address and backoff seed. */
+ __tg3_set_mac_addr(tp);
+
+ /* Calculate RDMAC_MODE setting early, we need it to determine
+ * the RCVLPC_STATE_ENABLE mask.
+ */
+ rdmac_mode = (RDMAC_MODE_ENABLE | RDMAC_MODE_TGTABORT_ENAB |
+ RDMAC_MODE_MSTABORT_ENAB | RDMAC_MODE_PARITYERR_ENAB |
+ RDMAC_MODE_ADDROFLOW_ENAB | RDMAC_MODE_FIFOOFLOW_ENAB |
+ RDMAC_MODE_FIFOURUN_ENAB | RDMAC_MODE_FIFOOREAD_ENAB |
+ RDMAC_MODE_LNGREAD_ENAB);
+ if (tp->tg3_flags & TG3_FLAG_SPLIT_MODE)
+ rdmac_mode |= RDMAC_MODE_SPLIT_ENABLE;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
+ if (tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) {
+ if (!(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH) &&
+ !(tp->tg3_flags2 & TG3_FLG2_IS_5788)) {
+ rdmac_mode |= RDMAC_MODE_FIFO_LONG_BURST;
+ }
+ }
+ }
+
+ /* Setup host coalescing engine. */
+ tw32(HOSTCC_MODE, 0);
+ for (i = 0; i < 2000; i++) {
+ if (!(tr32(HOSTCC_MODE) & HOSTCC_MODE_ENABLE))
+ break;
+ udelay(10);
+ }
+
+ tp->mac_mode = MAC_MODE_TXSTAT_ENABLE | MAC_MODE_RXSTAT_ENABLE |
+ MAC_MODE_TDE_ENABLE | MAC_MODE_RDE_ENABLE | MAC_MODE_FHDE_ENABLE;
+ tw32_carefully(MAC_MODE, tp->mac_mode | MAC_MODE_RXSTAT_CLEAR | MAC_MODE_TXSTAT_CLEAR);
+
+ tp->grc_local_ctrl = GRC_LCLCTRL_INT_ON_ATTN | GRC_LCLCTRL_AUTO_SEEPROM;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700)
+ tp->grc_local_ctrl |= (GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OUTPUT1);
+ tw32_carefully(GRC_LOCAL_CTRL, tp->grc_local_ctrl);
+
+ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0);
+ tr32(MAILBOX_INTERRUPT_0);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ tw32_carefully(DMAC_MODE, DMAC_MODE_ENABLE);
+ }
+
+ val = ( WDMAC_MODE_ENABLE | WDMAC_MODE_TGTABORT_ENAB |
+ WDMAC_MODE_MSTABORT_ENAB | WDMAC_MODE_PARITYERR_ENAB |
+ WDMAC_MODE_ADDROFLOW_ENAB | WDMAC_MODE_FIFOOFLOW_ENAB |
+ WDMAC_MODE_FIFOURUN_ENAB | WDMAC_MODE_FIFOOREAD_ENAB |
+ WDMAC_MODE_LNGREAD_ENAB);
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) &&
+ ((tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH) != 0) &&
+ !(tp->tg3_flags2 & TG3_FLG2_IS_5788)) {
+ val |= WDMAC_MODE_RX_ACCEL;
+ }
+ tw32_carefully(WDMAC_MODE, val);
+
+ if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) != 0) {
+ val = tr32(TG3PCI_X_CAPS);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) {
+ val &= PCIX_CAPS_BURST_MASK;
+ val |= (PCIX_CAPS_MAX_BURST_CPIOB << PCIX_CAPS_BURST_SHIFT);
+ } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) {
+ val &= ~(PCIX_CAPS_SPLIT_MASK | PCIX_CAPS_BURST_MASK);
+ val |= (PCIX_CAPS_MAX_BURST_CPIOB << PCIX_CAPS_BURST_SHIFT);
+ if (tp->tg3_flags & TG3_FLAG_SPLIT_MODE)
+ val |= (tp->split_mode_max_reqs <<
+ PCIX_CAPS_SPLIT_SHIFT);
+ }
+ tw32(TG3PCI_X_CAPS, val);
+ }
+
+ tw32_carefully(RDMAC_MODE, rdmac_mode);
+ {
+ static const uint32_t table_all[] = {
+ /* MTU + ethernet header + FCS + optional VLAN tag */
+ MAC_RX_MTU_SIZE, ETH_MAX_MTU + ETH_HLEN + 8,
+
+ /* The slot time is changed by tg3_setup_phy if we
+ * run at gigabit with half duplex.
+ */
+ MAC_TX_LENGTHS,
+ (2 << TX_LENGTHS_IPG_CRS_SHIFT) |
+ (6 << TX_LENGTHS_IPG_SHIFT) |
+ (32 << TX_LENGTHS_SLOT_TIME_SHIFT),
+
+ /* Receive rules. */
+ MAC_RCV_RULE_CFG, RCV_RULE_CFG_DEFAULT_CLASS,
+ RCVLPC_CONFIG, 0x0181,
+
+ /* Receive/send statistics. */
+ RCVLPC_STATS_ENABLE, 0xffffff,
+ RCVLPC_STATSCTRL, RCVLPC_STATSCTRL_ENABLE,
+ SNDDATAI_STATSENAB, 0xffffff,
+ SNDDATAI_STATSCTRL, (SNDDATAI_SCTRL_ENABLE |SNDDATAI_SCTRL_FASTUPD),
+
+ /* Host coalescing engine */
+ HOSTCC_RXCOL_TICKS, 0,
+ HOSTCC_TXCOL_TICKS, LOW_TXCOL_TICKS,
+ HOSTCC_RXMAX_FRAMES, 1,
+ HOSTCC_TXMAX_FRAMES, LOW_RXMAX_FRAMES,
+ HOSTCC_RXCOAL_MAXF_INT, 1,
+ HOSTCC_TXCOAL_MAXF_INT, 0,
+
+ /* Status/statistics block address. */
+ /* Etherboot lives below 4GB, so HIGH == 0 */
+ HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH, 0,
+
+ /* No need to enable 32byte coalesce mode. */
+ HOSTCC_MODE, HOSTCC_MODE_ENABLE | 0,
+
+ RCVCC_MODE, RCVCC_MODE_ENABLE | RCVCC_MODE_ATTN_ENABLE,
+ RCVLPC_MODE, RCVLPC_MODE_ENABLE,
+
+ RCVDCC_MODE, RCVDCC_MODE_ENABLE | RCVDCC_MODE_ATTN_ENABLE,
+
+ SNDDATAC_MODE, SNDDATAC_MODE_ENABLE,
+ SNDBDC_MODE, SNDBDC_MODE_ENABLE | SNDBDC_MODE_ATTN_ENABLE,
+ RCVBDI_MODE, RCVBDI_MODE_ENABLE | RCVBDI_MODE_RCB_ATTN_ENAB,
+ RCVDBDI_MODE, RCVDBDI_MODE_ENABLE | RCVDBDI_MODE_INV_RING_SZ,
+ SNDDATAI_MODE, SNDDATAI_MODE_ENABLE,
+ SNDBDI_MODE, SNDBDI_MODE_ENABLE | SNDBDI_MODE_ATTN_ENABLE,
+ SNDBDS_MODE, SNDBDS_MODE_ENABLE | SNDBDS_MODE_ATTN_ENABLE,
+
+ /* Accept all multicast frames. */
+ MAC_HASH_REG_0, 0xffffffff,
+ MAC_HASH_REG_1, 0xffffffff,
+ MAC_HASH_REG_2, 0xffffffff,
+ MAC_HASH_REG_3, 0xffffffff,
+ };
+ static const uint32_t table_not_5705[] = {
+ /* Host coalescing engine */
+ HOSTCC_RXCOAL_TICK_INT, 0,
+ HOSTCC_TXCOAL_TICK_INT, 0,
+
+ /* Status/statistics block address. */
+ /* Etherboot lives below 4GB, so HIGH == 0 */
+ HOSTCC_STAT_COAL_TICKS, DEFAULT_STAT_COAL_TICKS,
+ HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH, 0,
+ HOSTCC_STATS_BLK_NIC_ADDR, NIC_SRAM_STATS_BLK,
+ HOSTCC_STATUS_BLK_NIC_ADDR, NIC_SRAM_STATUS_BLK,
+
+ RCVLSC_MODE, RCVLSC_MODE_ENABLE | RCVLSC_MODE_ATTN_ENABLE,
+
+ MBFREE_MODE, MBFREE_MODE_ENABLE,
+ };
+ TG3_WRITE_SETTINGS(table_all);
+ tw32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW,
+ virt_to_bus(tp->hw_stats));
+ tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW,
+ virt_to_bus(tp->hw_status));
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ TG3_WRITE_SETTINGS(table_not_5705);
+ }
+ }
+
+ tp->tx_mode = TX_MODE_ENABLE;
+ tw32_carefully(MAC_TX_MODE, tp->tx_mode);
+
+ tp->rx_mode = RX_MODE_ENABLE;
+ tw32_carefully(MAC_RX_MODE, tp->rx_mode);
+
+ tp->mi_mode = MAC_MI_MODE_BASE;
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+
+ tw32(MAC_LED_CTRL, 0);
+ tw32(MAC_MI_STAT, MAC_MI_STAT_LNKSTAT_ATTN_ENAB);
+ if (tp->phy_id == PHY_ID_SERDES) {
+ tw32_carefully(MAC_RX_MODE, RX_MODE_RESET);
+ }
+ tp->rx_mode |= RX_MODE_KEEP_VLAN_TAG; /* drop tagged vlan packets */
+ tw32_carefully(MAC_RX_MODE, tp->rx_mode);
+
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5703_A1)
+ tw32(MAC_SERDES_CFG, 0x616000);
+
+ /* Prevent chip from dropping frames when flow control
+ * is enabled.
+ */
+ tw32(MAC_LOW_WMARK_MAX_RX_FRAME, 2);
+ tr32(MAC_LOW_WMARK_MAX_RX_FRAME);
+
+ err = tg3_setup_phy(tp);
+
+ /* Ignore CRC stats */
+
+ /* Initialize receive rules. */
+ tw32(MAC_RCV_RULE_0, 0xc2000000 & RCV_RULE_DISABLE_MASK);
+ tw32(MAC_RCV_VALUE_0, 0xffffffff & RCV_RULE_DISABLE_MASK);
+ tw32(MAC_RCV_RULE_1, 0x86000004 & RCV_RULE_DISABLE_MASK);
+ tw32(MAC_RCV_VALUE_1, 0xffffffff & RCV_RULE_DISABLE_MASK);
+
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)
+ || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750))
+ limit = 8;
+ else
+ limit = 16;
+ if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF)
+ limit -= 4;
+ switch (limit) {
+ case 16: tw32(MAC_RCV_RULE_15, 0); tw32(MAC_RCV_VALUE_15, 0);
+ case 15: tw32(MAC_RCV_RULE_14, 0); tw32(MAC_RCV_VALUE_14, 0);
+ case 14: tw32(MAC_RCV_RULE_13, 0); tw32(MAC_RCV_VALUE_13, 0);
+ case 13: tw32(MAC_RCV_RULE_12, 0); tw32(MAC_RCV_VALUE_12, 0);
+ case 12: tw32(MAC_RCV_RULE_11, 0); tw32(MAC_RCV_VALUE_11, 0);
+ case 11: tw32(MAC_RCV_RULE_10, 0); tw32(MAC_RCV_VALUE_10, 0);
+ case 10: tw32(MAC_RCV_RULE_9, 0); tw32(MAC_RCV_VALUE_9, 0);
+ case 9: tw32(MAC_RCV_RULE_8, 0); tw32(MAC_RCV_VALUE_8, 0);
+ case 8: tw32(MAC_RCV_RULE_7, 0); tw32(MAC_RCV_VALUE_7, 0);
+ case 7: tw32(MAC_RCV_RULE_6, 0); tw32(MAC_RCV_VALUE_6, 0);
+ case 6: tw32(MAC_RCV_RULE_5, 0); tw32(MAC_RCV_VALUE_5, 0);
+ case 5: tw32(MAC_RCV_RULE_4, 0); tw32(MAC_RCV_VALUE_4, 0);
+ case 4: /* tw32(MAC_RCV_RULE_3, 0); tw32(MAC_RCV_VALUE_3, 0); */
+ case 3: /* tw32(MAC_RCV_RULE_2, 0); tw32(MAC_RCV_VALUE_2, 0); */
+ case 2:
+ case 1:
+ default:
+ break;
+ };
+
+ return err;
+}
+
+
+
+/* Chips other than 5700/5701 use the NVRAM for fetching info. */
+static void tg3_nvram_init(struct tg3 *tp)
+{
+ tw32(GRC_EEPROM_ADDR,
+ (EEPROM_ADDR_FSM_RESET |
+ (EEPROM_DEFAULT_CLOCK_PERIOD <<
+ EEPROM_ADDR_CLKPERD_SHIFT)));
+
+ mdelay(1);
+
+ /* Enable seeprom accesses. */
+ tw32_carefully(GRC_LOCAL_CTRL,
+ tr32(GRC_LOCAL_CTRL) | GRC_LCLCTRL_AUTO_SEEPROM);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) {
+ uint32_t nvcfg1 = tr32(NVRAM_CFG1);
+
+ tp->tg3_flags |= TG3_FLAG_NVRAM;
+ if (nvcfg1 & NVRAM_CFG1_FLASHIF_ENAB) {
+ if (nvcfg1 & NVRAM_CFG1_BUFFERED_MODE)
+ tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;
+ } else {
+ nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS;
+ tw32(NVRAM_CFG1, nvcfg1);
+ }
+
+ } else {
+ tp->tg3_flags &= ~(TG3_FLAG_NVRAM | TG3_FLAG_NVRAM_BUFFERED);
+ }
+}
+
+
+static int tg3_nvram_read_using_eeprom(
+ struct tg3 *tp __unused, uint32_t offset, uint32_t *val)
+{
+ uint32_t tmp;
+ int i;
+
+ if (offset > EEPROM_ADDR_ADDR_MASK ||
+ (offset % 4) != 0) {
+ return -EINVAL;
+ }
+
+ tmp = tr32(GRC_EEPROM_ADDR) & ~(EEPROM_ADDR_ADDR_MASK |
+ EEPROM_ADDR_DEVID_MASK |
+ EEPROM_ADDR_READ);
+ tw32(GRC_EEPROM_ADDR,
+ tmp |
+ (0 << EEPROM_ADDR_DEVID_SHIFT) |
+ ((offset << EEPROM_ADDR_ADDR_SHIFT) &
+ EEPROM_ADDR_ADDR_MASK) |
+ EEPROM_ADDR_READ | EEPROM_ADDR_START);
+
+ for (i = 0; i < 10000; i++) {
+ tmp = tr32(GRC_EEPROM_ADDR);
+
+ if (tmp & EEPROM_ADDR_COMPLETE)
+ break;
+ udelay(100);
+ }
+ if (!(tmp & EEPROM_ADDR_COMPLETE)) {
+ return -EBUSY;
+ }
+
+ *val = tr32(GRC_EEPROM_DATA);
+ return 0;
+}
+
+static int tg3_nvram_read(struct tg3 *tp, uint32_t offset, uint32_t *val)
+{
+ int i, saw_done_clear;
+
+ if (!(tp->tg3_flags & TG3_FLAG_NVRAM))
+ return tg3_nvram_read_using_eeprom(tp, offset, val);
+
+ if (tp->tg3_flags & TG3_FLAG_NVRAM_BUFFERED)
+ offset = ((offset / NVRAM_BUFFERED_PAGE_SIZE) <<
+ NVRAM_BUFFERED_PAGE_POS) +
+ (offset % NVRAM_BUFFERED_PAGE_SIZE);
+
+ if (offset > NVRAM_ADDR_MSK)
+ return -EINVAL;
+
+ tw32(NVRAM_SWARB, SWARB_REQ_SET1);
+ for (i = 0; i < 1000; i++) {
+ if (tr32(NVRAM_SWARB) & SWARB_GNT1)
+ break;
+ udelay(20);
+ }
+
+ tw32(NVRAM_ADDR, offset);
+ tw32(NVRAM_CMD,
+ NVRAM_CMD_RD | NVRAM_CMD_GO |
+ NVRAM_CMD_FIRST | NVRAM_CMD_LAST | NVRAM_CMD_DONE);
+
+ /* Wait for done bit to clear then set again. */
+ saw_done_clear = 0;
+ for (i = 0; i < 1000; i++) {
+ udelay(10);
+ if (!saw_done_clear &&
+ !(tr32(NVRAM_CMD) & NVRAM_CMD_DONE))
+ saw_done_clear = 1;
+ else if (saw_done_clear &&
+ (tr32(NVRAM_CMD) & NVRAM_CMD_DONE))
+ break;
+ }
+ if (i >= 1000) {
+ tw32(NVRAM_SWARB, SWARB_REQ_CLR1);
+ return -EBUSY;
+ }
+
+ *val = bswap_32(tr32(NVRAM_RDDATA));
+ tw32(NVRAM_SWARB, 0x20);
+
+ return 0;
+}
+
+struct subsys_tbl_ent {
+ uint16_t subsys_vendor, subsys_devid;
+ uint32_t phy_id;
+};
+
+static struct subsys_tbl_ent subsys_id_to_phy_id[] = {
+ /* Broadcom boards. */
+ { 0x14e4, 0x1644, PHY_ID_BCM5401 }, /* BCM95700A6 */
+ { 0x14e4, 0x0001, PHY_ID_BCM5701 }, /* BCM95701A5 */
+ { 0x14e4, 0x0002, PHY_ID_BCM8002 }, /* BCM95700T6 */
+ { 0x14e4, 0x0003, PHY_ID_SERDES }, /* BCM95700A9 */
+ { 0x14e4, 0x0005, PHY_ID_BCM5701 }, /* BCM95701T1 */
+ { 0x14e4, 0x0006, PHY_ID_BCM5701 }, /* BCM95701T8 */
+ { 0x14e4, 0x0007, PHY_ID_SERDES }, /* BCM95701A7 */
+ { 0x14e4, 0x0008, PHY_ID_BCM5701 }, /* BCM95701A10 */
+ { 0x14e4, 0x8008, PHY_ID_BCM5701 }, /* BCM95701A12 */
+ { 0x14e4, 0x0009, PHY_ID_BCM5701 }, /* BCM95703Ax1 */
+ { 0x14e4, 0x8009, PHY_ID_BCM5701 }, /* BCM95703Ax2 */
+
+ /* 3com boards. */
+ { PCI_VENDOR_ID_3COM, 0x1000, PHY_ID_BCM5401 }, /* 3C996T */
+ { PCI_VENDOR_ID_3COM, 0x1006, PHY_ID_BCM5701 }, /* 3C996BT */
+ /* { PCI_VENDOR_ID_3COM, 0x1002, PHY_ID_XXX }, 3C996CT */
+ /* { PCI_VENDOR_ID_3COM, 0x1003, PHY_ID_XXX }, 3C997T */
+ { PCI_VENDOR_ID_3COM, 0x1004, PHY_ID_SERDES }, /* 3C996SX */
+ /* { PCI_VENDOR_ID_3COM, 0x1005, PHY_ID_XXX }, 3C997SZ */
+ { PCI_VENDOR_ID_3COM, 0x1007, PHY_ID_BCM5701 }, /* 3C1000T */
+ { PCI_VENDOR_ID_3COM, 0x1008, PHY_ID_BCM5701 }, /* 3C940BR01 */
+
+ /* DELL boards. */
+ { PCI_VENDOR_ID_DELL, 0x00d1, PHY_ID_BCM5401 }, /* VIPER */
+ { PCI_VENDOR_ID_DELL, 0x0106, PHY_ID_BCM5401 }, /* JAGUAR */
+ { PCI_VENDOR_ID_DELL, 0x0109, PHY_ID_BCM5411 }, /* MERLOT */
+ { PCI_VENDOR_ID_DELL, 0x010a, PHY_ID_BCM5411 }, /* SLIM_MERLOT */
+ { PCI_VENDOR_ID_DELL, 0x0179, PHY_ID_BCM5751 }, /* EtherXpress */
+
+ /* Fujitsu Siemens Computer */
+ { PCI_VENDOR_ID_FSC, 0x105d, PHY_ID_BCM5751 }, /* Futro C200 */
+
+ /* Compaq boards. */
+ { PCI_VENDOR_ID_COMPAQ, 0x007c, PHY_ID_BCM5701 }, /* BANSHEE */
+ { PCI_VENDOR_ID_COMPAQ, 0x009a, PHY_ID_BCM5701 }, /* BANSHEE_2 */
+ { PCI_VENDOR_ID_COMPAQ, 0x007d, PHY_ID_SERDES }, /* CHANGELING */
+ { PCI_VENDOR_ID_COMPAQ, 0x0085, PHY_ID_BCM5701 }, /* NC7780 */
+ { PCI_VENDOR_ID_COMPAQ, 0x0099, PHY_ID_BCM5701 } /* NC7780_2 */
+};
+
+static int tg3_phy_probe(struct tg3 *tp)
+{
+ uint32_t eeprom_phy_id, hw_phy_id_1, hw_phy_id_2;
+ uint32_t hw_phy_id, hw_phy_id_masked;
+ enum phy_led_mode eeprom_led_mode;
+ uint32_t val;
+ unsigned i;
+ int eeprom_signature_found, err;
+
+ tp->phy_id = PHY_ID_INVALID;
+
+ for (i = 0; i < sizeof(subsys_id_to_phy_id)/sizeof(subsys_id_to_phy_id[0]); i++) {
+ if ((subsys_id_to_phy_id[i].subsys_vendor == tp->subsystem_vendor) &&
+ (subsys_id_to_phy_id[i].subsys_devid == tp->subsystem_device)) {
+ tp->phy_id = subsys_id_to_phy_id[i].phy_id;
+ break;
+ }
+ }
+
+ eeprom_phy_id = PHY_ID_INVALID;
+ eeprom_led_mode = led_mode_auto;
+ eeprom_signature_found = 0;
+ tg3_read_mem(NIC_SRAM_DATA_SIG, &val);
+ if (val == NIC_SRAM_DATA_SIG_MAGIC) {
+ uint32_t nic_cfg;
+
+ tg3_read_mem(NIC_SRAM_DATA_CFG, &nic_cfg);
+ tp->nic_sram_data_cfg = nic_cfg;
+
+ eeprom_signature_found = 1;
+
+ if ((nic_cfg & NIC_SRAM_DATA_CFG_PHY_TYPE_MASK) ==
+ NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER) {
+ eeprom_phy_id = PHY_ID_SERDES;
+ } else {
+ uint32_t nic_phy_id;
+
+ tg3_read_mem(NIC_SRAM_DATA_PHY_ID, &nic_phy_id);
+ if (nic_phy_id != 0) {
+ uint32_t id1 = nic_phy_id & NIC_SRAM_DATA_PHY_ID1_MASK;
+ uint32_t id2 = nic_phy_id & NIC_SRAM_DATA_PHY_ID2_MASK;
+
+ eeprom_phy_id = (id1 >> 16) << 10;
+ eeprom_phy_id |= (id2 & 0xfc00) << 16;
+ eeprom_phy_id |= (id2 & 0x03ff) << 0;
+ }
+ }
+
+ switch (nic_cfg & NIC_SRAM_DATA_CFG_LED_MODE_MASK) {
+ case NIC_SRAM_DATA_CFG_LED_TRIPLE_SPD:
+ eeprom_led_mode = led_mode_three_link;
+ break;
+
+ case NIC_SRAM_DATA_CFG_LED_LINK_SPD:
+ eeprom_led_mode = led_mode_link10;
+ break;
+
+ default:
+ eeprom_led_mode = led_mode_auto;
+ break;
+ };
+ if (((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) &&
+ (nic_cfg & NIC_SRAM_DATA_CFG_EEPROM_WP)) {
+ tp->tg3_flags |= TG3_FLAG_EEPROM_WRITE_PROT;
+ }
+
+ if (nic_cfg & NIC_SRAM_DATA_CFG_ASF_ENABLE)
+ tp->tg3_flags |= TG3_FLAG_ENABLE_ASF;
+ if (nic_cfg & NIC_SRAM_DATA_CFG_FIBER_WOL)
+ tp->tg3_flags |= TG3_FLAG_SERDES_WOL_CAP;
+ }
+
+ /* Now read the physical PHY_ID from the chip and verify
+ * that it is sane. If it doesn't look good, we fall back
+ * to either the hard-coded table based PHY_ID and failing
+ * that the value found in the eeprom area.
+ */
+ err = tg3_readphy(tp, MII_PHYSID1, &hw_phy_id_1);
+ err |= tg3_readphy(tp, MII_PHYSID2, &hw_phy_id_2);
+
+ hw_phy_id = (hw_phy_id_1 & 0xffff) << 10;
+ hw_phy_id |= (hw_phy_id_2 & 0xfc00) << 16;
+ hw_phy_id |= (hw_phy_id_2 & 0x03ff) << 0;
+
+ hw_phy_id_masked = hw_phy_id & PHY_ID_MASK;
+
+ if (!err && KNOWN_PHY_ID(hw_phy_id_masked)) {
+ tp->phy_id = hw_phy_id;
+ } else {
+ /* phy_id currently holds the value found in the
+ * subsys_id_to_phy_id[] table or PHY_ID_INVALID
+ * if a match was not found there.
+ */
+ if (tp->phy_id == PHY_ID_INVALID) {
+ if (!eeprom_signature_found ||
+ !KNOWN_PHY_ID(eeprom_phy_id & PHY_ID_MASK))
+ return -ENODEV;
+ tp->phy_id = eeprom_phy_id;
+ }
+ }
+
+ err = tg3_phy_reset(tp);
+ if (err)
+ return err;
+
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 ||
+ tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) {
+ uint32_t mii_tg3_ctrl;
+
+ /* These chips, when reset, only advertise 10Mb
+ * capabilities. Fix that.
+ */
+ err = tg3_writephy(tp, MII_ADVERTISE,
+ (ADVERTISE_CSMA |
+ ADVERTISE_PAUSE_CAP |
+ ADVERTISE_10HALF |
+ ADVERTISE_10FULL |
+ ADVERTISE_100HALF |
+ ADVERTISE_100FULL));
+ mii_tg3_ctrl = (MII_TG3_CTRL_ADV_1000_HALF |
+ MII_TG3_CTRL_ADV_1000_FULL |
+ MII_TG3_CTRL_AS_MASTER |
+ MII_TG3_CTRL_ENABLE_AS_MASTER);
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+ mii_tg3_ctrl = 0;
+
+ err |= tg3_writephy(tp, MII_TG3_CTRL, mii_tg3_ctrl);
+ err |= tg3_writephy(tp, MII_BMCR,
+ (BMCR_ANRESTART | BMCR_ANENABLE));
+ }
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) {
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00);
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f);
+ tg3_writedsp(tp, MII_TG3_DSP_RW_PORT, 0x2aaa);
+ }
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) {
+ tg3_writephy(tp, 0x1c, 0x8d68);
+ tg3_writephy(tp, 0x1c, 0x8d68);
+ }
+
+ /* Enable Ethernet@WireSpeed */
+ tg3_phy_set_wirespeed(tp);
+
+ if (!err && ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401)) {
+ err = tg3_init_5401phy_dsp(tp);
+ }
+
+ /* Determine the PHY led mode.
+ * Be careful if this gets set wrong it can result in an inability to
+ * establish a link.
+ */
+ if (tp->phy_id == PHY_ID_SERDES) {
+ tp->led_mode = led_mode_three_link;
+ }
+ else if (tp->subsystem_vendor == PCI_VENDOR_ID_DELL) {
+ tp->led_mode = led_mode_link10;
+ } else {
+ tp->led_mode = led_mode_three_link;
+ if (eeprom_signature_found &&
+ eeprom_led_mode != led_mode_auto)
+ tp->led_mode = eeprom_led_mode;
+ }
+
+ if (tp->phy_id == PHY_ID_SERDES)
+ tp->link_config.advertising =
+ (ADVERTISED_1000baseT_Half |
+ ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg |
+ ADVERTISED_FIBRE);
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+ tp->link_config.advertising &=
+ ~(ADVERTISED_1000baseT_Half |
+ ADVERTISED_1000baseT_Full);
+
+ return err;
+}
+
+#if SUPPORT_PARTNO_STR
+static void tg3_read_partno(struct tg3 *tp)
+{
+ unsigned char vpd_data[256];
+ int i;
+
+ for (i = 0; i < 256; i += 4) {
+ uint32_t tmp;
+
+ if (tg3_nvram_read(tp, 0x100 + i, &tmp))
+ goto out_not_found;
+
+ vpd_data[i + 0] = ((tmp >> 0) & 0xff);
+ vpd_data[i + 1] = ((tmp >> 8) & 0xff);
+ vpd_data[i + 2] = ((tmp >> 16) & 0xff);
+ vpd_data[i + 3] = ((tmp >> 24) & 0xff);
+ }
+
+ /* Now parse and find the part number. */
+ for (i = 0; i < 256; ) {
+ unsigned char val = vpd_data[i];
+ int block_end;
+
+ if (val == 0x82 || val == 0x91) {
+ i = (i + 3 +
+ (vpd_data[i + 1] +
+ (vpd_data[i + 2] << 8)));
+ continue;
+ }
+
+ if (val != 0x90)
+ goto out_not_found;
+
+ block_end = (i + 3 +
+ (vpd_data[i + 1] +
+ (vpd_data[i + 2] << 8)));
+ i += 3;
+ while (i < block_end) {
+ if (vpd_data[i + 0] == 'P' &&
+ vpd_data[i + 1] == 'N') {
+ int partno_len = vpd_data[i + 2];
+
+ if (partno_len > 24)
+ goto out_not_found;
+
+ memcpy(tp->board_part_number,
+ &vpd_data[i + 3],
+ partno_len);
+
+ /* Success. */
+ return;
+ }
+ }
+
+ /* Part number not found. */
+ goto out_not_found;
+ }
+
+out_not_found:
+ memcpy(tp->board_part_number, "none", sizeof("none"));
+}
+#else
+#define tg3_read_partno(TP) ((TP)->board_part_number[0] = '\0')
+#endif
+
+static int tg3_get_invariants(struct tg3 *tp)
+{
+ uint32_t misc_ctrl_reg;
+ uint32_t pci_state_reg, grc_misc_cfg;
+ uint16_t pci_cmd;
+ uint8_t pci_latency;
+ uint32_t val ;
+ int err;
+
+ /* Read the subsystem vendor and device ids */
+ pci_read_config_word(tp->pdev, PCI_SUBSYSTEM_VENDOR_ID, &tp->subsystem_vendor);
+ pci_read_config_word(tp->pdev, PCI_SUBSYSTEM_ID, &tp->subsystem_device);
+
+ /* The sun_5704 code needs infrastructure etherboot does have
+ * ignore it for now.
+ */
+
+ /* If we have an AMD 762 or Intel ICH/ICH0 chipset, write
+ * reordering to the mailbox registers done by the host
+ * controller can cause major troubles. We read back from
+ * every mailbox register write to force the writes to be
+ * posted to the chip in order.
+ *
+ * TG3_FLAG_MBOX_WRITE_REORDER has been forced on.
+ */
+
+ /* Force memory write invalidate off. If we leave it on,
+ * then on 5700_BX chips we have to enable a workaround.
+ * The workaround is to set the TG3PCI_DMA_RW_CTRL boundry
+ * to match the cacheline size. The Broadcom driver have this
+ * workaround but turns MWI off all the times so never uses
+ * it. This seems to suggest that the workaround is insufficient.
+ */
+ pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd);
+ pci_cmd &= ~PCI_COMMAND_INVALIDATE;
+ /* Also, force SERR#/PERR# in PCI command. */
+ pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
+ pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd);
+
+ /* It is absolutely critical that TG3PCI_MISC_HOST_CTRL
+ * has the register indirect write enable bit set before
+ * we try to access any of the MMIO registers. It is also
+ * critical that the PCI-X hw workaround situation is decided
+ * before that as well.
+ */
+ pci_read_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, &misc_ctrl_reg);
+
+ tp->pci_chip_rev_id = (misc_ctrl_reg >> MISC_HOST_CTRL_CHIPREV_SHIFT);
+
+ /* Initialize misc host control in PCI block. */
+ tp->misc_host_ctrl |= (misc_ctrl_reg &
+ MISC_HOST_CTRL_CHIPREV);
+ pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
+ tp->misc_host_ctrl);
+
+ pci_read_config_byte(tp->pdev, PCI_LATENCY_TIMER, &pci_latency);
+ if (pci_latency < 64) {
+ pci_write_config_byte(tp->pdev, PCI_LATENCY_TIMER, 64);
+ }
+
+ pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE, &pci_state_reg);
+
+ /* If this is a 5700 BX chipset, and we are in PCI-X
+ * mode, enable register write workaround.
+ *
+ * The workaround is to use indirect register accesses
+ * for all chip writes not to mailbox registers.
+ *
+ * In etherboot to simplify things we just always use this work around.
+ */
+ if ((pci_state_reg & PCISTATE_CONV_PCI_MODE) == 0) {
+ tp->tg3_flags |= TG3_FLAG_PCIX_MODE;
+ }
+ /* Back to back register writes can cause problems on the 5701,
+ * the workaround is to read back all reg writes except those to
+ * mailbox regs.
+ * In etherboot we always use indirect register accesses so
+ * we don't see this.
+ */
+
+ if ((pci_state_reg & PCISTATE_BUS_SPEED_HIGH) != 0)
+ tp->tg3_flags |= TG3_FLAG_PCI_HIGH_SPEED;
+ if ((pci_state_reg & PCISTATE_BUS_32BIT) != 0)
+ tp->tg3_flags |= TG3_FLAG_PCI_32BIT;
+
+ /* Chip-specific fixup from Broadcom driver */
+ if ((tp->pci_chip_rev_id == CHIPREV_ID_5704_A0) &&
+ (!(pci_state_reg & PCISTATE_RETRY_SAME_DMA))) {
+ pci_state_reg |= PCISTATE_RETRY_SAME_DMA;
+ pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE, pci_state_reg);
+ }
+
+ /* determine if it is PCIE system */
+ // Alf : I have no idea what this is about...
+ // But it's definitely usefull
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) {
+ val = tr32(TG3PCI_MSI_CAP_ID) ;
+ if (((val >> 8) & 0xff) == T3_PCIE_CAPABILITY_ID_REG) {
+ val = tr32(T3_PCIE_CAPABILITY_ID_REG) ;
+ if ((val & 0xff) == T3_PCIE_CAPABILITY_ID) {
+ tp->tg3_flags2 |= TG3_FLG2_PCI_EXPRESS ;
+ }
+ }
+ }
+
+ /* Force the chip into D0. */
+ tg3_set_power_state_0(tp);
+
+ /* Etherboot does not ask the tg3 to do checksums */
+ /* Etherboot does not ask the tg3 to do jumbo frames */
+ /* Ehterboot does not ask the tg3 to use WakeOnLan. */
+
+ /* A few boards don't want Ethernet@WireSpeed phy feature */
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) ||
+ ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) &&
+ (tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) &&
+ (tp->pci_chip_rev_id != CHIPREV_ID_5705_A1))) {
+ tp->tg3_flags2 |= TG3_FLG2_NO_ETH_WIRE_SPEED;
+ }
+
+ /* Avoid tagged irq status etherboot does not use irqs */
+
+ /* Only 5701 and later support tagged irq status mode.
+ * Also, 5788 chips cannot use tagged irq status.
+ *
+ * However, since etherboot does not use irqs avoid tagged irqs
+ * status because the interrupt condition is more difficult to
+ * fully clear in that mode.
+ */
+
+ /* Since some 5700_AX && 5700_BX have problems with 32BYTE
+ * coalesce_mode, and the rest work fine anything set.
+ * Don't enable HOST_CC_MODE_32BYTE in etherboot.
+ */
+
+ /* Initialize MAC MI mode, polling disabled. */
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+
+ /* Initialize data/descriptor byte/word swapping. */
+ tw32(GRC_MODE, tp->grc_mode);
+
+ tg3_switch_clocks(tp);
+
+ /* Clear this out for sanity. */
+ tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0);
+
+ /* Etherboot does not need to check if the PCIX_TARGET_HWBUG
+ * is needed. It always uses it.
+ */
+
+ udelay(50);
+ tg3_nvram_init(tp);
+
+ /* The TX descriptors will reside in main memory.
+ */
+
+ /* See which board we are using.
+ */
+ grc_misc_cfg = tr32(GRC_MISC_CFG);
+ grc_misc_cfg &= GRC_MISC_CFG_BOARD_ID_MASK;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 &&
+ grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5704CIOBE) {
+ tp->tg3_flags |= TG3_FLAG_SPLIT_MODE;
+ tp->split_mode_max_reqs = SPLIT_MODE_5704_MAX_REQ;
+ }
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 &&
+ (grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788 ||
+ grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788M))
+ tp->tg3_flags2 |= TG3_FLG2_IS_5788;
+
+#define PCI_DEVICE_ID_TIGON3_5901 0x170d
+#define PCI_DEVICE_ID_TIGON3_5901_2 0x170e
+
+ /* these are limited to 10/100 only */
+ if (((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) &&
+ ((grc_misc_cfg == 0x8000) || (grc_misc_cfg == 0x4000))) ||
+ ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) &&
+ (tp->pdev->vendor == PCI_VENDOR_ID_BROADCOM) &&
+ ((tp->pdev->device == PCI_DEVICE_ID_TIGON3_5901) ||
+ (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5901_2)))) {
+ tp->tg3_flags |= TG3_FLAG_10_100_ONLY;
+ }
+
+ err = tg3_phy_probe(tp);
+ if (err) {
+ printf("phy probe failed, err %d\n", err);
+ }
+
+ tg3_read_partno(tp);
+
+
+ /* 5700 BX chips need to have their TX producer index mailboxes
+ * written twice to workaround a bug.
+ * In etherboot we do this unconditionally to simplify things.
+ */
+
+ /* 5700 chips can get confused if TX buffers straddle the
+ * 4GB address boundary in some cases.
+ *
+ * In etherboot we can ignore the problem as etherboot lives below 4GB.
+ */
+
+ /* In etherboot wake-on-lan is unconditionally disabled */
+ return err;
+}
+
+static int tg3_get_device_address(struct tg3 *tp)
+{
+ struct nic *nic = tp->nic;
+ uint32_t hi, lo, mac_offset;
+
+ if (PCI_FUNC(tp->pdev->devfn) == 0)
+ mac_offset = 0x7c;
+ else
+ mac_offset = 0xcc;
+
+ /* First try to get it from MAC address mailbox. */
+ tg3_read_mem(NIC_SRAM_MAC_ADDR_HIGH_MBOX, &hi);
+ if ((hi >> 16) == 0x484b) {
+ nic->node_addr[0] = (hi >> 8) & 0xff;
+ nic->node_addr[1] = (hi >> 0) & 0xff;
+
+ tg3_read_mem(NIC_SRAM_MAC_ADDR_LOW_MBOX, &lo);
+ nic->node_addr[2] = (lo >> 24) & 0xff;
+ nic->node_addr[3] = (lo >> 16) & 0xff;
+ nic->node_addr[4] = (lo >> 8) & 0xff;
+ nic->node_addr[5] = (lo >> 0) & 0xff;
+ }
+ /* Next, try NVRAM. */
+ else if (!tg3_nvram_read(tp, mac_offset + 0, &hi) &&
+ !tg3_nvram_read(tp, mac_offset + 4, &lo)) {
+ nic->node_addr[0] = ((hi >> 16) & 0xff);
+ nic->node_addr[1] = ((hi >> 24) & 0xff);
+ nic->node_addr[2] = ((lo >> 0) & 0xff);
+ nic->node_addr[3] = ((lo >> 8) & 0xff);
+ nic->node_addr[4] = ((lo >> 16) & 0xff);
+ nic->node_addr[5] = ((lo >> 24) & 0xff);
+ }
+ /* Finally just fetch it out of the MAC control regs. */
+ else {
+ hi = tr32(MAC_ADDR_0_HIGH);
+ lo = tr32(MAC_ADDR_0_LOW);
+
+ nic->node_addr[5] = lo & 0xff;
+ nic->node_addr[4] = (lo >> 8) & 0xff;
+ nic->node_addr[3] = (lo >> 16) & 0xff;
+ nic->node_addr[2] = (lo >> 24) & 0xff;
+ nic->node_addr[1] = hi & 0xff;
+ nic->node_addr[0] = (hi >> 8) & 0xff;
+ }
+
+ return 0;
+}
+
+
+static int tg3_setup_dma(struct tg3 *tp)
+{
+ tw32(TG3PCI_CLOCK_CTRL, 0);
+
+ if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) == 0) {
+ tp->dma_rwctrl =
+ (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) |
+ (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) |
+ (0x7 << DMA_RWCTRL_WRITE_WATER_SHIFT) |
+ (0x7 << DMA_RWCTRL_READ_WATER_SHIFT) |
+ (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
+ tp->dma_rwctrl &= ~(DMA_RWCTRL_MIN_DMA << DMA_RWCTRL_MIN_DMA_SHIFT);
+ }
+ } else {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704)
+ tp->dma_rwctrl =
+ (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) |
+ (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) |
+ (0x3 << DMA_RWCTRL_WRITE_WATER_SHIFT) |
+ (0x7 << DMA_RWCTRL_READ_WATER_SHIFT) |
+ (0x00 << DMA_RWCTRL_MIN_DMA_SHIFT);
+ else
+ tp->dma_rwctrl =
+ (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) |
+ (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) |
+ (0x3 << DMA_RWCTRL_WRITE_WATER_SHIFT) |
+ (0x3 << DMA_RWCTRL_READ_WATER_SHIFT) |
+ (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT);
+
+ /* Wheee, some more chip bugs... */
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704)) {
+ uint32_t ccval = tr32(TG3PCI_CLOCK_CTRL) & 0x1f;
+
+ if ((ccval == 0x6) || (ccval == 0x7)) {
+ tp->dma_rwctrl |= DMA_RWCTRL_ONE_DMA;
+ }
+ }
+ }
+
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704)) {
+ tp->dma_rwctrl &= ~(DMA_RWCTRL_MIN_DMA << DMA_RWCTRL_MIN_DMA_SHIFT);
+ }
+
+ /*
+ Alf : Tried that, but it does not work. Should be this way though :-(
+ if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) {
+ tp->dma_rwctrl |= 0x001f0000;
+ }
+ */
+ tp->dma_rwctrl |= DMA_RWCTRL_ASSERT_ALL_BE;
+
+ tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl);
+
+ return 0;
+}
+
+static void tg3_init_link_config(struct tg3 *tp)
+{
+ tp->link_config.advertising =
+ (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg | ADVERTISED_MII);
+ tp->carrier_ok = 0;
+ tp->link_config.active_speed = SPEED_INVALID;
+ tp->link_config.active_duplex = DUPLEX_INVALID;
+}
+
+
+#if SUPPORT_PHY_STR
+static const char * tg3_phy_string(struct tg3 *tp)
+{
+ switch (tp->phy_id & PHY_ID_MASK) {
+ case PHY_ID_BCM5400: return "5400";
+ case PHY_ID_BCM5401: return "5401";
+ case PHY_ID_BCM5411: return "5411";
+ case PHY_ID_BCM5701: return "5701";
+ case PHY_ID_BCM5703: return "5703";
+ case PHY_ID_BCM5704: return "5704";
+ case PHY_ID_BCM5705: return "5705";
+ case PHY_ID_BCM5750: return "5750";
+ case PHY_ID_BCM5751: return "5751";
+ case PHY_ID_BCM8002: return "8002/serdes";
+ case PHY_ID_SERDES: return "serdes";
+ default: return "unknown";
+ };
+}
+#else
+#define tg3_phy_string(TP) "?"
+#endif
+
+
+static void tg3_poll_link(struct tg3 *tp)
+{
+ uint32_t mac_stat;
+
+ mac_stat = tr32(MAC_STATUS);
+ if (tp->phy_id == PHY_ID_SERDES) {
+ if (tp->carrier_ok?
+ (mac_stat & MAC_STATUS_LNKSTATE_CHANGED):
+ (mac_stat & MAC_STATUS_PCS_SYNCED)) {
+ tw32_carefully(MAC_MODE, tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK);
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ tg3_setup_phy(tp);
+ }
+ }
+ else {
+ if (mac_stat & MAC_STATUS_LNKSTATE_CHANGED) {
+ tg3_setup_phy(tp);
+ }
+ }
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static void tg3_ack_irqs(struct tg3 *tp)
+{
+ if (tp->hw_status->status & SD_STATUS_UPDATED) {
+ /*
+ * writing any value to intr-mbox-0 clears PCI INTA# and
+ * chip-internal interrupt pending events.
+ * writing non-zero to intr-mbox-0 additional tells the
+ * NIC to stop sending us irqs, engaging "in-intr-handler"
+ * event coalescing.
+ */
+ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
+ 0x00000001);
+ /*
+ * Flush PCI write. This also guarantees that our
+ * status block has been flushed to host memory.
+ */
+ tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
+ tp->hw_status->status &= ~SD_STATUS_UPDATED;
+ }
+}
+
+static int tg3_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+
+ struct tg3 *tp = &tg3;
+ int result;
+
+ result = 0;
+
+ if ( (tp->hw_status->idx[0].rx_producer != tp->rx_rcb_ptr) && !retrieve )
+ return 1;
+
+ tg3_ack_irqs(tp);
+
+ if (tp->hw_status->idx[0].rx_producer != tp->rx_rcb_ptr) {
+ struct tg3_rx_buffer_desc *desc;
+ unsigned int len;
+ desc = &tp->rx_rcb[tp->rx_rcb_ptr];
+ if ((desc->opaque & RXD_OPAQUE_RING_MASK) == RXD_OPAQUE_RING_STD) {
+ len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT) - 4; /* omit crc */
+
+ nic->packetlen = len;
+ memcpy(nic->packet, bus_to_virt(desc->addr_lo), len);
+ result = 1;
+ }
+ tp->rx_rcb_ptr = (tp->rx_rcb_ptr + 1) % TG3_RX_RCB_RING_SIZE;
+
+ /* ACK the status ring */
+ tw32_mailbox2(MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW, tp->rx_rcb_ptr);
+
+ /* Refill RX ring. */
+ if (result) {
+ tp->rx_std_ptr = (tp->rx_std_ptr + 1) % TG3_RX_RING_SIZE;
+ tw32_mailbox2(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW, tp->rx_std_ptr);
+ }
+ }
+ tg3_poll_link(tp);
+ return result;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+#if 0
+static void tg3_set_txd(struct tg3 *tp, int entry,
+ dma_addr_t mapping, int len, uint32_t flags,
+ uint32_t mss_and_is_end)
+{
+ struct tg3_tx_buffer_desc *txd = &tp->tx_ring[entry];
+ int is_end = (mss_and_is_end & 0x1);
+ if (is_end) {
+ flags |= TXD_FLAG_END;
+ }
+
+ txd->addr_hi = 0;
+ txd->addr_lo = mapping & 0xffffffff;
+ txd->len_flags = (len << TXD_LEN_SHIFT) | flags;
+ txd->vlan_tag = 0 << TXD_VLAN_TAG_SHIFT;
+}
+#endif
+
+static void tg3_transmit(struct nic *nic, const char *dst_addr,
+ unsigned int type, unsigned int size, const char *packet)
+{
+ static int frame_idx;
+ struct eth_frame *frame;
+
+ /* send the packet to destination */
+ struct tg3_tx_buffer_desc *txd;
+ struct tg3 *tp;
+ uint32_t entry;
+ int i;
+
+ /* Wait until there is a free packet frame */
+ tp = &tg3;
+ i = 0;
+ entry = tp->tx_prod;
+ while((tp->hw_status->idx[0].tx_consumer != entry) &&
+ (tp->hw_status->idx[0].tx_consumer != PREV_TX(entry))) {
+ mdelay(10); /* give the nick a chance */
+ if (++i > 500) { /* timeout 5s for transmit */
+ printf("transmit timed out\n");
+ tg3_halt(tp);
+ tg3_setup_hw(tp);
+ return;
+ }
+ }
+ if (i != 0) {
+ printf("#");
+ }
+
+ /* Copy the packet to the our local buffer */
+ frame = &tg3_bss.tx_frame[frame_idx];
+ memcpy(frame->dst_addr, dst_addr, ETH_ALEN);
+ memcpy(frame->src_addr, nic->node_addr, ETH_ALEN);
+ frame->type = htons(type);
+ memset(frame->data, 0, sizeof(frame->data));
+ memcpy(frame->data, packet, size);
+
+ /* Setup the ring buffer entry to transmit */
+ txd = &tp->tx_ring[entry];
+ txd->addr_hi = 0; /* Etherboot runs under 4GB */
+ txd->addr_lo = virt_to_bus(frame);
+ txd->len_flags = ((size + ETH_HLEN) << TXD_LEN_SHIFT) | TXD_FLAG_END;
+ txd->vlan_tag = 0 << TXD_VLAN_TAG_SHIFT;
+
+ /* Advance to the next entry */
+ entry = NEXT_TX(entry);
+ frame_idx ^= 1;
+
+ /* Packets are ready, update Tx producer idx local and on card */
+ tw32_mailbox((MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW), entry);
+ tw32_mailbox2((MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW), entry);
+ tp->tx_prod = entry;
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void tg3_disable ( struct nic *nic __unused ) {
+ struct tg3 *tp = &tg3;
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+ tg3_halt(tp);
+ tp->tg3_flags &= ~(TG3_FLAG_INIT_COMPLETE|TG3_FLAG_GOT_SERDES_FLOWCTL);
+ tp->carrier_ok = 0;
+ iounmap((void *)tp->regs);
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void tg3_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct nic_operations tg3_operations = {
+ .connect = dummy_connect,
+ .poll = tg3_poll,
+ .transmit = tg3_transmit,
+ .irq = tg3_irq,
+
+};
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+You should omit the last argument struct pci_device * for a non-PCI NIC
+***************************************************************************/
+static int tg3_probe ( struct nic *nic, struct pci_device *pdev ) {
+
+ struct tg3 *tp = &tg3;
+ unsigned long tg3reg_base, tg3reg_len;
+ int i, err, pm_cap;
+
+ memset(tp, 0, sizeof(*tp));
+
+ adjust_pci_device(pdev);
+
+ nic->irqno = 0;
+ nic->ioaddr = pdev->ioaddr;
+
+ /* Find power-management capability. */
+ pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pm_cap == 0) {
+ printf("Cannot find PowerManagement capability, aborting.\n");
+ return 0;
+ }
+ tg3reg_base = pci_bar_start(pdev, PCI_BASE_ADDRESS_0);
+ if (tg3reg_base == -1UL) {
+ printf("Unuseable bar\n");
+ return 0;
+ }
+ tg3reg_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_0);
+
+ tp->pdev = pdev;
+ tp->nic = nic;
+ tp->pm_cap = pm_cap;
+ tp->rx_mode = 0;
+ tp->tx_mode = 0;
+ tp->mi_mode = MAC_MI_MODE_BASE;
+ tp->tg3_flags = 0 & ~TG3_FLAG_INIT_COMPLETE;
+
+ /* The word/byte swap controls here control register access byte
+ * swapping. DMA data byte swapping is controlled in the GRC_MODE
+ * setting below.
+ */
+ tp->misc_host_ctrl =
+ MISC_HOST_CTRL_MASK_PCI_INT |
+ MISC_HOST_CTRL_WORD_SWAP |
+ MISC_HOST_CTRL_INDIR_ACCESS |
+ MISC_HOST_CTRL_PCISTATE_RW;
+
+ /* The NONFRM (non-frame) byte/word swap controls take effect
+ * on descriptor entries, anything which isn't packet data.
+ *
+ * The StrongARM chips on the board (one for tx, one for rx)
+ * are running in big-endian mode.
+ */
+ tp->grc_mode = (GRC_MODE_WSWAP_DATA | GRC_MODE_BSWAP_DATA |
+ GRC_MODE_WSWAP_NONFRM_DATA);
+#if __BYTE_ORDER == __BIG_ENDIAN
+ tp->grc_mode |= GRC_MODE_BSWAP_NONFRM_DATA;
+#endif
+ tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len);
+ if (tp->regs == 0UL) {
+ printf("Cannot map device registers, aborting\n");
+ return 0;
+ }
+
+ tg3_init_link_config(tp);
+
+ err = tg3_get_invariants(tp);
+ if (err) {
+ printf("Problem fetching invariants of chip, aborting.\n");
+ goto err_out_iounmap;
+ }
+
+ err = tg3_get_device_address(tp);
+ if (err) {
+ printf("Could not obtain valid ethernet address, aborting.\n");
+ goto err_out_iounmap;
+ }
+
+ DBG ( "Ethernet addr: %s\n", eth_ntoa ( nic->node_addr ) );
+
+ tg3_setup_dma(tp);
+
+ /* Now that we have fully setup the chip, save away a snapshot
+ * of the PCI config space. We need to restore this after
+ * GRC_MISC_CFG core clock resets and some resume events.
+ */
+ pci_save_state(tp->pdev, tp->pci_cfg_state);
+
+ printf("Tigon3 [partno(%s) rev %hx PHY(%s)] (PCI%s:%s:%s)\n",
+ tp->board_part_number,
+ tp->pci_chip_rev_id,
+ tg3_phy_string(tp),
+ ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "X" : ""),
+ ((tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED) ?
+ ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "133MHz" : "66MHz") :
+ ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "100MHz" : "33MHz")),
+ ((tp->tg3_flags & TG3_FLAG_PCI_32BIT) ? "32-bit" : "64-bit"));
+
+
+ err = tg3_setup_hw(tp);
+ if (err) {
+ goto err_out_disable;
+ }
+ tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE;
+
+ /* Wait for a reasonable time for the link to come up */
+ tg3_poll_link(tp);
+ for(i = 0; !tp->carrier_ok && (i < VALID_LINK_TIMEOUT*100); i++) {
+ mdelay(1);
+ tg3_poll_link(tp);
+ }
+ if (!tp->carrier_ok){
+ printf("Valid link not established\n");
+ goto err_out_disable;
+ }
+
+ nic->nic_op = &tg3_operations;
+ return 1;
+
+ err_out_iounmap:
+ iounmap((void *)tp->regs);
+ return 0;
+ err_out_disable:
+ tg3_disable(nic);
+ return 0;
+}
+
+
+static struct pci_device_id tg3_nics[] = {
+PCI_ROM(0x14e4, 0x1644, "tg3-5700", "Broadcom Tigon 3 5700"),
+PCI_ROM(0x14e4, 0x1645, "tg3-5701", "Broadcom Tigon 3 5701"),
+PCI_ROM(0x14e4, 0x1646, "tg3-5702", "Broadcom Tigon 3 5702"),
+PCI_ROM(0x14e4, 0x1647, "tg3-5703", "Broadcom Tigon 3 5703"),
+PCI_ROM(0x14e4, 0x1648, "tg3-5704", "Broadcom Tigon 3 5704"),
+PCI_ROM(0x14e4, 0x164d, "tg3-5702FE", "Broadcom Tigon 3 5702FE"),
+PCI_ROM(0x14e4, 0x1653, "tg3-5705", "Broadcom Tigon 3 5705"),
+PCI_ROM(0x14e4, 0x1654, "tg3-5705_2", "Broadcom Tigon 3 5705_2"),
+PCI_ROM(0x14e4, 0x165d, "tg3-5705M", "Broadcom Tigon 3 5705M"),
+PCI_ROM(0x14e4, 0x165e, "tg3-5705M_2", "Broadcom Tigon 3 5705M_2"),
+PCI_ROM(0x14e4, 0x1677, "tg3-5751", "Broadcom Tigon 3 5751"),
+PCI_ROM(0x14e4, 0x1696, "tg3-5782", "Broadcom Tigon 3 5782"),
+PCI_ROM(0x14e4, 0x169c, "tg3-5788", "Broadcom Tigon 3 5788"),
+PCI_ROM(0x14e4, 0x16a6, "tg3-5702X", "Broadcom Tigon 3 5702X"),
+PCI_ROM(0x14e4, 0x16a7, "tg3-5703X", "Broadcom Tigon 3 5703X"),
+PCI_ROM(0x14e4, 0x16a8, "tg3-5704S", "Broadcom Tigon 3 5704S"),
+PCI_ROM(0x14e4, 0x16c6, "tg3-5702A3", "Broadcom Tigon 3 5702A3"),
+PCI_ROM(0x14e4, 0x16c7, "tg3-5703A3", "Broadcom Tigon 3 5703A3"),
+PCI_ROM(0x14e4, 0x170d, "tg3-5901", "Broadcom Tigon 3 5901"),
+PCI_ROM(0x14e4, 0x170e, "tg3-5901_2", "Broadcom Tigon 3 5901_2"),
+PCI_ROM(0x1148, 0x4400, "tg3-9DXX", "Syskonnect 9DXX"),
+PCI_ROM(0x1148, 0x4500, "tg3-9MXX", "Syskonnect 9MXX"),
+PCI_ROM(0x173b, 0x03e8, "tg3-ac1000", "Altima AC1000"),
+PCI_ROM(0x173b, 0x03e9, "tg3-ac1001", "Altima AC1001"),
+PCI_ROM(0x173b, 0x03ea, "tg3-ac9100", "Altima AC9100"),
+PCI_ROM(0x173b, 0x03eb, "tg3-ac1003", "Altima AC1003"),
+PCI_ROM(0x0e11, 0x00ca, "tg3-hp", "HP Tigon 3"),
+};
+
+PCI_DRIVER ( tg3_driver, tg3_nics, PCI_NO_CLASS );
+
+DRIVER ( "TG3", nic_driver, pci_driver, tg3_driver,
+ tg3_probe, tg3_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/tg3.h b/gpxe/src/drivers/net/tg3.h
new file mode 100644
index 00000000..fd038f58
--- /dev/null
+++ b/gpxe/src/drivers/net/tg3.h
@@ -0,0 +1,2211 @@
+/* $Id$
+ * tg3.h: Definitions for Broadcom Tigon3 ethernet driver.
+ *
+ * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)
+ * Copyright (C) 2001 Jeff Garzik (jgarzik@mandrakesoft.com)
+ */
+
+#ifndef _T3_H
+#define _T3_H
+
+#include "stdint.h"
+
+typedef unsigned long dma_addr_t;
+
+/* From mii.h */
+
+/* Indicates what features are advertised by the interface. */
+#define ADVERTISED_10baseT_Half (1 << 0)
+#define ADVERTISED_10baseT_Full (1 << 1)
+#define ADVERTISED_100baseT_Half (1 << 2)
+#define ADVERTISED_100baseT_Full (1 << 3)
+#define ADVERTISED_1000baseT_Half (1 << 4)
+#define ADVERTISED_1000baseT_Full (1 << 5)
+#define ADVERTISED_Autoneg (1 << 6)
+#define ADVERTISED_TP (1 << 7)
+#define ADVERTISED_AUI (1 << 8)
+#define ADVERTISED_MII (1 << 9)
+#define ADVERTISED_FIBRE (1 << 10)
+#define ADVERTISED_BNC (1 << 11)
+
+/* The following are all involved in forcing a particular link
+ * mode for the device for setting things. When getting the
+ * devices settings, these indicate the current mode and whether
+ * it was foced up into this mode or autonegotiated.
+ */
+
+/* The forced speed, 10Mb, 100Mb, gigabit. */
+#define SPEED_10 0
+#define SPEED_100 1
+#define SPEED_1000 2
+#define SPEED_INVALID 3
+
+
+/* Duplex, half or full. */
+#define DUPLEX_HALF 0x00
+#define DUPLEX_FULL 0x01
+#define DUPLEX_INVALID 0x02
+
+/* Which connector port. */
+#define PORT_TP 0x00
+#define PORT_AUI 0x01
+#define PORT_MII 0x02
+#define PORT_FIBRE 0x03
+#define PORT_BNC 0x04
+
+/* Which tranceiver to use. */
+#define XCVR_INTERNAL 0x00
+#define XCVR_EXTERNAL 0x01
+#define XCVR_DUMMY1 0x02
+#define XCVR_DUMMY2 0x03
+#define XCVR_DUMMY3 0x04
+
+/* Enable or disable autonegotiation. If this is set to enable,
+ * the forced link modes above are completely ignored.
+ */
+#define AUTONEG_DISABLE 0x00
+#define AUTONEG_ENABLE 0x01
+
+/* Wake-On-Lan options. */
+#define WAKE_PHY (1 << 0)
+#define WAKE_UCAST (1 << 1)
+#define WAKE_MCAST (1 << 2)
+#define WAKE_BCAST (1 << 3)
+#define WAKE_ARP (1 << 4)
+#define WAKE_MAGIC (1 << 5)
+#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */
+
+/* Generic MII registers. */
+
+#define MII_BMCR 0x00 /* Basic mode control register */
+#define MII_BMSR 0x01 /* Basic mode status register */
+#define MII_PHYSID1 0x02 /* PHYS ID 1 */
+#define MII_PHYSID2 0x03 /* PHYS ID 2 */
+#define MII_ADVERTISE 0x04 /* Advertisement control reg */
+#define MII_LPA 0x05 /* Link partner ability reg */
+#define MII_EXPANSION 0x06 /* Expansion register */
+#define MII_DCOUNTER 0x12 /* Disconnect counter */
+#define MII_FCSCOUNTER 0x13 /* False carrier counter */
+#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
+#define MII_RERRCOUNTER 0x15 /* Receive error counter */
+#define MII_SREVISION 0x16 /* Silicon revision */
+#define MII_RESV1 0x17 /* Reserved... */
+#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */
+#define MII_PHYADDR 0x19 /* PHY address */
+#define MII_RESV2 0x1a /* Reserved... */
+#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
+#define MII_NCONFIG 0x1c /* Network interface config */
+
+/* Basic mode control register. */
+#define BMCR_RESV 0x007f /* Unused... */
+#define BMCR_CTST 0x0080 /* Collision test */
+#define BMCR_FULLDPLX 0x0100 /* Full duplex */
+#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
+#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */
+#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */
+#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */
+#define BMCR_SPEED100 0x2000 /* Select 100Mbps */
+#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */
+#define BMCR_RESET 0x8000 /* Reset the DP83840 */
+
+/* Basic mode status register. */
+#define BMSR_ERCAP 0x0001 /* Ext-reg capability */
+#define BMSR_JCD 0x0002 /* Jabber detected */
+#define BMSR_LSTATUS 0x0004 /* Link status */
+#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */
+#define BMSR_RFAULT 0x0010 /* Remote fault detected */
+#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
+#define BMSR_RESV 0x07c0 /* Unused... */
+#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */
+#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */
+#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */
+#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */
+#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */
+
+/* Advertisement control register. */
+#define ADVERTISE_SLCT 0x001f /* Selector bits */
+#define ADVERTISE_CSMA 0x0001 /* Only selector supported */
+#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
+#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
+#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */
+#define ADVERTISE_RESV 0x1c00 /* Unused... */
+#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */
+#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */
+#define ADVERTISE_NPAGE 0x8000 /* Next page bit */
+
+#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
+ ADVERTISE_CSMA)
+#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
+ ADVERTISE_100HALF | ADVERTISE_100FULL)
+
+/* Link partner ability register. */
+#define LPA_SLCT 0x001f /* Same as advertise selector */
+#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */
+#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */
+#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */
+#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */
+#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */
+#define LPA_RESV 0x1c00 /* Unused... */
+#define LPA_RFAULT 0x2000 /* Link partner faulted */
+#define LPA_LPACK 0x4000 /* Link partner acked us */
+#define LPA_NPAGE 0x8000 /* Next page bit */
+
+#define LPA_DUPLEX (LPA_10FULL | LPA_100FULL)
+#define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4)
+
+/* Expansion register for auto-negotiation. */
+#define EXPANSION_NWAY 0x0001 /* Can do N-way auto-nego */
+#define EXPANSION_LCWP 0x0002 /* Got new RX page code word */
+#define EXPANSION_ENABLENPAGE 0x0004 /* This enables npage words */
+#define EXPANSION_NPCAPABLE 0x0008 /* Link partner supports npage */
+#define EXPANSION_MFAULTS 0x0010 /* Multiple faults detected */
+#define EXPANSION_RESV 0xffe0 /* Unused... */
+
+/* N-way test register. */
+#define NWAYTEST_RESV1 0x00ff /* Unused... */
+#define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */
+#define NWAYTEST_RESV2 0xfe00 /* Unused... */
+
+
+/* From tg3.h */
+
+#define TG3_64BIT_REG_HIGH 0x00UL
+#define TG3_64BIT_REG_LOW 0x04UL
+
+/* Descriptor block info. */
+#define TG3_BDINFO_HOST_ADDR 0x0UL /* 64-bit */
+#define TG3_BDINFO_MAXLEN_FLAGS 0x8UL /* 32-bit */
+#define BDINFO_FLAGS_USE_EXT_RECV 0x00000001 /* ext rx_buffer_desc */
+#define BDINFO_FLAGS_DISABLED 0x00000002
+#define BDINFO_FLAGS_MAXLEN_MASK 0xffff0000
+#define BDINFO_FLAGS_MAXLEN_SHIFT 16
+#define TG3_BDINFO_NIC_ADDR 0xcUL /* 32-bit */
+#define TG3_BDINFO_SIZE 0x10UL
+
+#define RX_COPY_THRESHOLD 256
+
+#define RX_STD_MAX_SIZE 1536
+#define RX_STD_MAX_SIZE_5705 512
+#define RX_JUMBO_MAX_SIZE 0xdeadbeef /* XXX */
+
+/* First 256 bytes are a mirror of PCI config space. */
+#define TG3PCI_VENDOR 0x00000000
+#define TG3PCI_VENDOR_BROADCOM 0x14e4
+#define TG3PCI_DEVICE 0x00000002
+#define TG3PCI_DEVICE_TIGON3_1 0x1644 /* BCM5700 */
+#define TG3PCI_DEVICE_TIGON3_2 0x1645 /* BCM5701 */
+#define TG3PCI_DEVICE_TIGON3_3 0x1646 /* BCM5702 */
+#define TG3PCI_DEVICE_TIGON3_4 0x1647 /* BCM5703 */
+#define TG3PCI_COMMAND 0x00000004
+#define TG3PCI_STATUS 0x00000006
+#define TG3PCI_CCREVID 0x00000008
+#define TG3PCI_CACHELINESZ 0x0000000c
+#define TG3PCI_LATTIMER 0x0000000d
+#define TG3PCI_HEADERTYPE 0x0000000e
+#define TG3PCI_BIST 0x0000000f
+#define TG3PCI_BASE0_LOW 0x00000010
+#define TG3PCI_BASE0_HIGH 0x00000014
+/* 0x18 --> 0x2c unused */
+#define TG3PCI_SUBSYSVENID 0x0000002c
+#define TG3PCI_SUBSYSID 0x0000002e
+#define TG3PCI_ROMADDR 0x00000030
+#define TG3PCI_CAPLIST 0x00000034
+/* 0x35 --> 0x3c unused */
+#define TG3PCI_IRQ_LINE 0x0000003c
+#define TG3PCI_IRQ_PIN 0x0000003d
+#define TG3PCI_MIN_GNT 0x0000003e
+#define TG3PCI_MAX_LAT 0x0000003f
+#define TG3PCI_X_CAPS 0x00000040
+#define PCIX_CAPS_RELAXED_ORDERING 0x00020000
+#define PCIX_CAPS_SPLIT_MASK 0x00700000
+#define PCIX_CAPS_SPLIT_SHIFT 20
+#define PCIX_CAPS_BURST_MASK 0x000c0000
+#define PCIX_CAPS_BURST_SHIFT 18
+#define PCIX_CAPS_MAX_BURST_CPIOB 2
+#define TG3PCI_PM_CAP_PTR 0x00000041
+#define TG3PCI_X_COMMAND 0x00000042
+#define TG3PCI_X_STATUS 0x00000044
+#define TG3PCI_PM_CAP_ID 0x00000048
+#define TG3PCI_VPD_CAP_PTR 0x00000049
+#define TG3PCI_PM_CAPS 0x0000004a
+#define TG3PCI_PM_CTRL_STAT 0x0000004c
+#define TG3PCI_BR_SUPP_EXT 0x0000004e
+#define TG3PCI_PM_DATA 0x0000004f
+#define TG3PCI_VPD_CAP_ID 0x00000050
+#define TG3PCI_MSI_CAP_PTR 0x00000051
+#define TG3PCI_VPD_ADDR_FLAG 0x00000052
+#define VPD_ADDR_FLAG_WRITE 0x00008000
+#define TG3PCI_VPD_DATA 0x00000054
+#define TG3PCI_MSI_CAP_ID 0x00000058
+#define TG3PCI_NXT_CAP_PTR 0x00000059
+#define TG3PCI_MSI_CTRL 0x0000005a
+#define TG3PCI_MSI_ADDR_LOW 0x0000005c
+#define TG3PCI_MSI_ADDR_HIGH 0x00000060
+#define TG3PCI_MSI_DATA 0x00000064
+/* 0x66 --> 0x68 unused */
+#define TG3PCI_MISC_HOST_CTRL 0x00000068
+#define MISC_HOST_CTRL_CLEAR_INT 0x00000001
+#define MISC_HOST_CTRL_MASK_PCI_INT 0x00000002
+#define MISC_HOST_CTRL_BYTE_SWAP 0x00000004
+#define MISC_HOST_CTRL_WORD_SWAP 0x00000008
+#define MISC_HOST_CTRL_PCISTATE_RW 0x00000010
+#define MISC_HOST_CTRL_CLKREG_RW 0x00000020
+#define MISC_HOST_CTRL_REGWORD_SWAP 0x00000040
+#define MISC_HOST_CTRL_INDIR_ACCESS 0x00000080
+#define MISC_HOST_CTRL_IRQ_MASK_MODE 0x00000100
+#define MISC_HOST_CTRL_TAGGED_STATUS 0x00000200
+#define MISC_HOST_CTRL_CHIPREV 0xffff0000
+#define MISC_HOST_CTRL_CHIPREV_SHIFT 16
+#define GET_CHIP_REV_ID(MISC_HOST_CTRL) \
+ (((MISC_HOST_CTRL) & MISC_HOST_CTRL_CHIPREV) >> \
+ MISC_HOST_CTRL_CHIPREV_SHIFT)
+#define CHIPREV_ID_5700_A0 0x7000
+#define CHIPREV_ID_5700_A1 0x7001
+#define CHIPREV_ID_5700_B0 0x7100
+#define CHIPREV_ID_5700_B1 0x7101
+#define CHIPREV_ID_5700_B3 0x7102
+#define CHIPREV_ID_5700_ALTIMA 0x7104
+#define CHIPREV_ID_5700_C0 0x7200
+#define CHIPREV_ID_5701_A0 0x0000
+#define CHIPREV_ID_5701_B0 0x0100
+#define CHIPREV_ID_5701_B2 0x0102
+#define CHIPREV_ID_5701_B5 0x0105
+#define CHIPREV_ID_5703_A0 0x1000
+#define CHIPREV_ID_5703_A1 0x1001
+#define CHIPREV_ID_5703_A2 0x1002
+#define CHIPREV_ID_5703_A3 0x1003
+#define CHIPREV_ID_5704_A0 0x2000
+#define CHIPREV_ID_5704_A1 0x2001
+#define CHIPREV_ID_5704_A2 0x2002
+#define CHIPREV_ID_5705_A0 0x3000
+#define CHIPREV_ID_5705_A1 0x3001
+#define CHIPREV_ID_5705_A2 0x3002
+#define CHIPREV_ID_5705_A3 0x3003
+#define CHIPREV_ID_5750_A0 0x4000
+#define CHIPREV_ID_5750_A1 0x4001
+#define CHIPREV_ID_5750_A3 0x4003
+#define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12)
+#define ASIC_REV_5700 0x07
+#define ASIC_REV_5701 0x00
+#define ASIC_REV_5703 0x01
+#define ASIC_REV_5704 0x02
+#define ASIC_REV_5705 0x03
+#define ASIC_REV_5750 0x04
+#define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8)
+#define CHIPREV_5700_AX 0x70
+#define CHIPREV_5700_BX 0x71
+#define CHIPREV_5700_CX 0x72
+#define CHIPREV_5701_AX 0x00
+#define GET_METAL_REV(CHIP_REV_ID) ((CHIP_REV_ID) & 0xff)
+#define METAL_REV_A0 0x00
+#define METAL_REV_A1 0x01
+#define METAL_REV_B0 0x00
+#define METAL_REV_B1 0x01
+#define METAL_REV_B2 0x02
+#define TG3PCI_DMA_RW_CTRL 0x0000006c
+#define DMA_RWCTRL_MIN_DMA 0x000000ff
+#define DMA_RWCTRL_MIN_DMA_SHIFT 0
+#define DMA_RWCTRL_READ_BNDRY_MASK 0x00000700
+#define DMA_RWCTRL_READ_BNDRY_DISAB 0x00000000
+#define DMA_RWCTRL_READ_BNDRY_16 0x00000100
+#define DMA_RWCTRL_READ_BNDRY_32 0x00000200
+#define DMA_RWCTRL_READ_BNDRY_64 0x00000300
+#define DMA_RWCTRL_READ_BNDRY_128 0x00000400
+#define DMA_RWCTRL_READ_BNDRY_256 0x00000500
+#define DMA_RWCTRL_READ_BNDRY_512 0x00000600
+#define DMA_RWCTRL_READ_BNDRY_1024 0x00000700
+#define DMA_RWCTRL_WRITE_BNDRY_MASK 0x00003800
+#define DMA_RWCTRL_WRITE_BNDRY_DISAB 0x00000000
+#define DMA_RWCTRL_WRITE_BNDRY_16 0x00000800
+#define DMA_RWCTRL_WRITE_BNDRY_32 0x00001000
+#define DMA_RWCTRL_WRITE_BNDRY_64 0x00001800
+#define DMA_RWCTRL_WRITE_BNDRY_128 0x00002000
+#define DMA_RWCTRL_WRITE_BNDRY_256 0x00002800
+#define DMA_RWCTRL_WRITE_BNDRY_512 0x00003000
+#define DMA_RWCTRL_WRITE_BNDRY_1024 0x00003800
+#define DMA_RWCTRL_ONE_DMA 0x00004000
+#define DMA_RWCTRL_READ_WATER 0x00070000
+#define DMA_RWCTRL_READ_WATER_SHIFT 16
+#define DMA_RWCTRL_WRITE_WATER 0x00380000
+#define DMA_RWCTRL_WRITE_WATER_SHIFT 19
+#define DMA_RWCTRL_USE_MEM_READ_MULT 0x00400000
+#define DMA_RWCTRL_ASSERT_ALL_BE 0x00800000
+#define DMA_RWCTRL_PCI_READ_CMD 0x0f000000
+#define DMA_RWCTRL_PCI_READ_CMD_SHIFT 24
+#define DMA_RWCTRL_PCI_WRITE_CMD 0xf0000000
+#define DMA_RWCTRL_PCI_WRITE_CMD_SHIFT 28
+#define TG3PCI_PCISTATE 0x00000070
+#define PCISTATE_FORCE_RESET 0x00000001
+#define PCISTATE_INT_NOT_ACTIVE 0x00000002
+#define PCISTATE_CONV_PCI_MODE 0x00000004
+#define PCISTATE_BUS_SPEED_HIGH 0x00000008
+#define PCISTATE_BUS_32BIT 0x00000010
+#define PCISTATE_ROM_ENABLE 0x00000020
+#define PCISTATE_ROM_RETRY_ENABLE 0x00000040
+#define PCISTATE_FLAT_VIEW 0x00000100
+#define PCISTATE_RETRY_SAME_DMA 0x00002000
+#define TG3PCI_CLOCK_CTRL 0x00000074
+#define CLOCK_CTRL_CORECLK_DISABLE 0x00000200
+#define CLOCK_CTRL_RXCLK_DISABLE 0x00000400
+#define CLOCK_CTRL_TXCLK_DISABLE 0x00000800
+#define CLOCK_CTRL_ALTCLK 0x00001000
+#define CLOCK_CTRL_PWRDOWN_PLL133 0x00008000
+#define CLOCK_CTRL_44MHZ_CORE 0x00040000
+#define CLOCK_CTRL_625_CORE 0x00100000
+#define CLOCK_CTRL_FORCE_CLKRUN 0x00200000
+#define CLOCK_CTRL_CLKRUN_OENABLE 0x00400000
+#define CLOCK_CTRL_DELAY_PCI_GRANT 0x80000000
+#define TG3PCI_REG_BASE_ADDR 0x00000078
+#define TG3PCI_MEM_WIN_BASE_ADDR 0x0000007c
+#define TG3PCI_REG_DATA 0x00000080
+#define TG3PCI_MEM_WIN_DATA 0x00000084
+#define TG3PCI_MODE_CTRL 0x00000088
+#define TG3PCI_MISC_CFG 0x0000008c
+#define TG3PCI_MISC_LOCAL_CTRL 0x00000090
+/* 0x94 --> 0x98 unused */
+#define TG3PCI_STD_RING_PROD_IDX 0x00000098 /* 64-bit */
+#define TG3PCI_RCV_RET_RING_CON_IDX 0x000000a0 /* 64-bit */
+#define TG3PCI_SND_PROD_IDX 0x000000a8 /* 64-bit */
+/* 0xb0 --> 0x100 unused */
+
+/* 0x100 --> 0x200 unused */
+
+/* Mailbox registers */
+#define MAILBOX_INTERRUPT_0 0x00000200 /* 64-bit */
+#define MAILBOX_INTERRUPT_1 0x00000208 /* 64-bit */
+#define MAILBOX_INTERRUPT_2 0x00000210 /* 64-bit */
+#define MAILBOX_INTERRUPT_3 0x00000218 /* 64-bit */
+#define MAILBOX_GENERAL_0 0x00000220 /* 64-bit */
+#define MAILBOX_GENERAL_1 0x00000228 /* 64-bit */
+#define MAILBOX_GENERAL_2 0x00000230 /* 64-bit */
+#define MAILBOX_GENERAL_3 0x00000238 /* 64-bit */
+#define MAILBOX_GENERAL_4 0x00000240 /* 64-bit */
+#define MAILBOX_GENERAL_5 0x00000248 /* 64-bit */
+#define MAILBOX_GENERAL_6 0x00000250 /* 64-bit */
+#define MAILBOX_GENERAL_7 0x00000258 /* 64-bit */
+#define MAILBOX_RELOAD_STAT 0x00000260 /* 64-bit */
+#define MAILBOX_RCV_STD_PROD_IDX 0x00000268 /* 64-bit */
+#define MAILBOX_RCV_JUMBO_PROD_IDX 0x00000270 /* 64-bit */
+#define MAILBOX_RCV_MINI_PROD_IDX 0x00000278 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_0 0x00000280 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_1 0x00000288 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_2 0x00000290 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_3 0x00000298 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_4 0x000002a0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_5 0x000002a8 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_6 0x000002b0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_7 0x000002b8 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_8 0x000002c0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_9 0x000002c8 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_10 0x000002d0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_11 0x000002d8 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_12 0x000002e0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_13 0x000002e8 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_14 0x000002f0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_15 0x000002f8 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_0 0x00000300 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_1 0x00000308 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_2 0x00000310 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_3 0x00000318 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_4 0x00000320 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_5 0x00000328 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_6 0x00000330 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_7 0x00000338 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_8 0x00000340 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_9 0x00000348 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_10 0x00000350 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_11 0x00000358 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_12 0x00000360 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_13 0x00000368 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_14 0x00000370 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_15 0x00000378 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_0 0x00000380 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_1 0x00000388 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_2 0x00000390 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_3 0x00000398 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_4 0x000003a0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_5 0x000003a8 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_6 0x000003b0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_7 0x000003b8 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_8 0x000003c0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_9 0x000003c8 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_10 0x000003d0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_11 0x000003d8 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_12 0x000003e0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_13 0x000003e8 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_14 0x000003f0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_15 0x000003f8 /* 64-bit */
+
+/* MAC control registers */
+#define MAC_MODE 0x00000400
+#define MAC_MODE_RESET 0x00000001
+#define MAC_MODE_HALF_DUPLEX 0x00000002
+#define MAC_MODE_PORT_MODE_MASK 0x0000000c
+#define MAC_MODE_PORT_MODE_TBI 0x0000000c
+#define MAC_MODE_PORT_MODE_GMII 0x00000008
+#define MAC_MODE_PORT_MODE_MII 0x00000004
+#define MAC_MODE_PORT_MODE_NONE 0x00000000
+#define MAC_MODE_PORT_INT_LPBACK 0x00000010
+#define MAC_MODE_TAGGED_MAC_CTRL 0x00000080
+#define MAC_MODE_TX_BURSTING 0x00000100
+#define MAC_MODE_MAX_DEFER 0x00000200
+#define MAC_MODE_LINK_POLARITY 0x00000400
+#define MAC_MODE_RXSTAT_ENABLE 0x00000800
+#define MAC_MODE_RXSTAT_CLEAR 0x00001000
+#define MAC_MODE_RXSTAT_FLUSH 0x00002000
+#define MAC_MODE_TXSTAT_ENABLE 0x00004000
+#define MAC_MODE_TXSTAT_CLEAR 0x00008000
+#define MAC_MODE_TXSTAT_FLUSH 0x00010000
+#define MAC_MODE_SEND_CONFIGS 0x00020000
+#define MAC_MODE_MAGIC_PKT_ENABLE 0x00040000
+#define MAC_MODE_ACPI_ENABLE 0x00080000
+#define MAC_MODE_MIP_ENABLE 0x00100000
+#define MAC_MODE_TDE_ENABLE 0x00200000
+#define MAC_MODE_RDE_ENABLE 0x00400000
+#define MAC_MODE_FHDE_ENABLE 0x00800000
+#define MAC_STATUS 0x00000404
+#define MAC_STATUS_PCS_SYNCED 0x00000001
+#define MAC_STATUS_SIGNAL_DET 0x00000002
+#define MAC_STATUS_RCVD_CFG 0x00000004
+#define MAC_STATUS_CFG_CHANGED 0x00000008
+#define MAC_STATUS_SYNC_CHANGED 0x00000010
+#define MAC_STATUS_PORT_DEC_ERR 0x00000400
+#define MAC_STATUS_LNKSTATE_CHANGED 0x00001000
+#define MAC_STATUS_MI_COMPLETION 0x00400000
+#define MAC_STATUS_MI_INTERRUPT 0x00800000
+#define MAC_STATUS_AP_ERROR 0x01000000
+#define MAC_STATUS_ODI_ERROR 0x02000000
+#define MAC_STATUS_RXSTAT_OVERRUN 0x04000000
+#define MAC_STATUS_TXSTAT_OVERRUN 0x08000000
+#define MAC_EVENT 0x00000408
+#define MAC_EVENT_PORT_DECODE_ERR 0x00000400
+#define MAC_EVENT_LNKSTATE_CHANGED 0x00001000
+#define MAC_EVENT_MI_COMPLETION 0x00400000
+#define MAC_EVENT_MI_INTERRUPT 0x00800000
+#define MAC_EVENT_AP_ERROR 0x01000000
+#define MAC_EVENT_ODI_ERROR 0x02000000
+#define MAC_EVENT_RXSTAT_OVERRUN 0x04000000
+#define MAC_EVENT_TXSTAT_OVERRUN 0x08000000
+#define MAC_LED_CTRL 0x0000040c
+#define LED_CTRL_LNKLED_OVERRIDE 0x00000001
+#define LED_CTRL_1000MBPS_ON 0x00000002
+#define LED_CTRL_100MBPS_ON 0x00000004
+#define LED_CTRL_10MBPS_ON 0x00000008
+#define LED_CTRL_TRAFFIC_OVERRIDE 0x00000010
+#define LED_CTRL_TRAFFIC_BLINK 0x00000020
+#define LED_CTRL_TRAFFIC_LED 0x00000040
+#define LED_CTRL_1000MBPS_STATUS 0x00000080
+#define LED_CTRL_100MBPS_STATUS 0x00000100
+#define LED_CTRL_10MBPS_STATUS 0x00000200
+#define LED_CTRL_TRAFFIC_STATUS 0x00000400
+#define LED_CTRL_MAC_MODE 0x00000000
+#define LED_CTRL_PHY_MODE_1 0x00000800
+#define LED_CTRL_PHY_MODE_2 0x00001000
+#define LED_CTRL_BLINK_RATE_MASK 0x7ff80000
+#define LED_CTRL_BLINK_RATE_SHIFT 19
+#define LED_CTRL_BLINK_PER_OVERRIDE 0x00080000
+#define LED_CTRL_BLINK_RATE_OVERRIDE 0x80000000
+#define MAC_ADDR_0_HIGH 0x00000410 /* upper 2 bytes */
+#define MAC_ADDR_0_LOW 0x00000414 /* lower 4 bytes */
+#define MAC_ADDR_1_HIGH 0x00000418 /* upper 2 bytes */
+#define MAC_ADDR_1_LOW 0x0000041c /* lower 4 bytes */
+#define MAC_ADDR_2_HIGH 0x00000420 /* upper 2 bytes */
+#define MAC_ADDR_2_LOW 0x00000424 /* lower 4 bytes */
+#define MAC_ADDR_3_HIGH 0x00000428 /* upper 2 bytes */
+#define MAC_ADDR_3_LOW 0x0000042c /* lower 4 bytes */
+#define MAC_ACPI_MBUF_PTR 0x00000430
+#define MAC_ACPI_LEN_OFFSET 0x00000434
+#define ACPI_LENOFF_LEN_MASK 0x0000ffff
+#define ACPI_LENOFF_LEN_SHIFT 0
+#define ACPI_LENOFF_OFF_MASK 0x0fff0000
+#define ACPI_LENOFF_OFF_SHIFT 16
+#define MAC_TX_BACKOFF_SEED 0x00000438
+#define TX_BACKOFF_SEED_MASK 0x000003ff
+#define MAC_RX_MTU_SIZE 0x0000043c
+#define RX_MTU_SIZE_MASK 0x0000ffff
+#define MAC_PCS_TEST 0x00000440
+#define PCS_TEST_PATTERN_MASK 0x000fffff
+#define PCS_TEST_PATTERN_SHIFT 0
+#define PCS_TEST_ENABLE 0x00100000
+#define MAC_TX_AUTO_NEG 0x00000444
+#define TX_AUTO_NEG_MASK 0x0000ffff
+#define TX_AUTO_NEG_SHIFT 0
+#define MAC_RX_AUTO_NEG 0x00000448
+#define RX_AUTO_NEG_MASK 0x0000ffff
+#define RX_AUTO_NEG_SHIFT 0
+#define MAC_MI_COM 0x0000044c
+#define MI_COM_CMD_MASK 0x0c000000
+#define MI_COM_CMD_WRITE 0x04000000
+#define MI_COM_CMD_READ 0x08000000
+#define MI_COM_READ_FAILED 0x10000000
+#define MI_COM_START 0x20000000
+#define MI_COM_BUSY 0x20000000
+#define MI_COM_PHY_ADDR_MASK 0x03e00000
+#define MI_COM_PHY_ADDR_SHIFT 21
+#define MI_COM_REG_ADDR_MASK 0x001f0000
+#define MI_COM_REG_ADDR_SHIFT 16
+#define MI_COM_DATA_MASK 0x0000ffff
+#define MAC_MI_STAT 0x00000450
+#define MAC_MI_STAT_LNKSTAT_ATTN_ENAB 0x00000001
+#define MAC_MI_MODE 0x00000454
+#define MAC_MI_MODE_CLK_10MHZ 0x00000001
+#define MAC_MI_MODE_SHORT_PREAMBLE 0x00000002
+#define MAC_MI_MODE_AUTO_POLL 0x00000010
+#define MAC_MI_MODE_CORE_CLK_62MHZ 0x00008000
+#define MAC_MI_MODE_BASE 0x000c0000 /* XXX magic values XXX */
+#define MAC_AUTO_POLL_STATUS 0x00000458
+#define MAC_AUTO_POLL_ERROR 0x00000001
+#define MAC_TX_MODE 0x0000045c
+#define TX_MODE_RESET 0x00000001
+#define TX_MODE_ENABLE 0x00000002
+#define TX_MODE_FLOW_CTRL_ENABLE 0x00000010
+#define TX_MODE_BIG_BCKOFF_ENABLE 0x00000020
+#define TX_MODE_LONG_PAUSE_ENABLE 0x00000040
+#define MAC_TX_STATUS 0x00000460
+#define TX_STATUS_XOFFED 0x00000001
+#define TX_STATUS_SENT_XOFF 0x00000002
+#define TX_STATUS_SENT_XON 0x00000004
+#define TX_STATUS_LINK_UP 0x00000008
+#define TX_STATUS_ODI_UNDERRUN 0x00000010
+#define TX_STATUS_ODI_OVERRUN 0x00000020
+#define MAC_TX_LENGTHS 0x00000464
+#define TX_LENGTHS_SLOT_TIME_MASK 0x000000ff
+#define TX_LENGTHS_SLOT_TIME_SHIFT 0
+#define TX_LENGTHS_IPG_MASK 0x00000f00
+#define TX_LENGTHS_IPG_SHIFT 8
+#define TX_LENGTHS_IPG_CRS_MASK 0x00003000
+#define TX_LENGTHS_IPG_CRS_SHIFT 12
+#define MAC_RX_MODE 0x00000468
+#define RX_MODE_RESET 0x00000001
+#define RX_MODE_ENABLE 0x00000002
+#define RX_MODE_FLOW_CTRL_ENABLE 0x00000004
+#define RX_MODE_KEEP_MAC_CTRL 0x00000008
+#define RX_MODE_KEEP_PAUSE 0x00000010
+#define RX_MODE_ACCEPT_OVERSIZED 0x00000020
+#define RX_MODE_ACCEPT_RUNTS 0x00000040
+#define RX_MODE_LEN_CHECK 0x00000080
+#define RX_MODE_PROMISC 0x00000100
+#define RX_MODE_NO_CRC_CHECK 0x00000200
+#define RX_MODE_KEEP_VLAN_TAG 0x00000400
+#define MAC_RX_STATUS 0x0000046c
+#define RX_STATUS_REMOTE_TX_XOFFED 0x00000001
+#define RX_STATUS_XOFF_RCVD 0x00000002
+#define RX_STATUS_XON_RCVD 0x00000004
+#define MAC_HASH_REG_0 0x00000470
+#define MAC_HASH_REG_1 0x00000474
+#define MAC_HASH_REG_2 0x00000478
+#define MAC_HASH_REG_3 0x0000047c
+#define MAC_RCV_RULE_0 0x00000480
+#define MAC_RCV_VALUE_0 0x00000484
+#define MAC_RCV_RULE_1 0x00000488
+#define MAC_RCV_VALUE_1 0x0000048c
+#define MAC_RCV_RULE_2 0x00000490
+#define MAC_RCV_VALUE_2 0x00000494
+#define MAC_RCV_RULE_3 0x00000498
+#define MAC_RCV_VALUE_3 0x0000049c
+#define MAC_RCV_RULE_4 0x000004a0
+#define MAC_RCV_VALUE_4 0x000004a4
+#define MAC_RCV_RULE_5 0x000004a8
+#define MAC_RCV_VALUE_5 0x000004ac
+#define MAC_RCV_RULE_6 0x000004b0
+#define MAC_RCV_VALUE_6 0x000004b4
+#define MAC_RCV_RULE_7 0x000004b8
+#define MAC_RCV_VALUE_7 0x000004bc
+#define MAC_RCV_RULE_8 0x000004c0
+#define MAC_RCV_VALUE_8 0x000004c4
+#define MAC_RCV_RULE_9 0x000004c8
+#define MAC_RCV_VALUE_9 0x000004cc
+#define MAC_RCV_RULE_10 0x000004d0
+#define MAC_RCV_VALUE_10 0x000004d4
+#define MAC_RCV_RULE_11 0x000004d8
+#define MAC_RCV_VALUE_11 0x000004dc
+#define MAC_RCV_RULE_12 0x000004e0
+#define MAC_RCV_VALUE_12 0x000004e4
+#define MAC_RCV_RULE_13 0x000004e8
+#define MAC_RCV_VALUE_13 0x000004ec
+#define MAC_RCV_RULE_14 0x000004f0
+#define MAC_RCV_VALUE_14 0x000004f4
+#define MAC_RCV_RULE_15 0x000004f8
+#define MAC_RCV_VALUE_15 0x000004fc
+#define RCV_RULE_DISABLE_MASK 0x7fffffff
+#define MAC_RCV_RULE_CFG 0x00000500
+#define RCV_RULE_CFG_DEFAULT_CLASS 0x00000008
+#define MAC_LOW_WMARK_MAX_RX_FRAME 0x00000504
+/* 0x508 --> 0x520 unused */
+#define MAC_HASHREGU_0 0x00000520
+#define MAC_HASHREGU_1 0x00000524
+#define MAC_HASHREGU_2 0x00000528
+#define MAC_HASHREGU_3 0x0000052c
+#define MAC_EXTADDR_0_HIGH 0x00000530
+#define MAC_EXTADDR_0_LOW 0x00000534
+#define MAC_EXTADDR_1_HIGH 0x00000538
+#define MAC_EXTADDR_1_LOW 0x0000053c
+#define MAC_EXTADDR_2_HIGH 0x00000540
+#define MAC_EXTADDR_2_LOW 0x00000544
+#define MAC_EXTADDR_3_HIGH 0x00000548
+#define MAC_EXTADDR_3_LOW 0x0000054c
+#define MAC_EXTADDR_4_HIGH 0x00000550
+#define MAC_EXTADDR_4_LOW 0x00000554
+#define MAC_EXTADDR_5_HIGH 0x00000558
+#define MAC_EXTADDR_5_LOW 0x0000055c
+#define MAC_EXTADDR_6_HIGH 0x00000560
+#define MAC_EXTADDR_6_LOW 0x00000564
+#define MAC_EXTADDR_7_HIGH 0x00000568
+#define MAC_EXTADDR_7_LOW 0x0000056c
+#define MAC_EXTADDR_8_HIGH 0x00000570
+#define MAC_EXTADDR_8_LOW 0x00000574
+#define MAC_EXTADDR_9_HIGH 0x00000578
+#define MAC_EXTADDR_9_LOW 0x0000057c
+#define MAC_EXTADDR_10_HIGH 0x00000580
+#define MAC_EXTADDR_10_LOW 0x00000584
+#define MAC_EXTADDR_11_HIGH 0x00000588
+#define MAC_EXTADDR_11_LOW 0x0000058c
+#define MAC_SERDES_CFG 0x00000590
+#define MAC_SERDES_STAT 0x00000594
+/* 0x598 --> 0x600 unused */
+#define MAC_TX_MAC_STATE_BASE 0x00000600 /* 16 bytes */
+#define MAC_RX_MAC_STATE_BASE 0x00000610 /* 20 bytes */
+/* 0x624 --> 0x800 unused */
+#define MAC_TX_STATS_OCTETS 0x00000800
+#define MAC_TX_STATS_RESV1 0x00000804
+#define MAC_TX_STATS_COLLISIONS 0x00000808
+#define MAC_TX_STATS_XON_SENT 0x0000080c
+#define MAC_TX_STATS_XOFF_SENT 0x00000810
+#define MAC_TX_STATS_RESV2 0x00000814
+#define MAC_TX_STATS_MAC_ERRORS 0x00000818
+#define MAC_TX_STATS_SINGLE_COLLISIONS 0x0000081c
+#define MAC_TX_STATS_MULT_COLLISIONS 0x00000820
+#define MAC_TX_STATS_DEFERRED 0x00000824
+#define MAC_TX_STATS_RESV3 0x00000828
+#define MAC_TX_STATS_EXCESSIVE_COL 0x0000082c
+#define MAC_TX_STATS_LATE_COL 0x00000830
+#define MAC_TX_STATS_RESV4_1 0x00000834
+#define MAC_TX_STATS_RESV4_2 0x00000838
+#define MAC_TX_STATS_RESV4_3 0x0000083c
+#define MAC_TX_STATS_RESV4_4 0x00000840
+#define MAC_TX_STATS_RESV4_5 0x00000844
+#define MAC_TX_STATS_RESV4_6 0x00000848
+#define MAC_TX_STATS_RESV4_7 0x0000084c
+#define MAC_TX_STATS_RESV4_8 0x00000850
+#define MAC_TX_STATS_RESV4_9 0x00000854
+#define MAC_TX_STATS_RESV4_10 0x00000858
+#define MAC_TX_STATS_RESV4_11 0x0000085c
+#define MAC_TX_STATS_RESV4_12 0x00000860
+#define MAC_TX_STATS_RESV4_13 0x00000864
+#define MAC_TX_STATS_RESV4_14 0x00000868
+#define MAC_TX_STATS_UCAST 0x0000086c
+#define MAC_TX_STATS_MCAST 0x00000870
+#define MAC_TX_STATS_BCAST 0x00000874
+#define MAC_TX_STATS_RESV5_1 0x00000878
+#define MAC_TX_STATS_RESV5_2 0x0000087c
+#define MAC_RX_STATS_OCTETS 0x00000880
+#define MAC_RX_STATS_RESV1 0x00000884
+#define MAC_RX_STATS_FRAGMENTS 0x00000888
+#define MAC_RX_STATS_UCAST 0x0000088c
+#define MAC_RX_STATS_MCAST 0x00000890
+#define MAC_RX_STATS_BCAST 0x00000894
+#define MAC_RX_STATS_FCS_ERRORS 0x00000898
+#define MAC_RX_STATS_ALIGN_ERRORS 0x0000089c
+#define MAC_RX_STATS_XON_PAUSE_RECVD 0x000008a0
+#define MAC_RX_STATS_XOFF_PAUSE_RECVD 0x000008a4
+#define MAC_RX_STATS_MAC_CTRL_RECVD 0x000008a8
+#define MAC_RX_STATS_XOFF_ENTERED 0x000008ac
+#define MAC_RX_STATS_FRAME_TOO_LONG 0x000008b0
+#define MAC_RX_STATS_JABBERS 0x000008b4
+#define MAC_RX_STATS_UNDERSIZE 0x000008b8
+/* 0x8bc --> 0xc00 unused */
+
+/* Send data initiator control registers */
+#define SNDDATAI_MODE 0x00000c00
+#define SNDDATAI_MODE_RESET 0x00000001
+#define SNDDATAI_MODE_ENABLE 0x00000002
+#define SNDDATAI_MODE_STAT_OFLOW_ENAB 0x00000004
+#define SNDDATAI_STATUS 0x00000c04
+#define SNDDATAI_STATUS_STAT_OFLOW 0x00000004
+#define SNDDATAI_STATSCTRL 0x00000c08
+#define SNDDATAI_SCTRL_ENABLE 0x00000001
+#define SNDDATAI_SCTRL_FASTUPD 0x00000002
+#define SNDDATAI_SCTRL_CLEAR 0x00000004
+#define SNDDATAI_SCTRL_FLUSH 0x00000008
+#define SNDDATAI_SCTRL_FORCE_ZERO 0x00000010
+#define SNDDATAI_STATSENAB 0x00000c0c
+#define SNDDATAI_STATSINCMASK 0x00000c10
+/* 0xc14 --> 0xc80 unused */
+#define SNDDATAI_COS_CNT_0 0x00000c80
+#define SNDDATAI_COS_CNT_1 0x00000c84
+#define SNDDATAI_COS_CNT_2 0x00000c88
+#define SNDDATAI_COS_CNT_3 0x00000c8c
+#define SNDDATAI_COS_CNT_4 0x00000c90
+#define SNDDATAI_COS_CNT_5 0x00000c94
+#define SNDDATAI_COS_CNT_6 0x00000c98
+#define SNDDATAI_COS_CNT_7 0x00000c9c
+#define SNDDATAI_COS_CNT_8 0x00000ca0
+#define SNDDATAI_COS_CNT_9 0x00000ca4
+#define SNDDATAI_COS_CNT_10 0x00000ca8
+#define SNDDATAI_COS_CNT_11 0x00000cac
+#define SNDDATAI_COS_CNT_12 0x00000cb0
+#define SNDDATAI_COS_CNT_13 0x00000cb4
+#define SNDDATAI_COS_CNT_14 0x00000cb8
+#define SNDDATAI_COS_CNT_15 0x00000cbc
+#define SNDDATAI_DMA_RDQ_FULL_CNT 0x00000cc0
+#define SNDDATAI_DMA_PRIO_RDQ_FULL_CNT 0x00000cc4
+#define SNDDATAI_SDCQ_FULL_CNT 0x00000cc8
+#define SNDDATAI_NICRNG_SSND_PIDX_CNT 0x00000ccc
+#define SNDDATAI_STATS_UPDATED_CNT 0x00000cd0
+#define SNDDATAI_INTERRUPTS_CNT 0x00000cd4
+#define SNDDATAI_AVOID_INTERRUPTS_CNT 0x00000cd8
+#define SNDDATAI_SND_THRESH_HIT_CNT 0x00000cdc
+/* 0xce0 --> 0x1000 unused */
+
+/* Send data completion control registers */
+#define SNDDATAC_MODE 0x00001000
+#define SNDDATAC_MODE_RESET 0x00000001
+#define SNDDATAC_MODE_ENABLE 0x00000002
+/* 0x1004 --> 0x1400 unused */
+
+/* Send BD ring selector */
+#define SNDBDS_MODE 0x00001400
+#define SNDBDS_MODE_RESET 0x00000001
+#define SNDBDS_MODE_ENABLE 0x00000002
+#define SNDBDS_MODE_ATTN_ENABLE 0x00000004
+#define SNDBDS_STATUS 0x00001404
+#define SNDBDS_STATUS_ERROR_ATTN 0x00000004
+#define SNDBDS_HWDIAG 0x00001408
+/* 0x140c --> 0x1440 */
+#define SNDBDS_SEL_CON_IDX_0 0x00001440
+#define SNDBDS_SEL_CON_IDX_1 0x00001444
+#define SNDBDS_SEL_CON_IDX_2 0x00001448
+#define SNDBDS_SEL_CON_IDX_3 0x0000144c
+#define SNDBDS_SEL_CON_IDX_4 0x00001450
+#define SNDBDS_SEL_CON_IDX_5 0x00001454
+#define SNDBDS_SEL_CON_IDX_6 0x00001458
+#define SNDBDS_SEL_CON_IDX_7 0x0000145c
+#define SNDBDS_SEL_CON_IDX_8 0x00001460
+#define SNDBDS_SEL_CON_IDX_9 0x00001464
+#define SNDBDS_SEL_CON_IDX_10 0x00001468
+#define SNDBDS_SEL_CON_IDX_11 0x0000146c
+#define SNDBDS_SEL_CON_IDX_12 0x00001470
+#define SNDBDS_SEL_CON_IDX_13 0x00001474
+#define SNDBDS_SEL_CON_IDX_14 0x00001478
+#define SNDBDS_SEL_CON_IDX_15 0x0000147c
+/* 0x1480 --> 0x1800 unused */
+
+/* Send BD initiator control registers */
+#define SNDBDI_MODE 0x00001800
+#define SNDBDI_MODE_RESET 0x00000001
+#define SNDBDI_MODE_ENABLE 0x00000002
+#define SNDBDI_MODE_ATTN_ENABLE 0x00000004
+#define SNDBDI_STATUS 0x00001804
+#define SNDBDI_STATUS_ERROR_ATTN 0x00000004
+#define SNDBDI_IN_PROD_IDX_0 0x00001808
+#define SNDBDI_IN_PROD_IDX_1 0x0000180c
+#define SNDBDI_IN_PROD_IDX_2 0x00001810
+#define SNDBDI_IN_PROD_IDX_3 0x00001814
+#define SNDBDI_IN_PROD_IDX_4 0x00001818
+#define SNDBDI_IN_PROD_IDX_5 0x0000181c
+#define SNDBDI_IN_PROD_IDX_6 0x00001820
+#define SNDBDI_IN_PROD_IDX_7 0x00001824
+#define SNDBDI_IN_PROD_IDX_8 0x00001828
+#define SNDBDI_IN_PROD_IDX_9 0x0000182c
+#define SNDBDI_IN_PROD_IDX_10 0x00001830
+#define SNDBDI_IN_PROD_IDX_11 0x00001834
+#define SNDBDI_IN_PROD_IDX_12 0x00001838
+#define SNDBDI_IN_PROD_IDX_13 0x0000183c
+#define SNDBDI_IN_PROD_IDX_14 0x00001840
+#define SNDBDI_IN_PROD_IDX_15 0x00001844
+/* 0x1848 --> 0x1c00 unused */
+
+/* Send BD completion control registers */
+#define SNDBDC_MODE 0x00001c00
+#define SNDBDC_MODE_RESET 0x00000001
+#define SNDBDC_MODE_ENABLE 0x00000002
+#define SNDBDC_MODE_ATTN_ENABLE 0x00000004
+/* 0x1c04 --> 0x2000 unused */
+
+/* Receive list placement control registers */
+#define RCVLPC_MODE 0x00002000
+#define RCVLPC_MODE_RESET 0x00000001
+#define RCVLPC_MODE_ENABLE 0x00000002
+#define RCVLPC_MODE_CLASS0_ATTN_ENAB 0x00000004
+#define RCVLPC_MODE_MAPOOR_AATTN_ENAB 0x00000008
+#define RCVLPC_MODE_STAT_OFLOW_ENAB 0x00000010
+#define RCVLPC_STATUS 0x00002004
+#define RCVLPC_STATUS_CLASS0 0x00000004
+#define RCVLPC_STATUS_MAPOOR 0x00000008
+#define RCVLPC_STATUS_STAT_OFLOW 0x00000010
+#define RCVLPC_LOCK 0x00002008
+#define RCVLPC_LOCK_REQ_MASK 0x0000ffff
+#define RCVLPC_LOCK_REQ_SHIFT 0
+#define RCVLPC_LOCK_GRANT_MASK 0xffff0000
+#define RCVLPC_LOCK_GRANT_SHIFT 16
+#define RCVLPC_NON_EMPTY_BITS 0x0000200c
+#define RCVLPC_NON_EMPTY_BITS_MASK 0x0000ffff
+#define RCVLPC_CONFIG 0x00002010
+#define RCVLPC_STATSCTRL 0x00002014
+#define RCVLPC_STATSCTRL_ENABLE 0x00000001
+#define RCVLPC_STATSCTRL_FASTUPD 0x00000002
+#define RCVLPC_STATS_ENABLE 0x00002018
+#define RCVLPC_STATSENAB_LNGBRST_RFIX 0x00400000
+#define RCVLPC_STATS_INCMASK 0x0000201c
+/* 0x2020 --> 0x2100 unused */
+#define RCVLPC_SELLST_BASE 0x00002100 /* 16 16-byte entries */
+#define SELLST_TAIL 0x00000004
+#define SELLST_CONT 0x00000008
+#define SELLST_UNUSED 0x0000000c
+#define RCVLPC_COS_CNTL_BASE 0x00002200 /* 16 4-byte entries */
+#define RCVLPC_DROP_FILTER_CNT 0x00002240
+#define RCVLPC_DMA_WQ_FULL_CNT 0x00002244
+#define RCVLPC_DMA_HIPRIO_WQ_FULL_CNT 0x00002248
+#define RCVLPC_NO_RCV_BD_CNT 0x0000224c
+#define RCVLPC_IN_DISCARDS_CNT 0x00002250
+#define RCVLPC_IN_ERRORS_CNT 0x00002254
+#define RCVLPC_RCV_THRESH_HIT_CNT 0x00002258
+/* 0x225c --> 0x2400 unused */
+
+/* Receive Data and Receive BD Initiator Control */
+#define RCVDBDI_MODE 0x00002400
+#define RCVDBDI_MODE_RESET 0x00000001
+#define RCVDBDI_MODE_ENABLE 0x00000002
+#define RCVDBDI_MODE_JUMBOBD_NEEDED 0x00000004
+#define RCVDBDI_MODE_FRM_TOO_BIG 0x00000008
+#define RCVDBDI_MODE_INV_RING_SZ 0x00000010
+#define RCVDBDI_STATUS 0x00002404
+#define RCVDBDI_STATUS_JUMBOBD_NEEDED 0x00000004
+#define RCVDBDI_STATUS_FRM_TOO_BIG 0x00000008
+#define RCVDBDI_STATUS_INV_RING_SZ 0x00000010
+#define RCVDBDI_SPLIT_FRAME_MINSZ 0x00002408
+/* 0x240c --> 0x2440 unused */
+#define RCVDBDI_JUMBO_BD 0x00002440 /* TG3_BDINFO_... */
+#define RCVDBDI_STD_BD 0x00002450 /* TG3_BDINFO_... */
+#define RCVDBDI_MINI_BD 0x00002460 /* TG3_BDINFO_... */
+#define RCVDBDI_JUMBO_CON_IDX 0x00002470
+#define RCVDBDI_STD_CON_IDX 0x00002474
+#define RCVDBDI_MINI_CON_IDX 0x00002478
+/* 0x247c --> 0x2480 unused */
+#define RCVDBDI_BD_PROD_IDX_0 0x00002480
+#define RCVDBDI_BD_PROD_IDX_1 0x00002484
+#define RCVDBDI_BD_PROD_IDX_2 0x00002488
+#define RCVDBDI_BD_PROD_IDX_3 0x0000248c
+#define RCVDBDI_BD_PROD_IDX_4 0x00002490
+#define RCVDBDI_BD_PROD_IDX_5 0x00002494
+#define RCVDBDI_BD_PROD_IDX_6 0x00002498
+#define RCVDBDI_BD_PROD_IDX_7 0x0000249c
+#define RCVDBDI_BD_PROD_IDX_8 0x000024a0
+#define RCVDBDI_BD_PROD_IDX_9 0x000024a4
+#define RCVDBDI_BD_PROD_IDX_10 0x000024a8
+#define RCVDBDI_BD_PROD_IDX_11 0x000024ac
+#define RCVDBDI_BD_PROD_IDX_12 0x000024b0
+#define RCVDBDI_BD_PROD_IDX_13 0x000024b4
+#define RCVDBDI_BD_PROD_IDX_14 0x000024b8
+#define RCVDBDI_BD_PROD_IDX_15 0x000024bc
+#define RCVDBDI_HWDIAG 0x000024c0
+/* 0x24c4 --> 0x2800 unused */
+
+/* Receive Data Completion Control */
+#define RCVDCC_MODE 0x00002800
+#define RCVDCC_MODE_RESET 0x00000001
+#define RCVDCC_MODE_ENABLE 0x00000002
+#define RCVDCC_MODE_ATTN_ENABLE 0x00000004
+/* 0x2804 --> 0x2c00 unused */
+
+/* Receive BD Initiator Control Registers */
+#define RCVBDI_MODE 0x00002c00
+#define RCVBDI_MODE_RESET 0x00000001
+#define RCVBDI_MODE_ENABLE 0x00000002
+#define RCVBDI_MODE_RCB_ATTN_ENAB 0x00000004
+#define RCVBDI_STATUS 0x00002c04
+#define RCVBDI_STATUS_RCB_ATTN 0x00000004
+#define RCVBDI_JUMBO_PROD_IDX 0x00002c08
+#define RCVBDI_STD_PROD_IDX 0x00002c0c
+#define RCVBDI_MINI_PROD_IDX 0x00002c10
+#define RCVBDI_MINI_THRESH 0x00002c14
+#define RCVBDI_STD_THRESH 0x00002c18
+#define RCVBDI_JUMBO_THRESH 0x00002c1c
+/* 0x2c20 --> 0x3000 unused */
+
+/* Receive BD Completion Control Registers */
+#define RCVCC_MODE 0x00003000
+#define RCVCC_MODE_RESET 0x00000001
+#define RCVCC_MODE_ENABLE 0x00000002
+#define RCVCC_MODE_ATTN_ENABLE 0x00000004
+#define RCVCC_STATUS 0x00003004
+#define RCVCC_STATUS_ERROR_ATTN 0x00000004
+#define RCVCC_JUMP_PROD_IDX 0x00003008
+#define RCVCC_STD_PROD_IDX 0x0000300c
+#define RCVCC_MINI_PROD_IDX 0x00003010
+/* 0x3014 --> 0x3400 unused */
+
+/* Receive list selector control registers */
+#define RCVLSC_MODE 0x00003400
+#define RCVLSC_MODE_RESET 0x00000001
+#define RCVLSC_MODE_ENABLE 0x00000002
+#define RCVLSC_MODE_ATTN_ENABLE 0x00000004
+#define RCVLSC_STATUS 0x00003404
+#define RCVLSC_STATUS_ERROR_ATTN 0x00000004
+/* 0x3408 --> 0x3800 unused */
+
+/* Mbuf cluster free registers */
+#define MBFREE_MODE 0x00003800
+#define MBFREE_MODE_RESET 0x00000001
+#define MBFREE_MODE_ENABLE 0x00000002
+#define MBFREE_STATUS 0x00003804
+/* 0x3808 --> 0x3c00 unused */
+
+/* Host coalescing control registers */
+#define HOSTCC_MODE 0x00003c00
+#define HOSTCC_MODE_RESET 0x00000001
+#define HOSTCC_MODE_ENABLE 0x00000002
+#define HOSTCC_MODE_ATTN 0x00000004
+#define HOSTCC_MODE_NOW 0x00000008
+#define HOSTCC_MODE_FULL_STATUS 0x00000000
+#define HOSTCC_MODE_64BYTE 0x00000080
+#define HOSTCC_MODE_32BYTE 0x00000100
+#define HOSTCC_MODE_CLRTICK_RXBD 0x00000200
+#define HOSTCC_MODE_CLRTICK_TXBD 0x00000400
+#define HOSTCC_MODE_NOINT_ON_NOW 0x00000800
+#define HOSTCC_MODE_NOINT_ON_FORCE 0x00001000
+#define HOSTCC_STATUS 0x00003c04
+#define HOSTCC_STATUS_ERROR_ATTN 0x00000004
+#define HOSTCC_RXCOL_TICKS 0x00003c08
+#define LOW_RXCOL_TICKS 0x00000032
+#define DEFAULT_RXCOL_TICKS 0x00000048
+#define HIGH_RXCOL_TICKS 0x00000096
+#define HOSTCC_TXCOL_TICKS 0x00003c0c
+#define LOW_TXCOL_TICKS 0x00000096
+#define DEFAULT_TXCOL_TICKS 0x0000012c
+#define HIGH_TXCOL_TICKS 0x00000145
+#define HOSTCC_RXMAX_FRAMES 0x00003c10
+#define LOW_RXMAX_FRAMES 0x00000005
+#define DEFAULT_RXMAX_FRAMES 0x00000008
+#define HIGH_RXMAX_FRAMES 0x00000012
+#define HOSTCC_TXMAX_FRAMES 0x00003c14
+#define LOW_TXMAX_FRAMES 0x00000035
+#define DEFAULT_TXMAX_FRAMES 0x0000004b
+#define HIGH_TXMAX_FRAMES 0x00000052
+#define HOSTCC_RXCOAL_TICK_INT 0x00003c18
+#define DEFAULT_RXCOAL_TICK_INT 0x00000019
+#define HOSTCC_TXCOAL_TICK_INT 0x00003c1c
+#define DEFAULT_TXCOAL_TICK_INT 0x00000019
+#define HOSTCC_RXCOAL_MAXF_INT 0x00003c20
+#define DEFAULT_RXCOAL_MAXF_INT 0x00000005
+#define HOSTCC_TXCOAL_MAXF_INT 0x00003c24
+#define DEFAULT_TXCOAL_MAXF_INT 0x00000005
+#define HOSTCC_STAT_COAL_TICKS 0x00003c28
+#define DEFAULT_STAT_COAL_TICKS 0x000f4240
+/* 0x3c2c --> 0x3c30 unused */
+#define HOSTCC_STATS_BLK_HOST_ADDR 0x00003c30 /* 64-bit */
+#define HOSTCC_STATUS_BLK_HOST_ADDR 0x00003c38 /* 64-bit */
+#define HOSTCC_STATS_BLK_NIC_ADDR 0x00003c40
+#define HOSTCC_STATUS_BLK_NIC_ADDR 0x00003c44
+#define HOSTCC_FLOW_ATTN 0x00003c48
+/* 0x3c4c --> 0x3c50 unused */
+#define HOSTCC_JUMBO_CON_IDX 0x00003c50
+#define HOSTCC_STD_CON_IDX 0x00003c54
+#define HOSTCC_MINI_CON_IDX 0x00003c58
+/* 0x3c5c --> 0x3c80 unused */
+#define HOSTCC_RET_PROD_IDX_0 0x00003c80
+#define HOSTCC_RET_PROD_IDX_1 0x00003c84
+#define HOSTCC_RET_PROD_IDX_2 0x00003c88
+#define HOSTCC_RET_PROD_IDX_3 0x00003c8c
+#define HOSTCC_RET_PROD_IDX_4 0x00003c90
+#define HOSTCC_RET_PROD_IDX_5 0x00003c94
+#define HOSTCC_RET_PROD_IDX_6 0x00003c98
+#define HOSTCC_RET_PROD_IDX_7 0x00003c9c
+#define HOSTCC_RET_PROD_IDX_8 0x00003ca0
+#define HOSTCC_RET_PROD_IDX_9 0x00003ca4
+#define HOSTCC_RET_PROD_IDX_10 0x00003ca8
+#define HOSTCC_RET_PROD_IDX_11 0x00003cac
+#define HOSTCC_RET_PROD_IDX_12 0x00003cb0
+#define HOSTCC_RET_PROD_IDX_13 0x00003cb4
+#define HOSTCC_RET_PROD_IDX_14 0x00003cb8
+#define HOSTCC_RET_PROD_IDX_15 0x00003cbc
+#define HOSTCC_SND_CON_IDX_0 0x00003cc0
+#define HOSTCC_SND_CON_IDX_1 0x00003cc4
+#define HOSTCC_SND_CON_IDX_2 0x00003cc8
+#define HOSTCC_SND_CON_IDX_3 0x00003ccc
+#define HOSTCC_SND_CON_IDX_4 0x00003cd0
+#define HOSTCC_SND_CON_IDX_5 0x00003cd4
+#define HOSTCC_SND_CON_IDX_6 0x00003cd8
+#define HOSTCC_SND_CON_IDX_7 0x00003cdc
+#define HOSTCC_SND_CON_IDX_8 0x00003ce0
+#define HOSTCC_SND_CON_IDX_9 0x00003ce4
+#define HOSTCC_SND_CON_IDX_10 0x00003ce8
+#define HOSTCC_SND_CON_IDX_11 0x00003cec
+#define HOSTCC_SND_CON_IDX_12 0x00003cf0
+#define HOSTCC_SND_CON_IDX_13 0x00003cf4
+#define HOSTCC_SND_CON_IDX_14 0x00003cf8
+#define HOSTCC_SND_CON_IDX_15 0x00003cfc
+/* 0x3d00 --> 0x4000 unused */
+
+/* Memory arbiter control registers */
+#define MEMARB_MODE 0x00004000
+#define MEMARB_MODE_RESET 0x00000001
+#define MEMARB_MODE_ENABLE 0x00000002
+#define MEMARB_STATUS 0x00004004
+#define MEMARB_TRAP_ADDR_LOW 0x00004008
+#define MEMARB_TRAP_ADDR_HIGH 0x0000400c
+/* 0x4010 --> 0x4400 unused */
+
+/* Buffer manager control registers */
+#define BUFMGR_MODE 0x00004400
+#define BUFMGR_MODE_RESET 0x00000001
+#define BUFMGR_MODE_ENABLE 0x00000002
+#define BUFMGR_MODE_ATTN_ENABLE 0x00000004
+#define BUFMGR_MODE_BM_TEST 0x00000008
+#define BUFMGR_MODE_MBLOW_ATTN_ENAB 0x00000010
+#define BUFMGR_STATUS 0x00004404
+#define BUFMGR_STATUS_ERROR 0x00000004
+#define BUFMGR_STATUS_MBLOW 0x00000010
+#define BUFMGR_MB_POOL_ADDR 0x00004408
+#define BUFMGR_MB_POOL_SIZE 0x0000440c
+#define BUFMGR_MB_RDMA_LOW_WATER 0x00004410
+#define DEFAULT_MB_RDMA_LOW_WATER 0x00000050
+#define DEFAULT_MB_RDMA_LOW_WATER_5705 0x00000000
+#define DEFAULT_MB_RDMA_LOW_WATER_JUMBO 0x00000130
+#define BUFMGR_MB_MACRX_LOW_WATER 0x00004414
+#define DEFAULT_MB_MACRX_LOW_WATER 0x00000020
+#define DEFAULT_MB_MACRX_LOW_WATER_5705 0x00000010
+#define DEFAULT_MB_MACRX_LOW_WATER_JUMBO 0x00000098
+#define BUFMGR_MB_HIGH_WATER 0x00004418
+#define DEFAULT_MB_HIGH_WATER 0x00000060
+#define DEFAULT_MB_HIGH_WATER_5705 0x00000060
+#define DEFAULT_MB_HIGH_WATER_JUMBO 0x0000017c
+#define BUFMGR_RX_MB_ALLOC_REQ 0x0000441c
+#define BUFMGR_MB_ALLOC_BIT 0x10000000
+#define BUFMGR_RX_MB_ALLOC_RESP 0x00004420
+#define BUFMGR_TX_MB_ALLOC_REQ 0x00004424
+#define BUFMGR_TX_MB_ALLOC_RESP 0x00004428
+#define BUFMGR_DMA_DESC_POOL_ADDR 0x0000442c
+#define BUFMGR_DMA_DESC_POOL_SIZE 0x00004430
+#define BUFMGR_DMA_LOW_WATER 0x00004434
+#define DEFAULT_DMA_LOW_WATER 0x00000005
+#define BUFMGR_DMA_HIGH_WATER 0x00004438
+#define DEFAULT_DMA_HIGH_WATER 0x0000000a
+#define BUFMGR_RX_DMA_ALLOC_REQ 0x0000443c
+#define BUFMGR_RX_DMA_ALLOC_RESP 0x00004440
+#define BUFMGR_TX_DMA_ALLOC_REQ 0x00004444
+#define BUFMGR_TX_DMA_ALLOC_RESP 0x00004448
+#define BUFMGR_HWDIAG_0 0x0000444c
+#define BUFMGR_HWDIAG_1 0x00004450
+#define BUFMGR_HWDIAG_2 0x00004454
+/* 0x4458 --> 0x4800 unused */
+
+/* Read DMA control registers */
+#define RDMAC_MODE 0x00004800
+#define RDMAC_MODE_RESET 0x00000001
+#define RDMAC_MODE_ENABLE 0x00000002
+#define RDMAC_MODE_TGTABORT_ENAB 0x00000004
+#define RDMAC_MODE_MSTABORT_ENAB 0x00000008
+#define RDMAC_MODE_PARITYERR_ENAB 0x00000010
+#define RDMAC_MODE_ADDROFLOW_ENAB 0x00000020
+#define RDMAC_MODE_FIFOOFLOW_ENAB 0x00000040
+#define RDMAC_MODE_FIFOURUN_ENAB 0x00000080
+#define RDMAC_MODE_FIFOOREAD_ENAB 0x00000100
+#define RDMAC_MODE_LNGREAD_ENAB 0x00000200
+#define RDMAC_MODE_SPLIT_ENABLE 0x00000800
+#define RDMAC_MODE_SPLIT_RESET 0x00001000
+#define RDMAC_MODE_FIFO_SIZE_128 0x00020000
+#define RDMAC_MODE_FIFO_LONG_BURST 0x00030000
+#define RDMAC_STATUS 0x00004804
+#define RDMAC_STATUS_TGTABORT 0x00000004
+#define RDMAC_STATUS_MSTABORT 0x00000008
+#define RDMAC_STATUS_PARITYERR 0x00000010
+#define RDMAC_STATUS_ADDROFLOW 0x00000020
+#define RDMAC_STATUS_FIFOOFLOW 0x00000040
+#define RDMAC_STATUS_FIFOURUN 0x00000080
+#define RDMAC_STATUS_FIFOOREAD 0x00000100
+#define RDMAC_STATUS_LNGREAD 0x00000200
+/* 0x4808 --> 0x4c00 unused */
+
+/* Write DMA control registers */
+#define WDMAC_MODE 0x00004c00
+#define WDMAC_MODE_RESET 0x00000001
+#define WDMAC_MODE_ENABLE 0x00000002
+#define WDMAC_MODE_TGTABORT_ENAB 0x00000004
+#define WDMAC_MODE_MSTABORT_ENAB 0x00000008
+#define WDMAC_MODE_PARITYERR_ENAB 0x00000010
+#define WDMAC_MODE_ADDROFLOW_ENAB 0x00000020
+#define WDMAC_MODE_FIFOOFLOW_ENAB 0x00000040
+#define WDMAC_MODE_FIFOURUN_ENAB 0x00000080
+#define WDMAC_MODE_FIFOOREAD_ENAB 0x00000100
+#define WDMAC_MODE_LNGREAD_ENAB 0x00000200
+#define WDMAC_MODE_RX_ACCEL 0x00000400
+#define WDMAC_STATUS 0x00004c04
+#define WDMAC_STATUS_TGTABORT 0x00000004
+#define WDMAC_STATUS_MSTABORT 0x00000008
+#define WDMAC_STATUS_PARITYERR 0x00000010
+#define WDMAC_STATUS_ADDROFLOW 0x00000020
+#define WDMAC_STATUS_FIFOOFLOW 0x00000040
+#define WDMAC_STATUS_FIFOURUN 0x00000080
+#define WDMAC_STATUS_FIFOOREAD 0x00000100
+#define WDMAC_STATUS_LNGREAD 0x00000200
+/* 0x4c08 --> 0x5000 unused */
+
+/* Per-cpu register offsets (arm9) */
+#define CPU_MODE 0x00000000
+#define CPU_MODE_RESET 0x00000001
+#define CPU_MODE_HALT 0x00000400
+#define CPU_STATE 0x00000004
+#define CPU_EVTMASK 0x00000008
+/* 0xc --> 0x1c reserved */
+#define CPU_PC 0x0000001c
+#define CPU_INSN 0x00000020
+#define CPU_SPAD_UFLOW 0x00000024
+#define CPU_WDOG_CLEAR 0x00000028
+#define CPU_WDOG_VECTOR 0x0000002c
+#define CPU_WDOG_PC 0x00000030
+#define CPU_HW_BP 0x00000034
+/* 0x38 --> 0x44 unused */
+#define CPU_WDOG_SAVED_STATE 0x00000044
+#define CPU_LAST_BRANCH_ADDR 0x00000048
+#define CPU_SPAD_UFLOW_SET 0x0000004c
+/* 0x50 --> 0x200 unused */
+#define CPU_R0 0x00000200
+#define CPU_R1 0x00000204
+#define CPU_R2 0x00000208
+#define CPU_R3 0x0000020c
+#define CPU_R4 0x00000210
+#define CPU_R5 0x00000214
+#define CPU_R6 0x00000218
+#define CPU_R7 0x0000021c
+#define CPU_R8 0x00000220
+#define CPU_R9 0x00000224
+#define CPU_R10 0x00000228
+#define CPU_R11 0x0000022c
+#define CPU_R12 0x00000230
+#define CPU_R13 0x00000234
+#define CPU_R14 0x00000238
+#define CPU_R15 0x0000023c
+#define CPU_R16 0x00000240
+#define CPU_R17 0x00000244
+#define CPU_R18 0x00000248
+#define CPU_R19 0x0000024c
+#define CPU_R20 0x00000250
+#define CPU_R21 0x00000254
+#define CPU_R22 0x00000258
+#define CPU_R23 0x0000025c
+#define CPU_R24 0x00000260
+#define CPU_R25 0x00000264
+#define CPU_R26 0x00000268
+#define CPU_R27 0x0000026c
+#define CPU_R28 0x00000270
+#define CPU_R29 0x00000274
+#define CPU_R30 0x00000278
+#define CPU_R31 0x0000027c
+/* 0x280 --> 0x400 unused */
+
+#define RX_CPU_BASE 0x00005000
+#define TX_CPU_BASE 0x00005400
+
+/* Mailboxes */
+#define GRCMBOX_INTERRUPT_0 0x00005800 /* 64-bit */
+#define GRCMBOX_INTERRUPT_1 0x00005808 /* 64-bit */
+#define GRCMBOX_INTERRUPT_2 0x00005810 /* 64-bit */
+#define GRCMBOX_INTERRUPT_3 0x00005818 /* 64-bit */
+#define GRCMBOX_GENERAL_0 0x00005820 /* 64-bit */
+#define GRCMBOX_GENERAL_1 0x00005828 /* 64-bit */
+#define GRCMBOX_GENERAL_2 0x00005830 /* 64-bit */
+#define GRCMBOX_GENERAL_3 0x00005838 /* 64-bit */
+#define GRCMBOX_GENERAL_4 0x00005840 /* 64-bit */
+#define GRCMBOX_GENERAL_5 0x00005848 /* 64-bit */
+#define GRCMBOX_GENERAL_6 0x00005850 /* 64-bit */
+#define GRCMBOX_GENERAL_7 0x00005858 /* 64-bit */
+#define GRCMBOX_RELOAD_STAT 0x00005860 /* 64-bit */
+#define GRCMBOX_RCVSTD_PROD_IDX 0x00005868 /* 64-bit */
+#define GRCMBOX_RCVJUMBO_PROD_IDX 0x00005870 /* 64-bit */
+#define GRCMBOX_RCVMINI_PROD_IDX 0x00005878 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_0 0x00005880 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_1 0x00005888 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_2 0x00005890 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_3 0x00005898 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_4 0x000058a0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_5 0x000058a8 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_6 0x000058b0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_7 0x000058b8 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_8 0x000058c0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_9 0x000058c8 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_10 0x000058d0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_11 0x000058d8 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_12 0x000058e0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_13 0x000058e8 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_14 0x000058f0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_15 0x000058f8 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_0 0x00005900 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_1 0x00005908 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_2 0x00005910 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_3 0x00005918 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_4 0x00005920 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_5 0x00005928 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_6 0x00005930 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_7 0x00005938 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_8 0x00005940 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_9 0x00005948 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_10 0x00005950 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_11 0x00005958 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_12 0x00005960 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_13 0x00005968 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_14 0x00005970 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_15 0x00005978 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_0 0x00005980 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_1 0x00005988 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_2 0x00005990 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_3 0x00005998 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_4 0x000059a0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_5 0x000059a8 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_6 0x000059b0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_7 0x000059b8 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_8 0x000059c0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_9 0x000059c8 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_10 0x000059d0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_11 0x000059d8 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_12 0x000059e0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_13 0x000059e8 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_14 0x000059f0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_15 0x000059f8 /* 64-bit */
+#define GRCMBOX_HIGH_PRIO_EV_VECTOR 0x00005a00
+#define GRCMBOX_HIGH_PRIO_EV_MASK 0x00005a04
+#define GRCMBOX_LOW_PRIO_EV_VEC 0x00005a08
+#define GRCMBOX_LOW_PRIO_EV_MASK 0x00005a0c
+/* 0x5a10 --> 0x5c00 */
+
+/* Flow Through queues */
+#define FTQ_RESET 0x00005c00
+/* 0x5c04 --> 0x5c10 unused */
+#define FTQ_DMA_NORM_READ_CTL 0x00005c10
+#define FTQ_DMA_NORM_READ_FULL_CNT 0x00005c14
+#define FTQ_DMA_NORM_READ_FIFO_ENQDEQ 0x00005c18
+#define FTQ_DMA_NORM_READ_WRITE_PEEK 0x00005c1c
+#define FTQ_DMA_HIGH_READ_CTL 0x00005c20
+#define FTQ_DMA_HIGH_READ_FULL_CNT 0x00005c24
+#define FTQ_DMA_HIGH_READ_FIFO_ENQDEQ 0x00005c28
+#define FTQ_DMA_HIGH_READ_WRITE_PEEK 0x00005c2c
+#define FTQ_DMA_COMP_DISC_CTL 0x00005c30
+#define FTQ_DMA_COMP_DISC_FULL_CNT 0x00005c34
+#define FTQ_DMA_COMP_DISC_FIFO_ENQDEQ 0x00005c38
+#define FTQ_DMA_COMP_DISC_WRITE_PEEK 0x00005c3c
+#define FTQ_SEND_BD_COMP_CTL 0x00005c40
+#define FTQ_SEND_BD_COMP_FULL_CNT 0x00005c44
+#define FTQ_SEND_BD_COMP_FIFO_ENQDEQ 0x00005c48
+#define FTQ_SEND_BD_COMP_WRITE_PEEK 0x00005c4c
+#define FTQ_SEND_DATA_INIT_CTL 0x00005c50
+#define FTQ_SEND_DATA_INIT_FULL_CNT 0x00005c54
+#define FTQ_SEND_DATA_INIT_FIFO_ENQDEQ 0x00005c58
+#define FTQ_SEND_DATA_INIT_WRITE_PEEK 0x00005c5c
+#define FTQ_DMA_NORM_WRITE_CTL 0x00005c60
+#define FTQ_DMA_NORM_WRITE_FULL_CNT 0x00005c64
+#define FTQ_DMA_NORM_WRITE_FIFO_ENQDEQ 0x00005c68
+#define FTQ_DMA_NORM_WRITE_WRITE_PEEK 0x00005c6c
+#define FTQ_DMA_HIGH_WRITE_CTL 0x00005c70
+#define FTQ_DMA_HIGH_WRITE_FULL_CNT 0x00005c74
+#define FTQ_DMA_HIGH_WRITE_FIFO_ENQDEQ 0x00005c78
+#define FTQ_DMA_HIGH_WRITE_WRITE_PEEK 0x00005c7c
+#define FTQ_SWTYPE1_CTL 0x00005c80
+#define FTQ_SWTYPE1_FULL_CNT 0x00005c84
+#define FTQ_SWTYPE1_FIFO_ENQDEQ 0x00005c88
+#define FTQ_SWTYPE1_WRITE_PEEK 0x00005c8c
+#define FTQ_SEND_DATA_COMP_CTL 0x00005c90
+#define FTQ_SEND_DATA_COMP_FULL_CNT 0x00005c94
+#define FTQ_SEND_DATA_COMP_FIFO_ENQDEQ 0x00005c98
+#define FTQ_SEND_DATA_COMP_WRITE_PEEK 0x00005c9c
+#define FTQ_HOST_COAL_CTL 0x00005ca0
+#define FTQ_HOST_COAL_FULL_CNT 0x00005ca4
+#define FTQ_HOST_COAL_FIFO_ENQDEQ 0x00005ca8
+#define FTQ_HOST_COAL_WRITE_PEEK 0x00005cac
+#define FTQ_MAC_TX_CTL 0x00005cb0
+#define FTQ_MAC_TX_FULL_CNT 0x00005cb4
+#define FTQ_MAC_TX_FIFO_ENQDEQ 0x00005cb8
+#define FTQ_MAC_TX_WRITE_PEEK 0x00005cbc
+#define FTQ_MB_FREE_CTL 0x00005cc0
+#define FTQ_MB_FREE_FULL_CNT 0x00005cc4
+#define FTQ_MB_FREE_FIFO_ENQDEQ 0x00005cc8
+#define FTQ_MB_FREE_WRITE_PEEK 0x00005ccc
+#define FTQ_RCVBD_COMP_CTL 0x00005cd0
+#define FTQ_RCVBD_COMP_FULL_CNT 0x00005cd4
+#define FTQ_RCVBD_COMP_FIFO_ENQDEQ 0x00005cd8
+#define FTQ_RCVBD_COMP_WRITE_PEEK 0x00005cdc
+#define FTQ_RCVLST_PLMT_CTL 0x00005ce0
+#define FTQ_RCVLST_PLMT_FULL_CNT 0x00005ce4
+#define FTQ_RCVLST_PLMT_FIFO_ENQDEQ 0x00005ce8
+#define FTQ_RCVLST_PLMT_WRITE_PEEK 0x00005cec
+#define FTQ_RCVDATA_INI_CTL 0x00005cf0
+#define FTQ_RCVDATA_INI_FULL_CNT 0x00005cf4
+#define FTQ_RCVDATA_INI_FIFO_ENQDEQ 0x00005cf8
+#define FTQ_RCVDATA_INI_WRITE_PEEK 0x00005cfc
+#define FTQ_RCVDATA_COMP_CTL 0x00005d00
+#define FTQ_RCVDATA_COMP_FULL_CNT 0x00005d04
+#define FTQ_RCVDATA_COMP_FIFO_ENQDEQ 0x00005d08
+#define FTQ_RCVDATA_COMP_WRITE_PEEK 0x00005d0c
+#define FTQ_SWTYPE2_CTL 0x00005d10
+#define FTQ_SWTYPE2_FULL_CNT 0x00005d14
+#define FTQ_SWTYPE2_FIFO_ENQDEQ 0x00005d18
+#define FTQ_SWTYPE2_WRITE_PEEK 0x00005d1c
+/* 0x5d20 --> 0x6000 unused */
+
+/* Message signaled interrupt registers */
+#define MSGINT_MODE 0x00006000
+#define MSGINT_MODE_RESET 0x00000001
+#define MSGINT_MODE_ENABLE 0x00000002
+#define MSGINT_STATUS 0x00006004
+#define MSGINT_FIFO 0x00006008
+/* 0x600c --> 0x6400 unused */
+
+/* DMA completion registers */
+#define DMAC_MODE 0x00006400
+#define DMAC_MODE_RESET 0x00000001
+#define DMAC_MODE_ENABLE 0x00000002
+/* 0x6404 --> 0x6800 unused */
+
+/* GRC registers */
+#define GRC_MODE 0x00006800
+#define GRC_MODE_UPD_ON_COAL 0x00000001
+#define GRC_MODE_BSWAP_NONFRM_DATA 0x00000002
+#define GRC_MODE_WSWAP_NONFRM_DATA 0x00000004
+#define GRC_MODE_BSWAP_DATA 0x00000010
+#define GRC_MODE_WSWAP_DATA 0x00000020
+#define GRC_MODE_SPLITHDR 0x00000100
+#define GRC_MODE_NOFRM_CRACKING 0x00000200
+#define GRC_MODE_INCL_CRC 0x00000400
+#define GRC_MODE_ALLOW_BAD_FRMS 0x00000800
+#define GRC_MODE_NOIRQ_ON_SENDS 0x00002000
+#define GRC_MODE_NOIRQ_ON_RCV 0x00004000
+#define GRC_MODE_FORCE_PCI32BIT 0x00008000
+#define GRC_MODE_HOST_STACKUP 0x00010000
+#define GRC_MODE_HOST_SENDBDS 0x00020000
+#define GRC_MODE_NO_TX_PHDR_CSUM 0x00100000
+#define GRC_MODE_NO_RX_PHDR_CSUM 0x00800000
+#define GRC_MODE_IRQ_ON_TX_CPU_ATTN 0x01000000
+#define GRC_MODE_IRQ_ON_RX_CPU_ATTN 0x02000000
+#define GRC_MODE_IRQ_ON_MAC_ATTN 0x04000000
+#define GRC_MODE_IRQ_ON_DMA_ATTN 0x08000000
+#define GRC_MODE_IRQ_ON_FLOW_ATTN 0x10000000
+#define GRC_MODE_4X_NIC_SEND_RINGS 0x20000000
+#define GRC_MODE_MCAST_FRM_ENABLE 0x40000000
+#define GRC_MISC_CFG 0x00006804
+#define GRC_MISC_CFG_CORECLK_RESET 0x00000001
+#define GRC_MISC_CFG_PRESCALAR_MASK 0x000000fe
+#define GRC_MISC_CFG_PRESCALAR_SHIFT 1
+#define GRC_MISC_CFG_BOARD_ID_MASK 0x0001e000
+#define GRC_MISC_CFG_BOARD_ID_5700 0x0001e000
+#define GRC_MISC_CFG_BOARD_ID_5701 0x00000000
+#define GRC_MISC_CFG_BOARD_ID_5702FE 0x00004000
+#define GRC_MISC_CFG_BOARD_ID_5703 0x00000000
+#define GRC_MISC_CFG_BOARD_ID_5703S 0x00002000
+#define GRC_MISC_CFG_BOARD_ID_5704 0x00000000
+#define GRC_MISC_CFG_BOARD_ID_5704CIOBE 0x00004000
+#define GRC_MISC_CFG_BOARD_ID_5704_A2 0x00008000
+#define GRC_MISC_CFG_BOARD_ID_5788 0x00010000
+#define GRC_MISC_CFG_BOARD_ID_5788M 0x00018000
+#define GRC_MISC_CFG_BOARD_ID_AC91002A1 0x00018000
+#define GRC_MISC_CFG_KEEP_GPHY_POWER 0x04000000
+#define GRC_LOCAL_CTRL 0x00006808
+#define GRC_LCLCTRL_INT_ACTIVE 0x00000001
+#define GRC_LCLCTRL_CLEARINT 0x00000002
+#define GRC_LCLCTRL_SETINT 0x00000004
+#define GRC_LCLCTRL_INT_ON_ATTN 0x00000008
+#define GRC_LCLCTRL_GPIO_INPUT0 0x00000100
+#define GRC_LCLCTRL_GPIO_INPUT1 0x00000200
+#define GRC_LCLCTRL_GPIO_INPUT2 0x00000400
+#define GRC_LCLCTRL_GPIO_OE0 0x00000800
+#define GRC_LCLCTRL_GPIO_OE1 0x00001000
+#define GRC_LCLCTRL_GPIO_OE2 0x00002000
+#define GRC_LCLCTRL_GPIO_OUTPUT0 0x00004000
+#define GRC_LCLCTRL_GPIO_OUTPUT1 0x00008000
+#define GRC_LCLCTRL_GPIO_OUTPUT2 0x00010000
+#define GRC_LCLCTRL_EXTMEM_ENABLE 0x00020000
+#define GRC_LCLCTRL_MEMSZ_MASK 0x001c0000
+#define GRC_LCLCTRL_MEMSZ_256K 0x00000000
+#define GRC_LCLCTRL_MEMSZ_512K 0x00040000
+#define GRC_LCLCTRL_MEMSZ_1M 0x00080000
+#define GRC_LCLCTRL_MEMSZ_2M 0x000c0000
+#define GRC_LCLCTRL_MEMSZ_4M 0x00100000
+#define GRC_LCLCTRL_MEMSZ_8M 0x00140000
+#define GRC_LCLCTRL_MEMSZ_16M 0x00180000
+#define GRC_LCLCTRL_BANK_SELECT 0x00200000
+#define GRC_LCLCTRL_SSRAM_TYPE 0x00400000
+#define GRC_LCLCTRL_AUTO_SEEPROM 0x01000000
+#define GRC_TIMER 0x0000680c
+#define GRC_RX_CPU_EVENT 0x00006810
+#define GRC_RX_TIMER_REF 0x00006814
+#define GRC_RX_CPU_SEM 0x00006818
+#define GRC_REMOTE_RX_CPU_ATTN 0x0000681c
+#define GRC_TX_CPU_EVENT 0x00006820
+#define GRC_TX_TIMER_REF 0x00006824
+#define GRC_TX_CPU_SEM 0x00006828
+#define GRC_REMOTE_TX_CPU_ATTN 0x0000682c
+#define GRC_MEM_POWER_UP 0x00006830 /* 64-bit */
+#define GRC_EEPROM_ADDR 0x00006838
+#define EEPROM_ADDR_WRITE 0x00000000
+#define EEPROM_ADDR_READ 0x80000000
+#define EEPROM_ADDR_COMPLETE 0x40000000
+#define EEPROM_ADDR_FSM_RESET 0x20000000
+#define EEPROM_ADDR_DEVID_MASK 0x1c000000
+#define EEPROM_ADDR_DEVID_SHIFT 26
+#define EEPROM_ADDR_START 0x02000000
+#define EEPROM_ADDR_CLKPERD_SHIFT 16
+#define EEPROM_ADDR_ADDR_MASK 0x0000ffff
+#define EEPROM_ADDR_ADDR_SHIFT 0
+#define EEPROM_DEFAULT_CLOCK_PERIOD 0x60
+#define EEPROM_CHIP_SIZE (64 * 1024)
+#define GRC_EEPROM_DATA 0x0000683c
+#define GRC_EEPROM_CTRL 0x00006840
+#define GRC_MDI_CTRL 0x00006844
+#define GRC_SEEPROM_DELAY 0x00006848
+/* 0x684c --> 0x6c00 unused */
+
+/* 0x6c00 --> 0x7000 unused */
+
+/* NVRAM Control registers */
+#define NVRAM_CMD 0x00007000
+#define NVRAM_CMD_RESET 0x00000001
+#define NVRAM_CMD_DONE 0x00000008
+#define NVRAM_CMD_GO 0x00000010
+#define NVRAM_CMD_WR 0x00000020
+#define NVRAM_CMD_RD 0x00000000
+#define NVRAM_CMD_ERASE 0x00000040
+#define NVRAM_CMD_FIRST 0x00000080
+#define NVRAM_CMD_LAST 0x00000100
+#define NVRAM_STAT 0x00007004
+#define NVRAM_WRDATA 0x00007008
+#define NVRAM_ADDR 0x0000700c
+#define NVRAM_ADDR_MSK 0x00ffffff
+#define NVRAM_RDDATA 0x00007010
+#define NVRAM_CFG1 0x00007014
+#define NVRAM_CFG1_FLASHIF_ENAB 0x00000001
+#define NVRAM_CFG1_BUFFERED_MODE 0x00000002
+#define NVRAM_CFG1_PASS_THRU 0x00000004
+#define NVRAM_CFG1_BIT_BANG 0x00000008
+#define NVRAM_CFG1_COMPAT_BYPASS 0x80000000
+#define NVRAM_CFG2 0x00007018
+#define NVRAM_CFG3 0x0000701c
+#define NVRAM_SWARB 0x00007020
+#define SWARB_REQ_SET0 0x00000001
+#define SWARB_REQ_SET1 0x00000002
+#define SWARB_REQ_SET2 0x00000004
+#define SWARB_REQ_SET3 0x00000008
+#define SWARB_REQ_CLR0 0x00000010
+#define SWARB_REQ_CLR1 0x00000020
+#define SWARB_REQ_CLR2 0x00000040
+#define SWARB_REQ_CLR3 0x00000080
+#define SWARB_GNT0 0x00000100
+#define SWARB_GNT1 0x00000200
+#define SWARB_GNT2 0x00000400
+#define SWARB_GNT3 0x00000800
+#define SWARB_REQ0 0x00001000
+#define SWARB_REQ1 0x00002000
+#define SWARB_REQ2 0x00004000
+#define SWARB_REQ3 0x00008000
+#define NVRAM_BUFFERED_PAGE_SIZE 264
+#define NVRAM_BUFFERED_PAGE_POS 9
+/* 0x7024 --> 0x7400 unused */
+
+/* 0x7400 --> 0x8000 unused */
+
+/* 32K Window into NIC internal memory */
+#define NIC_SRAM_WIN_BASE 0x00008000
+
+/* Offsets into first 32k of NIC internal memory. */
+#define NIC_SRAM_PAGE_ZERO 0x00000000
+#define NIC_SRAM_SEND_RCB 0x00000100 /* 16 * TG3_BDINFO_... */
+#define NIC_SRAM_RCV_RET_RCB 0x00000200 /* 16 * TG3_BDINFO_... */
+#define NIC_SRAM_STATS_BLK 0x00000300
+#define NIC_SRAM_STATUS_BLK 0x00000b00
+
+#define NIC_SRAM_FIRMWARE_MBOX 0x00000b50
+#define NIC_SRAM_FIRMWARE_MBOX_MAGIC1 0x4B657654
+#define NIC_SRAM_FIRMWARE_MBOX_MAGIC2 0x4861764b /* !dma on linkchg */
+
+#define NIC_SRAM_DATA_SIG 0x00000b54
+#define NIC_SRAM_DATA_SIG_MAGIC 0x4b657654 /* ascii for 'KevT' */
+
+#define NIC_SRAM_DATA_CFG 0x00000b58
+#define NIC_SRAM_DATA_CFG_LED_MODE_MASK 0x0000000c
+#define NIC_SRAM_DATA_CFG_LED_MODE_UNKNOWN 0x00000000
+#define NIC_SRAM_DATA_CFG_LED_TRIPLE_SPD 0x00000004
+#define NIC_SRAM_DATA_CFG_LED_OPEN_DRAIN 0x00000004
+#define NIC_SRAM_DATA_CFG_LED_LINK_SPD 0x00000008
+#define NIC_SRAM_DATA_CFG_LED_OUTPUT 0x00000008
+#define NIC_SRAM_DATA_CFG_PHY_TYPE_MASK 0x00000030
+#define NIC_SRAM_DATA_CFG_PHY_TYPE_UNKNOWN 0x00000000
+#define NIC_SRAM_DATA_CFG_PHY_TYPE_COPPER 0x00000010
+#define NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER 0x00000020
+#define NIC_SRAM_DATA_CFG_WOL_ENABLE 0x00000040
+#define NIC_SRAM_DATA_CFG_ASF_ENABLE 0x00000080
+#define NIC_SRAM_DATA_CFG_EEPROM_WP 0x00000100
+#define NIC_SRAM_DATA_CFG_MINI_PCI 0x00001000
+#define NIC_SRAM_DATA_CFG_FIBER_WOL 0x00004000
+
+#define NIC_SRAM_DATA_PHY_ID 0x00000b74
+#define NIC_SRAM_DATA_PHY_ID1_MASK 0xffff0000
+#define NIC_SRAM_DATA_PHY_ID2_MASK 0x0000ffff
+
+#define NIC_SRAM_FW_CMD_MBOX 0x00000b78
+#define FWCMD_NICDRV_ALIVE 0x00000001
+#define FWCMD_NICDRV_PAUSE_FW 0x00000002
+#define FWCMD_NICDRV_IPV4ADDR_CHG 0x00000003
+#define FWCMD_NICDRV_IPV6ADDR_CHG 0x00000004
+#define FWCMD_NICDRV_FIX_DMAR 0x00000005
+#define FWCMD_NICDRV_FIX_DMAW 0x00000006
+#define NIC_SRAM_FW_CMD_LEN_MBOX 0x00000b7c
+#define NIC_SRAM_FW_CMD_DATA_MBOX 0x00000b80
+#define NIC_SRAM_FW_ASF_STATUS_MBOX 0x00000c00
+#define NIC_SRAM_FW_DRV_STATE_MBOX 0x00000c04
+#define DRV_STATE_START 0x00000001
+#define DRV_STATE_UNLOAD 0x00000002
+#define DRV_STATE_WOL 0x00000003
+#define DRV_STATE_SUSPEND 0x00000004
+
+#define NIC_SRAM_FW_RESET_TYPE_MBOX 0x00000c08
+
+#define NIC_SRAM_MAC_ADDR_HIGH_MBOX 0x00000c14
+#define NIC_SRAM_MAC_ADDR_LOW_MBOX 0x00000c18
+
+#define NIC_SRAM_RX_MINI_BUFFER_DESC 0x00001000
+
+#define NIC_SRAM_DMA_DESC_POOL_BASE 0x00002000
+#define NIC_SRAM_DMA_DESC_POOL_SIZE 0x00002000
+#define NIC_SRAM_TX_BUFFER_DESC 0x00004000 /* 512 entries */
+#define NIC_SRAM_RX_BUFFER_DESC 0x00006000 /* 256 entries */
+#define NIC_SRAM_RX_JUMBO_BUFFER_DESC 0x00007000 /* 256 entries */
+#define NIC_SRAM_MBUF_POOL_BASE 0x00008000
+#define NIC_SRAM_MBUF_POOL_SIZE96 0x00018000
+#define NIC_SRAM_MBUF_POOL_SIZE64 0x00010000
+#define NIC_SRAM_MBUF_POOL_BASE5705 0x00010000
+#define NIC_SRAM_MBUF_POOL_SIZE5705 0x0000e000
+
+/* Currently this is fixed. */
+#define PHY_ADDR 0x01
+
+/* Tigon3 specific PHY MII registers. */
+#define TG3_BMCR_SPEED1000 0x0040
+
+#define MII_TG3_CTRL 0x09 /* 1000-baseT control register */
+#define MII_TG3_CTRL_ADV_1000_HALF 0x0100
+#define MII_TG3_CTRL_ADV_1000_FULL 0x0200
+#define MII_TG3_CTRL_AS_MASTER 0x0800
+#define MII_TG3_CTRL_ENABLE_AS_MASTER 0x1000
+
+#define MII_TG3_EXT_CTRL 0x10 /* Extended control register */
+#define MII_TG3_EXT_CTRL_LNK3_LED_MODE 0x0002
+#define MII_TG3_EXT_CTRL_TBI 0x8000
+
+#define MII_TG3_EXT_STAT 0x11 /* Extended status register */
+#define MII_TG3_EXT_STAT_LPASS 0x0100
+
+#define MII_TG3_DSP_RW_PORT 0x15 /* DSP coefficient read/write port */
+
+#define MII_TG3_DSP_ADDRESS 0x17 /* DSP address register */
+
+#define MII_TG3_AUX_CTRL 0x18 /* auxilliary control register */
+
+#define MII_TG3_AUX_STAT 0x19 /* auxilliary status register */
+#define MII_TG3_AUX_STAT_LPASS 0x0004
+#define MII_TG3_AUX_STAT_SPDMASK 0x0700
+#define MII_TG3_AUX_STAT_10HALF 0x0100
+#define MII_TG3_AUX_STAT_10FULL 0x0200
+#define MII_TG3_AUX_STAT_100HALF 0x0300
+#define MII_TG3_AUX_STAT_100_4 0x0400
+#define MII_TG3_AUX_STAT_100FULL 0x0500
+#define MII_TG3_AUX_STAT_1000HALF 0x0600
+#define MII_TG3_AUX_STAT_1000FULL 0x0700
+
+#define MII_TG3_ISTAT 0x1a /* IRQ status register */
+#define MII_TG3_IMASK 0x1b /* IRQ mask register */
+
+/* ISTAT/IMASK event bits */
+#define MII_TG3_INT_LINKCHG 0x0002
+#define MII_TG3_INT_SPEEDCHG 0x0004
+#define MII_TG3_INT_DUPLEXCHG 0x0008
+#define MII_TG3_INT_ANEG_PAGE_RX 0x0400
+
+/* XXX Add this to mii.h */
+#ifndef ADVERTISE_PAUSE
+#define ADVERTISE_PAUSE_CAP 0x0400
+#endif
+#ifndef ADVERTISE_PAUSE_ASYM
+#define ADVERTISE_PAUSE_ASYM 0x0800
+#endif
+#ifndef LPA_PAUSE
+#define LPA_PAUSE_CAP 0x0400
+#endif
+#ifndef LPA_PAUSE_ASYM
+#define LPA_PAUSE_ASYM 0x0800
+#endif
+
+/* There are two ways to manage the TX descriptors on the tigon3.
+ * Either the descriptors are in host DMA'able memory, or they
+ * exist only in the cards on-chip SRAM. All 16 send bds are under
+ * the same mode, they may not be configured individually.
+ *
+ * The mode we use is controlled by TG3_FLAG_HOST_TXDS in tp->tg3_flags.
+ *
+ * To use host memory TX descriptors:
+ * 1) Set GRC_MODE_HOST_SENDBDS in GRC_MODE register.
+ * Make sure GRC_MODE_4X_NIC_SEND_RINGS is clear.
+ * 2) Allocate DMA'able memory.
+ * 3) In NIC_SRAM_SEND_RCB (of desired index) of on-chip SRAM:
+ * a) Set TG3_BDINFO_HOST_ADDR to DMA address of memory
+ * obtained in step 2
+ * b) Set TG3_BDINFO_NIC_ADDR to NIC_SRAM_TX_BUFFER_DESC.
+ * c) Set len field of TG3_BDINFO_MAXLEN_FLAGS to number
+ * of TX descriptors. Leave flags field clear.
+ * 4) Access TX descriptors via host memory. The chip
+ * will refetch into local SRAM as needed when producer
+ * index mailboxes are updated.
+ *
+ * To use on-chip TX descriptors:
+ * 1) Set GRC_MODE_4X_NIC_SEND_RINGS in GRC_MODE register.
+ * Make sure GRC_MODE_HOST_SENDBDS is clear.
+ * 2) In NIC_SRAM_SEND_RCB (of desired index) of on-chip SRAM:
+ * a) Set TG3_BDINFO_HOST_ADDR to zero.
+ * b) Set TG3_BDINFO_NIC_ADDR to NIC_SRAM_TX_BUFFER_DESC
+ * c) TG3_BDINFO_MAXLEN_FLAGS is don't care.
+ * 3) Access TX descriptors directly in on-chip SRAM
+ * using normal {read,write}l(). (and not using
+ * pointer dereferencing of ioremap()'d memory like
+ * the broken Broadcom driver does)
+ *
+ * Note that BDINFO_FLAGS_DISABLED should be set in the flags field of
+ * TG3_BDINFO_MAXLEN_FLAGS of all unused SEND_RCB indices.
+ */
+struct tg3_tx_buffer_desc {
+ uint32_t addr_hi;
+ uint32_t addr_lo;
+
+ uint32_t len_flags;
+#define TXD_FLAG_TCPUDP_CSUM 0x0001
+#define TXD_FLAG_IP_CSUM 0x0002
+#define TXD_FLAG_END 0x0004
+#define TXD_FLAG_IP_FRAG 0x0008
+#define TXD_FLAG_IP_FRAG_END 0x0010
+#define TXD_FLAG_VLAN 0x0040
+#define TXD_FLAG_COAL_NOW 0x0080
+#define TXD_FLAG_CPU_PRE_DMA 0x0100
+#define TXD_FLAG_CPU_POST_DMA 0x0200
+#define TXD_FLAG_ADD_SRC_ADDR 0x1000
+#define TXD_FLAG_CHOOSE_SRC_ADDR 0x6000
+#define TXD_FLAG_NO_CRC 0x8000
+#define TXD_LEN_SHIFT 16
+
+ uint32_t vlan_tag;
+#define TXD_VLAN_TAG_SHIFT 0
+#define TXD_MSS_SHIFT 16
+};
+
+#define TXD_ADDR 0x00UL /* 64-bit */
+#define TXD_LEN_FLAGS 0x08UL /* 32-bit (upper 16-bits are len) */
+#define TXD_VLAN_TAG 0x0cUL /* 32-bit (upper 16-bits are tag) */
+#define TXD_SIZE 0x10UL
+
+struct tg3_rx_buffer_desc {
+ uint32_t addr_hi;
+ uint32_t addr_lo;
+
+ uint32_t idx_len;
+#define RXD_IDX_MASK 0xffff0000
+#define RXD_IDX_SHIFT 16
+#define RXD_LEN_MASK 0x0000ffff
+#define RXD_LEN_SHIFT 0
+
+ uint32_t type_flags;
+#define RXD_TYPE_SHIFT 16
+#define RXD_FLAGS_SHIFT 0
+
+#define RXD_FLAG_END 0x0004
+#define RXD_FLAG_MINI 0x0800
+#define RXD_FLAG_JUMBO 0x0020
+#define RXD_FLAG_VLAN 0x0040
+#define RXD_FLAG_ERROR 0x0400
+#define RXD_FLAG_IP_CSUM 0x1000
+#define RXD_FLAG_TCPUDP_CSUM 0x2000
+#define RXD_FLAG_IS_TCP 0x4000
+
+ uint32_t ip_tcp_csum;
+#define RXD_IPCSUM_MASK 0xffff0000
+#define RXD_IPCSUM_SHIFT 16
+#define RXD_TCPCSUM_MASK 0x0000ffff
+#define RXD_TCPCSUM_SHIFT 0
+
+ uint32_t err_vlan;
+
+#define RXD_VLAN_MASK 0x0000ffff
+
+#define RXD_ERR_BAD_CRC 0x00010000
+#define RXD_ERR_COLLISION 0x00020000
+#define RXD_ERR_LINK_LOST 0x00040000
+#define RXD_ERR_PHY_DECODE 0x00080000
+#define RXD_ERR_ODD_NIBBLE_RCVD_MII 0x00100000
+#define RXD_ERR_MAC_ABRT 0x00200000
+#define RXD_ERR_TOO_SMALL 0x00400000
+#define RXD_ERR_NO_RESOURCES 0x00800000
+#define RXD_ERR_HUGE_FRAME 0x01000000
+#define RXD_ERR_MASK 0xffff0000
+
+ uint32_t reserved;
+ uint32_t opaque;
+#define RXD_OPAQUE_INDEX_MASK 0x0000ffff
+#define RXD_OPAQUE_INDEX_SHIFT 0
+#define RXD_OPAQUE_RING_STD 0x00010000
+#define RXD_OPAQUE_RING_JUMBO 0x00020000
+#define RXD_OPAQUE_RING_MINI 0x00040000
+#define RXD_OPAQUE_RING_MASK 0x00070000
+};
+
+struct tg3_ext_rx_buffer_desc {
+ struct {
+ uint32_t addr_hi;
+ uint32_t addr_lo;
+ } addrlist[3];
+ uint32_t len2_len1;
+ uint32_t resv_len3;
+ struct tg3_rx_buffer_desc std;
+};
+
+/* We only use this when testing out the DMA engine
+ * at probe time. This is the internal format of buffer
+ * descriptors used by the chip at NIC_SRAM_DMA_DESCS.
+ */
+struct tg3_internal_buffer_desc {
+ uint32_t addr_hi;
+ uint32_t addr_lo;
+ uint32_t nic_mbuf;
+ /* XXX FIX THIS */
+#if __BYTE_ORDER == __BIG_ENDIAN
+ uint16_t cqid_sqid;
+ uint16_t len;
+#else
+ uint16_t len;
+ uint16_t cqid_sqid;
+#endif
+ uint32_t flags;
+ uint32_t __cookie1;
+ uint32_t __cookie2;
+ uint32_t __cookie3;
+};
+
+#define TG3_HW_STATUS_SIZE 0x50
+struct tg3_hw_status {
+ uint32_t status;
+#define SD_STATUS_UPDATED 0x00000001
+#define SD_STATUS_LINK_CHG 0x00000002
+#define SD_STATUS_ERROR 0x00000004
+
+ uint32_t status_tag;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+ uint16_t rx_consumer;
+ uint16_t rx_jumbo_consumer;
+#else
+ uint16_t rx_jumbo_consumer;
+ uint16_t rx_consumer;
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+ uint16_t reserved;
+ uint16_t rx_mini_consumer;
+#else
+ uint16_t rx_mini_consumer;
+ uint16_t reserved;
+#endif
+ struct {
+#if __BYTE_ORDER == __BIG_ENDIAN
+ uint16_t tx_consumer;
+ uint16_t rx_producer;
+#else
+ uint16_t rx_producer;
+ uint16_t tx_consumer;
+#endif
+ } idx[16];
+};
+
+typedef struct {
+ uint32_t high, low;
+} tg3_stat64_t;
+
+struct tg3_hw_stats {
+ uint8_t __reserved0[0x400-0x300];
+
+ /* Statistics maintained by Receive MAC. */
+ tg3_stat64_t rx_octets;
+ uint64_t __reserved1;
+ tg3_stat64_t rx_fragments;
+ tg3_stat64_t rx_ucast_packets;
+ tg3_stat64_t rx_mcast_packets;
+ tg3_stat64_t rx_bcast_packets;
+ tg3_stat64_t rx_fcs_errors;
+ tg3_stat64_t rx_align_errors;
+ tg3_stat64_t rx_xon_pause_rcvd;
+ tg3_stat64_t rx_xoff_pause_rcvd;
+ tg3_stat64_t rx_mac_ctrl_rcvd;
+ tg3_stat64_t rx_xoff_entered;
+ tg3_stat64_t rx_frame_too_long_errors;
+ tg3_stat64_t rx_jabbers;
+ tg3_stat64_t rx_undersize_packets;
+ tg3_stat64_t rx_in_length_errors;
+ tg3_stat64_t rx_out_length_errors;
+ tg3_stat64_t rx_64_or_less_octet_packets;
+ tg3_stat64_t rx_65_to_127_octet_packets;
+ tg3_stat64_t rx_128_to_255_octet_packets;
+ tg3_stat64_t rx_256_to_511_octet_packets;
+ tg3_stat64_t rx_512_to_1023_octet_packets;
+ tg3_stat64_t rx_1024_to_1522_octet_packets;
+ tg3_stat64_t rx_1523_to_2047_octet_packets;
+ tg3_stat64_t rx_2048_to_4095_octet_packets;
+ tg3_stat64_t rx_4096_to_8191_octet_packets;
+ tg3_stat64_t rx_8192_to_9022_octet_packets;
+
+ uint64_t __unused0[37];
+
+ /* Statistics maintained by Transmit MAC. */
+ tg3_stat64_t tx_octets;
+ uint64_t __reserved2;
+ tg3_stat64_t tx_collisions;
+ tg3_stat64_t tx_xon_sent;
+ tg3_stat64_t tx_xoff_sent;
+ tg3_stat64_t tx_flow_control;
+ tg3_stat64_t tx_mac_errors;
+ tg3_stat64_t tx_single_collisions;
+ tg3_stat64_t tx_mult_collisions;
+ tg3_stat64_t tx_deferred;
+ uint64_t __reserved3;
+ tg3_stat64_t tx_excessive_collisions;
+ tg3_stat64_t tx_late_collisions;
+ tg3_stat64_t tx_collide_2times;
+ tg3_stat64_t tx_collide_3times;
+ tg3_stat64_t tx_collide_4times;
+ tg3_stat64_t tx_collide_5times;
+ tg3_stat64_t tx_collide_6times;
+ tg3_stat64_t tx_collide_7times;
+ tg3_stat64_t tx_collide_8times;
+ tg3_stat64_t tx_collide_9times;
+ tg3_stat64_t tx_collide_10times;
+ tg3_stat64_t tx_collide_11times;
+ tg3_stat64_t tx_collide_12times;
+ tg3_stat64_t tx_collide_13times;
+ tg3_stat64_t tx_collide_14times;
+ tg3_stat64_t tx_collide_15times;
+ tg3_stat64_t tx_ucast_packets;
+ tg3_stat64_t tx_mcast_packets;
+ tg3_stat64_t tx_bcast_packets;
+ tg3_stat64_t tx_carrier_sense_errors;
+ tg3_stat64_t tx_discards;
+ tg3_stat64_t tx_errors;
+
+ uint64_t __unused1[31];
+
+ /* Statistics maintained by Receive List Placement. */
+ tg3_stat64_t COS_rx_packets[16];
+ tg3_stat64_t COS_rx_filter_dropped;
+ tg3_stat64_t dma_writeq_full;
+ tg3_stat64_t dma_write_prioq_full;
+ tg3_stat64_t rxbds_empty;
+ tg3_stat64_t rx_discards;
+ tg3_stat64_t rx_errors;
+ tg3_stat64_t rx_threshold_hit;
+
+ uint64_t __unused2[9];
+
+ /* Statistics maintained by Send Data Initiator. */
+ tg3_stat64_t COS_out_packets[16];
+ tg3_stat64_t dma_readq_full;
+ tg3_stat64_t dma_read_prioq_full;
+ tg3_stat64_t tx_comp_queue_full;
+
+ /* Statistics maintained by Host Coalescing. */
+ tg3_stat64_t ring_set_send_prod_index;
+ tg3_stat64_t ring_status_update;
+ tg3_stat64_t nic_irqs;
+ tg3_stat64_t nic_avoided_irqs;
+ tg3_stat64_t nic_tx_threshold_hit;
+
+ uint8_t __reserved4[0xb00-0x9c0];
+};
+
+enum phy_led_mode {
+ led_mode_auto,
+ led_mode_three_link,
+ led_mode_link10
+};
+
+#if 0
+/* 'mapping' is superfluous as the chip does not write into
+ * the tx/rx post rings so we could just fetch it from there.
+ * But the cache behavior is better how we are doing it now.
+ */
+struct ring_info {
+ struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(mapping)
+};
+
+struct tx_ring_info {
+ struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(mapping)
+ uint32_t prev_vlan_tag;
+};
+#endif
+
+struct tg3_config_info {
+ uint32_t flags;
+};
+
+struct tg3_link_config {
+ /* Describes what we're trying to get. */
+ uint32_t advertising;
+#if 0
+ uint16_t speed;
+ uint8_t duplex;
+ uint8_t autoneg;
+#define SPEED_INVALID 0xffff
+#define DUPLEX_INVALID 0xff
+#define AUTONEG_INVALID 0xff
+#endif
+
+ /* Describes what we actually have. */
+ uint8_t active_speed;
+ uint8_t active_duplex;
+
+ /* When we go in and out of low power mode we need
+ * to swap with this state.
+ */
+#if 0
+ int phy_is_low_power;
+ uint16_t orig_speed;
+ uint8_t orig_duplex;
+ uint8_t orig_autoneg;
+#endif
+};
+
+struct tg3_bufmgr_config {
+ uint32_t mbuf_read_dma_low_water;
+ uint32_t mbuf_mac_rx_low_water;
+ uint32_t mbuf_high_water;
+
+ uint32_t mbuf_read_dma_low_water_jumbo;
+ uint32_t mbuf_mac_rx_low_water_jumbo;
+ uint32_t mbuf_high_water_jumbo;
+
+ uint32_t dma_low_water;
+ uint32_t dma_high_water;
+};
+
+struct tg3 {
+#if 0
+ /* SMP locking strategy:
+ *
+ * lock: Held during all operations except TX packet
+ * processing.
+ *
+ * tx_lock: Held during tg3_start_xmit{,_4gbug} and tg3_tx
+ *
+ * If you want to shut up all asynchronous processing you must
+ * acquire both locks, 'lock' taken before 'tx_lock'. IRQs must
+ * be disabled to take 'lock' but only softirq disabling is
+ * necessary for acquisition of 'tx_lock'.
+ */
+ spinlock_t lock;
+ spinlock_t tx_lock;
+#endif
+
+ uint32_t tx_prod;
+#if 0
+ uint32_t tx_cons;
+#endif
+ uint32_t rx_rcb_ptr;
+ uint32_t rx_std_ptr;
+#if 0
+ uint32_t rx_jumbo_ptr;
+ spinlock_t indirect_lock;
+
+ struct net_device_stats net_stats;
+ struct net_device_stats net_stats_prev;
+#endif
+ unsigned long phy_crc_errors;
+
+#if 0
+ uint32_t rx_offset;
+#endif
+ uint32_t tg3_flags;
+#if 0
+#define TG3_FLAG_HOST_TXDS 0x00000001
+#endif
+#define TG3_FLAG_TXD_MBOX_HWBUG 0x00000002
+#define TG3_FLAG_RX_CHECKSUMS 0x00000004
+#define TG3_FLAG_USE_LINKCHG_REG 0x00000008
+#define TG3_FLAG_USE_MI_INTERRUPT 0x00000010
+#define TG3_FLAG_ENABLE_ASF 0x00000020
+#define TG3_FLAG_5701_REG_WRITE_BUG 0x00000040
+#define TG3_FLAG_POLL_SERDES 0x00000080
+#define TG3_FLAG_MBOX_WRITE_REORDER 0x00000100
+#define TG3_FLAG_PCIX_TARGET_HWBUG 0x00000200
+#define TG3_FLAG_WOL_SPEED_100MB 0x00000400
+#define TG3_FLAG_WOL_ENABLE 0x00000800
+#define TG3_FLAG_EEPROM_WRITE_PROT 0x00001000
+#define TG3_FLAG_NVRAM 0x00002000
+#define TG3_FLAG_NVRAM_BUFFERED 0x00004000
+#define TG3_FLAG_RX_PAUSE 0x00008000
+#define TG3_FLAG_TX_PAUSE 0x00010000
+#define TG3_FLAG_PCIX_MODE 0x00020000
+#define TG3_FLAG_PCI_HIGH_SPEED 0x00040000
+#define TG3_FLAG_PCI_32BIT 0x00080000
+#define TG3_FLAG_NO_TX_PSEUDO_CSUM 0x00100000
+#define TG3_FLAG_NO_RX_PSEUDO_CSUM 0x00200000
+#define TG3_FLAG_SERDES_WOL_CAP 0x00400000
+#define TG3_FLAG_JUMBO_ENABLE 0x00800000
+#define TG3_FLAG_10_100_ONLY 0x01000000
+#define TG3_FLAG_PAUSE_AUTONEG 0x02000000
+#define TG3_FLAG_PAUSE_RX 0x04000000
+#define TG3_FLAG_PAUSE_TX 0x08000000
+#define TG3_FLAG_BROKEN_CHECKSUMS 0x10000000
+#define TG3_FLAG_GOT_SERDES_FLOWCTL 0x20000000
+#define TG3_FLAG_SPLIT_MODE 0x40000000
+#define TG3_FLAG_INIT_COMPLETE 0x80000000
+
+ uint32_t tg3_flags2;
+#define TG3_FLG2_RESTART_TIMER 0x00000001
+#define TG3_FLG2_SUN_5704 0x00000002
+#define TG3_FLG2_NO_ETH_WIRE_SPEED 0x00000004
+#define TG3_FLG2_IS_5788 0x00000008
+#define TG3_FLG2_MAX_RXPEND_64 0x00000010
+#define TG3_FLG2_TSO_CAPABLE 0x00000020
+ // Alf: Hope I'm not breaking anything here !
+#define TG3_FLG2_PCI_EXPRESS 0x00000040
+
+
+
+ uint32_t split_mode_max_reqs;
+#define SPLIT_MODE_5704_MAX_REQ 3
+
+#if 0
+ struct timer_list timer;
+ uint16_t timer_counter;
+ uint16_t timer_multiplier;
+ uint32_t timer_offset;
+ uint16_t asf_counter;
+ uint16_t asf_multiplier;
+#endif
+
+ struct tg3_link_config link_config;
+ struct tg3_bufmgr_config bufmgr_config;
+
+#if 0
+ uint32_t rx_pending;
+ uint32_t rx_jumbo_pending;
+ uint32_t tx_pending;
+#endif
+
+ /* cache h/w values, often passed straight to h/w */
+ uint32_t rx_mode;
+ uint32_t tx_mode;
+ uint32_t mac_mode;
+ uint32_t mi_mode;
+ uint32_t misc_host_ctrl;
+ uint32_t grc_mode;
+ uint32_t grc_local_ctrl;
+ uint32_t dma_rwctrl;
+#if 0
+ uint32_t coalesce_mode;
+#endif
+
+ /* PCI block */
+ uint16_t pci_chip_rev_id;
+#if 0
+ uint8_t pci_cacheline_sz;
+ uint8_t pci_lat_timer;
+ uint8_t pci_hdr_type;
+ uint8_t pci_bist;
+#endif
+ uint32_t pci_cfg_state[64 / sizeof(uint32_t)];
+
+ int pm_cap;
+
+ /* PHY info */
+ uint32_t phy_id;
+#define PHY_ID_MASK 0xfffffff0
+#define PHY_ID_BCM5400 0x60008040
+#define PHY_ID_BCM5401 0x60008050
+#define PHY_ID_BCM5411 0x60008070
+#define PHY_ID_BCM5701 0x60008110
+#define PHY_ID_BCM5703 0x60008160
+#define PHY_ID_BCM5704 0x60008190
+#define PHY_ID_BCM5705 0x600081a0
+#define PHY_ID_BCM5750 0x60008180
+#define PHY_ID_BCM8002 0x60010140
+#define PHY_ID_BCM5751 0x00206180
+#define PHY_ID_SERDES 0xfeedbee0
+#define PHY_ID_INVALID 0xffffffff
+#define PHY_ID_REV_MASK 0x0000000f
+#define PHY_REV_BCM5401_B0 0x1
+#define PHY_REV_BCM5401_B2 0x3
+#define PHY_REV_BCM5401_C0 0x6
+#define PHY_REV_BCM5411_X0 0x1 /* Found on Netgear GA302T */
+
+ enum phy_led_mode led_mode;
+
+ char board_part_number[24];
+ uint32_t nic_sram_data_cfg;
+ uint32_t pci_clock_ctrl;
+#if 0
+ struct pci_device *pdev_peer;
+#endif
+
+ /* This macro assumes the passed PHY ID is already masked
+ * with PHY_ID_MASK.
+ */
+#define KNOWN_PHY_ID(X) \
+ ((X) == PHY_ID_BCM5400 || (X) == PHY_ID_BCM5401 || \
+ (X) == PHY_ID_BCM5411 || (X) == PHY_ID_BCM5701 || \
+ (X) == PHY_ID_BCM5703 || (X) == PHY_ID_BCM5704 || \
+ (X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5750 || (X) == PHY_ID_BCM5751 || \
+ (X) == PHY_ID_BCM8002 || (X) == PHY_ID_SERDES)
+
+ unsigned long regs;
+ struct pci_device *pdev;
+ struct nic *nic;
+#if 0
+ struct net_device *dev;
+#endif
+#if TG3_VLAN_TAG_USED
+ struct vlan_group *vlgrp;
+#endif
+
+ struct tg3_rx_buffer_desc *rx_std;
+#if 0
+ struct ring_info *rx_std_buffers;
+ dma_addr_t rx_std_mapping;
+ struct tg3_rx_buffer_desc *rx_jumbo;
+ struct ring_info *rx_jumbo_buffers;
+ dma_addr_t rx_jumbo_mapping;
+#endif
+
+ struct tg3_rx_buffer_desc *rx_rcb;
+#if 0
+ dma_addr_t rx_rcb_mapping;
+#endif
+
+ /* TX descs are only used if TG3_FLAG_HOST_TXDS is set. */
+ struct tg3_tx_buffer_desc *tx_ring;
+#if 0
+ struct tx_ring_info *tx_buffers;
+ dma_addr_t tx_desc_mapping;
+#endif
+
+ struct tg3_hw_status *hw_status;
+#if 0
+ dma_addr_t status_mapping;
+#endif
+#if 0
+ uint32_t msg_enable;
+#endif
+
+ struct tg3_hw_stats *hw_stats;
+#if 0
+ dma_addr_t stats_mapping;
+#endif
+
+ int carrier_ok;
+ uint16_t subsystem_vendor;
+ uint16_t subsystem_device;
+};
+
+#endif /* !(_T3_H) */
diff --git a/gpxe/src/drivers/net/tlan.c b/gpxe/src/drivers/net/tlan.c
new file mode 100644
index 00000000..4fae0170
--- /dev/null
+++ b/gpxe/src/drivers/net/tlan.c
@@ -0,0 +1,1720 @@
+/**************************************************************************
+*
+* tlan.c -- Etherboot device driver for the Texas Instruments ThunderLAN
+* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* lan.c: Linux ThunderLan Driver:
+*
+* by James Banks
+*
+* (C) 1997-1998 Caldera, Inc.
+* (C) 1998 James Banks
+* (C) 1999-2001 Torben Mathiasen
+* (C) 2002 Samuel Chessman
+*
+* REVISION HISTORY:
+* ================
+* v1.0 07-08-2003 timlegge Initial not quite working version
+* v1.1 07-27-2003 timlegge Sync 5.0 and 5.1 versions
+* v1.2 08-19-2003 timlegge Implement Multicast Support
+* v1.3 08-23-2003 timlegge Fix the transmit Function
+* v1.4 01-17-2004 timlegge Initial driver output cleanup
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+#include "tlan.h"
+
+#define drv_version "v1.4"
+#define drv_date "01-17-2004"
+
+/* NIC specific static variables go here */
+#define HZ 100
+#define TX_TIME_OUT (6*HZ)
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+static void TLan_ResetLists(struct nic *nic __unused);
+static void TLan_ResetAdapter(struct nic *nic __unused);
+static void TLan_FinishReset(struct nic *nic __unused);
+
+static void TLan_EeSendStart(u16);
+static int TLan_EeSendByte(u16, u8, int);
+static void TLan_EeReceiveByte(u16, u8 *, int);
+static int TLan_EeReadByte(u16 io_base, u8, u8 *);
+
+static void TLan_PhyDetect(struct nic *nic);
+static void TLan_PhyPowerDown(struct nic *nic);
+static void TLan_PhyPowerUp(struct nic *nic);
+
+
+static void TLan_SetMac(struct nic *nic __unused, int areg, unsigned char *mac);
+
+static void TLan_PhyReset(struct nic *nic);
+static void TLan_PhyStartLink(struct nic *nic);
+static void TLan_PhyFinishAutoNeg(struct nic *nic);
+
+#ifdef MONITOR
+static void TLan_PhyMonitor(struct nic *nic);
+#endif
+
+
+static void refill_rx(struct nic *nic __unused);
+
+static int TLan_MiiReadReg(struct nic *nic __unused, u16, u16, u16 *);
+static void TLan_MiiSendData(u16, u32, unsigned);
+static void TLan_MiiSync(u16);
+static void TLan_MiiWriteReg(struct nic *nic __unused, u16, u16, u16);
+
+
+static const char *media[] = {
+ "10BaseT-HD ", "10BaseT-FD ", "100baseTx-HD ",
+ "100baseTx-FD", "100baseT4", 0
+};
+
+/* This much match tlan_pci_tbl[]! */
+enum tlan_nics {
+ NETEL10 = 0, NETEL100 = 1, NETFLEX3I = 2, THUNDER = 3, NETFLEX3B =
+ 4, NETEL100PI = 5,
+ NETEL100D = 6, NETEL100I = 7, OC2183 = 8, OC2325 = 9, OC2326 =
+ 10, NETELLIGENT_10_100_WS_5100 = 11,
+ NETELLIGENT_10_T2 = 12
+};
+
+struct pci_id_info {
+ const char *name;
+ int nic_id;
+ struct match_info {
+ u32 pci, pci_mask, subsystem, subsystem_mask;
+ u32 revision, revision_mask; /* Only 8 bits. */
+ } id;
+ u32 flags;
+ u16 addrOfs; /* Address Offset */
+};
+
+static const struct pci_id_info tlan_pci_tbl[] = {
+ {"Compaq Netelligent 10 T PCI UTP", NETEL10,
+ {0xae340e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED, 0x83},
+ {"Compaq Netelligent 10/100 TX PCI UTP", NETEL100,
+ {0xae320e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED, 0x83},
+ {"Compaq Integrated NetFlex-3/P", NETFLEX3I,
+ {0xae350e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_NONE, 0x83},
+ {"Compaq NetFlex-3/P", THUNDER,
+ {0xf1300e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY, 0x83},
+ {"Compaq NetFlex-3/P", NETFLEX3B,
+ {0xf1500e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_NONE, 0x83},
+ {"Compaq Netelligent Integrated 10/100 TX UTP", NETEL100PI,
+ {0xae430e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED, 0x83},
+ {"Compaq Netelligent Dual 10/100 TX PCI UTP", NETEL100D,
+ {0xae400e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_NONE, 0x83},
+ {"Compaq Netelligent 10/100 TX Embedded UTP", NETEL100I,
+ {0xb0110e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_NONE, 0x83},
+ {"Olicom OC-2183/2185", OC2183,
+ {0x0013108d, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_USE_INTERN_10, 0x83},
+ {"Olicom OC-2325", OC2325,
+ {0x0012108d, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_UNMANAGED_PHY, 0xF8},
+ {"Olicom OC-2326", OC2326,
+ {0x0014108d, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_USE_INTERN_10, 0xF8},
+ {"Compaq Netelligent 10/100 TX UTP", NETELLIGENT_10_100_WS_5100,
+ {0xb0300e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED, 0x83},
+ {"Compaq Netelligent 10 T/2 PCI UTP/Coax", NETELLIGENT_10_T2,
+ {0xb0120e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_NONE, 0x83},
+ {"Compaq NetFlex-3/E", 0, /* EISA card */
+ {0, 0, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED | TLAN_ADAPTER_UNMANAGED_PHY |
+ TLAN_ADAPTER_BIT_RATE_PHY, 0x83},
+ {"Compaq NetFlex-3/E", 0, /* EISA card */
+ {0, 0, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED, 0x83},
+ {0, 0,
+ {0, 0, 0, 0, 0, 0},
+ 0, 0},
+};
+
+struct TLanList {
+ u32 forward;
+ u16 cStat;
+ u16 frameSize;
+ struct {
+ u32 count;
+ u32 address;
+ } buffer[TLAN_BUFFERS_PER_LIST];
+};
+
+struct {
+ struct TLanList tx_ring[TLAN_NUM_TX_LISTS];
+ unsigned char txb[TLAN_MAX_FRAME_SIZE * TLAN_NUM_TX_LISTS];
+ struct TLanList rx_ring[TLAN_NUM_RX_LISTS];
+ unsigned char rxb[TLAN_MAX_FRAME_SIZE * TLAN_NUM_RX_LISTS];
+} tlan_buffers __shared;
+#define tx_ring tlan_buffers.tx_ring
+#define txb tlan_buffers.txb
+#define rx_ring tlan_buffers.rx_ring
+#define rxb tlan_buffers.rxb
+
+typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE];
+
+static int chip_idx;
+
+/*****************************************************************
+* TLAN Private Information Structure
+*
+****************************************************************/
+static struct tlan_private {
+ unsigned short vendor_id; /* PCI Vendor code */
+ unsigned short dev_id; /* PCI Device code */
+ const char *nic_name;
+ unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indicies */
+ unsigned rx_buf_sz; /* Based on mtu + Slack */
+ struct TLanList *txList;
+ u32 txHead;
+ u32 txInProgress;
+ u32 txTail;
+ int eoc;
+ u32 phyOnline;
+ u32 aui;
+ u32 duplex;
+ u32 phy[2];
+ u32 phyNum;
+ u32 speed;
+ u8 tlanRev;
+ u8 tlanFullDuplex;
+ u8 link;
+ u8 neg_be_verbose;
+} TLanPrivateInfo;
+
+static struct tlan_private *priv;
+
+static u32 BASE;
+
+/***************************************************************
+* TLan_ResetLists
+*
+* Returns:
+* Nothing
+* Parms:
+* dev The device structure with the list
+* stuctures to be reset.
+*
+* This routine sets the variables associated with managing
+* the TLAN lists to their initial values.
+*
+**************************************************************/
+
+static void TLan_ResetLists(struct nic *nic __unused)
+{
+
+ int i;
+ struct TLanList *list;
+ priv->txHead = 0;
+ priv->txTail = 0;
+
+ for (i = 0; i < TLAN_NUM_TX_LISTS; i++) {
+ list = &tx_ring[i];
+ list->cStat = TLAN_CSTAT_UNUSED;
+ list->buffer[0].address = virt_to_bus(txb +
+ (i * TLAN_MAX_FRAME_SIZE));
+ list->buffer[2].count = 0;
+ list->buffer[2].address = 0;
+ list->buffer[9].address = 0;
+ }
+
+ priv->cur_rx = 0;
+ priv->rx_buf_sz = (TLAN_MAX_FRAME_SIZE);
+// priv->rx_head_desc = &rx_ring[0];
+
+ /* Initialize all the Rx descriptors */
+ for (i = 0; i < TLAN_NUM_RX_LISTS; i++) {
+ rx_ring[i].forward = virt_to_le32desc(&rx_ring[i + 1]);
+ rx_ring[i].cStat = TLAN_CSTAT_READY;
+ rx_ring[i].frameSize = TLAN_MAX_FRAME_SIZE;
+ rx_ring[i].buffer[0].count =
+ TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+ rx_ring[i].buffer[0].address =
+ virt_to_le32desc(&rxb[i * TLAN_MAX_FRAME_SIZE]);
+ rx_ring[i].buffer[1].count = 0;
+ rx_ring[i].buffer[1].address = 0;
+ }
+
+ /* Mark the last entry as wrapping the ring */
+ rx_ring[i - 1].forward = virt_to_le32desc(&rx_ring[0]);
+ priv->dirty_rx = (unsigned int) (i - TLAN_NUM_RX_LISTS);
+
+} /* TLan_ResetLists */
+
+/***************************************************************
+* TLan_Reset
+*
+* Returns:
+* 0
+* Parms:
+* dev Pointer to device structure of adapter
+* to be reset.
+*
+* This function resets the adapter and it's physical
+* device. See Chap. 3, pp. 9-10 of the "ThunderLAN
+* Programmer's Guide" for details. The routine tries to
+* implement what is detailed there, though adjustments
+* have been made.
+*
+**************************************************************/
+
+void TLan_ResetAdapter(struct nic *nic __unused)
+{
+ int i;
+ u32 addr;
+ u32 data;
+ u8 data8;
+
+ priv->tlanFullDuplex = FALSE;
+ priv->phyOnline = 0;
+/* 1. Assert reset bit. */
+
+ data = inl(BASE + TLAN_HOST_CMD);
+ data |= TLAN_HC_AD_RST;
+ outl(data, BASE + TLAN_HOST_CMD);
+
+ udelay(1000);
+
+/* 2. Turn off interrupts. ( Probably isn't necessary ) */
+
+ data = inl(BASE + TLAN_HOST_CMD);
+ data |= TLAN_HC_INT_OFF;
+ outl(data, BASE + TLAN_HOST_CMD);
+/* 3. Clear AREGs and HASHs. */
+
+ for (i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4) {
+ TLan_DioWrite32(BASE, (u16) i, 0);
+ }
+
+/* 4. Setup NetConfig register. */
+
+ data =
+ TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+ TLan_DioWrite16(BASE, TLAN_NET_CONFIG, (u16) data);
+
+/* 5. Load Ld_Tmr and Ld_Thr in HOST_CMD. */
+
+ outl(TLAN_HC_LD_TMR | 0x3f, BASE + TLAN_HOST_CMD);
+ outl(TLAN_HC_LD_THR | 0x0, BASE + TLAN_HOST_CMD);
+
+/* 6. Unreset the MII by setting NMRST (in NetSio) to 1. */
+
+ outw(TLAN_NET_SIO, BASE + TLAN_DIO_ADR);
+ addr = BASE + TLAN_DIO_DATA + TLAN_NET_SIO;
+ TLan_SetBit(TLAN_NET_SIO_NMRST, addr);
+
+/* 7. Setup the remaining registers. */
+
+ if (priv->tlanRev >= 0x30) {
+ data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC;
+ TLan_DioWrite8(BASE, TLAN_INT_DIS, data8);
+ }
+ TLan_PhyDetect(nic);
+ data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN;
+
+ if (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_BIT_RATE_PHY) {
+ data |= TLAN_NET_CFG_BIT;
+ if (priv->aui == 1) {
+ TLan_DioWrite8(BASE, TLAN_ACOMMIT, 0x0a);
+ } else if (priv->duplex == TLAN_DUPLEX_FULL) {
+ TLan_DioWrite8(BASE, TLAN_ACOMMIT, 0x00);
+ priv->tlanFullDuplex = TRUE;
+ } else {
+ TLan_DioWrite8(BASE, TLAN_ACOMMIT, 0x08);
+ }
+ }
+
+ if (priv->phyNum == 0) {
+ data |= TLAN_NET_CFG_PHY_EN;
+ }
+ TLan_DioWrite16(BASE, TLAN_NET_CONFIG, (u16) data);
+
+ if (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_UNMANAGED_PHY) {
+ TLan_FinishReset(nic);
+ } else {
+ TLan_PhyPowerDown(nic);
+ }
+
+} /* TLan_ResetAdapter */
+
+void TLan_FinishReset(struct nic *nic)
+{
+
+ u8 data;
+ u32 phy;
+ u8 sio;
+ u16 status;
+ u16 partner;
+ u16 tlphy_ctl;
+ u16 tlphy_par;
+ u16 tlphy_id1, tlphy_id2;
+ int i;
+
+ phy = priv->phy[priv->phyNum];
+
+ data = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP;
+ if (priv->tlanFullDuplex) {
+ data |= TLAN_NET_CMD_DUPLEX;
+ }
+ TLan_DioWrite8(BASE, TLAN_NET_CMD, data);
+ data = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5;
+ if (priv->phyNum == 0) {
+ data |= TLAN_NET_MASK_MASK7;
+ }
+ TLan_DioWrite8(BASE, TLAN_NET_MASK, data);
+ TLan_DioWrite16(BASE, TLAN_MAX_RX, ((1536) + 7) & ~7);
+ TLan_MiiReadReg(nic, phy, MII_GEN_ID_HI, &tlphy_id1);
+ TLan_MiiReadReg(nic, phy, MII_GEN_ID_LO, &tlphy_id2);
+
+ if ((tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_UNMANAGED_PHY)
+ || (priv->aui)) {
+ status = MII_GS_LINK;
+ DBG ( "TLAN: %s: Link forced.\n", priv->nic_name );
+ } else {
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+ udelay(1000);
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+ if ((status & MII_GS_LINK) && /* We only support link info on Nat.Sem. PHY's */
+ (tlphy_id1 == NAT_SEM_ID1)
+ && (tlphy_id2 == NAT_SEM_ID2)) {
+ TLan_MiiReadReg(nic, phy, MII_AN_LPA, &partner);
+ TLan_MiiReadReg(nic, phy, TLAN_TLPHY_PAR,
+ &tlphy_par);
+
+ DBG ( "TLAN: %s: Link active with ",
+ priv->nic_name );
+ if (!(tlphy_par & TLAN_PHY_AN_EN_STAT)) {
+ DBG ( "forced 10%sMbps %s-Duplex\n",
+ tlphy_par & TLAN_PHY_SPEED_100 ? ""
+ : "0",
+ tlphy_par & TLAN_PHY_DUPLEX_FULL ?
+ "Full" : "Half" );
+ } else {
+ DBG
+ ( "AutoNegotiation enabled, at 10%sMbps %s-Duplex\n",
+ tlphy_par & TLAN_PHY_SPEED_100 ? "" :
+ "0",
+ tlphy_par & TLAN_PHY_DUPLEX_FULL ?
+ "Full" : "Half" );
+ DBG ( "TLAN: Partner capability: " );
+ for (i = 5; i <= 10; i++)
+ if (partner & (1 << i)) {
+ DBG ( "%s", media[i - 5] );
+ }
+ DBG ( "\n" );
+ }
+
+ TLan_DioWrite8(BASE, TLAN_LED_REG, TLAN_LED_LINK);
+#ifdef MONITOR
+ /* We have link beat..for now anyway */
+ priv->link = 1;
+ /*Enabling link beat monitoring */
+ /* TLan_SetTimer( nic, (10*HZ), TLAN_TIMER_LINK_BEAT ); */
+ mdelay(10000);
+ TLan_PhyMonitor(nic);
+#endif
+ } else if (status & MII_GS_LINK) {
+ DBG ( "TLAN: %s: Link active\n", priv->nic_name );
+ TLan_DioWrite8(BASE, TLAN_LED_REG, TLAN_LED_LINK);
+ }
+ }
+
+ if (priv->phyNum == 0) {
+ TLan_MiiReadReg(nic, phy, TLAN_TLPHY_CTL, &tlphy_ctl);
+ tlphy_ctl |= TLAN_TC_INTEN;
+ TLan_MiiWriteReg(nic, phy, TLAN_TLPHY_CTL, tlphy_ctl);
+ sio = TLan_DioRead8(BASE, TLAN_NET_SIO);
+ sio |= TLAN_NET_SIO_MINTEN;
+ TLan_DioWrite8(BASE, TLAN_NET_SIO, sio);
+ }
+
+ if (status & MII_GS_LINK) {
+ TLan_SetMac(nic, 0, nic->node_addr);
+ priv->phyOnline = 1;
+ outb((TLAN_HC_INT_ON >> 8), BASE + TLAN_HOST_CMD + 1);
+ outl(virt_to_bus(&rx_ring), BASE + TLAN_CH_PARM);
+ outl(TLAN_HC_GO | TLAN_HC_RT, BASE + TLAN_HOST_CMD);
+ } else {
+ DBG
+ ( "TLAN: %s: Link inactive, will retry in 10 secs...\n",
+ priv->nic_name );
+ /* TLan_SetTimer( nic, (10*HZ), TLAN_TIMER_FINISH_RESET ); */
+ mdelay(10000);
+ TLan_FinishReset(nic);
+ return;
+
+ }
+
+} /* TLan_FinishReset */
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int tlan_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+ u32 framesize;
+ u32 host_cmd = 0;
+ u32 ack = 1;
+ int eoc = 0;
+ int entry = priv->cur_rx % TLAN_NUM_RX_LISTS;
+ u16 tmpCStat = le32_to_cpu(rx_ring[entry].cStat);
+ u16 host_int = inw(BASE + TLAN_HOST_INT);
+
+ if ((tmpCStat & TLAN_CSTAT_FRM_CMP) && !retrieve)
+ return 1;
+
+ outw(host_int, BASE + TLAN_HOST_INT);
+
+ if (!(tmpCStat & TLAN_CSTAT_FRM_CMP))
+ return 0;
+
+ /* printf("PI-1: 0x%hX\n", host_int); */
+ if (tmpCStat & TLAN_CSTAT_EOC)
+ eoc = 1;
+
+ framesize = rx_ring[entry].frameSize;
+
+ nic->packetlen = framesize;
+
+ DBG ( ".%d.", (unsigned int) framesize );
+
+ memcpy(nic->packet, rxb +
+ (priv->cur_rx * TLAN_MAX_FRAME_SIZE), nic->packetlen);
+
+ rx_ring[entry].cStat = 0;
+
+ DBG ( "%d", entry );
+
+ entry = (entry + 1) % TLAN_NUM_RX_LISTS;
+ priv->cur_rx = entry;
+ if (eoc) {
+ if ((rx_ring[entry].cStat & TLAN_CSTAT_READY) ==
+ TLAN_CSTAT_READY) {
+ ack |= TLAN_HC_GO | TLAN_HC_RT;
+ host_cmd = TLAN_HC_ACK | ack | 0x001C0000;
+ outl(host_cmd, BASE + TLAN_HOST_CMD);
+ }
+ } else {
+ host_cmd = TLAN_HC_ACK | ack | (0x000C0000);
+ outl(host_cmd, BASE + TLAN_HOST_CMD);
+
+ DBG ( "AC: 0x%hX\n", inw(BASE + TLAN_CH_PARM) );
+ DBG ( "PI-2: 0x%hX\n", inw(BASE + TLAN_HOST_INT) );
+ }
+ refill_rx(nic);
+ return (1); /* initially as this is called to flush the input */
+}
+
+static void refill_rx(struct nic *nic __unused)
+{
+ int entry = 0;
+
+ for (;
+ (priv->cur_rx - priv->dirty_rx +
+ TLAN_NUM_RX_LISTS) % TLAN_NUM_RX_LISTS > 0;
+ priv->dirty_rx = (priv->dirty_rx + 1) % TLAN_NUM_RX_LISTS) {
+ entry = priv->dirty_rx % TLAN_NUM_TX_LISTS;
+ rx_ring[entry].frameSize = TLAN_MAX_FRAME_SIZE;
+ rx_ring[entry].cStat = TLAN_CSTAT_READY;
+ }
+
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void tlan_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ u16 nstype;
+ u32 to;
+ struct TLanList *tail_list;
+ struct TLanList *head_list;
+ u8 *tail_buffer;
+ u32 ack = 0;
+ u32 host_cmd;
+ int eoc = 0;
+ u16 tmpCStat;
+ u16 host_int = inw(BASE + TLAN_HOST_INT);
+
+ int entry = 0;
+
+ DBG ( "INT0-0x%hX\n", host_int );
+
+ if (!priv->phyOnline) {
+ printf("TRANSMIT: %s PHY is not ready\n", priv->nic_name);
+ return;
+ }
+
+ tail_list = priv->txList + priv->txTail;
+
+ if (tail_list->cStat != TLAN_CSTAT_UNUSED) {
+ printf("TRANSMIT: %s is busy (Head=%p Tail=%x)\n",
+ priv->nic_name, priv->txList, (unsigned int) priv->txTail);
+ tx_ring[entry].cStat = TLAN_CSTAT_UNUSED;
+// priv->txBusyCount++;
+ return;
+ }
+
+ tail_list->forward = 0;
+
+ tail_buffer = txb + (priv->txTail * TLAN_MAX_FRAME_SIZE);
+
+ /* send the packet to destination */
+ memcpy(tail_buffer, d, ETH_ALEN);
+ memcpy(tail_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) t);
+ memcpy(tail_buffer + 2 * ETH_ALEN, (u8 *) & nstype, 2);
+ memcpy(tail_buffer + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ s &= 0x0FFF;
+ while (s < ETH_ZLEN)
+ tail_buffer[s++] = '\0';
+
+ /*=====================================================*/
+ /* Receive
+ * 0000 0000 0001 1100
+ * 0000 0000 0000 1100
+ * 0000 0000 0000 0011 = 0x0003
+ *
+ * 0000 0000 0000 0000 0000 0000 0000 0011
+ * 0000 0000 0000 1100 0000 0000 0000 0000 = 0x000C0000
+ *
+ * Transmit
+ * 0000 0000 0001 1100
+ * 0000 0000 0000 0100
+ * 0000 0000 0000 0001 = 0x0001
+ *
+ * 0000 0000 0000 0000 0000 0000 0000 0001
+ * 0000 0000 0000 0100 0000 0000 0000 0000 = 0x00040000
+ * */
+
+ /* Setup the transmit descriptor */
+ tail_list->frameSize = (u16) s;
+ tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) s;
+ tail_list->buffer[1].count = 0;
+ tail_list->buffer[1].address = 0;
+
+ tail_list->cStat = TLAN_CSTAT_READY;
+
+ DBG ( "INT1-0x%hX\n", inw(BASE + TLAN_HOST_INT) );
+
+ if (!priv->txInProgress) {
+ priv->txInProgress = 1;
+ outl(virt_to_le32desc(tail_list), BASE + TLAN_CH_PARM);
+ outl(TLAN_HC_GO, BASE + TLAN_HOST_CMD);
+ } else {
+ if (priv->txTail == 0) {
+ DBG ( "Out buffer\n" );
+ (priv->txList + (TLAN_NUM_TX_LISTS - 1))->forward =
+ virt_to_le32desc(tail_list);
+ } else {
+ DBG ( "Fix this \n" );
+ (priv->txList + (priv->txTail - 1))->forward =
+ virt_to_le32desc(tail_list);
+ }
+ }
+
+ CIRC_INC(priv->txTail, TLAN_NUM_TX_LISTS);
+
+ DBG ( "INT2-0x%hX\n", inw(BASE + TLAN_HOST_INT) );
+
+ to = currticks() + TX_TIME_OUT;
+ while ((tail_list->cStat == TLAN_CSTAT_READY) && currticks() < to);
+
+ head_list = priv->txList + priv->txHead;
+ while (((tmpCStat = head_list->cStat) & TLAN_CSTAT_FRM_CMP)
+ && (ack < 255)) {
+ ack++;
+ if(tmpCStat & TLAN_CSTAT_EOC)
+ eoc =1;
+ head_list->cStat = TLAN_CSTAT_UNUSED;
+ CIRC_INC(priv->txHead, TLAN_NUM_TX_LISTS);
+ head_list = priv->txList + priv->txHead;
+
+ }
+ if(!ack)
+ printf("Incomplete TX Frame\n");
+
+ if(eoc) {
+ head_list = priv->txList + priv->txHead;
+ if ((head_list->cStat & TLAN_CSTAT_READY) == TLAN_CSTAT_READY) {
+ outl(virt_to_le32desc(head_list), BASE + TLAN_CH_PARM);
+ ack |= TLAN_HC_GO;
+ } else {
+ priv->txInProgress = 0;
+ }
+ }
+ if(ack) {
+ host_cmd = TLAN_HC_ACK | ack;
+ outl(host_cmd, BASE + TLAN_HOST_CMD);
+ }
+
+ if(priv->tlanRev < 0x30 ) {
+ ack = 1;
+ head_list = priv->txList + priv->txHead;
+ if ((head_list->cStat & TLAN_CSTAT_READY) == TLAN_CSTAT_READY) {
+ outl(virt_to_le32desc(head_list), BASE + TLAN_CH_PARM);
+ ack |= TLAN_HC_GO;
+ } else {
+ priv->txInProgress = 0;
+ }
+ host_cmd = TLAN_HC_ACK | ack | 0x00140000;
+ outl(host_cmd, BASE + TLAN_HOST_CMD);
+
+ }
+
+ if (currticks() >= to) {
+ printf("TX Time Out");
+ }
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void tlan_disable ( struct nic *nic __unused ) {
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ *
+ */
+ outl(TLAN_HC_AD_RST, BASE + TLAN_HOST_CMD);
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void tlan_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct nic_operations tlan_operations = {
+ .connect = dummy_connect,
+ .poll = tlan_poll,
+ .transmit = tlan_transmit,
+ .irq = tlan_irq,
+
+};
+
+static void TLan_SetMulticastList(struct nic *nic) {
+ int i;
+ u8 tmp;
+
+ /* !IFF_PROMISC */
+ tmp = TLan_DioRead8(BASE, TLAN_NET_CMD);
+ TLan_DioWrite8(BASE, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF);
+
+ /* IFF_ALLMULTI */
+ for(i = 0; i< 3; i++)
+ TLan_SetMac(nic, i + 1, NULL);
+ TLan_DioWrite32(BASE, TLAN_HASH_1, 0xFFFFFFFF);
+ TLan_DioWrite32(BASE, TLAN_HASH_2, 0xFFFFFFFF);
+
+
+}
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+#define board_found 1
+#define valid_link 0
+static int tlan_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ u16 data = 0;
+ int err;
+ int i;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr;
+
+ BASE = pci->ioaddr;
+
+ /* Set nic as PCI bus master */
+ adjust_pci_device(pci);
+
+ /* Point to private storage */
+ priv = &TLanPrivateInfo;
+
+ /* Figure out which chip we're dealing with */
+ i = 0;
+ chip_idx = -1;
+ while (tlan_pci_tbl[i].name) {
+ if ((((u32) pci->device << 16) | pci->vendor) ==
+ (tlan_pci_tbl[i].id.pci & 0xffffffff)) {
+ chip_idx = i;
+ break;
+ }
+ i++;
+ }
+
+ priv->vendor_id = pci->vendor;
+ priv->dev_id = pci->device;
+ priv->nic_name = pci->driver_name;
+ priv->eoc = 0;
+
+ err = 0;
+ for (i = 0; i < 6; i++)
+ err |= TLan_EeReadByte(BASE,
+ (u8) tlan_pci_tbl[chip_idx].
+ addrOfs + i,
+ (u8 *) & nic->node_addr[i]);
+ if (err) {
+ printf ( "TLAN: %s: Error reading MAC from eeprom: %d\n",
+ pci->driver_name, err);
+ } else {
+ DBG ( "%s: %s at ioaddr %#lX, ",
+ pci->driver_name, eth_ntoa ( nic->node_addr ), pci->ioaddr );
+ }
+
+ priv->tlanRev = TLan_DioRead8(BASE, TLAN_DEF_REVISION);
+ printf("revision: 0x%hX\n", priv->tlanRev);
+
+ TLan_ResetLists(nic);
+ TLan_ResetAdapter(nic);
+
+ data = inl(BASE + TLAN_HOST_CMD);
+ data |= TLAN_HC_INT_OFF;
+ outw(data, BASE + TLAN_HOST_CMD);
+
+ TLan_SetMulticastList(nic);
+ udelay(100);
+ priv->txList = tx_ring;
+
+/* if (board_found && valid_link)
+ {*/
+ /* point to NIC specific routines */
+ nic->nic_op = &tlan_operations;
+ return 1;
+}
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Eeprom routines
+
+ The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A
+ EEPROM. These functions are based on information in Microchip's
+ data sheet. I don't know how well this functions will work with
+ other EEPROMs.
+
+******************************************************************************
+*****************************************************************************/
+
+
+/***************************************************************
+* TLan_EeSendStart
+*
+* Returns:
+* Nothing
+* Parms:
+* io_base The IO port base address for the
+* TLAN device with the EEPROM to
+* use.
+*
+* This function sends a start cycle to an EEPROM attached
+* to a TLAN chip.
+*
+**************************************************************/
+
+void TLan_EeSendStart(u16 io_base)
+{
+ u16 sio;
+
+ outw(TLAN_NET_SIO, io_base + TLAN_DIO_ADR);
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_SetBit(TLAN_NET_SIO_EDATA, sio);
+ TLan_SetBit(TLAN_NET_SIO_ETXEN, sio);
+ TLan_ClearBit(TLAN_NET_SIO_EDATA, sio);
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+
+} /* TLan_EeSendStart */
+
+/***************************************************************
+* TLan_EeSendByte
+*
+* Returns:
+* If the correct ack was received, 0, otherwise 1
+* Parms: io_base The IO port base address for the
+* TLAN device with the EEPROM to
+* use.
+* data The 8 bits of information to
+* send to the EEPROM.
+* stop If TLAN_EEPROM_STOP is passed, a
+* stop cycle is sent after the
+* byte is sent after the ack is
+* read.
+*
+* This function sends a byte on the serial EEPROM line,
+* driving the clock to send each bit. The function then
+* reverses transmission direction and reads an acknowledge
+* bit.
+*
+**************************************************************/
+
+int TLan_EeSendByte(u16 io_base, u8 data, int stop)
+{
+ int err;
+ u8 place;
+ u16 sio;
+
+ outw(TLAN_NET_SIO, io_base + TLAN_DIO_ADR);
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ /* Assume clock is low, tx is enabled; */
+ for (place = 0x80; place != 0; place >>= 1) {
+ if (place & data)
+ TLan_SetBit(TLAN_NET_SIO_EDATA, sio);
+ else
+ TLan_ClearBit(TLAN_NET_SIO_EDATA, sio);
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+ }
+ TLan_ClearBit(TLAN_NET_SIO_ETXEN, sio);
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ err = TLan_GetBit(TLAN_NET_SIO_EDATA, sio);
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_SetBit(TLAN_NET_SIO_ETXEN, sio);
+
+ if ((!err) && stop) {
+ TLan_ClearBit(TLAN_NET_SIO_EDATA, sio); /* STOP, raise data while clock is high */
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_SetBit(TLAN_NET_SIO_EDATA, sio);
+ }
+
+ return (err);
+
+} /* TLan_EeSendByte */
+
+/***************************************************************
+* TLan_EeReceiveByte
+*
+* Returns:
+* Nothing
+* Parms:
+* io_base The IO port base address for the
+* TLAN device with the EEPROM to
+* use.
+* data An address to a char to hold the
+* data sent from the EEPROM.
+* stop If TLAN_EEPROM_STOP is passed, a
+* stop cycle is sent after the
+* byte is received, and no ack is
+* sent.
+*
+* This function receives 8 bits of data from the EEPROM
+* over the serial link. It then sends and ack bit, or no
+* ack and a stop bit. This function is used to retrieve
+* data after the address of a byte in the EEPROM has been
+* sent.
+*
+**************************************************************/
+
+void TLan_EeReceiveByte(u16 io_base, u8 * data, int stop)
+{
+ u8 place;
+ u16 sio;
+
+ outw(TLAN_NET_SIO, io_base + TLAN_DIO_ADR);
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+ *data = 0;
+
+ /* Assume clock is low, tx is enabled; */
+ TLan_ClearBit(TLAN_NET_SIO_ETXEN, sio);
+ for (place = 0x80; place; place >>= 1) {
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ if (TLan_GetBit(TLAN_NET_SIO_EDATA, sio))
+ *data |= place;
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+ }
+
+ TLan_SetBit(TLAN_NET_SIO_ETXEN, sio);
+ if (!stop) {
+ TLan_ClearBit(TLAN_NET_SIO_EDATA, sio); /* Ack = 0 */
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+ } else {
+ TLan_SetBit(TLAN_NET_SIO_EDATA, sio); /* No ack = 1 (?) */
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_EDATA, sio); /* STOP, raise data while clock is high */
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_SetBit(TLAN_NET_SIO_EDATA, sio);
+ }
+
+} /* TLan_EeReceiveByte */
+
+/***************************************************************
+* TLan_EeReadByte
+*
+* Returns:
+* No error = 0, else, the stage at which the error
+* occurred.
+* Parms:
+* io_base The IO port base address for the
+* TLAN device with the EEPROM to
+* use.
+* ee_addr The address of the byte in the
+* EEPROM whose contents are to be
+* retrieved.
+* data An address to a char to hold the
+* data obtained from the EEPROM.
+*
+* This function reads a byte of information from an byte
+* cell in the EEPROM.
+*
+**************************************************************/
+
+int TLan_EeReadByte(u16 io_base, u8 ee_addr, u8 * data)
+{
+ int err;
+ int ret = 0;
+
+
+ TLan_EeSendStart(io_base);
+ err = TLan_EeSendByte(io_base, 0xA0, TLAN_EEPROM_ACK);
+ if (err) {
+ ret = 1;
+ goto fail;
+ }
+ err = TLan_EeSendByte(io_base, ee_addr, TLAN_EEPROM_ACK);
+ if (err) {
+ ret = 2;
+ goto fail;
+ }
+ TLan_EeSendStart(io_base);
+ err = TLan_EeSendByte(io_base, 0xA1, TLAN_EEPROM_ACK);
+ if (err) {
+ ret = 3;
+ goto fail;
+ }
+ TLan_EeReceiveByte(io_base, data, TLAN_EEPROM_STOP);
+ fail:
+
+ return ret;
+
+} /* TLan_EeReadByte */
+
+
+/*****************************************************************************
+******************************************************************************
+
+ThunderLAN Driver MII Routines
+
+These routines are based on the information in Chap. 2 of the
+"ThunderLAN Programmer's Guide", pp. 15-24.
+
+******************************************************************************
+*****************************************************************************/
+
+
+/***************************************************************
+* TLan_MiiReadReg
+*
+* Returns:
+* 0 if ack received ok
+* 1 otherwise.
+*
+* Parms:
+* dev The device structure containing
+* The io address and interrupt count
+* for this device.
+* phy The address of the PHY to be queried.
+* reg The register whose contents are to be
+* retreived.
+* val A pointer to a variable to store the
+* retrieved value.
+*
+* This function uses the TLAN's MII bus to retreive the contents
+* of a given register on a PHY. It sends the appropriate info
+* and then reads the 16-bit register value from the MII bus via
+* the TLAN SIO register.
+*
+**************************************************************/
+
+int TLan_MiiReadReg(struct nic *nic __unused, u16 phy, u16 reg, u16 * val)
+{
+ u8 nack;
+ u16 sio, tmp;
+ u32 i;
+ int err;
+ int minten;
+
+ err = FALSE;
+ outw(TLAN_NET_SIO, BASE + TLAN_DIO_ADR);
+ sio = BASE + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_MiiSync(BASE);
+
+ minten = TLan_GetBit(TLAN_NET_SIO_MINTEN, sio);
+ if (minten)
+ TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
+
+ TLan_MiiSendData(BASE, 0x1, 2); /* Start ( 01b ) */
+ TLan_MiiSendData(BASE, 0x2, 2); /* Read ( 10b ) */
+ TLan_MiiSendData(BASE, phy, 5); /* Device # */
+ TLan_MiiSendData(BASE, reg, 5); /* Register # */
+
+
+ TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock Idle bit */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Wait 300ns */
+
+ nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK */
+ if (nack) { /* No ACK, so fake it */
+ for (i = 0; i < 16; i++) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ tmp = 0xffff;
+ err = TRUE;
+ } else { /* ACK, so read data */
+ for (tmp = 0, i = 0x8000; i; i >>= 1) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio))
+ tmp |= i;
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ }
+
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+
+ if (minten)
+ TLan_SetBit(TLAN_NET_SIO_MINTEN, sio);
+
+ *val = tmp;
+
+ return err;
+
+} /* TLan_MiiReadReg */
+
+/***************************************************************
+* TLan_MiiSendData
+*
+* Returns:
+* Nothing
+* Parms:
+* base_port The base IO port of the adapter in
+* question.
+* dev The address of the PHY to be queried.
+* data The value to be placed on the MII bus.
+* num_bits The number of bits in data that are to
+* be placed on the MII bus.
+*
+* This function sends on sequence of bits on the MII
+* configuration bus.
+*
+**************************************************************/
+
+void TLan_MiiSendData(u16 base_port, u32 data, unsigned num_bits)
+{
+ u16 sio;
+ u32 i;
+
+ if (num_bits == 0)
+ return;
+
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+ TLan_SetBit(TLAN_NET_SIO_MTXEN, sio);
+
+ for (i = (0x1 << (num_bits - 1)); i; i >>= 1) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ (void) TLan_GetBit(TLAN_NET_SIO_MCLK, sio);
+ if (data & i)
+ TLan_SetBit(TLAN_NET_SIO_MDATA, sio);
+ else
+ TLan_ClearBit(TLAN_NET_SIO_MDATA, sio);
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ (void) TLan_GetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+
+} /* TLan_MiiSendData */
+
+/***************************************************************
+* TLan_MiiSync
+*
+* Returns:
+* Nothing
+* Parms:
+* base_port The base IO port of the adapter in
+* question.
+*
+* This functions syncs all PHYs in terms of the MII configuration
+* bus.
+*
+**************************************************************/
+
+void TLan_MiiSync(u16 base_port)
+{
+ int i;
+ u16 sio;
+
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio);
+ for (i = 0; i < 32; i++) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+
+} /* TLan_MiiSync */
+
+/***************************************************************
+* TLan_MiiWriteReg
+*
+* Returns:
+* Nothing
+* Parms:
+* dev The device structure for the device
+* to write to.
+* phy The address of the PHY to be written to.
+* reg The register whose contents are to be
+* written.
+* val The value to be written to the register.
+*
+* This function uses the TLAN's MII bus to write the contents of a
+* given register on a PHY. It sends the appropriate info and then
+* writes the 16-bit register value from the MII configuration bus
+* via the TLAN SIO register.
+*
+**************************************************************/
+
+void TLan_MiiWriteReg(struct nic *nic __unused, u16 phy, u16 reg, u16 val)
+{
+ u16 sio;
+ int minten;
+
+ outw(TLAN_NET_SIO, BASE + TLAN_DIO_ADR);
+ sio = BASE + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_MiiSync(BASE);
+
+ minten = TLan_GetBit(TLAN_NET_SIO_MINTEN, sio);
+ if (minten)
+ TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
+
+ TLan_MiiSendData(BASE, 0x1, 2); /* Start ( 01b ) */
+ TLan_MiiSendData(BASE, 0x1, 2); /* Write ( 01b ) */
+ TLan_MiiSendData(BASE, phy, 5); /* Device # */
+ TLan_MiiSendData(BASE, reg, 5); /* Register # */
+
+ TLan_MiiSendData(BASE, 0x2, 2); /* Send ACK */
+ TLan_MiiSendData(BASE, val, 16); /* Send Data */
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+
+ if (minten)
+ TLan_SetBit(TLAN_NET_SIO_MINTEN, sio);
+
+
+} /* TLan_MiiWriteReg */
+
+/***************************************************************
+* TLan_SetMac
+*
+* Returns:
+* Nothing
+* Parms:
+* dev Pointer to device structure of adapter
+* on which to change the AREG.
+* areg The AREG to set the address in (0 - 3).
+* mac A pointer to an array of chars. Each
+* element stores one byte of the address.
+* IE, it isn't in ascii.
+*
+* This function transfers a MAC address to one of the
+* TLAN AREGs (address registers). The TLAN chip locks
+* the register on writing to offset 0 and unlocks the
+* register after writing to offset 5. If NULL is passed
+* in mac, then the AREG is filled with 0's.
+*
+**************************************************************/
+
+void TLan_SetMac(struct nic *nic __unused, int areg, unsigned char *mac)
+{
+ int i;
+
+ areg *= 6;
+
+ if (mac != NULL) {
+ for (i = 0; i < 6; i++)
+ TLan_DioWrite8(BASE, TLAN_AREG_0 + areg + i,
+ mac[i]);
+ } else {
+ for (i = 0; i < 6; i++)
+ TLan_DioWrite8(BASE, TLAN_AREG_0 + areg + i, 0);
+ }
+
+} /* TLan_SetMac */
+
+/*********************************************************************
+* TLan_PhyDetect
+*
+* Returns:
+* Nothing
+* Parms:
+* dev A pointer to the device structure of the adapter
+* for which the PHY needs determined.
+*
+* So far I've found that adapters which have external PHYs
+* may also use the internal PHY for part of the functionality.
+* (eg, AUI/Thinnet). This function finds out if this TLAN
+* chip has an internal PHY, and then finds the first external
+* PHY (starting from address 0) if it exists).
+*
+********************************************************************/
+
+void TLan_PhyDetect(struct nic *nic)
+{
+ u16 control;
+ u16 hi;
+ u16 lo;
+ u32 phy;
+
+ if (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_UNMANAGED_PHY) {
+ priv->phyNum = 0xFFFF;
+ return;
+ }
+
+ TLan_MiiReadReg(nic, TLAN_PHY_MAX_ADDR, MII_GEN_ID_HI, &hi);
+
+ if (hi != 0xFFFF) {
+ priv->phy[0] = TLAN_PHY_MAX_ADDR;
+ } else {
+ priv->phy[0] = TLAN_PHY_NONE;
+ }
+
+ priv->phy[1] = TLAN_PHY_NONE;
+ for (phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++) {
+ TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &control);
+ TLan_MiiReadReg(nic, phy, MII_GEN_ID_HI, &hi);
+ TLan_MiiReadReg(nic, phy, MII_GEN_ID_LO, &lo);
+ if ((control != 0xFFFF) || (hi != 0xFFFF)
+ || (lo != 0xFFFF)) {
+ printf("PHY found at %hX %hX %hX %hX\n",
+ (unsigned int) phy, control, hi, lo);
+ if ((priv->phy[1] == TLAN_PHY_NONE)
+ && (phy != TLAN_PHY_MAX_ADDR)) {
+ priv->phy[1] = phy;
+ }
+ }
+ }
+
+ if (priv->phy[1] != TLAN_PHY_NONE) {
+ priv->phyNum = 1;
+ } else if (priv->phy[0] != TLAN_PHY_NONE) {
+ priv->phyNum = 0;
+ } else {
+ printf
+ ("TLAN: Cannot initialize device, no PHY was found!\n");
+ }
+
+} /* TLan_PhyDetect */
+
+void TLan_PhyPowerDown(struct nic *nic)
+{
+
+ u16 value;
+ DBG ( "%s: Powering down PHY(s).\n", priv->nic_name );
+ value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE;
+ TLan_MiiSync(BASE);
+ TLan_MiiWriteReg(nic, priv->phy[priv->phyNum], MII_GEN_CTL, value);
+ if ((priv->phyNum == 0) && (priv->phy[1] != TLAN_PHY_NONE)
+ &&
+ (!(tlan_pci_tbl[chip_idx].
+ flags & TLAN_ADAPTER_USE_INTERN_10))) {
+ TLan_MiiSync(BASE);
+ TLan_MiiWriteReg(nic, priv->phy[1], MII_GEN_CTL, value);
+ }
+
+ /* Wait for 50 ms and powerup
+ * This is abitrary. It is intended to make sure the
+ * tranceiver settles.
+ */
+ /* TLan_SetTimer( dev, (HZ/20), TLAN_TIMER_PHY_PUP ); */
+ mdelay(50);
+ TLan_PhyPowerUp(nic);
+
+} /* TLan_PhyPowerDown */
+
+
+void TLan_PhyPowerUp(struct nic *nic)
+{
+ u16 value;
+
+ DBG ( "%s: Powering up PHY.\n", priv->nic_name );
+ TLan_MiiSync(BASE);
+ value = MII_GC_LOOPBK;
+ TLan_MiiWriteReg(nic, priv->phy[priv->phyNum], MII_GEN_CTL, value);
+ TLan_MiiSync(BASE);
+ /* Wait for 500 ms and reset the
+ * tranceiver. The TLAN docs say both 50 ms and
+ * 500 ms, so do the longer, just in case.
+ */
+ mdelay(500);
+ TLan_PhyReset(nic);
+ /* TLan_SetTimer( dev, (HZ/20), TLAN_TIMER_PHY_RESET ); */
+
+} /* TLan_PhyPowerUp */
+
+void TLan_PhyReset(struct nic *nic)
+{
+ u16 phy;
+ u16 value;
+
+ phy = priv->phy[priv->phyNum];
+
+ DBG ( "%s: Reseting PHY.\n", priv->nic_name );
+ TLan_MiiSync(BASE);
+ value = MII_GC_LOOPBK | MII_GC_RESET;
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, value);
+ TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &value);
+ while (value & MII_GC_RESET) {
+ TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &value);
+ }
+
+ /* Wait for 500 ms and initialize.
+ * I don't remember why I wait this long.
+ * I've changed this to 50ms, as it seems long enough.
+ */
+ /* TLan_SetTimer( dev, (HZ/20), TLAN_TIMER_PHY_START_LINK ); */
+ mdelay(50);
+ TLan_PhyStartLink(nic);
+
+} /* TLan_PhyReset */
+
+
+void TLan_PhyStartLink(struct nic *nic)
+{
+
+ u16 ability;
+ u16 control;
+ u16 data;
+ u16 phy;
+ u16 status;
+ u16 tctl;
+
+ phy = priv->phy[priv->phyNum];
+ DBG ( "%s: Trying to activate link.\n", priv->nic_name );
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &ability);
+
+ if ((status & MII_GS_AUTONEG) && (!priv->aui)) {
+ ability = status >> 11;
+ if (priv->speed == TLAN_SPEED_10 &&
+ priv->duplex == TLAN_DUPLEX_HALF) {
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x0000);
+ } else if (priv->speed == TLAN_SPEED_10 &&
+ priv->duplex == TLAN_DUPLEX_FULL) {
+ priv->tlanFullDuplex = TRUE;
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x0100);
+ } else if (priv->speed == TLAN_SPEED_100 &&
+ priv->duplex == TLAN_DUPLEX_HALF) {
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x2000);
+ } else if (priv->speed == TLAN_SPEED_100 &&
+ priv->duplex == TLAN_DUPLEX_FULL) {
+ priv->tlanFullDuplex = TRUE;
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x2100);
+ } else {
+
+ /* Set Auto-Neg advertisement */
+ TLan_MiiWriteReg(nic, phy, MII_AN_ADV,
+ (ability << 5) | 1);
+ /* Enablee Auto-Neg */
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x1000);
+ /* Restart Auto-Neg */
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x1200);
+ /* Wait for 4 sec for autonegotiation
+ * to complete. The max spec time is less than this
+ * but the card need additional time to start AN.
+ * .5 sec should be plenty extra.
+ */
+ DBG ( "TLAN: %s: Starting autonegotiation.\n",
+ priv->nic_name );
+ mdelay(4000);
+ TLan_PhyFinishAutoNeg(nic);
+ /* TLan_SetTimer( dev, (2*HZ), TLAN_TIMER_PHY_FINISH_AN ); */
+ return;
+ }
+
+ }
+
+ if ((priv->aui) && (priv->phyNum != 0)) {
+ priv->phyNum = 0;
+ data =
+ TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN |
+ TLAN_NET_CFG_PHY_EN;
+ TLan_DioWrite16(BASE, TLAN_NET_CONFIG, data);
+ mdelay(50);
+ /* TLan_SetTimer( dev, (40*HZ/1000), TLAN_TIMER_PHY_PDOWN ); */
+ TLan_PhyPowerDown(nic);
+ return;
+ } else if (priv->phyNum == 0) {
+ control = 0;
+ TLan_MiiReadReg(nic, phy, TLAN_TLPHY_CTL, &tctl);
+ if (priv->aui) {
+ tctl |= TLAN_TC_AUISEL;
+ } else {
+ tctl &= ~TLAN_TC_AUISEL;
+ if (priv->duplex == TLAN_DUPLEX_FULL) {
+ control |= MII_GC_DUPLEX;
+ priv->tlanFullDuplex = TRUE;
+ }
+ if (priv->speed == TLAN_SPEED_100) {
+ control |= MII_GC_SPEEDSEL;
+ }
+ }
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, control);
+ TLan_MiiWriteReg(nic, phy, TLAN_TLPHY_CTL, tctl);
+ }
+
+ /* Wait for 2 sec to give the tranceiver time
+ * to establish link.
+ */
+ /* TLan_SetTimer( dev, (4*HZ), TLAN_TIMER_FINISH_RESET ); */
+ mdelay(2000);
+ TLan_FinishReset(nic);
+
+} /* TLan_PhyStartLink */
+
+void TLan_PhyFinishAutoNeg(struct nic *nic)
+{
+
+ u16 an_adv;
+ u16 an_lpa;
+ u16 data;
+ u16 mode;
+ u16 phy;
+ u16 status;
+
+ phy = priv->phy[priv->phyNum];
+
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+ udelay(1000);
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+
+ if (!(status & MII_GS_AUTOCMPLT)) {
+ /* Wait for 8 sec to give the process
+ * more time. Perhaps we should fail after a while.
+ */
+ if (!priv->neg_be_verbose++) {
+ printf
+ ("TLAN: Giving autonegotiation more time.\n");
+ printf
+ ("TLAN: Please check that your adapter has\n");
+ printf
+ ("TLAN: been properly connected to a HUB or Switch.\n");
+ printf
+ ("TLAN: Trying to establish link in the background...\n");
+ }
+ mdelay(8000);
+ TLan_PhyFinishAutoNeg(nic);
+ /* TLan_SetTimer( dev, (8*HZ), TLAN_TIMER_PHY_FINISH_AN ); */
+ return;
+ }
+
+ DBG ( "TLAN: %s: Autonegotiation complete.\n", priv->nic_name );
+ TLan_MiiReadReg(nic, phy, MII_AN_ADV, &an_adv);
+ TLan_MiiReadReg(nic, phy, MII_AN_LPA, &an_lpa);
+ mode = an_adv & an_lpa & 0x03E0;
+ if (mode & 0x0100) {
+ printf("Full Duplex\n");
+ priv->tlanFullDuplex = TRUE;
+ } else if (!(mode & 0x0080) && (mode & 0x0040)) {
+ priv->tlanFullDuplex = TRUE;
+ printf("Full Duplex\n");
+ }
+
+ if ((!(mode & 0x0180))
+ && (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_USE_INTERN_10)
+ && (priv->phyNum != 0)) {
+ priv->phyNum = 0;
+ data =
+ TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN |
+ TLAN_NET_CFG_PHY_EN;
+ TLan_DioWrite16(BASE, TLAN_NET_CONFIG, data);
+ /* TLan_SetTimer( nic, (400*HZ/1000), TLAN_TIMER_PHY_PDOWN ); */
+ mdelay(400);
+ TLan_PhyPowerDown(nic);
+ return;
+ }
+
+ if (priv->phyNum == 0) {
+ if ((priv->duplex == TLAN_DUPLEX_FULL)
+ || (an_adv & an_lpa & 0x0040)) {
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL,
+ MII_GC_AUTOENB | MII_GC_DUPLEX);
+ DBG
+ ( "TLAN: Starting internal PHY with FULL-DUPLEX\n" );
+ } else {
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL,
+ MII_GC_AUTOENB);
+ DBG
+ ( "TLAN: Starting internal PHY with HALF-DUPLEX\n" );
+ }
+ }
+
+ /* Wait for 100 ms. No reason in partiticular.
+ */
+ /* TLan_SetTimer( dev, (HZ/10), TLAN_TIMER_FINISH_RESET ); */
+ mdelay(100);
+ TLan_FinishReset(nic);
+
+} /* TLan_PhyFinishAutoNeg */
+
+#ifdef MONITOR
+
+/*********************************************************************
+*
+* TLan_phyMonitor
+*
+* Returns:
+* None
+*
+* Params:
+* dev The device structure of this device.
+*
+*
+* This function monitors PHY condition by reading the status
+* register via the MII bus. This can be used to give info
+* about link changes (up/down), and possible switch to alternate
+* media.
+*
+********************************************************************/
+
+void TLan_PhyMonitor(struct net_device *dev)
+{
+ TLanPrivateInfo *priv = dev->priv;
+ u16 phy;
+ u16 phy_status;
+
+ phy = priv->phy[priv->phyNum];
+
+ /* Get PHY status register */
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &phy_status);
+
+ /* Check if link has been lost */
+ if (!(phy_status & MII_GS_LINK)) {
+ if (priv->link) {
+ priv->link = 0;
+ printf("TLAN: %s has lost link\n", priv->nic_name);
+ priv->flags &= ~IFF_RUNNING;
+ mdelay(2000);
+ TLan_PhyMonitor(nic);
+ /* TLan_SetTimer( dev, (2*HZ), TLAN_TIMER_LINK_BEAT ); */
+ return;
+ }
+ }
+
+ /* Link restablished? */
+ if ((phy_status & MII_GS_LINK) && !priv->link) {
+ priv->link = 1;
+ printf("TLAN: %s has reestablished link\n",
+ priv->nic_name);
+ priv->flags |= IFF_RUNNING;
+ }
+
+ /* Setup a new monitor */
+ /* TLan_SetTimer( dev, (2*HZ), TLAN_TIMER_LINK_BEAT ); */
+ mdelay(2000);
+ TLan_PhyMonitor(nic);
+}
+
+#endif /* MONITOR */
+
+static struct pci_device_id tlan_nics[] = {
+ PCI_ROM(0x0e11, 0xae34, "netel10", "Compaq Netelligent 10 T PCI UTP"),
+ PCI_ROM(0x0e11, 0xae32, "netel100","Compaq Netelligent 10/100 TX PCI UTP"),
+ PCI_ROM(0x0e11, 0xae35, "netflex3i", "Compaq Integrated NetFlex-3/P"),
+ PCI_ROM(0x0e11, 0xf130, "thunder", "Compaq NetFlex-3/P"),
+ PCI_ROM(0x0e11, 0xf150, "netflex3b", "Compaq NetFlex-3/P"),
+ PCI_ROM(0x0e11, 0xae43, "netel100pi", "Compaq Netelligent Integrated 10/100 TX UTP"),
+ PCI_ROM(0x0e11, 0xae40, "netel100d", "Compaq Netelligent Dual 10/100 TX PCI UTP"),
+ PCI_ROM(0x0e11, 0xb011, "netel100i", "Compaq Netelligent 10/100 TX Embedded UTP"),
+ PCI_ROM(0x108d, 0x0013, "oc2183", "Olicom OC-2183/2185"),
+ PCI_ROM(0x108d, 0x0012, "oc2325", "Olicom OC-2325"),
+ PCI_ROM(0x108d, 0x0014, "oc2326", "Olicom OC-2326"),
+ PCI_ROM(0x0e11, 0xb030, "netelligent_10_100_ws_5100", "Compaq Netelligent 10/100 TX UTP"),
+ PCI_ROM(0x0e11, 0xb012, "netelligent_10_t2", "Compaq Netelligent 10 T/2 PCI UTP/Coax"),
+};
+
+PCI_DRIVER ( tlan_driver, tlan_nics, PCI_NO_CLASS );
+
+DRIVER ( "TLAN/PCI", nic_driver, pci_driver, tlan_driver,
+ tlan_probe, tlan_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/tlan.h b/gpxe/src/drivers/net/tlan.h
new file mode 100644
index 00000000..de57d598
--- /dev/null
+++ b/gpxe/src/drivers/net/tlan.h
@@ -0,0 +1,524 @@
+/**************************************************************************
+*
+* tlan.c -- Etherboot device driver for the Texas Instruments ThunderLAN
+* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code (almost all) based on:
+* tlan.c: Linux ThunderLan Driver:
+*
+* by James Banks
+*
+* (C) 1997-1998 Caldera, Inc.
+* (C) 1998 James Banks
+* (C) 1999-2001 Torben Mathiasen
+* (C) 2002 Samuel Chessman
+*
+* REVISION HISTORY:
+* ================
+* v1.0 07-08-2003 timlegge Initial not quite working version
+*
+* Indent Style: indent -kr -i8
+***************************************************************************/
+
+/*****************************************************************
+* TLan Definitions
+*
+****************************************************************/
+
+#define FALSE 0
+#define TRUE 1
+
+#define TLAN_MIN_FRAME_SIZE 64
+#define TLAN_MAX_FRAME_SIZE 1600
+
+#define TLAN_NUM_RX_LISTS 4
+#define TLAN_NUM_TX_LISTS 2
+
+#define TLAN_IGNORE 0
+#define TLAN_RECORD 1
+/*
+#define TLAN_DBG(lvl, format, args...) if (debug&lvl) printf("TLAN: " format, ##args );
+*/
+#define TLAN_DEBUG_GNRL 0x0001
+#define TLAN_DEBUG_TX 0x0002
+#define TLAN_DEBUG_RX 0x0004
+#define TLAN_DEBUG_LIST 0x0008
+#define TLAN_DEBUG_PROBE 0x0010
+
+#define TX_TIMEOUT (10*HZ) /* We need time for auto-neg */
+#define MAX_TLAN_BOARDS 8 /* Max number of boards installed at a time */
+
+
+ /*****************************************************************
+ * Device Identification Definitions
+ *
+ ****************************************************************/
+
+#define PCI_DEVICE_ID_NETELLIGENT_10_T2 0xB012
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100 0xB030
+#ifndef PCI_DEVICE_ID_OLICOM_OC2183
+#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013
+#endif
+#ifndef PCI_DEVICE_ID_OLICOM_OC2325
+#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012
+#endif
+#ifndef PCI_DEVICE_ID_OLICOM_OC2326
+#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014
+#endif
+
+typedef struct tlan_adapter_entry {
+ u16 vendorId;
+ u16 deviceId;
+ char *deviceLabel;
+ u32 flags;
+ u16 addrOfs;
+} TLanAdapterEntry;
+
+#define TLAN_ADAPTER_NONE 0x00000000
+#define TLAN_ADAPTER_UNMANAGED_PHY 0x00000001
+#define TLAN_ADAPTER_BIT_RATE_PHY 0x00000002
+#define TLAN_ADAPTER_USE_INTERN_10 0x00000004
+#define TLAN_ADAPTER_ACTIVITY_LED 0x00000008
+
+#define TLAN_SPEED_DEFAULT 0
+#define TLAN_SPEED_10 10
+#define TLAN_SPEED_100 100
+
+#define TLAN_DUPLEX_DEFAULT 0
+#define TLAN_DUPLEX_HALF 1
+#define TLAN_DUPLEX_FULL 2
+
+
+
+ /*****************************************************************
+ * EISA Definitions
+ *
+ ****************************************************************/
+
+#define EISA_ID 0xc80 /* EISA ID Registers */
+#define EISA_ID0 0xc80 /* EISA ID Register 0 */
+#define EISA_ID1 0xc81 /* EISA ID Register 1 */
+#define EISA_ID2 0xc82 /* EISA ID Register 2 */
+#define EISA_ID3 0xc83 /* EISA ID Register 3 */
+#define EISA_CR 0xc84 /* EISA Control Register */
+#define EISA_REG0 0xc88 /* EISA Configuration Register 0 */
+#define EISA_REG1 0xc89 /* EISA Configuration Register 1 */
+#define EISA_REG2 0xc8a /* EISA Configuration Register 2 */
+#define EISA_REG3 0xc8f /* EISA Configuration Register 3 */
+#define EISA_APROM 0xc90 /* Ethernet Address PROM */
+
+
+
+ /*****************************************************************
+ * Rx/Tx List Definitions
+ *
+ ****************************************************************/
+
+#define TLAN_BUFFERS_PER_LIST 10
+#define TLAN_LAST_BUFFER 0x80000000
+#define TLAN_CSTAT_UNUSED 0x8000
+#define TLAN_CSTAT_FRM_CMP 0x4000
+#define TLAN_CSTAT_READY 0x3000
+#define TLAN_CSTAT_EOC 0x0800
+#define TLAN_CSTAT_RX_ERROR 0x0400
+#define TLAN_CSTAT_PASS_CRC 0x0200
+#define TLAN_CSTAT_DP_PR 0x0100
+
+
+
+
+
+
+ /*****************************************************************
+ * PHY definitions
+ *
+ ****************************************************************/
+
+#define TLAN_PHY_MAX_ADDR 0x1F
+#define TLAN_PHY_NONE 0x20
+
+
+
+ /*****************************************************************
+ * TLan Driver Timer Definitions
+ *
+ ****************************************************************/
+
+#define TLAN_TIMER_LINK_BEAT 1
+#define TLAN_TIMER_ACTIVITY 2
+#define TLAN_TIMER_PHY_PDOWN 3
+#define TLAN_TIMER_PHY_PUP 4
+#define TLAN_TIMER_PHY_RESET 5
+#define TLAN_TIMER_PHY_START_LINK 6
+#define TLAN_TIMER_PHY_FINISH_AN 7
+#define TLAN_TIMER_FINISH_RESET 8
+
+#define TLAN_TIMER_ACT_DELAY (HZ/10)
+
+
+
+
+ /*****************************************************************
+ * TLan Driver Eeprom Definitions
+ *
+ ****************************************************************/
+
+#define TLAN_EEPROM_ACK 0
+#define TLAN_EEPROM_STOP 1
+
+
+
+
+ /*****************************************************************
+ * Host Register Offsets and Contents
+ *
+ ****************************************************************/
+
+#define TLAN_HOST_CMD 0x00
+#define TLAN_HC_GO 0x80000000
+#define TLAN_HC_STOP 0x40000000
+#define TLAN_HC_ACK 0x20000000
+#define TLAN_HC_CS_MASK 0x1FE00000
+#define TLAN_HC_EOC 0x00100000
+#define TLAN_HC_RT 0x00080000
+#define TLAN_HC_NES 0x00040000
+#define TLAN_HC_AD_RST 0x00008000
+#define TLAN_HC_LD_TMR 0x00004000
+#define TLAN_HC_LD_THR 0x00002000
+#define TLAN_HC_REQ_INT 0x00001000
+#define TLAN_HC_INT_OFF 0x00000800
+#define TLAN_HC_INT_ON 0x00000400
+#define TLAN_HC_AC_MASK 0x000000FF
+#define TLAN_CH_PARM 0x04
+#define TLAN_DIO_ADR 0x08
+#define TLAN_DA_ADR_INC 0x8000
+#define TLAN_DA_RAM_ADR 0x4000
+#define TLAN_HOST_INT 0x0A
+#define TLAN_HI_IV_MASK 0x1FE0
+#define TLAN_HI_IT_MASK 0x001C
+#define TLAN_DIO_DATA 0x0C
+
+
+/* ThunderLAN Internal Register DIO Offsets */
+
+#define TLAN_NET_CMD 0x00
+#define TLAN_NET_CMD_NRESET 0x80
+#define TLAN_NET_CMD_NWRAP 0x40
+#define TLAN_NET_CMD_CSF 0x20
+#define TLAN_NET_CMD_CAF 0x10
+#define TLAN_NET_CMD_NOBRX 0x08
+#define TLAN_NET_CMD_DUPLEX 0x04
+#define TLAN_NET_CMD_TRFRAM 0x02
+#define TLAN_NET_CMD_TXPACE 0x01
+#define TLAN_NET_SIO 0x01
+#define TLAN_NET_SIO_MINTEN 0x80
+#define TLAN_NET_SIO_ECLOK 0x40
+#define TLAN_NET_SIO_ETXEN 0x20
+#define TLAN_NET_SIO_EDATA 0x10
+#define TLAN_NET_SIO_NMRST 0x08
+#define TLAN_NET_SIO_MCLK 0x04
+#define TLAN_NET_SIO_MTXEN 0x02
+#define TLAN_NET_SIO_MDATA 0x01
+#define TLAN_NET_STS 0x02
+#define TLAN_NET_STS_MIRQ 0x80
+#define TLAN_NET_STS_HBEAT 0x40
+#define TLAN_NET_STS_TXSTOP 0x20
+#define TLAN_NET_STS_RXSTOP 0x10
+#define TLAN_NET_STS_RSRVD 0x0F
+#define TLAN_NET_MASK 0x03
+#define TLAN_NET_MASK_MASK7 0x80
+#define TLAN_NET_MASK_MASK6 0x40
+#define TLAN_NET_MASK_MASK5 0x20
+#define TLAN_NET_MASK_MASK4 0x10
+#define TLAN_NET_MASK_RSRVD 0x0F
+#define TLAN_NET_CONFIG 0x04
+#define TLAN_NET_CFG_RCLK 0x8000
+#define TLAN_NET_CFG_TCLK 0x4000
+#define TLAN_NET_CFG_BIT 0x2000
+#define TLAN_NET_CFG_RXCRC 0x1000
+#define TLAN_NET_CFG_PEF 0x0800
+#define TLAN_NET_CFG_1FRAG 0x0400
+#define TLAN_NET_CFG_1CHAN 0x0200
+#define TLAN_NET_CFG_MTEST 0x0100
+#define TLAN_NET_CFG_PHY_EN 0x0080
+#define TLAN_NET_CFG_MSMASK 0x007F
+#define TLAN_MAN_TEST 0x06
+#define TLAN_DEF_VENDOR_ID 0x08
+#define TLAN_DEF_DEVICE_ID 0x0A
+#define TLAN_DEF_REVISION 0x0C
+#define TLAN_DEF_SUBCLASS 0x0D
+#define TLAN_DEF_MIN_LAT 0x0E
+#define TLAN_DEF_MAX_LAT 0x0F
+#define TLAN_AREG_0 0x10
+#define TLAN_AREG_1 0x16
+#define TLAN_AREG_2 0x1C
+#define TLAN_AREG_3 0x22
+#define TLAN_HASH_1 0x28
+#define TLAN_HASH_2 0x2C
+#define TLAN_GOOD_TX_FRMS 0x30
+#define TLAN_TX_UNDERUNS 0x33
+#define TLAN_GOOD_RX_FRMS 0x34
+#define TLAN_RX_OVERRUNS 0x37
+#define TLAN_DEFERRED_TX 0x38
+#define TLAN_CRC_ERRORS 0x3A
+#define TLAN_CODE_ERRORS 0x3B
+#define TLAN_MULTICOL_FRMS 0x3C
+#define TLAN_SINGLECOL_FRMS 0x3E
+#define TLAN_EXCESSCOL_FRMS 0x40
+#define TLAN_LATE_COLS 0x41
+#define TLAN_CARRIER_LOSS 0x42
+#define TLAN_ACOMMIT 0x43
+#define TLAN_LED_REG 0x44
+#define TLAN_LED_ACT 0x10
+#define TLAN_LED_LINK 0x01
+#define TLAN_BSIZE_REG 0x45
+#define TLAN_MAX_RX 0x46
+#define TLAN_INT_DIS 0x48
+#define TLAN_ID_TX_EOC 0x04
+#define TLAN_ID_RX_EOF 0x02
+#define TLAN_ID_RX_EOC 0x01
+
+
+
+/* ThunderLAN Interrupt Codes */
+
+#define TLAN_INT_NUMBER_OF_INTS 8
+
+#define TLAN_INT_NONE 0x0000
+#define TLAN_INT_TX_EOF 0x0001
+#define TLAN_INT_STAT_OVERFLOW 0x0002
+#define TLAN_INT_RX_EOF 0x0003
+#define TLAN_INT_DUMMY 0x0004
+#define TLAN_INT_TX_EOC 0x0005
+#define TLAN_INT_STATUS_CHECK 0x0006
+#define TLAN_INT_RX_EOC 0x0007
+
+
+
+/* ThunderLAN MII Registers */
+
+/* Generic MII/PHY Registers */
+
+#define MII_GEN_CTL 0x00
+#define MII_GC_RESET 0x8000
+#define MII_GC_LOOPBK 0x4000
+#define MII_GC_SPEEDSEL 0x2000
+#define MII_GC_AUTOENB 0x1000
+#define MII_GC_PDOWN 0x0800
+#define MII_GC_ISOLATE 0x0400
+#define MII_GC_AUTORSRT 0x0200
+#define MII_GC_DUPLEX 0x0100
+#define MII_GC_COLTEST 0x0080
+#define MII_GC_RESERVED 0x007F
+#define MII_GEN_STS 0x01
+#define MII_GS_100BT4 0x8000
+#define MII_GS_100BTXFD 0x4000
+#define MII_GS_100BTXHD 0x2000
+#define MII_GS_10BTFD 0x1000
+#define MII_GS_10BTHD 0x0800
+#define MII_GS_RESERVED 0x07C0
+#define MII_GS_AUTOCMPLT 0x0020
+#define MII_GS_RFLT 0x0010
+#define MII_GS_AUTONEG 0x0008
+#define MII_GS_LINK 0x0004
+#define MII_GS_JABBER 0x0002
+#define MII_GS_EXTCAP 0x0001
+#define MII_GEN_ID_HI 0x02
+#define MII_GEN_ID_LO 0x03
+#define MII_GIL_OUI 0xFC00
+#define MII_GIL_MODEL 0x03F0
+#define MII_GIL_REVISION 0x000F
+#define MII_AN_ADV 0x04
+#define MII_AN_LPA 0x05
+#define MII_AN_EXP 0x06
+
+/* ThunderLAN Specific MII/PHY Registers */
+
+#define TLAN_TLPHY_ID 0x10
+#define TLAN_TLPHY_CTL 0x11
+#define TLAN_TC_IGLINK 0x8000
+#define TLAN_TC_SWAPOL 0x4000
+#define TLAN_TC_AUISEL 0x2000
+#define TLAN_TC_SQEEN 0x1000
+#define TLAN_TC_MTEST 0x0800
+#define TLAN_TC_RESERVED 0x07F8
+#define TLAN_TC_NFEW 0x0004
+#define TLAN_TC_INTEN 0x0002
+#define TLAN_TC_TINT 0x0001
+#define TLAN_TLPHY_STS 0x12
+#define TLAN_TS_MINT 0x8000
+#define TLAN_TS_PHOK 0x4000
+#define TLAN_TS_POLOK 0x2000
+#define TLAN_TS_TPENERGY 0x1000
+#define TLAN_TS_RESERVED 0x0FFF
+#define TLAN_TLPHY_PAR 0x19
+#define TLAN_PHY_CIM_STAT 0x0020
+#define TLAN_PHY_SPEED_100 0x0040
+#define TLAN_PHY_DUPLEX_FULL 0x0080
+#define TLAN_PHY_AN_EN_STAT 0x0400
+
+/* National Sem. & Level1 PHY id's */
+#define NAT_SEM_ID1 0x2000
+#define NAT_SEM_ID2 0x5C01
+#define LEVEL1_ID1 0x7810
+#define LEVEL1_ID2 0x0000
+
+#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0
+
+/* Routines to access internal registers. */
+
+static inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3)));
+
+} /* TLan_DioRead8 */
+
+
+
+
+static inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2)));
+
+} /* TLan_DioRead16 */
+
+
+
+
+static inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inl(base_addr + TLAN_DIO_DATA));
+
+} /* TLan_DioRead32 */
+
+
+
+
+static inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3));
+
+}
+
+
+
+
+static inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+
+static inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+#if 0
+static inline void TLan_ClearBit(u8 bit, u16 port)
+{
+ outb_p(inb_p(port) & ~bit, port);
+}
+
+
+
+
+static inline int TLan_GetBit(u8 bit, u16 port)
+{
+ return ((int) (inb_p(port) & bit));
+}
+
+
+
+
+static inline void TLan_SetBit(u8 bit, u16 port)
+{
+ outb_p(inb_p(port) | bit, port);
+}
+#endif
+
+#define TLan_ClearBit( bit, port ) outb_p(inb_p(port) & ~bit, port)
+#define TLan_GetBit( bit, port ) ((int) (inb_p(port) & bit))
+#define TLan_SetBit( bit, port ) outb_p(inb_p(port) | bit, port)
+
+#ifdef I_LIKE_A_FAST_HASH_FUNCTION
+/* given 6 bytes, view them as 8 6-bit numbers and return the XOR of those */
+/* the code below is about seven times as fast as the original code */
+static inline u32 TLan_HashFunc(u8 * a)
+{
+ u8 hash;
+
+ hash = (a[0] ^ a[3]); /* & 077 */
+ hash ^= ((a[0] ^ a[3]) >> 6); /* & 003 */
+ hash ^= ((a[1] ^ a[4]) << 2); /* & 074 */
+ hash ^= ((a[1] ^ a[4]) >> 4); /* & 017 */
+ hash ^= ((a[2] ^ a[5]) << 4); /* & 060 */
+ hash ^= ((a[2] ^ a[5]) >> 2); /* & 077 */
+
+ return (hash & 077);
+}
+
+#else /* original code */
+
+static inline u32 xor(u32 a, u32 b)
+{
+ return ((a && !b) || (!a && b));
+}
+
+#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) )
+#define DA( a, bit ) ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) )
+
+static inline u32 TLan_HashFunc(u8 * a)
+{
+ u32 hash;
+
+ hash =
+ XOR8(DA(a, 0), DA(a, 6), DA(a, 12), DA(a, 18), DA(a, 24),
+ DA(a, 30), DA(a, 36), DA(a, 42));
+ hash |=
+ XOR8(DA(a, 1), DA(a, 7), DA(a, 13), DA(a, 19), DA(a, 25),
+ DA(a, 31), DA(a, 37), DA(a, 43)) << 1;
+ hash |=
+ XOR8(DA(a, 2), DA(a, 8), DA(a, 14), DA(a, 20), DA(a, 26),
+ DA(a, 32), DA(a, 38), DA(a, 44)) << 2;
+ hash |=
+ XOR8(DA(a, 3), DA(a, 9), DA(a, 15), DA(a, 21), DA(a, 27),
+ DA(a, 33), DA(a, 39), DA(a, 45)) << 3;
+ hash |=
+ XOR8(DA(a, 4), DA(a, 10), DA(a, 16), DA(a, 22), DA(a, 28),
+ DA(a, 34), DA(a, 40), DA(a, 46)) << 4;
+ hash |=
+ XOR8(DA(a, 5), DA(a, 11), DA(a, 17), DA(a, 23), DA(a, 29),
+ DA(a, 35), DA(a, 41), DA(a, 47)) << 5;
+
+ return hash;
+
+}
+
+#endif /* I_LIKE_A_FAST_HASH_FUNCTION */
diff --git a/gpxe/src/drivers/net/tulip.c b/gpxe/src/drivers/net/tulip.c
new file mode 100644
index 00000000..53cfb85f
--- /dev/null
+++ b/gpxe/src/drivers/net/tulip.c
@@ -0,0 +1,2097 @@
+/* -*- Mode:C; c-basic-offset:4; -*- */
+
+/*
+ Tulip and clone Etherboot Driver
+
+ By Marty Connor (mdc@etherboot.org)
+ Copyright (C) 2001 Entity Cyber, Inc.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ As of April 2001 this driver should support most tulip cards that
+ the Linux tulip driver supports because Donald Becker's Linux media
+ detection code is now included.
+
+ Based on Ken Yap's Tulip Etherboot Driver and Donald Becker's
+ Linux Tulip Driver. Supports N-Way speed auto-configuration on
+ MX98715, MX98715A and MX98725. Support inexpensive PCI 10/100 cards
+ based on the Macronix MX987x5 chip, such as the SOHOware Fast
+ model SFA110A, and the LinkSYS model LNE100TX. The NetGear
+ model FA310X, based on the LC82C168 chip is supported.
+ The TRENDnet TE100-PCIA NIC which uses a genuine Intel 21143-PD
+ chipset is supported. Also, Davicom DM9102's.
+
+ Documentation and source code used:
+ Source for Etherboot driver at
+ http://etherboot.sourceforge.net/
+ MX98715A Data Sheet and MX98715A Application Note
+ on http://www.macronix.com/ (PDF format files)
+ Source for Linux tulip driver at
+ http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
+
+ Adapted by Ken Yap from
+ FreeBSD netboot DEC 21143 driver
+ Author: David Sharp
+ date: Nov/98
+
+ Some code fragments were taken from verious places, Ken Yap's
+ etherboot, FreeBSD's if_de.c, and various Linux related files.
+ DEC's manuals for the 21143 and SROM format were very helpful.
+ The Linux de driver development page has a number of links to
+ useful related information. Have a look at:
+ ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/tulip-devel.html
+*/
+
+/*********************************************************************/
+/* Revision History */
+/*********************************************************************/
+
+/*
+ 08 Feb 2005 Ramesh Chander chhabaramesh at yahoo.co.in added table entries
+ for SGThomson STE10/100A
+ 07 Sep 2003 timlegge Multicast Support Added
+ 11 Apr 2001 mdc [patch to etherboot 4.7.24]
+ Major rewrite to include Linux tulip driver media detection
+ code. This driver should support a lot more cards now.
+ 16 Jul 2000 mdc 0.75b11
+ Added support for ADMtek 0985 Centaur-P, a "Comet" tulip clone
+ which is used on the LinkSYS LNE100TX v4.x cards. We already
+ support LNE100TX v2.0 cards, which use a different controller.
+ 04 Jul 2000 jam ?
+ Added test of status after receiving a packet from the card.
+ Also uncommented the tulip_disable routine. Stray packets
+ seemed to be causing problems.
+ 27 Apr 2000 njl ?
+ 29 Feb 2000 mdc 0.75b7
+ Increased reset delay to 3 seconds because Macronix cards seem to
+ need more reset time before card comes back to a usable state.
+ 26 Feb 2000 mdc 0.75b6
+ Added a 1 second delay after initializing the transmitter because
+ some cards seem to need the time or they drop the first packet
+ transmitted.
+ 23 Feb 2000 mdc 0.75b5
+ removed udelay code and used currticks() for more reliable delay
+ code in reset pause and sanity timeouts. Added function prototypes
+ and TX debugging code.
+ 21 Feb 2000 mdc patch to Etherboot 4.4.3
+ Incorporated patches from Bob Edwards and Paul Mackerras of
+ Linuxcare's OZLabs to deal with inefficiencies in tulip_transmit
+ and udelay. We now wait for packet transmission to complete
+ (or sanity timeout).
+ 04 Feb 2000 Robert.Edwards@anu.edu.au patch to Etherboot 4.4.2
+ patch to tulip.c that implements the automatic selection of the MII
+ interface on cards using the Intel/DEC 21143 reference design, in
+ particular, the TRENDnet TE100-PCIA NIC which uses a genuine Intel
+ 21143-PD chipset.
+ 11 Jan 2000 mdc 0.75b4
+ Added support for NetGear FA310TX card based on the LC82C168
+ chip. This should also support Lite-On LC82C168 boards.
+ Added simple MII support. Re-arranged code to better modularize
+ initializations.
+ 04 Dec 1999 mdc 0.75b3
+ Added preliminary support for LNE100TX PCI cards. Should work for
+ PNIC2 cards. No MII support, but single interface (RJ45) tulip
+ cards seem to not care.
+ 03 Dec 1999 mdc 0.75b2
+ Renamed from mx987x5 to tulip, merged in original tulip init code
+ from tulip.c to support other tulip compatible cards.
+ 02 Dec 1999 mdc 0.75b1
+ Released Beta MX987x5 Driver for code review and testing to netboot
+ and thinguin mailing lists.
+*/
+
+
+/*********************************************************************/
+/* Declarations */
+/*********************************************************************/
+
+#include "etherboot.h"
+#include "nic.h"
+
+#include <gpxe/ethernet.h>
+#include <gpxe/pci.h>
+
+/* User settable parameters */
+
+#undef TULIP_DEBUG
+#undef TULIP_DEBUG_WHERE
+#ifdef TULIP_DEBUG
+static int tulip_debug = 2; /* 1 normal messages, 0 quiet .. 7 verbose. */
+#endif
+
+#define TX_TIME_OUT 2*TICKS_PER_SEC
+
+/* helpful macros if on a big_endian machine for changing byte order.
+ not strictly needed on Intel */
+#define get_unaligned(ptr) (*(ptr))
+#define put_unaligned(val, ptr) ((void)( *(ptr) = (val) ))
+#define get_u16(ptr) (*(u16 *)(ptr))
+#define virt_to_le32desc(addr) virt_to_bus(addr)
+
+#define TULIP_IOTYPE PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0
+#define TULIP_SIZE 0x80
+
+/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
+ to support a pre-NWay full-duplex signaling mechanism using short frames.
+ No one knows what it should be, but if left at its default value some
+ 10base2(!) packets trigger a full-duplex-request interrupt. */
+#define FULL_DUPLEX_MAGIC 0x6969
+
+static const int csr0 = 0x01A00000 | 0x8000;
+
+/* The possible media types that can be set in options[] are: */
+#define MEDIA_MASK 31
+static const char * const medianame[32] = {
+ "10baseT", "10base2", "AUI", "100baseTx",
+ "10baseT-FDX", "100baseTx-FDX", "100baseT4", "100baseFx",
+ "100baseFx-FDX", "MII 10baseT", "MII 10baseT-FDX", "MII",
+ "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FDX", "MII 100baseT4",
+ "MII 100baseFx-HDX", "MII 100baseFx-FDX", "Home-PNA 1Mbps", "Invalid-19",
+};
+
+/* This much match tulip_tbl[]! Note 21142 == 21143. */
+enum tulip_chips {
+ DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
+ LC82C168, MX98713, MX98715, MX98725, AX88141, AX88140, PNIC2, COMET,
+ COMPEX9881, I21145, XIRCOM, SGThomson, /*Ramesh Chander*/
+};
+
+enum pci_id_flags_bits {
+ /* Set PCI command register bits before calling probe1(). */
+ PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+ /* Read and map the single following PCI BAR. */
+ PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
+ PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
+ PCI_UNUSED_IRQ=0x800,
+};
+
+struct pci_id_info {
+ char *name;
+ struct match_info {
+ u32 pci, pci_mask, subsystem, subsystem_mask;
+ u32 revision, revision_mask; /* Only 8 bits. */
+ } id;
+ enum pci_id_flags_bits pci_flags;
+ int io_size; /* Needed for I/O region check or ioremap(). */
+ int drv_flags; /* Driver use, intended as capability flags. */
+};
+
+static const struct pci_id_info pci_id_tbl[] = {
+ { "Digital DC21040 Tulip", { 0x00021011, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 0x80, DC21040 },
+ { "Digital DC21041 Tulip", { 0x00141011, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 0x80, DC21041 },
+ { "Digital DS21140A Tulip", { 0x00091011, 0xffffffff, 0,0, 0x20,0xf0 },
+ TULIP_IOTYPE, 0x80, DC21140 },
+ { "Digital DS21140 Tulip", { 0x00091011, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 0x80, DC21140 },
+ { "Digital DS21143 Tulip", { 0x00191011, 0xffffffff, 0,0, 65,0xff },
+ TULIP_IOTYPE, TULIP_SIZE, DC21142 },
+ { "Digital DS21142 Tulip", { 0x00191011, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, TULIP_SIZE, DC21142 },
+ { "Kingston KNE110tx (PNIC)", { 0x000211AD, 0xffffffff, 0xf0022646, 0xffffffff, 0, 0 },
+ TULIP_IOTYPE, 256, LC82C168 },
+ { "Lite-On 82c168 PNIC", { 0x000211AD, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, LC82C168 },
+ { "Macronix 98713 PMAC", { 0x051210d9, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, MX98713 },
+ { "Macronix 98715 PMAC", { 0x053110d9, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, MX98715 },
+ { "Macronix 98725 PMAC", { 0x053110d9, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, MX98725 },
+ { "ASIX AX88141", { 0x1400125B, 0xffffffff, 0,0, 0x10, 0xf0 },
+ TULIP_IOTYPE, 128, AX88141 },
+ { "ASIX AX88140", { 0x1400125B, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 128, AX88140 },
+ { "Lite-On LC82C115 PNIC-II", { 0xc11511AD, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, PNIC2 },
+ { "ADMtek AN981 Comet", { 0x09811317, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET },
+ { "ADMTek AN983 Comet", { 0x12161113, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET },
+ { "ADMTek Comet AN983b", { 0x95111317, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET },
+ { "ADMtek Centaur-P", { 0x09851317, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET },
+ { "ADMtek Centaur-C", { 0x19851317, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET },
+ { "Compex RL100-TX", { 0x988111F6, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 128, COMPEX9881 },
+ { "Intel 21145 Tulip", { 0x00398086, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 128, I21145 },
+ { "Xircom Tulip clone", { 0x0003115d, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 128, XIRCOM },
+ { "Davicom DM9102", { 0x91021282, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 0x80, DC21140 },
+ { "Davicom DM9100", { 0x91001282, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 0x80, DC21140 },
+ { "Macronix mxic-98715 (EN1217)", { 0x12171113, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, MX98715 },
+ { "3Com 3cSOHO100B-TX (ADMtek Centuar)", { 0x930010b7, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, TULIP_SIZE, COMET },
+ { "SG Thomson STE10/100A", { 0x2774104a, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET }, /*Ramesh Chander*/
+ { 0, { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 },
+};
+
+enum tbl_flag {
+ HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8,
+ HAS_PWRDWN=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */
+ HAS_PNICNWAY=0x80, HAS_NWAY=0x40, /* Uses internal NWay xcvr. */
+ HAS_INTR_MITIGATION=0x100, IS_ASIX=0x200, HAS_8023X=0x400,
+};
+
+/* Note: this table must match enum tulip_chips above. */
+static struct tulip_chip_table {
+ char *chip_name;
+ int flags;
+} tulip_tbl[] = {
+ { "Digital DC21040 Tulip", 0},
+ { "Digital DC21041 Tulip", HAS_MEDIA_TABLE | HAS_NWAY },
+ { "Digital DS21140 Tulip", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM },
+ { "Digital DS21143 Tulip", HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII
+ | HAS_PWRDWN | HAS_NWAY | HAS_INTR_MITIGATION },
+ { "Lite-On 82c168 PNIC", HAS_MII | HAS_PNICNWAY },
+ { "Macronix 98713 PMAC", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM },
+ { "Macronix 98715 PMAC", HAS_MEDIA_TABLE },
+ { "Macronix 98725 PMAC", HAS_MEDIA_TABLE },
+ { "ASIX AX88140", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM
+ | MC_HASH_ONLY | IS_ASIX },
+ { "ASIX AX88141", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY
+ | IS_ASIX },
+ { "Lite-On PNIC-II", HAS_MII | HAS_NWAY | HAS_8023X },
+ { "ADMtek Comet", HAS_MII | MC_HASH_ONLY },
+ { "Compex 9881 PMAC", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM },
+ { "Intel DS21145 Tulip", HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII
+ | HAS_PWRDWN | HAS_NWAY },
+ { "Xircom tulip work-alike", HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII
+ | HAS_PWRDWN | HAS_NWAY },
+ { "SGThomson STE10/100A", HAS_MII | MC_HASH_ONLY }, /*Ramesh Chander*/
+ { 0, 0 },
+};
+
+/* A full-duplex map for media types. */
+enum MediaIs {
+ MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
+ MediaIs100=16};
+
+static const char media_cap[32] =
+{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20, 20,31,0,0, };
+static u8 t21040_csr13[] = {2,0x0C,8,4, 4,0,0,0, 0,0,0,0, 4,0,0,0};
+
+/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD */
+static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
+static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
+static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
+
+/* not used
+static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
+*/
+static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
+/* not used
+static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
+*/
+
+/* Offsets to the Command and Status Registers, "CSRs". All accesses
+ must be longword instructions and quadword aligned. */
+enum tulip_offsets {
+ CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+ CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+ CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x80, CSR20=0xA0
+};
+
+/* The bits in the CSR5 status registers, mostly interrupt sources. */
+enum status_bits {
+ TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
+ NormalIntr=0x10000, AbnormalIntr=0x8000,
+ RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
+ TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
+};
+
+/* The configuration bits in CSR6. */
+enum csr6_mode_bits {
+ TxOn=0x2000, RxOn=0x0002, FullDuplex=0x0200,
+ AcceptBroadcast=0x0100, AcceptAllMulticast=0x0080,
+ AcceptAllPhys=0x0040, AcceptRunt=0x0008,
+};
+
+
+enum desc_status_bits {
+ DescOwnded=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,
+};
+
+struct medialeaf {
+ u8 type;
+ u8 media;
+ unsigned char *leafdata;
+};
+
+struct mediatable {
+ u16 defaultmedia;
+ u8 leafcount, csr12dir; /* General purpose pin directions. */
+ unsigned has_mii:1, has_nonmii:1, has_reset:6;
+ u32 csr15dir, csr15val; /* 21143 NWay setting. */
+ struct medialeaf mleaf[0];
+};
+
+struct mediainfo {
+ struct mediainfo *next;
+ int info_type;
+ int index;
+ unsigned char *info;
+};
+
+/* EEPROM Address width definitions */
+#define EEPROM_ADDRLEN 6
+#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << addr_len)
+#define EE_READ_CMD (6 << addr_len)
+#define EE_ERASE_CMD (7 << addr_len)
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
+#define EE_CS 0x01 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x01
+#define EE_WRITE_1 0x05
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+#define EE_ENB (0x4800 | EE_CS)
+
+/* Delay between EEPROM clock transitions. Even at 33Mhz current PCI
+ implementations don't overrun the EEPROM clock. We add a bus
+ turn-around to insure that this remains true. */
+#define eeprom_delay() inl(ee_addr)
+
+/* Size of transmit and receive buffers */
+#define BUFLEN 1536
+
+/* Ring-wrap flag in length field, use for last ring entry.
+ 0x01000000 means chain on buffer2 address,
+ 0x02000000 means use the ring start address in CSR2/3.
+ Note: Some work-alike chips do not function correctly in chained mode.
+ The ASIX chip works only in chained mode.
+ Thus we indicate ring mode, but always write the 'next' field for
+ chained mode as well. */
+#define DESC_RING_WRAP 0x02000000
+
+/* transmit and receive descriptor format */
+struct tulip_rx_desc {
+ volatile u32 status;
+ u32 length;
+ u32 buffer1, buffer2;
+};
+
+struct tulip_tx_desc {
+ volatile u32 status;
+ u32 length;
+ u32 buffer1, buffer2;
+};
+
+/*********************************************************************/
+/* Global Storage */
+/*********************************************************************/
+
+static u32 ioaddr;
+
+struct tulip_private {
+ int cur_rx;
+ int chip_id; /* index into tulip_tbl[] */
+ int pci_id_idx; /* index into pci_id_tbl[] */
+ int revision;
+ int flags;
+ unsigned short vendor_id; /* PCI card vendor code */
+ unsigned short dev_id; /* PCI card device code */
+ unsigned char ehdr[ETH_HLEN]; /* buffer for ethernet header */
+ const char *nic_name;
+ unsigned int csr0, csr6; /* Current CSR0, CSR6 settings. */
+ unsigned int if_port;
+ unsigned int full_duplex; /* Full-duplex operation requested. */
+ unsigned int full_duplex_lock;
+ unsigned int medialock; /* Do not sense media type. */
+ unsigned int mediasense; /* Media sensing in progress. */
+ unsigned int nway, nwayset; /* 21143 internal NWay. */
+ unsigned int default_port;
+ unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */
+ u8 media_table_storage[(sizeof(struct mediatable) + 32*sizeof(struct medialeaf))];
+ u16 sym_advertise, mii_advertise; /* NWay to-advertise. */
+ struct mediatable *mtable;
+ u16 lpar; /* 21143 Link partner ability. */
+ u16 advertising[4]; /* MII advertise, from SROM table. */
+ signed char phys[4], mii_cnt; /* MII device addresses. */
+ int cur_index; /* Current media index. */
+ int saved_if_port;
+};
+
+/* Note: transmit and receive buffers must be longword aligned and
+ longword divisable */
+
+#define TX_RING_SIZE 2
+#define RX_RING_SIZE 4
+struct {
+ struct tulip_tx_desc tx_ring[TX_RING_SIZE];
+ unsigned char txb[BUFLEN];
+ struct tulip_rx_desc rx_ring[RX_RING_SIZE];
+ unsigned char rxb[RX_RING_SIZE * BUFLEN];
+ struct tulip_private tpx;
+} tulip_bss __shared __attribute__ ((aligned(4)));
+#define tx_ring tulip_bss.tx_ring
+#define txb tulip_bss.txb
+#define rx_ring tulip_bss.rx_ring
+#define rxb tulip_bss.rxb
+
+static struct tulip_private *tp;
+
+/* Known cards that have old-style EEPROMs.
+ Writing this table is described at
+ http://cesdis.gsfc.nasa.gov/linux/drivers/tulip-drivers/tulip-media.html */
+static struct fixups {
+ char *name;
+ unsigned char addr0, addr1, addr2;
+ u16 newtable[32]; /* Max length below. */
+} eeprom_fixups[] = {
+ {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
+ 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
+ {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x041f,
+ 0x0000, 0x009E, /* 10baseT */
+ 0x0004, 0x009E, /* 10baseT-FD */
+ 0x0903, 0x006D, /* 100baseTx */
+ 0x0905, 0x006D, /* 100baseTx-FD */ }},
+ {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x063f,
+ 0x0107, 0x8021, /* 100baseFx */
+ 0x0108, 0x8021, /* 100baseFx-FD */
+ 0x0100, 0x009E, /* 10baseT */
+ 0x0104, 0x009E, /* 10baseT-FD */
+ 0x0103, 0x006D, /* 100baseTx */
+ 0x0105, 0x006D, /* 100baseTx-FD */ }},
+ {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0513,
+ 0x1001, 0x009E, /* 10base2, CSR12 0x10*/
+ 0x0000, 0x009E, /* 10baseT */
+ 0x0004, 0x009E, /* 10baseT-FD */
+ 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */
+ 0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}},
+ {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x051F,
+ 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */
+ 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */
+ 0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */
+ 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
+ 0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */
+ }},
+ {0, 0, 0, 0, {}}};
+
+static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
+ "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
+
+
+/*********************************************************************/
+/* Function Prototypes */
+/*********************************************************************/
+static int mdio_read(struct nic *nic, int phy_id, int location);
+static void mdio_write(struct nic *nic, int phy_id, int location, int value);
+static int read_eeprom(unsigned long ioaddr, int location, int addr_len);
+static void parse_eeprom(struct nic *nic);
+static int tulip_probe(struct nic *nic,struct pci_device *pci);
+static void tulip_init_ring(struct nic *nic);
+static void tulip_reset(struct nic *nic);
+static void tulip_transmit(struct nic *nic, const char *d, unsigned int t,
+ unsigned int s, const char *p);
+static int tulip_poll(struct nic *nic, int retrieve);
+static void tulip_disable(struct nic *nic);
+static void nway_start(struct nic *nic);
+static void pnic_do_nway(struct nic *nic);
+static void select_media(struct nic *nic, int startup);
+static void init_media(struct nic *nic);
+static void start_link(struct nic *nic);
+static int tulip_check_duplex(struct nic *nic);
+
+static void tulip_wait(unsigned int nticks);
+
+#ifdef TULIP_DEBUG_WHERE
+static void whereami(const char *str);
+#endif
+
+#ifdef TULIP_DEBUG
+static void tulip_more(void);
+#endif
+
+
+/*********************************************************************/
+/* Utility Routines */
+/*********************************************************************/
+
+#ifdef TULIP_DEBUG_WHERE
+static void whereami (const char *str, struct pci_device *pci)
+{
+ printf("%s: %s\n", tp->nic_name, str);
+ /* sleep(2); */
+}
+#endif
+
+#ifdef TULIP_DEBUG
+static void tulip_more(void)
+{
+ printf("\n\n-- more --");
+ while (!iskey())
+ /* wait */;
+ getchar();
+ printf("\n\n");
+}
+#endif /* TULIP_DEBUG */
+
+static void tulip_wait(unsigned int nticks)
+{
+ unsigned int to = currticks() + nticks;
+ while (currticks() < to)
+ /* wait */ ;
+}
+
+
+/*********************************************************************/
+/* Media Descriptor Code */
+/*********************************************************************/
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details. */
+
+/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back PCI I/O cycles, but we insert a delay to avoid
+ "overclocking" issues or future 66Mhz PCI. */
+#define mdio_delay() inl(mdio_addr)
+
+/* Read and write the MII registers using software-generated serial
+ MDIO protocol. It is just different enough from the EEPROM protocol
+ to not share code. The maxium data clock rate is 2.5 Mhz. */
+#define MDIO_SHIFT_CLK 0x10000
+#define MDIO_DATA_WRITE0 0x00000
+#define MDIO_DATA_WRITE1 0x20000
+#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */
+#define MDIO_ENB_IN 0x40000
+#define MDIO_DATA_READ 0x80000
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details. */
+
+int mdio_read(struct nic *nic __unused, int phy_id, int location)
+{
+ int i;
+ int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int retval = 0;
+ long mdio_addr = ioaddr + CSR9;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("mdio_read\n");
+#endif
+
+ if (tp->chip_id == LC82C168) {
+ int i = 1000;
+ outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
+ inl(ioaddr + 0xA0);
+ inl(ioaddr + 0xA0);
+ while (--i > 0)
+ if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
+ return retval & 0xffff;
+ return 0xffff;
+ }
+
+ if (tp->chip_id == COMET) {
+ if (phy_id == 1) {
+ if (location < 7)
+ return inl(ioaddr + 0xB4 + (location<<2));
+ else if (location == 17)
+ return inl(ioaddr + 0xD0);
+ else if (location >= 29 && location <= 31)
+ return inl(ioaddr + 0xD4 + ((location-29)<<2));
+ }
+ return 0xffff;
+ }
+
+ /* Establish sync by sending at least 32 logic ones. */
+ for (i = 32; i >= 0; i--) {
+ outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
+ mdio_delay();
+ outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+
+ outl(MDIO_ENB | dataval, mdio_addr);
+ mdio_delay();
+ outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ outl(MDIO_ENB_IN, mdio_addr);
+ mdio_delay();
+ retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+ outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ return (retval>>1) & 0xffff;
+}
+
+void mdio_write(struct nic *nic __unused, int phy_id, int location, int value)
+{
+ int i;
+ int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+ long mdio_addr = ioaddr + CSR9;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("mdio_write\n");
+#endif
+
+ if (tp->chip_id == LC82C168) {
+ int i = 1000;
+ outl(cmd, ioaddr + 0xA0);
+ do
+ if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
+ break;
+ while (--i > 0);
+ return;
+ }
+
+ if (tp->chip_id == COMET) {
+ if (phy_id != 1)
+ return;
+ if (location < 7)
+ outl(value, ioaddr + 0xB4 + (location<<2));
+ else if (location == 17)
+ outl(value, ioaddr + 0xD0);
+ else if (location >= 29 && location <= 31)
+ outl(value, ioaddr + 0xD4 + ((location-29)<<2));
+ return;
+ }
+
+ /* Establish sync by sending 32 logic ones. */
+ for (i = 32; i >= 0; i--) {
+ outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
+ mdio_delay();
+ outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+ outl(MDIO_ENB | dataval, mdio_addr);
+ mdio_delay();
+ outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ outl(MDIO_ENB_IN, mdio_addr);
+ mdio_delay();
+ outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+}
+
+
+/*********************************************************************/
+/* EEPROM Reading Code */
+/*********************************************************************/
+/* EEPROM routines adapted from the Linux Tulip Code */
+/* Reading a serial EEPROM is a "bit" grungy, but we work our way
+ through:->.
+*/
+static int read_eeprom(unsigned long ioaddr, int location, int addr_len)
+{
+ int i;
+ unsigned short retval = 0;
+ long ee_addr = ioaddr + CSR9;
+ int read_cmd = location | EE_READ_CMD;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("read_eeprom\n");
+#endif
+
+ outl(EE_ENB & ~EE_CS, ee_addr);
+ outl(EE_ENB, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 4 + addr_len; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ outl(EE_ENB | dataval, ee_addr);
+ eeprom_delay();
+ outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ }
+ outl(EE_ENB, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ outl(EE_ENB, ee_addr);
+ eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ outl(EE_ENB & ~EE_CS, ee_addr);
+ return retval;
+}
+
+
+/*********************************************************************/
+/* EEPROM Parsing Code */
+/*********************************************************************/
+static void parse_eeprom(struct nic *nic)
+{
+ unsigned char *p, *ee_data = tp->eeprom;
+ int new_advertise = 0;
+ int i;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("parse_eeprom\n");
+#endif
+
+ tp->mtable = 0;
+ /* Detect an old-style (SA only) EEPROM layout:
+ memcmp(ee_data, ee_data+16, 8). */
+ for (i = 0; i < 8; i ++)
+ if (ee_data[i] != ee_data[16+i])
+ break;
+ if (i >= 8) {
+ /* Do a fix-up based on the vendor half of the station address. */
+ for (i = 0; eeprom_fixups[i].name; i++) {
+ if (nic->node_addr[0] == eeprom_fixups[i].addr0
+ && nic->node_addr[1] == eeprom_fixups[i].addr1
+ && nic->node_addr[2] == eeprom_fixups[i].addr2) {
+ if (nic->node_addr[2] == 0xE8 && ee_data[0x1a] == 0x55)
+ i++; /* An Accton EN1207, not an outlaw Maxtech. */
+ memcpy(ee_data + 26, eeprom_fixups[i].newtable,
+ sizeof(eeprom_fixups[i].newtable));
+#ifdef TULIP_DEBUG
+ printf("%s: Old format EEPROM on '%s' board.\n%s: Using substitute media control info.\n",
+ tp->nic_name, eeprom_fixups[i].name, tp->nic_name);
+#endif
+ break;
+ }
+ }
+ if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
+#ifdef TULIP_DEBUG
+ printf("%s: Old style EEPROM with no media selection information.\n",
+ tp->nic_name);
+#endif
+ return;
+ }
+ }
+
+ if (ee_data[19] > 1) {
+#ifdef TULIP_DEBUG
+ printf("%s: Multiport cards (%d ports) may not work correctly.\n",
+ tp->nic_name, ee_data[19]);
+#endif
+ }
+
+ p = (void *)ee_data + ee_data[27];
+
+ if (ee_data[27] == 0) { /* No valid media table. */
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1) {
+ printf("%s: No Valid Media Table. ee_data[27] = %hhX\n",
+ tp->nic_name, ee_data[27]);
+ }
+#endif
+ } else if (tp->chip_id == DC21041) {
+ int media = get_u16(p);
+ int count = p[2];
+ p += 3;
+
+ printf("%s: 21041 Media table, default media %hX (%s).\n",
+ tp->nic_name, media,
+ media & 0x0800 ? "Autosense" : medianame[media & 15]);
+ for (i = 0; i < count; i++) {
+ unsigned char media_block = *p++;
+ int media_code = media_block & MEDIA_MASK;
+ if (media_block & 0x40)
+ p += 6;
+ switch(media_code) {
+ case 0: new_advertise |= 0x0020; break;
+ case 4: new_advertise |= 0x0040; break;
+ }
+ printf("%s: 21041 media #%d, %s.\n",
+ tp->nic_name, media_code, medianame[media_code]);
+ }
+ } else {
+ unsigned char csr12dir = 0;
+ int count;
+ struct mediatable *mtable;
+ u16 media = get_u16(p);
+
+ p += 2;
+ if (tp->flags & CSR12_IN_SROM)
+ csr12dir = *p++;
+ count = *p++;
+
+ tp->mtable = mtable = (struct mediatable *)&tp->media_table_storage[0];
+
+ mtable->defaultmedia = media;
+ mtable->leafcount = count;
+ mtable->csr12dir = csr12dir;
+ mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0;
+ mtable->csr15dir = mtable->csr15val = 0;
+
+ printf("%s: EEPROM default media type %s.\n", tp->nic_name,
+ media & 0x0800 ? "Autosense" : medianame[media & MEDIA_MASK]);
+
+ for (i = 0; i < count; i++) {
+ struct medialeaf *leaf = &mtable->mleaf[i];
+
+ if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
+ leaf->type = 0;
+ leaf->media = p[0] & 0x3f;
+ leaf->leafdata = p;
+ if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */
+ mtable->has_mii = 1;
+ p += 4;
+ } else {
+ switch(leaf->type = p[1]) {
+ case 5:
+ mtable->has_reset = i;
+ leaf->media = p[2] & 0x0f;
+ break;
+ case 1: case 3:
+ mtable->has_mii = 1;
+ leaf->media = 11;
+ break;
+ case 2:
+ if ((p[2] & 0x3f) == 0) {
+ u32 base15 = (p[2] & 0x40) ? get_u16(p + 7) : 0x0008;
+ u16 *p1 = (u16 *)(p + (p[2] & 0x40 ? 9 : 3));
+ mtable->csr15dir = (get_unaligned(p1 + 0)<<16) + base15;
+ mtable->csr15val = (get_unaligned(p1 + 1)<<16) + base15;
+ }
+ /* Fall through. */
+ case 0: case 4:
+ mtable->has_nonmii = 1;
+ leaf->media = p[2] & MEDIA_MASK;
+ switch (leaf->media) {
+ case 0: new_advertise |= 0x0020; break;
+ case 4: new_advertise |= 0x0040; break;
+ case 3: new_advertise |= 0x0080; break;
+ case 5: new_advertise |= 0x0100; break;
+ case 6: new_advertise |= 0x0200; break;
+ }
+ break;
+ default:
+ leaf->media = 19;
+ }
+ leaf->leafdata = p + 2;
+ p += (p[0] & 0x3f) + 1;
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1 && leaf->media == 11) {
+ unsigned char *bp = leaf->leafdata;
+ printf("%s: MII interface PHY %d, setup/reset sequences %d/%d long, capabilities %hhX %hhX.\n",
+ tp->nic_name, bp[0], bp[1], bp[2 + bp[1]*2],
+ bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
+ }
+#endif
+ printf("%s: Index #%d - Media %s (#%d) described "
+ "by a %s (%d) block.\n",
+ tp->nic_name, i, medianame[leaf->media], leaf->media,
+ leaf->type < 6 ? block_name[leaf->type] : "UNKNOWN",
+ leaf->type);
+ }
+ if (new_advertise)
+ tp->sym_advertise = new_advertise;
+ }
+}
+
+
+/*********************************************************************/
+/* tulip_init_ring - setup the tx and rx descriptors */
+/*********************************************************************/
+static void tulip_init_ring(struct nic *nic __unused)
+{
+ int i;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_init_ring\n");
+#endif
+
+ tp->cur_rx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_ring[i].status = cpu_to_le32(0x80000000);
+ rx_ring[i].length = cpu_to_le32(BUFLEN);
+ rx_ring[i].buffer1 = virt_to_le32desc(&rxb[i * BUFLEN]);
+ rx_ring[i].buffer2 = virt_to_le32desc(&rx_ring[i+1]);
+ }
+ /* Mark the last entry as wrapping the ring. */
+ rx_ring[i-1].length = cpu_to_le32(DESC_RING_WRAP | BUFLEN);
+ rx_ring[i-1].buffer2 = virt_to_le32desc(&rx_ring[0]);
+
+ /* We only use 1 transmit buffer, but we use 2 descriptors so
+ transmit engines have somewhere to point to if they feel the need */
+
+ tx_ring[0].status = 0x00000000;
+ tx_ring[0].buffer1 = virt_to_le32desc(&txb[0]);
+ tx_ring[0].buffer2 = virt_to_le32desc(&tx_ring[1]);
+
+ /* this descriptor should never get used, since it will never be owned
+ by the machine (status will always == 0) */
+ tx_ring[1].status = 0x00000000;
+ tx_ring[1].buffer1 = virt_to_le32desc(&txb[0]);
+ tx_ring[1].buffer2 = virt_to_le32desc(&tx_ring[0]);
+
+ /* Mark the last entry as wrapping the ring, though this should never happen */
+ tx_ring[1].length = cpu_to_le32(DESC_RING_WRAP | BUFLEN);
+}
+
+
+static void set_rx_mode(struct nic *nic __unused) {
+ int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+
+ tp->csr6 &= ~0x00D5;
+
+ /* !IFF_PROMISC */
+ tp->csr6 |= AcceptAllMulticast;
+ csr6 |= AcceptAllMulticast;
+
+ outl(csr6, ioaddr + CSR6);
+
+
+
+}
+
+/*********************************************************************/
+/* eth_reset - Reset adapter */
+/*********************************************************************/
+static void tulip_reset(struct nic *nic)
+{
+ int i;
+ unsigned long to;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_reset\n");
+#endif
+
+ /* Stop Tx and RX */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* On some chip revs we must set the MII/SYM port before the reset!? */
+ if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) {
+ outl(0x814C0000, ioaddr + CSR6);
+ }
+
+ /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
+ outl(0x00000001, ioaddr + CSR0);
+ tulip_wait(1);
+
+ /* turn off reset and set cache align=16lword, burst=unlimit */
+ outl(tp->csr0, ioaddr + CSR0);
+
+ /* Wait the specified 50 PCI cycles after a reset */
+ tulip_wait(1);
+
+ /* set up transmit and receive descriptors */
+ tulip_init_ring(nic);
+
+ if (tp->chip_id == PNIC2) {
+ u32 addr_high = (nic->node_addr[1]<<8) + (nic->node_addr[0]<<0);
+ /* This address setting does not appear to impact chip operation?? */
+ outl((nic->node_addr[5]<<8) + nic->node_addr[4] +
+ (nic->node_addr[3]<<24) + (nic->node_addr[2]<<16),
+ ioaddr + 0xB0);
+ outl(addr_high + (addr_high<<16), ioaddr + 0xB8);
+ }
+
+ /* MC_HASH_ONLY boards don't support setup packets */
+ if (tp->flags & MC_HASH_ONLY) {
+ u32 addr_low = cpu_to_le32(get_unaligned((u32 *)nic->node_addr));
+ u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(nic->node_addr+4)));
+
+ /* clear multicast hash filters and setup MAC address filters */
+ if (tp->flags & IS_ASIX) {
+ outl(0, ioaddr + CSR13);
+ outl(addr_low, ioaddr + CSR14);
+ outl(1, ioaddr + CSR13);
+ outl(addr_high, ioaddr + CSR14);
+ outl(2, ioaddr + CSR13);
+ outl(0, ioaddr + CSR14);
+ outl(3, ioaddr + CSR13);
+ outl(0, ioaddr + CSR14);
+ } else if (tp->chip_id == COMET) {
+ outl(addr_low, ioaddr + 0xA4);
+ outl(addr_high, ioaddr + 0xA8);
+ outl(0, ioaddr + 0xAC);
+ outl(0, ioaddr + 0xB0);
+ }
+ } else {
+ /* for other boards we send a setup packet to initialize
+ the filters */
+ u32 tx_flags = 0x08000000 | 192;
+
+ /* construct perfect filter frame with mac address as first match
+ and broadcast address for all others */
+ for (i=0; i<192; i++)
+ txb[i] = 0xFF;
+ txb[0] = nic->node_addr[0];
+ txb[1] = nic->node_addr[1];
+ txb[4] = nic->node_addr[2];
+ txb[5] = nic->node_addr[3];
+ txb[8] = nic->node_addr[4];
+ txb[9] = nic->node_addr[5];
+
+ tx_ring[0].length = cpu_to_le32(tx_flags);
+ tx_ring[0].buffer1 = virt_to_le32desc(&txb[0]);
+ tx_ring[0].status = cpu_to_le32(0x80000000);
+ }
+
+ /* Point to rx and tx descriptors */
+ outl(virt_to_le32desc(&rx_ring[0]), ioaddr + CSR3);
+ outl(virt_to_le32desc(&tx_ring[0]), ioaddr + CSR4);
+
+ init_media(nic);
+
+ /* set the chip's operating mode (but don't turn on xmit and recv yet) */
+ outl((tp->csr6 & ~0x00002002), ioaddr + CSR6);
+
+ /* send setup packet for cards that support it */
+ if (!(tp->flags & MC_HASH_ONLY)) {
+ /* enable transmit wait for completion */
+ outl(tp->csr6 | 0x00002000, ioaddr + CSR6);
+ /* immediate transmit demand */
+ outl(0, ioaddr + CSR1);
+
+ to = currticks() + TX_TIME_OUT;
+ while ((tx_ring[0].status & 0x80000000) && (currticks() < to))
+ /* wait */ ;
+
+ if (currticks() >= to) {
+ printf ("%s: TX Setup Timeout.\n", tp->nic_name);
+ }
+ }
+
+ if (tp->chip_id == LC82C168)
+ tulip_check_duplex(nic);
+
+ set_rx_mode(nic);
+
+ /* enable transmit and receive */
+ outl(tp->csr6 | 0x00002002, ioaddr + CSR6);
+}
+
+
+/*********************************************************************/
+/* eth_transmit - Transmit a frame */
+/*********************************************************************/
+static void tulip_transmit(struct nic *nic, const char *d, unsigned int t,
+ unsigned int s, const char *p)
+{
+ u16 nstype;
+ u32 to;
+ u32 csr6 = inl(ioaddr + CSR6);
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_transmit\n");
+#endif
+
+ /* Disable Tx */
+ outl(csr6 & ~0x00002000, ioaddr + CSR6);
+
+ memcpy(txb, d, ETH_ALEN);
+ memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) t);
+ memcpy(txb + 2 * ETH_ALEN, (u8 *)&nstype, 2);
+ memcpy(txb + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ s &= 0x0FFF;
+
+ /* pad to minimum packet size */
+ while (s < ETH_ZLEN)
+ txb[s++] = '\0';
+
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: sending %d bytes ethtype %hX\n", tp->nic_name, s, t);
+#endif
+
+ /* setup the transmit descriptor */
+ /* 0x60000000 = no interrupt on completion */
+ tx_ring[0].length = cpu_to_le32(0x60000000 | s);
+ tx_ring[0].status = cpu_to_le32(0x80000000);
+
+ /* Point to transmit descriptor */
+ outl(virt_to_le32desc(&tx_ring[0]), ioaddr + CSR4);
+
+ /* Enable Tx */
+ outl(csr6 | 0x00002000, ioaddr + CSR6);
+ /* immediate transmit demand */
+ outl(0, ioaddr + CSR1);
+
+ to = currticks() + TX_TIME_OUT;
+ while ((tx_ring[0].status & 0x80000000) && (currticks() < to))
+ /* wait */ ;
+
+ if (currticks() >= to) {
+ printf ("TX Timeout!\n");
+ }
+
+ /* Disable Tx */
+ outl(csr6 & ~0x00002000, ioaddr + CSR6);
+}
+
+/*********************************************************************/
+/* eth_poll - Wait for a frame */
+/*********************************************************************/
+static int tulip_poll(struct nic *nic, int retrieve)
+{
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_poll\n");
+#endif
+
+ /* no packet waiting. packet still owned by NIC */
+ if (rx_ring[tp->cur_rx].status & 0x80000000)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_poll got one\n");
+#endif
+
+ nic->packetlen = (rx_ring[tp->cur_rx].status & 0x3FFF0000) >> 16;
+
+ /* if we get a corrupted packet. throw it away and move on */
+ if (rx_ring[tp->cur_rx].status & 0x00008000) {
+ /* return the descriptor and buffer to receive ring */
+ rx_ring[tp->cur_rx].status = 0x80000000;
+ tp->cur_rx = (++tp->cur_rx) % RX_RING_SIZE;
+ return 0;
+ }
+
+ /* copy packet to working buffer */
+ memcpy(nic->packet, rxb + tp->cur_rx * BUFLEN, nic->packetlen);
+
+ /* return the descriptor and buffer to receive ring */
+ rx_ring[tp->cur_rx].status = 0x80000000;
+ tp->cur_rx = (++tp->cur_rx) % RX_RING_SIZE;
+
+ return 1;
+}
+
+/*********************************************************************/
+/* eth_disable - Disable the interface */
+/*********************************************************************/
+static void tulip_disable ( struct nic *nic ) {
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_disable\n");
+#endif
+
+ tulip_reset(nic);
+
+ /* disable interrupts */
+ outl(0x00000000, ioaddr + CSR7);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* Clear the missed-packet counter. */
+ (volatile unsigned long)inl(ioaddr + CSR8);
+}
+
+/*********************************************************************/
+/*IRQ - Enable, Disable, or Force interrupts */
+/*********************************************************************/
+static void tulip_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct nic_operations tulip_operations = {
+ .connect = dummy_connect,
+ .poll = tulip_poll,
+ .transmit = tulip_transmit,
+ .irq = tulip_irq,
+
+};
+
+/*********************************************************************/
+/* eth_probe - Look for an adapter */
+/*********************************************************************/
+static int tulip_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ u32 i;
+ u8 chip_rev;
+ u8 ee_data[EEPROM_SIZE];
+ unsigned short sum;
+ int chip_idx;
+ static unsigned char last_phys_addr[ETH_ALEN] = {0x00, 'L', 'i', 'n', 'u', 'x'};
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ ioaddr = pci->ioaddr;
+ nic->ioaddr = pci->ioaddr & ~3;
+ nic->irqno = 0;
+
+ /* point to private storage */
+ tp = &tulip_bss.tpx;
+
+ tp->vendor_id = pci->vendor;
+ tp->dev_id = pci->device;
+ tp->nic_name = pci->driver_name;
+
+ tp->if_port = 0;
+ tp->default_port = 0;
+
+ adjust_pci_device(pci);
+
+ /* disable interrupts */
+ outl(0x00000000, ioaddr + CSR7);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* Clear the missed-packet counter. */
+ (volatile unsigned long)inl(ioaddr + CSR8);
+
+ printf("\n"); /* so we start on a fresh line */
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_probe\n");
+#endif
+
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf ("%s: Looking for Tulip Chip: Vendor=%hX Device=%hX\n", tp->nic_name,
+ tp->vendor, tp->dev_id);
+#endif
+
+ /* Figure out which chip we're dealing with */
+ i = 0;
+ chip_idx = -1;
+
+ while (pci_id_tbl[i].name) {
+ if ( (((u32) tp->dev_id << 16) | tp->vendor_id) ==
+ (pci_id_tbl[i].id.pci & pci_id_tbl[i].id.pci_mask) ) {
+ chip_idx = pci_id_tbl[i].drv_flags;
+ break;
+ }
+ i++;
+ }
+
+ if (chip_idx == -1) {
+ printf ("%s: Unknown Tulip Chip: Vendor=%hX Device=%hX\n", tp->nic_name,
+ tp->vendor_id, tp->dev_id);
+ return 0;
+ }
+
+ tp->pci_id_idx = i;
+ tp->flags = tulip_tbl[chip_idx].flags;
+
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1) {
+ printf ("%s: tp->pci_id_idx == %d, name == %s\n", tp->nic_name,
+ tp->pci_id_idx, pci_id_tbl[tp->pci_id_idx].name);
+ printf ("%s: chip_idx == %d, name == %s\n", tp->nic_name, chip_idx,
+ tulip_tbl[chip_idx].chip_name);
+ }
+#endif
+
+ /* Bring the 21041/21143 out of sleep mode.
+ Caution: Snooze mode does not work with some boards! */
+ if (tp->flags & HAS_PWRDWN)
+ pci_write_config_dword(pci, 0x40, 0x00000000);
+
+ if (inl(ioaddr + CSR5) == 0xFFFFFFFF) {
+ printf("%s: The Tulip chip at %X is not functioning.\n",
+ tp->nic_name, (unsigned int) ioaddr);
+ return 0;
+ }
+
+ pci_read_config_byte(pci, PCI_REVISION, &chip_rev);
+
+ printf("%s: [chip: %s] rev %d at %hX\n", tp->nic_name,
+ tulip_tbl[chip_idx].chip_name, chip_rev, (unsigned int) ioaddr);
+ printf("%s: Vendor=%hX Device=%hX", tp->nic_name, tp->vendor_id, tp->dev_id);
+
+ if (chip_idx == DC21041 && inl(ioaddr + CSR9) & 0x8000) {
+ printf(" 21040 compatible mode.");
+ chip_idx = DC21040;
+ }
+
+ printf("\n");
+
+ /* The SROM/EEPROM interface varies dramatically. */
+ sum = 0;
+ if (chip_idx == DC21040) {
+ outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */
+ for (i = 0; i < ETH_ALEN; i++) {
+ int value, boguscnt = 100000;
+ do
+ value = inl(ioaddr + CSR9);
+ while (value < 0 && --boguscnt > 0);
+ nic->node_addr[i] = value;
+ sum += value & 0xff;
+ }
+ } else if (chip_idx == LC82C168) {
+ for (i = 0; i < 3; i++) {
+ int value, boguscnt = 100000;
+ outl(0x600 | i, ioaddr + 0x98);
+ do
+ value = inl(ioaddr + CSR9);
+ while (value < 0 && --boguscnt > 0);
+ put_unaligned(le16_to_cpu(value), ((u16*)nic->node_addr) + i);
+ sum += value & 0xffff;
+ }
+ } else if (chip_idx == COMET) {
+ /* No need to read the EEPROM. */
+ put_unaligned(inl(ioaddr + 0xA4), (u32 *)nic->node_addr);
+ put_unaligned(inl(ioaddr + 0xA8), (u16 *)(nic->node_addr + 4));
+ for (i = 0; i < ETH_ALEN; i ++)
+ sum += nic->node_addr[i];
+ } else {
+ /* A serial EEPROM interface, we read now and sort it out later. */
+ int sa_offset = 0;
+ int ee_addr_size = read_eeprom(ioaddr, 0xff, 8) & 0x40000 ? 8 : 6;
+
+ for (i = 0; i < sizeof(ee_data)/2; i++)
+ ((u16 *)ee_data)[i] =
+ le16_to_cpu(read_eeprom(ioaddr, i, ee_addr_size));
+
+ /* DEC now has a specification (see Notes) but early board makers
+ just put the address in the first EEPROM locations. */
+ /* This does memcmp(eedata, eedata+16, 8) */
+ for (i = 0; i < 8; i ++)
+ if (ee_data[i] != ee_data[16+i])
+ sa_offset = 20;
+ if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) {
+ sa_offset = 2; /* Grrr, damn Matrox boards. */
+ }
+ for (i = 0; i < ETH_ALEN; i ++) {
+ nic->node_addr[i] = ee_data[i + sa_offset];
+ sum += ee_data[i + sa_offset];
+ }
+ }
+ /* Lite-On boards have the address byte-swapped. */
+ if ((nic->node_addr[0] == 0xA0 || nic->node_addr[0] == 0xC0)
+ && nic->node_addr[1] == 0x00)
+ for (i = 0; i < ETH_ALEN; i+=2) {
+ char tmp = nic->node_addr[i];
+ nic->node_addr[i] = nic->node_addr[i+1];
+ nic->node_addr[i+1] = tmp;
+ }
+
+ if (sum == 0 || sum == ETH_ALEN*0xff) {
+ printf("%s: EEPROM not present!\n", tp->nic_name);
+ for (i = 0; i < ETH_ALEN-1; i++)
+ nic->node_addr[i] = last_phys_addr[i];
+ nic->node_addr[i] = last_phys_addr[i] + 1;
+ }
+
+ for (i = 0; i < ETH_ALEN; i++)
+ last_phys_addr[i] = nic->node_addr[i];
+
+ DBG ( "%s: %s at ioaddr %hX\n", tp->nic_name, eth_ntoa ( nic->node_addr ),
+ (unsigned int) ioaddr );
+
+ tp->chip_id = chip_idx;
+ tp->revision = chip_rev;
+ tp->csr0 = csr0;
+
+ /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
+ And the ASIX must have a burst limit or horrible things happen. */
+ if (chip_idx == DC21143 && chip_rev == 65)
+ tp->csr0 &= ~0x01000000;
+ else if (tp->flags & IS_ASIX)
+ tp->csr0 |= 0x2000;
+
+ if (media_cap[tp->default_port] & MediaIsMII) {
+ static const u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60,
+ 0x80, 0x100, 0x200 };
+ tp->mii_advertise = media2advert[tp->default_port - 9];
+ tp->mii_advertise |= (tp->flags & HAS_8023X); /* Matching bits! */
+ }
+
+ /* This is logically part of the probe routine, but too complex
+ to write inline. */
+ if (tp->flags & HAS_MEDIA_TABLE) {
+ memcpy(tp->eeprom, ee_data, sizeof(tp->eeprom));
+ parse_eeprom(nic);
+ }
+
+ start_link(nic);
+
+ /* reset the device and make ready for tx and rx of packets */
+ tulip_reset(nic);
+ nic->nic_op = &tulip_operations;
+
+ /* give the board a chance to reset before returning */
+ tulip_wait(4*TICKS_PER_SEC);
+
+ return 1;
+}
+
+static void start_link(struct nic *nic)
+{
+ int i;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("start_link\n");
+#endif
+
+ if ((tp->flags & ALWAYS_CHECK_MII) ||
+ (tp->mtable && tp->mtable->has_mii) ||
+ ( ! tp->mtable && (tp->flags & HAS_MII))) {
+ unsigned int phy, phy_idx;
+ if (tp->mtable && tp->mtable->has_mii) {
+ for (i = 0; i < tp->mtable->leafcount; i++)
+ if (tp->mtable->mleaf[i].media == 11) {
+ tp->cur_index = i;
+ tp->saved_if_port = tp->if_port;
+ select_media(nic, 2);
+ tp->if_port = tp->saved_if_port;
+ break;
+ }
+ }
+
+ /* Find the connected MII xcvrs. */
+ for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
+ phy++) {
+ int mii_status = mdio_read(nic, phy, 1);
+ if ((mii_status & 0x8301) == 0x8001 ||
+ ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) {
+ int mii_reg0 = mdio_read(nic, phy, 0);
+ int mii_advert = mdio_read(nic, phy, 4);
+ int to_advert;
+
+ if (tp->mii_advertise)
+ to_advert = tp->mii_advertise;
+ else if (tp->advertising[phy_idx])
+ to_advert = tp->advertising[phy_idx];
+ else /* Leave unchanged. */
+ tp->mii_advertise = to_advert = mii_advert;
+
+ tp->phys[phy_idx++] = phy;
+ printf("%s: MII transceiver %d config %hX status %hX advertising %hX.\n",
+ tp->nic_name, phy, mii_reg0, mii_status, mii_advert);
+ /* Fixup for DLink with miswired PHY. */
+ if (mii_advert != to_advert) {
+ printf("%s: Advertising %hX on PHY %d previously advertising %hX.\n",
+ tp->nic_name, to_advert, phy, mii_advert);
+ mdio_write(nic, phy, 4, to_advert);
+ }
+ /* Enable autonegotiation: some boards default to off. */
+ mdio_write(nic, phy, 0, mii_reg0 |
+ (tp->full_duplex ? 0x1100 : 0x1000) |
+ (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0));
+ }
+ }
+ tp->mii_cnt = phy_idx;
+ if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) {
+ printf("%s: ***WARNING***: No MII transceiver found!\n",
+ tp->nic_name);
+ tp->phys[0] = 1;
+ }
+ }
+
+ /* Reset the xcvr interface and turn on heartbeat. */
+ switch (tp->chip_id) {
+ case DC21040:
+ outl(0x00000000, ioaddr + CSR13);
+ outl(0x00000004, ioaddr + CSR13);
+ break;
+ case DC21041:
+ /* This is nway_start(). */
+ if (tp->sym_advertise == 0)
+ tp->sym_advertise = 0x0061;
+ outl(0x00000000, ioaddr + CSR13);
+ outl(0xFFFFFFFF, ioaddr + CSR14);
+ outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
+ outl(inl(ioaddr + CSR6) | 0x0200, ioaddr + CSR6);
+ outl(0x0000EF01, ioaddr + CSR13);
+ break;
+ case DC21140: default:
+ if (tp->mtable)
+ outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
+ break;
+ case DC21142:
+ case PNIC2:
+ if (tp->mii_cnt || media_cap[tp->if_port] & MediaIsMII) {
+ outl(0x82020000, ioaddr + CSR6);
+ outl(0x0000, ioaddr + CSR13);
+ outl(0x0000, ioaddr + CSR14);
+ outl(0x820E0000, ioaddr + CSR6);
+ } else
+ nway_start(nic);
+ break;
+ case LC82C168:
+ if ( ! tp->mii_cnt) {
+ tp->nway = 1;
+ tp->nwayset = 0;
+ outl(0x00420000, ioaddr + CSR6);
+ outl(0x30, ioaddr + CSR12);
+ outl(0x0001F078, ioaddr + 0xB8);
+ outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
+ }
+ break;
+ case MX98713: case COMPEX9881:
+ outl(0x00000000, ioaddr + CSR6);
+ outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
+ outl(0x00000001, ioaddr + CSR13);
+ break;
+ case MX98715: case MX98725:
+ outl(0x01a80000, ioaddr + CSR6);
+ outl(0xFFFFFFFF, ioaddr + CSR14);
+ outl(0x00001000, ioaddr + CSR12);
+ break;
+ case COMET:
+ /* No initialization necessary. */
+ break;
+ }
+}
+
+static void nway_start(struct nic *nic __unused)
+{
+ int csr14 = ((tp->sym_advertise & 0x0780) << 9) |
+ ((tp->sym_advertise&0x0020)<<1) | 0xffbf;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("nway_start\n");
+#endif
+
+ tp->if_port = 0;
+ tp->nway = tp->mediasense = 1;
+ tp->nwayset = tp->lpar = 0;
+ if (tp->chip_id == PNIC2) {
+ tp->csr6 = 0x01000000 | (tp->sym_advertise & 0x0040 ? 0x0200 : 0);
+ return;
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Restarting internal NWay autonegotiation, %X.\n",
+ tp->nic_name, csr14);
+#endif
+ outl(0x0001, ioaddr + CSR13);
+ outl(csr14, ioaddr + CSR14);
+ tp->csr6 = 0x82420000 | (tp->sym_advertise & 0x0040 ? 0x0200 : 0);
+ outl(tp->csr6, ioaddr + CSR6);
+ if (tp->mtable && tp->mtable->csr15dir) {
+ outl(tp->mtable->csr15dir, ioaddr + CSR15);
+ outl(tp->mtable->csr15val, ioaddr + CSR15);
+ } else if (tp->chip_id != PNIC2)
+ outw(0x0008, ioaddr + CSR15);
+ if (tp->chip_id == DC21041) /* Trigger NWAY. */
+ outl(0xEF01, ioaddr + CSR12);
+ else
+ outl(0x1301, ioaddr + CSR12);
+}
+
+static void init_media(struct nic *nic)
+{
+ int i;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("init_media\n");
+#endif
+
+ tp->saved_if_port = tp->if_port;
+ if (tp->if_port == 0)
+ tp->if_port = tp->default_port;
+
+ /* Allow selecting a default media. */
+ i = 0;
+ if (tp->mtable == NULL)
+ goto media_picked;
+ if (tp->if_port) {
+ int looking_for = media_cap[tp->if_port] & MediaIsMII ? 11 :
+ (tp->if_port == 12 ? 0 : tp->if_port);
+ for (i = 0; i < tp->mtable->leafcount; i++)
+ if (tp->mtable->mleaf[i].media == looking_for) {
+ printf("%s: Using user-specified media %s.\n",
+ tp->nic_name, medianame[tp->if_port]);
+ goto media_picked;
+ }
+ }
+ if ((tp->mtable->defaultmedia & 0x0800) == 0) {
+ int looking_for = tp->mtable->defaultmedia & 15;
+ for (i = 0; i < tp->mtable->leafcount; i++)
+ if (tp->mtable->mleaf[i].media == looking_for) {
+ printf("%s: Using EEPROM-set media %s.\n",
+ tp->nic_name, medianame[looking_for]);
+ goto media_picked;
+ }
+ }
+ /* Start sensing first non-full-duplex media. */
+ for (i = tp->mtable->leafcount - 1;
+ (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--)
+ ;
+ media_picked:
+
+ tp->csr6 = 0;
+ tp->cur_index = i;
+ tp->nwayset = 0;
+
+ if (tp->if_port) {
+ if (tp->chip_id == DC21143 && media_cap[tp->if_port] & MediaIsMII) {
+ /* We must reset the media CSRs when we force-select MII mode. */
+ outl(0x0000, ioaddr + CSR13);
+ outl(0x0000, ioaddr + CSR14);
+ outl(0x0008, ioaddr + CSR15);
+ }
+ select_media(nic, 1);
+ return;
+ }
+ switch(tp->chip_id) {
+ case DC21041:
+ /* tp->nway = 1;*/
+ nway_start(nic);
+ break;
+ case DC21142:
+ if (tp->mii_cnt) {
+ select_media(nic, 1);
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Using MII transceiver %d, status %hX.\n",
+ tp->nic_name, tp->phys[0], mdio_read(nic, tp->phys[0], 1));
+#endif
+ outl(0x82020000, ioaddr + CSR6);
+ tp->csr6 = 0x820E0000;
+ tp->if_port = 11;
+ outl(0x0000, ioaddr + CSR13);
+ outl(0x0000, ioaddr + CSR14);
+ } else
+ nway_start(nic);
+ break;
+ case PNIC2:
+ nway_start(nic);
+ break;
+ case LC82C168:
+ if (tp->mii_cnt) {
+ tp->if_port = 11;
+ tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0);
+ outl(0x0001, ioaddr + CSR15);
+ } else if (inl(ioaddr + CSR5) & TPLnkPass)
+ pnic_do_nway(nic);
+ else {
+ /* Start with 10mbps to do autonegotiation. */
+ outl(0x32, ioaddr + CSR12);
+ tp->csr6 = 0x00420000;
+ outl(0x0001B078, ioaddr + 0xB8);
+ outl(0x0201B078, ioaddr + 0xB8);
+ }
+ break;
+ case MX98713: case COMPEX9881:
+ tp->if_port = 0;
+ tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0);
+ outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
+ break;
+ case MX98715: case MX98725:
+ /* Provided by BOLO, Macronix - 12/10/1998. */
+ tp->if_port = 0;
+ tp->csr6 = 0x01a80200;
+ outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
+ outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0);
+ break;
+ case COMET:
+ /* Enable automatic Tx underrun recovery */
+ outl(inl(ioaddr + 0x88) | 1, ioaddr + 0x88);
+ tp->if_port = 0;
+ tp->csr6 = 0x00040000;
+ break;
+ case AX88140: case AX88141:
+ tp->csr6 = tp->mii_cnt ? 0x00040100 : 0x00000100;
+ break;
+ default:
+ select_media(nic, 1);
+ }
+}
+
+static void pnic_do_nway(struct nic *nic __unused)
+{
+ u32 phy_reg = inl(ioaddr + 0xB8);
+ u32 new_csr6 = tp->csr6 & ~0x40C40200;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("pnic_do_nway\n");
+#endif
+
+ if (phy_reg & 0x78000000) { /* Ignore baseT4 */
+ if (phy_reg & 0x20000000) tp->if_port = 5;
+ else if (phy_reg & 0x40000000) tp->if_port = 3;
+ else if (phy_reg & 0x10000000) tp->if_port = 4;
+ else if (phy_reg & 0x08000000) tp->if_port = 0;
+ tp->nwayset = 1;
+ new_csr6 = (tp->if_port & 1) ? 0x01860000 : 0x00420000;
+ outl(0x32 | (tp->if_port & 1), ioaddr + CSR12);
+ if (tp->if_port & 1)
+ outl(0x1F868, ioaddr + 0xB8);
+ if (phy_reg & 0x30000000) {
+ tp->full_duplex = 1;
+ new_csr6 |= 0x00000200;
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: PNIC autonegotiated status %X, %s.\n",
+ tp->nic_name, phy_reg, medianame[tp->if_port]);
+#endif
+ if (tp->csr6 != new_csr6) {
+ tp->csr6 = new_csr6;
+ outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */
+ outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ }
+ }
+}
+
+/* Set up the transceiver control registers for the selected media type. */
+static void select_media(struct nic *nic, int startup)
+{
+ struct mediatable *mtable = tp->mtable;
+ u32 new_csr6;
+ int i;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("select_media\n");
+#endif
+
+ if (mtable) {
+ struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
+ unsigned char *p = mleaf->leafdata;
+ switch (mleaf->type) {
+ case 0: /* 21140 non-MII xcvr. */
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Using a 21140 non-MII transceiver"
+ " with control setting %hhX.\n",
+ tp->nic_name, p[1]);
+#endif
+ tp->if_port = p[0];
+ if (startup)
+ outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+ outl(p[1], ioaddr + CSR12);
+ new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
+ break;
+ case 2: case 4: {
+ u16 setup[5];
+ u32 csr13val, csr14val, csr15dir, csr15val;
+ for (i = 0; i < 5; i++)
+ setup[i] = get_u16(&p[i*2 + 1]);
+
+ tp->if_port = p[0] & 15;
+ if (media_cap[tp->if_port] & MediaAlwaysFD)
+ tp->full_duplex = 1;
+
+ if (startup && mtable->has_reset) {
+ struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
+ unsigned char *rst = rleaf->leafdata;
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Resetting the transceiver.\n",
+ tp->nic_name);
+#endif
+ for (i = 0; i < rst[0]; i++)
+ outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: 21143 non-MII %s transceiver control "
+ "%hX/%hX.\n",
+ tp->nic_name, medianame[tp->if_port], setup[0], setup[1]);
+#endif
+ if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */
+ csr13val = setup[0];
+ csr14val = setup[1];
+ csr15dir = (setup[3]<<16) | setup[2];
+ csr15val = (setup[4]<<16) | setup[2];
+ outl(0, ioaddr + CSR13);
+ outl(csr14val, ioaddr + CSR14);
+ outl(csr15dir, ioaddr + CSR15); /* Direction */
+ outl(csr15val, ioaddr + CSR15); /* Data */
+ outl(csr13val, ioaddr + CSR13);
+ } else {
+ csr13val = 1;
+ csr14val = 0x0003FF7F;
+ csr15dir = (setup[0]<<16) | 0x0008;
+ csr15val = (setup[1]<<16) | 0x0008;
+ if (tp->if_port <= 4)
+ csr14val = t21142_csr14[tp->if_port];
+ if (startup) {
+ outl(0, ioaddr + CSR13);
+ outl(csr14val, ioaddr + CSR14);
+ }
+ outl(csr15dir, ioaddr + CSR15); /* Direction */
+ outl(csr15val, ioaddr + CSR15); /* Data */
+ if (startup) outl(csr13val, ioaddr + CSR13);
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Setting CSR15 to %X/%X.\n",
+ tp->nic_name, csr15dir, csr15val);
+#endif
+ if (mleaf->type == 4)
+ new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
+ else
+ new_csr6 = 0x82420000;
+ break;
+ }
+ case 1: case 3: {
+ int phy_num = p[0];
+ int init_length = p[1];
+ u16 *misc_info;
+
+ tp->if_port = 11;
+ new_csr6 = 0x020E0000;
+ if (mleaf->type == 3) { /* 21142 */
+ u16 *init_sequence = (u16*)(p+2);
+ u16 *reset_sequence = &((u16*)(p+3))[init_length];
+ int reset_length = p[2 + init_length*2];
+ misc_info = reset_sequence + reset_length;
+ if (startup)
+ for (i = 0; i < reset_length; i++)
+ outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);
+ for (i = 0; i < init_length; i++)
+ outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);
+ } else {
+ u8 *init_sequence = p + 2;
+ u8 *reset_sequence = p + 3 + init_length;
+ int reset_length = p[2 + init_length];
+ misc_info = (u16*)(reset_sequence + reset_length);
+ if (startup) {
+ outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+ for (i = 0; i < reset_length; i++)
+ outl(reset_sequence[i], ioaddr + CSR12);
+ }
+ for (i = 0; i < init_length; i++)
+ outl(init_sequence[i], ioaddr + CSR12);
+ }
+ tp->advertising[phy_num] = get_u16(&misc_info[1]) | 1;
+ if (startup < 2) {
+ if (tp->mii_advertise == 0)
+ tp->mii_advertise = tp->advertising[phy_num];
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Advertising %hX on MII %d.\n",
+ tp->nic_name, tp->mii_advertise, tp->phys[phy_num]);
+#endif
+ mdio_write(nic, tp->phys[phy_num], 4, tp->mii_advertise);
+ }
+ break;
+ }
+ default:
+ printf("%s: Invalid media table selection %d.\n",
+ tp->nic_name, mleaf->type);
+ new_csr6 = 0x020E0000;
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Using media type %s, CSR12 is %hhX.\n",
+ tp->nic_name, medianame[tp->if_port],
+ inl(ioaddr + CSR12) & 0xff);
+#endif
+ } else if (tp->chip_id == DC21041) {
+ int port = tp->if_port <= 4 ? tp->if_port : 0;
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: 21041 using media %s, CSR12 is %hX.\n",
+ tp->nic_name, medianame[port == 3 ? 12: port],
+ inl(ioaddr + CSR12));
+#endif
+ outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+ outl(t21041_csr14[port], ioaddr + CSR14);
+ outl(t21041_csr15[port], ioaddr + CSR15);
+ outl(t21041_csr13[port], ioaddr + CSR13);
+ new_csr6 = 0x80020000;
+ } else if (tp->chip_id == LC82C168) {
+ if (startup && ! tp->medialock)
+ tp->if_port = tp->mii_cnt ? 11 : 0;
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: PNIC PHY status is %hX, media %s.\n",
+ tp->nic_name, inl(ioaddr + 0xB8), medianame[tp->if_port]);
+#endif
+ if (tp->mii_cnt) {
+ new_csr6 = 0x810C0000;
+ outl(0x0001, ioaddr + CSR15);
+ outl(0x0201B07A, ioaddr + 0xB8);
+ } else if (startup) {
+ /* Start with 10mbps to do autonegotiation. */
+ outl(0x32, ioaddr + CSR12);
+ new_csr6 = 0x00420000;
+ outl(0x0001B078, ioaddr + 0xB8);
+ outl(0x0201B078, ioaddr + 0xB8);
+ } else if (tp->if_port == 3 || tp->if_port == 5) {
+ outl(0x33, ioaddr + CSR12);
+ new_csr6 = 0x01860000;
+ /* Trigger autonegotiation. */
+ outl(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8);
+ } else {
+ outl(0x32, ioaddr + CSR12);
+ new_csr6 = 0x00420000;
+ outl(0x1F078, ioaddr + 0xB8);
+ }
+ } else if (tp->chip_id == DC21040) { /* 21040 */
+ /* Turn on the xcvr interface. */
+#ifdef TULIP_DEBUG
+ int csr12 = inl(ioaddr + CSR12);
+ if (tulip_debug > 1)
+ printf("%s: 21040 media type is %s, CSR12 is %hhX.\n",
+ tp->nic_name, medianame[tp->if_port], csr12);
+#endif
+ if (media_cap[tp->if_port] & MediaAlwaysFD)
+ tp->full_duplex = 1;
+ new_csr6 = 0x20000;
+ /* Set the full duplux match frame. */
+ outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
+ outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+ if (t21040_csr13[tp->if_port] & 8) {
+ outl(0x0705, ioaddr + CSR14);
+ outl(0x0006, ioaddr + CSR15);
+ } else {
+ outl(0xffff, ioaddr + CSR14);
+ outl(0x0000, ioaddr + CSR15);
+ }
+ outl(0x8f01 | t21040_csr13[tp->if_port], ioaddr + CSR13);
+ } else { /* Unknown chip type with no media table. */
+ if (tp->default_port == 0)
+ tp->if_port = tp->mii_cnt ? 11 : 3;
+ if (media_cap[tp->if_port] & MediaIsMII) {
+ new_csr6 = 0x020E0000;
+ } else if (media_cap[tp->if_port] & MediaIsFx) {
+ new_csr6 = 0x028600000;
+ } else
+ new_csr6 = 0x038600000;
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: No media description table, assuming "
+ "%s transceiver, CSR12 %hhX.\n",
+ tp->nic_name, medianame[tp->if_port],
+ inl(ioaddr + CSR12));
+#endif
+ }
+
+ tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
+ return;
+}
+
+/*
+ Check the MII negotiated duplex and change the CSR6 setting if
+ required.
+ Return 0 if everything is OK.
+ Return < 0 if the transceiver is missing or has no link beat.
+*/
+static int tulip_check_duplex(struct nic *nic)
+{
+ unsigned int bmsr, lpa, negotiated, new_csr6;
+
+ bmsr = mdio_read(nic, tp->phys[0], 1);
+ lpa = mdio_read(nic, tp->phys[0], 5);
+
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: MII status %#x, Link partner report "
+ "%#x.\n", tp->nic_name, bmsr, lpa);
+#endif
+
+ if (bmsr == 0xffff)
+ return -2;
+ if ((bmsr & 4) == 0) {
+ int new_bmsr = mdio_read(nic, tp->phys[0], 1);
+ if ((new_bmsr & 4) == 0) {
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: No link beat on the MII interface,"
+ " status %#x.\n", tp->nic_name,
+ new_bmsr);
+#endif
+ return -1;
+ }
+ }
+ tp->full_duplex = lpa & 0x140;
+
+ new_csr6 = tp->csr6;
+ negotiated = lpa & tp->advertising[0];
+
+ if(negotiated & 0x380) new_csr6 &= ~0x400000;
+ else new_csr6 |= 0x400000;
+ if (tp->full_duplex) new_csr6 |= 0x200;
+ else new_csr6 &= ~0x200;
+
+ if (new_csr6 != tp->csr6) {
+ tp->csr6 = new_csr6;
+
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 0)
+ printf("%s: Setting %s-duplex based on MII"
+ "#%d link partner capability of %#x.\n",
+ tp->nic_name,
+ tp->full_duplex ? "full" : "half",
+ tp->phys[0], lpa);
+#endif
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct pci_device_id tulip_nics[] = {
+PCI_ROM(0x1011, 0x0002, "dc21040", "Digital Tulip"),
+PCI_ROM(0x1011, 0x0009, "ds21140", "Digital Tulip Fast"),
+PCI_ROM(0x1011, 0x0014, "dc21041", "Digital Tulip+"),
+PCI_ROM(0x1011, 0x0019, "ds21142", "Digital Tulip 21142"),
+PCI_ROM(0x10b7, 0x9300, "3csoho100b-tx","3ComSOHO100B-TX"),
+PCI_ROM(0x10b9, 0x5261, "ali1563", "ALi 1563 integrated ethernet"),
+PCI_ROM(0x10d9, 0x0512, "mx98713", "Macronix MX987x3"),
+PCI_ROM(0x10d9, 0x0531, "mx98715", "Macronix MX987x5"),
+PCI_ROM(0x1113, 0x1217, "mxic-98715", "Macronix MX987x5"),
+PCI_ROM(0x11ad, 0xc115, "lc82c115", "LinkSys LNE100TX"),
+PCI_ROM(0x11ad, 0x0002, "82c168", "Netgear FA310TX"),
+PCI_ROM(0x1282, 0x9100, "dm9100", "Davicom 9100"),
+PCI_ROM(0x1282, 0x9102, "dm9102", "Davicom 9102"),
+PCI_ROM(0x1282, 0x9009, "dm9009", "Davicom 9009"),
+PCI_ROM(0x1282, 0x9132, "dm9132", "Davicom 9132"),
+PCI_ROM(0x1317, 0x0985, "centaur-p", "ADMtek Centaur-P"),
+PCI_ROM(0x1317, 0x0981, "an981", "ADMtek AN981 Comet"), /* ADMTek Centaur-P (stmicro) */
+PCI_ROM(0x1113, 0x1216, "an983", "ADMTek AN983 Comet"),
+PCI_ROM(0x1317, 0x9511, "an983b", "ADMTek Comet 983b"),
+PCI_ROM(0x1317, 0x1985, "centaur-c", "ADMTek Centaur-C"),
+PCI_ROM(0x8086, 0x0039, "intel21145", "Intel Tulip"),
+PCI_ROM(0x125b, 0x1400, "ax88140", "ASIX AX88140"),
+PCI_ROM(0x11f6, 0x9881, "rl100tx", "Compex RL100-TX"),
+PCI_ROM(0x115d, 0x0003, "xircomtulip", "Xircom Tulip"),
+PCI_ROM(0x104a, 0x0981, "tulip-0981", "Tulip 0x104a 0x0981"),
+PCI_ROM(0x104a, 0x2774, "SGThomson-STE10100A", "Tulip 0x104a 0x2774"), /*Modified by Ramesh Chander*/
+PCI_ROM(0x1113, 0x9511, "tulip-9511", "Tulip 0x1113 0x9511"),
+PCI_ROM(0x1186, 0x1561, "tulip-1561", "Tulip 0x1186 0x1561"),
+PCI_ROM(0x1259, 0xa120, "tulip-a120", "Tulip 0x1259 0xa120"),
+PCI_ROM(0x13d1, 0xab02, "tulip-ab02", "Tulip 0x13d1 0xab02"),
+PCI_ROM(0x13d1, 0xab03, "tulip-ab03", "Tulip 0x13d1 0xab03"),
+PCI_ROM(0x13d1, 0xab08, "tulip-ab08", "Tulip 0x13d1 0xab08"),
+PCI_ROM(0x14f1, 0x1803, "lanfinity", "Conexant LANfinity"),
+PCI_ROM(0x1626, 0x8410, "tulip-8410", "Tulip 0x1626 0x8410"),
+PCI_ROM(0x1737, 0xab08, "tulip-1737-ab08","Tulip 0x1737 0xab08"),
+PCI_ROM(0x1737, 0xab09, "tulip-ab09", "Tulip 0x1737 0xab09"),
+};
+
+PCI_DRIVER ( tulip_driver, tulip_nics, PCI_NO_CLASS );
+
+DRIVER ( "Tulip", nic_driver, pci_driver, tulip_driver,
+ tulip_probe, tulip_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/tulip.txt b/gpxe/src/drivers/net/tulip.txt
new file mode 100644
index 00000000..b4f6756d
--- /dev/null
+++ b/gpxe/src/drivers/net/tulip.txt
@@ -0,0 +1,54 @@
+This software may be used and distributed according to the terms of
+the GNU Public License, incorporated herein by reference.
+
+This is a tulip and clone driver for Etherboot. See the revision
+history in the tulip.c file for information on changes. This version
+of the driver incorporates changes from Bob Edwards and Paul Mackerras
+who cantributed changes to support the TRENDnet TE100-PCIA NIC which
+uses a genuine Intel 21143-PD chipset. There are also various code
+cleanups to make time-based activities more reliable.
+
+Of course you have to have all the usual Etherboot environment
+(bootp/dhcp/NFS) set up, and you need a Linux kernel with v0.91g
+(7.16.99) or later of the tulip.c driver compiled in to support some
+MX98715 based cards. That file is available at:
+
+ http://cesdis.gsfc.nasa.gov/linux/drivers/test/tulip.c
+
+NOTES
+
+I've tested this driver with a SOHOware Fast 10/100 Model SDA110A,
+a Linksys LNE100TX v2.0, and a Netgear FA310TX card, and it worked at
+both 10 and 100 mbits. Other cards based on the tulip family may work as
+well.
+
+These cards are about 20$US, are supported by Linux and now Etherboot,
+and being PCI, they auto-configure IRQ and IOADDR and auto-negotiate
+10/100 half/full duplex. It seems like a pretty good value compared to
+some of the pricier cards, and can lower the cost of building/adapting
+thin client workstations substantially while giving a considerable
+performance increase.
+
+On some PCI tulip clone chipsets (MX987x5, LC82C115, LC82C168) this driver
+lets the card choose the fastest speed it can negotiate with the peer
+device. On other cards, it chooses 10mbit half-duplex.
+
+I burned an AM27C256 (32KByte) EPROM with mx987x5.lzrom and it worked.
+According to the data sheet the MX98715A supports up to 64K (27C512)
+EPROMs,
+
+I've liberally commented the code and header files in the hope that it
+will help the next person who hacks the code or needs to support some
+tulip clone card, or wishes to add functionality.
+
+Anyway, please test this if you can on your tulip based card, and let
+me (mdc@etherboot.org) and the Etherboot-Discuss list
+(etherboot-discuss@lists.sourceforge.net) know how things go. I also
+would appreciate code review by people who program. I'm a strong
+believer in "another set of eyes".
+
+Regards,
+
+Marty Connor
+mdc@etherboot.org
+http://www.etherboot.org/
diff --git a/gpxe/src/drivers/net/via-rhine.c b/gpxe/src/drivers/net/via-rhine.c
new file mode 100644
index 00000000..81350091
--- /dev/null
+++ b/gpxe/src/drivers/net/via-rhine.c
@@ -0,0 +1,1443 @@
+/* rhine.c:Fast Ethernet driver for Linux. */
+/*
+ Adapted 09-jan-2000 by Paolo Marini (paolom@prisma-eng.it)
+
+ originally written by Donald Becker.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+ Drivers derived from this code also fall under the GPL and must retain
+ this authorship and copyright notice.
+
+ Under no circumstances are the authors responsible for
+ the proper functioning of this software, nor do the authors assume any
+ responsibility for damages incurred with its use.
+
+ This driver is designed for the VIA VT86C100A Rhine-II PCI Fast Ethernet
+ controller.
+
+*/
+
+static const char *version = "rhine.c v1.0.2 2004-10-29\n";
+
+/* A few user-configurable values. */
+
+// max time out delay time
+#define W_MAX_TIMEOUT 0x0FFFU
+
+/* Size of the in-memory receive ring. */
+#define RX_BUF_LEN_IDX 3 /* 0==8K, 1==16K, 2==32K, 3==64K */
+#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX)
+
+/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
+#define TX_BUF_SIZE 1536
+#define RX_BUF_SIZE 1536
+
+/* PCI Tuning Parameters
+ Threshold is bytes transferred to chip before transmission starts. */
+#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
+
+/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024. */
+#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */
+#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */
+#define TX_DMA_BURST 4
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT ((2000*HZ)/1000)
+
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+
+/* define all ioaddr */
+
+#define byPAR0 ioaddr
+#define byRCR ioaddr + 6
+#define byTCR ioaddr + 7
+#define byCR0 ioaddr + 8
+#define byCR1 ioaddr + 9
+#define byISR0 ioaddr + 0x0c
+#define byISR1 ioaddr + 0x0d
+#define byIMR0 ioaddr + 0x0e
+#define byIMR1 ioaddr + 0x0f
+#define byMAR0 ioaddr + 0x10
+#define byMAR1 ioaddr + 0x11
+#define byMAR2 ioaddr + 0x12
+#define byMAR3 ioaddr + 0x13
+#define byMAR4 ioaddr + 0x14
+#define byMAR5 ioaddr + 0x15
+#define byMAR6 ioaddr + 0x16
+#define byMAR7 ioaddr + 0x17
+#define dwCurrentRxDescAddr ioaddr + 0x18
+#define dwCurrentTxDescAddr ioaddr + 0x1c
+#define dwCurrentRDSE0 ioaddr + 0x20
+#define dwCurrentRDSE1 ioaddr + 0x24
+#define dwCurrentRDSE2 ioaddr + 0x28
+#define dwCurrentRDSE3 ioaddr + 0x2c
+#define dwNextRDSE0 ioaddr + 0x30
+#define dwNextRDSE1 ioaddr + 0x34
+#define dwNextRDSE2 ioaddr + 0x38
+#define dwNextRDSE3 ioaddr + 0x3c
+#define dwCurrentTDSE0 ioaddr + 0x40
+#define dwCurrentTDSE1 ioaddr + 0x44
+#define dwCurrentTDSE2 ioaddr + 0x48
+#define dwCurrentTDSE3 ioaddr + 0x4c
+#define dwNextTDSE0 ioaddr + 0x50
+#define dwNextTDSE1 ioaddr + 0x54
+#define dwNextTDSE2 ioaddr + 0x58
+#define dwNextTDSE3 ioaddr + 0x5c
+#define dwCurrRxDMAPtr ioaddr + 0x60
+#define dwCurrTxDMAPtr ioaddr + 0x64
+#define byMPHY ioaddr + 0x6c
+#define byMIISR ioaddr + 0x6d
+#define byBCR0 ioaddr + 0x6e
+#define byBCR1 ioaddr + 0x6f
+#define byMIICR ioaddr + 0x70
+#define byMIIAD ioaddr + 0x71
+#define wMIIDATA ioaddr + 0x72
+#define byEECSR ioaddr + 0x74
+#define byTEST ioaddr + 0x75
+#define byGPIO ioaddr + 0x76
+#define byCFGA ioaddr + 0x78
+#define byCFGB ioaddr + 0x79
+#define byCFGC ioaddr + 0x7a
+#define byCFGD ioaddr + 0x7b
+#define wTallyCntMPA ioaddr + 0x7c
+#define wTallyCntCRC ioaddr + 0x7d
+#define bySTICKHW ioaddr + 0x83
+#define byWOLcrClr ioaddr + 0xA4
+#define byWOLcgClr ioaddr + 0xA7
+#define byPwrcsrClr ioaddr + 0xAC
+
+/*--------------------- Exioaddr Definitions -------------------------*/
+
+/*
+ * Bits in the RCR register
+ */
+
+#define RCR_RRFT2 0x80
+#define RCR_RRFT1 0x40
+#define RCR_RRFT0 0x20
+#define RCR_PROM 0x10
+#define RCR_AB 0x08
+#define RCR_AM 0x04
+#define RCR_AR 0x02
+#define RCR_SEP 0x01
+
+/*
+ * Bits in the TCR register
+ */
+
+#define TCR_RTSF 0x80
+#define TCR_RTFT1 0x40
+#define TCR_RTFT0 0x20
+#define TCR_OFSET 0x08
+#define TCR_LB1 0x04 /* loopback[1] */
+#define TCR_LB0 0x02 /* loopback[0] */
+
+/*
+ * Bits in the CR0 register
+ */
+
+#define CR0_RDMD 0x40 /* rx descriptor polling demand */
+#define CR0_TDMD 0x20 /* tx descriptor polling demand */
+#define CR0_TXON 0x10
+#define CR0_RXON 0x08
+#define CR0_STOP 0x04 /* stop NIC, default = 1 */
+#define CR0_STRT 0x02 /* start NIC */
+#define CR0_INIT 0x01 /* start init process */
+
+
+/*
+ * Bits in the CR1 register
+ */
+
+#define CR1_SFRST 0x80 /* software reset */
+#define CR1_RDMD1 0x40 /* RDMD1 */
+#define CR1_TDMD1 0x20 /* TDMD1 */
+#define CR1_KEYPAG 0x10 /* turn on par/key */
+#define CR1_DPOLL 0x08 /* disable rx/tx auto polling */
+#define CR1_FDX 0x04 /* full duplex mode */
+#define CR1_ETEN 0x02 /* early tx mode */
+#define CR1_EREN 0x01 /* early rx mode */
+
+/*
+ * Bits in the CR register
+ */
+
+#define CR_RDMD 0x0040 /* rx descriptor polling demand */
+#define CR_TDMD 0x0020 /* tx descriptor polling demand */
+#define CR_TXON 0x0010
+#define CR_RXON 0x0008
+#define CR_STOP 0x0004 /* stop NIC, default = 1 */
+#define CR_STRT 0x0002 /* start NIC */
+#define CR_INIT 0x0001 /* start init process */
+#define CR_SFRST 0x8000 /* software reset */
+#define CR_RDMD1 0x4000 /* RDMD1 */
+#define CR_TDMD1 0x2000 /* TDMD1 */
+#define CR_KEYPAG 0x1000 /* turn on par/key */
+#define CR_DPOLL 0x0800 /* disable rx/tx auto polling */
+#define CR_FDX 0x0400 /* full duplex mode */
+#define CR_ETEN 0x0200 /* early tx mode */
+#define CR_EREN 0x0100 /* early rx mode */
+
+/*
+ * Bits in the IMR0 register
+ */
+
+#define IMR0_CNTM 0x80
+#define IMR0_BEM 0x40
+#define IMR0_RUM 0x20
+#define IMR0_TUM 0x10
+#define IMR0_TXEM 0x08
+#define IMR0_RXEM 0x04
+#define IMR0_PTXM 0x02
+#define IMR0_PRXM 0x01
+
+/* define imrshadow */
+
+#define IMRShadow 0x5AFF
+
+/*
+ * Bits in the IMR1 register
+ */
+
+#define IMR1_INITM 0x80
+#define IMR1_SRCM 0x40
+#define IMR1_NBFM 0x10
+#define IMR1_PRAIM 0x08
+#define IMR1_RES0M 0x04
+#define IMR1_ETM 0x02
+#define IMR1_ERM 0x01
+
+/*
+ * Bits in the ISR register
+ */
+
+#define ISR_INITI 0x8000
+#define ISR_SRCI 0x4000
+#define ISR_ABTI 0x2000
+#define ISR_NORBF 0x1000
+#define ISR_PKTRA 0x0800
+#define ISR_RES0 0x0400
+#define ISR_ETI 0x0200
+#define ISR_ERI 0x0100
+#define ISR_CNT 0x0080
+#define ISR_BE 0x0040
+#define ISR_RU 0x0020
+#define ISR_TU 0x0010
+#define ISR_TXE 0x0008
+#define ISR_RXE 0x0004
+#define ISR_PTX 0x0002
+#define ISR_PRX 0x0001
+
+/*
+ * Bits in the ISR0 register
+ */
+
+#define ISR0_CNT 0x80
+#define ISR0_BE 0x40
+#define ISR0_RU 0x20
+#define ISR0_TU 0x10
+#define ISR0_TXE 0x08
+#define ISR0_RXE 0x04
+#define ISR0_PTX 0x02
+#define ISR0_PRX 0x01
+
+/*
+ * Bits in the ISR1 register
+ */
+
+#define ISR1_INITI 0x80
+#define ISR1_SRCI 0x40
+#define ISR1_NORBF 0x10
+#define ISR1_PKTRA 0x08
+#define ISR1_ETI 0x02
+#define ISR1_ERI 0x01
+
+/* ISR ABNORMAL CONDITION */
+
+#define ISR_ABNORMAL ISR_BE+ISR_RU+ISR_TU+ISR_CNT+ISR_NORBF+ISR_PKTRA
+
+/*
+ * Bits in the MIISR register
+ */
+
+#define MIISR_MIIERR 0x08
+#define MIISR_MRERR 0x04
+#define MIISR_LNKFL 0x02
+#define MIISR_SPEED 0x01
+
+/*
+ * Bits in the MIICR register
+ */
+
+#define MIICR_MAUTO 0x80
+#define MIICR_RCMD 0x40
+#define MIICR_WCMD 0x20
+#define MIICR_MDPM 0x10
+#define MIICR_MOUT 0x08
+#define MIICR_MDO 0x04
+#define MIICR_MDI 0x02
+#define MIICR_MDC 0x01
+
+/*
+ * Bits in the EECSR register
+ */
+
+#define EECSR_EEPR 0x80 /* eeprom programed status, 73h means programed */
+#define EECSR_EMBP 0x40 /* eeprom embeded programming */
+#define EECSR_AUTOLD 0x20 /* eeprom content reload */
+#define EECSR_DPM 0x10 /* eeprom direct programming */
+#define EECSR_CS 0x08 /* eeprom CS pin */
+#define EECSR_SK 0x04 /* eeprom SK pin */
+#define EECSR_DI 0x02 /* eeprom DI pin */
+#define EECSR_DO 0x01 /* eeprom DO pin */
+
+/*
+ * Bits in the BCR0 register
+ */
+
+#define BCR0_CRFT2 0x20
+#define BCR0_CRFT1 0x10
+#define BCR0_CRFT0 0x08
+#define BCR0_DMAL2 0x04
+#define BCR0_DMAL1 0x02
+#define BCR0_DMAL0 0x01
+
+/*
+ * Bits in the BCR1 register
+ */
+
+#define BCR1_CTSF 0x20
+#define BCR1_CTFT1 0x10
+#define BCR1_CTFT0 0x08
+#define BCR1_POT2 0x04
+#define BCR1_POT1 0x02
+#define BCR1_POT0 0x01
+
+/*
+ * Bits in the CFGA register
+ */
+
+#define CFGA_EELOAD 0x80 /* enable eeprom embeded and direct programming */
+#define CFGA_JUMPER 0x40
+#define CFGA_MTGPIO 0x08
+#define CFGA_T10EN 0x02
+#define CFGA_AUTO 0x01
+
+/*
+ * Bits in the CFGB register
+ */
+
+#define CFGB_PD 0x80
+#define CFGB_POLEN 0x02
+#define CFGB_LNKEN 0x01
+
+/*
+ * Bits in the CFGC register
+ */
+
+#define CFGC_M10TIO 0x80
+#define CFGC_M10POL 0x40
+#define CFGC_PHY1 0x20
+#define CFGC_PHY0 0x10
+#define CFGC_BTSEL 0x08
+#define CFGC_BPS2 0x04 /* bootrom select[2] */
+#define CFGC_BPS1 0x02 /* bootrom select[1] */
+#define CFGC_BPS0 0x01 /* bootrom select[0] */
+
+/*
+ * Bits in the CFGD register
+ */
+
+#define CFGD_GPIOEN 0x80
+#define CFGD_DIAG 0x40
+#define CFGD_MAGIC 0x10
+#define CFGD_RANDOM 0x08
+#define CFGD_CFDX 0x04
+#define CFGD_CEREN 0x02
+#define CFGD_CETEN 0x01
+
+/* Bits in RSR */
+#define RSR_RERR 0x00000001
+#define RSR_CRC 0x00000002
+#define RSR_FAE 0x00000004
+#define RSR_FOV 0x00000008
+#define RSR_LONG 0x00000010
+#define RSR_RUNT 0x00000020
+#define RSR_SERR 0x00000040
+#define RSR_BUFF 0x00000080
+#define RSR_EDP 0x00000100
+#define RSR_STP 0x00000200
+#define RSR_CHN 0x00000400
+#define RSR_PHY 0x00000800
+#define RSR_BAR 0x00001000
+#define RSR_MAR 0x00002000
+#define RSR_RXOK 0x00008000
+#define RSR_ABNORMAL RSR_RERR+RSR_LONG+RSR_RUNT
+
+/* Bits in TSR */
+#define TSR_NCR0 0x00000001
+#define TSR_NCR1 0x00000002
+#define TSR_NCR2 0x00000004
+#define TSR_NCR3 0x00000008
+#define TSR_COLS 0x00000010
+#define TSR_CDH 0x00000080
+#define TSR_ABT 0x00000100
+#define TSR_OWC 0x00000200
+#define TSR_CRS 0x00000400
+#define TSR_UDF 0x00000800
+#define TSR_TBUFF 0x00001000
+#define TSR_SERR 0x00002000
+#define TSR_JAB 0x00004000
+#define TSR_TERR 0x00008000
+#define TSR_ABNORMAL TSR_TERR+TSR_OWC+TSR_ABT+TSR_JAB+TSR_CRS
+#define TSR_OWN_BIT 0x80000000
+
+#define CB_DELAY_LOOP_WAIT 10 /* 10ms */
+/* enabled mask value of irq */
+
+#define W_IMR_MASK_VALUE 0x1BFF /* initial value of IMR */
+
+/* Ethernet address filter type */
+#define PKT_TYPE_DIRECTED 0x0001 /* obsolete, directed address is always accepted */
+#define PKT_TYPE_MULTICAST 0x0002
+#define PKT_TYPE_ALL_MULTICAST 0x0004
+#define PKT_TYPE_BROADCAST 0x0008
+#define PKT_TYPE_PROMISCUOUS 0x0020
+#define PKT_TYPE_LONG 0x2000
+#define PKT_TYPE_RUNT 0x4000
+#define PKT_TYPE_ERROR 0x8000 /* accept error packets, e.g. CRC error */
+
+/* Loopback mode */
+
+#define NIC_LB_NONE 0x00
+#define NIC_LB_INTERNAL 0x01
+#define NIC_LB_PHY 0x02 /* MII or Internal-10BaseT loopback */
+
+#define TX_RING_SIZE 2
+#define RX_RING_SIZE 2
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
+
+#define PCI_REG_MODE3 0x53
+#define MODE3_MIION 0x04 /* in PCI_REG_MOD3 OF PCI space */
+
+enum rhine_revs {
+ VT86C100A = 0x00,
+ VTunknown0 = 0x20,
+ VT6102 = 0x40,
+ VT8231 = 0x50, /* Integrated MAC */
+ VT8233 = 0x60, /* Integrated MAC */
+ VT8235 = 0x74, /* Integrated MAC */
+ VT8237 = 0x78, /* Integrated MAC */
+ VTunknown1 = 0x7C,
+ VT6105 = 0x80,
+ VT6105_B0 = 0x83,
+ VT6105L = 0x8A,
+ VT6107 = 0x8C,
+ VTunknown2 = 0x8E,
+ VT6105M = 0x90,
+};
+
+/* Transmit and receive descriptors definition */
+
+struct rhine_tx_desc
+{
+ union VTC_tx_status_tag
+ {
+ struct
+ {
+ unsigned long ncro:1;
+ unsigned long ncr1:1;
+ unsigned long ncr2:1;
+ unsigned long ncr3:1;
+ unsigned long cols:1;
+ unsigned long reserve_1:2;
+ unsigned long cdh:1;
+ unsigned long abt:1;
+ unsigned long owc:1;
+ unsigned long crs:1;
+ unsigned long udf:1;
+ unsigned long tbuff:1;
+ unsigned long serr:1;
+ unsigned long jab:1;
+ unsigned long terr:1;
+ unsigned long reserve_2:15;
+ unsigned long own_bit:1;
+ }
+ bits;
+ unsigned long lw;
+ }
+ tx_status;
+
+ union VTC_tx_ctrl_tag
+ {
+ struct
+ {
+ unsigned long tx_buf_size:11;
+ unsigned long extend_tx_buf_size:4;
+ unsigned long chn:1;
+ unsigned long crc:1;
+ unsigned long reserve_1:4;
+ unsigned long stp:1;
+ unsigned long edp:1;
+ unsigned long ic:1;
+ unsigned long reserve_2:8;
+ }
+ bits;
+ unsigned long lw;
+ }
+ tx_ctrl;
+
+ unsigned long buf_addr_1:32;
+ unsigned long buf_addr_2:32;
+
+};
+
+struct rhine_rx_desc
+{
+ union VTC_rx_status_tag
+ {
+ struct
+ {
+ unsigned long rerr:1;
+ unsigned long crc_error:1;
+ unsigned long fae:1;
+ unsigned long fov:1;
+ unsigned long toolong:1;
+ unsigned long runt:1;
+ unsigned long serr:1;
+ unsigned long buff:1;
+ unsigned long edp:1;
+ unsigned long stp:1;
+ unsigned long chn:1;
+ unsigned long phy:1;
+ unsigned long bar:1;
+ unsigned long mar:1;
+ unsigned long reserve_1:1;
+ unsigned long rxok:1;
+ unsigned long frame_length:11;
+ unsigned long reverve_2:4;
+ unsigned long own_bit:1;
+ }
+ bits;
+ unsigned long lw;
+ }
+ rx_status;
+
+ union VTC_rx_ctrl_tag
+ {
+ struct
+ {
+ unsigned long rx_buf_size:11;
+ unsigned long extend_rx_buf_size:4;
+ unsigned long reserved_1:17;
+ }
+ bits;
+ unsigned long lw;
+ }
+ rx_ctrl;
+
+ unsigned long buf_addr_1:32;
+ unsigned long buf_addr_2:32;
+
+};
+
+struct {
+ char txbuf[TX_RING_SIZE * PKT_BUF_SZ + 32];
+ char rxbuf[RX_RING_SIZE * PKT_BUF_SZ + 32];
+ char txdesc[TX_RING_SIZE * sizeof (struct rhine_tx_desc) + 32];
+ char rxdesc[RX_RING_SIZE * sizeof (struct rhine_rx_desc) + 32];
+} rhine_buffers __shared;
+
+/* The I/O extent. */
+#define rhine_TOTAL_SIZE 0x80
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry rhine_drv =
+ { "rhine", rhine_probe, rhine_TOTAL_SIZE, NULL };
+#endif
+
+static int rhine_debug = 1;
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This driver is designed for the VIA 86c100A Rhine-II PCI Fast Ethernet
+controller.
+
+II. Board-specific settings
+
+Boards with this chip are functional only in a bus-master PCI slot.
+
+Many operational settings are loaded from the EEPROM to the Config word at
+offset 0x78. This driver assumes that they are correct.
+If this driver is compiled to use PCI memory space operations the EEPROM
+must be configured to enable memory ops.
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+This driver uses two statically allocated fixed-size descriptor lists
+formed into rings by a branch from the final descriptor to the beginning of
+the list. The ring sizes are set at compile time by RX/TX_RING_SIZE.
+
+IIIb/c. Transmit/Receive Structure
+
+This driver attempts to use a zero-copy receive and transmit scheme.
+
+Alas, all data buffers are required to start on a 32 bit boundary, so
+the driver must often copy transmit packets into bounce buffers.
+
+The driver allocates full frame size skbuffs for the Rx ring buffers at
+open() time and passes the skb->data field to the chip as receive data
+buffers. When an incoming frame is less than RX_COPYBREAK bytes long,
+a fresh skbuff is allocated and the frame is copied to the new skbuff.
+When the incoming frame is larger, the skbuff is passed directly up the
+protocol stack. Buffers consumed this way are replaced by newly allocated
+skbuffs in the last phase of netdev_rx().
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames. New boards are typically used in generously configured machines
+and the underfilled buffers have negligible impact compared to the benefit of
+a single allocation size, so the default value of zero results in never
+copying packets. When copying is done, the cost is usually mitigated by using
+a combined copy/checksum routine. Copying also preloads the cache, which is
+most useful with small frames.
+
+Since the VIA chips are only able to transfer data to buffers on 32 bit
+boundaries, the the IP header at offset 14 in an ethernet frame isn't
+longword aligned for further processing. Copying these unaligned buffers
+has the beneficial effect of 16-byte aligning the IP header.
+
+IIId. Synchronization
+
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and interrupt handling software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'lp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. After reaping the stats, it marks the Tx queue entry as
+empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
+clears both the tx_full and tbusy flags.
+
+IV. Notes
+
+IVb. References
+
+Preliminary VT86C100A manual from http://www.via.com.tw/
+http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+
+IVc. Errata
+
+The VT86C100A manual is not reliable information.
+The chip does not handle unaligned transmit or receive buffers, resulting
+in significant performance degradation for bounce buffer copies on transmit
+and unaligned IP headers on receive.
+The chip does not pad to minimum transmit length.
+
+*/
+
+/* The rest of these values should never change. */
+#define NUM_TX_DESC 2 /* Number of Tx descriptor registers. */
+
+static struct rhine_private
+{
+ char devname[8]; /* Used only for kernel debugging. */
+ const char *product_name;
+ struct rhine_rx_desc *rx_ring;
+ struct rhine_tx_desc *tx_ring;
+ char *rx_buffs[RX_RING_SIZE];
+ char *tx_buffs[TX_RING_SIZE];
+
+ /* temporary Rx buffers. */
+
+ int chip_id;
+ int chip_revision;
+ unsigned short ioaddr;
+ unsigned int cur_rx, cur_tx; /* The next free and used entries */
+ unsigned int dirty_rx, dirty_tx;
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ unsigned char mc_filter[8]; /* Current multicast filter. */
+ char phys[4]; /* MII device addresses. */
+ unsigned int tx_full:1; /* The Tx queue is full. */
+ unsigned int full_duplex:1; /* Full-duplex operation requested. */
+ unsigned int default_port:4; /* Last dev->if_port value. */
+ unsigned int media2:4; /* Secondary monitored media port. */
+ unsigned int medialock:1; /* Don't sense media type. */
+ unsigned int mediasense:1; /* Media sensing in progress. */
+}
+rhine;
+
+static void rhine_probe1 (struct nic *nic, struct pci_device *pci, int ioaddr,
+ int chip_id, int options);
+static int QueryAuto (int);
+static int ReadMII (int byMIIIndex, int);
+static void WriteMII (char, char, char, int);
+static void MIIDelay (void);
+static void rhine_init_ring (struct nic *dev);
+static void rhine_disable (struct nic *nic);
+static void rhine_reset (struct nic *nic);
+static int rhine_poll (struct nic *nic, int retreive);
+static void rhine_transmit (struct nic *nic, const char *d, unsigned int t,
+ unsigned int s, const char *p);
+static void reload_eeprom(int ioaddr);
+
+
+static void reload_eeprom(int ioaddr)
+{
+ int i;
+ outb(0x20, byEECSR);
+ /* Typically 2 cycles to reload. */
+ for (i = 0; i < 150; i++)
+ if (! (inb(byEECSR) & 0x20))
+ break;
+}
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void
+rhine_init_ring (struct nic *nic)
+{
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ int i;
+
+ tp->tx_full = 0;
+ tp->cur_rx = tp->cur_tx = 0;
+ tp->dirty_rx = tp->dirty_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++)
+ {
+
+ tp->rx_ring[i].rx_status.bits.own_bit = 1;
+ tp->rx_ring[i].rx_ctrl.bits.rx_buf_size = 1536;
+
+ tp->rx_ring[i].buf_addr_1 = virt_to_bus (tp->rx_buffs[i]);
+ tp->rx_ring[i].buf_addr_2 = virt_to_bus (&tp->rx_ring[i + 1]);
+ /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->rx_ring[i].buf_addr_1,tp->rx_ring[i].buf_addr_2); */
+ }
+ /* Mark the last entry as wrapping the ring. */
+ /* tp->rx_ring[i-1].rx_ctrl.bits.rx_buf_size =1518; */
+ tp->rx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->rx_ring[0]);
+ /*printf("[%d]buf1=%hX,buf2=%hX",i-1,tp->rx_ring[i-1].buf_addr_1,tp->rx_ring[i-1].buf_addr_2); */
+
+ /* The Tx buffer descriptor is filled in as needed, but we
+ do need to clear the ownership bit. */
+
+ for (i = 0; i < TX_RING_SIZE; i++)
+ {
+
+ tp->tx_ring[i].tx_status.lw = 0;
+ tp->tx_ring[i].tx_ctrl.lw = 0x00e08000;
+ tp->tx_ring[i].buf_addr_1 = virt_to_bus (tp->tx_buffs[i]);
+ tp->tx_ring[i].buf_addr_2 = virt_to_bus (&tp->tx_ring[i + 1]);
+ /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->tx_ring[i].buf_addr_1,tp->tx_ring[i].buf_addr_2); */
+ }
+
+ tp->tx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->tx_ring[0]);
+ /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->tx_ring[i-1].buf_addr_1,tp->tx_ring[i-1].buf_addr_2); */
+}
+
+int
+QueryAuto (int ioaddr)
+{
+ int byMIIIndex;
+ int MIIReturn;
+
+ int advertising,mii_reg5;
+ int negociated;
+
+ byMIIIndex = 0x04;
+ MIIReturn = ReadMII (byMIIIndex, ioaddr);
+ advertising=MIIReturn;
+
+ byMIIIndex = 0x05;
+ MIIReturn = ReadMII (byMIIIndex, ioaddr);
+ mii_reg5=MIIReturn;
+
+ negociated=mii_reg5 & advertising;
+
+ if ( (negociated & 0x100) || (negociated & 0x1C0) == 0x40 )
+ return 1;
+ else
+ return 0;
+
+}
+
+int
+ReadMII (int byMIIIndex, int ioaddr)
+{
+ int ReturnMII;
+ char byMIIAdrbak;
+ char byMIICRbak;
+ char byMIItemp;
+ tick_t ct;
+
+ byMIIAdrbak = inb (byMIIAD);
+ byMIICRbak = inb (byMIICR);
+ outb (byMIICRbak & 0x7f, byMIICR);
+ MIIDelay ();
+
+ outb (byMIIIndex, byMIIAD);
+ MIIDelay ();
+
+ outb (inb (byMIICR) | 0x40, byMIICR);
+
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x40;
+
+ ct = currticks();
+ while (byMIItemp != 0 && ct + 2*USECS_IN_MSEC < currticks())
+ {
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x40;
+ }
+ MIIDelay ();
+
+ ReturnMII = inw (wMIIDATA);
+
+ outb (byMIIAdrbak, byMIIAD);
+ outb (byMIICRbak, byMIICR);
+ MIIDelay ();
+
+ return (ReturnMII);
+
+}
+
+void
+WriteMII (char byMIISetByte, char byMIISetBit, char byMIIOP, int ioaddr)
+{
+ int ReadMIItmp;
+ int MIIMask;
+ char byMIIAdrbak;
+ char byMIICRbak;
+ char byMIItemp;
+ tick_t ct;
+
+
+ byMIIAdrbak = inb (byMIIAD);
+
+ byMIICRbak = inb (byMIICR);
+ outb (byMIICRbak & 0x7f, byMIICR);
+ MIIDelay ();
+ outb (byMIISetByte, byMIIAD);
+ MIIDelay ();
+
+ outb (inb (byMIICR) | 0x40, byMIICR);
+
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x40;
+
+ ct = currticks();
+ while (byMIItemp != 0 && ct + 2*USECS_IN_MSEC < currticks())
+ {
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x40;
+ }
+ MIIDelay ();
+
+ ReadMIItmp = inw (wMIIDATA);
+ MIIMask = 0x0001;
+ MIIMask = MIIMask << byMIISetBit;
+
+
+ if (byMIIOP == 0)
+ {
+ MIIMask = ~MIIMask;
+ ReadMIItmp = ReadMIItmp & MIIMask;
+ }
+ else
+ {
+ ReadMIItmp = ReadMIItmp | MIIMask;
+
+ }
+ outw (ReadMIItmp, wMIIDATA);
+ MIIDelay ();
+
+ outb (inb (byMIICR) | 0x20, byMIICR);
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x20;
+
+ ct = currticks();
+ while (byMIItemp != 0 && ct + 2*USECS_IN_MSEC < currticks())
+ {
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x20;
+ }
+ MIIDelay ();
+
+ outb (byMIIAdrbak & 0x7f, byMIIAD);
+ outb (byMIICRbak, byMIICR);
+ MIIDelay ();
+
+}
+
+void
+MIIDelay (void)
+{
+ int i;
+ for (i = 0; i < 0x7fff; i++)
+ {
+ ( void ) inb (0x61);
+ ( void ) inb (0x61);
+ ( void ) inb (0x61);
+ ( void ) inb (0x61);
+ }
+}
+
+/* Offsets to the device registers. */
+enum register_offsets {
+ StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08,
+ IntrStatus=0x0C, IntrEnable=0x0E,
+ MulticastFilter0=0x10, MulticastFilter1=0x14,
+ RxRingPtr=0x18, TxRingPtr=0x1C, GFIFOTest=0x54,
+ MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIBusConfig=0x6E,
+ MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72, MACRegEEcsr=0x74,
+ ConfigA=0x78, ConfigB=0x79, ConfigC=0x7A, ConfigD=0x7B,
+ RxMissed=0x7C, RxCRCErrs=0x7E, MiscCmd=0x81,
+ StickyHW=0x83, IntrStatus2=0x84, WOLcrClr=0xA4, WOLcgClr=0xA7,
+ PwrcsrClr=0xAC,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum intr_status_bits {
+ IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020,
+ IntrTxDone=0x0002, IntrTxError=0x0008, IntrTxUnderrun=0x0210,
+ IntrPCIErr=0x0040,
+ IntrStatsMax=0x0080, IntrRxEarly=0x0100,
+ IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000,
+ IntrTxAborted=0x2000, IntrLinkChange=0x4000,
+ IntrRxWakeUp=0x8000,
+ IntrNormalSummary=0x0003, IntrAbnormalSummary=0xC260,
+ IntrTxDescRace=0x080000, /* mapped from IntrStatus2 */
+ IntrTxErrSummary=0x082218,
+};
+#define DEFAULT_INTR (IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow | \
+ IntrRxDropped | IntrRxNoBuf)
+
+/***************************************************************************
+ IRQ - PXE IRQ Handler
+***************************************************************************/
+void rhine_irq ( struct nic *nic, irq_action_t action ) {
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ /* Enable interrupts by setting the interrupt mask. */
+ unsigned int intr_status;
+
+ switch ( action ) {
+ case DISABLE :
+ case ENABLE :
+ intr_status = inw(nic->ioaddr + IntrStatus);
+ /* On Rhine-II, Bit 3 indicates Tx descriptor write-back race. */
+
+ /* added comment by guard */
+ /* For supporting VT6107, please use revision id to recognize different chips in driver */
+ // if (tp->chip_id == 0x3065)
+ if( tp->chip_revision < 0x80 && tp->chip_revision >=0x40 )
+ intr_status |= inb(nic->ioaddr + IntrStatus2) << 16;
+ intr_status = (intr_status & ~DEFAULT_INTR);
+ if ( action == ENABLE )
+ intr_status = intr_status | DEFAULT_INTR;
+ outw(intr_status, nic->ioaddr + IntrEnable);
+ break;
+ case FORCE :
+ outw(0x0010, nic->ioaddr + 0x84);
+ break;
+ }
+}
+
+static struct nic_operations rhine_operations;
+
+static int
+rhine_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+
+ if (!pci->ioaddr)
+ return 0;
+
+ rhine_probe1 (nic, pci, pci->ioaddr, pci->device, -1);
+
+ adjust_pci_device ( pci );
+
+ rhine_reset (nic);
+
+ nic->nic_op = &rhine_operations;
+
+ nic->irqno = pci->irq;
+ nic->ioaddr = tp->ioaddr;
+
+ return 1;
+}
+
+static void set_rx_mode(struct nic *nic __unused) {
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ unsigned char rx_mode;
+ int ioaddr = tp->ioaddr;
+
+ /* ! IFF_PROMISC */
+ outl(0xffffffff, byMAR0);
+ outl(0xffffffff, byMAR4);
+ rx_mode = 0x0C;
+
+ outb(0x60 /* thresh */ | rx_mode, byRCR );
+}
+
+static void
+rhine_probe1 (struct nic *nic, struct pci_device *pci, int ioaddr, int chip_id, int options)
+{
+ struct rhine_private *tp;
+ static int did_version = 0; /* Already printed version info. */
+ unsigned int i, ww;
+ unsigned int timeout;
+ int FDXFlag;
+ int byMIIvalue, LineSpeed, MIICRbak;
+ uint8_t revision_id;
+ unsigned char mode3_reg;
+
+ if (rhine_debug > 0 && did_version++ == 0)
+ printf (version);
+
+ // get revision id.
+ pci_read_config_byte(pci, PCI_REVISION, &revision_id);
+
+ /* D-Link provided reset code (with comment additions) */
+ if (revision_id >= 0x40) {
+ unsigned char byOrgValue;
+
+ if(rhine_debug > 0)
+ printf("Enabling Sticky Bit Workaround for Chip_id: 0x%hX\n"
+ , chip_id);
+ /* clear sticky bit before reset & read ethernet address */
+ byOrgValue = inb(bySTICKHW);
+ byOrgValue = byOrgValue & 0xFC;
+ outb(byOrgValue, bySTICKHW);
+
+ /* (bits written are cleared?) */
+ /* disable force PME-enable */
+ outb(0x80, byWOLcgClr);
+ /* disable power-event config bit */
+ outb(0xFF, byWOLcrClr);
+ /* clear power status (undocumented in vt6102 docs?) */
+ outb(0xFF, byPwrcsrClr);
+
+ }
+
+ /* Reset the chip to erase previous misconfiguration. */
+ outw(CR_SFRST, byCR0);
+ // if vt3043 delay after reset
+ if (revision_id <0x40) {
+ udelay(10000);
+ }
+ // polling till software reset complete
+ // W_MAX_TIMEOUT is the timeout period
+ for(ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ if ((inw(byCR0) & CR_SFRST) == 0)
+ break;
+ }
+
+ // issue AUTOLoad in EECSR to reload eeprom
+ outb(0x20, byEECSR );
+
+ // if vt3065 delay after reset
+ if (revision_id >=0x40) {
+ // delay 8ms to let MAC stable
+ mdelay(8);
+ /*
+ * for 3065D, EEPROM reloaded will cause bit 0 in MAC_REG_CFGA
+ * turned on. it makes MAC receive magic packet
+ * automatically. So, we turn it off. (D-Link)
+ */
+ outb(inb(byCFGA) & 0xFE, byCFGA);
+ }
+
+ /* turn on bit2 in PCI configuration register 0x53 , only for 3065*/
+ if (revision_id >= 0x40) {
+ pci_read_config_byte(pci, PCI_REG_MODE3, &mode3_reg);
+ pci_write_config_byte(pci, PCI_REG_MODE3, mode3_reg|MODE3_MIION);
+ }
+
+
+ /* back off algorithm ,disable the right-most 4-bit off CFGD*/
+ outb(inb(byCFGD) & (~(CFGD_RANDOM | CFGD_CFDX | CFGD_CEREN | CFGD_CETEN)), byCFGD);
+
+ /* reload eeprom */
+ reload_eeprom(ioaddr);
+
+ /* Perhaps this should be read from the EEPROM? */
+ for (i = 0; i < ETH_ALEN; i++)
+ nic->node_addr[i] = inb (byPAR0 + i);
+
+ DBG ( "IO address %#hX Ethernet Address: %s\n", ioaddr, eth_ntoa ( nic->node_addr ) );
+
+ /* restart MII auto-negotiation */
+ WriteMII (0, 9, 1, ioaddr);
+ printf ("Analyzing Media type,this may take several seconds... ");
+ for (i = 0; i < 5; i++)
+ {
+ /* need to wait 1 millisecond - we will round it up to 50-100ms */
+ timeout = currticks() + 2;
+ for (timeout = currticks() + 2; currticks() < timeout;)
+ /* nothing */;
+ if (ReadMII (1, ioaddr) & 0x0020)
+ break;
+ }
+ printf ("OK.\n");
+
+#if 0
+ /* JJM : for Debug */
+ printf("MII : Address %hhX ",inb(ioaddr+0x6c));
+ {
+ unsigned char st1,st2,adv1,adv2,l1,l2;
+
+ st1=ReadMII(1,ioaddr)>>8;
+ st2=ReadMII(1,ioaddr)&0xFF;
+ adv1=ReadMII(4,ioaddr)>>8;
+ adv2=ReadMII(4,ioaddr)&0xFF;
+ l1=ReadMII(5,ioaddr)>>8;
+ l2=ReadMII(5,ioaddr)&0xFF;
+ printf(" status 0x%hhX%hhX, advertising 0x%hhX%hhX, link 0x%hhX%hhX\n", st1,st2,adv1,adv2,l1,l2);
+ }
+#endif
+
+
+ /* query MII to know LineSpeed,duplex mode */
+ byMIIvalue = inb (ioaddr + 0x6d);
+ LineSpeed = byMIIvalue & MIISR_SPEED;
+ if (LineSpeed != 0) //JJM
+ {
+ printf ("Linespeed=10Mbs");
+ }
+ else
+ {
+ printf ("Linespeed=100Mbs");
+ }
+
+ FDXFlag = QueryAuto (ioaddr);
+ if (FDXFlag == 1)
+ {
+ printf (" Fullduplex\n");
+ outw (CR_FDX, byCR0);
+ }
+ else
+ {
+ printf (" Halfduplex\n");
+ }
+
+
+ /* set MII 10 FULL ON, only apply in vt3043 */
+ if(chip_id == 0x3043)
+ WriteMII (0x17, 1, 1, ioaddr);
+
+ /* turn on MII link change */
+ MIICRbak = inb (byMIICR);
+ outb (MIICRbak & 0x7F, byMIICR);
+ MIIDelay ();
+ outb (0x41, byMIIAD);
+ MIIDelay ();
+
+ /* while((inb(byMIIAD)&0x20)==0) ; */
+ outb (MIICRbak | 0x80, byMIICR);
+
+ nic->priv_data = &rhine;
+ tp = &rhine;
+ tp->chip_id = chip_id;
+ tp->ioaddr = ioaddr;
+ tp->phys[0] = -1;
+ tp->chip_revision = revision_id;
+
+ /* The lower four bits are the media type. */
+ if (options > 0)
+ {
+ tp->full_duplex = (options & 16) ? 1 : 0;
+ tp->default_port = options & 15;
+ if (tp->default_port)
+ tp->medialock = 1;
+ }
+ return;
+}
+
+static void
+rhine_disable ( struct nic *nic ) {
+
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ int ioaddr = tp->ioaddr;
+
+ rhine_reset(nic);
+
+ printf ("rhine disable\n");
+ /* Switch to loopback mode to avoid hardware races. */
+ writeb(0x60 | 0x01, byTCR);
+ /* Stop the chip's Tx and Rx processes. */
+ writew(CR_STOP, byCR0);
+}
+
+/**************************************************************************
+ETH_RESET - Reset adapter
+***************************************************************************/
+static void
+rhine_reset (struct nic *nic)
+{
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ int ioaddr = tp->ioaddr;
+ int i, j;
+ int FDXFlag, CRbak;
+ int rx_ring_tmp, rx_ring_tmp1;
+ int tx_ring_tmp, tx_ring_tmp1;
+ int rx_bufs_tmp, rx_bufs_tmp1;
+ int tx_bufs_tmp, tx_bufs_tmp1;
+
+ /* printf ("rhine_reset\n"); */
+ /* Soft reset the chip. */
+ /*outb(CmdReset, ioaddr + ChipCmd); */
+
+ tx_bufs_tmp = (int) rhine_buffers.txbuf;
+ tx_ring_tmp = (int) rhine_buffers.txdesc;
+ rx_bufs_tmp = (int) rhine_buffers.rxbuf;
+ rx_ring_tmp = (int) rhine_buffers.rxdesc;
+
+ /* tune RD TD 32 byte alignment */
+ rx_ring_tmp1 = (int) virt_to_bus ((char *) rx_ring_tmp);
+ j = (rx_ring_tmp1 + 32) & (~0x1f);
+ /* printf ("txring[%d]", j); */
+ tp->rx_ring = (struct rhine_rx_desc *) bus_to_virt (j);
+
+ tx_ring_tmp1 = (int) virt_to_bus ((char *) tx_ring_tmp);
+ j = (tx_ring_tmp1 + 32) & (~0x1f);
+ tp->tx_ring = (struct rhine_tx_desc *) bus_to_virt (j);
+ /* printf ("rxring[%X]", j); */
+
+
+ tx_bufs_tmp1 = (int) virt_to_bus ((char *) tx_bufs_tmp);
+ j = (int) (tx_bufs_tmp1 + 32) & (~0x1f);
+ tx_bufs_tmp = (int) bus_to_virt (j);
+ /* printf ("txb[%X]", j); */
+
+ rx_bufs_tmp1 = (int) virt_to_bus ((char *) rx_bufs_tmp);
+ j = (int) (rx_bufs_tmp1 + 32) & (~0x1f);
+ rx_bufs_tmp = (int) bus_to_virt (j);
+ /* printf ("rxb[%X][%X]", rx_bufs_tmp1, j); */
+
+ for (i = 0; i < RX_RING_SIZE; i++)
+ {
+ tp->rx_buffs[i] = (char *) rx_bufs_tmp;
+ /* printf("r[%X]",tp->rx_buffs[i]); */
+ rx_bufs_tmp += 1536;
+ }
+
+ for (i = 0; i < TX_RING_SIZE; i++)
+ {
+ tp->tx_buffs[i] = (char *) tx_bufs_tmp;
+ /* printf("t[%X]",tp->tx_buffs[i]); */
+ tx_bufs_tmp += 1536;
+ }
+
+ /* software reset */
+ outb (CR1_SFRST, byCR1);
+ MIIDelay ();
+
+ /* printf ("init ring"); */
+ rhine_init_ring (nic);
+ /*write TD RD Descriptor to MAC */
+ outl (virt_to_bus (tp->rx_ring), dwCurrentRxDescAddr);
+ outl (virt_to_bus (tp->tx_ring), dwCurrentTxDescAddr);
+
+ /* Setup Multicast */
+ set_rx_mode(nic);
+
+ /* set TCR RCR threshold to store and forward*/
+ outb (0x3e, byBCR0);
+ outb (0x38, byBCR1);
+ outb (0x2c, byRCR);
+ outb (0x60, byTCR);
+ /* Set Fulldupex */
+ FDXFlag = QueryAuto (ioaddr);
+ if (FDXFlag == 1)
+ {
+ outb (CFGD_CFDX, byCFGD);
+ outw (CR_FDX, byCR0);
+ }
+
+ /* KICK NIC to WORK */
+ CRbak = inw (byCR0);
+ CRbak = CRbak & 0xFFFB; /* not CR_STOP */
+ outw ((CRbak | CR_STRT | CR_TXON | CR_RXON | CR_DPOLL), byCR0);
+
+ /* disable all known interrupt */
+ outw (0, byIMR0);
+}
+/* Beware of PCI posted writes */
+#define IOSYNC do { readb(nic->ioaddr + StationAddr); } while (0)
+
+static int
+rhine_poll (struct nic *nic, int retreive)
+{
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ int rxstatus, good = 0;;
+
+ if (tp->rx_ring[tp->cur_rx].rx_status.bits.own_bit == 0)
+ {
+ unsigned int intr_status;
+ /* There is a packet ready */
+ if(!retreive)
+ return 1;
+
+ intr_status = inw(nic->ioaddr + IntrStatus);
+ /* On Rhine-II, Bit 3 indicates Tx descriptor write-back race. */
+#if 0
+ if (tp->chip_id == 0x3065)
+ intr_status |= inb(nic->ioaddr + IntrStatus2) << 16;
+#endif
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ if (intr_status & IntrTxDescRace)
+ outb(0x08, nic->ioaddr + IntrStatus2);
+ outw(intr_status & 0xffff, nic->ioaddr + IntrStatus);
+ IOSYNC;
+
+ rxstatus = tp->rx_ring[tp->cur_rx].rx_status.lw;
+ if ((rxstatus & 0x0300) != 0x0300)
+ {
+ printf("rhine_poll: bad status\n");
+ }
+ else if (rxstatus & (RSR_ABNORMAL))
+ {
+ printf ("rxerr[%X]\n", rxstatus);
+ }
+ else
+ good = 1;
+
+ if (good)
+ {
+ nic->packetlen = tp->rx_ring[tp->cur_rx].rx_status.bits.frame_length;
+ memcpy (nic->packet, tp->rx_buffs[tp->cur_rx], nic->packetlen);
+ /* printf ("Packet RXed\n"); */
+ }
+ tp->rx_ring[tp->cur_rx].rx_status.bits.own_bit = 1;
+ tp->cur_rx++;
+ tp->cur_rx = tp->cur_rx % RX_RING_SIZE;
+ }
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(DEFAULT_INTR & ~IntrRxDone, nic->ioaddr + IntrStatus);
+
+ IOSYNC;
+
+ return good;
+}
+
+static void
+rhine_transmit (struct nic *nic,
+ const char *d, unsigned int t, unsigned int s, const char *p)
+{
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ int ioaddr = tp->ioaddr;
+ int entry;
+ unsigned char CR1bak;
+ unsigned char CR0bak;
+ unsigned int nstype;
+ tick_t ct;
+
+
+ /*printf ("rhine_transmit\n"); */
+ /* setup ethernet header */
+
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = tp->cur_tx % TX_RING_SIZE;
+
+ memcpy (tp->tx_buffs[entry], d, ETH_ALEN); /* dst */
+ memcpy (tp->tx_buffs[entry] + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+
+ nstype=htons(t);
+ memcpy(tp->tx_buffs[entry] + 2 * ETH_ALEN, (char*)&nstype, 2);
+
+ memcpy (tp->tx_buffs[entry] + ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ while (s < ETH_ZLEN)
+ *((char *) tp->tx_buffs[entry] + (s++)) = 0;
+
+ tp->tx_ring[entry].tx_ctrl.bits.tx_buf_size = s;
+
+ tp->tx_ring[entry].tx_status.bits.own_bit = 1;
+
+
+ CR1bak = inb (byCR1);
+
+ CR1bak = CR1bak | CR1_TDMD1;
+ /*printf("tdsw=[%X]",tp->tx_ring[entry].tx_status.lw); */
+ /*printf("tdcw=[%X]",tp->tx_ring[entry].tx_ctrl.lw); */
+ /*printf("tdbuf1=[%X]",tp->tx_ring[entry].buf_addr_1); */
+ /*printf("tdbuf2=[%X]",tp->tx_ring[entry].buf_addr_2); */
+ /*printf("td1=[%X]",inl(dwCurrentTDSE0)); */
+ /*printf("td2=[%X]",inl(dwCurrentTDSE1)); */
+ /*printf("td3=[%X]",inl(dwCurrentTDSE2)); */
+ /*printf("td4=[%X]",inl(dwCurrentTDSE3)); */
+
+ outb (CR1bak, byCR1);
+ do
+ {
+ ct = currticks();
+ /* Wait until transmit is finished or timeout*/
+ while((tp->tx_ring[entry].tx_status.bits.own_bit !=0) &&
+ ct + 10*USECS_IN_MSEC < currticks())
+ ;
+
+ if(tp->tx_ring[entry].tx_status.bits.terr == 0)
+ break;
+
+ if(tp->tx_ring[entry].tx_status.bits.abt == 1)
+ {
+ // turn on TX
+ CR0bak = inb(byCR0);
+ CR0bak = CR0bak|CR_TXON;
+ outb(CR0bak,byCR0);
+ }
+ }while(0);
+ tp->cur_tx++;
+
+ /*outw(IMRShadow,byIMR0); */
+ /*dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); */
+ /*tp->tx_skbuff[entry] = 0; */
+}
+
+static struct nic_operations rhine_operations = {
+ .connect = dummy_connect,
+ .poll = rhine_poll,
+ .transmit = rhine_transmit,
+ .irq = rhine_irq,
+
+};
+
+static struct pci_device_id rhine_nics[] = {
+PCI_ROM(0x1106, 0x3065, "dlink-530tx", "VIA 6102"),
+PCI_ROM(0x1106, 0x3106, "via-rhine-6105", "VIA 6105"),
+PCI_ROM(0x1106, 0x3043, "dlink-530tx-old", "VIA 3043"), /* Rhine-I 86c100a */
+PCI_ROM(0x1106, 0x3053, "via6105m", "VIA 6105M"),
+PCI_ROM(0x1106, 0x6100, "via-rhine-old", "VIA 86C100A"), /* Rhine-II */
+};
+
+PCI_DRIVER ( rhine_driver, rhine_nics, PCI_NO_CLASS );
+
+DRIVER ( "VIA 86C100", nic_driver, pci_driver, rhine_driver,
+ rhine_probe, rhine_disable );
+
+/* EOF via-rhine.c */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/via-velocity.c b/gpxe/src/drivers/net/via-velocity.c
new file mode 100644
index 00000000..428f609c
--- /dev/null
+++ b/gpxe/src/drivers/net/via-velocity.c
@@ -0,0 +1,1939 @@
+/**************************************************************************
+* via-velocity.c: Etherboot device driver for the VIA 6120 Gigabit
+* Changes for Etherboot port:
+* Copyright (c) 2006 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* This driver is based on:
+* via-velocity.c: VIA Velocity VT6120, VT6122 Ethernet driver
+* The changes are (c) Copyright 2004, Red Hat Inc.
+* <alan@redhat.com>
+* Additional fixes and clean up: Francois Romieu
+*
+* Original code:
+* Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+* All rights reserved.
+* Author: Chuang Liang-Shing, AJ Jiang
+*
+* Linux Driver Version 2.6.15.4
+*
+* REVISION HISTORY:
+* ================
+*
+* v1.0 03-06-2006 timlegge Initial port of Linux driver
+*
+* Indent Options: indent -kr -i8
+*************************************************************************/
+
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+
+#include "via-velocity.h"
+
+typedef int pci_power_t;
+
+#define PCI_D0 ((int) 0)
+#define PCI_D1 ((int) 1)
+#define PCI_D2 ((int) 2)
+#define PCI_D3hot ((int) 3)
+#define PCI_D3cold ((int) 4)
+#define PCI_POWER_ERROR ((int) -1)
+
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+//FIXME: Move to pci.c
+int pci_set_power_state(struct pci_device *dev, int state);
+
+/* FIXME: Move BASE to the private structure */
+static u32 BASE;
+
+/* NIC specific static variables go here */
+#define VELOCITY_PARAM(N,D) \
+ static const int N[MAX_UNITS]=OPTION_DEFAULT;
+/* MODULE_PARM(N, "1-" __MODULE_STRING(MAX_UNITS) "i");\
+ MODULE_PARM_DESC(N, D); */
+
+VELOCITY_PARAM(RxDescriptors, "Number of receive descriptors");
+VELOCITY_PARAM(TxDescriptors, "Number of transmit descriptors");
+
+
+#define VLAN_ID_MIN 0
+#define VLAN_ID_MAX 4095
+#define VLAN_ID_DEF 0
+/* VID_setting[] is used for setting the VID of NIC.
+ 0: default VID.
+ 1-4094: other VIDs.
+*/
+VELOCITY_PARAM(VID_setting, "802.1Q VLAN ID");
+
+#define RX_THRESH_MIN 0
+#define RX_THRESH_MAX 3
+#define RX_THRESH_DEF 0
+/* rx_thresh[] is used for controlling the receive fifo threshold.
+ 0: indicate the rxfifo threshold is 128 bytes.
+ 1: indicate the rxfifo threshold is 512 bytes.
+ 2: indicate the rxfifo threshold is 1024 bytes.
+ 3: indicate the rxfifo threshold is store & forward.
+*/
+VELOCITY_PARAM(rx_thresh, "Receive fifo threshold");
+
+#define DMA_LENGTH_MIN 0
+#define DMA_LENGTH_MAX 7
+#define DMA_LENGTH_DEF 0
+
+/* DMA_length[] is used for controlling the DMA length
+ 0: 8 DWORDs
+ 1: 16 DWORDs
+ 2: 32 DWORDs
+ 3: 64 DWORDs
+ 4: 128 DWORDs
+ 5: 256 DWORDs
+ 6: SF(flush till emply)
+ 7: SF(flush till emply)
+*/
+VELOCITY_PARAM(DMA_length, "DMA length");
+
+#define TAGGING_DEF 0
+/* enable_tagging[] is used for enabling 802.1Q VID tagging.
+ 0: disable VID seeting(default).
+ 1: enable VID setting.
+*/
+VELOCITY_PARAM(enable_tagging, "Enable 802.1Q tagging");
+
+#define IP_ALIG_DEF 0
+/* IP_byte_align[] is used for IP header DWORD byte aligned
+ 0: indicate the IP header won't be DWORD byte aligned.(Default) .
+ 1: indicate the IP header will be DWORD byte aligned.
+ In some enviroment, the IP header should be DWORD byte aligned,
+ or the packet will be droped when we receive it. (eg: IPVS)
+*/
+VELOCITY_PARAM(IP_byte_align, "Enable IP header dword aligned");
+
+#define TX_CSUM_DEF 1
+/* txcsum_offload[] is used for setting the checksum offload ability of NIC.
+ (We only support RX checksum offload now)
+ 0: disable csum_offload[checksum offload
+ 1: enable checksum offload. (Default)
+*/
+VELOCITY_PARAM(txcsum_offload, "Enable transmit packet checksum offload");
+
+#define FLOW_CNTL_DEF 1
+#define FLOW_CNTL_MIN 1
+#define FLOW_CNTL_MAX 5
+
+/* flow_control[] is used for setting the flow control ability of NIC.
+ 1: hardware deafult - AUTO (default). Use Hardware default value in ANAR.
+ 2: enable TX flow control.
+ 3: enable RX flow control.
+ 4: enable RX/TX flow control.
+ 5: disable
+*/
+VELOCITY_PARAM(flow_control, "Enable flow control ability");
+
+#define MED_LNK_DEF 0
+#define MED_LNK_MIN 0
+#define MED_LNK_MAX 4
+/* speed_duplex[] is used for setting the speed and duplex mode of NIC.
+ 0: indicate autonegotiation for both speed and duplex mode
+ 1: indicate 100Mbps half duplex mode
+ 2: indicate 100Mbps full duplex mode
+ 3: indicate 10Mbps half duplex mode
+ 4: indicate 10Mbps full duplex mode
+
+ Note:
+ if EEPROM have been set to the force mode, this option is ignored
+ by driver.
+*/
+VELOCITY_PARAM(speed_duplex, "Setting the speed and duplex mode");
+
+#define VAL_PKT_LEN_DEF 0
+/* ValPktLen[] is used for setting the checksum offload ability of NIC.
+ 0: Receive frame with invalid layer 2 length (Default)
+ 1: Drop frame with invalid layer 2 length
+*/
+VELOCITY_PARAM(ValPktLen, "Receiving or Drop invalid 802.3 frame");
+
+#define WOL_OPT_DEF 0
+#define WOL_OPT_MIN 0
+#define WOL_OPT_MAX 7
+/* wol_opts[] is used for controlling wake on lan behavior.
+ 0: Wake up if recevied a magic packet. (Default)
+ 1: Wake up if link status is on/off.
+ 2: Wake up if recevied an arp packet.
+ 4: Wake up if recevied any unicast packet.
+ Those value can be sumed up to support more than one option.
+*/
+VELOCITY_PARAM(wol_opts, "Wake On Lan options");
+
+#define INT_WORKS_DEF 20
+#define INT_WORKS_MIN 10
+#define INT_WORKS_MAX 64
+
+VELOCITY_PARAM(int_works, "Number of packets per interrupt services");
+
+/* The descriptors for this card are required to be aligned on
+64 byte boundaries. As the align attribute does not guarantee alignment
+greater than the alignment of the start address (which for Etherboot
+is 16 bytes of alignment) it requires some extra steps. Add 64 to the
+size of the array and the init_ring adjusts the alignment */
+
+/* Define the TX Descriptor */
+static u8 tx_ring[TX_DESC_DEF * sizeof(struct tx_desc) + 64];
+
+/* Create a static buffer of size PKT_BUF_SZ for each TX Descriptor.
+All descriptors point to a part of this buffer */
+static u8 txb[(TX_DESC_DEF * PKT_BUF_SZ) + 64];
+
+/* Define the RX Descriptor */
+static u8 rx_ring[RX_DESC_DEF * sizeof(struct rx_desc) + 64];
+
+/* Create a static buffer of size PKT_BUF_SZ for each RX Descriptor
+ All descriptors point to a part of this buffer */
+static u8 rxb[(RX_DESC_DEF * PKT_BUF_SZ) + 64];
+
+static void velocity_init_info(struct pci_device *pdev,
+ struct velocity_info *vptr,
+ struct velocity_info_tbl *info);
+static int velocity_get_pci_info(struct velocity_info *,
+ struct pci_device *pdev);
+static int velocity_open(struct nic *nic, struct pci_device *pci);
+
+static int velocity_soft_reset(struct velocity_info *vptr);
+static void velocity_init_cam_filter(struct velocity_info *vptr);
+static void mii_init(struct velocity_info *vptr, u32 mii_status);
+static u32 velocity_get_opt_media_mode(struct velocity_info *vptr);
+static void velocity_print_link_status(struct velocity_info *vptr);
+static void safe_disable_mii_autopoll(struct mac_regs *regs);
+static void enable_flow_control_ability(struct velocity_info *vptr);
+static void enable_mii_autopoll(struct mac_regs *regs);
+static int velocity_mii_read(struct mac_regs *, u8 byIdx, u16 * pdata);
+static int velocity_mii_write(struct mac_regs *, u8 byMiiAddr, u16 data);
+static u32 mii_check_media_mode(struct mac_regs *regs);
+static u32 check_connection_type(struct mac_regs *regs);
+static int velocity_set_media_mode(struct velocity_info *vptr,
+ u32 mii_status);
+
+
+/*
+ * Internal board variants. At the moment we have only one
+ */
+
+static struct velocity_info_tbl chip_info_table[] = {
+ {CHIP_TYPE_VT6110,
+ "VIA Networking Velocity Family Gigabit Ethernet Adapter", 256, 1,
+ 0x00FFFFFFUL},
+ {0, NULL, 0, 0, 0}
+};
+
+/**
+ * velocity_set_int_opt - parser for integer options
+ * @opt: pointer to option value
+ * @val: value the user requested (or -1 for default)
+ * @min: lowest value allowed
+ * @max: highest value allowed
+ * @def: default value
+ * @name: property name
+ * @dev: device name
+ *
+ * Set an integer property in the module options. This function does
+ * all the verification and checking as well as reporting so that
+ * we don't duplicate code for each option.
+ */
+
+static void velocity_set_int_opt(int *opt, int val, int min, int max,
+ int def, char *name, const char *devname)
+{
+ if (val == -1) {
+ printf("%s: set value of parameter %s to %d\n",
+ devname, name, def);
+ *opt = def;
+ } else if (val < min || val > max) {
+ printf
+ ("%s: the value of parameter %s is invalid, the valid range is (%d-%d)\n",
+ devname, name, min, max);
+ *opt = def;
+ } else {
+ printf("%s: set value of parameter %s to %d\n",
+ devname, name, val);
+ *opt = val;
+ }
+}
+
+/**
+ * velocity_set_bool_opt - parser for boolean options
+ * @opt: pointer to option value
+ * @val: value the user requested (or -1 for default)
+ * @def: default value (yes/no)
+ * @flag: numeric value to set for true.
+ * @name: property name
+ * @dev: device name
+ *
+ * Set a boolean property in the module options. This function does
+ * all the verification and checking as well as reporting so that
+ * we don't duplicate code for each option.
+ */
+
+static void velocity_set_bool_opt(u32 * opt, int val, int def, u32 flag,
+ char *name, const char *devname)
+{
+ (*opt) &= (~flag);
+ if (val == -1) {
+ printf("%s: set parameter %s to %s\n",
+ devname, name, def ? "TRUE" : "FALSE");
+ *opt |= (def ? flag : 0);
+ } else if (val < 0 || val > 1) {
+ printf
+ ("%s: the value of parameter %s is invalid, the valid range is (0-1)\n",
+ devname, name);
+ *opt |= (def ? flag : 0);
+ } else {
+ printf("%s: set parameter %s to %s\n",
+ devname, name, val ? "TRUE" : "FALSE");
+ *opt |= (val ? flag : 0);
+ }
+}
+
+/**
+ * velocity_get_options - set options on device
+ * @opts: option structure for the device
+ * @index: index of option to use in module options array
+ * @devname: device name
+ *
+ * Turn the module and command options into a single structure
+ * for the current device
+ */
+
+static void velocity_get_options(struct velocity_opt *opts, int index,
+ const char *devname)
+{
+
+ /* FIXME Do the options need to be configurable */
+ velocity_set_int_opt(&opts->rx_thresh, -1, RX_THRESH_MIN,
+ RX_THRESH_MAX, RX_THRESH_DEF, "rx_thresh",
+ devname);
+ velocity_set_int_opt(&opts->DMA_length, DMA_length[index],
+ DMA_LENGTH_MIN, DMA_LENGTH_MAX,
+ DMA_LENGTH_DEF, "DMA_length", devname);
+ velocity_set_int_opt(&opts->numrx, RxDescriptors[index],
+ RX_DESC_MIN, RX_DESC_MAX, RX_DESC_DEF,
+ "RxDescriptors", devname);
+ velocity_set_int_opt(&opts->numtx, TxDescriptors[index],
+ TX_DESC_MIN, TX_DESC_MAX, TX_DESC_DEF,
+ "TxDescriptors", devname);
+ velocity_set_int_opt(&opts->vid, VID_setting[index], VLAN_ID_MIN,
+ VLAN_ID_MAX, VLAN_ID_DEF, "VID_setting",
+ devname);
+ velocity_set_bool_opt(&opts->flags, enable_tagging[index],
+ TAGGING_DEF, VELOCITY_FLAGS_TAGGING,
+ "enable_tagging", devname);
+ velocity_set_bool_opt(&opts->flags, txcsum_offload[index],
+ TX_CSUM_DEF, VELOCITY_FLAGS_TX_CSUM,
+ "txcsum_offload", devname);
+ velocity_set_int_opt(&opts->flow_cntl, flow_control[index],
+ FLOW_CNTL_MIN, FLOW_CNTL_MAX, FLOW_CNTL_DEF,
+ "flow_control", devname);
+ velocity_set_bool_opt(&opts->flags, IP_byte_align[index],
+ IP_ALIG_DEF, VELOCITY_FLAGS_IP_ALIGN,
+ "IP_byte_align", devname);
+ velocity_set_bool_opt(&opts->flags, ValPktLen[index],
+ VAL_PKT_LEN_DEF, VELOCITY_FLAGS_VAL_PKT_LEN,
+ "ValPktLen", devname);
+ velocity_set_int_opt((void *) &opts->spd_dpx, speed_duplex[index],
+ MED_LNK_MIN, MED_LNK_MAX, MED_LNK_DEF,
+ "Media link mode", devname);
+ velocity_set_int_opt((int *) &opts->wol_opts, wol_opts[index],
+ WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF,
+ "Wake On Lan options", devname);
+ velocity_set_int_opt((int *) &opts->int_works, int_works[index],
+ INT_WORKS_MIN, INT_WORKS_MAX, INT_WORKS_DEF,
+ "Interrupt service works", devname);
+ opts->numrx = (opts->numrx & ~3);
+}
+
+/**
+ * velocity_init_cam_filter - initialise CAM
+ * @vptr: velocity to program
+ *
+ * Initialize the content addressable memory used for filters. Load
+ * appropriately according to the presence of VLAN
+ */
+
+static void velocity_init_cam_filter(struct velocity_info *vptr)
+{
+ struct mac_regs *regs = vptr->mac_regs;
+
+ /* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */
+ WORD_REG_BITS_SET(MCFG_PQEN, MCFG_RTGOPT, &regs->MCFG);
+ WORD_REG_BITS_ON(MCFG_VIDFR, &regs->MCFG);
+
+ /* Disable all CAMs */
+ memset(vptr->vCAMmask, 0, sizeof(u8) * 8);
+ memset(vptr->mCAMmask, 0, sizeof(u8) * 8);
+ mac_set_cam_mask(regs, vptr->vCAMmask, VELOCITY_VLAN_ID_CAM);
+ mac_set_cam_mask(regs, vptr->mCAMmask, VELOCITY_MULTICAST_CAM);
+
+ /* Enable first VCAM */
+ if (vptr->flags & VELOCITY_FLAGS_TAGGING) {
+ /* If Tagging option is enabled and VLAN ID is not zero, then
+ turn on MCFG_RTGOPT also */
+ if (vptr->options.vid != 0)
+ WORD_REG_BITS_ON(MCFG_RTGOPT, &regs->MCFG);
+
+ mac_set_cam(regs, 0, (u8 *) & (vptr->options.vid),
+ VELOCITY_VLAN_ID_CAM);
+ vptr->vCAMmask[0] |= 1;
+ mac_set_cam_mask(regs, vptr->vCAMmask,
+ VELOCITY_VLAN_ID_CAM);
+ } else {
+ u16 temp = 0;
+ mac_set_cam(regs, 0, (u8 *) & temp, VELOCITY_VLAN_ID_CAM);
+ temp = 1;
+ mac_set_cam_mask(regs, (u8 *) & temp,
+ VELOCITY_VLAN_ID_CAM);
+ }
+}
+
+static inline void velocity_give_many_rx_descs(struct velocity_info *vptr)
+{
+ struct mac_regs *regs = vptr->mac_regs;
+ int avail, dirty, unusable;
+
+ /*
+ * RD number must be equal to 4X per hardware spec
+ * (programming guide rev 1.20, p.13)
+ */
+ if (vptr->rd_filled < 4)
+ return;
+
+ wmb();
+
+ unusable = vptr->rd_filled & 0x0003;
+ dirty = vptr->rd_dirty - unusable;
+ for (avail = vptr->rd_filled & 0xfffc; avail; avail--) {
+ dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1;
+// printf("return dirty: %d\n", dirty);
+ vptr->rd_ring[dirty].rdesc0.owner = OWNED_BY_NIC;
+ }
+
+ writew(vptr->rd_filled & 0xfffc, &regs->RBRDU);
+ vptr->rd_filled = unusable;
+}
+
+static int velocity_rx_refill(struct velocity_info *vptr)
+{
+ int dirty = vptr->rd_dirty, done = 0, ret = 0;
+
+// printf("rx_refill - rd_curr = %d, dirty = %d\n", vptr->rd_curr, dirty);
+ do {
+ struct rx_desc *rd = vptr->rd_ring + dirty;
+
+ /* Fine for an all zero Rx desc at init time as well */
+ if (rd->rdesc0.owner == OWNED_BY_NIC)
+ break;
+// printf("rx_refill - after owner %d\n", dirty);
+
+ rd->inten = 1;
+ rd->pa_high = 0;
+ rd->rdesc0.len = cpu_to_le32(vptr->rx_buf_sz);;
+
+ done++;
+ dirty = (dirty < vptr->options.numrx - 1) ? dirty + 1 : 0;
+ } while (dirty != vptr->rd_curr);
+
+ if (done) {
+// printf("\nGive Back Desc\n");
+ vptr->rd_dirty = dirty;
+ vptr->rd_filled += done;
+ velocity_give_many_rx_descs(vptr);
+ }
+
+ return ret;
+}
+
+extern void hex_dump(const char *data, const unsigned int len);
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int velocity_poll(struct nic *nic, int retrieve)
+{
+ /* Work out whether or not there's an ethernet packet ready to
+ * read. Return 0 if not.
+ */
+
+ int rd_curr = vptr->rd_curr % RX_DESC_DEF;
+ struct rx_desc *rd = &(vptr->rd_ring[rd_curr]);
+
+ if (rd->rdesc0.owner == OWNED_BY_NIC)
+ return 0;
+ rmb();
+
+ if ( ! retrieve ) return 1;
+
+ /*
+ * Don't drop CE or RL error frame although RXOK is off
+ */
+ if ((rd->rdesc0.RSR & RSR_RXOK)
+ || (!(rd->rdesc0.RSR & RSR_RXOK)
+ && (rd->rdesc0.RSR & (RSR_CE | RSR_RL)))) {
+
+ nic->packetlen = rd->rdesc0.len;
+ // ptr->rxb + (rd_curr * PKT_BUF_SZ)
+ memcpy(nic->packet, bus_to_virt(rd->pa_low),
+ nic->packetlen - 4);
+
+ vptr->rd_curr++;
+ vptr->rd_curr = vptr->rd_curr % RX_DESC_DEF;
+ velocity_rx_refill(vptr);
+ return 1; /* Remove this line once this method is implemented */
+ }
+ return 0;
+}
+
+#define TX_TIMEOUT (1000);
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void velocity_transmit(struct nic *nic, const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ unsigned int size, /* size */
+ const char *packet)
+{ /* Packet */
+ u16 nstype;
+ u32 to;
+ u8 *ptxb;
+ unsigned int pktlen;
+ struct tx_desc *td_ptr;
+
+ int entry = vptr->td_curr % TX_DESC_DEF;
+ td_ptr = &(vptr->td_rings[entry]);
+
+ /* point to the current txb incase multiple tx_rings are used */
+ ptxb = vptr->txb + (entry * PKT_BUF_SZ);
+ memcpy(ptxb, dest, ETH_ALEN); /* Destination */
+ memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* Source */
+ nstype = htons((u16) type); /* Type */
+ memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* Type */
+ memcpy(ptxb + ETH_HLEN, packet, size);
+
+ td_ptr->tdesc1.TCPLS = TCPLS_NORMAL;
+ td_ptr->tdesc1.TCR = TCR0_TIC;
+ td_ptr->td_buf[0].queue = 0;
+
+ size += ETH_HLEN;
+ while (size < ETH_ZLEN) /* pad to min length */
+ ptxb[size++] = '\0';
+
+ if (size < ETH_ZLEN) {
+// printf("Padd that packet\n");
+ pktlen = ETH_ZLEN;
+// memcpy(ptxb, skb->data, skb->len);
+ memset(ptxb + size, 0, ETH_ZLEN - size);
+
+ vptr->td_rings[entry].tdesc0.pktsize = pktlen;
+ vptr->td_rings[entry].td_buf[0].pa_low = virt_to_bus(ptxb);
+ vptr->td_rings[entry].td_buf[0].pa_high &=
+ cpu_to_le32(0xffff0000L);
+ vptr->td_rings[entry].td_buf[0].bufsize =
+ vptr->td_rings[entry].tdesc0.pktsize;
+ vptr->td_rings[entry].tdesc1.CMDZ = 2;
+ } else {
+// printf("Correct size packet\n");
+ td_ptr->tdesc0.pktsize = size;
+ td_ptr->td_buf[0].pa_low = virt_to_bus(ptxb);
+ td_ptr->td_buf[0].pa_high = 0;
+ td_ptr->td_buf[0].bufsize = td_ptr->tdesc0.pktsize;
+// tdinfo->nskb_dma = 1;
+ td_ptr->tdesc1.CMDZ = 2;
+ }
+
+ if (vptr->flags & VELOCITY_FLAGS_TAGGING) {
+ td_ptr->tdesc1.pqinf.VID = (vptr->options.vid & 0xfff);
+ td_ptr->tdesc1.pqinf.priority = 0;
+ td_ptr->tdesc1.pqinf.CFI = 0;
+ td_ptr->tdesc1.TCR |= TCR0_VETAG;
+ }
+
+ vptr->td_curr = (entry + 1);
+
+ {
+
+ int prev = entry - 1;
+
+ if (prev < 0)
+ prev = TX_DESC_DEF - 1;
+ td_ptr->tdesc0.owner |= OWNED_BY_NIC;
+ td_ptr = &(vptr->td_rings[prev]);
+ td_ptr->td_buf[0].queue = 1;
+ mac_tx_queue_wake(vptr->mac_regs, 0);
+
+ }
+
+ to = currticks() + TX_TIMEOUT;
+ while ((td_ptr->tdesc0.owner & OWNED_BY_NIC) && (currticks() < to)); /* wait */
+
+ if (currticks() >= to) {
+ printf("TX Time Out");
+ }
+
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void velocity_disable(struct nic *nic __unused)
+{
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+ struct mac_regs *regs = vptr->mac_regs;
+ mac_disable_int(regs);
+ writel(CR0_STOP, &regs->CR0Set);
+ writew(0xFFFF, &regs->TDCSRClr);
+ writeb(0xFF, &regs->RDCSRClr);
+ safe_disable_mii_autopoll(regs);
+ mac_clear_isr(regs);
+
+ /* Power down the chip */
+// pci_set_power_state(vptr->pdev, PCI_D3hot);
+
+ vptr->flags &= (~VELOCITY_FLAGS_OPENED);
+}
+
+/**************************************************************************
+IRQ - handle interrupts
+***************************************************************************/
+static void velocity_irq(struct nic *nic __unused, irq_action_t action)
+{
+ /* This routine is somewhat optional. Etherboot itself
+ * doesn't use interrupts, but they are required under some
+ * circumstances when we're acting as a PXE stack.
+ *
+ * If you don't implement this routine, the only effect will
+ * be that your driver cannot be used via Etherboot's UNDI
+ * API. This won't affect programs that use only the UDP
+ * portion of the PXE API, such as pxelinux.
+ */
+
+ switch (action) {
+ case DISABLE:
+ case ENABLE:
+ /* Set receive interrupt enabled/disabled state */
+ /*
+ outb ( action == ENABLE ? IntrMaskEnabled : IntrMaskDisabled,
+ nic->ioaddr + IntrMaskRegister );
+ */
+ break;
+ case FORCE:
+ /* Force NIC to generate a receive interrupt */
+ /*
+ outb ( ForceInterrupt, nic->ioaddr + IntrForceRegister );
+ */
+ break;
+ }
+}
+
+static struct nic_operations velocity_operations = {
+ .connect = dummy_connect,
+ .poll = velocity_poll,
+ .transmit = velocity_transmit,
+ .irq = velocity_irq,
+};
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int velocity_probe( struct nic *nic, struct pci_device *pci)
+{
+ int ret, i;
+ struct mac_regs *regs;
+
+ printf("via-velocity.c: Found %s Vendor=0x%hX Device=0x%hX\n",
+ pci->driver_name, pci->vendor, pci->device);
+
+ /* point to private storage */
+ vptr = &vptx;
+ info = chip_info_table;
+
+ velocity_init_info(pci, vptr, info);
+
+//FIXME: pci_enable_device(pci);
+//FIXME: pci_set_power_state(pci, PCI_D0);
+
+ ret = velocity_get_pci_info(vptr, pci);
+ if (ret < 0) {
+ printf("Failed to find PCI device.\n");
+ return 0;
+ }
+
+ regs = ioremap(vptr->memaddr, vptr->io_size);
+ if (regs == NULL) {
+ printf("Unable to remap io\n");
+ return 0;
+ }
+
+ vptr->mac_regs = regs;
+
+ BASE = vptr->ioaddr;
+
+ printf("Chip ID: %hX\n", vptr->chip_id);
+
+ for (i = 0; i < 6; i++)
+ nic->node_addr[i] = readb(&regs->PAR[i]);
+
+ DBG ( "%s: %s at ioaddr %#hX\n", pci->driver_name, eth_ntoa ( nic->node_addr ),
+ (unsigned int) BASE );
+
+ velocity_get_options(&vptr->options, 0, pci->driver_name);
+
+ /*
+ * Mask out the options cannot be set to the chip
+ */
+ vptr->options.flags &= 0x00FFFFFFUL; //info->flags = 0x00FFFFFFUL;
+
+ /*
+ * Enable the chip specified capbilities
+ */
+
+ vptr->flags =
+ vptr->options.
+ flags | (0x00FFFFFFUL /*info->flags */ & 0xFF000000UL);
+
+ vptr->wol_opts = vptr->options.wol_opts;
+ vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED;
+
+ vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs);
+
+ if (vptr->flags & VELOCITY_FLAGS_TX_CSUM) {
+ printf("features missing\n");
+ }
+
+ /* and leave the chip powered down */
+// FIXME: pci_set_power_state(pci, PCI_D3hot);
+
+ check_connection_type(vptr->mac_regs);
+ velocity_open(nic, pci);
+
+ /* store NIC parameters */
+ nic->nic_op = &velocity_operations;
+ return 1;
+}
+
+//#define IORESOURCE_IO 0x00000100 /* Resource type */
+
+/**
+ * velocity_init_info - init private data
+ * @pdev: PCI device
+ * @vptr: Velocity info
+ * @info: Board type
+ *
+ * Set up the initial velocity_info struct for the device that has been
+ * discovered.
+ */
+
+static void velocity_init_info(struct pci_device *pdev,
+ struct velocity_info *vptr,
+ struct velocity_info_tbl *info)
+{
+ memset(vptr, 0, sizeof(struct velocity_info));
+
+ vptr->pdev = pdev;
+ vptr->chip_id = info->chip_id;
+ vptr->io_size = info->io_size;
+ vptr->num_txq = info->txqueue;
+ vptr->multicast_limit = MCAM_SIZE;
+
+ printf
+ ("chip_id: 0x%hX, io_size: %d, num_txq %d, multicast_limit: %d\n",
+ vptr->chip_id, (unsigned int) vptr->io_size, vptr->num_txq,
+ vptr->multicast_limit);
+ printf("Name: %s\n", info->name);
+
+// spin_lock_init(&vptr->lock);
+// INIT_LIST_HEAD(&vptr->list);
+}
+
+/**
+ * velocity_get_pci_info - retrieve PCI info for device
+ * @vptr: velocity device
+ * @pdev: PCI device it matches
+ *
+ * Retrieve the PCI configuration space data that interests us from
+ * the kernel PCI layer
+ */
+
+#define IORESOURCE_IO 0x00000100 /* Resource type */
+#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */
+
+#define IORESOURCE_MEM 0x00000200
+#define BAR_0 0
+#define BAR_1 1
+#define BAR_5 5
+#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */
+#define PCI_BASE_ADDRESS_SPACE_IO 0x01
+#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00
+#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
+#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */
+#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */
+#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */
+#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */
+//#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL)
+// #define PCI_BASE_ADDRESS_IO_MASK (~0x03UL)
+
+unsigned long pci_resource_flags(struct pci_device *pdev, unsigned int bar)
+{
+ uint32_t l, sz;
+ unsigned long flags = 0;
+
+ pci_read_config_dword(pdev, bar, &l);
+ pci_write_config_dword(pdev, bar, ~0);
+ pci_read_config_dword(pdev, bar, &sz);
+ pci_write_config_dword(pdev, bar, l);
+
+ if (!sz || sz == 0xffffffff)
+ printf("Weird size\n");
+ if (l == 0xffffffff)
+ l = 0;
+ if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ /* sz = pci_size(l, sz, PCI_BASE_ADDRESS_MEM_MASK);
+ if (!sz)
+ continue;
+ res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
+ */ flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK;
+ printf("Memory Resource\n");
+ } else {
+ // sz = pci_size(l, sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff);
+ /// if (!sz)
+ /// continue;
+// res->start = l & PCI_BASE_ADDRESS_IO_MASK;
+ flags |= l & ~PCI_BASE_ADDRESS_IO_MASK;
+ printf("I/O Resource\n");
+ }
+ if (flags & PCI_BASE_ADDRESS_SPACE_IO) {
+ printf("Why is it here\n");
+ flags |= IORESOURCE_IO;
+ } else {
+ printf("here\n");
+//flags &= ~IORESOURCE_IO;
+ }
+
+
+ if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
+ flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
+
+
+ return flags;
+}
+static int velocity_get_pci_info(struct velocity_info *vptr,
+ struct pci_device *pdev)
+{
+ if (pci_read_config_byte(pdev, PCI_REVISION_ID, &vptr->rev_id) < 0) {
+ printf("DEBUG: pci_read_config_byte failed\n");
+ return -1;
+ }
+
+ adjust_pci_device(pdev);
+
+ vptr->ioaddr = pci_bar_start(pdev, PCI_BASE_ADDRESS_0);
+ vptr->memaddr = pci_bar_start(pdev, PCI_BASE_ADDRESS_1);
+
+ printf("Looking for I/O Resource - Found:");
+ if (!
+ (pci_resource_flags(pdev, PCI_BASE_ADDRESS_0) & IORESOURCE_IO))
+ {
+ printf
+ ("DEBUG: region #0 is not an I/O resource, aborting.\n");
+ return -1;
+ }
+
+ printf("Looking for Memory Resource - Found:");
+ if ((pci_resource_flags(pdev, PCI_BASE_ADDRESS_1) & IORESOURCE_IO)) {
+ printf("DEBUG: region #1 is an I/O resource, aborting.\n");
+ return -1;
+ }
+
+ if (pci_bar_size(pdev, PCI_BASE_ADDRESS_1) < 256) {
+ printf("DEBUG: region #1 is too small.\n");
+ return -1;
+ }
+ vptr->pdev = pdev;
+
+ return 0;
+}
+
+/**
+ * velocity_print_link_status - link status reporting
+ * @vptr: velocity to report on
+ *
+ * Turn the link status of the velocity card into a kernel log
+ * description of the new link state, detailing speed and duplex
+ * status
+ */
+
+static void velocity_print_link_status(struct velocity_info *vptr)
+{
+
+ if (vptr->mii_status & VELOCITY_LINK_FAIL) {
+ printf("failed to detect cable link\n");
+ } else if (vptr->options.spd_dpx == SPD_DPX_AUTO) {
+ printf("Link autonegation");
+
+ if (vptr->mii_status & VELOCITY_SPEED_1000)
+ printf(" speed 1000M bps");
+ else if (vptr->mii_status & VELOCITY_SPEED_100)
+ printf(" speed 100M bps");
+ else
+ printf(" speed 10M bps");
+
+ if (vptr->mii_status & VELOCITY_DUPLEX_FULL)
+ printf(" full duplex\n");
+ else
+ printf(" half duplex\n");
+ } else {
+ printf("Link forced");
+ switch (vptr->options.spd_dpx) {
+ case SPD_DPX_100_HALF:
+ printf(" speed 100M bps half duplex\n");
+ break;
+ case SPD_DPX_100_FULL:
+ printf(" speed 100M bps full duplex\n");
+ break;
+ case SPD_DPX_10_HALF:
+ printf(" speed 10M bps half duplex\n");
+ break;
+ case SPD_DPX_10_FULL:
+ printf(" speed 10M bps full duplex\n");
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/**
+ * velocity_rx_reset - handle a receive reset
+ * @vptr: velocity we are resetting
+ *
+ * Reset the ownership and status for the receive ring side.
+ * Hand all the receive queue to the NIC.
+ */
+
+static void velocity_rx_reset(struct velocity_info *vptr)
+{
+
+ struct mac_regs *regs = vptr->mac_regs;
+ int i;
+
+//ptr->rd_dirty = vptr->rd_filled = vptr->rd_curr = 0;
+
+ /*
+ * Init state, all RD entries belong to the NIC
+ */
+ for (i = 0; i < vptr->options.numrx; ++i)
+ vptr->rd_ring[i].rdesc0.owner = OWNED_BY_NIC;
+
+ writew(RX_DESC_DEF, &regs->RBRDU);
+ writel(virt_to_le32desc(vptr->rd_ring), &regs->RDBaseLo);
+ writew(0, &regs->RDIdx);
+ writew(RX_DESC_DEF - 1, &regs->RDCSize);
+}
+
+/**
+ * velocity_init_registers - initialise MAC registers
+ * @vptr: velocity to init
+ * @type: type of initialisation (hot or cold)
+ *
+ * Initialise the MAC on a reset or on first set up on the
+ * hardware.
+ */
+
+static void velocity_init_registers(struct nic *nic,
+ struct velocity_info *vptr,
+ enum velocity_init_type type)
+{
+ struct mac_regs *regs = vptr->mac_regs;
+ int i, mii_status;
+
+ mac_wol_reset(regs);
+
+ switch (type) {
+ case VELOCITY_INIT_RESET:
+ case VELOCITY_INIT_WOL:
+
+//netif_stop_queue(vptr->dev);
+
+ /*
+ * Reset RX to prevent RX pointer not on the 4X location
+ */
+ velocity_rx_reset(vptr);
+ mac_rx_queue_run(regs);
+ mac_rx_queue_wake(regs);
+
+ mii_status = velocity_get_opt_media_mode(vptr);
+
+ if (velocity_set_media_mode(vptr, mii_status) !=
+ VELOCITY_LINK_CHANGE) {
+ velocity_print_link_status(vptr);
+ if (!(vptr->mii_status & VELOCITY_LINK_FAIL))
+ printf("Link Failed\n");
+// netif_wake_queue(vptr->dev);
+ }
+
+ enable_flow_control_ability(vptr);
+
+ mac_clear_isr(regs);
+ writel(CR0_STOP, &regs->CR0Clr);
+ //writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT),
+ writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT),
+ &regs->CR0Set);
+ break;
+
+ case VELOCITY_INIT_COLD:
+ default:
+ /*
+ * Do reset
+ */
+ velocity_soft_reset(vptr);
+ mdelay(5);
+
+ mac_eeprom_reload(regs);
+ for (i = 0; i < 6; i++) {
+ writeb(nic->node_addr[i], &(regs->PAR[i]));
+ }
+ /*
+ * clear Pre_ACPI bit.
+ */
+ BYTE_REG_BITS_OFF(CFGA_PACPI, &(regs->CFGA));
+ mac_set_rx_thresh(regs, vptr->options.rx_thresh);
+ mac_set_dma_length(regs, vptr->options.DMA_length);
+
+ writeb(WOLCFG_SAM | WOLCFG_SAB, &regs->WOLCFGSet);
+ /*
+ * Back off algorithm use original IEEE standard
+ */
+ BYTE_REG_BITS_SET(CFGB_OFSET,
+ (CFGB_CRANDOM | CFGB_CAP | CFGB_MBA |
+ CFGB_BAKOPT), &regs->CFGB);
+
+ /*
+ * Init CAM filter
+ */
+ velocity_init_cam_filter(vptr);
+
+ /*
+ * Set packet filter: Receive directed and broadcast address
+ */
+//FIXME Multicast velocity_set_multi(nic);
+
+ /*
+ * Enable MII auto-polling
+ */
+ enable_mii_autopoll(regs);
+
+ vptr->int_mask = INT_MASK_DEF;
+
+ writel(virt_to_le32desc(vptr->rd_ring), &regs->RDBaseLo);
+ writew(vptr->options.numrx - 1, &regs->RDCSize);
+ mac_rx_queue_run(regs);
+ mac_rx_queue_wake(regs);
+
+ writew(vptr->options.numtx - 1, &regs->TDCSize);
+
+// for (i = 0; i < vptr->num_txq; i++) {
+ writel(virt_to_le32desc(vptr->td_rings),
+ &(regs->TDBaseLo[0]));
+ mac_tx_queue_run(regs, 0);
+// }
+
+ init_flow_control_register(vptr);
+
+ writel(CR0_STOP, &regs->CR0Clr);
+ writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT),
+ &regs->CR0Set);
+
+ mii_status = velocity_get_opt_media_mode(vptr);
+// netif_stop_queue(vptr->dev);
+
+ mii_init(vptr, mii_status);
+
+ if (velocity_set_media_mode(vptr, mii_status) !=
+ VELOCITY_LINK_CHANGE) {
+ velocity_print_link_status(vptr);
+ if (!(vptr->mii_status & VELOCITY_LINK_FAIL))
+ printf("Link Faaailll\n");
+// netif_wake_queue(vptr->dev);
+ }
+
+ enable_flow_control_ability(vptr);
+ mac_hw_mibs_init(regs);
+ mac_write_int_mask(vptr->int_mask, regs);
+ mac_clear_isr(regs);
+
+
+ }
+ velocity_print_link_status(vptr);
+}
+
+/**
+ * velocity_soft_reset - soft reset
+ * @vptr: velocity to reset
+ *
+ * Kick off a soft reset of the velocity adapter and then poll
+ * until the reset sequence has completed before returning.
+ */
+
+static int velocity_soft_reset(struct velocity_info *vptr)
+{
+ struct mac_regs *regs = vptr->mac_regs;
+ unsigned int i = 0;
+
+ writel(CR0_SFRST, &regs->CR0Set);
+
+ for (i = 0; i < W_MAX_TIMEOUT; i++) {
+ udelay(5);
+ if (!DWORD_REG_BITS_IS_ON(CR0_SFRST, &regs->CR0Set))
+ break;
+ }
+
+ if (i == W_MAX_TIMEOUT) {
+ writel(CR0_FORSRST, &regs->CR0Set);
+ /* FIXME: PCI POSTING */
+ /* delay 2ms */
+ mdelay(2);
+ }
+ return 0;
+}
+
+/**
+ * velocity_init_rings - set up DMA rings
+ * @vptr: Velocity to set up
+ *
+ * Allocate PCI mapped DMA rings for the receive and transmit layer
+ * to use.
+ */
+
+static int velocity_init_rings(struct velocity_info *vptr)
+{
+
+ int idx;
+
+ vptr->rd_curr = 0;
+ vptr->td_curr = 0;
+ memset(vptr->td_rings, 0, TX_DESC_DEF * sizeof(struct tx_desc));
+ memset(vptr->rd_ring, 0, RX_DESC_DEF * sizeof(struct rx_desc));
+// memset(vptr->tx_buffs, 0, TX_DESC_DEF * PKT_BUF_SZ);
+
+
+ for (idx = 0; idx < RX_DESC_DEF; idx++) {
+ vptr->rd_ring[idx].rdesc0.RSR = 0;
+ vptr->rd_ring[idx].rdesc0.len = 0;
+ vptr->rd_ring[idx].rdesc0.reserved = 0;
+ vptr->rd_ring[idx].rdesc0.owner = 0;
+ vptr->rd_ring[idx].len = cpu_to_le32(vptr->rx_buf_sz);
+ vptr->rd_ring[idx].inten = 1;
+ vptr->rd_ring[idx].pa_low =
+ virt_to_bus(vptr->rxb + (RX_DESC_DEF * idx));
+ vptr->rd_ring[idx].pa_high = 0;
+ vptr->rd_ring[idx].rdesc0.owner = OWNED_BY_NIC;
+ }
+
+/* for (i = 0; idx < TX_DESC_DEF; idx++ ) {
+ vptr->td_rings[idx].tdesc1.TCPLS = TCPLS_NORMAL;
+ vptr->td_rings[idx].tdesc1.TCR = TCR0_TIC;
+ vptr->td_rings[idx].td_buf[0].queue = 0;
+ vptr->td_rings[idx].tdesc0.owner = ~OWNED_BY_NIC;
+ vptr->td_rings[idx].tdesc0.pktsize = 0;
+ vptr->td_rings[idx].td_buf[0].pa_low = cpu_to_le32(virt_to_bus(vptr->txb + (idx * PKT_BUF_SZ)));
+ vptr->td_rings[idx].td_buf[0].pa_high = 0;
+ vptr->td_rings[idx].td_buf[0].bufsize = 0;
+ vptr->td_rings[idx].tdesc1.CMDZ = 2;
+ }
+*/
+ return 0;
+}
+
+/**
+ * velocity_open - interface activation callback
+ * @dev: network layer device to open
+ *
+ * Called when the network layer brings the interface up. Returns
+ * a negative posix error code on failure, or zero on success.
+ *
+ * All the ring allocation and set up is done on open for this
+ * adapter to minimise memory usage when inactive
+ */
+
+#define PCI_BYTE_REG_BITS_ON(x,i,p) do{\
+ u8 byReg;\
+ pci_read_config_byte((p), (i), &(byReg));\
+ (byReg) |= (x);\
+ pci_write_config_byte((p), (i), (byReg));\
+} while (0)
+
+//
+// Registers in the PCI configuration space
+//
+#define PCI_REG_COMMAND 0x04 //
+#define PCI_REG_MODE0 0x60 //
+#define PCI_REG_MODE1 0x61 //
+#define PCI_REG_MODE2 0x62 //
+#define PCI_REG_MODE3 0x63 //
+#define PCI_REG_DELAY_TIMER 0x64 //
+
+// Bits in the (MODE2, 0x62) register
+//
+#define MODE2_PCEROPT 0x80 // take PCI bus ERror as a fatal and shutdown from software control
+#define MODE2_TXQ16 0x40 // TX write-back Queue control. 0->32 entries available in Tx write-back queue, 1->16 entries
+#define MODE2_TXPOST 0x08 // (Not support in VT3119)
+#define MODE2_AUTOOPT 0x04 // (VT3119 GHCI without such behavior)
+#define MODE2_MODE10T 0x02 // used to control tx Threshold for 10M case
+#define MODE2_TCPLSOPT 0x01 // TCP large send field update disable, hardware will not update related fields, leave it to software.
+
+//
+// Bits in the MODE3 register
+//
+#define MODE3_MIION 0x04 // MII symbol codine error detect enable ??
+
+// Bits in the (COMMAND, 0x04) register
+#define COMMAND_BUSM 0x04
+#define COMMAND_WAIT 0x80
+static int velocity_open(struct nic *nic, struct pci_device *pci __unused)
+{
+ int ret;
+
+ u8 diff;
+ u32 TxPhyAddr, RxPhyAddr;
+ u32 TxBufPhyAddr, RxBufPhyAddr;
+ vptr->TxDescArrays = tx_ring;
+ if (vptr->TxDescArrays == 0)
+ printf("Allot Error");
+
+ /* Tx Descriptor needs 64 bytes alignment; */
+ TxPhyAddr = virt_to_bus(vptr->TxDescArrays);
+ printf("Unaligned Address : %lX\n", TxPhyAddr);
+ diff = 64 - (TxPhyAddr - ((TxPhyAddr >> 6) << 6));
+ TxPhyAddr += diff;
+ vptr->td_rings = (struct tx_desc *) (vptr->TxDescArrays + diff);
+
+ printf("Aligned Address: %lX\n", virt_to_bus(vptr->td_rings));
+ vptr->tx_buffs = txb;
+ /* Rx Buffer needs 64 bytes alignment; */
+ TxBufPhyAddr = virt_to_bus(vptr->tx_buffs);
+ diff = 64 - (TxBufPhyAddr - ((TxBufPhyAddr >> 6) << 6));
+ TxBufPhyAddr += diff;
+ vptr->txb = (unsigned char *) (vptr->tx_buffs + diff);
+
+ vptr->RxDescArrays = rx_ring;
+ /* Rx Descriptor needs 64 bytes alignment; */
+ RxPhyAddr = virt_to_bus(vptr->RxDescArrays);
+ diff = 64 - (RxPhyAddr - ((RxPhyAddr >> 6) << 6));
+ RxPhyAddr += diff;
+ vptr->rd_ring = (struct rx_desc *) (vptr->RxDescArrays + diff);
+
+ vptr->rx_buffs = rxb;
+ /* Rx Buffer needs 64 bytes alignment; */
+ RxBufPhyAddr = virt_to_bus(vptr->rx_buffs);
+ diff = 64 - (RxBufPhyAddr - ((RxBufPhyAddr >> 6) << 6));
+ RxBufPhyAddr += diff;
+ vptr->rxb = (unsigned char *) (vptr->rx_buffs + diff);
+
+ if (vptr->RxDescArrays == NULL || vptr->RxDescArrays == NULL) {
+ printf("Allocate tx_ring or rd_ring failed\n");
+ return 0;
+ }
+
+ vptr->rx_buf_sz = PKT_BUF_SZ;
+/*
+ // turn this on to avoid retry forever
+ PCI_BYTE_REG_BITS_ON(MODE2_PCEROPT, PCI_REG_MODE2, pci);
+ // for some legacy BIOS and OS don't open BusM
+ // bit in PCI configuration space. So, turn it on.
+ PCI_BYTE_REG_BITS_ON(COMMAND_BUSM, PCI_REG_COMMAND, pci);
+ // turn this on to detect MII coding error
+ PCI_BYTE_REG_BITS_ON(MODE3_MIION, PCI_REG_MODE3, pci);
+ */
+ ret = velocity_init_rings(vptr);
+
+ /* Ensure chip is running */
+//FIXME: pci_set_power_state(vptr->pdev, PCI_D0);
+
+ velocity_init_registers(nic, vptr, VELOCITY_INIT_COLD);
+ mac_write_int_mask(0, vptr->mac_regs);
+// _int(vptr->mac_regs);
+ //mac_enable_int(vptr->mac_regs);
+
+ vptr->flags |= VELOCITY_FLAGS_OPENED;
+ return 1;
+
+}
+
+/*
+ * MII access , media link mode setting functions
+ */
+
+
+/**
+ * mii_init - set up MII
+ * @vptr: velocity adapter
+ * @mii_status: links tatus
+ *
+ * Set up the PHY for the current link state.
+ */
+
+static void mii_init(struct velocity_info *vptr, u32 mii_status __unused)
+{
+ u16 BMCR;
+
+ switch (PHYID_GET_PHY_ID(vptr->phy_id)) {
+ case PHYID_CICADA_CS8201:
+ /*
+ * Reset to hardware default
+ */
+ MII_REG_BITS_OFF((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR,
+ vptr->mac_regs);
+ /*
+ * Turn on ECHODIS bit in NWay-forced full mode and turn it
+ * off it in NWay-forced half mode for NWay-forced v.s.
+ * legacy-forced issue.
+ */
+ if (vptr->mii_status & VELOCITY_DUPLEX_FULL)
+ MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR,
+ vptr->mac_regs);
+ else
+ MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR,
+ vptr->mac_regs);
+ /*
+ * Turn on Link/Activity LED enable bit for CIS8201
+ */
+ MII_REG_BITS_ON(PLED_LALBE, MII_REG_PLED, vptr->mac_regs);
+ break;
+ case PHYID_VT3216_32BIT:
+ case PHYID_VT3216_64BIT:
+ /*
+ * Reset to hardware default
+ */
+ MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR,
+ vptr->mac_regs);
+ /*
+ * Turn on ECHODIS bit in NWay-forced full mode and turn it
+ * off it in NWay-forced half mode for NWay-forced v.s.
+ * legacy-forced issue
+ */
+ if (vptr->mii_status & VELOCITY_DUPLEX_FULL)
+ MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR,
+ vptr->mac_regs);
+ else
+ MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR,
+ vptr->mac_regs);
+ break;
+
+ case PHYID_MARVELL_1000:
+ case PHYID_MARVELL_1000S:
+ /*
+ * Assert CRS on Transmit
+ */
+ MII_REG_BITS_ON(PSCR_ACRSTX, MII_REG_PSCR, vptr->mac_regs);
+ /*
+ * Reset to hardware default
+ */
+ MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR,
+ vptr->mac_regs);
+ break;
+ default:
+ ;
+ }
+ velocity_mii_read(vptr->mac_regs, MII_REG_BMCR, &BMCR);
+ if (BMCR & BMCR_ISO) {
+ BMCR &= ~BMCR_ISO;
+ velocity_mii_write(vptr->mac_regs, MII_REG_BMCR, BMCR);
+ }
+}
+
+/**
+ * safe_disable_mii_autopoll - autopoll off
+ * @regs: velocity registers
+ *
+ * Turn off the autopoll and wait for it to disable on the chip
+ */
+
+static void safe_disable_mii_autopoll(struct mac_regs *regs)
+{
+ u16 ww;
+
+ /* turn off MAUTO */
+ writeb(0, &regs->MIICR);
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ udelay(1);
+ if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, &regs->MIISR))
+ break;
+ }
+}
+
+/**
+ * enable_mii_autopoll - turn on autopolling
+ * @regs: velocity registers
+ *
+ * Enable the MII link status autopoll feature on the Velocity
+ * hardware. Wait for it to enable.
+ */
+
+static void enable_mii_autopoll(struct mac_regs *regs)
+{
+ unsigned int ii;
+
+ writeb(0, &(regs->MIICR));
+ writeb(MIIADR_SWMPL, &regs->MIIADR);
+
+ for (ii = 0; ii < W_MAX_TIMEOUT; ii++) {
+ udelay(1);
+ if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, &regs->MIISR))
+ break;
+ }
+
+ writeb(MIICR_MAUTO, &regs->MIICR);
+
+ for (ii = 0; ii < W_MAX_TIMEOUT; ii++) {
+ udelay(1);
+ if (!BYTE_REG_BITS_IS_ON(MIISR_MIDLE, &regs->MIISR))
+ break;
+ }
+
+}
+
+/**
+ * velocity_mii_read - read MII data
+ * @regs: velocity registers
+ * @index: MII register index
+ * @data: buffer for received data
+ *
+ * Perform a single read of an MII 16bit register. Returns zero
+ * on success or -ETIMEDOUT if the PHY did not respond.
+ */
+
+static int velocity_mii_read(struct mac_regs *regs, u8 index, u16 * data)
+{
+ u16 ww;
+
+ /*
+ * Disable MIICR_MAUTO, so that mii addr can be set normally
+ */
+ safe_disable_mii_autopoll(regs);
+
+ writeb(index, &regs->MIIADR);
+
+ BYTE_REG_BITS_ON(MIICR_RCMD, &regs->MIICR);
+
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ if (!(readb(&regs->MIICR) & MIICR_RCMD))
+ break;
+ }
+
+ *data = readw(&regs->MIIDATA);
+
+ enable_mii_autopoll(regs);
+ if (ww == W_MAX_TIMEOUT)
+ return -1;
+ return 0;
+}
+
+/**
+ * velocity_mii_write - write MII data
+ * @regs: velocity registers
+ * @index: MII register index
+ * @data: 16bit data for the MII register
+ *
+ * Perform a single write to an MII 16bit register. Returns zero
+ * on success or -ETIMEDOUT if the PHY did not respond.
+ */
+
+static int velocity_mii_write(struct mac_regs *regs, u8 mii_addr, u16 data)
+{
+ u16 ww;
+
+ /*
+ * Disable MIICR_MAUTO, so that mii addr can be set normally
+ */
+ safe_disable_mii_autopoll(regs);
+
+ /* MII reg offset */
+ writeb(mii_addr, &regs->MIIADR);
+ /* set MII data */
+ writew(data, &regs->MIIDATA);
+
+ /* turn on MIICR_WCMD */
+ BYTE_REG_BITS_ON(MIICR_WCMD, &regs->MIICR);
+
+ /* W_MAX_TIMEOUT is the timeout period */
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ udelay(5);
+ if (!(readb(&regs->MIICR) & MIICR_WCMD))
+ break;
+ }
+ enable_mii_autopoll(regs);
+
+ if (ww == W_MAX_TIMEOUT)
+ return -1;
+ return 0;
+}
+
+/**
+ * velocity_get_opt_media_mode - get media selection
+ * @vptr: velocity adapter
+ *
+ * Get the media mode stored in EEPROM or module options and load
+ * mii_status accordingly. The requested link state information
+ * is also returned.
+ */
+
+static u32 velocity_get_opt_media_mode(struct velocity_info *vptr)
+{
+ u32 status = 0;
+
+ switch (vptr->options.spd_dpx) {
+ case SPD_DPX_AUTO:
+ status = VELOCITY_AUTONEG_ENABLE;
+ break;
+ case SPD_DPX_100_FULL:
+ status = VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL;
+ break;
+ case SPD_DPX_10_FULL:
+ status = VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL;
+ break;
+ case SPD_DPX_100_HALF:
+ status = VELOCITY_SPEED_100;
+ break;
+ case SPD_DPX_10_HALF:
+ status = VELOCITY_SPEED_10;
+ break;
+ }
+ vptr->mii_status = status;
+ return status;
+}
+
+/**
+ * mii_set_auto_on - autonegotiate on
+ * @vptr: velocity
+ *
+ * Enable autonegotation on this interface
+ */
+
+static void mii_set_auto_on(struct velocity_info *vptr)
+{
+ if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs))
+ MII_REG_BITS_ON(BMCR_REAUTO, MII_REG_BMCR, vptr->mac_regs);
+ else
+ MII_REG_BITS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs);
+}
+
+
+/*
+static void mii_set_auto_off(struct velocity_info * vptr)
+{
+ MII_REG_BITS_OFF(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs);
+}
+*/
+
+/**
+ * set_mii_flow_control - flow control setup
+ * @vptr: velocity interface
+ *
+ * Set up the flow control on this interface according to
+ * the supplied user/eeprom options.
+ */
+
+static void set_mii_flow_control(struct velocity_info *vptr)
+{
+ /*Enable or Disable PAUSE in ANAR */
+ switch (vptr->options.flow_cntl) {
+ case FLOW_CNTL_TX:
+ MII_REG_BITS_OFF(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs);
+ MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs);
+ break;
+
+ case FLOW_CNTL_RX:
+ MII_REG_BITS_ON(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs);
+ MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs);
+ break;
+
+ case FLOW_CNTL_TX_RX:
+ MII_REG_BITS_ON(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs);
+ MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs);
+ break;
+
+ case FLOW_CNTL_DISABLE:
+ MII_REG_BITS_OFF(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs);
+ MII_REG_BITS_OFF(ANAR_ASMDIR, MII_REG_ANAR,
+ vptr->mac_regs);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * velocity_set_media_mode - set media mode
+ * @mii_status: old MII link state
+ *
+ * Check the media link state and configure the flow control
+ * PHY and also velocity hardware setup accordingly. In particular
+ * we need to set up CD polling and frame bursting.
+ */
+
+static int velocity_set_media_mode(struct velocity_info *vptr,
+ u32 mii_status)
+{
+ u32 curr_status;
+ struct mac_regs *regs = vptr->mac_regs;
+
+ vptr->mii_status = mii_check_media_mode(vptr->mac_regs);
+ curr_status = vptr->mii_status & (~VELOCITY_LINK_FAIL);
+
+ /* Set mii link status */
+ set_mii_flow_control(vptr);
+
+ /*
+ Check if new status is consisent with current status
+ if (((mii_status & curr_status) & VELOCITY_AUTONEG_ENABLE)
+ || (mii_status==curr_status)) {
+ vptr->mii_status=mii_check_media_mode(vptr->mac_regs);
+ vptr->mii_status=check_connection_type(vptr->mac_regs);
+ printf(MSG_LEVEL_INFO, "Velocity link no change\n");
+ return 0;
+ }
+ */
+
+ if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) {
+ MII_REG_BITS_ON(AUXCR_MDPPS, MII_REG_AUXCR,
+ vptr->mac_regs);
+ }
+
+ /*
+ * If connection type is AUTO
+ */
+ if (mii_status & VELOCITY_AUTONEG_ENABLE) {
+ printf("Velocity is AUTO mode\n");
+ /* clear force MAC mode bit */
+ BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, &regs->CHIPGCR);
+ /* set duplex mode of MAC according to duplex mode of MII */
+ MII_REG_BITS_ON(ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10,
+ MII_REG_ANAR, vptr->mac_regs);
+ MII_REG_BITS_ON(G1000CR_1000FD | G1000CR_1000,
+ MII_REG_G1000CR, vptr->mac_regs);
+ MII_REG_BITS_ON(BMCR_SPEED1G, MII_REG_BMCR,
+ vptr->mac_regs);
+
+ /* enable AUTO-NEGO mode */
+ mii_set_auto_on(vptr);
+ } else {
+ u16 ANAR;
+ u8 CHIPGCR;
+
+ /*
+ * 1. if it's 3119, disable frame bursting in halfduplex mode
+ * and enable it in fullduplex mode
+ * 2. set correct MII/GMII and half/full duplex mode in CHIPGCR
+ * 3. only enable CD heart beat counter in 10HD mode
+ */
+
+ /* set force MAC mode bit */
+ BYTE_REG_BITS_ON(CHIPGCR_FCMODE, &regs->CHIPGCR);
+
+ CHIPGCR = readb(&regs->CHIPGCR);
+ CHIPGCR &= ~CHIPGCR_FCGMII;
+
+ if (mii_status & VELOCITY_DUPLEX_FULL) {
+ CHIPGCR |= CHIPGCR_FCFDX;
+ writeb(CHIPGCR, &regs->CHIPGCR);
+ printf
+ ("DEBUG: set Velocity to forced full mode\n");
+ if (vptr->rev_id < REV_ID_VT3216_A0)
+ BYTE_REG_BITS_OFF(TCR_TB2BDIS, &regs->TCR);
+ } else {
+ CHIPGCR &= ~CHIPGCR_FCFDX;
+ printf
+ ("DEBUG: set Velocity to forced half mode\n");
+ writeb(CHIPGCR, &regs->CHIPGCR);
+ if (vptr->rev_id < REV_ID_VT3216_A0)
+ BYTE_REG_BITS_ON(TCR_TB2BDIS, &regs->TCR);
+ }
+
+ MII_REG_BITS_OFF(G1000CR_1000FD | G1000CR_1000,
+ MII_REG_G1000CR, vptr->mac_regs);
+
+ if (!(mii_status & VELOCITY_DUPLEX_FULL)
+ && (mii_status & VELOCITY_SPEED_10)) {
+ BYTE_REG_BITS_OFF(TESTCFG_HBDIS, &regs->TESTCFG);
+ } else {
+ BYTE_REG_BITS_ON(TESTCFG_HBDIS, &regs->TESTCFG);
+ }
+ /* MII_REG_BITS_OFF(BMCR_SPEED1G, MII_REG_BMCR, vptr->mac_regs); */
+ velocity_mii_read(vptr->mac_regs, MII_REG_ANAR, &ANAR);
+ ANAR &= (~(ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10));
+ if (mii_status & VELOCITY_SPEED_100) {
+ if (mii_status & VELOCITY_DUPLEX_FULL)
+ ANAR |= ANAR_TXFD;
+ else
+ ANAR |= ANAR_TX;
+ } else {
+ if (mii_status & VELOCITY_DUPLEX_FULL)
+ ANAR |= ANAR_10FD;
+ else
+ ANAR |= ANAR_10;
+ }
+ velocity_mii_write(vptr->mac_regs, MII_REG_ANAR, ANAR);
+ /* enable AUTO-NEGO mode */
+ mii_set_auto_on(vptr);
+ /* MII_REG_BITS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); */
+ }
+ /* vptr->mii_status=mii_check_media_mode(vptr->mac_regs); */
+ /* vptr->mii_status=check_connection_type(vptr->mac_regs); */
+ return VELOCITY_LINK_CHANGE;
+}
+
+/**
+ * mii_check_media_mode - check media state
+ * @regs: velocity registers
+ *
+ * Check the current MII status and determine the link status
+ * accordingly
+ */
+
+static u32 mii_check_media_mode(struct mac_regs *regs)
+{
+ u32 status = 0;
+ u16 ANAR;
+
+ if (!MII_REG_BITS_IS_ON(BMSR_LNK, MII_REG_BMSR, regs))
+ status |= VELOCITY_LINK_FAIL;
+
+ if (MII_REG_BITS_IS_ON(G1000CR_1000FD, MII_REG_G1000CR, regs))
+ status |= VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL;
+ else if (MII_REG_BITS_IS_ON(G1000CR_1000, MII_REG_G1000CR, regs))
+ status |= (VELOCITY_SPEED_1000);
+ else {
+ velocity_mii_read(regs, MII_REG_ANAR, &ANAR);
+ if (ANAR & ANAR_TXFD)
+ status |=
+ (VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL);
+ else if (ANAR & ANAR_TX)
+ status |= VELOCITY_SPEED_100;
+ else if (ANAR & ANAR_10FD)
+ status |=
+ (VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL);
+ else
+ status |= (VELOCITY_SPEED_10);
+ }
+
+ if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, regs)) {
+ velocity_mii_read(regs, MII_REG_ANAR, &ANAR);
+ if ((ANAR & (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10))
+ == (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) {
+ if (MII_REG_BITS_IS_ON
+ (G1000CR_1000 | G1000CR_1000FD,
+ MII_REG_G1000CR, regs))
+ status |= VELOCITY_AUTONEG_ENABLE;
+ }
+ }
+
+ return status;
+}
+
+static u32 check_connection_type(struct mac_regs *regs)
+{
+ u32 status = 0;
+ u8 PHYSR0;
+ u16 ANAR;
+ PHYSR0 = readb(&regs->PHYSR0);
+
+ /*
+ if (!(PHYSR0 & PHYSR0_LINKGD))
+ status|=VELOCITY_LINK_FAIL;
+ */
+
+ if (PHYSR0 & PHYSR0_FDPX)
+ status |= VELOCITY_DUPLEX_FULL;
+
+ if (PHYSR0 & PHYSR0_SPDG)
+ status |= VELOCITY_SPEED_1000;
+ if (PHYSR0 & PHYSR0_SPD10)
+ status |= VELOCITY_SPEED_10;
+ else
+ status |= VELOCITY_SPEED_100;
+
+ if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, regs)) {
+ velocity_mii_read(regs, MII_REG_ANAR, &ANAR);
+ if ((ANAR & (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10))
+ == (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) {
+ if (MII_REG_BITS_IS_ON
+ (G1000CR_1000 | G1000CR_1000FD,
+ MII_REG_G1000CR, regs))
+ status |= VELOCITY_AUTONEG_ENABLE;
+ }
+ }
+
+ return status;
+}
+
+/**
+ * enable_flow_control_ability - flow control
+ * @vptr: veloity to configure
+ *
+ * Set up flow control according to the flow control options
+ * determined by the eeprom/configuration.
+ */
+
+static void enable_flow_control_ability(struct velocity_info *vptr)
+{
+
+ struct mac_regs *regs = vptr->mac_regs;
+
+ switch (vptr->options.flow_cntl) {
+
+ case FLOW_CNTL_DEFAULT:
+ if (BYTE_REG_BITS_IS_ON(PHYSR0_RXFLC, &regs->PHYSR0))
+ writel(CR0_FDXRFCEN, &regs->CR0Set);
+ else
+ writel(CR0_FDXRFCEN, &regs->CR0Clr);
+
+ if (BYTE_REG_BITS_IS_ON(PHYSR0_TXFLC, &regs->PHYSR0))
+ writel(CR0_FDXTFCEN, &regs->CR0Set);
+ else
+ writel(CR0_FDXTFCEN, &regs->CR0Clr);
+ break;
+
+ case FLOW_CNTL_TX:
+ writel(CR0_FDXTFCEN, &regs->CR0Set);
+ writel(CR0_FDXRFCEN, &regs->CR0Clr);
+ break;
+
+ case FLOW_CNTL_RX:
+ writel(CR0_FDXRFCEN, &regs->CR0Set);
+ writel(CR0_FDXTFCEN, &regs->CR0Clr);
+ break;
+
+ case FLOW_CNTL_TX_RX:
+ writel(CR0_FDXTFCEN, &regs->CR0Set);
+ writel(CR0_FDXRFCEN, &regs->CR0Set);
+ break;
+
+ case FLOW_CNTL_DISABLE:
+ writel(CR0_FDXRFCEN, &regs->CR0Clr);
+ writel(CR0_FDXTFCEN, &regs->CR0Clr);
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+/* FIXME: Move to pci.c */
+/**
+ * pci_set_power_state - Set the power state of a PCI device
+ * @dev: PCI device to be suspended
+ * @state: Power state we're entering
+ *
+ * Transition a device to a new power state, using the Power Management
+ * Capabilities in the device's config space.
+ *
+ * RETURN VALUE:
+ * -EINVAL if trying to enter a lower state than we're already in.
+ * 0 if we're already in the requested state.
+ * -EIO if device does not support PCI PM.
+ * 0 if we can successfully change the power state.
+ */
+
+int pci_set_power_state(struct pci_device *dev, int state)
+{
+ int pm;
+ u16 pmcsr;
+ int current_state = 0;
+
+ /* bound the state we're entering */
+ if (state > 3)
+ state = 3;
+
+ /* Validate current state:
+ * Can enter D0 from any state, but if we can only go deeper
+ * to sleep if we're already in a low power state
+ */
+ if (state > 0 && current_state > state)
+ return -1;
+ else if (current_state == state)
+ return 0; /* we're already there */
+
+ /* find PCI PM capability in list */
+ pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+
+ /* abort if the device doesn't support PM capabilities */
+ if (!pm)
+ return -2;
+
+ /* check if this device supports the desired state */
+ if (state == 1 || state == 2) {
+ u16 pmc;
+ pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
+ if (state == 1 && !(pmc & PCI_PM_CAP_D1))
+ return -2;
+ else if (state == 2 && !(pmc & PCI_PM_CAP_D2))
+ return -2;
+ }
+
+ /* If we're in D3, force entire word to 0.
+ * This doesn't affect PME_Status, disables PME_En, and
+ * sets PowerState to 0.
+ */
+ if (current_state >= 3)
+ pmcsr = 0;
+ else {
+ pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
+ pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+ pmcsr |= state;
+ }
+
+ /* enter specified state */
+ pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr);
+
+ /* Mandatory power management transition delays */
+ /* see PCI PM 1.1 5.6.1 table 18 */
+ if (state == 3 || current_state == 3)
+ mdelay(10);
+ else if (state == 2 || current_state == 2)
+ udelay(200);
+ current_state = state;
+
+ return 0;
+}
+
+static struct pci_device_id velocity_nics[] = {
+ PCI_ROM(0x1106, 0x3119, "via-velocity", "VIA Networking Velocity Family Gigabit Ethernet Adapter"),
+};
+
+PCI_DRIVER ( velocity_driver, velocity_nics, PCI_NO_CLASS );
+
+DRIVER ( "VIA-VELOCITY/PCI", nic_driver, pci_driver, velocity_driver,
+ velocity_probe, velocity_disable );
diff --git a/gpxe/src/drivers/net/via-velocity.h b/gpxe/src/drivers/net/via-velocity.h
new file mode 100644
index 00000000..a43028bd
--- /dev/null
+++ b/gpxe/src/drivers/net/via-velocity.h
@@ -0,0 +1,1930 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This software may be redistributed and/or modified under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * File: via-velocity.h
+ *
+ * Purpose: Header file to define driver's private structures.
+ *
+ * Author: Chuang Liang-Shing, AJ Jiang
+ *
+ * Date: Jan 24, 2003
+ *
+ * Changes for Etherboot Port:
+ * Copyright (c) 2006 by Timothy Legge <tlegge@rogers.com>
+ */
+
+#ifndef VELOCITY_H
+#define VELOCITY_H
+
+#define VELOCITY_TX_CSUM_SUPPORT
+
+#define VELOCITY_NAME "via-velocity"
+#define VELOCITY_FULL_DRV_NAM "VIA Networking Velocity Family Gigabit Ethernet Adapter Driver"
+#define VELOCITY_VERSION "1.13"
+
+#define PKT_BUF_SZ 1564
+
+#define MAX_UNITS 8
+#define OPTION_DEFAULT { [0 ... MAX_UNITS-1] = -1}
+
+#define REV_ID_VT6110 (0)
+
+#define BYTE_REG_BITS_ON(x,p) do { writeb(readb((p))|(x),(p));} while (0)
+#define WORD_REG_BITS_ON(x,p) do { writew(readw((p))|(x),(p));} while (0)
+#define DWORD_REG_BITS_ON(x,p) do { writel(readl((p))|(x),(p));} while (0)
+
+#define BYTE_REG_BITS_IS_ON(x,p) (readb((p)) & (x))
+#define WORD_REG_BITS_IS_ON(x,p) (readw((p)) & (x))
+#define DWORD_REG_BITS_IS_ON(x,p) (readl((p)) & (x))
+
+#define BYTE_REG_BITS_OFF(x,p) do { writeb(readb((p)) & (~(x)),(p));} while (0)
+#define WORD_REG_BITS_OFF(x,p) do { writew(readw((p)) & (~(x)),(p));} while (0)
+#define DWORD_REG_BITS_OFF(x,p) do { writel(readl((p)) & (~(x)),(p));} while (0)
+
+#define BYTE_REG_BITS_SET(x,m,p) do { writeb( (readb((p)) & (~(m))) |(x),(p));} while (0)
+#define WORD_REG_BITS_SET(x,m,p) do { writew( (readw((p)) & (~(m))) |(x),(p));} while (0)
+#define DWORD_REG_BITS_SET(x,m,p) do { writel( (readl((p)) & (~(m)))|(x),(p));} while (0)
+
+#define VAR_USED(p) do {(p)=(p);} while (0)
+
+/*
+ * Purpose: Structures for MAX RX/TX descriptors.
+ */
+
+
+#define B_OWNED_BY_CHIP 1
+#define B_OWNED_BY_HOST 0
+
+/*
+ * Bits in the RSR0 register
+ */
+
+#define RSR_DETAG 0x0080
+#define RSR_SNTAG 0x0040
+#define RSR_RXER 0x0020
+#define RSR_RL 0x0010
+#define RSR_CE 0x0008
+#define RSR_FAE 0x0004
+#define RSR_CRC 0x0002
+#define RSR_VIDM 0x0001
+
+/*
+ * Bits in the RSR1 register
+ */
+
+#define RSR_RXOK 0x8000 // rx OK
+#define RSR_PFT 0x4000 // Perfect filtering address match
+#define RSR_MAR 0x2000 // MAC accept multicast address packet
+#define RSR_BAR 0x1000 // MAC accept broadcast address packet
+#define RSR_PHY 0x0800 // MAC accept physical address packet
+#define RSR_VTAG 0x0400 // 802.1p/1q tagging packet indicator
+#define RSR_STP 0x0200 // start of packet
+#define RSR_EDP 0x0100 // end of packet
+
+/*
+ * Bits in the RSR1 register
+ */
+
+#define RSR1_RXOK 0x80 // rx OK
+#define RSR1_PFT 0x40 // Perfect filtering address match
+#define RSR1_MAR 0x20 // MAC accept multicast address packet
+#define RSR1_BAR 0x10 // MAC accept broadcast address packet
+#define RSR1_PHY 0x08 // MAC accept physical address packet
+#define RSR1_VTAG 0x04 // 802.1p/1q tagging packet indicator
+#define RSR1_STP 0x02 // start of packet
+#define RSR1_EDP 0x01 // end of packet
+
+/*
+ * Bits in the CSM register
+ */
+
+#define CSM_IPOK 0x40 //IP Checkusm validatiaon ok
+#define CSM_TUPOK 0x20 //TCP/UDP Checkusm validatiaon ok
+#define CSM_FRAG 0x10 //Fragment IP datagram
+#define CSM_IPKT 0x04 //Received an IP packet
+#define CSM_TCPKT 0x02 //Received a TCP packet
+#define CSM_UDPKT 0x01 //Received a UDP packet
+
+/*
+ * Bits in the TSR0 register
+ */
+
+#define TSR0_ABT 0x0080 // Tx abort because of excessive collision
+#define TSR0_OWT 0x0040 // Jumbo frame Tx abort
+#define TSR0_OWC 0x0020 // Out of window collision
+#define TSR0_COLS 0x0010 // experience collision in this transmit event
+#define TSR0_NCR3 0x0008 // collision retry counter[3]
+#define TSR0_NCR2 0x0004 // collision retry counter[2]
+#define TSR0_NCR1 0x0002 // collision retry counter[1]
+#define TSR0_NCR0 0x0001 // collision retry counter[0]
+#define TSR0_TERR 0x8000 //
+#define TSR0_FDX 0x4000 // current transaction is serviced by full duplex mode
+#define TSR0_GMII 0x2000 // current transaction is serviced by GMII mode
+#define TSR0_LNKFL 0x1000 // packet serviced during link down
+#define TSR0_SHDN 0x0400 // shutdown case
+#define TSR0_CRS 0x0200 // carrier sense lost
+#define TSR0_CDH 0x0100 // AQE test fail (CD heartbeat)
+
+/*
+ * Bits in the TSR1 register
+ */
+
+#define TSR1_TERR 0x80 //
+#define TSR1_FDX 0x40 // current transaction is serviced by full duplex mode
+#define TSR1_GMII 0x20 // current transaction is serviced by GMII mode
+#define TSR1_LNKFL 0x10 // packet serviced during link down
+#define TSR1_SHDN 0x04 // shutdown case
+#define TSR1_CRS 0x02 // carrier sense lost
+#define TSR1_CDH 0x01 // AQE test fail (CD heartbeat)
+
+//
+// Bits in the TCR0 register
+//
+#define TCR0_TIC 0x80 // assert interrupt immediately while descriptor has been send complete
+#define TCR0_PIC 0x40 // priority interrupt request, INA# is issued over adaptive interrupt scheme
+#define TCR0_VETAG 0x20 // enable VLAN tag
+#define TCR0_IPCK 0x10 // request IP checksum calculation.
+#define TCR0_UDPCK 0x08 // request UDP checksum calculation.
+#define TCR0_TCPCK 0x04 // request TCP checksum calculation.
+#define TCR0_JMBO 0x02 // indicate a jumbo packet in GMAC side
+#define TCR0_CRC 0x01 // disable CRC generation
+
+#define TCPLS_NORMAL 3
+#define TCPLS_START 2
+#define TCPLS_END 1
+#define TCPLS_MED 0
+
+
+// max transmit or receive buffer size
+#define CB_RX_BUF_SIZE 2048UL // max buffer size
+ // NOTE: must be multiple of 4
+
+#define CB_MAX_RD_NUM 512 // MAX # of RD
+#define CB_MAX_TD_NUM 256 // MAX # of TD
+
+#define CB_INIT_RD_NUM_3119 128 // init # of RD, for setup VT3119
+#define CB_INIT_TD_NUM_3119 64 // init # of TD, for setup VT3119
+
+#define CB_INIT_RD_NUM 128 // init # of RD, for setup default
+#define CB_INIT_TD_NUM 64 // init # of TD, for setup default
+
+// for 3119
+#define CB_TD_RING_NUM 4 // # of TD rings.
+#define CB_MAX_SEG_PER_PKT 7 // max data seg per packet (Tx)
+
+
+/*
+ * If collisions excess 15 times , tx will abort, and
+ * if tx fifo underflow, tx will fail
+ * we should try to resend it
+ */
+
+#define CB_MAX_TX_ABORT_RETRY 3
+
+/*
+ * Receive descriptor
+ */
+
+struct rdesc0 {
+ u16 RSR; /* Receive status */
+ u16 len:14; /* Received packet length */
+ u16 reserved:1;
+ u16 owner:1; /* Who owns this buffer ? */
+};
+
+struct rdesc1 {
+ u16 PQTAG;
+ u8 CSM;
+ u8 IPKT;
+};
+
+struct rx_desc {
+ struct rdesc0 rdesc0;
+ struct rdesc1 rdesc1;
+ u32 pa_low; /* Low 32 bit PCI address */
+ u16 pa_high; /* Next 16 bit PCI address (48 total) */
+ u16 len:15; /* Frame size */
+ u16 inten:1; /* Enable interrupt */
+} __attribute__ ((__packed__));
+
+/*
+ * Transmit descriptor
+ */
+
+struct tdesc0 {
+ u16 TSR; /* Transmit status register */
+ u16 pktsize:14; /* Size of frame */
+ u16 reserved:1;
+ u16 owner:1; /* Who owns the buffer */
+};
+
+struct pqinf { /* Priority queue info */
+ u16 VID:12;
+ u16 CFI:1;
+ u16 priority:3;
+} __attribute__ ((__packed__));
+
+struct tdesc1 {
+ struct pqinf pqinf;
+ u8 TCR;
+ u8 TCPLS:2;
+ u8 reserved:2;
+ u8 CMDZ:4;
+} __attribute__ ((__packed__));
+
+struct td_buf {
+ u32 pa_low;
+ u16 pa_high;
+ u16 bufsize:14;
+ u16 reserved:1;
+ u16 queue:1;
+} __attribute__ ((__packed__));
+
+struct tx_desc {
+ struct tdesc0 tdesc0;
+ struct tdesc1 tdesc1;
+ struct td_buf td_buf[7];
+};
+
+#ifdef LINUX
+struct velocity_rd_info {
+ struct sk_buff *skb;
+ dma_addr_t skb_dma;
+};
+
+
+/**
+ * alloc_rd_info - allocate an rd info block
+ *
+ * Alocate and initialize a receive info structure used for keeping
+ * track of kernel side information related to each receive
+ * descriptor we are using
+ */
+
+static inline struct velocity_rd_info *alloc_rd_info(void)
+{
+ struct velocity_rd_info *ptr;
+ if ((ptr =
+ kmalloc(sizeof(struct velocity_rd_info), GFP_ATOMIC)) == NULL)
+ return NULL;
+ else {
+ memset(ptr, 0, sizeof(struct velocity_rd_info));
+ return ptr;
+ }
+}
+
+/*
+ * Used to track transmit side buffers.
+ */
+
+struct velocity_td_info {
+ struct sk_buff *skb;
+ u8 *buf;
+ int nskb_dma;
+ dma_addr_t skb_dma[7];
+ dma_addr_t buf_dma;
+};
+
+#endif
+enum {
+ OWNED_BY_HOST = 0,
+ OWNED_BY_NIC = 1
+} velocity_owner;
+
+
+/*
+ * MAC registers and macros.
+ */
+
+
+#define MCAM_SIZE 64
+#define VCAM_SIZE 64
+#define TX_QUEUE_NO 4
+
+#define MAX_HW_MIB_COUNTER 32
+#define VELOCITY_MIN_MTU (1514-14)
+#define VELOCITY_MAX_MTU (9000)
+
+/*
+ * Registers in the MAC
+ */
+
+#define MAC_REG_PAR 0x00 // physical address
+#define MAC_REG_RCR 0x06
+#define MAC_REG_TCR 0x07
+#define MAC_REG_CR0_SET 0x08
+#define MAC_REG_CR1_SET 0x09
+#define MAC_REG_CR2_SET 0x0A
+#define MAC_REG_CR3_SET 0x0B
+#define MAC_REG_CR0_CLR 0x0C
+#define MAC_REG_CR1_CLR 0x0D
+#define MAC_REG_CR2_CLR 0x0E
+#define MAC_REG_CR3_CLR 0x0F
+#define MAC_REG_MAR 0x10
+#define MAC_REG_CAM 0x10
+#define MAC_REG_DEC_BASE_HI 0x18
+#define MAC_REG_DBF_BASE_HI 0x1C
+#define MAC_REG_ISR_CTL 0x20
+#define MAC_REG_ISR_HOTMR 0x20
+#define MAC_REG_ISR_TSUPTHR 0x20
+#define MAC_REG_ISR_RSUPTHR 0x20
+#define MAC_REG_ISR_CTL1 0x21
+#define MAC_REG_TXE_SR 0x22
+#define MAC_REG_RXE_SR 0x23
+#define MAC_REG_ISR 0x24
+#define MAC_REG_ISR0 0x24
+#define MAC_REG_ISR1 0x25
+#define MAC_REG_ISR2 0x26
+#define MAC_REG_ISR3 0x27
+#define MAC_REG_IMR 0x28
+#define MAC_REG_IMR0 0x28
+#define MAC_REG_IMR1 0x29
+#define MAC_REG_IMR2 0x2A
+#define MAC_REG_IMR3 0x2B
+#define MAC_REG_TDCSR_SET 0x30
+#define MAC_REG_RDCSR_SET 0x32
+#define MAC_REG_TDCSR_CLR 0x34
+#define MAC_REG_RDCSR_CLR 0x36
+#define MAC_REG_RDBASE_LO 0x38
+#define MAC_REG_RDINDX 0x3C
+#define MAC_REG_TDBASE_LO 0x40
+#define MAC_REG_RDCSIZE 0x50
+#define MAC_REG_TDCSIZE 0x52
+#define MAC_REG_TDINDX 0x54
+#define MAC_REG_TDIDX0 0x54
+#define MAC_REG_TDIDX1 0x56
+#define MAC_REG_TDIDX2 0x58
+#define MAC_REG_TDIDX3 0x5A
+#define MAC_REG_PAUSE_TIMER 0x5C
+#define MAC_REG_RBRDU 0x5E
+#define MAC_REG_FIFO_TEST0 0x60
+#define MAC_REG_FIFO_TEST1 0x64
+#define MAC_REG_CAMADDR 0x68
+#define MAC_REG_CAMCR 0x69
+#define MAC_REG_GFTEST 0x6A
+#define MAC_REG_FTSTCMD 0x6B
+#define MAC_REG_MIICFG 0x6C
+#define MAC_REG_MIISR 0x6D
+#define MAC_REG_PHYSR0 0x6E
+#define MAC_REG_PHYSR1 0x6F
+#define MAC_REG_MIICR 0x70
+#define MAC_REG_MIIADR 0x71
+#define MAC_REG_MIIDATA 0x72
+#define MAC_REG_SOFT_TIMER0 0x74
+#define MAC_REG_SOFT_TIMER1 0x76
+#define MAC_REG_CFGA 0x78
+#define MAC_REG_CFGB 0x79
+#define MAC_REG_CFGC 0x7A
+#define MAC_REG_CFGD 0x7B
+#define MAC_REG_DCFG0 0x7C
+#define MAC_REG_DCFG1 0x7D
+#define MAC_REG_MCFG0 0x7E
+#define MAC_REG_MCFG1 0x7F
+
+#define MAC_REG_TBIST 0x80
+#define MAC_REG_RBIST 0x81
+#define MAC_REG_PMCC 0x82
+#define MAC_REG_STICKHW 0x83
+#define MAC_REG_MIBCR 0x84
+#define MAC_REG_EERSV 0x85
+#define MAC_REG_REVID 0x86
+#define MAC_REG_MIBREAD 0x88
+#define MAC_REG_BPMA 0x8C
+#define MAC_REG_EEWR_DATA 0x8C
+#define MAC_REG_BPMD_WR 0x8F
+#define MAC_REG_BPCMD 0x90
+#define MAC_REG_BPMD_RD 0x91
+#define MAC_REG_EECHKSUM 0x92
+#define MAC_REG_EECSR 0x93
+#define MAC_REG_EERD_DATA 0x94
+#define MAC_REG_EADDR 0x96
+#define MAC_REG_EMBCMD 0x97
+#define MAC_REG_JMPSR0 0x98
+#define MAC_REG_JMPSR1 0x99
+#define MAC_REG_JMPSR2 0x9A
+#define MAC_REG_JMPSR3 0x9B
+#define MAC_REG_CHIPGSR 0x9C
+#define MAC_REG_TESTCFG 0x9D
+#define MAC_REG_DEBUG 0x9E
+#define MAC_REG_CHIPGCR 0x9F
+#define MAC_REG_WOLCR0_SET 0xA0
+#define MAC_REG_WOLCR1_SET 0xA1
+#define MAC_REG_PWCFG_SET 0xA2
+#define MAC_REG_WOLCFG_SET 0xA3
+#define MAC_REG_WOLCR0_CLR 0xA4
+#define MAC_REG_WOLCR1_CLR 0xA5
+#define MAC_REG_PWCFG_CLR 0xA6
+#define MAC_REG_WOLCFG_CLR 0xA7
+#define MAC_REG_WOLSR0_SET 0xA8
+#define MAC_REG_WOLSR1_SET 0xA9
+#define MAC_REG_WOLSR0_CLR 0xAC
+#define MAC_REG_WOLSR1_CLR 0xAD
+#define MAC_REG_PATRN_CRC0 0xB0
+#define MAC_REG_PATRN_CRC1 0xB2
+#define MAC_REG_PATRN_CRC2 0xB4
+#define MAC_REG_PATRN_CRC3 0xB6
+#define MAC_REG_PATRN_CRC4 0xB8
+#define MAC_REG_PATRN_CRC5 0xBA
+#define MAC_REG_PATRN_CRC6 0xBC
+#define MAC_REG_PATRN_CRC7 0xBE
+#define MAC_REG_BYTEMSK0_0 0xC0
+#define MAC_REG_BYTEMSK0_1 0xC4
+#define MAC_REG_BYTEMSK0_2 0xC8
+#define MAC_REG_BYTEMSK0_3 0xCC
+#define MAC_REG_BYTEMSK1_0 0xD0
+#define MAC_REG_BYTEMSK1_1 0xD4
+#define MAC_REG_BYTEMSK1_2 0xD8
+#define MAC_REG_BYTEMSK1_3 0xDC
+#define MAC_REG_BYTEMSK2_0 0xE0
+#define MAC_REG_BYTEMSK2_1 0xE4
+#define MAC_REG_BYTEMSK2_2 0xE8
+#define MAC_REG_BYTEMSK2_3 0xEC
+#define MAC_REG_BYTEMSK3_0 0xF0
+#define MAC_REG_BYTEMSK3_1 0xF4
+#define MAC_REG_BYTEMSK3_2 0xF8
+#define MAC_REG_BYTEMSK3_3 0xFC
+
+/*
+ * Bits in the RCR register
+ */
+
+#define RCR_AS 0x80
+#define RCR_AP 0x40
+#define RCR_AL 0x20
+#define RCR_PROM 0x10
+#define RCR_AB 0x08
+#define RCR_AM 0x04
+#define RCR_AR 0x02
+#define RCR_SEP 0x01
+
+/*
+ * Bits in the TCR register
+ */
+
+#define TCR_TB2BDIS 0x80
+#define TCR_COLTMC1 0x08
+#define TCR_COLTMC0 0x04
+#define TCR_LB1 0x02 /* loopback[1] */
+#define TCR_LB0 0x01 /* loopback[0] */
+
+/*
+ * Bits in the CR0 register
+ */
+
+#define CR0_TXON 0x00000008UL
+#define CR0_RXON 0x00000004UL
+#define CR0_STOP 0x00000002UL /* stop MAC, default = 1 */
+#define CR0_STRT 0x00000001UL /* start MAC */
+#define CR0_SFRST 0x00008000UL /* software reset */
+#define CR0_TM1EN 0x00004000UL
+#define CR0_TM0EN 0x00002000UL
+#define CR0_DPOLL 0x00000800UL /* disable rx/tx auto polling */
+#define CR0_DISAU 0x00000100UL
+#define CR0_XONEN 0x00800000UL
+#define CR0_FDXTFCEN 0x00400000UL /* full-duplex TX flow control enable */
+#define CR0_FDXRFCEN 0x00200000UL /* full-duplex RX flow control enable */
+#define CR0_HDXFCEN 0x00100000UL /* half-duplex flow control enable */
+#define CR0_XHITH1 0x00080000UL /* TX XON high threshold 1 */
+#define CR0_XHITH0 0x00040000UL /* TX XON high threshold 0 */
+#define CR0_XLTH1 0x00020000UL /* TX pause frame low threshold 1 */
+#define CR0_XLTH0 0x00010000UL /* TX pause frame low threshold 0 */
+#define CR0_GSPRST 0x80000000UL
+#define CR0_FORSRST 0x40000000UL
+#define CR0_FPHYRST 0x20000000UL
+#define CR0_DIAG 0x10000000UL
+#define CR0_INTPCTL 0x04000000UL
+#define CR0_GINTMSK1 0x02000000UL
+#define CR0_GINTMSK0 0x01000000UL
+
+/*
+ * Bits in the CR1 register
+ */
+
+#define CR1_SFRST 0x80 /* software reset */
+#define CR1_TM1EN 0x40
+#define CR1_TM0EN 0x20
+#define CR1_DPOLL 0x08 /* disable rx/tx auto polling */
+#define CR1_DISAU 0x01
+
+/*
+ * Bits in the CR2 register
+ */
+
+#define CR2_XONEN 0x80
+#define CR2_FDXTFCEN 0x40 /* full-duplex TX flow control enable */
+#define CR2_FDXRFCEN 0x20 /* full-duplex RX flow control enable */
+#define CR2_HDXFCEN 0x10 /* half-duplex flow control enable */
+#define CR2_XHITH1 0x08 /* TX XON high threshold 1 */
+#define CR2_XHITH0 0x04 /* TX XON high threshold 0 */
+#define CR2_XLTH1 0x02 /* TX pause frame low threshold 1 */
+#define CR2_XLTH0 0x01 /* TX pause frame low threshold 0 */
+
+/*
+ * Bits in the CR3 register
+ */
+
+#define CR3_GSPRST 0x80
+#define CR3_FORSRST 0x40
+#define CR3_FPHYRST 0x20
+#define CR3_DIAG 0x10
+#define CR3_INTPCTL 0x04
+#define CR3_GINTMSK1 0x02
+#define CR3_GINTMSK0 0x01
+
+#define ISRCTL_UDPINT 0x8000
+#define ISRCTL_TSUPDIS 0x4000
+#define ISRCTL_RSUPDIS 0x2000
+#define ISRCTL_PMSK1 0x1000
+#define ISRCTL_PMSK0 0x0800
+#define ISRCTL_INTPD 0x0400
+#define ISRCTL_HCRLD 0x0200
+#define ISRCTL_SCRLD 0x0100
+
+/*
+ * Bits in the ISR_CTL1 register
+ */
+
+#define ISRCTL1_UDPINT 0x80
+#define ISRCTL1_TSUPDIS 0x40
+#define ISRCTL1_RSUPDIS 0x20
+#define ISRCTL1_PMSK1 0x10
+#define ISRCTL1_PMSK0 0x08
+#define ISRCTL1_INTPD 0x04
+#define ISRCTL1_HCRLD 0x02
+#define ISRCTL1_SCRLD 0x01
+
+/*
+ * Bits in the TXE_SR register
+ */
+
+#define TXESR_TFDBS 0x08
+#define TXESR_TDWBS 0x04
+#define TXESR_TDRBS 0x02
+#define TXESR_TDSTR 0x01
+
+/*
+ * Bits in the RXE_SR register
+ */
+
+#define RXESR_RFDBS 0x08
+#define RXESR_RDWBS 0x04
+#define RXESR_RDRBS 0x02
+#define RXESR_RDSTR 0x01
+
+/*
+ * Bits in the ISR register
+ */
+
+#define ISR_ISR3 0x80000000UL
+#define ISR_ISR2 0x40000000UL
+#define ISR_ISR1 0x20000000UL
+#define ISR_ISR0 0x10000000UL
+#define ISR_TXSTLI 0x02000000UL
+#define ISR_RXSTLI 0x01000000UL
+#define ISR_HFLD 0x00800000UL
+#define ISR_UDPI 0x00400000UL
+#define ISR_MIBFI 0x00200000UL
+#define ISR_SHDNI 0x00100000UL
+#define ISR_PHYI 0x00080000UL
+#define ISR_PWEI 0x00040000UL
+#define ISR_TMR1I 0x00020000UL
+#define ISR_TMR0I 0x00010000UL
+#define ISR_SRCI 0x00008000UL
+#define ISR_LSTPEI 0x00004000UL
+#define ISR_LSTEI 0x00002000UL
+#define ISR_OVFI 0x00001000UL
+#define ISR_FLONI 0x00000800UL
+#define ISR_RACEI 0x00000400UL
+#define ISR_TXWB1I 0x00000200UL
+#define ISR_TXWB0I 0x00000100UL
+#define ISR_PTX3I 0x00000080UL
+#define ISR_PTX2I 0x00000040UL
+#define ISR_PTX1I 0x00000020UL
+#define ISR_PTX0I 0x00000010UL
+#define ISR_PTXI 0x00000008UL
+#define ISR_PRXI 0x00000004UL
+#define ISR_PPTXI 0x00000002UL
+#define ISR_PPRXI 0x00000001UL
+
+/*
+ * Bits in the IMR register
+ */
+
+#define IMR_TXSTLM 0x02000000UL
+#define IMR_UDPIM 0x00400000UL
+#define IMR_MIBFIM 0x00200000UL
+#define IMR_SHDNIM 0x00100000UL
+#define IMR_PHYIM 0x00080000UL
+#define IMR_PWEIM 0x00040000UL
+#define IMR_TMR1IM 0x00020000UL
+#define IMR_TMR0IM 0x00010000UL
+
+#define IMR_SRCIM 0x00008000UL
+#define IMR_LSTPEIM 0x00004000UL
+#define IMR_LSTEIM 0x00002000UL
+#define IMR_OVFIM 0x00001000UL
+#define IMR_FLONIM 0x00000800UL
+#define IMR_RACEIM 0x00000400UL
+#define IMR_TXWB1IM 0x00000200UL
+#define IMR_TXWB0IM 0x00000100UL
+
+#define IMR_PTX3IM 0x00000080UL
+#define IMR_PTX2IM 0x00000040UL
+#define IMR_PTX1IM 0x00000020UL
+#define IMR_PTX0IM 0x00000010UL
+#define IMR_PTXIM 0x00000008UL
+#define IMR_PRXIM 0x00000004UL
+#define IMR_PPTXIM 0x00000002UL
+#define IMR_PPRXIM 0x00000001UL
+
+/* 0x0013FB0FUL = initial value of IMR */
+
+#define INT_MASK_DEF ( IMR_PPTXIM|IMR_PPRXIM| IMR_PTXIM|IMR_PRXIM | \
+ IMR_PWEIM|IMR_TXWB0IM|IMR_TXWB1IM|IMR_FLONIM| \
+ IMR_OVFIM|IMR_LSTEIM|IMR_LSTPEIM|IMR_SRCIM|IMR_MIBFIM|\
+ IMR_SHDNIM |IMR_TMR1IM|IMR_TMR0IM|IMR_TXSTLM )
+
+/*
+ * Bits in the TDCSR0/1, RDCSR0 register
+ */
+
+#define TRDCSR_DEAD 0x0008
+#define TRDCSR_WAK 0x0004
+#define TRDCSR_ACT 0x0002
+#define TRDCSR_RUN 0x0001
+
+/*
+ * Bits in the CAMADDR register
+ */
+
+#define CAMADDR_CAMEN 0x80
+#define CAMADDR_VCAMSL 0x40
+
+/*
+ * Bits in the CAMCR register
+ */
+
+#define CAMCR_PS1 0x80
+#define CAMCR_PS0 0x40
+#define CAMCR_AITRPKT 0x20
+#define CAMCR_AITR16 0x10
+#define CAMCR_CAMRD 0x08
+#define CAMCR_CAMWR 0x04
+#define CAMCR_PS_CAM_MASK 0x40
+#define CAMCR_PS_CAM_DATA 0x80
+#define CAMCR_PS_MAR 0x00
+
+/*
+ * Bits in the MIICFG register
+ */
+
+#define MIICFG_MPO1 0x80
+#define MIICFG_MPO0 0x40
+#define MIICFG_MFDC 0x20
+
+/*
+ * Bits in the MIISR register
+ */
+
+#define MIISR_MIDLE 0x80
+
+/*
+ * Bits in the PHYSR0 register
+ */
+
+#define PHYSR0_PHYRST 0x80
+#define PHYSR0_LINKGD 0x40
+#define PHYSR0_FDPX 0x10
+#define PHYSR0_SPDG 0x08
+#define PHYSR0_SPD10 0x04
+#define PHYSR0_RXFLC 0x02
+#define PHYSR0_TXFLC 0x01
+
+/*
+ * Bits in the PHYSR1 register
+ */
+
+#define PHYSR1_PHYTBI 0x01
+
+/*
+ * Bits in the MIICR register
+ */
+
+#define MIICR_MAUTO 0x80
+#define MIICR_RCMD 0x40
+#define MIICR_WCMD 0x20
+#define MIICR_MDPM 0x10
+#define MIICR_MOUT 0x08
+#define MIICR_MDO 0x04
+#define MIICR_MDI 0x02
+#define MIICR_MDC 0x01
+
+/*
+ * Bits in the MIIADR register
+ */
+
+#define MIIADR_SWMPL 0x80
+
+/*
+ * Bits in the CFGA register
+ */
+
+#define CFGA_PMHCTG 0x08
+#define CFGA_GPIO1PD 0x04
+#define CFGA_ABSHDN 0x02
+#define CFGA_PACPI 0x01
+
+/*
+ * Bits in the CFGB register
+ */
+
+#define CFGB_GTCKOPT 0x80
+#define CFGB_MIIOPT 0x40
+#define CFGB_CRSEOPT 0x20
+#define CFGB_OFSET 0x10
+#define CFGB_CRANDOM 0x08
+#define CFGB_CAP 0x04
+#define CFGB_MBA 0x02
+#define CFGB_BAKOPT 0x01
+
+/*
+ * Bits in the CFGC register
+ */
+
+#define CFGC_EELOAD 0x80
+#define CFGC_BROPT 0x40
+#define CFGC_DLYEN 0x20
+#define CFGC_DTSEL 0x10
+#define CFGC_BTSEL 0x08
+#define CFGC_BPS2 0x04 /* bootrom select[2] */
+#define CFGC_BPS1 0x02 /* bootrom select[1] */
+#define CFGC_BPS0 0x01 /* bootrom select[0] */
+
+/*
+ * Bits in the CFGD register
+ */
+
+#define CFGD_IODIS 0x80
+#define CFGD_MSLVDACEN 0x40
+#define CFGD_CFGDACEN 0x20
+#define CFGD_PCI64EN 0x10
+#define CFGD_HTMRL4 0x08
+
+/*
+ * Bits in the DCFG1 register
+ */
+
+#define DCFG_XMWI 0x8000
+#define DCFG_XMRM 0x4000
+#define DCFG_XMRL 0x2000
+#define DCFG_PERDIS 0x1000
+#define DCFG_MRWAIT 0x0400
+#define DCFG_MWWAIT 0x0200
+#define DCFG_LATMEN 0x0100
+
+/*
+ * Bits in the MCFG0 register
+ */
+
+#define MCFG_RXARB 0x0080
+#define MCFG_RFT1 0x0020
+#define MCFG_RFT0 0x0010
+#define MCFG_LOWTHOPT 0x0008
+#define MCFG_PQEN 0x0004
+#define MCFG_RTGOPT 0x0002
+#define MCFG_VIDFR 0x0001
+
+/*
+ * Bits in the MCFG1 register
+ */
+
+#define MCFG_TXARB 0x8000
+#define MCFG_TXQBK1 0x0800
+#define MCFG_TXQBK0 0x0400
+#define MCFG_TXQNOBK 0x0200
+#define MCFG_SNAPOPT 0x0100
+
+/*
+ * Bits in the PMCC register
+ */
+
+#define PMCC_DSI 0x80
+#define PMCC_D2_DIS 0x40
+#define PMCC_D1_DIS 0x20
+#define PMCC_D3C_EN 0x10
+#define PMCC_D3H_EN 0x08
+#define PMCC_D2_EN 0x04
+#define PMCC_D1_EN 0x02
+#define PMCC_D0_EN 0x01
+
+/*
+ * Bits in STICKHW
+ */
+
+#define STICKHW_SWPTAG 0x10
+#define STICKHW_WOLSR 0x08
+#define STICKHW_WOLEN 0x04
+#define STICKHW_DS1 0x02 /* R/W by software/cfg cycle */
+#define STICKHW_DS0 0x01 /* suspend well DS write port */
+
+/*
+ * Bits in the MIBCR register
+ */
+
+#define MIBCR_MIBISTOK 0x80
+#define MIBCR_MIBISTGO 0x40
+#define MIBCR_MIBINC 0x20
+#define MIBCR_MIBHI 0x10
+#define MIBCR_MIBFRZ 0x08
+#define MIBCR_MIBFLSH 0x04
+#define MIBCR_MPTRINI 0x02
+#define MIBCR_MIBCLR 0x01
+
+/*
+ * Bits in the EERSV register
+ */
+
+#define EERSV_BOOT_RPL ((u8) 0x01) /* Boot method selection for VT6110 */
+
+#define EERSV_BOOT_MASK ((u8) 0x06)
+#define EERSV_BOOT_INT19 ((u8) 0x00)
+#define EERSV_BOOT_INT18 ((u8) 0x02)
+#define EERSV_BOOT_LOCAL ((u8) 0x04)
+#define EERSV_BOOT_BEV ((u8) 0x06)
+
+
+/*
+ * Bits in BPCMD
+ */
+
+#define BPCMD_BPDNE 0x80
+#define BPCMD_EBPWR 0x02
+#define BPCMD_EBPRD 0x01
+
+/*
+ * Bits in the EECSR register
+ */
+
+#define EECSR_EMBP 0x40 /* eeprom embeded programming */
+#define EECSR_RELOAD 0x20 /* eeprom content reload */
+#define EECSR_DPM 0x10 /* eeprom direct programming */
+#define EECSR_ECS 0x08 /* eeprom CS pin */
+#define EECSR_ECK 0x04 /* eeprom CK pin */
+#define EECSR_EDI 0x02 /* eeprom DI pin */
+#define EECSR_EDO 0x01 /* eeprom DO pin */
+
+/*
+ * Bits in the EMBCMD register
+ */
+
+#define EMBCMD_EDONE 0x80
+#define EMBCMD_EWDIS 0x08
+#define EMBCMD_EWEN 0x04
+#define EMBCMD_EWR 0x02
+#define EMBCMD_ERD 0x01
+
+/*
+ * Bits in TESTCFG register
+ */
+
+#define TESTCFG_HBDIS 0x80
+
+/*
+ * Bits in CHIPGCR register
+ */
+
+#define CHIPGCR_FCGMII 0x80
+#define CHIPGCR_FCFDX 0x40
+#define CHIPGCR_FCRESV 0x20
+#define CHIPGCR_FCMODE 0x10
+#define CHIPGCR_LPSOPT 0x08
+#define CHIPGCR_TM1US 0x04
+#define CHIPGCR_TM0US 0x02
+#define CHIPGCR_PHYINTEN 0x01
+
+/*
+ * Bits in WOLCR0
+ */
+
+#define WOLCR_MSWOLEN7 0x0080 /* enable pattern match filtering */
+#define WOLCR_MSWOLEN6 0x0040
+#define WOLCR_MSWOLEN5 0x0020
+#define WOLCR_MSWOLEN4 0x0010
+#define WOLCR_MSWOLEN3 0x0008
+#define WOLCR_MSWOLEN2 0x0004
+#define WOLCR_MSWOLEN1 0x0002
+#define WOLCR_MSWOLEN0 0x0001
+#define WOLCR_ARP_EN 0x0001
+
+/*
+ * Bits in WOLCR1
+ */
+
+#define WOLCR_LINKOFF_EN 0x0800 /* link off detected enable */
+#define WOLCR_LINKON_EN 0x0400 /* link on detected enable */
+#define WOLCR_MAGIC_EN 0x0200 /* magic packet filter enable */
+#define WOLCR_UNICAST_EN 0x0100 /* unicast filter enable */
+
+
+/*
+ * Bits in PWCFG
+ */
+
+#define PWCFG_PHYPWOPT 0x80 /* internal MII I/F timing */
+#define PWCFG_PCISTICK 0x40 /* PCI sticky R/W enable */
+#define PWCFG_WOLTYPE 0x20 /* pulse(1) or button (0) */
+#define PWCFG_LEGCY_WOL 0x10
+#define PWCFG_PMCSR_PME_SR 0x08
+#define PWCFG_PMCSR_PME_EN 0x04 /* control by PCISTICK */
+#define PWCFG_LEGACY_WOLSR 0x02 /* Legacy WOL_SR shadow */
+#define PWCFG_LEGACY_WOLEN 0x01 /* Legacy WOL_EN shadow */
+
+/*
+ * Bits in WOLCFG
+ */
+
+#define WOLCFG_PMEOVR 0x80 /* for legacy use, force PMEEN always */
+#define WOLCFG_SAM 0x20 /* accept multicast case reset, default=0 */
+#define WOLCFG_SAB 0x10 /* accept broadcast case reset, default=0 */
+#define WOLCFG_SMIIACC 0x08 /* ?? */
+#define WOLCFG_SGENWH 0x02
+#define WOLCFG_PHYINTEN 0x01 /* 0:PHYINT trigger enable, 1:use internal MII
+ to report status change */
+/*
+ * Bits in WOLSR1
+ */
+
+#define WOLSR_LINKOFF_INT 0x0800
+#define WOLSR_LINKON_INT 0x0400
+#define WOLSR_MAGIC_INT 0x0200
+#define WOLSR_UNICAST_INT 0x0100
+
+/*
+ * Ethernet address filter type
+ */
+
+#define PKT_TYPE_NONE 0x0000 /* Turn off receiver */
+#define PKT_TYPE_DIRECTED 0x0001 /* obselete, directed address is always accepted */
+#define PKT_TYPE_MULTICAST 0x0002
+#define PKT_TYPE_ALL_MULTICAST 0x0004
+#define PKT_TYPE_BROADCAST 0x0008
+#define PKT_TYPE_PROMISCUOUS 0x0020
+#define PKT_TYPE_LONG 0x2000 /* NOTE.... the definition of LONG is >2048 bytes in our chip */
+#define PKT_TYPE_RUNT 0x4000
+#define PKT_TYPE_ERROR 0x8000 /* Accept error packets, e.g. CRC error */
+
+/*
+ * Loopback mode
+ */
+
+#define MAC_LB_NONE 0x00
+#define MAC_LB_INTERNAL 0x01
+#define MAC_LB_EXTERNAL 0x02
+
+/*
+ * Enabled mask value of irq
+ */
+
+#if defined(_SIM)
+#define IMR_MASK_VALUE 0x0033FF0FUL /* initial value of IMR
+ set IMR0 to 0x0F according to spec */
+
+#else
+#define IMR_MASK_VALUE 0x0013FB0FUL /* initial value of IMR
+ ignore MIBFI,RACEI to
+ reduce intr. frequency
+ NOTE.... do not enable NoBuf int mask at driver driver
+ when (1) NoBuf -> RxThreshold = SF
+ (2) OK -> RxThreshold = original value
+ */
+#endif
+
+/*
+ * Revision id
+ */
+
+#define REV_ID_VT3119_A0 0x00
+#define REV_ID_VT3119_A1 0x01
+#define REV_ID_VT3216_A0 0x10
+
+/*
+ * Max time out delay time
+ */
+
+#define W_MAX_TIMEOUT 0x0FFFU
+
+
+/*
+ * MAC registers as a structure. Cannot be directly accessed this
+ * way but generates offsets for readl/writel() calls
+ */
+
+struct mac_regs {
+ volatile u8 PAR[6]; /* 0x00 */
+ volatile u8 RCR;
+ volatile u8 TCR;
+
+ volatile u32 CR0Set; /* 0x08 */
+ volatile u32 CR0Clr; /* 0x0C */
+
+ volatile u8 MARCAM[8]; /* 0x10 */
+
+ volatile u32 DecBaseHi; /* 0x18 */
+ volatile u16 DbfBaseHi; /* 0x1C */
+ volatile u16 reserved_1E;
+
+ volatile u16 ISRCTL; /* 0x20 */
+ volatile u8 TXESR;
+ volatile u8 RXESR;
+
+ volatile u32 ISR; /* 0x24 */
+ volatile u32 IMR;
+
+ volatile u32 TDStatusPort; /* 0x2C */
+
+ volatile u16 TDCSRSet; /* 0x30 */
+ volatile u8 RDCSRSet;
+ volatile u8 reserved_33;
+ volatile u16 TDCSRClr;
+ volatile u8 RDCSRClr;
+ volatile u8 reserved_37;
+
+ volatile u32 RDBaseLo; /* 0x38 */
+ volatile u16 RDIdx; /* 0x3C */
+ volatile u16 reserved_3E;
+
+ volatile u32 TDBaseLo[4]; /* 0x40 */
+
+ volatile u16 RDCSize; /* 0x50 */
+ volatile u16 TDCSize; /* 0x52 */
+ volatile u16 TDIdx[4]; /* 0x54 */
+ volatile u16 tx_pause_timer; /* 0x5C */
+ volatile u16 RBRDU; /* 0x5E */
+
+ volatile u32 FIFOTest0; /* 0x60 */
+ volatile u32 FIFOTest1; /* 0x64 */
+
+ volatile u8 CAMADDR; /* 0x68 */
+ volatile u8 CAMCR; /* 0x69 */
+ volatile u8 GFTEST; /* 0x6A */
+ volatile u8 FTSTCMD; /* 0x6B */
+
+ volatile u8 MIICFG; /* 0x6C */
+ volatile u8 MIISR;
+ volatile u8 PHYSR0;
+ volatile u8 PHYSR1;
+ volatile u8 MIICR;
+ volatile u8 MIIADR;
+ volatile u16 MIIDATA;
+
+ volatile u16 SoftTimer0; /* 0x74 */
+ volatile u16 SoftTimer1;
+
+ volatile u8 CFGA; /* 0x78 */
+ volatile u8 CFGB;
+ volatile u8 CFGC;
+ volatile u8 CFGD;
+
+ volatile u16 DCFG; /* 0x7C */
+ volatile u16 MCFG;
+
+ volatile u8 TBIST; /* 0x80 */
+ volatile u8 RBIST;
+ volatile u8 PMCPORT;
+ volatile u8 STICKHW;
+
+ volatile u8 MIBCR; /* 0x84 */
+ volatile u8 reserved_85;
+ volatile u8 rev_id;
+ volatile u8 PORSTS;
+
+ volatile u32 MIBData; /* 0x88 */
+
+ volatile u16 EEWrData;
+
+ volatile u8 reserved_8E;
+ volatile u8 BPMDWr;
+ volatile u8 BPCMD;
+ volatile u8 BPMDRd;
+
+ volatile u8 EECHKSUM; /* 0x92 */
+ volatile u8 EECSR;
+
+ volatile u16 EERdData; /* 0x94 */
+ volatile u8 EADDR;
+ volatile u8 EMBCMD;
+
+
+ volatile u8 JMPSR0; /* 0x98 */
+ volatile u8 JMPSR1;
+ volatile u8 JMPSR2;
+ volatile u8 JMPSR3;
+ volatile u8 CHIPGSR; /* 0x9C */
+ volatile u8 TESTCFG;
+ volatile u8 DEBUG;
+ volatile u8 CHIPGCR;
+
+ volatile u16 WOLCRSet; /* 0xA0 */
+ volatile u8 PWCFGSet;
+ volatile u8 WOLCFGSet;
+
+ volatile u16 WOLCRClr; /* 0xA4 */
+ volatile u8 PWCFGCLR;
+ volatile u8 WOLCFGClr;
+
+ volatile u16 WOLSRSet; /* 0xA8 */
+ volatile u16 reserved_AA;
+
+ volatile u16 WOLSRClr; /* 0xAC */
+ volatile u16 reserved_AE;
+
+ volatile u16 PatternCRC[8]; /* 0xB0 */
+ volatile u32 ByteMask[4][4]; /* 0xC0 */
+} __attribute__ ((__packed__));
+
+
+enum hw_mib {
+ HW_MIB_ifRxAllPkts = 0,
+ HW_MIB_ifRxOkPkts,
+ HW_MIB_ifTxOkPkts,
+ HW_MIB_ifRxErrorPkts,
+ HW_MIB_ifRxRuntOkPkt,
+ HW_MIB_ifRxRuntErrPkt,
+ HW_MIB_ifRx64Pkts,
+ HW_MIB_ifTx64Pkts,
+ HW_MIB_ifRx65To127Pkts,
+ HW_MIB_ifTx65To127Pkts,
+ HW_MIB_ifRx128To255Pkts,
+ HW_MIB_ifTx128To255Pkts,
+ HW_MIB_ifRx256To511Pkts,
+ HW_MIB_ifTx256To511Pkts,
+ HW_MIB_ifRx512To1023Pkts,
+ HW_MIB_ifTx512To1023Pkts,
+ HW_MIB_ifRx1024To1518Pkts,
+ HW_MIB_ifTx1024To1518Pkts,
+ HW_MIB_ifTxEtherCollisions,
+ HW_MIB_ifRxPktCRCE,
+ HW_MIB_ifRxJumboPkts,
+ HW_MIB_ifTxJumboPkts,
+ HW_MIB_ifRxMacControlFrames,
+ HW_MIB_ifTxMacControlFrames,
+ HW_MIB_ifRxPktFAE,
+ HW_MIB_ifRxLongOkPkt,
+ HW_MIB_ifRxLongPktErrPkt,
+ HW_MIB_ifTXSQEErrors,
+ HW_MIB_ifRxNobuf,
+ HW_MIB_ifRxSymbolErrors,
+ HW_MIB_ifInRangeLengthErrors,
+ HW_MIB_ifLateCollisions,
+ HW_MIB_SIZE
+};
+
+enum chip_type {
+ CHIP_TYPE_VT6110 = 1,
+};
+
+struct velocity_info_tbl {
+ enum chip_type chip_id;
+ char *name;
+ int io_size;
+ int txqueue;
+ u32 flags;
+};
+
+static struct velocity_info_tbl *info;
+
+#define mac_hw_mibs_init(regs) {\
+ BYTE_REG_BITS_ON(MIBCR_MIBFRZ,&((regs)->MIBCR));\
+ BYTE_REG_BITS_ON(MIBCR_MIBCLR,&((regs)->MIBCR));\
+ do {}\
+ while (BYTE_REG_BITS_IS_ON(MIBCR_MIBCLR,&((regs)->MIBCR)));\
+ BYTE_REG_BITS_OFF(MIBCR_MIBFRZ,&((regs)->MIBCR));\
+}
+
+#define mac_read_isr(regs) readl(&((regs)->ISR))
+#define mac_write_isr(regs, x) writel((x),&((regs)->ISR))
+#define mac_clear_isr(regs) writel(0xffffffffL,&((regs)->ISR))
+
+#define mac_write_int_mask(mask, regs) writel((mask),&((regs)->IMR));
+#define mac_disable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Clr))
+#define mac_enable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Set))
+
+#define mac_hw_mibs_read(regs, MIBs) {\
+ int i;\
+ BYTE_REG_BITS_ON(MIBCR_MPTRINI,&((regs)->MIBCR));\
+ for (i=0;i<HW_MIB_SIZE;i++) {\
+ (MIBs)[i]=readl(&((regs)->MIBData));\
+ }\
+}
+
+#define mac_set_dma_length(regs, n) {\
+ BYTE_REG_BITS_SET((n),0x07,&((regs)->DCFG));\
+}
+
+#define mac_set_rx_thresh(regs, n) {\
+ BYTE_REG_BITS_SET((n),(MCFG_RFT0|MCFG_RFT1),&((regs)->MCFG));\
+}
+
+#define mac_rx_queue_run(regs) {\
+ writeb(TRDCSR_RUN, &((regs)->RDCSRSet));\
+}
+
+#define mac_rx_queue_wake(regs) {\
+ writeb(TRDCSR_WAK, &((regs)->RDCSRSet));\
+}
+
+#define mac_tx_queue_run(regs, n) {\
+ writew(TRDCSR_RUN<<((n)*4),&((regs)->TDCSRSet));\
+}
+
+#define mac_tx_queue_wake(regs, n) {\
+ writew(TRDCSR_WAK<<(n*4),&((regs)->TDCSRSet));\
+}
+
+#define mac_eeprom_reload(regs) {\
+ int i=0;\
+ BYTE_REG_BITS_ON(EECSR_RELOAD,&((regs)->EECSR));\
+ do {\
+ udelay(10);\
+ if (i++>0x1000) {\
+ break;\
+ }\
+ }while (BYTE_REG_BITS_IS_ON(EECSR_RELOAD,&((regs)->EECSR)));\
+}
+
+enum velocity_cam_type {
+ VELOCITY_VLAN_ID_CAM = 0,
+ VELOCITY_MULTICAST_CAM
+};
+
+/**
+ * mac_get_cam_mask - Read a CAM mask
+ * @regs: register block for this velocity
+ * @mask: buffer to store mask
+ * @cam_type: CAM to fetch
+ *
+ * Fetch the mask bits of the selected CAM and store them into the
+ * provided mask buffer.
+ */
+
+static inline void mac_get_cam_mask(struct mac_regs *regs, u8 * mask,
+ enum velocity_cam_type cam_type)
+{
+ int i;
+ /* Select CAM mask */
+ BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0,
+ &regs->CAMCR);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ writeb(CAMADDR_VCAMSL, &regs->CAMADDR);
+ else
+ writeb(0, &regs->CAMADDR);
+
+ /* read mask */
+ for (i = 0; i < 8; i++)
+ *mask++ = readb(&(regs->MARCAM[i]));
+
+ /* disable CAMEN */
+ writeb(0, &regs->CAMADDR);
+
+ /* Select mar */
+ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0,
+ &regs->CAMCR);
+
+}
+
+/**
+ * mac_set_cam_mask - Set a CAM mask
+ * @regs: register block for this velocity
+ * @mask: CAM mask to load
+ * @cam_type: CAM to store
+ *
+ * Store a new mask into a CAM
+ */
+
+static inline void mac_set_cam_mask(struct mac_regs *regs, u8 * mask,
+ enum velocity_cam_type cam_type)
+{
+ int i;
+ /* Select CAM mask */
+ BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0,
+ &regs->CAMCR);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL, &regs->CAMADDR);
+ else
+ writeb(CAMADDR_CAMEN, &regs->CAMADDR);
+
+ for (i = 0; i < 8; i++) {
+ writeb(*mask++, &(regs->MARCAM[i]));
+ }
+ /* disable CAMEN */
+ writeb(0, &regs->CAMADDR);
+
+ /* Select mar */
+ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0,
+ &regs->CAMCR);
+}
+
+/**
+ * mac_set_cam - set CAM data
+ * @regs: register block of this velocity
+ * @idx: Cam index
+ * @addr: 2 or 6 bytes of CAM data
+ * @cam_type: CAM to load
+ *
+ * Load an address or vlan tag into a CAM
+ */
+
+static inline void mac_set_cam(struct mac_regs *regs, int idx, u8 * addr,
+ enum velocity_cam_type cam_type)
+{
+ int i;
+
+ /* Select CAM mask */
+ BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0,
+ &regs->CAMCR);
+
+ idx &= (64 - 1);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx,
+ &regs->CAMADDR);
+ else
+ writeb(CAMADDR_CAMEN | idx, &regs->CAMADDR);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ writew(*((u16 *) addr), &regs->MARCAM[0]);
+ else {
+ for (i = 0; i < 6; i++) {
+ writeb(*addr++, &(regs->MARCAM[i]));
+ }
+ }
+ BYTE_REG_BITS_ON(CAMCR_CAMWR, &regs->CAMCR);
+
+ udelay(10);
+
+ writeb(0, &regs->CAMADDR);
+
+ /* Select mar */
+ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0,
+ &regs->CAMCR);
+}
+
+/**
+ * mac_get_cam - fetch CAM data
+ * @regs: register block of this velocity
+ * @idx: Cam index
+ * @addr: buffer to hold up to 6 bytes of CAM data
+ * @cam_type: CAM to load
+ *
+ * Load an address or vlan tag from a CAM into the buffer provided by
+ * the caller. VLAN tags are 2 bytes the address cam entries are 6.
+ */
+
+static inline void mac_get_cam(struct mac_regs *regs, int idx, u8 * addr,
+ enum velocity_cam_type cam_type)
+{
+ int i;
+
+ /* Select CAM mask */
+ BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0,
+ &regs->CAMCR);
+
+ idx &= (64 - 1);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx,
+ &regs->CAMADDR);
+ else
+ writeb(CAMADDR_CAMEN | idx, &regs->CAMADDR);
+
+ BYTE_REG_BITS_ON(CAMCR_CAMRD, &regs->CAMCR);
+
+ udelay(10);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ *((u16 *) addr) = readw(&(regs->MARCAM[0]));
+ else
+ for (i = 0; i < 6; i++, addr++)
+ *((u8 *) addr) = readb(&(regs->MARCAM[i]));
+
+ writeb(0, &regs->CAMADDR);
+
+ /* Select mar */
+ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0,
+ &regs->CAMCR);
+}
+
+/**
+ * mac_wol_reset - reset WOL after exiting low power
+ * @regs: register block of this velocity
+ *
+ * Called after we drop out of wake on lan mode in order to
+ * reset the Wake on lan features. This function doesn't restore
+ * the rest of the logic from the result of sleep/wakeup
+ */
+
+inline static void mac_wol_reset(struct mac_regs *regs)
+{
+
+ /* Turn off SWPTAG right after leaving power mode */
+ BYTE_REG_BITS_OFF(STICKHW_SWPTAG, &regs->STICKHW);
+ /* clear sticky bits */
+ BYTE_REG_BITS_OFF((STICKHW_DS1 | STICKHW_DS0), &regs->STICKHW);
+
+ BYTE_REG_BITS_OFF(CHIPGCR_FCGMII, &regs->CHIPGCR);
+ BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, &regs->CHIPGCR);
+ /* disable force PME-enable */
+ writeb(WOLCFG_PMEOVR, &regs->WOLCFGClr);
+ /* disable power-event config bit */
+ writew(0xFFFF, &regs->WOLCRClr);
+ /* clear power status */
+ writew(0xFFFF, &regs->WOLSRClr);
+}
+
+
+/*
+ * Header for WOL definitions. Used to compute hashes
+ */
+
+typedef u8 MCAM_ADDR[ETH_ALEN];
+
+struct arp_packet {
+ u8 dest_mac[ETH_ALEN];
+ u8 src_mac[ETH_ALEN];
+ u16 type;
+ u16 ar_hrd;
+ u16 ar_pro;
+ u8 ar_hln;
+ u8 ar_pln;
+ u16 ar_op;
+ u8 ar_sha[ETH_ALEN];
+ u8 ar_sip[4];
+ u8 ar_tha[ETH_ALEN];
+ u8 ar_tip[4];
+} __attribute__ ((__packed__));
+
+struct _magic_packet {
+ u8 dest_mac[6];
+ u8 src_mac[6];
+ u16 type;
+ u8 MAC[16][6];
+ u8 password[6];
+} __attribute__ ((__packed__));
+
+/*
+ * Store for chip context when saving and restoring status. Not
+ * all fields are saved/restored currently.
+ */
+
+struct velocity_context {
+ u8 mac_reg[256];
+ MCAM_ADDR cam_addr[MCAM_SIZE];
+ u16 vcam[VCAM_SIZE];
+ u32 cammask[2];
+ u32 patcrc[2];
+ u32 pattern[8];
+};
+
+
+/*
+ * MII registers.
+ */
+
+
+/*
+ * Registers in the MII (offset unit is WORD)
+ */
+
+#define MII_REG_BMCR 0x00 // physical address
+#define MII_REG_BMSR 0x01 //
+#define MII_REG_PHYID1 0x02 // OUI
+#define MII_REG_PHYID2 0x03 // OUI + Module ID + REV ID
+#define MII_REG_ANAR 0x04 //
+#define MII_REG_ANLPAR 0x05 //
+#define MII_REG_G1000CR 0x09 //
+#define MII_REG_G1000SR 0x0A //
+#define MII_REG_MODCFG 0x10 //
+#define MII_REG_TCSR 0x16 //
+#define MII_REG_PLED 0x1B //
+// NS, MYSON only
+#define MII_REG_PCR 0x17 //
+// ESI only
+#define MII_REG_PCSR 0x17 //
+#define MII_REG_AUXCR 0x1C //
+
+// Marvell 88E1000/88E1000S
+#define MII_REG_PSCR 0x10 // PHY specific control register
+
+//
+// Bits in the BMCR register
+//
+#define BMCR_RESET 0x8000 //
+#define BMCR_LBK 0x4000 //
+#define BMCR_SPEED100 0x2000 //
+#define BMCR_AUTO 0x1000 //
+#define BMCR_PD 0x0800 //
+#define BMCR_ISO 0x0400 //
+#define BMCR_REAUTO 0x0200 //
+#define BMCR_FDX 0x0100 //
+#define BMCR_SPEED1G 0x0040 //
+//
+// Bits in the BMSR register
+//
+#define BMSR_AUTOCM 0x0020 //
+#define BMSR_LNK 0x0004 //
+
+//
+// Bits in the ANAR register
+//
+#define ANAR_ASMDIR 0x0800 // Asymmetric PAUSE support
+#define ANAR_PAUSE 0x0400 // Symmetric PAUSE Support
+#define ANAR_T4 0x0200 //
+#define ANAR_TXFD 0x0100 //
+#define ANAR_TX 0x0080 //
+#define ANAR_10FD 0x0040 //
+#define ANAR_10 0x0020 //
+//
+// Bits in the ANLPAR register
+//
+#define ANLPAR_ASMDIR 0x0800 // Asymmetric PAUSE support
+#define ANLPAR_PAUSE 0x0400 // Symmetric PAUSE Support
+#define ANLPAR_T4 0x0200 //
+#define ANLPAR_TXFD 0x0100 //
+#define ANLPAR_TX 0x0080 //
+#define ANLPAR_10FD 0x0040 //
+#define ANLPAR_10 0x0020 //
+
+//
+// Bits in the G1000CR register
+//
+#define G1000CR_1000FD 0x0200 // PHY is 1000-T Full-duplex capable
+#define G1000CR_1000 0x0100 // PHY is 1000-T Half-duplex capable
+
+//
+// Bits in the G1000SR register
+//
+#define G1000SR_1000FD 0x0800 // LP PHY is 1000-T Full-duplex capable
+#define G1000SR_1000 0x0400 // LP PHY is 1000-T Half-duplex capable
+
+#define TCSR_ECHODIS 0x2000 //
+#define AUXCR_MDPPS 0x0004 //
+
+// Bits in the PLED register
+#define PLED_LALBE 0x0004 //
+
+// Marvell 88E1000/88E1000S Bits in the PHY specific control register (10h)
+#define PSCR_ACRSTX 0x0800 // Assert CRS on Transmit
+
+#define PHYID_CICADA_CS8201 0x000FC410UL
+#define PHYID_VT3216_32BIT 0x000FC610UL
+#define PHYID_VT3216_64BIT 0x000FC600UL
+#define PHYID_MARVELL_1000 0x01410C50UL
+#define PHYID_MARVELL_1000S 0x01410C40UL
+
+#define PHYID_REV_ID_MASK 0x0000000FUL
+
+#define PHYID_GET_PHY_REV_ID(i) ((i) & PHYID_REV_ID_MASK)
+#define PHYID_GET_PHY_ID(i) ((i) & ~PHYID_REV_ID_MASK)
+
+#define MII_REG_BITS_ON(x,i,p) do {\
+ u16 w;\
+ velocity_mii_read((p),(i),&(w));\
+ (w)|=(x);\
+ velocity_mii_write((p),(i),(w));\
+} while (0)
+
+#define MII_REG_BITS_OFF(x,i,p) do {\
+ u16 w;\
+ velocity_mii_read((p),(i),&(w));\
+ (w)&=(~(x));\
+ velocity_mii_write((p),(i),(w));\
+} while (0)
+
+#define MII_REG_BITS_IS_ON(x,i,p) ({\
+ u16 w;\
+ velocity_mii_read((p),(i),&(w));\
+ ((int) ((w) & (x)));})
+
+#define MII_GET_PHY_ID(p) ({\
+ u32 id; \
+ u16 id2; \
+ u16 id1; \
+ velocity_mii_read((p),MII_REG_PHYID2, &id2);\
+ velocity_mii_read((p),MII_REG_PHYID1, &id1);\
+ id = ( ( (u32)id2 ) << 16 ) | id1; \
+ (id);})
+
+#ifdef LINUX
+/*
+ * Inline debug routine
+ */
+
+
+enum velocity_msg_level {
+ MSG_LEVEL_ERR = 0, //Errors that will cause abnormal operation.
+ MSG_LEVEL_NOTICE = 1, //Some errors need users to be notified.
+ MSG_LEVEL_INFO = 2, //Normal message.
+ MSG_LEVEL_VERBOSE = 3, //Will report all trival errors.
+ MSG_LEVEL_DEBUG = 4 //Only for debug purpose.
+};
+
+#ifdef VELOCITY_DEBUG
+#define ASSERT(x) { \
+ if (!(x)) { \
+ printk(KERN_ERR "assertion %s failed: file %s line %d\n", #x,\
+ __FUNCTION__, __LINE__);\
+ BUG(); \
+ }\
+}
+#define VELOCITY_DBG(p,args...) printk(p, ##args)
+#else
+#define ASSERT(x)
+#define VELOCITY_DBG(x)
+#endif
+
+#define VELOCITY_PRT(l, p, args...) do {if (l<=msglevel) printf( p ,##args);} while (0)
+
+#define VELOCITY_PRT_CAMMASK(p,t) {\
+ int i;\
+ if ((t)==VELOCITY_MULTICAST_CAM) {\
+ for (i=0;i<(MCAM_SIZE/8);i++)\
+ printk("%02X",(p)->mCAMmask[i]);\
+ }\
+ else {\
+ for (i=0;i<(VCAM_SIZE/8);i++)\
+ printk("%02X",(p)->vCAMmask[i]);\
+ }\
+ printk("\n");\
+}
+
+#endif
+
+#define VELOCITY_WOL_MAGIC 0x00000000UL
+#define VELOCITY_WOL_PHY 0x00000001UL
+#define VELOCITY_WOL_ARP 0x00000002UL
+#define VELOCITY_WOL_UCAST 0x00000004UL
+#define VELOCITY_WOL_BCAST 0x00000010UL
+#define VELOCITY_WOL_MCAST 0x00000020UL
+#define VELOCITY_WOL_MAGIC_SEC 0x00000040UL
+
+/*
+ * Flags for options
+ */
+
+#define VELOCITY_FLAGS_TAGGING 0x00000001UL
+#define VELOCITY_FLAGS_TX_CSUM 0x00000002UL
+#define VELOCITY_FLAGS_RX_CSUM 0x00000004UL
+#define VELOCITY_FLAGS_IP_ALIGN 0x00000008UL
+#define VELOCITY_FLAGS_VAL_PKT_LEN 0x00000010UL
+
+#define VELOCITY_FLAGS_FLOW_CTRL 0x01000000UL
+
+/*
+ * Flags for driver status
+ */
+
+#define VELOCITY_FLAGS_OPENED 0x00010000UL
+#define VELOCITY_FLAGS_VMNS_CONNECTED 0x00020000UL
+#define VELOCITY_FLAGS_VMNS_COMMITTED 0x00040000UL
+#define VELOCITY_FLAGS_WOL_ENABLED 0x00080000UL
+
+/*
+ * Flags for MII status
+ */
+
+#define VELOCITY_LINK_FAIL 0x00000001UL
+#define VELOCITY_SPEED_10 0x00000002UL
+#define VELOCITY_SPEED_100 0x00000004UL
+#define VELOCITY_SPEED_1000 0x00000008UL
+#define VELOCITY_DUPLEX_FULL 0x00000010UL
+#define VELOCITY_AUTONEG_ENABLE 0x00000020UL
+#define VELOCITY_FORCED_BY_EEPROM 0x00000040UL
+
+/*
+ * For velocity_set_media_duplex
+ */
+
+#define VELOCITY_LINK_CHANGE 0x00000001UL
+
+enum speed_opt {
+ SPD_DPX_AUTO = 0,
+ SPD_DPX_100_HALF = 1,
+ SPD_DPX_100_FULL = 2,
+ SPD_DPX_10_HALF = 3,
+ SPD_DPX_10_FULL = 4
+};
+
+enum velocity_init_type {
+ VELOCITY_INIT_COLD = 0,
+ VELOCITY_INIT_RESET,
+ VELOCITY_INIT_WOL
+};
+
+enum velocity_flow_cntl_type {
+ FLOW_CNTL_DEFAULT = 1,
+ FLOW_CNTL_TX,
+ FLOW_CNTL_RX,
+ FLOW_CNTL_TX_RX,
+ FLOW_CNTL_DISABLE,
+};
+
+struct velocity_opt {
+ int numrx; /* Number of RX descriptors */
+ int numtx; /* Number of TX descriptors */
+ enum speed_opt spd_dpx; /* Media link mode */
+ int vid; /* vlan id */
+ int DMA_length; /* DMA length */
+ int rx_thresh; /* RX_THRESH */
+ int flow_cntl;
+ int wol_opts; /* Wake on lan options */
+ int td_int_count;
+ int int_works;
+ int rx_bandwidth_hi;
+ int rx_bandwidth_lo;
+ int rx_bandwidth_en;
+ u32 flags;
+};
+
+#define RX_DESC_MIN 4
+#define RX_DESC_MAX 255
+#define RX_DESC_DEF RX_DESC_MIN
+
+#define TX_DESC_MIN 1
+#define TX_DESC_MAX 256
+#define TX_DESC_DEF TX_DESC_MIN
+
+static struct velocity_info {
+// struct list_head list;
+
+ struct pci_device *pdev;
+// struct net_device *dev;
+// struct net_device_stats stats;
+
+#ifdef CONFIG_PM
+ u32 pci_state[16];
+#endif
+
+// dma_addr_t rd_pool_dma;
+// dma_addr_t td_pool_dma[TX_QUEUE_NO];
+
+// dma_addr_t tx_bufs_dma;
+ u8 *tx_bufs;
+
+ u8 ip_addr[4];
+ enum chip_type chip_id;
+
+ struct mac_regs *mac_regs;
+ unsigned long memaddr;
+ unsigned long ioaddr;
+ u32 io_size;
+
+ u8 rev_id;
+
+#define AVAIL_TD(p,q) ((p)->options.numtx-((p)->td_used[(q)]))
+
+ int num_txq;
+
+ volatile int td_used[TX_QUEUE_NO];
+ int td_curr;
+ int td_tail[TX_QUEUE_NO];
+ unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */
+ unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */
+ unsigned char *tx_buffs;
+ unsigned char *rx_buffs;
+
+ unsigned char *txb;
+ unsigned char *rxb;
+ struct tx_desc *td_rings;
+ struct velocity_td_info *td_infos[TX_QUEUE_NO];
+
+ int rd_curr;
+ int rd_dirty;
+ u32 rd_filled;
+ struct rx_desc *rd_ring;
+ struct velocity_rd_info *rd_info; /* It's an array */
+
+#define GET_RD_BY_IDX(vptr, idx) (vptr->rd_ring[idx])
+ u32 mib_counter[MAX_HW_MIB_COUNTER];
+ struct velocity_opt options;
+
+ u32 int_mask;
+
+ u32 flags;
+
+ int rx_buf_sz;
+ u32 mii_status;
+ u32 phy_id;
+ int multicast_limit;
+
+ u8 vCAMmask[(VCAM_SIZE / 8)];
+ u8 mCAMmask[(MCAM_SIZE / 8)];
+
+// spinlock_t lock;
+
+ int wol_opts;
+ u8 wol_passwd[6];
+
+ struct velocity_context context;
+
+ u32 ticks;
+ u32 rx_bytes;
+
+} vptx;
+
+static struct velocity_info *vptr;
+
+#ifdef LINUX
+/**
+ * velocity_get_ip - find an IP address for the device
+ * @vptr: Velocity to query
+ *
+ * Dig out an IP address for this interface so that we can
+ * configure wakeup with WOL for ARP. If there are multiple IP
+ * addresses on this chain then we use the first - multi-IP WOL is not
+ * supported.
+ *
+ * CHECK ME: locking
+ */
+
+inline static int velocity_get_ip(struct velocity_info *vptr)
+{
+ struct in_device *in_dev = (struct in_device *) vptr->dev->ip_ptr;
+ struct in_ifaddr *ifa;
+
+ if (in_dev != NULL) {
+ ifa = (struct in_ifaddr *) in_dev->ifa_list;
+ if (ifa != NULL) {
+ memcpy(vptr->ip_addr, &ifa->ifa_address, 4);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+/**
+ * velocity_update_hw_mibs - fetch MIB counters from chip
+ * @vptr: velocity to update
+ *
+ * The velocity hardware keeps certain counters in the hardware
+ * side. We need to read these when the user asks for statistics
+ * or when they overflow (causing an interrupt). The read of the
+ * statistic clears it, so we keep running master counters in user
+ * space.
+ */
+
+static inline void velocity_update_hw_mibs(struct velocity_info *vptr)
+{
+ u32 tmp;
+ int i;
+ BYTE_REG_BITS_ON(MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR));
+
+ while (BYTE_REG_BITS_IS_ON
+ (MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR)));
+
+ BYTE_REG_BITS_ON(MIBCR_MPTRINI, &(vptr->mac_regs->MIBCR));
+ for (i = 0; i < HW_MIB_SIZE; i++) {
+ tmp = readl(&(vptr->mac_regs->MIBData)) & 0x00FFFFFFUL;
+ vptr->mib_counter[i] += tmp;
+ }
+}
+#endif
+/**
+ * init_flow_control_register - set up flow control
+ * @vptr: velocity to configure
+ *
+ * Configure the flow control registers for this velocity device.
+ */
+
+static inline void init_flow_control_register(struct velocity_info *vptr)
+{
+ struct mac_regs *regs = vptr->mac_regs;
+
+ /* Set {XHITH1, XHITH0, XLTH1, XLTH0} in FlowCR1 to {1, 0, 1, 1}
+ depend on RD=64, and Turn on XNOEN in FlowCR1 */
+ writel((CR0_XONEN | CR0_XHITH1 | CR0_XLTH1 | CR0_XLTH0),
+ &regs->CR0Set);
+ writel((CR0_FDXTFCEN | CR0_FDXRFCEN | CR0_HDXFCEN | CR0_XHITH0),
+ &regs->CR0Clr);
+
+ /* Set TxPauseTimer to 0xFFFF */
+ writew(0xFFFF, &regs->tx_pause_timer);
+
+ /* Initialize RBRDU to Rx buffer count. */
+ writew(vptr->options.numrx, &regs->RBRDU);
+}
+
+
+#endif
diff --git a/gpxe/src/drivers/net/w89c840.c b/gpxe/src/drivers/net/w89c840.c
new file mode 100644
index 00000000..14497640
--- /dev/null
+++ b/gpxe/src/drivers/net/w89c840.c
@@ -0,0 +1,962 @@
+/*
+ * Etherboot - BOOTP/TFTP Bootstrap Program
+ *
+ * w89c840.c -- This file implements the winbond-840 driver for etherboot.
+ *
+ */
+
+/*
+ * Adapted by Igor V. Kovalenko
+ * -- <garrison@mail.ru>
+ * OR
+ * -- <iko@crec.mipt.ru>
+ * Initial adaptaion stage, including testing, completed 23 August 2000.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * date version by what
+ * Written: Aug 20 2000 V0.10 iko Initial revision.
+ * changes: Aug 22 2000 V0.90 iko Works!
+ * Aug 23 2000 V0.91 iko Cleanup, posted to etherboot
+ * maintainer.
+ * Aug 26 2000 V0.92 iko Fixed Rx ring handling.
+ * First Linux Kernel (TM)
+ * successfully loaded using
+ * this driver.
+ * Jan 07 2001 V0.93 iko Transmitter timeouts are handled
+ * using timer2 routines. Proposed
+ * by Ken Yap to eliminate CPU speed
+ * dependency.
+ * Dec 12 2003 V0.94 timlegge Fixed issues in 5.2, removed
+ * interrupt usage, enabled
+ * multicast support
+ *
+ * This is the etherboot driver for cards based on Winbond W89c840F chip.
+ *
+ * It was written from skeleton source, with Donald Becker's winbond-840.c
+ * kernel driver as a guideline. Mostly the w89c840 related definitions
+ * and the lower level routines have been cut-and-pasted into this source.
+ *
+ * Frankly speaking, about 90% of the code was obtained using cut'n'paste
+ * sequence :) while the remainder appeared while brainstorming
+ * Linux Kernel 2.4.0-testX source code. Thanks, Donald and Linus!
+ *
+ * There was a demand for using this card in a rather large
+ * remote boot environment at MSKP OVTI Lab of
+ * Moscow Institute for Physics and Technology (MIPT) -- http://www.mipt.ru/
+ * so you may count that for motivation.
+ *
+ */
+
+/*
+ * If you want to see debugging output then define W89C840_DEBUG
+ */
+
+/*
+#define W89C840_DEBUG
+*/
+
+/*
+ * Keep using IO_OPS for Etherboot driver!
+ */
+#define USE_IO_OPS
+
+#include "etherboot.h"
+#include "nic.h"
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+
+static const char *w89c840_version = "driver Version 0.94 - December 12, 2003";
+
+/* Linux support functions */
+#define virt_to_le32desc(addr) virt_to_bus(addr)
+#define le32desc_to_virt(addr) bus_to_virt(addr)
+
+/*
+#define cpu_to_le32(val) (val)
+#define le32_to_cpu(val) (val)
+*/
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+ The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+ Making the Tx ring too large decreases the effectiveness of channel
+ bonding and packet priority.
+ There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE 2
+#define RX_RING_SIZE 2
+
+/* The presumed FIFO size for working around the Tx-FIFO-overflow bug.
+ To avoid overflowing we don't queue again until we have room for a
+ full-size packet.
+ */
+#define TX_FIFO_SIZE (2048)
+#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16)
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (10*USECS_IN_MSEC)
+
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
+/*
+ * Used to be this much CPU loops on Celeron@400 (?),
+ * now using real timer and TX_TIMEOUT!
+ * #define TX_LOOP_COUNT 10000000
+ */
+
+#if !defined(__OPTIMIZE__)
+#warning You must compile this file with the correct options!
+#warning See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+enum chip_capability_flags {CanHaveMII=1, HasBrokenTx=2};
+
+#ifdef USE_IO_OPS
+#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER)
+#else
+#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER)
+#endif
+
+static u32 driver_flags = CanHaveMII | HasBrokenTx;
+
+/* This driver was written to use PCI memory space, however some x86 systems
+ work only with I/O space accesses. Pass -DUSE_IO_OPS to use PCI I/O space
+ accesses instead of memory space. */
+
+#ifdef USE_IO_OPS
+#undef readb
+#undef readw
+#undef readl
+#undef writeb
+#undef writew
+#undef writel
+#define readb inb
+#define readw inw
+#define readl inl
+#define writeb outb
+#define writew outw
+#define writel outl
+#endif
+
+/* Offsets to the Command and Status Registers, "CSRs".
+ While similar to the Tulip, these registers are longword aligned.
+ Note: It's not useful to define symbolic names for every register bit in
+ the device. The name can only partially document the semantics and make
+ the driver longer and more difficult to read.
+*/
+enum w840_offsets {
+ PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08,
+ RxRingPtr=0x0C, TxRingPtr=0x10,
+ IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C,
+ RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C,
+ CurRxDescAddr=0x30, CurRxBufAddr=0x34, /* Debug use */
+ MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40,
+ CurTxDescAddr=0x4C, CurTxBufAddr=0x50,
+};
+
+/* Bits in the interrupt status/enable registers. */
+/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
+enum intr_status_bits {
+ NormalIntr=0x10000, AbnormalIntr=0x8000,
+ IntrPCIErr=0x2000, TimerInt=0x800,
+ IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40,
+ TxFIFOUnderflow=0x20, RxErrIntr=0x10,
+ TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01,
+};
+
+/* Bits in the NetworkConfig register. */
+enum rx_mode_bits {
+ AcceptErr=0x80, AcceptRunt=0x40,
+ AcceptBroadcast=0x20, AcceptMulticast=0x10,
+ AcceptAllPhys=0x08, AcceptMyPhys=0x02,
+};
+
+enum mii_reg_bits {
+ MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000,
+ MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000,
+};
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct w840_rx_desc {
+ s32 status;
+ s32 length;
+ u32 buffer1;
+ u32 next_desc;
+};
+
+struct w840_tx_desc {
+ s32 status;
+ s32 length;
+ u32 buffer1, buffer2; /* We use only buffer 1. */
+};
+
+/* Bits in network_desc.status */
+enum desc_status_bits {
+ DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000,
+ DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000,
+ DescIntr=0x80000000,
+};
+#define PRIV_ALIGN 15 /* Required alignment mask */
+#define PRIV_ALIGN_BYTES 32
+
+static struct winbond_private
+{
+ /* Descriptor rings first for alignment. */
+ struct w840_rx_desc rx_ring[RX_RING_SIZE];
+ struct w840_tx_desc tx_ring[TX_RING_SIZE];
+ struct net_device *next_module; /* Link for devices of this type. */
+ void *priv_addr; /* Unaligned address for kfree */
+ const char *product_name;
+ /* Frequently used values: keep some adjacent for cache effect. */
+ int chip_id, drv_flags;
+ struct pci_dev *pci_dev;
+ int csr6;
+ struct w840_rx_desc *rx_head_desc;
+ unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
+ unsigned int rx_buf_sz; /* Based on MTU+slack. */
+ unsigned int cur_tx, dirty_tx;
+ int tx_q_bytes;
+ unsigned int tx_full:1; /* The Tx queue is full. */
+ /* These values are keep track of the transceiver/media in use. */
+ unsigned int full_duplex:1; /* Full-duplex operation requested. */
+ unsigned int duplex_lock:1;
+ unsigned int medialock:1; /* Do not sense media. */
+ unsigned int default_port:4; /* Last dev->if_port value. */
+ /* MII transceiver section. */
+ int mii_cnt; /* MII device addresses. */
+ u16 advertising; /* NWay media advertisement */
+ unsigned char phys[2]; /* MII device addresses. */
+} w840private __attribute__ ((aligned (PRIV_ALIGN_BYTES)));
+
+/* NIC specific static variables go here */
+
+static int ioaddr;
+static unsigned short eeprom [0x40];
+struct {
+ char rx_packet[PKT_BUF_SZ * RX_RING_SIZE];
+ char tx_packet[PKT_BUF_SZ * TX_RING_SIZE];
+} w89c840_buf __shared;
+
+static int eeprom_read(long ioaddr, int location);
+static int mdio_read(int base_address, int phy_id, int location);
+#if 0
+static void mdio_write(int base_address, int phy_id, int location, int value);
+#endif
+
+static void check_duplex(void);
+static void set_rx_mode(void);
+static void init_ring(void);
+
+#if defined(W89C840_DEBUG)
+static void decode_interrupt(u32 intr_status)
+{
+ printf("Interrupt status: ");
+
+#define TRACE_INTR(_intr_) \
+ if (intr_status & (_intr_)) { printf (" " #_intr_); }
+
+ TRACE_INTR(NormalIntr);
+ TRACE_INTR(AbnormalIntr);
+ TRACE_INTR(IntrPCIErr);
+ TRACE_INTR(TimerInt);
+ TRACE_INTR(IntrRxDied);
+ TRACE_INTR(RxNoBuf);
+ TRACE_INTR(IntrRxDone);
+ TRACE_INTR(TxFIFOUnderflow);
+ TRACE_INTR(RxErrIntr);
+ TRACE_INTR(TxIdle);
+ TRACE_INTR(IntrTxStopped);
+ TRACE_INTR(IntrTxDone);
+
+ printf("\n");
+ /*sleep(1);*/
+}
+#endif
+
+/**************************************************************************
+w89c840_reset - Reset adapter
+***************************************************************************/
+static void w89c840_reset(struct nic *nic)
+{
+ int i;
+
+ /* Reset the chip to erase previous misconfiguration.
+ No hold time required! */
+ writel(0x00000001, ioaddr + PCIBusCfg);
+
+ init_ring();
+
+ writel(virt_to_bus(w840private.rx_ring), ioaddr + RxRingPtr);
+ writel(virt_to_bus(w840private.tx_ring), ioaddr + TxRingPtr);
+
+ for (i = 0; i < ETH_ALEN; i++)
+ writeb(nic->node_addr[i], ioaddr + StationAddr + i);
+
+ /* Initialize other registers. */
+ /* Configure the PCI bus bursts and FIFO thresholds.
+ 486: Set 8 longword cache alignment, 8 longword burst.
+ 586: Set 16 longword cache alignment, no burst limit.
+ Cache alignment bits 15:14 Burst length 13:8
+ 0000 <not allowed> 0000 align to cache 0800 8 longwords
+ 4000 8 longwords 0100 1 longword 1000 16 longwords
+ 8000 16 longwords 0200 2 longwords 2000 32 longwords
+ C000 32 longwords 0400 4 longwords
+ Wait the specified 50 PCI cycles after a reset by initializing
+ Tx and Rx queues and the address filter list. */
+
+ writel(0xE010, ioaddr + PCIBusCfg);
+
+ writel(0, ioaddr + RxStartDemand);
+ w840private.csr6 = 0x20022002;
+ check_duplex();
+ set_rx_mode();
+
+ /* Do not enable the interrupts Etherboot doesn't need them */
+/*
+ writel(0x1A0F5, ioaddr + IntrStatus);
+ writel(0x1A0F5, ioaddr + IntrEnable);
+*/
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : Done reset.\n");
+#endif
+}
+
+#if 0
+static void handle_intr(u32 intr_stat)
+{
+ if ((intr_stat & (NormalIntr|AbnormalIntr)) == 0) {
+ /* we are polling, do not return now */
+ /*return 0;*/
+ } else {
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ writel(intr_stat & 0x001ffff, ioaddr + IntrStatus);
+ }
+
+ if (intr_stat & AbnormalIntr) {
+ /* There was an abnormal interrupt */
+ printf("\n-=- Abnormal interrupt.\n");
+
+#if defined(W89C840_DEBUG)
+ decode_interrupt(intr_stat);
+#endif
+
+ if (intr_stat & RxNoBuf) {
+ /* There was an interrupt */
+ printf("-=- <=> No receive buffers available.\n");
+ writel(0, ioaddr + RxStartDemand);
+ }
+ }
+}
+#endif
+
+/**************************************************************************
+w89c840_poll - Wait for a frame
+***************************************************************************/
+static int w89c840_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+ int packet_received = 0;
+
+#if defined(W89C840_DEBUG)
+ u32 intr_status = readl(ioaddr + IntrStatus);
+#endif
+
+ do {
+ /* Code from netdev_rx(dev) */
+
+ int entry = w840private.cur_rx % RX_RING_SIZE;
+
+ struct w840_rx_desc *desc = w840private.rx_head_desc;
+ s32 status = desc->status;
+
+ if (status & DescOwn) {
+ /* DescOwn bit is still set, we should wait for RX to complete */
+ packet_received = 0;
+ break;
+ }
+
+ if ( !retrieve ) {
+ packet_received = 1;
+ break;
+ }
+
+ if ((status & 0x38008300) != 0x0300) {
+ if ((status & 0x38000300) != 0x0300) {
+ /* Ingore earlier buffers. */
+ if ((status & 0xffff) != 0x7fff) {
+ printf("winbond-840 : Oversized Ethernet frame spanned "
+ "multiple buffers, entry %d status %X !\n",
+ w840private.cur_rx, (unsigned int) status);
+ }
+ } else if (status & 0x8000) {
+ /* There was a fatal error. */
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : Receive error, Rx status %X :", status);
+ if (status & 0x0890) {
+ printf(" RXLEN_ERROR");
+ }
+ if (status & 0x004C) {
+ printf(", FRAME_ERROR");
+ }
+ if (status & 0x0002) {
+ printf(", CRC_ERROR");
+ }
+ printf("\n");
+#endif
+
+ /* Simpy do a reset now... */
+ w89c840_reset(nic);
+
+ packet_received = 0;
+ break;
+ }
+ } else {
+ /* Omit the four octet CRC from the length. */
+ int pkt_len = ((status >> 16) & 0x7ff) - 4;
+
+#if defined(W89C840_DEBUG)
+ printf(" netdev_rx() normal Rx pkt ring %d length %d status %X\n", entry, pkt_len, status);
+#endif
+
+ nic->packetlen = pkt_len;
+
+ /* Check if the packet is long enough to accept without copying
+ to a minimally-sized skbuff. */
+
+ memcpy(nic->packet, le32desc_to_virt(w840private.rx_ring[entry].buffer1), pkt_len);
+ packet_received = 1;
+
+ /* Release buffer to NIC */
+ w840private.rx_ring[entry].status = DescOwn;
+
+#if defined(W89C840_DEBUG)
+ /* You will want this info for the initial debug. */
+ printf(" Rx data %hhX:%hhX:%hhX:%hhX:%hhX:"
+ "%hhX %hhX:%hhX:%hhX:%hhX:%hhX:%hhX %hhX%hhX "
+ "%hhX.%hhX.%hhX.%hhX.\n",
+ nic->packet[0], nic->packet[1], nic->packet[2], nic->packet[3],
+ nic->packet[4], nic->packet[5], nic->packet[6], nic->packet[7],
+ nic->packet[8], nic->packet[9], nic->packet[10],
+ nic->packet[11], nic->packet[12], nic->packet[13],
+ nic->packet[14], nic->packet[15], nic->packet[16],
+ nic->packet[17]);
+#endif
+
+ }
+
+ entry = (++w840private.cur_rx) % RX_RING_SIZE;
+ w840private.rx_head_desc = &w840private.rx_ring[entry];
+ } while (0);
+
+ return packet_received;
+}
+
+/**************************************************************************
+w89c840_transmit - Transmit a frame
+***************************************************************************/
+
+static void w89c840_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ /* send the packet to destination */
+ unsigned entry;
+ int transmit_status;
+ tick_t ct;
+
+ /* Caution: the write order is important here, set the field
+ with the "ownership" bits last. */
+
+ /* Fill in our transmit buffer */
+ entry = w840private.cur_tx % TX_RING_SIZE;
+
+ memcpy (w89c840_buf.tx_packet, d, ETH_ALEN); /* dst */
+ memcpy (w89c840_buf.tx_packet + ETH_ALEN, nic->node_addr, ETH_ALEN);/*src*/
+
+ *((char *) w89c840_buf.tx_packet + 12) = t >> 8; /* type */
+ *((char *) w89c840_buf.tx_packet + 13) = t;
+
+ memcpy (w89c840_buf.tx_packet + ETH_HLEN, p, s);
+ s += ETH_HLEN;
+
+ while (s < ETH_ZLEN)
+ *((char *) w89c840_buf.tx_packet + ETH_HLEN + (s++)) = 0;
+
+ w840private.tx_ring[entry].buffer1
+ = virt_to_le32desc(w89c840_buf.tx_packet);
+
+ w840private.tx_ring[entry].length = (DescWholePkt | (u32) s);
+ if (entry >= TX_RING_SIZE-1) /* Wrap ring */
+ w840private.tx_ring[entry].length |= (DescIntr | DescEndRing);
+ w840private.tx_ring[entry].status = (DescOwn);
+ w840private.cur_tx++;
+
+ w840private.tx_q_bytes = (u16) s;
+ writel(0, ioaddr + TxStartDemand);
+
+ /* Work around horrible bug in the chip by marking the queue as full
+ when we do not have FIFO room for a maximum sized packet. */
+
+ if ((w840private.drv_flags & HasBrokenTx) && w840private.tx_q_bytes > TX_BUG_FIFO_LIMIT) {
+ /* Actually this is left to help finding error tails later in debugging...
+ * See Linux kernel driver in winbond-840.c for details.
+ */
+ w840private.tx_full = 1;
+ }
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : Transmit frame # %d size %d queued in slot %d.\n", w840private.cur_tx, s, entry);
+#endif
+
+ /* Now wait for TX to complete. */
+ transmit_status = w840private.tx_ring[entry].status;
+
+ ct = currticks();
+ {
+#if defined W89C840_DEBUG
+ u32 intr_stat = 0;
+#endif
+ while (1) {
+
+#if defined(W89C840_DEBUG)
+ decode_interrupt(intr_stat);
+#endif
+
+ while ( (transmit_status & DescOwn) && ct + TX_TIMEOUT < currticks()) {
+
+ transmit_status = w840private.tx_ring[entry].status;
+ }
+
+ break;
+ }
+ }
+
+ if ((transmit_status & DescOwn) == 0) {
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : transmission complete after wait loop iterations, status %X\n",
+ w840private.tx_ring[entry].status);
+#endif
+
+ return;
+ }
+
+ /* Transmit timed out... */
+
+ printf("winbond-840 : transmission TIMEOUT : status %X\n",
+ (unsigned int) w840private.tx_ring[entry].status);
+
+ return;
+}
+
+/**************************************************************************
+w89c840_disable - Turn off ethernet interface
+***************************************************************************/
+static void w89c840_disable ( struct nic *nic ) {
+
+ w89c840_reset(nic);
+
+ /* Don't know what to do to disable the board. Is this needed at all? */
+ /* Yes, a live NIC can corrupt the loaded memory later [Ken] */
+ /* Stop the chip's Tx and Rx processes. */
+ writel(w840private.csr6 &= ~0x20FA, ioaddr + NetworkConfig);
+}
+
+/**************************************************************************
+w89c840_irq - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void w89c840_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct nic_operations w89c840_operations = {
+ .connect = dummy_connect,
+ .poll = w89c840_poll,
+ .transmit = w89c840_transmit,
+ .irq = w89c840_irq,
+
+};
+
+static struct pci_device_id w89c840_nics[] = {
+PCI_ROM(0x1050, 0x0840, "winbond840", "Winbond W89C840F"),
+PCI_ROM(0x11f6, 0x2011, "compexrl100atx", "Compex RL100ATX"),
+};
+
+PCI_DRIVER ( w89c840_driver, w89c840_nics, PCI_NO_CLASS );
+
+/**************************************************************************
+w89c840_probe - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int w89c840_probe ( struct nic *nic, struct pci_device *p ) {
+
+
+ u16 sum = 0;
+ int i, j;
+ unsigned short value;
+
+ if (p->ioaddr == 0)
+ return 0;
+
+ nic->ioaddr = p->ioaddr;
+ nic->irqno = 0;
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840: PCI bus %hhX device function %hhX: I/O address: %hX\n", p->bus, p->devfn, ioaddr);
+#endif
+
+ ioaddr = ioaddr & ~3; /* Mask the bit that says "this is an io addr" */
+
+#define PCI_DEVICE_ID_WINBOND2_89C840 0x0840
+#define PCI_DEVICE_ID_COMPEX_RL100ATX 0x2011
+
+ /* From Matt Hortman <mbhortman@acpthinclient.com> */
+ if (p->vendor == PCI_VENDOR_ID_WINBOND2
+ && p->device == PCI_DEVICE_ID_WINBOND2_89C840) {
+
+ /* detected "Winbond W89c840 Fast Ethernet PCI NIC" */
+
+ } else if ( p->vendor == PCI_VENDOR_ID_COMPEX
+ && p->device == PCI_DEVICE_ID_COMPEX_RL100ATX) {
+
+ /* detected "Compex RL100ATX Fast Ethernet PCI NIC" */
+
+ } else {
+ /* Gee, guess what? They missed again. */
+ printf("device ID : %X - is not a Compex RL100ATX NIC.\n",
+ p->device);
+ return 0;
+ }
+
+ printf(" %s\n", w89c840_version);
+
+ adjust_pci_device(p);
+
+ /* Ok. Got one. Read the eeprom. */
+ for (j = 0, i = 0; i < 0x40; i++) {
+ value = eeprom_read(ioaddr, i);
+ eeprom[i] = value;
+ sum += value;
+ }
+
+ for (i=0;i<ETH_ALEN;i++) {
+ nic->node_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff;
+ }
+
+ DBG ( "Ethernet addr: %s\n", eth_ntoa ( nic->node_addr ) );
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840: EEPROM checksum %hX, got eeprom", sum);
+#endif
+
+ /* Reset the chip to erase previous misconfiguration.
+ No hold time required! */
+ writel(0x00000001, ioaddr + PCIBusCfg);
+
+ if (driver_flags & CanHaveMII) {
+ int phy, phy_idx = 0;
+ for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
+ int mii_status = mdio_read(ioaddr, phy, 1);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ w840private.phys[phy_idx++] = phy;
+ w840private.advertising = mdio_read(ioaddr, phy, 4);
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : MII PHY found at address %d, status "
+ "%X advertising %hX.\n", phy, mii_status, w840private.advertising);
+#endif
+
+ }
+ }
+
+ w840private.mii_cnt = phy_idx;
+
+ if (phy_idx == 0) {
+ printf("winbond-840 : MII PHY not found -- this device may not operate correctly.\n");
+ }
+ }
+
+ /* point to NIC specific routines */
+ nic->nic_op = &w89c840_operations;
+
+ w89c840_reset(nic);
+
+ return 1;
+}
+
+/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are
+ often serial bit streams generated by the host processor.
+ The example below is for the common 93c46 EEPROM, 64 16 bit words. */
+
+/* Delay between EEPROM clock transitions.
+ No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
+ a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that
+ made udelay() unreliable.
+ The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
+ depricated.
+*/
+#define eeprom_delay(ee_addr) readl(ee_addr)
+
+enum EEPROM_Ctrl_Bits {
+ EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805,
+ EE_ChipSelect=0x801, EE_DataIn=0x08,
+};
+
+/* The EEPROM commands include the alway-set leading bit. */
+enum EEPROM_Cmds {
+ EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
+};
+
+static int eeprom_read(long addr, int location)
+{
+ int i;
+ int retval = 0;
+ int ee_addr = addr + EECtrl;
+ int read_cmd = location | EE_ReadCmd;
+ writel(EE_ChipSelect, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 10; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
+ writel(dataval, ee_addr);
+ eeprom_delay(ee_addr);
+ writel(dataval | EE_ShiftClk, ee_addr);
+ eeprom_delay(ee_addr);
+ }
+ writel(EE_ChipSelect, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
+ eeprom_delay(ee_addr);
+ retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0);
+ writel(EE_ChipSelect, ee_addr);
+ eeprom_delay(ee_addr);
+ }
+
+ /* Terminate the EEPROM access. */
+ writel(0, ee_addr);
+ return retval;
+}
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details.
+
+ The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back 33Mhz PCI cycles. */
+#define mdio_delay(mdio_addr) readl(mdio_addr)
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+ This only set with older tranceivers, so the extra
+ code size of a per-interface flag is not worthwhile. */
+static char mii_preamble_required = 1;
+
+#define MDIO_WRITE0 (MDIO_EnbOutput)
+#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput)
+
+/* Generate the preamble required for initial synchronization and
+ a few older transceivers. */
+static void mdio_sync(long mdio_addr)
+{
+ int bits = 32;
+
+ /* Establish sync by sending at least 32 logic ones. */
+ while (--bits >= 0) {
+ writel(MDIO_WRITE1, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+}
+
+static int mdio_read(int base_address, int phy_id, int location)
+{
+ long mdio_addr = base_address + MIICtrl;
+ int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int i, retval = 0;
+
+ if (mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+ writel(dataval, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 20; i > 0; i--) {
+ writel(MDIO_EnbIn, mdio_addr);
+ mdio_delay(mdio_addr);
+ retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0);
+ writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+#if 0
+static void mdio_write(int base_address, int phy_id, int location, int value)
+{
+ long mdio_addr = base_address + MIICtrl;
+ int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+ int i;
+
+ if (location == 4 && phy_id == w840private.phys[0])
+ w840private.advertising = value;
+
+ if (mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+ writel(dataval, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ writel(MDIO_EnbIn, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ return;
+}
+#endif
+
+static void check_duplex(void)
+{
+ int mii_reg5 = mdio_read(ioaddr, w840private.phys[0], 5);
+ int negotiated = mii_reg5 & w840private.advertising;
+ int duplex;
+
+ if (w840private.duplex_lock || mii_reg5 == 0xffff)
+ return;
+
+ duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+ if (w840private.full_duplex != duplex) {
+ w840private.full_duplex = duplex;
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : Setting %s-duplex based on MII # %d negotiated capability %X\n",
+ duplex ? "full" : "half", w840private.phys[0], negotiated);
+#endif
+
+ w840private.csr6 &= ~0x200;
+ w840private.csr6 |= duplex ? 0x200 : 0;
+ }
+}
+
+static void set_rx_mode(void)
+{
+ u32 mc_filter[2]; /* Multicast hash filter */
+ u32 rx_mode;
+
+ /* Accept all multicasts from now on. */
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+
+/*
+ * works OK with multicast enabled.
+ */
+
+ rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast;
+
+ writel(mc_filter[0], ioaddr + MulticastFilter0);
+ writel(mc_filter[1], ioaddr + MulticastFilter1);
+ w840private.csr6 &= ~0x00F8;
+ w840private.csr6 |= rx_mode;
+ writel(w840private.csr6, ioaddr + NetworkConfig);
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : Done setting RX mode.\n");
+#endif
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void init_ring(void)
+{
+ int i;
+ char * p;
+
+ w840private.tx_full = 0;
+ w840private.tx_q_bytes = w840private.cur_rx = w840private.cur_tx = 0;
+ w840private.dirty_rx = w840private.dirty_tx = 0;
+
+ w840private.rx_buf_sz = PKT_BUF_SZ;
+ w840private.rx_head_desc = &w840private.rx_ring[0];
+
+ /* Initial all Rx descriptors. Fill in the Rx buffers. */
+
+ p = &w89c840_buf.rx_packet[0];
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ w840private.rx_ring[i].length = w840private.rx_buf_sz;
+ w840private.rx_ring[i].status = 0;
+ w840private.rx_ring[i].next_desc = virt_to_le32desc(&w840private.rx_ring[i+1]);
+
+ w840private.rx_ring[i].buffer1 = virt_to_le32desc(p + (PKT_BUF_SZ * i));
+ w840private.rx_ring[i].status = DescOwn | DescIntr;
+ }
+
+ /* Mark the last entry as wrapping the ring. */
+ w840private.rx_ring[i-1].length |= DescEndRing;
+ w840private.rx_ring[i-1].next_desc = virt_to_le32desc(&w840private.rx_ring[0]);
+
+ w840private.dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ w840private.tx_ring[i].status = 0;
+ }
+ return;
+}
+
+
+DRIVER ( "W89C840F", nic_driver, pci_driver, w89c840_driver,
+ w89c840_probe, w89c840_disable );
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * c-indent-level: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/gpxe/src/drivers/net/wlan_compat.h b/gpxe/src/drivers/net/wlan_compat.h
new file mode 100644
index 00000000..a4f75e3a
--- /dev/null
+++ b/gpxe/src/drivers/net/wlan_compat.h
@@ -0,0 +1,575 @@
+/* src/include/wlan/wlan_compat.h
+*
+* Types and macros to aid in portability
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _WLAN_COMPAT_H
+#define _WLAN_COMPAT_H
+
+/*=============================================================*/
+/*------ Establish Platform Identity --------------------------*/
+/*=============================================================*/
+/* Key macros: */
+/* WLAN_CPU_FAMILY */
+ #define WLAN_Ix86 1
+ #define WLAN_PPC 2
+ #define WLAN_Ix96 3
+ #define WLAN_ARM 4
+ #define WLAN_ALPHA 5
+ #define WLAN_MIPS 6
+ #define WLAN_HPPA 7
+/* WLAN_CPU_CORE */
+ #define WLAN_I386CORE 1
+ #define WLAN_PPCCORE 2
+ #define WLAN_I296 3
+ #define WLAN_ARMCORE 4
+ #define WLAN_ALPHACORE 5
+ #define WLAN_MIPSCORE 6
+ #define WLAN_HPPACORE 7
+/* WLAN_CPU_PART */
+ #define WLAN_I386PART 1
+ #define WLAN_MPC860 2
+ #define WLAN_MPC823 3
+ #define WLAN_I296SA 4
+ #define WLAN_PPCPART 5
+ #define WLAN_ARMPART 6
+ #define WLAN_ALPHAPART 7
+ #define WLAN_MIPSPART 8
+ #define WLAN_HPPAPART 9
+/* WLAN_SYSARCH */
+ #define WLAN_PCAT 1
+ #define WLAN_MBX 2
+ #define WLAN_RPX 3
+ #define WLAN_LWARCH 4
+ #define WLAN_PMAC 5
+ #define WLAN_SKIFF 6
+ #define WLAN_BITSY 7
+ #define WLAN_ALPHAARCH 7
+ #define WLAN_MIPSARCH 9
+ #define WLAN_HPPAARCH 10
+/* WLAN_OS */
+ #define WLAN_LINUX_KERNEL 1
+ #define WLAN_LINUX_USER 2
+/* WLAN_HOSTIF (generally set on the command line, not detected) */
+ #define WLAN_PCMCIA 1
+ #define WLAN_ISA 2
+ #define WLAN_PCI 3
+ #define WLAN_USB 4
+ #define WLAN_PLX 5
+
+/* Note: the PLX HOSTIF above refers to some vendors implementations for */
+/* PCI. It's a PLX chip that is a PCI to PCMCIA adapter, but it */
+/* isn't a real PCMCIA host interface adapter providing all the */
+/* card&socket services. */
+
+/* Lets try to figure out what we've got. Kernel mode or User mode? */
+#if defined(__KERNEL__)
+ #define WLAN_OS WLAN_LINUX_KERNEL
+#else
+ #define WLAN_OS WLAN_LINUX_USER
+#endif
+
+#ifdef __powerpc__
+#ifndef __ppc__
+#define __ppc__
+#endif
+#endif
+
+#if (defined(CONFIG_PPC) || defined(CONFIG_8xx))
+#ifndef __ppc__
+#define __ppc__
+#endif
+#endif
+
+#if defined(__KERNEL__)
+#if defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
+ #define WLAN_CPU_FAMILY WLAN_Ix86
+ #define WLAN_CPU_CORE WLAN_I386CORE
+ #define WLAN_CPU_PART WLAN_I386PART
+ #define WLAN_SYSARCH WLAN_PCAT
+#elif defined(__ppc__)
+ #define WLAN_CPU_FAMILY WLAN_PPC
+ #define WLAN_CPU_CORE WLAN_PPCCORE
+ #if defined(CONFIG_MBX)
+ #define WLAN_CPU_PART WLAN_MPC860
+ #define WLAN_SYSARCH WLAN_MBX
+ #elif defined(CONFIG_RPXLITE)
+ #define WLAN_CPU_PART WLAN_MPC823
+ #define WLAN_SYSARCH WLAN_RPX
+ #elif defined(CONFIG_RPXCLASSIC)
+ #define WLAN_CPU_PART WLAN_MPC860
+ #define WLAN_SYSARCH WLAN_RPX
+ #else
+ #define WLAN_CPU_PART WLAN_PPCPART
+ #define WLAN_SYSARCH WLAN_PMAC
+ #endif
+#elif defined(__arm__)
+ #define WLAN_CPU_FAMILY WLAN_ARM
+ #define WLAN_CPU_CORE WLAN_ARMCORE
+ #define WLAN_CPU_PART WLAN_ARM_PART
+ #define WLAN_SYSARCH WLAN_SKIFF
+#elif defined(__alpha__)
+ #define WLAN_CPU_FAMILY WLAN_ALPHA
+ #define WLAN_CPU_CORE WLAN_ALPHACORE
+ #define WLAN_CPU_PART WLAN_ALPHAPART
+ #define WLAN_SYSARCH WLAN_ALPHAARCH
+#elif defined(__mips__)
+ #define WLAN_CPU_FAMILY WLAN_MIPS
+ #define WLAN_CPU_CORE WLAN_MIPSCORE
+ #define WLAN_CPU_PART WLAN_MIPSPART
+ #define WLAN_SYSARCH WLAN_MIPSARCH
+#elif defined(__hppa__)
+ #define WLAN_CPU_FAMILY WLAN_HPPA
+ #define WLAN_CPU_CORE WLAN_HPPACORE
+ #define WLAN_CPU_PART WLAN_HPPAPART
+ #define WLAN_SYSARCH WLAN_HPPAARCH
+#else
+ #error "No CPU identified!"
+#endif
+#endif /* __KERNEL__ */
+
+/*
+ Some big endian machines implicitly do all I/O in little endian mode.
+
+ In particular:
+ Linux/PPC on PowerMacs (PCI)
+ Arm/Intel Xscale (PCI)
+
+ This may also affect PLX boards and other BE &| PPC platforms;
+ as new ones are discovered, add them below.
+*/
+
+#if (WLAN_HOSTIF == WLAN_PCI)
+#if ((WLAN_SYSARCH == WLAN_SKIFF) || (WLAN_SYSARCH == WLAN_PMAC))
+#define REVERSE_ENDIAN
+#endif
+#endif
+
+/*=============================================================*/
+/*------ Bit settings -----------------------------------------*/
+/*=============================================================*/
+
+#define BIT0 0x00000001
+#define BIT1 0x00000002
+#define BIT2 0x00000004
+#define BIT3 0x00000008
+#define BIT4 0x00000010
+#define BIT5 0x00000020
+#define BIT6 0x00000040
+#define BIT7 0x00000080
+#define BIT8 0x00000100
+#define BIT9 0x00000200
+#define BIT10 0x00000400
+#define BIT11 0x00000800
+#define BIT12 0x00001000
+#define BIT13 0x00002000
+#define BIT14 0x00004000
+#define BIT15 0x00008000
+#define BIT16 0x00010000
+#define BIT17 0x00020000
+#define BIT18 0x00040000
+#define BIT19 0x00080000
+#define BIT20 0x00100000
+#define BIT21 0x00200000
+#define BIT22 0x00400000
+#define BIT23 0x00800000
+#define BIT24 0x01000000
+#define BIT25 0x02000000
+#define BIT26 0x04000000
+#define BIT27 0x08000000
+#define BIT28 0x10000000
+#define BIT29 0x20000000
+#define BIT30 0x40000000
+#define BIT31 0x80000000
+
+typedef unsigned char UINT8;
+typedef unsigned short UINT16;
+typedef unsigned long UINT32;
+
+typedef signed char INT8;
+typedef signed short INT16;
+typedef signed long INT32;
+
+typedef unsigned int UINT;
+typedef signed int INT;
+
+typedef unsigned long long UINT64;
+typedef signed long long INT64;
+
+#define UINT8_MAX (0xffUL)
+#define UINT16_MAX (0xffffUL)
+#define UINT32_MAX (0xffffffffUL)
+
+#define INT8_MAX (0x7fL)
+#define INT16_MAX (0x7fffL)
+#define INT32_MAX (0x7fffffffL)
+
+/*=============================================================*/
+/*------ Compiler Portability Macros --------------------------*/
+/*=============================================================*/
+#define __WLAN_ATTRIB_PACK__ __attribute__ ((packed))
+#define __WLAN_PRAGMA_PACK1__
+#define __WLAN_PRAGMA_PACKDFLT__
+#define __WLAN_INLINE__ inline
+#define WLAN_MIN_ARRAY 0
+
+/*=============================================================*/
+/*------ OS Portability Macros --------------------------------*/
+/*=============================================================*/
+
+#ifndef WLAN_DBVAR
+#define WLAN_DBVAR wlan_debug
+#endif
+
+#if (WLAN_OS == WLAN_LINUX_KERNEL)
+ #define WLAN_LOG_ERROR0(x) printk(KERN_ERR "%s: " x , __FUNCTION__ );
+ #define WLAN_LOG_ERROR1(x,n) printk(KERN_ERR "%s: " x , __FUNCTION__ , (n));
+ #define WLAN_LOG_ERROR2(x,n1,n2) printk(KERN_ERR "%s: " x , __FUNCTION__ , (n1), (n2));
+ #define WLAN_LOG_ERROR3(x,n1,n2,n3) printk(KERN_ERR "%s: " x , __FUNCTION__, (n1), (n2), (n3));
+ #define WLAN_LOG_ERROR4(x,n1,n2,n3,n4) printk(KERN_ERR "%s: " x , __FUNCTION__, (n1), (n2), (n3), (n4));
+
+ #define WLAN_LOG_WARNING0(x) printk(KERN_WARNING "%s: " x , __FUNCTION__);
+ #define WLAN_LOG_WARNING1(x,n) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n));
+ #define WLAN_LOG_WARNING2(x,n1,n2) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n1), (n2));
+ #define WLAN_LOG_WARNING3(x,n1,n2,n3) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n1), (n2), (n3));
+ #define WLAN_LOG_WARNING4(x,n1,n2,n3,n4) printk(KERN_WARNING "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4));
+
+ #define WLAN_LOG_NOTICE0(x) printk(KERN_NOTICE "%s: " x , __FUNCTION__);
+ #define WLAN_LOG_NOTICE1(x,n) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n));
+ #define WLAN_LOG_NOTICE2(x,n1,n2) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2));
+ #define WLAN_LOG_NOTICE3(x,n1,n2,n3) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2), (n3));
+ #define WLAN_LOG_NOTICE4(x,n1,n2,n3,n4) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2), (n3), (n4));
+
+ #define WLAN_LOG_INFO0(x) printk(KERN_INFO x);
+ #define WLAN_LOG_INFO1(x,n) printk(KERN_INFO x, (n));
+ #define WLAN_LOG_INFO2(x,n1,n2) printk(KERN_INFO x, (n1), (n2));
+ #define WLAN_LOG_INFO3(x,n1,n2,n3) printk(KERN_INFO x, (n1), (n2), (n3));
+ #define WLAN_LOG_INFO4(x,n1,n2,n3,n4) printk(KERN_INFO x, (n1), (n2), (n3), (n4));
+ #define WLAN_LOG_INFO5(x,n1,n2,n3,n4,n5) printk(KERN_INFO x, (n1), (n2), (n3), (n4), (n5));
+
+ #if defined(WLAN_INCLUDE_DEBUG)
+ #define WLAN_ASSERT(c) if ((!(c)) && WLAN_DBVAR >= 1) { \
+ WLAN_LOG_DEBUG0(1, "Assertion failure!\n"); }
+ #define WLAN_HEX_DUMP( l, x, p, n) if( WLAN_DBVAR >= (l) ){ \
+ int __i__; \
+ printk(KERN_DEBUG x ":"); \
+ for( __i__=0; __i__ < (n); __i__++) \
+ printk( " %02x", ((UINT8*)(p))[__i__]); \
+ printk("\n"); }
+
+ #define DBFENTER { if ( WLAN_DBVAR >= 4 ){ WLAN_LOG_DEBUG0(3,"Enter\n"); } }
+ #define DBFEXIT { if ( WLAN_DBVAR >= 4 ){ WLAN_LOG_DEBUG0(3,"Exit\n"); } }
+
+ #define WLAN_LOG_DEBUG0(l,x) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ );
+ #define WLAN_LOG_DEBUG1(l,x,n) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n));
+ #define WLAN_LOG_DEBUG2(l,x,n1,n2) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2));
+ #define WLAN_LOG_DEBUG3(l,x,n1,n2,n3) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3));
+ #define WLAN_LOG_DEBUG4(l,x,n1,n2,n3,n4) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4));
+ #define WLAN_LOG_DEBUG5(l,x,n1,n2,n3,n4,n5) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4), (n5));
+ #define WLAN_LOG_DEBUG6(l,x,n1,n2,n3,n4,n5,n6) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4), (n5), (n6));
+ #else
+ #define WLAN_ASSERT(c)
+ #define WLAN_HEX_DUMP( l, s, p, n)
+
+ #define DBFENTER
+ #define DBFEXIT
+
+ #define WLAN_LOG_DEBUG0(l, s)
+ #define WLAN_LOG_DEBUG1(l, s,n)
+ #define WLAN_LOG_DEBUG2(l, s,n1,n2)
+ #define WLAN_LOG_DEBUG3(l, s,n1,n2,n3)
+ #define WLAN_LOG_DEBUG4(l, s,n1,n2,n3,n4)
+ #define WLAN_LOG_DEBUG5(l, s,n1,n2,n3,n4,n5)
+ #endif
+#else
+ #define WLAN_LOG_ERROR0(s)
+ #define WLAN_LOG_ERROR1(s,n)
+ #define WLAN_LOG_ERROR2(s,n1,n2)
+ #define WLAN_LOG_ERROR3(s,n1,n2,n3)
+ #define WLAN_LOG_ERROR4(s,n1,n2,n3,n4)
+
+ #define WLAN_LOG_WARNING0(s)
+ #define WLAN_LOG_WARNING1(s,n)
+ #define WLAN_LOG_WARNING2(s,n1,n2)
+ #define WLAN_LOG_WARNING3(s,n1,n2,n3)
+ #define WLAN_LOG_WARNING4(s,n1,n2,n3,n4)
+
+ #define WLAN_LOG_NOTICE0(s)
+ #define WLAN_LOG_NOTICE1(s,n)
+ #define WLAN_LOG_NOTICE2(s,n1,n2)
+ #define WLAN_LOG_NOTICE3(s,n1,n2,n3)
+ #define WLAN_LOG_NOTICE4(s,n1,n2,n3,n4)
+
+ #define WLAN_ASSERT(c)
+ #define WLAN_HEX_DUMP( l, s, p, n)
+
+ #define DBFENTER
+ #define DBFEXIT
+
+ #define WLAN_LOG_INFO0(s)
+ #define WLAN_LOG_INFO1(s,n)
+ #define WLAN_LOG_INFO2(s,n1,n2)
+ #define WLAN_LOG_INFO3(s,n1,n2,n3)
+ #define WLAN_LOG_INFO4(s,n1,n2,n3,n4)
+ #define WLAN_LOG_INFO5(s,n1,n2,n3,n4,n5)
+
+ #define WLAN_LOG_DEBUG0(l, s)
+ #define WLAN_LOG_DEBUG1(l, s,n)
+ #define WLAN_LOG_DEBUG2(l, s,n1,n2)
+ #define WLAN_LOG_DEBUG3(l, s,n1,n2,n3)
+ #define WLAN_LOG_DEBUG4(l, s,n1,n2,n3,n4)
+ #define WLAN_LOG_DEBUG5(l, s,n1,n2,n3,n4,n5)
+#endif
+
+#define wlan_ms_per_tick (1000UL / (wlan_ticks_per_sec))
+#define wlan_ms_to_ticks(n) ( (n) / (wlan_ms_per_tick))
+#define wlan_tu2ticks(n) ( (n) / (wlan_ms_per_tick))
+#define WLAN_INT_DISABLE(n) { save_flags((n)); cli(); }
+#define WLAN_INT_ENABLE(n) { sti(); restore_flags((n)); }
+
+#ifdef CONFIG_MODVERSIONS
+#define MODVERSIONS 1
+#include <linux/modversions.h>
+#endif
+
+#ifdef CONFIG_SMP
+#define __SMP__ 1
+#endif
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17))
+#define CONFIG_NETLINK 1
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0))
+#define kfree_s(a, b) kfree((a))
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18))
+#ifndef init_waitqueue_head
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,0,16))
+#define init_waitqueue_head(p) (*(p) = NULL)
+#else
+#define init_waitqueue_head(p) init_waitqueue(p)
+#endif
+typedef struct wait_queue *wait_queue_head_t;
+typedef struct wait_queue wait_queue_t;
+#define set_current_state(b) { current->state = (b); mb(); }
+#define init_waitqueue_entry(a, b) { (a)->task = current; }
+#endif
+#endif
+
+#ifndef wait_event_interruptible_timeout
+// retval == 0; signal met; we're good.
+// retval < 0; interrupted by signal.
+// retval > 0; timed out.
+#define __wait_event_interruptible_timeout(wq, condition, timeout, ret) \
+do { \
+ int __ret = 0; \
+ if (!(condition)) { \
+ wait_queue_t __wait; \
+ unsigned long expire; \
+ init_waitqueue_entry(&__wait, current); \
+ \
+ expire = timeout + jiffies; \
+ add_wait_queue(&wq, &__wait); \
+ for (;;) { \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ if (condition) \
+ break; \
+ if (jiffies > expire) { \
+ ret = jiffies - expire; \
+ break; \
+ } \
+ if (!signal_pending(current)) { \
+ schedule_timeout(timeout); \
+ continue; \
+ } \
+ ret = -ERESTARTSYS; \
+ break; \
+ } \
+ set_current_state(TASK_RUNNING); \
+ remove_wait_queue(&wq, &__wait); \
+ } \
+} while (0)
+
+#define wait_event_interruptible_timeout(wq, condition, timeout) \
+({ \
+ int __ret = 0; \
+ if (!(condition)) \
+ __wait_event_interruptible_timeout(wq, condition, \
+ timeout, __ret); \
+ __ret; \
+})
+
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,90))
+#define spin_lock(l) do { } while (0)
+#define spin_unlock(l) do { } while (0)
+#define spin_lock_irqsave(l,f) do { save_flags(f); cli(); } while (0)
+#define spin_unlock_irqrestore(l,f) do { restore_flags(f); } while (0)
+#define spin_lock_init(s) do { } while (0)
+#define spin_trylock(l) (1)
+typedef int spinlock_t;
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0))
+#ifdef CONFIG_SMP
+#define spin_is_locked(x) (*(volatile char *)(&(x)->lock) <= 0)
+#else
+#define spin_is_locked(l) (0)
+#endif
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,38))
+typedef struct device netdevice_t;
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4))
+typedef struct net_device netdevice_t;
+#else
+#undef netdevice_t
+typedef struct net_device netdevice_t;
+#endif
+
+#ifdef WIRELESS_EXT
+#if (WIRELESS_EXT < 13)
+struct iw_request_info
+{
+ __u16 cmd; /* Wireless Extension command */
+ __u16 flags; /* More to come ;-) */
+};
+#endif
+#endif
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18))
+#define MODULE_PARM(a,b) extern int __bogus_decl
+#define MODULE_AUTHOR(a) extern int __bogus_decl
+#define MODULE_DESCRIPTION(a) extern int __bogus_decl
+#define MODULE_SUPPORTED_DEVICE(a) extern int __bogus_decl
+#undef GET_USE_COUNT
+#define GET_USE_COUNT(m) mod_use_count_
+#endif
+
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(m) extern int __bogus_decl
+#endif
+
+/* TODO: Do we care about this? */
+#ifndef MODULE_DEVICE_TABLE
+#define MODULE_DEVICE_TABLE(foo,bar)
+#endif
+
+#define wlan_minutes2ticks(a) ((a)*(wlan_ticks_per_sec * 60))
+#define wlan_seconds2ticks(a) ((a)*(wlan_ticks_per_sec))
+
+/*=============================================================*/
+/*------ Hardware Portability Macros --------------------------*/
+/*=============================================================*/
+
+#define ieee2host16(n) __le16_to_cpu(n)
+#define ieee2host32(n) __le32_to_cpu(n)
+#define host2ieee16(n) __cpu_to_le16(n)
+#define host2ieee32(n) __cpu_to_le32(n)
+
+#if (WLAN_CPU_FAMILY == WLAN_PPC)
+ #define wlan_inw(a) in_be16((unsigned short *)((a)+_IO_BASE))
+ #define wlan_inw_le16_to_cpu(a) inw((a))
+ #define wlan_outw(v,a) out_be16((unsigned short *)((a)+_IO_BASE), (v))
+ #define wlan_outw_cpu_to_le16(v,a) outw((v),(a))
+#else
+ #define wlan_inw(a) inw((a))
+ #define wlan_inw_le16_to_cpu(a) __cpu_to_le16(inw((a)))
+ #define wlan_outw(v,a) outw((v),(a))
+ #define wlan_outw_cpu_to_le16(v,a) outw(__cpu_to_le16((v)),(a))
+#endif
+
+/*=============================================================*/
+/*--- General Macros ------------------------------------------*/
+/*=============================================================*/
+
+#define wlan_max(a, b) (((a) > (b)) ? (a) : (b))
+#define wlan_min(a, b) (((a) < (b)) ? (a) : (b))
+
+#define wlan_isprint(c) (((c) > (0x19)) && ((c) < (0x7f)))
+
+#define wlan_hexchar(x) (((x) < 0x0a) ? ('0' + (x)) : ('a' + ((x) - 0x0a)))
+
+/* Create a string of printable chars from something that might not be */
+/* It's recommended that the str be 4*len + 1 bytes long */
+#define wlan_mkprintstr(buf, buflen, str, strlen) \
+{ \
+ int i = 0; \
+ int j = 0; \
+ memset(str, 0, (strlen)); \
+ for (i = 0; i < (buflen); i++) { \
+ if ( wlan_isprint((buf)[i]) ) { \
+ (str)[j] = (buf)[i]; \
+ j++; \
+ } else { \
+ (str)[j] = '\\'; \
+ (str)[j+1] = 'x'; \
+ (str)[j+2] = wlan_hexchar(((buf)[i] & 0xf0) >> 4); \
+ (str)[j+3] = wlan_hexchar(((buf)[i] & 0x0f)); \
+ j += 4; \
+ } \
+ } \
+}
+
+/*=============================================================*/
+/*--- Variables -----------------------------------------------*/
+/*=============================================================*/
+
+extern int wlan_debug;
+extern int wlan_ethconv; /* What's the default ethconv? */
+
+/*=============================================================*/
+/*--- Functions -----------------------------------------------*/
+/*=============================================================*/
+#endif /* _WLAN_COMPAT_H */
+
diff --git a/gpxe/src/drivers/nvs/nvs.c b/gpxe/src/drivers/nvs/nvs.c
new file mode 100644
index 00000000..8e94b872
--- /dev/null
+++ b/gpxe/src/drivers/nvs/nvs.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <gpxe/nvs.h>
+
+/** @file
+ *
+ * Non-volatile storage
+ *
+ */
+
+/**
+ * Read from non-volatile storage device
+ *
+ * @v nvs NVS device
+ * @v address Address from which to read
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+int nvs_read ( struct nvs_device *nvs, unsigned int address,
+ void *data, size_t len ) {
+ size_t frag_len;
+ int rc;
+
+ /* We don't even attempt to handle buffer lengths that aren't
+ * an integral number of words.
+ */
+ assert ( ( len & ( ( 1 << nvs->word_len_log2 ) - 1 ) ) == 0 );
+
+ while ( len ) {
+
+ /* Calculate space remaining up to next block boundary */
+ frag_len = ( ( nvs->block_size -
+ ( address & ( nvs->block_size - 1 ) ) )
+ << nvs->word_len_log2 );
+
+ /* Limit to space remaining in buffer */
+ if ( frag_len > len )
+ frag_len = len;
+
+ /* Read this portion of the buffer from the device */
+ if ( ( rc = nvs->read ( nvs, address, data, frag_len ) ) != 0 )
+ return rc;
+
+ /* Update parameters */
+ data += frag_len;
+ address += ( frag_len >> nvs->word_len_log2 );
+ len -= frag_len;
+ }
+
+ return 0;
+}
+
+/**
+ * Verify content of non-volatile storage device
+ *
+ * @v nvs NVS device
+ * @v address Address from which to read
+ * @v data Data to compare against
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+static int nvs_verify ( struct nvs_device *nvs, unsigned int address,
+ const void *data, size_t len ) {
+ uint8_t read_data[len];
+ int rc;
+
+ /* Read data into temporary buffer */
+ if ( ( rc = nvs_read ( nvs, address, read_data, len ) ) != 0 )
+ return rc;
+
+ /* Compare data */
+ if ( memcmp ( data, read_data, len ) != 0 ) {
+ DBG ( "NVS %p verification failed at %#04x+%zd\n",
+ nvs, address, len );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * Write to non-volatile storage device
+ *
+ * @v nvs NVS device
+ * @v address Address to which to write
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+int nvs_write ( struct nvs_device *nvs, unsigned int address,
+ const void *data, size_t len ) {
+ size_t frag_len;
+ int rc;
+
+ /* We don't even attempt to handle buffer lengths that aren't
+ * an integral number of words.
+ */
+ assert ( ( len & ( ( 1 << nvs->word_len_log2 ) - 1 ) ) == 0 );
+
+ while ( len ) {
+
+ /* Calculate space remaining up to next block boundary */
+ frag_len = ( ( nvs->block_size -
+ ( address & ( nvs->block_size - 1 ) ) )
+ << nvs->word_len_log2 );
+
+ /* Limit to space remaining in buffer */
+ if ( frag_len > len )
+ frag_len = len;
+
+ /* Write this portion of the buffer to the device */
+ if ( ( rc = nvs->write ( nvs, address, data, frag_len ) ) != 0)
+ return rc;
+
+ /* Read back and verify data */
+ if ( ( rc = nvs_verify ( nvs, address, data, frag_len ) ) != 0)
+ return rc;
+
+ /* Update parameters */
+ data += frag_len;
+ address += ( frag_len >> nvs->word_len_log2 );
+ len -= frag_len;
+ }
+
+ return 0;
+}
diff --git a/gpxe/src/drivers/nvs/spi.c b/gpxe/src/drivers/nvs/spi.c
new file mode 100644
index 00000000..dbdc32f6
--- /dev/null
+++ b/gpxe/src/drivers/nvs/spi.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <errno.h>
+#include <unistd.h>
+#include <gpxe/spi.h>
+
+/** @file
+ *
+ * SPI devices
+ *
+ */
+
+/**
+ * Munge SPI device address into command
+ *
+ * @v command SPI command
+ * @v address Address
+ * @v munge_address Device requires address munging
+ * @ret command Actual SPI command to use
+ *
+ * Some devices with 9-bit addresses (e.g. AT25040A EEPROM) use bit 3
+ * of the command byte as address bit A8, rather than having a
+ * two-byte address. This function takes care of generating the
+ * appropriate command.
+ */
+static inline unsigned int spi_command ( unsigned int command,
+ unsigned int address,
+ int munge_address ) {
+ return ( command | ( ( ( address >> 8 ) & munge_address ) << 3 ) );
+}
+
+/**
+ * Wait for SPI device to complete operation
+ *
+ * @v device SPI device
+ * @ret rc Return status code
+ */
+static int spi_wait ( struct spi_device *device ) {
+ struct spi_bus *bus = device->bus;
+ uint8_t status;
+ int i;
+ int rc;
+
+ for ( i = 0 ; i < 50 ; i++ ) {
+ udelay ( 20 );
+ if ( ( rc = bus->rw ( bus, device, SPI_RDSR, -1, NULL,
+ &status, sizeof ( status ) ) ) != 0 )
+ return rc;
+ if ( ! ( status & SPI_STATUS_NRDY ) )
+ return 0;
+ }
+ DBG ( "SPI %p timed out\n", device );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Read data from SPI device
+ *
+ * @v nvs NVS device
+ * @v address Address from which to read
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+int spi_read ( struct nvs_device *nvs, unsigned int address,
+ void *data, size_t len ) {
+ struct spi_device *device = nvs_to_spi ( nvs );
+ struct spi_bus *bus = device->bus;
+ unsigned int command = spi_command ( SPI_READ, address,
+ device->munge_address );
+ int rc;
+
+ DBG ( "SPI %p reading %zd bytes from %#04x\n", device, len, address );
+ if ( ( rc = bus->rw ( bus, device, command, address,
+ NULL, data, len ) ) != 0 ) {
+ DBG ( "SPI %p failed to read data from device\n", device );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Write data to SPI device
+ *
+ * @v nvs NVS device
+ * @v address Address from which to read
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+int spi_write ( struct nvs_device *nvs, unsigned int address,
+ const void *data, size_t len ) {
+ struct spi_device *device = nvs_to_spi ( nvs );
+ struct spi_bus *bus = device->bus;
+ unsigned int command = spi_command ( SPI_WRITE, address,
+ device->munge_address );
+ int rc;
+
+ DBG ( "SPI %p writing %zd bytes to %#04x\n", device, len, address );
+
+ if ( ( rc = bus->rw ( bus, device, SPI_WREN, -1,
+ NULL, NULL, 0 ) ) != 0 ) {
+ DBG ( "SPI %p failed to write-enable device\n", device );
+ return rc;
+ }
+
+ if ( ( rc = bus->rw ( bus, device, command, address,
+ data, NULL, len ) ) != 0 ) {
+ DBG ( "SPI %p failed to write data to device\n", device );
+ return rc;
+ }
+
+ if ( ( rc = spi_wait ( device ) ) != 0 ) {
+ DBG ( "SPI %p failed to complete write operation\n", device );
+ return rc;
+ }
+
+ return 0;
+}
+
diff --git a/gpxe/src/drivers/nvs/threewire.c b/gpxe/src/drivers/nvs/threewire.c
new file mode 100644
index 00000000..dbecf909
--- /dev/null
+++ b/gpxe/src/drivers/nvs/threewire.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <assert.h>
+#include <unistd.h>
+#include <gpxe/threewire.h>
+
+/** @file
+ *
+ * Three-wire serial devices
+ *
+ */
+
+/**
+ * Read data from three-wire device
+ *
+ * @v nvs NVS device
+ * @v address Address from which to read
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+int threewire_read ( struct nvs_device *nvs, unsigned int address,
+ void *data, size_t len ) {
+ struct spi_device *device = nvs_to_spi ( nvs );
+ struct spi_bus *bus = device->bus;
+
+ assert ( bus->mode == SPI_MODE_THREEWIRE );
+
+ DBG ( "3wire %p reading %zd bytes at %04x\n", device, len, address );
+
+ return bus->rw ( bus, device, THREEWIRE_READ, address,
+ NULL, data, len );
+}
+
+/**
+ * Write data to three-wire device
+ *
+ * @v nvs NVS device
+ * @v address Address from which to read
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+int threewire_write ( struct nvs_device *nvs, unsigned int address,
+ const void *data, size_t len ) {
+ struct spi_device *device = nvs_to_spi ( nvs );
+ struct spi_bus *bus = device->bus;
+ int rc;
+
+ assert ( bus->mode == SPI_MODE_THREEWIRE );
+
+ DBG ( "3wire %p writing %zd bytes at %04x\n", device, len, address );
+
+ /* Enable device for writing */
+ if ( ( rc = bus->rw ( bus, device, THREEWIRE_EWEN,
+ THREEWIRE_EWEN_ADDRESS, NULL, NULL, 0 ) ) != 0 )
+ return rc;
+
+ /* Write data */
+ if ( ( rc = bus->rw ( bus, device, THREEWIRE_WRITE, address,
+ data, NULL, len ) ) != 0 )
+ return rc;
+
+ /* Our model of an SPI bus doesn't provide a mechanism for
+ * "assert CS, wait for MISO to become high, so just wait for
+ * long enough to ensure that the write has completed.
+ */
+ mdelay ( THREEWIRE_WRITE_MDELAY );
+
+ return 0;
+}
diff --git a/gpxe/src/hci/commands/autoboot_cmd.c b/gpxe/src/hci/commands/autoboot_cmd.c
new file mode 100644
index 00000000..0e6f2948
--- /dev/null
+++ b/gpxe/src/hci/commands/autoboot_cmd.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <gpxe/command.h>
+#include <usr/autoboot.h>
+
+static int autoboot_exec ( int argc, char **argv ) {
+
+ if ( argc != 1 ) {
+ printf ( "Usage:\n"
+ " %s\n"
+ "\n"
+ "Attempts to boot the system\n",
+ argv[0] );
+ return 1;
+ }
+
+ autoboot();
+
+ /* Can never return success by definition */
+ return 1;
+}
+
+struct command autoboot_command __command = {
+ .name = "autoboot",
+ .exec = autoboot_exec,
+};
diff --git a/gpxe/src/hci/commands/config_cmd.c b/gpxe/src/hci/commands/config_cmd.c
new file mode 100644
index 00000000..5a34cea2
--- /dev/null
+++ b/gpxe/src/hci/commands/config_cmd.c
@@ -0,0 +1,37 @@
+#include <string.h>
+#include <stdio.h>
+#include <gpxe/command.h>
+#include <gpxe/settings.h>
+#include <gpxe/settings_ui.h>
+
+static int config_exec ( int argc, char **argv ) {
+ struct settings *settings = NULL;
+ int rc;
+
+ if ( argc > 2 ) {
+ printf ( "Usage: %s [scope]\n"
+ "Opens the option configuration console\n", argv[0] );
+ return 1;
+ }
+
+ if ( argc == 2 ) {
+ settings = find_settings ( argv[1] );
+ if ( ! settings ) {
+ printf ( "No such scope \"%s\"\n", argv[1] );
+ return 1;
+ }
+ }
+
+ if ( ( rc = settings_ui ( settings ) ) != 0 ) {
+ printf ( "Could not save settings: %s\n",
+ strerror ( rc ) );
+ return 1;
+ }
+
+ return 0;
+}
+
+struct command config_command __command = {
+ .name = "config",
+ .exec = config_exec,
+};
diff --git a/gpxe/src/hci/commands/dhcp_cmd.c b/gpxe/src/hci/commands/dhcp_cmd.c
new file mode 100644
index 00000000..07acc615
--- /dev/null
+++ b/gpxe/src/hci/commands/dhcp_cmd.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include <getopt.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/command.h>
+#include <usr/dhcpmgmt.h>
+
+/** @file
+ *
+ * DHCP management commands
+ *
+ */
+
+/**
+ * "dhcp" command syntax message
+ *
+ * @v argv Argument list
+ */
+static void dhcp_syntax ( char **argv ) {
+ printf ( "Usage:\n"
+ " %s <interface>\n"
+ "\n"
+ "Configure a network interface using DHCP\n",
+ argv[0] );
+}
+
+/**
+ * The "dhcp" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int dhcp_exec ( int argc, char **argv ) {
+ static struct option longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+ const char *name;
+ struct net_device *netdev;
+ int c;
+ int rc;
+
+ /* Parse options */
+ while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){
+ switch ( c ) {
+ case 'h':
+ /* Display help text */
+ default:
+ /* Unrecognised/invalid option */
+ dhcp_syntax ( argv );
+ return 1;
+ }
+ }
+
+ /* Need exactly one interface name remaining after the options */
+ if ( optind != ( argc - 1 ) ) {
+ dhcp_syntax ( argv );
+ return 1;
+ }
+ name = argv[optind];
+
+ /* Perform DHCP */
+ netdev = find_netdev ( name );
+ if ( ! netdev ) {
+ printf ( "No such interface: %s\n", name );
+ return 1;
+ }
+ if ( ( rc = dhcp ( netdev ) ) != 0 ) {
+ printf ( "Could not configure %s: %s\n", netdev->name,
+ strerror ( rc ) );
+ return 1;
+ }
+
+ return 0;
+}
+
+/** DHCP management commands */
+struct command dhcp_commands[] __command = {
+ {
+ .name = "dhcp",
+ .exec = dhcp_exec,
+ },
+};
diff --git a/gpxe/src/hci/commands/ifmgmt_cmd.c b/gpxe/src/hci/commands/ifmgmt_cmd.c
new file mode 100644
index 00000000..f2508e55
--- /dev/null
+++ b/gpxe/src/hci/commands/ifmgmt_cmd.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/command.h>
+#include <usr/ifmgmt.h>
+
+/** @file
+ *
+ * Network interface management commands
+ *
+ */
+
+/** Options shared by all if<xxx> commands */
+static struct option ifcommon_longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+};
+
+/**
+ * Print syntax of if<xxx> command
+ *
+ * @v argv Command arguments
+ * @v verb Verb describing the action of the command
+ */
+static void ifcommon_syntax ( char **argv, const char *verb ) {
+ printf ( "Usage:\n"
+ " %s [<interface>] [<interface>...]\n"
+ "\n"
+ "%s the specified network interfaces\n",
+ argv[0], verb );
+}
+
+/**
+ * Execute if<xxx> command over all network devices
+ *
+ * @v payload Command to execute
+ * @ret rc Exit code
+ */
+static int ifcommon_do_all ( int ( * payload ) ( struct net_device * ) ) {
+ struct net_device *netdev;
+ int rc = 0;
+
+ /* Execute payload for each network device */
+ for_each_netdev ( netdev ) {
+ if ( payload ( netdev ) != 0 )
+ rc = 1;
+ }
+ return rc;
+}
+
+/**
+ * Execute if<xxx> command over list of network devices
+ *
+ * @v payload Command to execute
+ * @ret rc Exit code
+ */
+static int ifcommon_do_list ( int ( * payload ) ( struct net_device * ),
+ char **list, unsigned int count ) {
+ const char *netdev_name;
+ struct net_device *netdev;
+ int rc = 0;
+
+ while ( count-- ) {
+ netdev_name = *(list++);
+ netdev = find_netdev ( netdev_name );
+ if ( ! netdev ) {
+ printf ( "%s: no such interface\n", netdev_name );
+ rc = 1;
+ continue;
+ }
+ if ( payload ( netdev ) != 0 )
+ rc = 1;
+ }
+ return rc;
+}
+
+/**
+ * Execute if<xxx> command
+ *
+ * @v payload Command to execute
+ * @v verb Verb describing the action of the command
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static __attribute__ (( regparm ( 2 ) )) int
+ifcommon_exec ( int ( * payload ) ( struct net_device * ),
+ const char *verb, int argc, char **argv ) {
+ int c;
+
+ /* Parse options */
+ while ( ( c = getopt_long ( argc, argv, "h", ifcommon_longopts,
+ NULL ) ) >= 0 ) {
+ switch ( c ) {
+ case 'h':
+ /* Display help text */
+ default:
+ /* Unrecognised/invalid option */
+ ifcommon_syntax ( argv, verb );
+ return 1;
+ }
+ }
+
+ if ( optind == argc ) {
+ return ifcommon_do_all ( payload );
+ } else {
+ return ifcommon_do_list ( payload, &argv[optind],
+ ( argc - optind ) );
+ }
+}
+
+/* "ifopen" command */
+
+static int ifopen_payload ( struct net_device *netdev ) {
+ return ifopen ( netdev );
+}
+
+static int ifopen_exec ( int argc, char **argv ) {
+ return ifcommon_exec ( ifopen_payload, "Open", argc, argv );
+}
+
+/* "ifclose" command */
+
+static int ifclose_payload ( struct net_device *netdev ) {
+ ifclose ( netdev );
+ return 0;
+}
+
+static int ifclose_exec ( int argc, char **argv ) {
+ return ifcommon_exec ( ifclose_payload, "Close", argc, argv );
+}
+
+/* "ifstat" command */
+
+static int ifstat_payload ( struct net_device *netdev ) {
+ ifstat ( netdev );
+ return 0;
+}
+
+static int ifstat_exec ( int argc, char **argv ) {
+ return ifcommon_exec ( ifstat_payload, "Display status of",
+ argc, argv );
+}
+
+/** Interface management commands */
+struct command ifmgmt_commands[] __command = {
+ {
+ .name = "ifopen",
+ .exec = ifopen_exec,
+ },
+ {
+ .name = "ifclose",
+ .exec = ifclose_exec,
+ },
+ {
+ .name = "ifstat",
+ .exec = ifstat_exec,
+ },
+};
diff --git a/gpxe/src/hci/commands/image_cmd.c b/gpxe/src/hci/commands/image_cmd.c
new file mode 100644
index 00000000..44689dd2
--- /dev/null
+++ b/gpxe/src/hci/commands/image_cmd.c
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <gpxe/image.h>
+#include <gpxe/command.h>
+#include <gpxe/initrd.h>
+#include <usr/imgmgmt.h>
+
+/** @file
+ *
+ * Image management commands
+ *
+ */
+
+enum image_action {
+ IMG_FETCH = 0,
+ IMG_LOAD,
+ IMG_EXEC,
+};
+
+/**
+ * Fill in image command line
+ *
+ * @v image Image
+ * @v nargs Argument count
+ * @v args Argument list
+ * @ret rc Return status code
+ */
+static int imgfill_cmdline ( struct image *image, unsigned int nargs,
+ char **args ) {
+ char buf[256];
+ size_t used = 0;
+
+ memset ( buf, 0, sizeof ( buf ) );
+ while ( ( used < sizeof ( buf ) ) && nargs-- ) {
+ used += snprintf ( &buf[used], ( sizeof ( buf ) - used ),
+ " %s", *(args++) );
+ }
+
+ return image_set_cmdline ( image, &buf[1] );
+}
+
+/**
+ * "imgfetch"/"module"/"kernel" command syntax message
+ *
+ * @v argv Argument list
+ */
+static void imgfetch_core_syntax ( char **argv, enum image_action action ) {
+ static const char *actions[] = {
+ [IMG_FETCH] = "Fetch",
+ [IMG_LOAD] = "Fetch and load",
+ [IMG_EXEC] = "Fetch and execute",
+ };
+
+ printf ( "Usage:\n"
+ " %s [-n|--name <name>] filename [arguments...]\n"
+ "\n"
+ "%s executable/loadable image\n",
+ argv[0], actions[action] );
+}
+
+/**
+ * The "imgfetch"/"module"/"kernel" command body
+ *
+ * @v image_type Image type to assign (or NULL)
+ * @v load Image will be automatically loaded after fetching
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Return status code
+ */
+static int imgfetch_core_exec ( struct image_type *image_type,
+ enum image_action action,
+ int argc, char **argv ) {
+ static struct option longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { "name", required_argument, NULL, 'n' },
+ { NULL, 0, NULL, 0 },
+ };
+ struct image *image;
+ const char *name = NULL;
+ char *filename;
+ int ( * image_register ) ( struct image *image );
+ int c;
+ int rc;
+
+ /* Parse options */
+ while ( ( c = getopt_long ( argc, argv, "hn:",
+ longopts, NULL ) ) >= 0 ) {
+ switch ( c ) {
+ case 'n':
+ /* Set image name */
+ name = optarg;
+ break;
+ case 'h':
+ /* Display help text */
+ default:
+ /* Unrecognised/invalid option */
+ imgfetch_core_syntax ( argv, action );
+ return -EINVAL;
+ }
+ }
+
+ /* Need at least a filename remaining after the options */
+ if ( optind == argc ) {
+ imgfetch_core_syntax ( argv, action );
+ return -EINVAL;
+ }
+ filename = argv[optind++];
+ if ( ! name )
+ name = basename ( filename );
+
+ /* Allocate image */
+ image = alloc_image();
+ if ( ! image ) {
+ printf ( "%s\n", strerror ( -ENOMEM ) );
+ return -ENOMEM;
+ }
+
+ /* Fill in image name */
+ if ( name ) {
+ if ( ( rc = image_set_name ( image, name ) ) != 0 )
+ return rc;
+ }
+
+ /* Set image type (if specified) */
+ image->type = image_type;
+
+ /* Fill in command line */
+ if ( ( rc = imgfill_cmdline ( image, ( argc - optind ),
+ &argv[optind] ) ) != 0 )
+ return rc;
+
+ /* Fetch the image */
+ switch ( action ) {
+ case IMG_FETCH:
+ image_register = register_image;
+ break;
+ case IMG_LOAD:
+ image_register = register_and_autoload_image;
+ break;
+ case IMG_EXEC:
+ image_register = register_and_autoexec_image;
+ break;
+ default:
+ assert ( 0 );
+ return -EINVAL;
+ }
+ if ( ( rc = imgfetch ( image, filename, image_register ) ) != 0 ) {
+ printf ( "Could not fetch %s: %s\n",
+ filename, strerror ( rc ) );
+ image_put ( image );
+ return rc;
+ }
+
+ image_put ( image );
+ return 0;
+}
+
+/**
+ * The "imgfetch"/"module" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int imgfetch_exec ( int argc, char **argv ) {
+ int rc;
+
+ if ( ( rc = imgfetch_core_exec ( NULL, IMG_FETCH,
+ argc, argv ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * The "kernel" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int kernel_exec ( int argc, char **argv ) {
+ int rc;
+
+ if ( ( rc = imgfetch_core_exec ( NULL, IMG_LOAD, argc, argv ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * The "initrd" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int initrd_exec ( int argc, char **argv ) {
+ int rc;
+
+ if ( ( rc = imgfetch_core_exec ( &initrd_image_type, IMG_FETCH,
+ argc, argv ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * "imgload" command syntax message
+ *
+ * @v argv Argument list
+ */
+static void imgload_syntax ( char **argv ) {
+ printf ( "Usage:\n"
+ " %s <image name>\n"
+ "\n"
+ "Load executable/loadable image\n",
+ argv[0] );
+}
+
+/**
+ * The "imgload" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int imgload_exec ( int argc, char **argv ) {
+ static struct option longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+ struct image *image;
+ const char *name;
+ int c;
+ int rc;
+
+ /* Parse options */
+ while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){
+ switch ( c ) {
+ case 'h':
+ /* Display help text */
+ default:
+ /* Unrecognised/invalid option */
+ imgload_syntax ( argv );
+ return 1;
+ }
+ }
+
+ /* Need exactly one image name remaining after the options */
+ if ( optind != ( argc - 1 ) ) {
+ imgload_syntax ( argv );
+ return 1;
+ }
+ name = argv[optind];
+
+ /* Load all specified images */
+ image = find_image ( name );
+ if ( ! image ) {
+ printf ( "No such image: %s\n", name );
+ return 1;
+ }
+ if ( ( rc = imgload ( image ) ) != 0 ) {
+ printf ( "Could not load %s: %s\n", name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * "imgargs" command syntax message
+ *
+ * @v argv Argument list
+ */
+static void imgargs_syntax ( char **argv ) {
+ printf ( "Usage:\n"
+ " %s <image name> [<arguments>...]\n"
+ "\n"
+ "Set arguments for executable/loadable image\n",
+ argv[0] );
+}
+
+/**
+ * The "imgargs" command body
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int imgargs_exec ( int argc, char **argv ) {
+ static struct option longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+ struct image *image;
+ const char *name;
+ int c;
+ int rc;
+
+ /* Parse options */
+ while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){
+ switch ( c ) {
+ case 'h':
+ /* Display help text */
+ default:
+ /* Unrecognised/invalid option */
+ imgargs_syntax ( argv );
+ return 1;
+ }
+ }
+
+ /* Need at least an image name remaining after the options */
+ if ( optind == argc ) {
+ imgargs_syntax ( argv );
+ return 1;
+ }
+ name = argv[optind++];
+
+ /* Fill in command line */
+ image = find_image ( name );
+ if ( ! image ) {
+ printf ( "No such image: %s\n", name );
+ return 1;
+ }
+ if ( ( rc = imgfill_cmdline ( image, ( argc - optind ),
+ &argv[optind] ) ) != 0 )
+ return rc;
+
+
+ return 0;
+}
+
+/**
+ * "imgexec" command syntax message
+ *
+ * @v argv Argument list
+ */
+static void imgexec_syntax ( char **argv ) {
+ printf ( "Usage:\n"
+ " %s <image name>\n"
+ "\n"
+ "Execute executable/loadable image\n",
+ argv[0] );
+}
+
+/**
+ * The "imgexec" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int imgexec_exec ( int argc, char **argv ) {
+ static struct option longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+ struct image *image;
+ const char *name = NULL;
+ int c;
+ int rc;
+
+ /* Parse options */
+ while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){
+ switch ( c ) {
+ case 'h':
+ /* Display help text */
+ default:
+ /* Unrecognised/invalid option */
+ imgexec_syntax ( argv );
+ return 1;
+ }
+ }
+
+ /* Need no more than one image name */
+ if ( optind != argc )
+ name = argv[optind++];
+ if ( optind != argc ) {
+ imgexec_syntax ( argv );
+ return 1;
+ }
+
+ /* Execute specified image */
+ if ( name ) {
+ image = find_image ( name );
+ if ( ! image ) {
+ printf ( "No such image: %s\n", name );
+ return 1;
+ }
+ } else {
+ image = imgautoselect();
+ if ( ! image ) {
+ printf ( "No loaded images\n" );
+ return 1;
+ }
+ }
+
+ if ( ( rc = imgexec ( image ) ) != 0 ) {
+ printf ( "Could not execute %s: %s\n",
+ image->name, strerror ( rc ) );
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * "imgstat" command syntax message
+ *
+ * @v argv Argument list
+ */
+static void imgstat_syntax ( char **argv ) {
+ printf ( "Usage:\n"
+ " %s\n"
+ "\n"
+ "List executable/loadable images\n",
+ argv[0] );
+}
+
+/**
+ * The "imgstat" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int imgstat_exec ( int argc, char **argv ) {
+ static struct option longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+ struct image *image;
+ int c;
+
+ /* Parse options */
+ while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){
+ switch ( c ) {
+ case 'h':
+ /* Display help text */
+ default:
+ /* Unrecognised/invalid option */
+ imgstat_syntax ( argv );
+ return 1;
+ }
+ }
+
+ /* No arguments */
+ if ( optind != argc ) {
+ imgstat_syntax ( argv );
+ return 1;
+ }
+
+ /* Show status of all images */
+ for_each_image ( image ) {
+ imgstat ( image );
+ }
+ return 0;
+}
+
+/**
+ * "imgstat" command syntax message
+ *
+ * @v argv Argument list
+ */
+static void imgfree_syntax ( char **argv ) {
+ printf ( "Usage:\n"
+ " %s\n"
+ "\n"
+ "Free all executable/loadable images\n",
+ argv[0] );
+}
+
+/**
+ * The "imgfree" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int imgfree_exec ( int argc, char **argv ) {
+ static struct option longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+ struct image *image;
+ struct image *tmp;
+ int c;
+
+ /* Parse options */
+ while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){
+ switch ( c ) {
+ case 'h':
+ /* Display help text */
+ default:
+ /* Unrecognised/invalid option */
+ imgfree_syntax ( argv );
+ return 1;
+ }
+ }
+
+ /* No arguments */
+ if ( optind != argc ) {
+ imgfree_syntax ( argv );
+ return 1;
+ }
+
+ /* Free all images */
+ list_for_each_entry_safe ( image, tmp, &images, list ) {
+ imgfree ( image );
+ }
+ return 0;
+}
+
+/** Image management commands */
+struct command image_commands[] __command = {
+ {
+ .name = "imgfetch",
+ .exec = imgfetch_exec,
+ },
+ {
+ .name = "module",
+ .exec = imgfetch_exec, /* synonym for "imgfetch" */
+ },
+ {
+ .name = "kernel",
+ .exec = kernel_exec,
+ },
+ {
+ .name = "initrd",
+ .exec = initrd_exec,
+ },
+ {
+ .name = "imgload",
+ .exec = imgload_exec,
+ },
+ {
+ .name = "imgargs",
+ .exec = imgargs_exec,
+ },
+ {
+ .name = "imgexec",
+ .exec = imgexec_exec,
+ },
+ {
+ .name = "boot", /* synonym for "imgexec" */
+ .exec = imgexec_exec,
+ },
+ {
+ .name = "imgstat",
+ .exec = imgstat_exec,
+ },
+ {
+ .name = "imgfree",
+ .exec = imgfree_exec,
+ },
+};
diff --git a/gpxe/src/hci/commands/nvo_cmd.c b/gpxe/src/hci/commands/nvo_cmd.c
new file mode 100644
index 00000000..c0c07280
--- /dev/null
+++ b/gpxe/src/hci/commands/nvo_cmd.c
@@ -0,0 +1,77 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <gpxe/settings.h>
+#include <gpxe/command.h>
+
+static int show_exec ( int argc, char **argv ) {
+ char buf[256];
+ int rc;
+
+ if ( argc != 2 ) {
+ printf ( "Syntax: %s <identifier>\n", argv[0] );
+ return 1;
+ }
+
+ if ( ( rc = fetchf_named_setting ( argv[1], buf,
+ sizeof ( buf ) ) ) < 0 ){
+ printf ( "Could not find \"%s\": %s\n",
+ argv[1], strerror ( rc ) );
+ return 1;
+ }
+
+ printf ( "%s = %s\n", argv[1], buf );
+ return 0;
+}
+
+static int set_exec ( int argc, char **argv ) {
+ int rc;
+
+ if ( argc != 3 ) {
+ printf ( "Syntax: %s <identifier> <value>\n", argv[0] );
+ return 1;
+ }
+
+ if ( ( rc = storef_named_setting ( argv[1], argv[2] ) ) != 0 ) {
+ printf ( "Could not set \"%s\"=\"%s\": %s\n",
+ argv[1], argv[2], strerror ( rc ) );
+ return 1;
+ }
+
+ return 0;
+}
+
+static int clear_exec ( int argc, char **argv ) {
+ int rc;
+
+ if ( argc != 2 ) {
+ printf ( "Syntax: %s <identifier>\n", argv[0] );
+ return 1;
+ }
+
+ if ( ( rc = delete_named_setting ( argv[1] ) ) != 0 ) {
+ printf ( "Could not clear \"%s\": %s\n",
+ argv[1], strerror ( rc ) );
+ return 1;
+ }
+
+ return 0;
+}
+
+struct command nvo_commands[] __command = {
+ {
+ .name = "show",
+ .exec = show_exec,
+ },
+ {
+ .name = "set",
+ .exec = set_exec,
+ },
+ {
+ .name = "clear",
+ .exec = clear_exec,
+ },
+};
diff --git a/gpxe/src/hci/commands/route_cmd.c b/gpxe/src/hci/commands/route_cmd.c
new file mode 100644
index 00000000..227682cb
--- /dev/null
+++ b/gpxe/src/hci/commands/route_cmd.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <gpxe/command.h>
+#include <usr/route.h>
+
+/** @file
+ *
+ * Routing table management commands
+ *
+ */
+
+/**
+ * "route" command syntax message
+ *
+ * @v argv Argument list
+ */
+static void route_syntax ( char **argv ) {
+ printf ( "Usage:\n"
+ " %s\n"
+ "\n"
+ "Displays the routing table\n",
+ argv[0] );
+}
+
+/**
+ * The "route" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int route_exec ( int argc, char **argv ) {
+ static struct option longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+
+ int c;
+
+ /* Parse options */
+ while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){
+ switch ( c ) {
+ case 'h':
+ /* Display help text */
+ default:
+ /* Unrecognised/invalid option */
+ route_syntax ( argv );
+ return 1;
+ }
+ }
+
+ if ( optind != argc ) {
+ route_syntax ( argv );
+ return 1;
+ }
+
+ route();
+ return 0;
+}
+
+/** Routing table management commands */
+struct command route_commands[] __command = {
+ {
+ .name = "route",
+ .exec = route_exec,
+ },
+};
diff --git a/gpxe/src/hci/commands/sanboot_cmd.c b/gpxe/src/hci/commands/sanboot_cmd.c
new file mode 100644
index 00000000..d5bbfb85
--- /dev/null
+++ b/gpxe/src/hci/commands/sanboot_cmd.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <gpxe/command.h>
+#include <usr/autoboot.h>
+
+/**
+ * "sanboot" command syntax message
+ *
+ * @v argv Argument list
+ */
+static void sanboot_syntax ( char **argv ) {
+ printf ( "Usage:\n"
+ " %s <root-path>\n"
+ "\n"
+ "Boot from SAN target\n",
+ argv[0] );
+}
+
+/**
+ * The "sanboot" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int sanboot_exec ( int argc, char **argv ) {
+ static struct option longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+ const char *root_path = NULL;
+ int c;
+ int rc;
+
+ /* Parse options */
+ while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){
+ switch ( c ) {
+ case 'h':
+ /* Display help text */
+ default:
+ /* Unrecognised/invalid option */
+ sanboot_syntax ( argv );
+ return 1;
+ }
+ }
+
+ /* Need exactly one image name remaining after the options */
+ if ( optind != ( argc - 1 ) ) {
+ sanboot_syntax ( argv );
+ return 1;
+ }
+ root_path = argv[optind];
+
+ /* Boot from root path */
+ if ( ( rc = boot_root_path ( root_path ) ) != 0 ) {
+ printf ( "Could not boot from %s: %s\n",
+ root_path, strerror ( rc ) );
+ return 1;
+ }
+
+ return 0;
+}
+
+struct command sanboot_command __command = {
+ .name = "sanboot",
+ .exec = sanboot_exec,
+};
diff --git a/gpxe/src/hci/editstring.c b/gpxe/src/hci/editstring.c
new file mode 100644
index 00000000..347249f7
--- /dev/null
+++ b/gpxe/src/hci/editstring.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <gpxe/keys.h>
+#include <gpxe/editstring.h>
+
+/** @file
+ *
+ * Editable strings
+ *
+ */
+
+static void insert_delete ( struct edit_string *string, size_t delete_len,
+ const char *insert_text )
+ __attribute__ (( nonnull (1) ));
+static void insert_character ( struct edit_string *string,
+ unsigned int character ) __nonnull;
+static void delete_character ( struct edit_string *string ) __nonnull;
+static void backspace ( struct edit_string *string ) __nonnull;
+static void kill_eol ( struct edit_string *string ) __nonnull;
+
+/**
+ * Insert and/or delete text within an editable string
+ *
+ * @v string Editable string
+ * @v delete_len Length of text to delete from current cursor position
+ * @v insert_text Text to insert at current cursor position, or NULL
+ */
+static void insert_delete ( struct edit_string *string, size_t delete_len,
+ const char *insert_text ) {
+ size_t old_len, max_delete_len, insert_len, max_insert_len, new_len;
+
+ /* Calculate lengths */
+ old_len = strlen ( string->buf );
+ assert ( string->cursor <= old_len );
+ max_delete_len = ( old_len - string->cursor );
+ if ( delete_len > max_delete_len )
+ delete_len = max_delete_len;
+ insert_len = ( insert_text ? strlen ( insert_text ) : 0 );
+ max_insert_len = ( ( string->len - 1 ) - ( old_len - delete_len ) );
+ if ( insert_len > max_insert_len )
+ insert_len = max_insert_len;
+ new_len = ( old_len - delete_len + insert_len );
+
+ /* Fill in edit history */
+ string->mod_start = string->cursor;
+ string->mod_end = ( ( new_len > old_len ) ? new_len : old_len );
+
+ /* Move data following the cursor */
+ memmove ( ( string->buf + string->cursor + insert_len ),
+ ( string->buf + string->cursor + delete_len ),
+ ( max_delete_len + 1 - delete_len ) );
+
+ /* Copy inserted text to cursor position */
+ memcpy ( ( string->buf + string->cursor ), insert_text, insert_len );
+ string->cursor += insert_len;
+}
+
+/**
+ * Insert character at current cursor position
+ *
+ * @v string Editable string
+ * @v character Character to insert
+ */
+static void insert_character ( struct edit_string *string,
+ unsigned int character ) {
+ char insert_text[2] = { character, '\0' };
+ insert_delete ( string, 0, insert_text );
+}
+
+/**
+ * Delete character at current cursor position
+ *
+ * @v string Editable string
+ */
+static void delete_character ( struct edit_string *string ) {
+ insert_delete ( string, 1, NULL );
+}
+
+/**
+ * Delete character to left of current cursor position
+ *
+ * @v string Editable string
+ */
+static void backspace ( struct edit_string *string ) {
+ if ( string->cursor > 0 ) {
+ string->cursor--;
+ delete_character ( string );
+ }
+}
+
+/**
+ * Delete to end of line
+ *
+ * @v string Editable string
+ */
+static void kill_eol ( struct edit_string *string ) {
+ insert_delete ( string, ~( ( size_t ) 0 ), NULL );
+}
+
+/**
+ * Edit editable string
+ *
+ * @v string Editable string
+ * @v key Key pressed by user
+ * @ret key Key returned to application, or zero
+ *
+ * Handles keypresses and updates the content of the editable string.
+ * Basic line editing facilities (delete/insert/cursor) are supported.
+ * If edit_string() understands and uses the keypress it will return
+ * zero, otherwise it will return the original key.
+ *
+ * This function does not update the display in any way.
+ *
+ * The string's edit history will be updated to allow the caller to
+ * efficiently bring the display into sync with the string content.
+ */
+int edit_string ( struct edit_string *string, int key ) {
+ int retval = 0;
+ size_t len = strlen ( string->buf );
+
+ /* Prepare edit history */
+ string->last_cursor = string->cursor;
+ string->mod_start = string->cursor;
+ string->mod_end = string->cursor;
+
+ /* Interpret key */
+ if ( ( key >= 0x20 ) && ( key <= 0x7e ) ) {
+ /* Printable character; insert at current position */
+ insert_character ( string, key );
+ } else switch ( key ) {
+ case KEY_BACKSPACE:
+ /* Backspace */
+ backspace ( string );
+ break;
+ case KEY_DC:
+ case CTRL_D:
+ /* Delete character */
+ delete_character ( string );
+ break;
+ case CTRL_K:
+ /* Delete to end of line */
+ kill_eol ( string );
+ break;
+ case KEY_HOME:
+ case CTRL_A:
+ /* Start of line */
+ string->cursor = 0;
+ break;
+ case KEY_END:
+ case CTRL_E:
+ /* End of line */
+ string->cursor = len;
+ break;
+ case KEY_LEFT:
+ case CTRL_B:
+ /* Cursor left */
+ if ( string->cursor > 0 )
+ string->cursor--;
+ break;
+ case KEY_RIGHT:
+ case CTRL_F:
+ /* Cursor right */
+ if ( string->cursor < len )
+ string->cursor++;
+ break;
+ default:
+ retval = key;
+ break;
+ }
+
+ return retval;
+}
diff --git a/gpxe/src/hci/mucurses/alert.c b/gpxe/src/hci/mucurses/alert.c
new file mode 100644
index 00000000..00e959a8
--- /dev/null
+++ b/gpxe/src/hci/mucurses/alert.c
@@ -0,0 +1,18 @@
+#include <curses.h>
+#include <stdio.h>
+
+/** @file
+ *
+ * MuCurses alert functions
+ *
+ */
+
+/**
+ * Audible signal
+ *
+ * @ret rc return status code
+ */
+int beep ( void ) {
+ printf("\a");
+ return OK;
+}
diff --git a/gpxe/src/hci/mucurses/ansi_screen.c b/gpxe/src/hci/mucurses/ansi_screen.c
new file mode 100644
index 00000000..0742a7d4
--- /dev/null
+++ b/gpxe/src/hci/mucurses/ansi_screen.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <curses.h>
+#include <console.h>
+
+static void ansiscr_reset(struct _curses_screen *scr) __nonnull;
+static void ansiscr_movetoyx(struct _curses_screen *scr,
+ unsigned int y, unsigned int x) __nonnull;
+static void ansiscr_putc(struct _curses_screen *scr, chtype c) __nonnull;
+
+unsigned short _COLS = 80;
+unsigned short _LINES = 24;
+
+static void ansiscr_reset ( struct _curses_screen *scr ) {
+ /* Reset terminal attributes and clear screen */
+ scr->attrs = 0;
+ scr->curs_x = 0;
+ scr->curs_y = 0;
+ printf ( "\033[0m\033[2J\033[1;1H" );
+}
+
+static void ansiscr_movetoyx ( struct _curses_screen *scr,
+ unsigned int y, unsigned int x ) {
+ if ( ( x != scr->curs_x ) || ( y != scr->curs_y ) ) {
+ /* ANSI escape sequence to update cursor position */
+ printf ( "\033[%d;%dH", ( y + 1 ), ( x + 1 ) );
+ scr->curs_x = x;
+ scr->curs_y = y;
+ }
+}
+
+static void ansiscr_putc ( struct _curses_screen *scr, chtype c ) {
+ unsigned int character = ( c & A_CHARTEXT );
+ attr_t attrs = ( c & ( A_ATTRIBUTES | A_COLOR ) );
+ int bold = ( attrs & A_BOLD );
+ attr_t cpair = PAIR_NUMBER ( attrs );
+ short fcol;
+ short bcol;
+
+ /* Update attributes if changed */
+ if ( attrs != scr->attrs ) {
+ scr->attrs = attrs;
+ pair_content ( cpair, &fcol, &bcol );
+ /* ANSI escape sequence to update character attributes */
+ printf ( "\033[0;%d;3%d;4%dm", ( bold ? 1 : 22 ), fcol, bcol );
+ }
+
+ /* Print the actual character */
+ putchar ( character );
+
+ /* Update expected cursor position */
+ if ( ++(scr->curs_x) == _COLS ) {
+ scr->curs_x = 0;
+ ++scr->curs_y;
+ }
+}
+
+static int ansiscr_getc ( struct _curses_screen *scr __unused ) {
+ return getchar();
+}
+
+static bool ansiscr_peek ( struct _curses_screen *scr __unused ) {
+ return iskey();
+}
+
+SCREEN _ansi_screen = {
+ .init = ansiscr_reset,
+ .exit = ansiscr_reset,
+ .movetoyx = ansiscr_movetoyx,
+ .putc = ansiscr_putc,
+ .getc = ansiscr_getc,
+ .peek = ansiscr_peek,
+};
diff --git a/gpxe/src/hci/mucurses/clear.c b/gpxe/src/hci/mucurses/clear.c
new file mode 100644
index 00000000..1813939b
--- /dev/null
+++ b/gpxe/src/hci/mucurses/clear.c
@@ -0,0 +1,88 @@
+#include <curses.h>
+#include "mucurses.h"
+#include "cursor.h"
+
+/** @file
+ *
+ * MuCurses clearing functions
+ *
+ */
+
+/**
+ * Clear a window to the bottom from current cursor position
+ *
+ * @v *win subject window
+ * @ret rc return status code
+ */
+int wclrtobot ( WINDOW *win ) {
+ struct cursor_pos pos;
+
+ _store_curs_pos( win, &pos );
+ do {
+ _wputc( win, ' ', WRAP );
+ } while ( win->curs_y + win->curs_x );
+ _restore_curs_pos( win, &pos );
+
+ return OK;
+}
+
+/**
+ * Clear a window to the end of the current line
+ *
+ * @v *win subject window
+ * @ret rc return status code
+ */
+int wclrtoeol ( WINDOW *win ) {
+ struct cursor_pos pos;
+
+ _store_curs_pos( win, &pos );
+ while ( ( win->curs_y - pos.y ) == 0 ) {
+ _wputc( win, ' ', WRAP );
+ }
+ _restore_curs_pos( win, &pos );
+
+ return OK;
+}
+
+/**
+ * Delete character under the cursor in a window
+ *
+ * @v *win subject window
+ * @ret rc return status code
+ */
+int wdelch ( WINDOW *win ) {
+ _wputc( win, ' ', NOWRAP );
+ _wcursback( win );
+
+ return OK;
+}
+
+/**
+ * Delete line under a window's cursor
+ *
+ * @v *win subject window
+ * @ret rc return status code
+ */
+int wdeleteln ( WINDOW *win ) {
+ struct cursor_pos pos;
+
+ _store_curs_pos( win, &pos );
+ /* let's just set the cursor to the beginning of the line and
+ let wclrtoeol do the work :) */
+ wmove( win, win->curs_y, 0 );
+ wclrtoeol( win );
+ _restore_curs_pos( win, &pos );
+ return OK;
+}
+
+/**
+ * Completely clear a window
+ *
+ * @v *win subject window
+ * @ret rc return status code
+ */
+int werase ( WINDOW *win ) {
+ wmove( win, 0, 0 );
+ wclrtobot( win );
+ return OK;
+}
diff --git a/gpxe/src/hci/mucurses/colour.c b/gpxe/src/hci/mucurses/colour.c
new file mode 100644
index 00000000..2310641e
--- /dev/null
+++ b/gpxe/src/hci/mucurses/colour.c
@@ -0,0 +1,64 @@
+#include <curses.h>
+
+struct colour_pair {
+ short fcol;
+ short bcol;
+};
+
+static struct colour_pair cpairs[COLOUR_PAIRS] = {
+ [0] = { COLOUR_WHITE, COLOUR_BLACK },
+};
+
+/**
+ * Identify the RGB components of a given colour value
+ *
+ * @v colour colour value
+ * @v *red address to store red component
+ * @v *green address to store green component
+ * @v *blue address to store blue component
+ * @ret rc return status code
+ */
+int colour_content ( short colour, short *red, short *green, short *blue ) {
+ *red = ( ( colour & COLOUR_RED ) ? 1 : 0 );
+ *green = ( ( colour & COLOUR_GREEN ) ? 1 : 0 );
+ *blue = ( ( colour & COLOUR_BLUE ) ? 1 : 0 );
+ return OK;
+}
+
+/**
+ * Initialise colour pair
+ *
+ * @v pair colour pair number
+ * @v fcol foreground colour
+ * @v bcol background colour
+ */
+int init_pair ( short pair, short fcol, short bcol ) {
+ struct colour_pair *cpair;
+
+ if ( ( pair < 1 ) || ( pair >= COLOUR_PAIRS ) )
+ return ERR;
+
+ cpair = &cpairs[pair];
+ cpair->fcol = fcol;
+ cpair->bcol = bcol;
+ return OK;
+}
+
+/**
+ * Get colours of colour pair
+ *
+ * @v pair colour pair number
+ * @ret fcol foreground colour
+ * @ret bcol background colour
+ */
+int pair_content ( short pair, short *fcol, short *bcol ) {
+ struct colour_pair *cpair;
+
+ if ( ( pair < 0 ) || ( pair >= COLOUR_PAIRS ) )
+ return ERR;
+
+ cpair = &cpairs[pair];
+ *fcol = cpair->fcol;
+ *bcol = cpair->bcol;
+ return OK;
+}
diff --git a/gpxe/src/hci/mucurses/cursor.h b/gpxe/src/hci/mucurses/cursor.h
new file mode 100644
index 00000000..af86519c
--- /dev/null
+++ b/gpxe/src/hci/mucurses/cursor.h
@@ -0,0 +1,35 @@
+#ifndef CURSOR_H
+#define CURSOR_H
+
+/** @file
+ *
+ * MuCurses cursor implementation specific header file
+ *
+ */
+
+struct cursor_pos {
+ unsigned int y, x;
+};
+
+/**
+ * Restore cursor position from encoded backup variable
+ *
+ * @v *win window on which to operate
+ * @v *pos pointer to struct in which original cursor position is stored
+ */
+static inline void _restore_curs_pos ( WINDOW *win, struct cursor_pos *pos ) {
+ wmove ( win, pos->y, pos->x );
+}
+
+/**
+ * Store cursor position for later restoration
+ *
+ * @v *win window on which to operate
+ * @v *pos pointer to struct in which to store cursor position
+ */
+static inline void _store_curs_pos ( WINDOW *win, struct cursor_pos *pos ) {
+ pos->y = win->curs_y;
+ pos->x = win->curs_x;
+}
+
+#endif /* CURSOR_H */
diff --git a/gpxe/src/hci/mucurses/edging.c b/gpxe/src/hci/mucurses/edging.c
new file mode 100644
index 00000000..eccd3242
--- /dev/null
+++ b/gpxe/src/hci/mucurses/edging.c
@@ -0,0 +1,111 @@
+#include <curses.h>
+#include "mucurses.h"
+#include "cursor.h"
+
+/** @file
+ *
+ * MuCurses edging functions
+ *
+ */
+
+/**
+ * Draw borders from single-byte characters and renditions around a
+ * window
+ *
+ * @v *win window to be bordered
+ * @v verch vertical chtype
+ * @v horch horizontal chtype
+ * @ret rc return status code
+ */
+int box ( WINDOW *win, chtype verch, chtype horch ) {
+ chtype corner = '+' | win->attrs; /* default corner character */
+ return wborder( win, verch, verch, horch, horch,
+ corner, corner, corner, corner );
+}
+
+/**
+ * Draw borders from single-byte characters and renditions around a
+ * window
+ *
+ * @v *win window to be bordered
+ * @v ls left side
+ * @v rs right side
+ * @v ts top
+ * @v bs bottom
+ * @v tl top left corner
+ * @v tr top right corner
+ * @v bl bottom left corner
+ * @v br bottom right corner
+ * @ret rc return status code
+ */
+int wborder ( WINDOW *win, chtype ls, chtype rs,
+ chtype ts, chtype bs, chtype tl,
+ chtype tr, chtype bl, chtype br ) {
+ struct cursor_pos pos;
+
+ _store_curs_pos( win, &pos );
+ wmove(win,0,0);
+
+ _wputch(win,tl,WRAP);
+ while ( ( win->width - 1 ) - win->curs_x ) {
+ _wputch(win,ts,WRAP);
+ }
+ _wputch(win,tr,WRAP);
+
+ while ( ( win->height - 1 ) - win->curs_y ) {
+ _wputch(win,ls,WRAP);
+ wmove(win,win->curs_y,(win->width)-1);
+ _wputch(win,rs,WRAP);
+ }
+
+ _wputch(win,bl,WRAP);
+ while ( ( win->width -1 ) - win->curs_x ) {
+ _wputch(win,bs,WRAP);
+ }
+ _wputch(win,br,NOWRAP); /* do not wrap last char to leave
+ cursor in last position */
+ _restore_curs_pos( win, &pos );
+
+ return OK;
+}
+
+/**
+ * Create a horizontal line in a window
+ *
+ * @v *win subject window
+ * @v ch rendition and character
+ * @v n max number of chars (wide) to render
+ * @ret rc return status code
+ */
+int whline ( WINDOW *win, chtype ch, int n ) {
+ struct cursor_pos pos;
+
+ _store_curs_pos ( win, &pos );
+ while ( ( win->curs_x - win->width ) && n-- ) {
+ _wputch ( win, ch, NOWRAP );
+ }
+ _restore_curs_pos ( win, &pos );
+
+ return OK;
+}
+
+/**
+ * Create a vertical line in a window
+ *
+ * @v *win subject window
+ * @v ch rendition and character
+ * @v n max number of chars (high) to render
+ * @ret rc return status code
+ */
+int wvline ( WINDOW *win, chtype ch, int n ) {
+ struct cursor_pos pos;
+
+ _store_curs_pos ( win, &pos );
+ while ( ( win->curs_y - win->height ) && n-- ) {
+ _wputch ( win, ch, NOWRAP );
+ wmove( win, ++(win->curs_y), pos.x);
+ }
+ _restore_curs_pos ( win, &pos );
+
+ return OK;
+}
diff --git a/gpxe/src/hci/mucurses/kb.c b/gpxe/src/hci/mucurses/kb.c
new file mode 100644
index 00000000..cada7291
--- /dev/null
+++ b/gpxe/src/hci/mucurses/kb.c
@@ -0,0 +1,143 @@
+#include <curses.h>
+#include <stddef.h>
+#include <unistd.h>
+#include "mucurses.h"
+
+/** @file
+ *
+ * MuCurses keyboard input handling functions
+ */
+
+#define INPUT_DELAY 200 // half-blocking delay timer resolution (ms)
+#define INPUT_DELAY_TIMEOUT 1000 // half-blocking delay timeout
+
+int m_delay; /*
+ < 0 : blocking read
+ 0 : non-blocking read
+ > 0 : timed blocking read
+ */
+bool m_echo;
+bool m_cbreak;
+
+static int _wgetc ( WINDOW *win ) {
+ int timer, c;
+
+ if ( win == NULL )
+ return ERR;
+
+ timer = INPUT_DELAY_TIMEOUT;
+ while ( ! win->scr->peek( win->scr ) ) {
+ if ( m_delay == 0 ) // non-blocking read
+ return ERR;
+ if ( timer > 0 ) { // time-limited blocking read
+ if ( m_delay > 0 )
+ timer -= INPUT_DELAY;
+ mdelay( INPUT_DELAY );
+ } else { return ERR; } // non-blocking read
+ }
+
+ c = win->scr->getc( win->scr );
+
+ if ( m_echo && ( c >= 32 && c <= 126 ) ) // printable ASCII characters
+ _wputch( win, (chtype) ( c | win->attrs ), WRAP );
+
+ return c;
+}
+
+/**
+ * Pop a character from the FIFO into a window
+ *
+ * @v *win window in which to echo input
+ * @ret c char from input stream
+ */
+int wgetch ( WINDOW *win ) {
+ int c;
+
+ c = _wgetc( win );
+
+ if ( m_echo ) {
+ if ( c >= KEY_MIN ) {
+ switch(c) {
+ case KEY_LEFT :
+ case KEY_BACKSPACE :
+ _wcursback( win );
+ wdelch( win );
+ break;
+ default :
+ beep();
+ break;
+ }
+ } else {
+ _wputch( win, (chtype)( c | win->attrs ), WRAP );
+ }
+ }
+
+ return c;
+}
+
+/**
+ * Read at most n characters from the FIFO into a window
+ *
+ * @v *win window in which to echo input
+ * @v *str pointer to string in which to store result
+ * @v n maximum number of characters to read into string (inc. NUL)
+ * @ret rc return status code
+ */
+int wgetnstr ( WINDOW *win, char *str, int n ) {
+ char *_str;
+ int c;
+
+ if ( n == 0 ) {
+ str = '\0';
+ return OK;
+ }
+
+ _str = str;
+
+ while ( ( c = _wgetc( win ) ) != ERR ) {
+ /* termination enforcement - don't let us go past the
+ end of the allocated buffer... */
+ if ( n == 0 && ( c >= 32 && c <= 126 ) ) {
+ _wcursback( win );
+ wdelch( win );
+ } else {
+ if ( c >= KEY_MIN ) {
+ switch(c) {
+ case KEY_LEFT :
+ case KEY_BACKSPACE :
+ _wcursback( win );
+ wdelch( win );
+ break;
+ case KEY_ENTER :
+ *_str = '\0';
+ return OK;
+ default :
+ beep();
+ break;
+ }
+ }
+ if ( c >= 32 && c <= 126 ) {
+ *(_str++) = c; n--;
+ }
+ }
+ }
+
+ return ERR;
+}
+
+
+/**
+ *
+ */
+int echo ( void ) {
+ m_echo = TRUE;
+ return OK;
+}
+
+/**
+ *
+ */
+int noecho ( void ) {
+ m_echo = FALSE;
+ return OK;
+}
diff --git a/gpxe/src/hci/mucurses/mucurses.c b/gpxe/src/hci/mucurses/mucurses.c
new file mode 100644
index 00000000..3620d08b
--- /dev/null
+++ b/gpxe/src/hci/mucurses/mucurses.c
@@ -0,0 +1,145 @@
+#include <console.h>
+#include <curses.h>
+#include "mucurses.h"
+
+/** @file
+ *
+ * MuCurses core functions
+ *
+ */
+
+static void _wupdcurs ( WINDOW *win ) __nonnull;
+void _wputch ( WINDOW *win, chtype ch, int wrap ) __nonnull;
+void _wputc ( WINDOW *win, char c, int wrap ) __nonnull;
+void _wcursback ( WINDOW *win ) __nonnull;
+void _wputchstr ( WINDOW *win, const chtype *chstr, int wrap, int n ) __nonnull;
+void _wputstr ( WINDOW *win, const char *str, int wrap, int n ) __nonnull;
+int wmove ( WINDOW *win, int y, int x ) __nonnull;
+
+WINDOW _stdscr = {
+ .attrs = A_DEFAULT,
+ .ori_y = 0,
+ .ori_x = 0,
+ .curs_y = 0,
+ .curs_x = 0,
+ .scr = &_ansi_screen,
+};
+
+/*
+ * Primitives
+ */
+
+/**
+ * Update cursor position
+ *
+ * @v *win window in which to update position
+ */
+static void _wupdcurs ( WINDOW *win ) {
+ win->scr->movetoyx ( win->scr, win->ori_y + win->curs_y,
+ win->ori_x + win->curs_x );
+}
+
+/**
+ * Write a single character rendition to a window
+ *
+ * @v *win window in which to write
+ * @v ch character rendition to write
+ * @v wrap wrap "switch"
+ */
+void _wputch ( WINDOW *win, chtype ch, int wrap ) {
+ /* make sure we set the screen cursor to the right position
+ first! */
+ _wupdcurs(win);
+ win->scr->putc(win->scr, ch);
+ if ( ++(win->curs_x) - win->width == 0 ) {
+ if ( wrap == WRAP ) {
+ win->curs_x = 0;
+ /* specification says we should really scroll,
+ but we have no buffer to scroll with, so we
+ can only overwrite back at the beginning of
+ the window */
+ if ( ++(win->curs_y) - win->height == 0 )
+ win->curs_y = 0;
+ } else {
+ (win->curs_x)--;
+ }
+ }
+}
+
+/**
+ * Write a single character to a window
+ *
+ * @v *win window in which to write
+ * @v c character rendition to write
+ * @v wrap wrap "switch"
+ */
+void _wputc ( WINDOW *win, char c, int wrap ) {
+ _wputch ( win, ( c | win->attrs ), wrap );
+}
+
+/**
+ * Retreat the cursor back one position (useful for a whole host of
+ * ops)
+ *
+ * @v *win window in which to retreat
+ */
+void _wcursback ( WINDOW *win ) {
+ if ( win->curs_x == 0 ) {
+ if ( win->curs_y == 0 )
+ win->curs_y = win->height - 1;
+ win->curs_x = win->width = 1;
+ } else {
+ win->curs_x--;
+ }
+
+ _wupdcurs(win);
+}
+
+/**
+ * Write a chtype string to a window
+ *
+ * @v *win window in which to write
+ * @v *chstr chtype string
+ * @v wrap wrap "switch"
+ * @v n write at most n chtypes
+ */
+void _wputchstr ( WINDOW *win, const chtype *chstr, int wrap, int n ) {
+ for ( ; *chstr && n-- ; chstr++ ) {
+ _wputch(win,*chstr,wrap);
+ }
+}
+
+/**
+ * Write a standard c-style string to a window
+ *
+ * @v *win window in which to write
+ * @v *str string
+ * @v wrap wrap "switch"
+ * @v n write at most n chars from *str
+ */
+void _wputstr ( WINDOW *win, const char *str, int wrap, int n ) {
+ for ( ; *str && n-- ; str++ ) {
+ _wputc ( win, *str, wrap );
+ }
+}
+
+/**
+ * Move a window's cursor to the specified position
+ *
+ * @v *win window to be operated on
+ * @v y Y position
+ * @v x X position
+ * @ret rc return status code
+ */
+int wmove ( WINDOW *win, int y, int x ) {
+ /* chech for out-of-bounds errors */
+ if ( ( (unsigned)y >= win->height ) ||
+ ( (unsigned)x >= win->width ) ) {
+ return ERR;
+ }
+
+ win->curs_y = y;
+ win->curs_x = x;
+ _wupdcurs(win);
+ return OK;
+}
diff --git a/gpxe/src/hci/mucurses/mucurses.h b/gpxe/src/hci/mucurses/mucurses.h
new file mode 100644
index 00000000..1476733f
--- /dev/null
+++ b/gpxe/src/hci/mucurses/mucurses.h
@@ -0,0 +1,21 @@
+#ifndef _MUCURSES_H
+#define _MUCURSES_H
+
+/** @file
+ *
+ * MuCurses core implementation specific header file
+ *
+ */
+
+#define WRAP 0
+#define NOWRAP 1
+
+extern SCREEN _ansi_screen;
+
+extern void _wputch ( WINDOW *win, chtype ch, int wrap ) __nonnull;
+extern void _wputc ( WINDOW *win, char c, int wrap ) __nonnull;
+extern void _wputchstr ( WINDOW *win, const chtype *chstr, int wrap, int n ) __nonnull;
+extern void _wputstr ( WINDOW *win, const char *str, int wrap, int n ) __nonnull;
+extern void _wcursback ( WINDOW *win ) __nonnull;
+
+#endif /* _MUCURSES_H */
diff --git a/gpxe/src/hci/mucurses/print.c b/gpxe/src/hci/mucurses/print.c
new file mode 100644
index 00000000..9fca3088
--- /dev/null
+++ b/gpxe/src/hci/mucurses/print.c
@@ -0,0 +1,84 @@
+#include <curses.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <gpxe/vsprintf.h>
+#include "mucurses.h"
+
+/** @file
+ *
+ * MuCurses printing functions
+ *
+ */
+
+/**
+ * Add a single-byte character and rendition to a window and advance
+ * the cursor
+ *
+ * @v *win window to be rendered in
+ * @v ch character to be added at cursor
+ * @ret rc return status code
+ */
+int waddch ( WINDOW *win, const chtype ch ) {
+ _wputch( win, ch, WRAP );
+ return OK;
+}
+
+/**
+ * Add string of single-byte characters to a window
+ *
+ * @v *win window to be rendered in
+ * @v *str standard c-style string
+ * @v n max number of chars from string to render
+ * @ret rc return status code
+ */
+int waddnstr ( WINDOW *win, const char *str, int n ) {
+ _wputstr( win, str, WRAP, n );
+ return OK;
+}
+
+struct printw_context {
+ struct printf_context ctx;
+ WINDOW *win;
+};
+
+static void _printw_handler ( struct printf_context *ctx, unsigned int c ) {
+ struct printw_context *wctx =
+ container_of ( ctx, struct printw_context, ctx );
+
+ _wputch( wctx->win, c | wctx->win->attrs, WRAP );
+}
+
+/**
+ * Print formatted output in a window
+ *
+ * @v *win subject window
+ * @v *fmt formatted string
+ * @v varglist argument list
+ * @ret rc return status code
+ */
+int vw_printw ( WINDOW *win, const char *fmt, va_list varglist ) {
+ struct printw_context wctx;
+
+ wctx.win = win;
+ wctx.ctx.handler = _printw_handler;
+ vcprintf ( &(wctx.ctx), fmt, varglist );
+ return OK;
+}
+
+/**
+ * Print formatted output to a window
+ *
+ * @v *win subject window
+ * @v *fmt formatted string
+ * @v ... string arguments
+ * @ret rc return status code
+ */
+int wprintw ( WINDOW *win, const char *fmt, ... ) {
+ va_list args;
+ int i;
+
+ va_start ( args, fmt );
+ i = vw_printw ( win, fmt, args );
+ va_end ( args );
+ return i;
+}
diff --git a/gpxe/src/hci/mucurses/print_nadv.c b/gpxe/src/hci/mucurses/print_nadv.c
new file mode 100644
index 00000000..ee472e68
--- /dev/null
+++ b/gpxe/src/hci/mucurses/print_nadv.c
@@ -0,0 +1,26 @@
+#include <curses.h>
+#include "mucurses.h"
+#include "cursor.h"
+
+/** @file
+ *
+ * MuCurses printing functions (no cursor advance)
+ *
+ */
+
+/**
+ * Add string of single-byte characters and renditions to a window
+ *
+ * @v *win window to be rendered in
+ * @v *chstr pointer to first chtype in "string"
+ * @v n max number of chars from chstr to render
+ * @ret rc return status code
+ */
+int waddchnstr ( WINDOW *win, const chtype *chstr, int n ) {
+ struct cursor_pos pos;
+
+ _store_curs_pos( win, &pos );
+ _wputchstr( win, chstr, NOWRAP, n );
+ _restore_curs_pos( win, &pos );
+ return OK;
+}
diff --git a/gpxe/src/hci/mucurses/slk.c b/gpxe/src/hci/mucurses/slk.c
new file mode 100644
index 00000000..600658e7
--- /dev/null
+++ b/gpxe/src/hci/mucurses/slk.c
@@ -0,0 +1,363 @@
+#include <curses.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "mucurses.h"
+#include "cursor.h"
+
+/** @file
+ *
+ * Soft label key functions
+ */
+
+#define MIN_SPACE_SIZE 2
+
+#define SLK_MAX_LABEL_LEN 8
+
+#define SLK_MAX_NUM_LABELS 12
+
+#define SLK_MAX_NUM_SPACES 2
+
+struct _softlabel {
+ // label string
+ char label[SLK_MAX_LABEL_LEN];
+ /* Format of soft label
+ 0: left justify
+ 1: centre justify
+ 2: right justify
+ */
+ unsigned int fmt;
+};
+
+struct _softlabelkeys {
+ struct _softlabel fkeys[SLK_MAX_NUM_LABELS];
+ attr_t attrs;
+ /* Soft label layout format
+ 0: 3-2-3
+ 1: 4-4
+ 2: 4-4-4
+ 3: 4-4-4 with index line
+ */
+ unsigned int fmt;
+ unsigned int max_label_len;
+ unsigned int maj_space_len;
+ unsigned int num_labels;
+ unsigned int num_spaces;
+ unsigned int spaces[SLK_MAX_NUM_SPACES];
+ struct cursor_pos saved_cursor;
+ attr_t saved_attrs;
+ short saved_pair;
+};
+
+static struct _softlabelkeys *slks;
+
+/*
+ I either need to break the primitives here, or write a collection of
+ functions specifically for SLKs that directly access the screen
+ functions - since this technically isn't part of stdscr, I think
+ this should be ok...
+ */
+
+static void _enter_slk ( void ) {
+ _store_curs_pos ( stdscr, &slks->saved_cursor );
+ wattr_get ( stdscr, &slks->saved_attrs, &slks->saved_pair, NULL );
+ LINES++;
+ wmove ( stdscr, LINES, 0 );
+ wattrset ( stdscr, slks->attrs );
+}
+
+static void _leave_slk ( void ) {
+ LINES--;
+ wattr_set ( stdscr, slks->saved_attrs, slks->saved_pair, NULL );
+ _restore_curs_pos ( stdscr, &slks->saved_cursor );
+}
+
+static void _print_label ( struct _softlabel sl ) {
+ int space_ch;
+ char str[SLK_MAX_LABEL_LEN + 1];
+
+ assert ( slks->max_label_len <= SLK_MAX_LABEL_LEN );
+ space_ch = ' ';
+
+ // protect against gaps in the soft label keys array
+ if ( sl.label == NULL ) {
+ memset( str, space_ch, (size_t)(slks->max_label_len) );
+ } else {
+ /* we need to pad the label with varying amounts of leading
+ pad depending on the format of the label */
+ if ( sl.fmt == 1 ) {
+ memset( str, space_ch,
+ (size_t)(slks->max_label_len
+ - strlen(sl.label)) / 2 );
+ }
+ if ( sl.fmt == 2 ) {
+ memset( str, space_ch,
+ (size_t)(slks->max_label_len
+ - strlen(sl.label)) );
+ }
+ strcat(str,sl.label);
+
+ // post-padding
+ memset(str+strlen(str), space_ch,
+ (size_t)(slks->max_label_len - strlen(str)) );
+ }
+
+ // print the formatted label
+ _wputstr ( stdscr, str, NOWRAP, slks->max_label_len );
+}
+
+/**
+ * Return the attribute used for the soft function keys
+ *
+ * @ret attrs the current attributes of the soft function keys
+ */
+attr_t slk_attr ( void ) {
+ return ( slks == NULL ? 0 : slks->attrs );
+}
+
+/**
+ * Turn off soft function key attributes
+ *
+ * @v attrs attribute bit mask
+ * @ret rc return status code
+ */
+int slk_attroff ( const chtype attrs ) {
+ if ( slks == NULL )
+ return ERR;
+ slks->attrs &= ~( attrs & A_ATTRIBUTES );
+ return OK;
+}
+
+/**
+ * Turn on soft function key attributes
+ *
+ * @v attrs attribute bit mask
+ * @ret rc return status code
+ */
+int slk_attron ( const chtype attrs ) {
+ if ( slks == NULL )
+ return ERR;
+ slks->attrs |= ( attrs & A_ATTRIBUTES );
+ return OK;
+}
+
+/**
+ * Set soft function key attributes
+ *
+ * @v attrs attribute bit mask
+ * @ret rc return status code
+ */
+int slk_attrset ( const chtype attrs ) {
+ if ( slks == NULL )
+ return ERR;
+ slks->attrs = ( attrs & A_ATTRIBUTES );
+ return OK;
+}
+
+/**
+ * Turn off soft function key attributes
+ *
+ * @v attrs attribute bit mask
+ * @v *opts undefined (for future implementation)
+ * @ret rc return status code
+ */
+int slk_attr_off ( const attr_t attrs, void *opts __unused ) {
+ return slk_attroff( attrs );
+}
+
+/**
+ * Turn on soft function key attributes
+ *
+ * @v attrs attribute bit mask
+ * @v *opts undefined (for future implementation)
+ * @ret rc return status code
+ */
+int slk_attr_on ( attr_t attrs, void *opts __unused ) {
+ return slk_attron( attrs );
+}
+
+/**
+ * Set soft function key attributes
+ *
+ * @v attrs attribute bit mask
+ * @v colour_pair_number colour pair integer
+ * @v *opts undefined (for future implementation)
+ * @ret rc return status code
+ */
+int slk_attr_set ( const attr_t attrs, short colour_pair_number,
+ void *opts __unused ) {
+ if ( slks == NULL )
+ return ERR;
+
+ if ( ( unsigned short )colour_pair_number > COLORS )
+ return ERR;
+
+ slks->attrs = ( (unsigned short)colour_pair_number << CPAIR_SHIFT ) |
+ ( attrs & A_ATTRIBUTES );
+ return OK;
+}
+
+/**
+ * Clear the soft function key labels from the screen
+ *
+ * @ret rc return status code
+ */
+int slk_clear ( void ) {
+ if ( slks == NULL )
+ return ERR;
+
+ _enter_slk();
+ wclrtoeol ( stdscr );
+ _leave_slk();
+
+ return OK;
+}
+
+/**
+ * Set soft label colour pair
+ */
+int slk_colour ( short colour_pair_number ) {
+ if ( slks == NULL )
+ return ERR;
+ if ( ( unsigned short )colour_pair_number > COLORS )
+ return ERR;
+
+ slks->attrs = ( (unsigned short)colour_pair_number << CPAIR_SHIFT )
+ | ( slks->attrs & A_ATTRIBUTES );
+
+ return OK;
+}
+
+/**
+ * Initialise the soft function keys
+ *
+ * @v fmt format of keys
+ * @ret rc return status code
+ */
+int slk_init ( int fmt ) {
+ unsigned short nmaj, nmin, nblocks, available_width;
+
+ if ( (unsigned)fmt > 3 ) {
+ return ERR;
+ }
+
+ /* There seems to be no API call to free this data structure... */
+ if ( ! slks )
+ slks = calloc(1,sizeof(*slks));
+ if ( ! slks )
+ return ERR;
+
+ slks->attrs = A_DEFAULT;
+ slks->fmt = fmt;
+ switch(fmt) {
+ case 0:
+ nblocks = 8; nmaj = 2; nmin = 5;
+ slks->spaces[0] = 2; slks->spaces[1] = 4;
+ break;
+ case 1:
+ nblocks = 8; nmaj = 1; nmin = 6;
+ slks->spaces[0] = 3;
+ break;
+ case 2:
+ // same allocations as format 3
+ case 3:
+ nblocks = 12; nmaj = 2; nmin = 9;
+ slks->spaces[0] = 3; slks->spaces[1] = 7;
+ break;
+ default:
+ nblocks = 0; nmaj = 0; nmin = 0;
+ break;
+ }
+
+ // determine maximum label length and major space size
+ available_width = COLS - ( ( MIN_SPACE_SIZE * nmaj ) + nmin );
+ slks->max_label_len = available_width / nblocks;
+ slks->maj_space_len = MIN_SPACE_SIZE +
+ ( available_width % nblocks ) / nmaj;
+ slks->num_spaces = nmaj;
+ slks->num_labels = nblocks;
+
+ // strip a line from the screen
+ LINES -= 1;
+
+ return OK;
+}
+
+/**
+ * Return the label for the specified soft key
+ *
+ * @v labnum soft key identifier
+ * @ret label return label
+ */
+char* slk_label ( int labnum ) {
+ if ( slks == NULL )
+ return NULL;
+
+ return slks->fkeys[labnum].label;
+}
+
+/**
+ * Restore soft function key labels to the screen
+ *
+ * @ret rc return status code
+ */
+int slk_restore ( void ) {
+ unsigned int i, j, pos_x,
+ *next_space, *last_space;
+ chtype space_ch;
+
+ if ( slks == NULL )
+ return ERR;
+
+ pos_x = 0;
+
+ _enter_slk();
+
+ space_ch = (chtype)' ' | slks->attrs;
+ next_space = &(slks->spaces[0]);
+ last_space = &(slks->spaces[slks->num_spaces-1]);
+
+ for ( i = 0; i < slks->num_labels ; i++ ) {
+ _print_label( slks->fkeys[i] );
+ pos_x += slks->max_label_len;
+
+ if ( i == *next_space ) {
+ for ( j = 0; j < slks->maj_space_len; j++, pos_x++ )
+ _wputch ( stdscr, space_ch, NOWRAP );
+ if ( next_space < last_space )
+ next_space++;
+ } else {
+ if ( pos_x < COLS )
+ _wputch ( stdscr, space_ch, NOWRAP );
+ pos_x++;
+ }
+ }
+
+ _leave_slk();
+
+ return OK;
+}
+
+/**
+ * Configure specified soft key
+ *
+ * @v labnum soft label position to configure
+ * @v *label string to use as soft key label
+ * @v fmt justification format of label
+ * @ret rc return status code
+ */
+int slk_set ( int labnum, const char *label, int fmt ) {
+ if ( slks == NULL )
+ return ERR;
+ if ( (unsigned short)labnum >= slks->num_labels )
+ return ERR;
+ if ( (unsigned short)fmt >= 3 )
+ return ERR;
+
+ strncpy(slks->fkeys[labnum].label, label,
+ sizeof(slks->fkeys[labnum].label));
+ slks->fkeys[labnum].fmt = fmt;
+
+ return OK;
+}
diff --git a/gpxe/src/hci/mucurses/widgets/editbox.c b/gpxe/src/hci/mucurses/widgets/editbox.c
new file mode 100644
index 00000000..5c066fda
--- /dev/null
+++ b/gpxe/src/hci/mucurses/widgets/editbox.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <gpxe/editbox.h>
+
+/** @file
+ *
+ * Editable text box widget
+ *
+ */
+
+#define EDITBOX_MIN_CHARS 3
+
+/**
+ * Initialise text box widget
+ *
+ * @v box Editable text box widget
+ * @v buf Text buffer
+ * @v len Size of text buffer
+ * @v win Containing window
+ * @v row Row
+ * @v col Starting column
+ * @v width Width
+ *
+ */
+void init_editbox ( struct edit_box *box, char *buf, size_t len,
+ WINDOW *win, unsigned int row, unsigned int col,
+ unsigned int width ) {
+ memset ( box, 0, sizeof ( *box ) );
+ box->string.buf = buf;
+ box->string.len = len;
+ box->string.cursor = strlen ( buf );
+ box->win = ( win ? win : stdscr );
+ box->row = row;
+ box->col = col;
+ box->width = width;
+}
+
+/**
+ * Draw text box widget
+ *
+ * @v box Editable text box widget
+ *
+ */
+void draw_editbox ( struct edit_box *box ) {
+ size_t width = box->width;
+ char buf[ width + 1 ];
+ signed int cursor_offset, underflow, overflow, first;
+ size_t len;
+
+ /* Adjust starting offset so that cursor remains within box */
+ cursor_offset = ( box->string.cursor - box->first );
+ underflow = ( EDITBOX_MIN_CHARS - cursor_offset );
+ overflow = ( cursor_offset - ( width - 1 ) );
+ first = box->first;
+ if ( underflow > 0 ) {
+ first -= underflow;
+ if ( first < 0 )
+ first = 0;
+ } else if ( overflow > 0 ) {
+ first += overflow;
+ }
+ box->first = first;
+ cursor_offset = ( box->string.cursor - first );
+
+ /* Construct underscore-padded string portion */
+ memset ( buf, '_', width );
+ buf[width] = '\0';
+ len = ( strlen ( box->string.buf ) - first );
+ if ( len > width )
+ len = width;
+ memcpy ( buf, ( box->string.buf + first ), len );
+
+ /* Print box content and move cursor */
+ if ( ! box->win )
+ box->win = stdscr;
+ mvwprintw ( box->win, box->row, box->col, "%s", buf );
+ wmove ( box->win, box->row, ( box->col + cursor_offset ) );
+}
diff --git a/gpxe/src/hci/mucurses/winattrs.c b/gpxe/src/hci/mucurses/winattrs.c
new file mode 100644
index 00000000..15f97326
--- /dev/null
+++ b/gpxe/src/hci/mucurses/winattrs.c
@@ -0,0 +1,131 @@
+#include <curses.h>
+
+/** @file
+ *
+ * MuCurses window attribute functions
+ *
+ */
+
+/**
+ * Get the background rendition attributes for a window
+ *
+ * @v *win subject window
+ * @ret ch chtype rendition representation
+ */
+inline chtype getbkgd ( WINDOW *win ) {
+ return win->attrs;
+}
+
+/**
+ * Turn off attributes in a window
+ *
+ * @v win subject window
+ * @v attrs attributes to enable
+ * @ret rc return status code
+ */
+int wattroff ( WINDOW *win, int attrs ) {
+ win->attrs &= ~attrs;
+ return OK;
+}
+
+/**
+ * Turn on attributes in a window
+ *
+ * @v win subject window
+ * @v attrs attributes to enable
+ * @ret rc return status code
+ */
+int wattron ( WINDOW *win, int attrs ) {
+ win->attrs |= attrs;
+ return OK;
+}
+
+/**
+ * Set attributes in a window
+ *
+ * @v win subject window
+ * @v attrs attributes to enable
+ * @ret rc return status code
+ */
+int wattrset ( WINDOW *win, int attrs ) {
+ win->attrs = ( attrs | ( win->attrs & A_COLOR ) );
+ return OK;
+}
+
+/**
+ * Get attributes and colour pair information
+ *
+ * @v *win window to obtain information from
+ * @v *attrs address in which to store attributes
+ * @v *pair address in which to store colour pair
+ * @v *opts undefined (for future implementation)
+ * @ret rc return status cude
+ */
+int wattr_get ( WINDOW *win, attr_t *attrs, short *pair,
+ void *opts __unused ) {
+ *attrs = win->attrs & A_ATTRIBUTES;
+ *pair = PAIR_NUMBER ( win->attrs );
+ return OK;
+}
+
+/**
+ * Turn off attributes in a window
+ *
+ * @v *win subject window
+ * @v attrs attributes to toggle
+ * @v *opts undefined (for future implementation)
+ * @ret rc return status code
+ */
+int wattr_off ( WINDOW *win, attr_t attrs,
+ void *opts __unused ) {
+ wattroff( win, attrs );
+ return OK;
+}
+
+/**
+ * Turn on attributes in a window
+ *
+ * @v *win subject window
+ * @v attrs attributes to toggle
+ * @v *opts undefined (for future implementation)
+ * @ret rc return status code
+ */
+int wattr_on ( WINDOW *win, attr_t attrs,
+ void *opts __unused ) {
+ wattron( win, attrs );
+ return OK;
+}
+
+/**
+ * Set attributes and colour pair information in a window
+ *
+ * @v *win subject window
+ * @v attrs attributes to set
+ * @v cpair colour pair to set
+ * @v *opts undefined (for future implementation)
+ * @ret rc return status code
+ */
+int wattr_set ( WINDOW *win, attr_t attrs, short cpair,
+ void *opts __unused ) {
+ wattrset( win, attrs | COLOUR_PAIR ( cpair ) );
+ return OK;
+}
+
+/**
+ * Set colour pair for a window
+ *
+ * @v *win subject window
+ * @v colour_pair_number colour pair integer
+ * @v *opts undefined (for future implementation)
+ * @ret rc return status code
+ */
+int wcolour_set ( WINDOW *win, short colour_pair_number,
+ void *opts __unused ) {
+ if ( ( unsigned short )colour_pair_number > COLOUR_PAIRS )
+ return ERR;
+
+ win->attrs = ( ( win->attrs & A_ATTRIBUTES ) |
+ COLOUR_PAIR ( colour_pair_number ) );
+ return OK;
+}
+
diff --git a/gpxe/src/hci/mucurses/windows.c b/gpxe/src/hci/mucurses/windows.c
new file mode 100644
index 00000000..63d0af08
--- /dev/null
+++ b/gpxe/src/hci/mucurses/windows.c
@@ -0,0 +1,158 @@
+#include <curses.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include "mucurses.h"
+
+/** @file
+ *
+ * MuCurses windows instance functions
+ *
+ */
+
+/**
+ * Delete a window
+ *
+ * @v *win pointer to window being deleted
+ * @ret rc return status code
+ */
+int delwin ( WINDOW *win ) {
+ if ( win == NULL )
+ return ERR;
+
+ /* I think we should blank the region covered by the window -
+ ncurses doesn't do this, but they have a buffer, so they
+ may just be deleting from an offscreen context whereas we
+ are guaranteed to be deleting something onscreen */
+ wmove( win, 0, 0 );
+ chtype killch = (chtype)' ';
+ do {
+ _wputch( win, killch, WRAP );
+ } while ( win->curs_x + win->curs_y );
+
+ free( win );
+
+ wmove ( stdscr, 0, 0 );
+
+ return OK;
+}
+
+/**
+ * Create a new derived window
+ *
+ * @v parent parent window
+ * @v nlines window height
+ * @v ncols window width
+ * @v begin_y window y origin (relative to parent)
+ * @v begin_x window x origin (relative to parent)
+ * @ret ptr return pointer to child window
+ */
+WINDOW *derwin ( WINDOW *parent, int nlines, int ncols,
+ int begin_y, int begin_x ) {
+ WINDOW *child;
+ if ( parent == NULL )
+ return NULL;
+ if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL )
+ return NULL;
+ if ( ( (unsigned)ncols > parent->width ) ||
+ ( (unsigned)nlines > parent->height ) )
+ return NULL;
+ child->ori_y = parent->ori_y + begin_y;
+ child->ori_x = parent->ori_x + begin_x;
+ child->height = nlines;
+ child->width = ncols;
+ child->parent = parent;
+ child->scr = parent->scr;
+ return child;
+}
+
+/**
+ * Create a duplicate of the specified window
+ *
+ * @v orig original window
+ * @ret ptr pointer to duplicate window
+ */
+WINDOW *dupwin ( WINDOW *orig ) {
+ WINDOW *copy;
+ if ( orig == NULL )
+ return NULL;
+ if ( ( copy = malloc( sizeof( WINDOW ) ) ) == NULL )
+ return NULL;
+ copy->scr = orig->scr;
+ copy->attrs = orig->attrs;
+ copy->ori_y = orig->ori_y;
+ copy->ori_x = orig->ori_x;
+ copy->curs_y = orig->curs_y;
+ copy->curs_x = orig->curs_x;
+ copy->height = orig->height;
+ copy->width = orig->width;
+ return copy;
+}
+
+/**
+ * Move window origin to specified coordinates
+ *
+ * @v *win window to move
+ * @v y Y position
+ * @v x X position
+ * @ret rc return status code
+ */
+int mvwin ( WINDOW *win, int y, int x ) {
+ if ( win == NULL )
+ return ERR;
+ if ( ( ( (unsigned)y + win->height ) > LINES ) ||
+ ( ( (unsigned)x + win->width ) > COLS ) )
+ return ERR;
+
+ win->ori_y = y;
+ win->ori_x = x;
+
+ return OK;
+}
+
+/**
+ * Create new WINDOW
+ *
+ * @v nlines number of lines
+ * @v ncols number of columns
+ * @v begin_y column origin
+ * @v begin_x line origin
+ * @ret *win return pointer to new window
+ */
+WINDOW *newwin ( int nlines, int ncols, int begin_y, int begin_x ) {
+ WINDOW *win;
+ if ( ( win = malloc( sizeof(WINDOW) ) ) == NULL )
+ return NULL;
+ if ( ( (unsigned)( begin_y + nlines ) > stdscr->height ) &&
+ ( (unsigned)( begin_x + ncols ) > stdscr->width ) )
+ return NULL;
+ win->ori_y = begin_y;
+ win->ori_x = begin_x;
+ win->height = nlines;
+ win->width = ncols;
+ win->scr = stdscr->scr;
+ win->parent = stdscr;
+ return win;
+}
+
+/**
+ * Create a new sub-window
+ *
+ * @v orig parent window
+ * @v nlines window height
+ * @v ncols window width
+ * @v begin_y window y origin (absolute)
+ * @v begin_x window x origin (absolute)
+ * @ret ptr return pointer to child window
+ */
+WINDOW *subwin ( WINDOW *parent, int nlines, int ncols,
+ int begin_y, int begin_x ) {
+ WINDOW *child;
+ if ( parent == NULL )
+ return NULL;
+ if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL )
+ return NULL;
+ child = newwin( nlines, ncols, begin_y, begin_x );
+ child->parent = parent;
+ child->scr = parent->scr;
+ return child;
+}
diff --git a/gpxe/src/hci/mucurses/wininit.c b/gpxe/src/hci/mucurses/wininit.c
new file mode 100644
index 00000000..dfd0ca0a
--- /dev/null
+++ b/gpxe/src/hci/mucurses/wininit.c
@@ -0,0 +1,35 @@
+#include <stddef.h>
+#include <curses.h>
+
+/** @file
+ *
+ * MuCurses initialisation functions
+ *
+ */
+
+/**
+ * Initialise console environment
+ *
+ * @ret *win return pointer to stdscr
+ */
+WINDOW *initscr ( void ) {
+ /* determine console size */
+ /* initialise screen */
+ stdscr->scr->init( stdscr->scr );
+ stdscr->height = LINES;
+ stdscr->width = COLS;
+ erase();
+ return stdscr;
+}
+
+/**
+ * Finalise console environment
+ *
+ */
+int endwin ( void ) {
+ attrset ( 0 );
+ color_set ( 0, NULL );
+ erase();
+ stdscr->scr->exit( stdscr->scr );
+ return OK;
+}
diff --git a/gpxe/src/hci/readline.c b/gpxe/src/hci/readline.c
new file mode 100644
index 00000000..ff7a7679
--- /dev/null
+++ b/gpxe/src/hci/readline.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <console.h>
+#include <gpxe/keys.h>
+#include <gpxe/editstring.h>
+#include <readline/readline.h>
+
+/** @file
+ *
+ * Minimal readline
+ *
+ */
+
+#define READLINE_MAX 256
+
+static void sync_console ( struct edit_string *string ) __nonnull;
+
+/**
+ * Synchronise console with edited string
+ *
+ * @v string Editable string
+ */
+static void sync_console ( struct edit_string *string ) {
+ unsigned int mod_start = string->mod_start;
+ unsigned int mod_end = string->mod_end;
+ unsigned int cursor = string->last_cursor;
+ size_t len = strlen ( string->buf );
+
+ /* Expand region back to old cursor position if applicable */
+ if ( mod_start > string->last_cursor )
+ mod_start = string->last_cursor;
+
+ /* Expand region forward to new cursor position if applicable */
+ if ( mod_end < string->cursor )
+ mod_end = string->cursor;
+
+ /* Backspace to start of region */
+ while ( cursor > mod_start ) {
+ putchar ( '\b' );
+ cursor--;
+ }
+
+ /* Print modified region */
+ while ( cursor < mod_end ) {
+ putchar ( ( cursor >= len ) ? ' ' : string->buf[cursor] );
+ cursor++;
+ }
+
+ /* Backspace to new cursor position */
+ while ( cursor > string->cursor ) {
+ putchar ( '\b' );
+ cursor--;
+ }
+}
+
+/**
+ * Read line from console
+ *
+ * @v prompt Prompt string
+ * @ret line Line read from console (excluding terminating newline)
+ *
+ * The returned line is allocated with malloc(); the caller must
+ * eventually call free() to release the storage.
+ */
+char * readline ( const char *prompt ) {
+ char buf[READLINE_MAX];
+ struct edit_string string;
+ int key;
+ char *line;
+
+ if ( prompt )
+ printf ( "%s", prompt );
+
+ memset ( &string, 0, sizeof ( string ) );
+ string.buf = buf;
+ string.len = sizeof ( buf );
+ buf[0] = '\0';
+
+ while ( 1 ) {
+ key = edit_string ( &string, getkey() );
+ sync_console ( &string );
+ switch ( key ) {
+ case CR:
+ case LF:
+ putchar ( '\n' );
+ line = strdup ( buf );
+ if ( ! line )
+ printf ( "Out of memory\n" );
+ return line;
+ case CTRL_C:
+ putchar ( '\n' );
+ return NULL;
+ default:
+ /* Do nothing */
+ break;
+ }
+ }
+}
diff --git a/gpxe/src/hci/shell.c b/gpxe/src/hci/shell.c
new file mode 100644
index 00000000..18b1068e
--- /dev/null
+++ b/gpxe/src/hci/shell.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <readline/readline.h>
+#include <gpxe/command.h>
+#include <gpxe/shell.h>
+
+/** @file
+ *
+ * Minimal command shell
+ *
+ */
+
+static struct command commands[0]
+ __table_start ( struct command, commands );
+static struct command commands_end[0]
+ __table_end ( struct command, commands );
+
+/** The shell prompt string */
+static const char shell_prompt[] = "gPXE> ";
+
+/** Flag set in order to exit shell */
+static int exit_flag = 0;
+
+/** "exit" command body */
+static int exit_exec ( int argc, char **argv __unused ) {
+
+ if ( argc == 1 ) {
+ exit_flag = 1;
+ } else {
+ printf ( "Usage: exit\n"
+ "Exits the command shell\n" );
+ }
+
+ return 0;
+}
+
+/** "exit" command definition */
+struct command exit_command __command = {
+ .name = "exit",
+ .exec = exit_exec,
+};
+
+/** "help" command body */
+static int help_exec ( int argc __unused, char **argv __unused ) {
+ struct command *command;
+ unsigned int hpos = 0;
+
+ printf ( "\nAvailable commands:\n\n" );
+ for ( command = commands ; command < commands_end ; command++ ) {
+ hpos += printf ( " %s", command->name );
+ if ( hpos > ( 16 * 4 ) ) {
+ printf ( "\n" );
+ hpos = 0;
+ } else {
+ while ( hpos % 16 ) {
+ printf ( " " );
+ hpos++;
+ }
+ }
+ }
+ printf ( "\n\nType \"<command> --help\" for further information\n\n" );
+ return 0;
+}
+
+/** "help" command definition */
+struct command help_command __command = {
+ .name = "help",
+ .exec = help_exec,
+};
+
+/**
+ * Start command shell
+ *
+ */
+void shell ( void ) {
+ char *line;
+
+ exit_flag = 0;
+ while ( ! exit_flag ) {
+ line = readline ( shell_prompt );
+ if ( line ) {
+ system ( line );
+ free ( line );
+ }
+ }
+}
diff --git a/gpxe/src/hci/shell_banner.c b/gpxe/src/hci/shell_banner.c
new file mode 100644
index 00000000..62da487c
--- /dev/null
+++ b/gpxe/src/hci/shell_banner.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <console.h>
+#include <gpxe/features.h>
+#include <gpxe/timer.h>
+#include <gpxe/shell_banner.h>
+
+/** @file
+ *
+ * Shell startup banner
+ *
+ */
+
+#define BANNER_TIMEOUT ( 2 * TICKS_PER_SEC )
+
+#define NORMAL "\033[0m"
+#define BOLD "\033[1m"
+#define CYAN "\033[36m"
+
+static struct feature features[0] __table_start ( struct feature, features );
+static struct feature features_end[0] __table_end ( struct feature, features );
+
+/**
+ * Print shell banner and prompt for shell entry
+ *
+ * @ret enter_shell User wants to enter shell
+ */
+int shell_banner ( void ) {
+ unsigned long timeout = ( currticks() + BANNER_TIMEOUT );
+ struct feature *feature;
+ int key;
+ int enter_shell = 0;
+
+ /* Print welcome banner */
+ printf ( NORMAL "\n\n\n" BOLD "gPXE " VERSION
+ NORMAL " -- Open Source Boot Firmware -- "
+ CYAN "http://etherboot.org" NORMAL "\n"
+ "Features:" );
+ for ( feature = features ; feature < features_end ; feature++ ) {
+ printf ( " %s", feature->name );
+ }
+ printf ( "\nPress Ctrl-B for the gPXE command line..." );
+
+ /* Wait for key */
+ while ( currticks() < timeout ) {
+ if ( iskey() ) {
+ key = getchar();
+ if ( key == 0x02 /* Ctrl-B */ )
+ enter_shell = 1;
+ break;
+ }
+ }
+
+ /* Clear the "Press Ctrl-B" line */
+ printf ( "\r \r" );
+
+ return enter_shell;
+}
diff --git a/gpxe/src/hci/strerror.c b/gpxe/src/hci/strerror.c
new file mode 100644
index 00000000..4fc15d01
--- /dev/null
+++ b/gpxe/src/hci/strerror.c
@@ -0,0 +1,121 @@
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <gpxe/errortab.h>
+
+/** @file
+ *
+ * Error descriptions.
+ *
+ * The error numbers used by Etherboot are a superset of those defined
+ * by the PXE specification version 2.1. See errno.h for a listing of
+ * the error values.
+ *
+ * To save space in ROM images, error string tables are optional. Use
+ * the ERRORMSG_XXX options in config.h to select which error string
+ * tables you want to include. If an error string table is omitted,
+ * strerror() will simply return the text "Error 0x<errno>".
+ *
+ */
+
+static struct errortab errortab_start[0]
+ __table_start ( struct errortab, errortab );
+static struct errortab errortab_end[0]
+ __table_end ( struct errortab, errortab );
+
+/**
+ * Find error description
+ *
+ * @v errno Error number
+ * @v mask Mask of bits that we care about
+ * @ret errortab Error description, or NULL
+ */
+static struct errortab * find_error ( int errno, int mask ) {
+ struct errortab *errortab;
+
+ for ( errortab = errortab_start ; errortab < errortab_end ;
+ errortab++ ) {
+ if ( ( ( errortab->errno ^ errno ) & mask ) == 0 )
+ return errortab;
+ }
+
+ return NULL;
+}
+
+/**
+ * Find closest error description
+ *
+ * @v errno Error number
+ * @ret errortab Error description, or NULL
+ *
+ *
+ */
+static struct errortab * find_closest_error ( int errno ) {
+ struct errortab *errortab;
+
+ /* First, look for an exact match */
+ if ( ( errortab = find_error ( errno, 0x7fffffff ) ) != NULL )
+ return errortab;
+
+ /* Second, try masking off the gPXE-specific bit and seeing if
+ * we have an entry for the generic POSIX error message.
+ */
+ if ( ( errortab = find_error ( errno, 0x4f0000ff ) ) != NULL )
+ return errortab;
+
+ return NULL;
+}
+
+/**
+ * Retrieve string representation of error number.
+ *
+ * @v errno/rc Error number or return status code
+ * @ret strerror Pointer to error text
+ *
+ * If the error is not found in the linked-in error tables, generates
+ * a generic "Error 0x<errno>" message.
+ *
+ * The pointer returned by strerror() is valid only until the next
+ * call to strerror().
+ *
+ */
+const char * strerror ( int errno ) {
+ static char errbuf[64];
+ struct errortab *errortab;
+
+ /* Allow for strerror(rc) as well as strerror(errno) */
+ if ( errno < 0 )
+ errno = -errno;
+
+ /* Find the error description, if one exists */
+ errortab = find_closest_error ( errno );
+
+ /* Construct the error message */
+ if ( errortab ) {
+ snprintf ( errbuf, sizeof ( errbuf ), "%s (%#08x)",
+ errortab->text, errno );
+ } else {
+ snprintf ( errbuf, sizeof ( errbuf ), "Error %#08x", errno );
+ }
+
+ return errbuf;
+}
+
+/* Do not include ERRFILE portion in the numbers in the error table */
+#undef ERRFILE
+#define ERRFILE 0
+
+/** The most common errors */
+struct errortab common_errors[] __errortab = {
+ { 0, "No error" },
+ { ENOMEM, "Out of memory" },
+ { EINVAL, "Invalid argument" },
+ { ENOSPC, "No space left on device" },
+ { EIO, "Input/output error" },
+ { EACCES, "Permission denied" },
+ { ENOENT, "File not found" },
+ { ENETUNREACH, "Network unreachable" },
+ { ETIMEDOUT, "Connection timed out" },
+ { EPIPE, "Broken pipe" },
+ { ECANCELED, "Operation cancelled" },
+};
diff --git a/gpxe/src/hci/tui/settings_ui.c b/gpxe/src/hci/tui/settings_ui.c
new file mode 100644
index 00000000..0907bfd3
--- /dev/null
+++ b/gpxe/src/hci/tui/settings_ui.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <curses.h>
+#include <console.h>
+#include <gpxe/settings.h>
+#include <gpxe/editbox.h>
+#include <gpxe/keys.h>
+#include <gpxe/settings_ui.h>
+
+/** @file
+ *
+ * Option configuration console
+ *
+ */
+
+/* Colour pairs */
+#define CPAIR_NORMAL 1
+#define CPAIR_SELECT 2
+#define CPAIR_EDIT 3
+#define CPAIR_ALERT 4
+
+/* Screen layout */
+#define TITLE_ROW 1
+#define SETTINGS_LIST_ROW 3
+#define SETTINGS_LIST_COL 1
+#define INFO_ROW 20
+#define ALERT_ROW 20
+#define INSTRUCTION_ROW 22
+#define INSTRUCTION_PAD " "
+
+/** Layout of text within a setting widget */
+struct setting_row {
+ char start[0];
+ char pad1[1];
+ char name[15];
+ char pad2[1];
+ char value[60];
+ char pad3[1];
+ char nul;
+} __attribute__ (( packed ));
+
+/** A setting widget */
+struct setting_widget {
+ /** Settings block */
+ struct settings *settings;
+ /** Configuration setting */
+ struct setting *setting;
+ /** Screen row */
+ unsigned int row;
+ /** Screen column */
+ unsigned int col;
+ /** Edit box widget used for editing setting */
+ struct edit_box editbox;
+ /** Editing in progress flag */
+ int editing;
+ /** Buffer for setting's value */
+ char value[256]; /* enough size for a DHCP string */
+};
+
+/** Registered configuration settings */
+static struct setting all_settings[0]
+ __table_start ( struct setting, settings );
+static struct setting all_settings_end[0]
+ __table_end ( struct setting, settings );
+#define NUM_SETTINGS ( ( unsigned ) ( all_settings_end - all_settings ) )
+
+static void load_setting ( struct setting_widget *widget ) __nonnull;
+static int save_setting ( struct setting_widget *widget ) __nonnull;
+static void init_setting ( struct setting_widget *widget,
+ struct settings *settings,
+ struct setting *setting,
+ unsigned int row, unsigned int col ) __nonnull;
+static void draw_setting ( struct setting_widget *widget ) __nonnull;
+static int edit_setting ( struct setting_widget *widget, int key ) __nonnull;
+static void init_setting_index ( struct setting_widget *widget,
+ struct settings *settings,
+ unsigned int index ) __nonnull;
+static void vmsg ( unsigned int row, const char *fmt, va_list args ) __nonnull;
+static void msg ( unsigned int row, const char *fmt, ... ) __nonnull;
+static void valert ( const char *fmt, va_list args ) __nonnull;
+static void alert ( const char *fmt, ... ) __nonnull;
+static void draw_info_row ( struct setting *setting ) __nonnull;
+static int main_loop ( struct settings *settings ) __nonnull;
+
+/**
+ * Load setting widget value from configuration settings
+ *
+ * @v widget Setting widget
+ *
+ */
+static void load_setting ( struct setting_widget *widget ) {
+
+ /* Mark as not editing */
+ widget->editing = 0;
+
+ /* Read current setting value */
+ if ( fetchf_setting ( widget->settings, widget->setting,
+ widget->value, sizeof ( widget->value ) ) < 0 ) {
+ widget->value[0] = '\0';
+ }
+
+ /* Initialise edit box */
+ init_editbox ( &widget->editbox, widget->value,
+ sizeof ( widget->value ), NULL, widget->row,
+ ( widget->col + offsetof ( struct setting_row, value )),
+ sizeof ( ( ( struct setting_row * ) NULL )->value ) );
+}
+
+/**
+ * Save setting widget value back to configuration settings
+ *
+ * @v widget Setting widget
+ */
+static int save_setting ( struct setting_widget *widget ) {
+ return storef_setting ( widget->settings, widget->setting,
+ widget->value );
+}
+
+/**
+ * Initialise setting widget
+ *
+ * @v widget Setting widget
+ * @v settings Settings block
+ * @v setting Configuration setting
+ * @v row Screen row
+ * @v col Screen column
+ */
+static void init_setting ( struct setting_widget *widget,
+ struct settings *settings,
+ struct setting *setting,
+ unsigned int row, unsigned int col ) {
+
+ /* Initialise widget structure */
+ memset ( widget, 0, sizeof ( *widget ) );
+ widget->settings = settings;
+ widget->setting = setting;
+ widget->row = row;
+ widget->col = col;
+
+ /* Read current setting value */
+ load_setting ( widget );
+}
+
+/**
+ * Draw setting widget
+ *
+ * @v widget Setting widget
+ */
+static void draw_setting ( struct setting_widget *widget ) {
+ struct setting_row row;
+ unsigned int len;
+ unsigned int curs_col;
+ char *value;
+
+ /* Fill row with spaces */
+ memset ( &row, ' ', sizeof ( row ) );
+ row.nul = '\0';
+
+ /* Construct dot-padded name */
+ memset ( row.name, '.', sizeof ( row.name ) );
+ len = strlen ( widget->setting->name );
+ if ( len > sizeof ( row.name ) )
+ len = sizeof ( row.name );
+ memcpy ( row.name, widget->setting->name, len );
+
+ /* Construct space-padded value */
+ value = widget->value;
+ if ( ! *value )
+ value = "<not specified>";
+ len = strlen ( value );
+ if ( len > sizeof ( row.value ) )
+ len = sizeof ( row.value );
+ memcpy ( row.value, value, len );
+ curs_col = ( widget->col + offsetof ( typeof ( row ), value )
+ + len );
+
+ /* Print row */
+ mvprintw ( widget->row, widget->col, "%s", row.start );
+ move ( widget->row, curs_col );
+ if ( widget->editing )
+ draw_editbox ( &widget->editbox );
+}
+
+/**
+ * Edit setting widget
+ *
+ * @v widget Setting widget
+ * @v key Key pressed by user
+ * @ret key Key returned to application, or zero
+ */
+static int edit_setting ( struct setting_widget *widget, int key ) {
+ widget->editing = 1;
+ return edit_editbox ( &widget->editbox, key );
+}
+
+/**
+ * Initialise setting widget by index
+ *
+ * @v widget Setting widget
+ * @v settings Settings block
+ * @v index Index of setting with settings list
+ */
+static void init_setting_index ( struct setting_widget *widget,
+ struct settings *settings,
+ unsigned int index ) {
+ init_setting ( widget, settings, &all_settings[index],
+ ( SETTINGS_LIST_ROW + index ), SETTINGS_LIST_COL );
+}
+
+/**
+ * Print message centred on specified row
+ *
+ * @v row Row
+ * @v fmt printf() format string
+ * @v args printf() argument list
+ */
+static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
+ char buf[COLS];
+ size_t len;
+
+ len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
+ mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
+}
+
+/**
+ * Print message centred on specified row
+ *
+ * @v row Row
+ * @v fmt printf() format string
+ * @v .. printf() arguments
+ */
+static void msg ( unsigned int row, const char *fmt, ... ) {
+ va_list args;
+
+ va_start ( args, fmt );
+ vmsg ( row, fmt, args );
+ va_end ( args );
+}
+
+/**
+ * Clear message on specified row
+ *
+ * @v row Row
+ */
+static void clearmsg ( unsigned int row ) {
+ move ( row, 0 );
+ clrtoeol();
+}
+
+/**
+ * Print alert message
+ *
+ * @v fmt printf() format string
+ * @v args printf() argument list
+ */
+static void valert ( const char *fmt, va_list args ) {
+ clearmsg ( ALERT_ROW );
+ color_set ( CPAIR_ALERT, NULL );
+ vmsg ( ALERT_ROW, fmt, args );
+ sleep ( 2 );
+ color_set ( CPAIR_NORMAL, NULL );
+ clearmsg ( ALERT_ROW );
+}
+
+/**
+ * Print alert message
+ *
+ * @v fmt printf() format string
+ * @v ... printf() arguments
+ */
+static void alert ( const char *fmt, ... ) {
+ va_list args;
+
+ va_start ( args, fmt );
+ valert ( fmt, args );
+ va_end ( args );
+}
+
+/**
+ * Draw title row
+ */
+static void draw_title_row ( void ) {
+ attron ( A_BOLD );
+ msg ( TITLE_ROW, "gPXE option configuration console" );
+ attroff ( A_BOLD );
+}
+
+/**
+ * Draw information row
+ *
+ * @v setting Current configuration setting
+ */
+static void draw_info_row ( struct setting *setting ) {
+ clearmsg ( INFO_ROW );
+ attron ( A_BOLD );
+ msg ( INFO_ROW, "%s - %s", setting->name, setting->description );
+ attroff ( A_BOLD );
+}
+
+/**
+ * Draw instruction row
+ *
+ * @v editing Editing in progress flag
+ */
+static void draw_instruction_row ( int editing ) {
+ clearmsg ( INSTRUCTION_ROW );
+ if ( editing ) {
+ msg ( INSTRUCTION_ROW,
+ "Enter - accept changes" INSTRUCTION_PAD
+ "Ctrl-C - discard changes" );
+ } else {
+ msg ( INSTRUCTION_ROW,
+ "Ctrl-X - exit configuration utility" );
+ }
+}
+
+static int main_loop ( struct settings *settings ) {
+ struct setting_widget widget;
+ unsigned int current = 0;
+ unsigned int next;
+ int i;
+ int key;
+ int rc;
+
+ /* Print initial screen content */
+ draw_title_row();
+ color_set ( CPAIR_NORMAL, NULL );
+ for ( i = ( NUM_SETTINGS - 1 ) ; i >= 0 ; i-- ) {
+ init_setting_index ( &widget, settings, i );
+ draw_setting ( &widget );
+ }
+
+ while ( 1 ) {
+ /* Redraw information and instruction rows */
+ draw_info_row ( widget.setting );
+ draw_instruction_row ( widget.editing );
+
+ /* Redraw current setting */
+ color_set ( ( widget.editing ? CPAIR_EDIT : CPAIR_SELECT ),
+ NULL );
+ draw_setting ( &widget );
+ color_set ( CPAIR_NORMAL, NULL );
+
+ key = getkey();
+ if ( widget.editing ) {
+ key = edit_setting ( &widget, key );
+ switch ( key ) {
+ case CR:
+ case LF:
+ if ( ( rc = save_setting ( &widget ) ) != 0 ) {
+ alert ( " Could not set %s: %s ",
+ widget.setting->name,
+ strerror ( rc ) );
+ }
+ /* Fall through */
+ case CTRL_C:
+ load_setting ( &widget );
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+ } else {
+ next = current;
+ switch ( key ) {
+ case KEY_DOWN:
+ if ( next < ( NUM_SETTINGS - 1 ) )
+ next++;
+ break;
+ case KEY_UP:
+ if ( next > 0 )
+ next--;
+ break;
+ case CTRL_X:
+ return 0;
+ default:
+ edit_setting ( &widget, key );
+ break;
+ }
+ if ( next != current ) {
+ draw_setting ( &widget );
+ init_setting_index ( &widget, settings, next );
+ current = next;
+ }
+ }
+ }
+
+}
+
+int settings_ui ( struct settings *settings ) {
+ int rc;
+
+ initscr();
+ start_color();
+ init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE );
+ init_pair ( CPAIR_SELECT, COLOR_WHITE, COLOR_RED );
+ init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_CYAN );
+ init_pair ( CPAIR_ALERT, COLOR_WHITE, COLOR_RED );
+ color_set ( CPAIR_NORMAL, NULL );
+ erase();
+
+ rc = main_loop ( settings );
+
+ endwin();
+
+ return rc;
+}
diff --git a/gpxe/src/image/elf.c b/gpxe/src/image/elf.c
new file mode 100644
index 00000000..75c976ea
--- /dev/null
+++ b/gpxe/src/image/elf.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * ELF image format
+ *
+ */
+
+#include <errno.h>
+#include <elf.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/segment.h>
+#include <gpxe/image.h>
+#include <gpxe/elf.h>
+
+struct image_type elf_image_type __image_type ( PROBE_NORMAL );
+
+typedef Elf32_Ehdr Elf_Ehdr;
+typedef Elf32_Phdr Elf_Phdr;
+typedef Elf32_Off Elf_Off;
+
+/**
+ * Execute ELF image
+ *
+ * @v image ELF file
+ * @ret rc Return status code
+ */
+static int elf_exec ( struct image *image __unused ) {
+ return -ENOTSUP;
+}
+
+/**
+ * Load ELF segment into memory
+ *
+ * @v image ELF file
+ * @v phdr ELF program header
+ * @ret rc Return status code
+ */
+static int elf_load_segment ( struct image *image, Elf_Phdr *phdr ) {
+ physaddr_t dest;
+ userptr_t buffer;
+ int rc;
+
+ /* Do nothing for non-PT_LOAD segments */
+ if ( phdr->p_type != PT_LOAD )
+ return 0;
+
+ /* Check segment lies within image */
+ if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) {
+ DBG ( "ELF segment outside ELF file\n" );
+ return -ENOEXEC;
+ }
+
+ /* Find start address: use physical address for preference,
+ * fall back to virtual address if no physical address
+ * supplied.
+ */
+ dest = phdr->p_paddr;
+ if ( ! dest )
+ dest = phdr->p_vaddr;
+ if ( ! dest ) {
+ DBG ( "ELF segment loads to physical address 0\n" );
+ return -ENOEXEC;
+ }
+ buffer = phys_to_user ( dest );
+
+ DBG ( "ELF loading segment [%lx,%lx) to [%lx,%lx,%lx)\n",
+ phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ),
+ phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ),
+ ( phdr->p_paddr + phdr->p_memsz ) );
+
+ /* Verify and prepare segment */
+ if ( ( rc = prep_segment ( buffer, phdr->p_filesz,
+ phdr->p_memsz ) ) != 0 ) {
+ DBG ( "ELF could not prepare segment: %s\n", strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy image to segment */
+ memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz );
+
+ return 0;
+}
+
+/**
+ * Load ELF image into memory
+ *
+ * @v image ELF file
+ * @ret rc Return status code
+ */
+int elf_load ( struct image *image ) {
+ Elf_Ehdr ehdr;
+ Elf_Phdr phdr;
+ Elf_Off phoff;
+ unsigned int phnum;
+ int rc;
+
+ /* Read ELF header */
+ copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) );
+ if ( memcmp ( &ehdr.e_ident[EI_MAG0], ELFMAG, SELFMAG ) != 0 ) {
+ DBG ( "Invalid ELF signature\n" );
+ return -ENOEXEC;
+ }
+
+ /* This is an ELF image, valid or otherwise */
+ if ( ! image->type )
+ image->type = &elf_image_type;
+
+ /* Read ELF program headers */
+ for ( phoff = ehdr.e_phoff , phnum = ehdr.e_phnum ; phnum ;
+ phoff += ehdr.e_phentsize, phnum-- ) {
+ if ( phoff > image->len ) {
+ DBG ( "ELF program header %d outside ELF image\n",
+ phnum );
+ return -ENOEXEC;
+ }
+ copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) );
+ if ( ( rc = elf_load_segment ( image, &phdr ) ) != 0 )
+ return rc;
+ }
+
+ /* Record execution entry point in image private data field */
+ image->priv.phys = ehdr.e_entry;
+
+ return 0;
+}
+
+/** ELF image type */
+struct image_type elf_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "ELF",
+ .load = elf_load,
+ .exec = elf_exec,
+};
diff --git a/gpxe/src/image/embed.S b/gpxe/src/image/embed.S
new file mode 100644
index 00000000..4541bfdc
--- /dev/null
+++ b/gpxe/src/image/embed.S
@@ -0,0 +1,7 @@
+ .section ".data", "aw"
+ .balign 4
+ .globl _embedded_image_start
+_embedded_image_start:
+ .incbin EMBEDIMG
+ .globl _embedded_image_end
+_embedded_image_end:
diff --git a/gpxe/src/image/embedded.c b/gpxe/src/image/embedded.c
new file mode 100644
index 00000000..e2782a43
--- /dev/null
+++ b/gpxe/src/image/embedded.c
@@ -0,0 +1,49 @@
+/** @file
+ *
+ * Take a possible embedded image and put it in a struct image
+ * data structure.
+ */
+
+#include <stdio.h>
+#include <gpxe/image.h>
+#include <gpxe/malloc.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/umalloc.h>
+#include <gpxe/embedded.h>
+
+extern char _embedded_image_start[], _embedded_image_end[];
+
+struct image *embedded_image(void)
+{
+ static int reclaimed = 0;
+ struct image *image;
+ size_t eisize = _embedded_image_end - _embedded_image_start;
+
+ if ( !eisize )
+ return NULL; /* No embedded image */
+
+ if ( reclaimed )
+ return NULL; /* Already reclaimed */
+
+ printf("Embedded image: %d bytes at %p\n",
+ eisize, _embedded_image_start);
+
+ image = alloc_image();
+ if (!image)
+ return NULL;
+
+ image->len = eisize;
+ image->data = umalloc(eisize);
+ if (image->data == UNULL) {
+ image_put(image);
+ return image = NULL;
+ }
+ copy_to_user(image->data, 0, _embedded_image_start, eisize);
+
+ /* Reclaim embedded image memory */
+ reclaimed = 1;
+ mpopulate(_embedded_image_start, eisize);
+
+ return image;
+}
+
diff --git a/gpxe/src/image/initrd.c b/gpxe/src/image/initrd.c
new file mode 100644
index 00000000..f03564a8
--- /dev/null
+++ b/gpxe/src/image/initrd.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Linux initrd image format
+ *
+ * This file does nothing except provide a way to mark images as being
+ * initrds. The actual processing is done in the Linux kernel image
+ * code; this file exists so that we can include the "initrd" command
+ * without necessarily dragging in the Linux image format.
+ *
+ */
+
+#include <gpxe/image.h>
+#include <gpxe/initrd.h>
+
+/** Linux initrd image type */
+struct image_type initrd_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "initrd",
+};
diff --git a/gpxe/src/image/script.c b/gpxe/src/image/script.c
new file mode 100644
index 00000000..a24bc09a
--- /dev/null
+++ b/gpxe/src/image/script.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * gPXE scripts
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <gpxe/image.h>
+
+struct image_type script_image_type __image_type ( PROBE_NORMAL );
+
+/**
+ * Execute script
+ *
+ * @v image Script
+ * @ret rc Return status code
+ */
+static int script_exec ( struct image *image ) {
+ char cmdbuf[256];
+ size_t offset = 0;
+ size_t remaining;
+ size_t len;
+ char *eol;
+ int rc;
+
+ /* Temporarily de-register image, so that a "boot" command
+ * doesn't throw us into an execution loop. Hold a reference
+ * to avoid the image's being freed.
+ */
+ image_get ( image );
+ unregister_image ( image );
+
+ while ( offset < image->len ) {
+
+ /* Read up to cmdbuf bytes from script into buffer */
+ remaining = ( image->len - offset );
+ len = sizeof ( cmdbuf );
+ if ( len > remaining )
+ len = remaining;
+ memset ( cmdbuf, 0, sizeof ( cmdbuf ) );
+ copy_from_user ( cmdbuf, image->data, offset, len );
+
+ /* Find end of line */
+ eol = memchr ( cmdbuf, '\n', sizeof ( cmdbuf ) );
+ if ( ! eol )
+ eol = memchr ( cmdbuf, '\0', sizeof ( cmdbuf ) );
+ if ( ! eol ) {
+ DBG ( "Script line too long (max %zd bytes)\n",
+ sizeof ( cmdbuf ) );
+ rc = -ENOEXEC;
+ goto done;
+ }
+
+ /* Mark end of line and execute command */
+ *eol = '\0';
+ DBG ( "$ %s\n", cmdbuf );
+ if ( ( rc = system ( cmdbuf ) ) != 0 ) {
+ DBG ( "Command \"%s\" failed: %s\n",
+ cmdbuf, strerror ( rc ) );
+ goto done;
+ }
+
+ /* Move to next line */
+ offset += ( ( eol - cmdbuf ) + 1 );
+ }
+
+ rc = 0;
+ done:
+ /* Re-register image and return */
+ register_image ( image );
+ image_put ( image );
+ return rc;
+}
+
+/**
+ * Load script into memory
+ *
+ * @v image Script
+ * @ret rc Return status code
+ */
+static int script_load ( struct image *image ) {
+ static const char magic[] = "#!gpxe\n";
+ char test[ sizeof ( magic ) - 1 ];
+
+ /* Check for magic signature */
+ copy_from_user ( test, image->data, 0, sizeof ( test ) );
+ if ( memcmp ( test, magic, sizeof ( test ) ) != 0 ) {
+ DBG ( "Invalid magic signature\n" );
+ return -ENOEXEC;
+ }
+
+ /* This is a script */
+ image->type = &script_image_type;
+
+ /* We don't actually load it anywhere; we will pick the lines
+ * out of the image as we need them.
+ */
+
+ return 0;
+}
+
+/** Script image type */
+struct image_type script_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "script",
+ .load = script_load,
+ .exec = script_exec,
+};
diff --git a/gpxe/src/image/segment.c b/gpxe/src/image/segment.c
new file mode 100644
index 00000000..8deaef79
--- /dev/null
+++ b/gpxe/src/image/segment.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Executable image segments
+ *
+ */
+
+#include <errno.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/memmap.h>
+#include <gpxe/segment.h>
+
+/**
+ * Prepare segment for loading
+ *
+ * @v segment Segment start
+ * @v filesz Size of the "allocated bytes" portion of the segment
+ * @v memsz Size of the segment
+ * @ret rc Return status code
+ */
+int prep_segment ( userptr_t segment, size_t filesz, size_t memsz ) {
+ struct memory_map memmap;
+ physaddr_t start = user_to_phys ( segment, 0 );
+ physaddr_t mid = user_to_phys ( segment, filesz );
+ physaddr_t end = user_to_phys ( segment, memsz );
+ unsigned int i;
+
+ /* Sanity check */
+ if ( filesz > memsz ) {
+ DBG ( "Insane segment [%lx,%lx,%lx)\n", start, mid, end );
+ return -EINVAL;
+ }
+
+ /* Get a fresh memory map. This allows us to automatically
+ * avoid treading on any regions that Etherboot is currently
+ * editing out of the memory map.
+ */
+ get_memmap ( &memmap );
+
+ /* Look for a suitable memory region */
+ for ( i = 0 ; i < memmap.count ; i++ ) {
+ if ( ( start >= memmap.regions[i].start ) &&
+ ( end <= memmap.regions[i].end ) ) {
+ /* Found valid region: zero bss and return */
+ memset_user ( segment, filesz, 0, ( memsz - filesz ) );
+ return 0;
+ }
+ }
+
+ /* No suitable memory region found */
+ DBG ( "Segment [%lx,%lx,%lx) does not fit into available memory\n",
+ start, mid, end );
+ return -ERANGE;
+}
diff --git a/gpxe/src/include/.gitignore b/gpxe/src/include/.gitignore
new file mode 100644
index 00000000..de1598ef
--- /dev/null
+++ b/gpxe/src/include/.gitignore
@@ -0,0 +1 @@
+.buildserial.h
diff --git a/gpxe/src/include/alloca.h b/gpxe/src/include/alloca.h
new file mode 100644
index 00000000..08398fb3
--- /dev/null
+++ b/gpxe/src/include/alloca.h
@@ -0,0 +1,25 @@
+#ifndef _ALLOCA_H
+#define _ALLOCA_H
+
+/**
+ * @file
+ *
+ * Temporary memory allocation
+ *
+ */
+
+#include <stdint.h>
+
+/**
+ * Allocate temporary memory from the stack
+ *
+ * @v size Size to allocate
+ * @ret ptr Allocated memory
+ *
+ * This memory will be freed automatically when the containing
+ * function returns. There are several caveats regarding use of
+ * alloca(); use it only if you already know what they are.
+ */
+#define alloca(size) __builtin_alloca ( size )
+
+#endif /* _ALLOCA_H */
diff --git a/gpxe/src/include/assert.h b/gpxe/src/include/assert.h
new file mode 100644
index 00000000..93750a1e
--- /dev/null
+++ b/gpxe/src/include/assert.h
@@ -0,0 +1,65 @@
+#ifndef _ASSERT_H
+#define _ASSERT_H
+
+/** @file
+ *
+ * Assertions
+ *
+ * This file provides two assertion macros: assert() (for run-time
+ * assertions) and linker_assert() (for link-time assertions).
+ *
+ */
+
+#ifdef NDEBUG
+#define ASSERTING 0
+#else
+#define ASSERTING 1
+#endif
+
+/** printf() for assertions
+ *
+ * This function exists so that the assert() macro can expand to
+ * printf() calls without dragging the printf() prototype into scope.
+ *
+ * As far as the compiler is concerned, assert_printf() and printf() are
+ * completely unrelated calls; it's only at the assembly stage that
+ * references to the assert_printf symbol are collapsed into references
+ * to the printf symbol.
+ */
+extern int __attribute__ (( format ( printf, 1, 2 ) ))
+assert_printf ( const char *fmt, ... ) asm ( "printf" );
+
+/**
+ * Assert a condition at run-time.
+ *
+ * If the condition is not true, a debug message will be printed.
+ * Assertions only take effect in debug-enabled builds (see DBG()).
+ *
+ * @todo Make an assertion failure abort the program
+ *
+ */
+#define assert( condition ) \
+ do { \
+ if ( ASSERTING && ! (condition) ) { \
+ assert_printf ( "assert(%s) failed at %s line %d\n", \
+ #condition, __FILE__, __LINE__ ); \
+ } \
+ } while ( 0 )
+
+/**
+ * Assert a condition at link-time.
+ *
+ * If the condition is not true, the link will fail with an unresolved
+ * symbol (error_symbol).
+ *
+ * This macro is gPXE-specific. Do not use this macro in code
+ * intended to be portable.
+ *
+ */
+#define linker_assert( condition, error_symbol ) \
+ if ( ! (condition) ) { \
+ extern void error_symbol ( void ); \
+ error_symbol(); \
+ }
+
+#endif /* _ASSERT_H */
diff --git a/gpxe/src/include/big_bswap.h b/gpxe/src/include/big_bswap.h
new file mode 100644
index 00000000..3775fac1
--- /dev/null
+++ b/gpxe/src/include/big_bswap.h
@@ -0,0 +1,33 @@
+#ifndef ETHERBOOT_BIG_BSWAP_H
+#define ETHERBOOT_BIG_BSWAP_H
+
+#define ntohl(x) (x)
+#define htonl(x) (x)
+#define ntohs(x) (x)
+#define htons(x) (x)
+#define cpu_to_le64(x) __bswap_64(x)
+#define cpu_to_le32(x) __bswap_32(x)
+#define cpu_to_le16(x) __bswap_16(x)
+#define cpu_to_be64(x) (x)
+#define cpu_to_be32(x) (x)
+#define cpu_to_be16(x) (x)
+#define le64_to_cpu(x) __bswap_64(x)
+#define le32_to_cpu(x) __bswap_32(x)
+#define le16_to_cpu(x) __bswap_16(x)
+#define be64_to_cpu(x) (x)
+#define be32_to_cpu(x) (x)
+#define be16_to_cpu(x) (x)
+#define cpu_to_le64s(x) __bswap_64s(x)
+#define cpu_to_le32s(x) __bswap_32s(x)
+#define cpu_to_le16s(x) __bswap_16s(x)
+#define cpu_to_be64s(x) do {} while (0)
+#define cpu_to_be32s(x) do {} while (0)
+#define cpu_to_be16s(x) do {} while (0)
+#define le64_to_cpus(x) __bswap_64s(x)
+#define le32_to_cpus(x) __bswap_32s(x)
+#define le16_to_cpus(x) __bswap_16s(x)
+#define be64_to_cpus(x) do {} while (0)
+#define be32_to_cpus(x) do {} while (0)
+#define be16_to_cpus(x) do {} while (0)
+
+#endif /* ETHERBOOT_BIG_BSWAP_H */
diff --git a/gpxe/src/include/bootp.h b/gpxe/src/include/bootp.h
new file mode 100644
index 00000000..0e65477a
--- /dev/null
+++ b/gpxe/src/include/bootp.h
@@ -0,0 +1,230 @@
+#ifndef _BOOTP_H
+#define _BOOTP_H
+
+#ifdef ALTERNATE_DHCP_PORTS_1067_1068
+#undef NON_STANDARD_BOOTP_SERVER
+#define NON_STANDARD_BOOTP_SERVER 1067
+#undef NON_STANDARD_BOOTP_CLIENT
+#define NON_STANDARD_BOOTP_CLIENT 1068
+#endif
+
+#ifdef NON_STANDARD_BOOTP_SERVER
+#define BOOTP_SERVER NON_STANDARD_BOOTP_SERVER
+#else
+#define BOOTP_SERVER 67
+#endif
+#ifdef NON_STANDARD_BOOTP_CLIENT
+#define BOOTP_CLIENT NON_STANDARD_BOOTP_CLIENT
+#else
+#define BOOTP_CLIENT 68
+#endif
+#define PROXYDHCP_SERVER 4011 /* For PXE */
+
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+#define TAG_LEN(p) (*((p)+1))
+#define RFC1533_COOKIE 99, 130, 83, 99
+#define RFC1533_PAD 0
+#define RFC1533_NETMASK 1
+#define RFC1533_TIMEOFFSET 2
+#define RFC1533_GATEWAY 3
+#define RFC1533_TIMESERVER 4
+#define RFC1533_IEN116NS 5
+#define RFC1533_DNS 6
+#define RFC1533_LOGSERVER 7
+#define RFC1533_COOKIESERVER 8
+#define RFC1533_LPRSERVER 9
+#define RFC1533_IMPRESSSERVER 10
+#define RFC1533_RESOURCESERVER 11
+#define RFC1533_HOSTNAME 12
+#define RFC1533_BOOTFILESIZE 13
+#define RFC1533_MERITDUMPFILE 14
+#define RFC1533_DOMAINNAME 15
+#define RFC1533_SWAPSERVER 16
+#define RFC1533_ROOTPATH 17
+#define RFC1533_EXTENSIONPATH 18
+#define RFC1533_IPFORWARDING 19
+#define RFC1533_IPSOURCEROUTING 20
+#define RFC1533_IPPOLICYFILTER 21
+#define RFC1533_IPMAXREASSEMBLY 22
+#define RFC1533_IPTTL 23
+#define RFC1533_IPMTU 24
+#define RFC1533_IPMTUPLATEAU 25
+#define RFC1533_INTMTU 26
+#define RFC1533_INTLOCALSUBNETS 27
+#define RFC1533_INTBROADCAST 28
+#define RFC1533_INTICMPDISCOVER 29
+#define RFC1533_INTICMPRESPOND 30
+#define RFC1533_INTROUTEDISCOVER 31
+#define RFC1533_INTROUTESOLICIT 32
+#define RFC1533_INTSTATICROUTES 33
+#define RFC1533_LLTRAILERENCAP 34
+#define RFC1533_LLARPCACHETMO 35
+#define RFC1533_LLETHERNETENCAP 36
+#define RFC1533_TCPTTL 37
+#define RFC1533_TCPKEEPALIVETMO 38
+#define RFC1533_TCPKEEPALIVEGB 39
+#define RFC1533_NISDOMAIN 40
+#define RFC1533_NISSERVER 41
+#define RFC1533_NTPSERVER 42
+#define RFC1533_VENDOR 43
+#define RFC1533_NBNS 44
+#define RFC1533_NBDD 45
+#define RFC1533_NBNT 46
+#define RFC1533_NBSCOPE 47
+#define RFC1533_XFS 48
+#define RFC1533_XDM 49
+#ifndef NO_DHCP_SUPPORT
+#define RFC2132_REQ_ADDR 50
+#define RFC2132_MSG_TYPE 53
+#define RFC2132_SRV_ID 54
+#define RFC2132_PARAM_LIST 55
+#define RFC2132_MAX_SIZE 57
+#define RFC2132_VENDOR_CLASS_ID 60
+#define RFC2132_CLIENT_ID 61
+#define RFC2132_TFTP_SERVER_NAME 66
+#define RFC2132_BOOTFILE_NAME 67
+#define RFC3004_USER_CLASS 77
+
+#ifdef PXE_DHCP_STRICT
+/*
+ * The following options are acknowledged in RFC3679 because they are
+ * widely used by PXE implementations, but have never been properly
+ * allocated. Despite other PXE options being correctly packed in a
+ * vendor encapsulated field, these are exposed. Sigh. Note that the
+ * client UUID (option 97) is also noted in the PXE spec as using
+ * option 61.
+ */
+#define RFC3679_PXE_CLIENT_ARCH 93
+#define RFC3679_PXE_CLIENT_NDI 94
+#define RFC3679_PXE_CLIENT_UUID 97
+
+/* The lengths are fixed. */
+#define RFC3679_PXE_CLIENT_ARCH_LENGTH 2
+#define RFC3679_PXE_CLIENT_NDI_LENGTH 3
+#define RFC3679_PXE_CLIENT_UUID_LENGTH 17
+
+/*
+ * Values of RFC3679_PXE_CLIENT_ARCH can apparently be one of the
+ * following, according to the PXE spec. The spec only actually
+ * described the 2nd octet, not the first. Duh... assume 0.
+ */
+#define RFC3679_PXE_CLIENT_ARCH_IAX86PC 0,0
+#define RFC3679_PXE_CLIENT_ARCH_NECPC98 0,1
+#define RFC3679_PXE_CLIENT_ARCH_IA64PC 0,2
+#define RFC3679_PXE_CLIENT_ARCH_DECALPHA 0,3
+#define RFC3679_PXE_CLIENT_ARCH_ARCX86 0,4
+#define RFC3679_PXE_CLIENT_ARCH_INTELLEAN 0,5
+
+/*
+ * Only one valid value of NDI type (must be 1) and UNDI version (must
+ * be 2.1)
+ */
+#define RFC3679_PXE_CLIENT_NDI_21 1,2,1
+
+/*
+ * UUID - type must be 1 and then 16 octets of UID, as with the client ID.
+ * The value is a default for testing only
+ */
+#define RFC3679_PXE_CLIENT_UUID_TYPE 0
+#warning "UUID is a default for testing ONLY!"
+#define RFC3679_PXE_CLIENT_UUID_DEFAULT \
+ RFC3679_PXE_CLIENT_UUID_TYPE, \
+ 0xDE,0xAD,0xBE,0xEF, \
+ 0xDE,0xAD,0xBE,0xEF, \
+ 0xDE,0xAD,0xBE,0xEF, \
+ 0xDE,0xAD,0xBE,0xEF
+/*
+ * The Vendor Class ID. Note that the Arch and UNDI version numbers
+ * are fixed and must be same as the ARCH and NDI above.
+ */
+#define RFC2132_VENDOR_CLASS_ID_PXE_LENGTH 32
+#define RFC2132_VENDOR_CLASS_ID_PXE \
+ 'P','X','E','C','l','i','e','n','t',':', \
+ 'A','r','c','h',':','0','0','0','0','0',':', \
+ 'U','N','D','I',':','0','0','2','0','0','1'
+
+/*
+ * The following vendor options are required in the PXE spec to pull
+ * options for the *next* image. The PXE spec doesn't help us with
+ * this (like explaining why).
+ */
+#define RFC1533_VENDOR_PXE_OPT128 128
+#define RFC1533_VENDOR_PXE_OPT129 129
+#define RFC1533_VENDOR_PXE_OPT130 130
+#define RFC1533_VENDOR_PXE_OPT131 131
+#define RFC1533_VENDOR_PXE_OPT132 132
+#define RFC1533_VENDOR_PXE_OPT133 133
+#define RFC1533_VENDOR_PXE_OPT134 134
+#define RFC1533_VENDOR_PXE_OPT135 135
+
+#endif /* PXE_DHCP_STRICT */
+
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPACK 5
+#endif /* NO_DHCP_SUPPORT */
+
+#define RFC1533_VENDOR_MAJOR 0
+#define RFC1533_VENDOR_MINOR 0
+
+#define RFC1533_VENDOR_MAGIC 128
+#define RFC1533_VENDOR_ADDPARM 129
+#define RFC1533_VENDOR_ETHDEV 130
+/* We should really apply for an official Etherboot encap option */
+#define RFC1533_VENDOR_ETHERBOOT_ENCAP 150
+/* I'll leave it to FREEBSD to decide if they want to renumber */
+#ifdef IMAGE_FREEBSD
+#define RFC1533_VENDOR_HOWTO 132
+#define RFC1533_VENDOR_KERNEL_ENV 133
+#endif
+#define RFC1533_VENDOR_NIC_DEV_ID 175
+#define RFC1533_VENDOR_ARCH 177
+
+#define RFC1533_END 255
+
+#define BOOTP_VENDOR_LEN 64
+#ifndef NO_DHCP_SUPPORT
+#define DHCP_OPT_LEN 312
+#endif /* NO_DHCP_SUPPORT */
+
+/* Format of a bootp packet */
+struct bootp_t {
+ uint8_t bp_op;
+ uint8_t bp_htype;
+ uint8_t bp_hlen;
+ uint8_t bp_hops;
+ uint32_t bp_xid;
+ uint16_t bp_secs;
+ uint16_t unused;
+ in_addr bp_ciaddr;
+ in_addr bp_yiaddr;
+ in_addr bp_siaddr;
+ in_addr bp_giaddr;
+ uint8_t bp_hwaddr[16];
+ uint8_t bp_sname[64];
+ char bp_file[128];
+#ifdef NO_DHCP_SUPPORT
+ uint8_t bp_vend[BOOTP_VENDOR_LEN];
+#else
+ uint8_t bp_vend[DHCP_OPT_LEN];
+#endif /* NO_DHCP_SUPPORT */
+};
+
+/* Format of a bootp IP packet */
+struct bootpip_t
+{
+ struct iphdr ip;
+ struct udphdr udp;
+ struct bootp_t bp;
+};
+
+/* Format of bootp packet with extensions */
+struct bootpd_t {
+ struct bootp_t bootp_reply;
+ uint8_t bootp_extension[MAX_BOOTP_EXTLEN];
+};
+
+#endif /* _BOOTP_H */
diff --git a/gpxe/src/include/btext.h b/gpxe/src/include/btext.h
new file mode 100644
index 00000000..1d3f9e59
--- /dev/null
+++ b/gpxe/src/include/btext.h
@@ -0,0 +1,62 @@
+/*
+ * This file describes the structure passed from the BootX application
+ * (for MacOS) when it is used to boot Linux.
+ *
+ * Written by Benjamin Herrenschmidt.
+ *
+ * Move to LinuxBIOS by LYH yhlu@tyan.com
+ *
+ */
+
+
+#ifndef _BTEXT_H__
+#define _BTEXT_H__
+
+#if 1
+#define u32 unsigned int
+#define u16 unsigned short
+#define u8 unsigned char
+#endif
+
+/* Here are the boot informations that are passed to the bootstrap
+ * Note that the kernel arguments and the device tree are appended
+ * at the end of this structure. */
+typedef struct boot_infos
+{
+
+ /* NEW (vers. 2) this holds the current _logical_ base addr of
+ the frame buffer (for use by early boot message) */
+ u8* logicalDisplayBase;
+
+
+ /* Some infos about the current MacOS display */
+ u32 dispDeviceRect[4]; /* left,top,right,bottom */
+ u32 dispDeviceDepth; /* (8, 16 or 32) */
+ u32 dispDeviceBase; /* base address (physical) */
+ u32 dispDeviceRowBytes; /* rowbytes (in bytes) */
+ u32 dispDeviceColorsOffset; /* Colormap (8 bits only) or 0 (*) */
+
+
+ /* The framebuffer size (optional, currently 0) */
+ u32 frameBufferSize; /* Represents a max size, can be 0. */
+
+
+} boot_infos_t;
+
+/* (*) The format of the colormap is 256 * 3 * 2 bytes. Each color index is represented
+ * by 3 short words containing a 16 bits (unsigned) color component.
+ * Later versions may contain the gamma table for direct-color devices here.
+ */
+#define BOOTX_COLORTABLE_SIZE (256UL*3UL*2UL)
+
+
+/*
+ * Definitions for using the procedures in btext.c.
+ *
+ * Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ */
+
+extern boot_infos_t disp_bi;
+extern u32 boot_text_mapped;
+
+#endif /* _BTEXT_H */
diff --git a/gpxe/src/include/byteswap.h b/gpxe/src/include/byteswap.h
new file mode 100644
index 00000000..72b5a01d
--- /dev/null
+++ b/gpxe/src/include/byteswap.h
@@ -0,0 +1,22 @@
+#ifndef ETHERBOOT_BYTESWAP_H
+#define ETHERBOOT_BYTESWAP_H
+
+#include "endian.h"
+#include "bits/byteswap.h"
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#include "little_bswap.h"
+#endif
+#if __BYTE_ORDER == __BIG_ENDIAN
+#include "big_bswap.h"
+#endif
+
+/* Make routines available to all */
+#define swap64(x) __bswap_64(x)
+#define swap32(x) __bswap_32(x)
+#define swap16(x) __bswap_16(x)
+#define bswap_64(x) __bswap_64(x)
+#define bswap_32(x) __bswap_32(x)
+#define bswap_16(x) __bswap_16(x)
+
+#endif /* ETHERBOOT_BYTESWAP_H */
diff --git a/gpxe/src/include/cmdline.h b/gpxe/src/include/cmdline.h
new file mode 100644
index 00000000..9ab29c53
--- /dev/null
+++ b/gpxe/src/include/cmdline.h
@@ -0,0 +1,8 @@
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+/* Command line external functions */
+
+void cmdl_start();
+
+#endif
diff --git a/gpxe/src/include/cmdlinelib.h b/gpxe/src/include/cmdlinelib.h
new file mode 100644
index 00000000..1eb68994
--- /dev/null
+++ b/gpxe/src/include/cmdlinelib.h
@@ -0,0 +1,99 @@
+/* Command line library */
+#ifndef CMDLINELIB_H
+#define CMDLINELIB_H
+
+#define CMDL_BUFFER_SIZE 256
+//#define CMDL_OUTPUT_SIZE 256
+#define CMDL_PROMPT_SIZE 8
+#define CMDL_MAX_TAB_COMPLETE_RESULT 256
+
+typedef int (*cmdl_putchar_t)(int);
+typedef int (*cmdl_printf_t)( const char *format, ... );
+typedef int (*cmdl_getchar_t)();
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+enum{
+ CMDL_LEFT,
+ CMDL_RIGHT
+};
+
+enum{
+ CMDLK_FW=6,
+ CMDLK_BW=2,
+ CMDLK_BS=8,
+ CMDLK_HOME=2,
+ CMDLK_END=5,
+ CMDLK_DELTOEND=11,
+ CMDLK_DELARG=23,
+ CMDLK_ENTER=0x0d,
+ CMDLK_RETURN=0x0a,
+ CMDLK_TAB=9
+};
+
+typedef struct{
+
+ // buffers
+
+ //char* output;
+ char* buffer;
+ char* prompt;
+
+ // options and values
+
+ int cursor;
+ //int has_output;
+ int exit;
+ int refresh;
+ int tabstate;
+ int insert;
+
+ // callbacks
+
+ cmdl_putchar_t putchar;
+ cmdl_getchar_t getchar;
+ cmdl_printf_t printf;
+
+}cmd_line;
+
+typedef struct{
+ int argc;
+ char **argv;
+}cmdl_param_list;
+
+void cmdl_setputchar(cmd_line* cmd, cmdl_putchar_t in);
+void cmdl_setgetchar(cmd_line* cmd, cmdl_getchar_t in);
+void cmdl_setprintf(cmd_line* cmd, cmdl_printf_t in);
+
+//void cmdl_builtin_help(cmd_line* cmd, char* command);
+
+void cmdl_parsechar(cmd_line* cmd, char in);
+
+void cmdl_addreplace(cmd_line* cmd, char in);
+void cmdl_addinsert(cmd_line* cmd, char in);
+void cmdl_enterloop(cmd_line* cmd);
+void cmdl_exec(cmd_line* cmd);
+void cmdl_setexit(cmd_line* cmd, int exit);
+int cmdl_getexit(cmd_line* cmd);
+void cmdl_clearoutput(cmd_line* cmd);
+void cmdl_clearbuffer(cmd_line* cmd);
+int cmdl_printf(cmd_line* cmd, const char *format, ...);
+char* cmdl_getoutput(cmd_line* cmd);
+//void cmdl_addoutput_str(cmd_line* cmd, char output[CMDL_OUTPUT_SIZE]);
+void cmdl_addstr(cmd_line* cmd, char* str);
+int cmdl_movecursor(cmd_line* cmd, int direction);
+char* cmdl_getbuffer(cmd_line* cmd);
+void cmdl_addchar(cmd_line* cmd, char in);
+int cmdl_check(cmd_line* cmd);
+void cmdl_del(cmd_line* cmd);
+cmd_line* cmdl_create();
+void cmdl_free(cmd_line* cmd);
+char *cmdl_getprompt(cmd_line* cmd);
+void cmdl_setpropmt(cmd_line* cmd, char prompt[CMDL_PROMPT_SIZE]);
+cmdl_param_list* cmdl_getparams(const char* command);
+void cmdl_tabcomplete(cmd_line *cmd);
+
+#endif
+
diff --git a/gpxe/src/include/cmdlist.h b/gpxe/src/include/cmdlist.h
new file mode 100644
index 00000000..33cd349b
--- /dev/null
+++ b/gpxe/src/include/cmdlist.h
@@ -0,0 +1,18 @@
+#ifndef COMMANDLIST_H
+#define COMMANDLIST_H
+
+void test_req();
+void test2_req();
+void help_req();
+void nvo_cmd_req();
+
+void commandlist()
+{
+ // test_req();
+ // test2_req();
+ help_req();
+ nvo_cmd_req();
+}
+
+#endif
+
diff --git a/gpxe/src/include/coff.h b/gpxe/src/include/coff.h
new file mode 100644
index 00000000..a73fda52
--- /dev/null
+++ b/gpxe/src/include/coff.h
@@ -0,0 +1,73 @@
+#ifndef COFF_H
+#define COFF_H
+/* Based on the elf.h file
+ * Changed accordingly to support COFF file support
+ */
+
+
+/* Values for f_flags. */
+#define F_RELFLG 0x0001 /* If set, not reloc. info. Clear for executables */
+#define F_EXEC 0x0002 /* No unresolved symbols. Executable file ! */
+#define F_LNNO 0x0004 /* If set, line information numbers removed */
+#define F_LSYMS 0x0008 /* If set, local symbols removed */
+#define F_AR32WR 0x0100 /* Indicates little endian file */
+
+/* Values for e_machine (architecute). */
+#define EM_E1 0x17a /* Magic number for Hyperstone. Big endian format */
+
+/* Values for f_flags. */
+#define O_MAGIC 0x017c /* Optional's header magic number for Hyperstone */
+
+/* Values for s_flags. */
+#define S_TYPE_TEXT 0x0020 /* If set, the section contains only executable */
+#define S_TYPE_DATA 0x0040 /* If set, the section contains only initialized data */
+#define S_TYPE_BSS 0x0080 /* If set, the section is BSS no data stored */
+
+
+typedef struct
+{
+ unsigned short f_magic; /* magic number */
+ unsigned short f_nscns; /* number of sections */
+ unsigned long f_timdat; /* time & date stamp */
+ unsigned long f_symptr; /* file pointer to symtab */
+ unsigned long f_nsyms; /* number of symtab entries */
+ unsigned short f_opthdr; /* sizeof(optional hdr) */
+ unsigned short f_flags; /* flags */
+}
+COFF_filehdr;
+
+/*
+ * Optional header.
+ */
+typedef struct
+{
+ unsigned short magic; /* type of file */
+ unsigned short vstamp; /* version stamp */
+ unsigned long tsize; /* text size in bytes, padded to FW bdry*/
+ unsigned long dsize; /* initialized data " " */
+ unsigned long bsize; /* uninitialized data " " */
+ unsigned long entry; /* entry pt. */
+ unsigned long text_start; /* base of text used for this file */
+ unsigned long data_start; /* base of data used for this file */
+}
+COFF_opthdr;
+
+/*
+ * Section header.
+ */
+typedef struct
+{
+ char s_name[8]; /* section name */
+ unsigned long s_paddr; /* physical address, aliased s_nlib */
+ unsigned long s_vaddr; /* virtual address */
+ unsigned long s_size; /* section size */
+ unsigned long s_scnptr; /* file ptr to raw data for section */
+ unsigned long s_relptr; /* file ptr to relocation */
+ unsigned long s_lnnoptr; /* file ptr to line numbers */
+ unsigned short s_nreloc; /* number of relocation entries */
+ unsigned short s_nlnno; /* number of line number entries*/
+ unsigned long s_flags; /* flags */
+}
+COFF_scnhdr;
+
+#endif /* COFF_H */
diff --git a/gpxe/src/include/compiler.h b/gpxe/src/include/compiler.h
new file mode 100644
index 00000000..1d4312a6
--- /dev/null
+++ b/gpxe/src/include/compiler.h
@@ -0,0 +1,357 @@
+#ifndef COMPILER_H
+#define COMPILER_H
+
+/*
+ * Doxygen can't cope with some of the more esoteric areas of C, so we
+ * make its life simpler.
+ *
+ */
+#ifdef DOXYGEN
+#define __attribute__(x)
+#endif
+
+/** @file
+ *
+ * Global compiler definitions.
+ *
+ * This file is implicitly included by every @c .c file in Etherboot.
+ * It defines global macros such as DBG().
+ *
+ * We arrange for each object to export the symbol @c obj_OBJECT
+ * (where @c OBJECT is the object name, e.g. @c rtl8139) as a global
+ * symbol, so that the linker can drag in selected object files from
+ * the library using <tt> -u obj_OBJECT </tt>.
+ *
+ */
+
+/* Not quite sure why cpp requires two levels of macro call in order
+ * to actually expand OBJECT...
+ */
+#undef _H1
+#define _H1( x, y ) x ## y
+#undef _H2
+#define _H2( x, y ) _H1 ( x, y )
+#define PREFIX_OBJECT(prefix) _H2 ( prefix, OBJECT )
+#define OBJECT_SYMBOL PREFIX_OBJECT(obj_)
+#undef _STR
+#define _STR(s) #s
+#undef _XSTR
+#define _XSTR(s) _STR(s)
+#define OBJECT_SYMBOL_STR _XSTR ( OBJECT_SYMBOL )
+
+#ifdef ASSEMBLY
+
+ .globl OBJECT_SYMBOL
+ .equ OBJECT_SYMBOL, 0
+
+#else /* ASSEMBLY */
+
+__asm__ ( ".globl\t" OBJECT_SYMBOL_STR );
+__asm__ ( ".equ\t" OBJECT_SYMBOL_STR ", 0" );
+
+/**
+ * Drag in an object by object name.
+ *
+ * Macro to allow objects to explicitly drag in other objects by
+ * object name. Used by config.c.
+ *
+ */
+#define REQUIRE_OBJECT(object) \
+ __asm__ ( ".equ\tneed_" #object ", obj_" #object );
+
+/** @def DBG
+ *
+ * Print a debugging message.
+ *
+ * The debug level is set at build time by specifying the @c DEBUG=
+ * parameter on the @c make command line. For example, to enable
+ * debugging for the PCI bus functions (in pci.c) in a @c .dsk image
+ * for the @c rtl8139 card, you could use the command line
+ *
+ * @code
+ *
+ * make bin/rtl8139.dsk DEBUG=pci
+ *
+ * @endcode
+ *
+ * This will enable the debugging statements (DBG()) in pci.c. If
+ * debugging is not enabled, DBG() statements will be ignored.
+ *
+ * You can enable debugging in several objects simultaneously by
+ * separating them with commas, as in
+ *
+ * @code
+ *
+ * make bin/rtl8139.dsk DEBUG=pci,buffer,heap
+ *
+ * @endcode
+ *
+ * You can increase the debugging level for an object by specifying it
+ * with @c :N, where @c N is the level, as in
+ *
+ * @code
+ *
+ * make bin/rtl8139.dsk DEBUG=pci,buffer:2,heap
+ *
+ * @endcode
+ *
+ * which would enable debugging for the PCI, buffer-handling and
+ * heap-allocation code, with the buffer-handling code at level 2.
+ *
+ */
+
+/*
+ * If debug_OBJECT is set to a true value, the macro DBG(...) will
+ * expand to printf(...) when compiling OBJECT, and the symbol
+ * DEBUG_LEVEL will be inserted into the object file.
+ *
+ */
+#define DEBUG_SYMBOL PREFIX_OBJECT(debug_)
+
+#if DEBUG_SYMBOL
+#define DEBUG_SYMBOL_STR _XSTR ( DEBUG_SYMBOL )
+__asm__ ( ".equ\tDBGLVL, " DEBUG_SYMBOL_STR );
+#endif
+
+/** printf() for debugging
+ *
+ * This function exists so that the DBG() macros can expand to
+ * printf() calls without dragging the printf() prototype into scope.
+ *
+ * As far as the compiler is concerned, dbg_printf() and printf() are
+ * completely unrelated calls; it's only at the assembly stage that
+ * references to the dbg_printf symbol are collapsed into references
+ * to the printf symbol.
+ */
+extern int __attribute__ (( format ( printf, 1, 2 ) ))
+dbg_printf ( const char *fmt, ... ) asm ( "printf" );
+
+extern void dbg_autocolourise ( unsigned long id );
+extern void dbg_decolourise ( void );
+extern void dbg_hex_dump_da ( unsigned long dispaddr,
+ const void *data, unsigned long len );
+
+/* Compatibility with existing Makefile */
+#if DEBUG_SYMBOL
+#define DBGLVL DEBUG_SYMBOL
+#else
+#define DBGLVL 0
+#endif
+
+#define DBGLVL_LOG 1
+#define DBG_LOG ( DBGLVL & DBGLVL_LOG )
+#define DBGLVL_EXTRA 2
+#define DBG_EXTRA ( DBGLVL & DBGLVL_EXTRA )
+#define DBGLVL_PROFILE 4
+#define DBG_PROFILE ( DBGLVL & DBGLVL_PROFILE )
+#define DBGLVL_IO 8
+#define DBG_IO ( DBGLVL & DBGLVL_IO )
+
+/**
+ * Print debugging message if we are at a certain debug level
+ *
+ * @v level Debug level
+ * @v ... printf() argument list
+ */
+#define DBG_IF( level, ... ) do { \
+ if ( DBG_ ## level ) { \
+ dbg_printf ( __VA_ARGS__ ); \
+ } \
+ } while ( 0 )
+
+/**
+ * Print a hex dump if we are at a certain debug level
+ *
+ * @v level Debug level
+ * @v dispaddr Display address
+ * @v data Data to print
+ * @v len Length of data
+ */
+#define DBG_HDA_IF( level, dispaddr, data, len ) do { \
+ if ( DBG_ ## level ) { \
+ union { \
+ unsigned long ul; \
+ typeof ( dispaddr ) raw; \
+ } da; \
+ da.raw = dispaddr; \
+ dbg_hex_dump_da ( da.ul, data, len ); \
+ } \
+ } while ( 0 )
+
+/**
+ * Print a hex dump if we are at a certain debug level
+ *
+ * @v level Debug level
+ * @v data Data to print
+ * @v len Length of data
+ */
+#define DBG_HD_IF( level, data, len ) do { \
+ DBG_HDA_IF ( level, data, data, len ); \
+ } while ( 0 )
+
+/**
+ * Select colour for debug messages if we are at a certain debug level
+ *
+ * @v level Debug level
+ * @v id Message stream ID
+ */
+#define DBG_AC_IF( level, id ) do { \
+ if ( DBG_ ## level ) { \
+ union { \
+ unsigned long ul; \
+ typeof ( id ) raw; \
+ } dbg_stream; \
+ dbg_stream.raw = id; \
+ dbg_autocolourise ( dbg_stream.ul ); \
+ } \
+ } while ( 0 )
+
+/**
+ * Revert colour for debug messages if we are at a certain debug level
+ *
+ * @v level Debug level
+ */
+#define DBG_DC_IF( level ) do { \
+ if ( DBG_ ## level ) { \
+ dbg_decolourise(); \
+ } \
+ } while ( 0 )
+
+/* Autocolourising versions of the DBGxxx_IF() macros */
+
+#define DBGC_IF( level, id, ... ) do { \
+ DBG_AC_IF ( level, id ); \
+ DBG_IF ( level, __VA_ARGS__ ); \
+ DBG_DC_IF ( level ); \
+ } while ( 0 )
+
+#define DBGC_HDA_IF( level, id, ... ) do { \
+ DBG_AC_IF ( level, id ); \
+ DBG_HDA_IF ( level, __VA_ARGS__ ); \
+ DBG_DC_IF ( level ); \
+ } while ( 0 )
+
+#define DBGC_HD_IF( level, id, ... ) do { \
+ DBG_AC_IF ( level, id ); \
+ DBG_HD_IF ( level, __VA_ARGS__ ); \
+ DBG_DC_IF ( level ); \
+ } while ( 0 )
+
+/* Versions of the DBGxxx_IF() macros that imply DBGxxx_IF( LOG, ... )*/
+
+#define DBG( ... ) DBG_IF ( LOG, __VA_ARGS__ )
+#define DBG_HDA( ... ) DBG_HDA_IF ( LOG, __VA_ARGS__ )
+#define DBG_HD( ... ) DBG_HD_IF ( LOG, __VA_ARGS__ )
+#define DBGC( ... ) DBGC_IF ( LOG, __VA_ARGS__ )
+#define DBGC_HDA( ... ) DBGC_HDA_IF ( LOG, __VA_ARGS__ )
+#define DBGC_HD( ... ) DBGC_HD_IF ( LOG, __VA_ARGS__ )
+
+/* Versions of the DBGxxx_IF() macros that imply DBGxxx_IF( EXTRA, ... )*/
+
+#define DBG2( ... ) DBG_IF ( EXTRA, __VA_ARGS__ )
+#define DBG2_HDA( ... ) DBG_HDA_IF ( EXTRA, __VA_ARGS__ )
+#define DBG2_HD( ... ) DBG_HD_IF ( EXTRA, __VA_ARGS__ )
+#define DBGC2( ... ) DBGC_IF ( EXTRA, __VA_ARGS__ )
+#define DBGC2_HDA( ... ) DBGC_HDA_IF ( EXTRA, __VA_ARGS__ )
+#define DBGC2_HD( ... ) DBGC_HD_IF ( EXTRA, __VA_ARGS__ )
+
+/* Versions of the DBGxxx_IF() macros that imply DBGxxx_IF( PROFILE, ... )*/
+
+#define DBGP( ... ) DBG_IF ( PROFILE, __VA_ARGS__ )
+#define DBGP_HDA( ... ) DBG_HDA_IF ( PROFILE, __VA_ARGS__ )
+#define DBGP_HD( ... ) DBG_HD_IF ( PROFILE, __VA_ARGS__ )
+#define DBGCP( ... ) DBGC_IF ( PROFILE, __VA_ARGS__ )
+#define DBGCP_HDA( ... ) DBGC_HDA_IF ( PROFILE, __VA_ARGS__ )
+#define DBGCP_HD( ... ) DBGC_HD_IF ( PROFILE, __VA_ARGS__ )
+
+/* Versions of the DBGxxx_IF() macros that imply DBGxxx_IF( IO, ... )*/
+
+#define DBGIO( ... ) DBG_IF ( IO, __VA_ARGS__ )
+#define DBGIO_HDA( ... ) DBG_HDA_IF ( IO, __VA_ARGS__ )
+#define DBGIO_HD( ... ) DBG_HD_IF ( IO, __VA_ARGS__ )
+#define DBGCIO( ... ) DBGC_IF ( IO, __VA_ARGS__ )
+#define DBGCIO_HDA( ... ) DBGC_HDA_IF ( IO, __VA_ARGS__ )
+#define DBGCIO_HD( ... ) DBGC_HD_IF ( IO, __VA_ARGS__ )
+
+
+#if DEBUG_SYMBOL == 0
+#define NDEBUG
+#endif
+
+/** Select file identifier for errno.h (if used) */
+#define ERRFILE PREFIX_OBJECT ( ERRFILE_ )
+
+/** Declare a data structure as packed. */
+#define PACKED __attribute__ (( packed ))
+
+/** Declare a variable or data structure as unused. */
+#define __unused __attribute__ (( unused ))
+
+/** Apply standard C calling conventions */
+#define __cdecl __attribute__ (( cdecl , regparm(0) ))
+
+/**
+ * Declare a function as pure - i.e. without side effects
+ */
+#define __pure __attribute__ (( pure ))
+
+/**
+ * Declare a function as const - i.e. it does not access global memory
+ * (including dereferencing pointers passed to it) at all.
+ * Must also not call any non-const functions.
+ */
+#define __const __attribute__ (( const ))
+
+/**
+ * Declare a function's pointer parameters as non-null - i.e. force
+ * compiler to check pointers at compile time and enable possible
+ * optimizations based on that fact
+ */
+#define __nonnull __attribute__ (( nonnull ))
+
+/**
+ * Declare a pointer returned by a function as a unique memory address
+ * as returned by malloc-type functions.
+ */
+#define __malloc __attribute__ (( malloc ))
+
+/**
+ * Declare a function as used.
+ *
+ * Necessary only if the function is called only from assembler code.
+ */
+#define __used __attribute__ (( used ))
+
+/** Declare a data structure to be aligned with 16-byte alignment */
+#define __aligned __attribute__ (( aligned ( 16 ) ))
+
+/**
+ * Shared data.
+ *
+ * To save space in the binary when multiple-driver images are
+ * compiled, uninitialised data areas can be shared between drivers.
+ * This will typically be used to share statically-allocated receive
+ * and transmit buffers between drivers.
+ *
+ * Use as e.g.
+ *
+ * @code
+ *
+ * struct {
+ * char rx_buf[NUM_RX_BUF][RX_BUF_SIZE];
+ * char tx_buf[TX_BUF_SIZE];
+ * } my_static_data __shared;
+ *
+ * @endcode
+ *
+ */
+#define __shared __asm__ ( "_shared_bss" )
+
+/**
+ * Optimisation barrier
+ */
+#define barrier() __asm__ __volatile__ ( "" : : : "memory" )
+
+#endif /* ASSEMBLY */
+
+#endif /* COMPILER_H */
diff --git a/gpxe/src/include/console.h b/gpxe/src/include/console.h
new file mode 100644
index 00000000..9addd526
--- /dev/null
+++ b/gpxe/src/include/console.h
@@ -0,0 +1,114 @@
+#ifndef CONSOLE_H
+#define CONSOLE_H
+
+#include <gpxe/tables.h>
+
+/** @file
+ *
+ * User interaction.
+ *
+ * Various console devices can be selected via the build options
+ * CONSOLE_FIRMWARE, CONSOLE_SERIAL etc. The console functions
+ * putchar(), getchar() and iskey() delegate to the individual console
+ * drivers.
+ *
+ */
+
+/**
+ * A console driver
+ *
+ * Defines the functions that implement a particular console type.
+ * Must be made part of the console drivers table by using
+ * #__console_driver.
+ *
+ * @note Consoles that cannot be used before their initialisation
+ * function has completed should set #disabled=1 initially. This
+ * allows other console devices to still be used to print out early
+ * debugging messages.
+ *
+ */
+struct console_driver {
+ /** Console is disabled.
+ *
+ * The console's putchar(), putline(), getchar() and iskey()
+ * methods will not be called while #disabled==1. Typically
+ * the console's initialisation functions will set #disabled=0
+ * upon completion.
+ *
+ */
+ int disabled;
+
+ /** Write a character to the console.
+ *
+ * @v character Character to be written
+ * @ret None -
+ * @err None -
+ *
+ */
+ void ( *putchar ) ( int character );
+
+ /** Write an entire line to the console.
+ * This is intended to be used by line-oriented output media,
+ * like system logging facilities or line printers.
+ * Line output will not contain non-printable characters.
+ *
+ * @v linebuffer Pointer to the \0-terminated line
+ * @ret None -
+ * @err None -
+ */
+ void ( * putline ) ( unsigned char * linebuffer );
+
+ /** Read a character from the console.
+ *
+ * @v None -
+ * @ret character Character read
+ * @err None -
+ *
+ * If no character is available to be read, this method will
+ * block. The character read should not be echoed back to the
+ * console.
+ *
+ */
+ int ( *getchar ) ( void );
+
+ /** Check for available input.
+ *
+ * @v None -
+ * @ret True Input is available
+ * @ret False Input is not available
+ * @err None -
+ *
+ * This should return True if a subsequent call to getchar()
+ * will not block.
+ *
+ */
+ int ( *iskey ) ( void );
+};
+
+/**
+ * Mark a <tt> struct console_driver </tt> as being part of the
+ * console drivers table.
+ *
+ * Use as e.g.
+ *
+ * @code
+ *
+ * struct console_driver my_console __console_driver = {
+ * .putchar = my_putchar,
+ * .getchar = my_getchar,
+ * .iskey = my_iskey,
+ * };
+ *
+ * @endcode
+ *
+ */
+#define __console_driver __table ( struct console_driver, console, 01 )
+
+/* Function prototypes */
+
+extern void putchar ( int character );
+extern int getchar ( void );
+extern int iskey ( void );
+extern int getkey ( void );
+
+#endif /* CONSOLE_H */
diff --git a/gpxe/src/include/cpu.h b/gpxe/src/include/cpu.h
new file mode 100644
index 00000000..b2c428f7
--- /dev/null
+++ b/gpxe/src/include/cpu.h
@@ -0,0 +1,6 @@
+#ifndef CPU_H
+#define CPU_H
+
+#include "bits/cpu.h"
+
+#endif /* CPU_H */
diff --git a/gpxe/src/include/ctype.h b/gpxe/src/include/ctype.h
new file mode 100644
index 00000000..a79395d2
--- /dev/null
+++ b/gpxe/src/include/ctype.h
@@ -0,0 +1,28 @@
+#ifndef _CTYPE_H
+#define _CTYPE_H
+
+/** @file
+ *
+ * Character types
+ */
+
+#define isdigit(c) ((c & 0x04) != 0)
+#define islower(c) ((c & 0x02) != 0)
+//#define isspace(c) ((c & 0x20) != 0)
+#define isupper(c) ((c & 0x01) != 0)
+
+static inline unsigned char tolower(unsigned char c)
+{
+ if (isupper(c))
+ c -= 'A'-'a';
+ return c;
+}
+
+static inline unsigned char toupper(unsigned char c)
+{
+ if (islower(c))
+ c -= 'a'-'A';
+ return c;
+}
+
+#endif /* _CTYPE_H */
diff --git a/gpxe/src/include/curses.h b/gpxe/src/include/curses.h
new file mode 100644
index 00000000..6b1c42d8
--- /dev/null
+++ b/gpxe/src/include/curses.h
@@ -0,0 +1,753 @@
+#ifndef CURSES_H
+#define CURSES_H
+
+#include <stdint.h>
+#include <stdarg.h>
+
+/** @file
+ *
+ * MuCurses header file
+ *
+ */
+
+#undef ERR
+#define ERR (-1)
+
+#undef FALSE
+#define FALSE (0)
+
+#undef OK
+#define OK (0)
+
+#undef TRUE
+#define TRUE (1)
+
+typedef int bool;
+typedef uint32_t chtype;
+typedef uint32_t attr_t;
+
+/** Curses SCREEN object */
+typedef struct _curses_screen {
+ /** Current cursor position */
+ unsigned int curs_x, curs_y;
+ /** Current attribute */
+ attr_t attrs;
+
+ void ( *init ) ( struct _curses_screen *scr );
+ void ( *exit ) ( struct _curses_screen *scr );
+ /**
+ * Move cursor to position specified by x,y coords
+ *
+ * @v scr screen on which to operate
+ * @v y Y position
+ * @v x X position
+ */
+ void ( * movetoyx ) ( struct _curses_screen *scr,
+ unsigned int y, unsigned int x );
+ /**
+ * Write character to current cursor position
+ *
+ * @v scr screen on which to operate
+ * @v c character to be written
+ */
+ void ( * putc ) ( struct _curses_screen *scr, chtype c );
+ /**
+ * Pop a character from the keyboard input stream
+ *
+ * @v scr screen on which to operate
+ * @ret c popped character
+ */
+ int ( * getc ) ( struct _curses_screen *scr );
+ /**
+ * Checks to see whether a character is waiting in the input stream
+ *
+ * @v scr screen on which to operate
+ * @ret TRUE character waiting in stream
+ * @ret FALSE no character waiting in stream
+ */
+ bool ( *peek ) ( struct _curses_screen *scr );
+} SCREEN;
+
+/** Curses Window struct */
+typedef struct _curses_window {
+ /** screen with which window associates */
+ SCREEN *scr;
+ /** window attributes */
+ attr_t attrs;
+ /** window origin coordinates */
+ unsigned int ori_x, ori_y;
+ /** window cursor position */
+ unsigned int curs_x, curs_y;
+ /** window dimensions */
+ unsigned int width, height;
+ /** parent window */
+ struct _curses_window *parent;
+ /** windows that share the same parent as this one */
+ //struct list_head siblings;
+ /** windows der'd or sub'd from this one */
+ //struct list_head children;
+} WINDOW;
+
+extern WINDOW _stdscr;
+extern unsigned short _COLS;
+extern unsigned short _LINES;
+
+#define stdscr ( &_stdscr )
+#define COLS _COLS
+#define LINES _LINES
+
+#define MUCURSES_BITS( mask, shift ) (( mask ) << (shift))
+#define CPAIR_SHIFT 8
+#define ATTRS_SHIFT 16
+
+#define WA_DEFAULT ( 0x0000 << ATTRS_SHIFT )
+#define WA_ALTCHARSET ( 0x0001 << ATTRS_SHIFT )
+#define WA_BLINK ( 0x0002 << ATTRS_SHIFT )
+#define WA_BOLD ( 0x0004 << ATTRS_SHIFT )
+#define WA_DIM ( 0x0008 << ATTRS_SHIFT )
+#define WA_INVIS ( 0x0010 << ATTRS_SHIFT )
+#define WA_PROTECT ( 0x0020 << ATTRS_SHIFT )
+#define WA_REVERSE ( 0x0040 << ATTRS_SHIFT )
+#define WA_STANDOUT ( 0x0080 << ATTRS_SHIFT )
+#define WA_UNDERLINE ( 0x0100 << ATTRS_SHIFT )
+#define WA_HORIZONTAL ( 0x0200 << ATTRS_SHIFT )
+#define WA_VERTICAL ( 0x0400 << ATTRS_SHIFT )
+#define WA_LEFT ( 0x0800 << ATTRS_SHIFT )
+#define WA_RIGHT ( 0x1000 << ATTRS_SHIFT )
+#define WA_LOW ( 0x2000 << ATTRS_SHIFT )
+#define WA_TOP ( 0x4000 << ATTRS_SHIFT )
+
+#define A_DEFAULT WA_DEFAULT
+#define A_ALTCHARSET WA_ALTCHARSET
+#define A_BLINK WA_BLINK
+#define A_BOLD WA_BOLD
+#define A_DIM WA_DIM
+#define A_INVIS WA_INVIS
+#define A_PROTECT WA_PROTECT
+#define A_REVERSE WA_REVERSE
+#define A_STANDOUT WA_STANDOUT
+#define A_UNDERLINE WA_UNDERLINE
+
+#define A_ATTRIBUTES ( 0xffff << ATTRS_SHIFT )
+#define A_CHARTEXT ( 0xff )
+#define A_COLOUR ( 0xff << CPAIR_SHIFT )
+#define A_COLOR A_COLOUR
+
+#define COLOUR_PAIR(n) ( (n) << CPAIR_SHIFT )
+#define COLOR_PAIR(n) COLOUR_PAIR(n)
+#define PAIR_NUMBER(attrs) ( ( (attrs) & A_COLOUR ) >> CPAIR_SHIFT )
+
+#define COLOUR_PAIRS 8 /* Arbitrary limit */
+#define COLOR_PAIRS COLOUR_PAIRS
+
+#define ACS_ULCORNER '+'
+#define ACS_LLCORNER '+'
+#define ACS_URCORNER '+'
+#define ACS_LRCORNER '+'
+#define ACS_RTEE '+'
+#define ACS_LTEE '+'
+#define ACS_BTEE '+'
+#define ACS_TTEE '+'
+#define ACS_HLINE '-'
+#define ACS_VLINE '|'
+#define ACS_PLUS '+'
+#define ACS_S1 '-'
+#define ACS_S9 '_'
+#define ACS_DIAMOND '+'
+#define ACS_CKBOARD ':'
+#define ACS_DEGREE '\''
+#define ACS_PLMINUS '#'
+#define ACS_BULLET 'o'
+#define ACS_LARROW '<'
+#define ACS_RARROW '>'
+#define ACS_DARROW 'v'
+#define ACS_UARROW '^'
+#define ACS_BOARD '#'
+#define ACS_LANTERN '#'
+#define ACS_BLOCK '#'
+
+#define COLOUR_BLACK 0
+#define COLOUR_RED 1
+#define COLOUR_GREEN 2
+#define COLOUR_YELLOW 3
+#define COLOUR_BLUE 4
+#define COLOUR_MAGENTA 5
+#define COLOUR_CYAN 6
+#define COLOUR_WHITE 7
+#define COLOURS 7
+
+#define COLOUR_FG 30
+#define COLOUR_BG 40
+#define COLOR_FG COLOUR_FG
+#define COLOR_BG COLOUR_BG
+
+#define COLOR_BLACK COLOUR_BLACK
+#define COLOR_BLUE COLOUR_BLUE
+#define COLOR_GREEN COLOUR_GREEN
+#define COLOR_CYAN COLOUR_CYAN
+#define COLOR_RED COLOUR_RED
+#define COLOR_MAGENTA COLOUR_MAGENTA
+#define COLOR_YELLOW COLOUR_YELLOW
+#define COLOR_WHITE COLOUR_WHITE
+#define COLORS COLOURS
+
+/*
+ * KEY code constants are define in gpxe/keys.h
+ */
+#include <gpxe/keys.h>
+
+//extern int addch ( const chtype * );
+//extern int addchnstr ( const chtype *, int );
+//extern int addchstr ( const chtype * );
+//extern int addnstr ( const char *, int );
+//extern int addstr ( const char * );
+//extern int attroff ( int );
+//extern int attron ( int );
+//extern int attrset ( int );
+//extern int attr_get ( attr_t *, short *, void * );
+//extern int attr_off ( attr_t, void * );
+//extern int attr_on ( attr_t, void * );
+//extern int attr_set ( attr_t, short, void * );
+extern int baudrate ( void );
+extern int beep ( void );
+//extern void bkgdset ( chtype );
+/*extern int border ( chtype, chtype, chtype, chtype, chtype, chtype, chtype,
+ chtype );*/
+extern int box ( WINDOW *, chtype, chtype ) __nonnull;
+//extern bool can_change_colour ( void );
+#define can_change_color() can_change_colour()
+extern int cbreak ( void );
+//extern int clrtobot ( void );
+//extern int clrtoeol ( void );
+extern int colour_content ( short, short *, short *, short * ) __nonnull;
+#define color_content( c, r, g, b ) colour_content( (c), (r), (g), (b) )
+//extern int colour_set ( short, void * );
+#define color_set( cpno, opts ) colour_set( (cpno), (opts) )
+extern int copywin ( const WINDOW *, WINDOW *, int, int, int,
+ int, int, int, int );
+extern int curs_set ( int );
+extern int def_prog_mode ( void );
+extern int def_shell_mode ( void );
+extern int delay_output ( int );
+//extern int delch ( void );
+//extern int deleteln ( void );
+extern void delscreen ( SCREEN * );
+extern int delwin ( WINDOW * ) __nonnull;
+extern WINDOW *derwin ( WINDOW *, int, int, int, int ) __nonnull;
+//extern int doupdate ( void );
+extern WINDOW *dupwin ( WINDOW * ) __nonnull;
+extern int echo ( void );
+extern int echochar ( const chtype );
+extern int endwin ( void );
+extern char erasechar ( void );
+//extern int erase ( void );
+extern void filter ( void );
+extern int flash ( void );
+extern int flushinp ( void );
+extern __pure chtype getbkgd ( WINDOW * ) __nonnull;
+//extern int getch ( void );
+//extern int getnstr ( char *, int );
+//extern int getstr ( char * );
+extern int halfdelay ( int );
+//extern bool has_colors ( void );
+extern bool has_ic ( void );
+extern bool has_il ( void );
+//extern int hline ( chtype, int );
+extern void idcok ( WINDOW *, bool );
+extern int idlok ( WINDOW *, bool );
+//extern void immedok ( WINDOW *, bool );
+//extern chtype inch ( void );
+//extern int inchnstr ( chtype *, int );
+//extern int inchstr ( chtype * );
+extern WINDOW *initscr ( void );
+extern int init_colour ( short, short, short, short );
+#define init_color ( c, r, g, b ) init_colour ( (c), (r), (g), (b) )
+extern int init_pair ( short, short, short );
+//extern int innstr ( char *, int );
+//extern int insch ( chtype );
+//extern int insnstr ( const char *, int );
+//extern int insstr ( const char * );
+//extern int instr ( char * );
+extern int intrflush ( WINDOW *, bool );
+extern bool isendwin ( void );
+//extern bool is_linetouched ( WINDOW *, int );
+//extern bool is_wintouched ( WINDOW * );
+extern char *keyname ( int );
+extern int keypad ( WINDOW *, bool );
+extern char killchar ( void );
+extern int leaveok ( WINDOW *, bool );
+extern char *longname ( void );
+extern int meta ( WINDOW *, bool );
+//extern int move ( int, int );
+//extern int mvaddch ( int, int, const chtype );
+//extern int mvaddchnstr ( int, int, const chtype *, int );
+//extern int mvaddchstr ( int, int, const chtype * );
+//extern int mvaddnstr ( int, int, const char *, int );
+//extern int mvaddstr ( int, int, const char * );
+extern int mvcur ( int, int, int, int );
+//extern int mvdelch ( int, int );
+extern int mvderwin ( WINDOW *, int, int );
+//extern int mvgetch ( int, int );
+//extern int mvgetnstr ( int, int, char *, int );
+//extern int mvgetstr ( int, int, char * );
+//extern int mvhline ( int, int, chtype, int );
+//extern chtype mvinch ( int, int );
+//extern int mvinchnstr ( int, int, chtype *, int );
+//extern int mvinchstr ( int, int, chtype * );
+//extern int mvinnstr ( int, int, char *, int );
+//extern int mvinsch ( int, int, chtype );
+//extern int mvinsnstr ( int, int, const char *, int );
+//extern int mvinsstr ( int, int, const char * );
+//extern int mvinstr ( int, int, char * );
+//extern int mvprintw ( int, int, char *, ... );
+//extern int mvscanw ( int, int, char *, ... );
+//extern int mvvline ( int, int, chtype, int );
+//extern int mvwaddch ( WINDOW *, int, int, const chtype );
+//extern int mvwaddchnstr ( WINDOW *, int, int, const chtype *, int );
+//extern int mvwaddchstr ( WINDOW *, int, int, const chtype * );
+//extern int mvwaddnstr ( WINDOW *, int, int, const char *, int );
+//extern int mvwaddstr ( WINDOW *, int, int, const char * );
+//extern int mvwdelch ( WINDOW *, int, int );
+//extern int mvwgetch ( WINDOW *, int, int );
+//extern int mvwgetnstr ( WINDOW *, int, int, char *, int );
+//extern int mvwgetstr ( WINDOW *, int, int, char * );
+//extern int mvwhline ( WINDOW *, int, int, chtype, int );
+extern int mvwin ( WINDOW *, int, int ) __nonnull;
+//extern chtype mvwinch ( WINDOW *, int, int );
+//extern int mvwinchnstr ( WINDOW *, int, int, chtype *, int );
+//extern int mvwinchstr ( WINDOW *, int, int, chtype * );
+//extern int mvwinnstr ( WINDOW *, int, int, char *, int );
+//extern int mvwinsch ( WINDOW *, int, int, chtype );
+//extern int mvwinsnstr ( WINDOW *, int, int, const char *, int );
+//extern int mvwinsstr ( WINDOW *, int, int, const char * );
+//extern int mvwinstr ( WINDOW *, int, int, char * );
+//extern int mvwprintw ( WINDOW *, int, int, char *, ... );
+//extern int mvwscanw ( WINDOW *, int, int, char *, ... );
+//extern int mvwvline ( WINDOW *, int, int, chtype, int );
+extern int napms ( int );
+//extern WINDOW *newpad ( int, int );
+extern WINDOW *newwin ( int, int, int, int );
+extern int nl ( void );
+extern int nocbreak ( void );
+extern int nodelay ( WINDOW *, bool );
+extern int noecho ( void );
+extern int nonl ( void );
+extern void noqiflush ( void );
+extern int noraw ( void );
+extern int notimeout ( WINDOW *, bool );
+extern int overlay ( const WINDOW *, WINDOW * );
+extern int overwrite ( const WINDOW *, WINDOW * );
+extern int pair_content ( short, short *, short * ) __nonnull;
+//extern int pechochar ( WINDOW *, chtype );
+//extern int pnoutrefresh ( WINDOW *, int, int, int, int, int, int );
+//extern int prefresh ( WINDOW *, int, int, int, int, int, int );
+extern int printw ( char *, ... );
+extern int putp ( const char * );
+extern void qiflush ( void );
+extern int raw ( void );
+//extern int redrawwin ( WINDOW * );
+//extern int refresh ( void );
+extern int reset_prog_mode ( void );
+extern int reset_shell_mode ( void );
+extern int resetty ( void );
+extern int ripoffline ( int, int (*) ( WINDOW *, int) );
+extern int savetty ( void );
+//extern int scanw ( char *, ... );
+//extern int scrl ( int );
+//extern int scroll ( WINDOW * );
+//extern int scrollok ( WINDOW *, bool );
+//extern int setscrreg ( int, int );
+extern SCREEN *set_term ( SCREEN * );
+extern int setupterm ( char *, int, int * );
+extern int slk_attr_off ( const attr_t, void * );
+extern int slk_attroff ( const chtype );
+extern int slk_attr_on ( const attr_t, void * );
+extern int slk_attron ( const chtype );
+extern int slk_attr_set ( const attr_t, short, void * );
+extern int slk_attrset ( const chtype );
+extern int slk_clear ( void );
+extern int slk_colour ( short );
+#define slk_color( c ) slk_colour( (c) )
+extern int slk_init ( int );
+extern char *slk_label ( int );
+extern int slk_noutrefresh ( void );
+//extern int slk_refresh ( void );
+extern int slk_restore ( void );
+extern int slk_set ( int, const char *, int ) __nonnull;
+extern int slk_touch ( void );
+extern int standend ( void );
+extern int standout ( void );
+//extern int start_colour ( void );
+#define start_color() start_colour()
+//extern WINDOW *subpad ( WINDOW *, int, int, int, int );
+extern WINDOW *subwin ( WINDOW *, int, int, int, int ) __nonnull;
+extern int syncok ( WINDOW *, bool );
+extern chtype termattrs ( void );
+extern attr_t term_attrs ( void );
+extern char *termname ( void );
+extern int tigetflag ( char * );
+extern int tigetnum ( char * );
+extern char *tigetstr ( char * );
+extern void timeout ( int );
+//extern int touchline ( WINDOW *, int, int );
+//extern int touchwin ( WINDOW * );
+extern char *tparm ( char *, long, long, long, long, long, long, long, long,
+ long );
+extern int typeahead ( int );
+//extern int ungetch ( int );
+//extern int untouchwin ( WINDOW * );
+extern void use_env ( bool );
+extern int vid_attr ( attr_t, short, void * );
+extern int vidattr ( chtype );
+extern int vid_puts ( attr_t, short, void *, int ( *) ( int) );
+extern int vidputs ( chtype, int ( *) ( int) );
+//extern int vline ( chtype, int );
+//extern int vwprintw ( WINDOW *, const char *, va_list );
+extern int vw_printw ( WINDOW *, const char *, va_list ) __nonnull;
+//extern int vwscanw ( WINDOW *, char *, va_list );
+//extern int vw_scanw ( WINDOW *, char *, va_list );
+extern int waddch ( WINDOW *, const chtype ) __nonnull;
+extern int waddchnstr ( WINDOW *, const chtype *, int ) __nonnull;
+//extern int waddchstr ( WINDOW *, const chtype * );
+extern int waddnstr ( WINDOW *, const char *, int ) __nonnull;
+//extern int waddstr ( WINDOW *, const char * );
+extern int wattroff ( WINDOW *, int ) __nonnull;
+extern int wattron ( WINDOW *, int ) __nonnull;
+extern int wattrset ( WINDOW *, int ) __nonnull;
+extern int wattr_get ( WINDOW *, attr_t *, short *, void * )
+ __attribute__ (( nonnull (1, 2, 3)));
+extern int wattr_off ( WINDOW *, attr_t, void * )
+ __attribute__ (( nonnull (1)));
+extern int wattr_on ( WINDOW *, attr_t, void * )
+ __attribute__ (( nonnull (1)));
+extern int wattr_set ( WINDOW *, attr_t, short, void * )
+ __attribute__ (( nonnull (1)));
+//extern void wbkgdset ( WINDOW *, chtype );
+extern int wborder ( WINDOW *, chtype, chtype, chtype, chtype, chtype, chtype,
+ chtype, chtype ) __nonnull;
+extern int wclrtobot ( WINDOW * ) __nonnull;
+extern int wclrtoeol ( WINDOW * ) __nonnull;
+extern void wcursyncup ( WINDOW * );
+extern int wcolour_set ( WINDOW *, short, void * ) __nonnull;
+#define wcolor_set(w,s,v) wcolour_set((w),(s),(v))
+extern int wdelch ( WINDOW * ) __nonnull;
+extern int wdeleteln ( WINDOW * ) __nonnull;
+extern int wechochar ( WINDOW *, const chtype );
+extern int werase ( WINDOW * ) __nonnull;
+extern int wgetch ( WINDOW * );
+extern int wgetnstr ( WINDOW *, char *, int );
+//extern int wgetstr ( WINDOW *, char * );
+extern int whline ( WINDOW *, chtype, int ) __nonnull;
+//extern chtype winch ( WINDOW * );
+//extern int winchnstr ( WINDOW *, chtype *, int );
+//extern int winchstr ( WINDOW *, chtype * );
+//extern int winnstr ( WINDOW *, char *, int );
+//extern int winsch ( WINDOW *, chtype );
+//extern int winsnstr ( WINDOW *, const char *, int );
+//extern int winsstr ( WINDOW *, const char * );
+//extern int winstr ( WINDOW *, char * );
+extern int wmove ( WINDOW *, int, int );
+//extern int wnoutrefresh ( WINDOW * );
+extern int wprintw ( WINDOW *, const char *, ... ) __nonnull;
+//extern int wredrawln ( WINDOW *, int, int );
+//extern int wrefresh ( WINDOW * );
+//extern int wscanw ( WINDOW *, char *, ... );
+//extern int wscrl ( WINDOW *, int );
+//extern int wsetscrreg ( WINDOW *, int, int );
+//extern int wstandend ( WINDOW * );
+//extern int wstandout ( WINDOW * );
+extern void wsyncup ( WINDOW * );
+extern void wsyncdown ( WINDOW * );
+extern void wtimeout ( WINDOW *, int );
+//extern int wtouchln ( WINDOW *, int, int, int );
+extern int wvline ( WINDOW *, chtype, int ) __nonnull;
+
+/*
+ * There is frankly a ridiculous amount of redundancy within the
+ * curses API - ncurses decided to get around this by using #define
+ * macros, but I've decided to be type-safe and implement them all as
+ * static inlines instead...
+ */
+
+static inline int addch ( const chtype ch ) {
+ return waddch( stdscr, ch );
+}
+
+static inline int addchnstr ( const chtype *chstr, int n ) {
+ return waddchnstr ( stdscr, chstr, n );
+}
+
+static inline int addchstr ( const chtype *chstr ) {
+ return waddchnstr ( stdscr, chstr, -1 );
+}
+
+static inline int addnstr ( const char *str, int n ) {
+ return waddnstr ( stdscr, str, n );
+}
+
+static inline int addstr ( const char *str ) {
+ return waddnstr ( stdscr, str, -1 );
+}
+
+static inline int attroff ( int attrs ) {
+ return wattroff ( stdscr, attrs );
+}
+
+static inline int attron ( int attrs ) {
+ return wattron ( stdscr, attrs );
+}
+
+static inline int attrset ( int attrs ) {
+ return wattrset ( stdscr, attrs );
+}
+
+static inline int attr_get ( attr_t *attrs, short *pair, void *opts ) {
+ return wattr_get ( stdscr, attrs, pair, opts );
+}
+
+static inline int attr_off ( attr_t attrs, void *opts ) {
+ return wattr_off ( stdscr, attrs, opts );
+}
+
+static inline int attr_on ( attr_t attrs, void *opts ) {
+ return wattr_on ( stdscr, attrs, opts );
+}
+
+static inline int attr_set ( attr_t attrs, short cpair, void *opts ) {
+ return wattr_set ( stdscr, attrs, cpair, opts );
+}
+
+static inline void bkgdset ( chtype ch ) {
+ wattrset ( stdscr, ch );
+}
+
+static inline int border ( chtype ls, chtype rs, chtype ts, chtype bs,
+ chtype tl, chtype tr, chtype bl, chtype br ) {
+ return wborder ( stdscr, ls, rs, ts, bs, tl, tr, bl, br );
+}
+
+static inline bool can_change_colour ( void ) {
+ return FALSE;
+}
+
+static inline int clrtobot ( void ) {
+ return wclrtobot( stdscr );
+}
+
+static inline int clrtoeol ( void ) {
+ return wclrtoeol( stdscr );
+}
+
+static inline int colour_set ( short colour_pair_number, void *opts ) {
+ return wcolour_set ( stdscr, colour_pair_number, opts );
+}
+
+static inline int delch ( void ) {
+ return wdelch ( stdscr );
+}
+
+static inline int deleteln ( void ) {
+ return wdeleteln( stdscr );
+}
+
+static inline int erase ( void ) {
+ return werase ( stdscr );
+}
+
+static inline int getch ( void ) {
+ return wgetch ( stdscr );
+}
+
+static inline int getnstr ( char *str, int n ) {
+ return wgetnstr ( stdscr, str, n );
+}
+
+static inline int getstr ( char *str ) {
+ return wgetnstr ( stdscr, str, -1 );
+}
+
+static inline bool has_colors ( void ) {
+ return TRUE;
+}
+
+static inline int has_key ( int kc __unused ) {
+ return TRUE;
+}
+
+static inline int hline ( chtype ch, int n ) {
+ return whline ( stdscr, ch, n );
+}
+
+static inline int move ( int y, int x ) {
+ return wmove ( stdscr, y, x );
+}
+
+static inline int mvaddch ( int y, int x, const chtype ch ) {
+ return ( wmove ( stdscr, y, x ) == OK
+ ? waddch( stdscr, ch ) : ERR );
+}
+
+static inline int mvaddchnstr ( int y, int x, const chtype *chstr, int n ) {
+ return ( wmove ( stdscr, y, x ) == OK
+ ? waddchnstr ( stdscr, chstr, n ) : ERR );
+}
+
+static inline int mvaddchstr ( int y, int x, const chtype *chstr ) {
+ return ( wmove ( stdscr, y, x ) == OK
+ ? waddchnstr ( stdscr, chstr, -1 ) : ERR );
+}
+
+static inline int mvaddnstr ( int y, int x, const char *str, int n ) {
+ return ( wmove ( stdscr, y, x ) == OK
+ ? waddnstr ( stdscr, str, n ) : ERR );
+}
+
+static inline int mvaddstr ( int y, int x, const char *str ) {
+ return ( wmove ( stdscr, y, x ) == OK
+ ? waddnstr ( stdscr, str, -1 ) : ERR );
+}
+
+static inline int mvdelch ( int y, int x ) {
+ return ( wmove ( stdscr, y, x ) == OK
+ ? wdelch ( stdscr ) : ERR );
+}
+
+static inline int mvgetch ( int y, int x ) {
+ return ( wmove ( stdscr, y, x ) == OK
+ ? wgetch ( stdscr ) : ERR );
+}
+
+static inline int mvgetnstr ( int y, int x, char *str, int n ) {
+ return ( wmove ( stdscr, y, x ) == OK
+ ? wgetnstr ( stdscr, str, n ) : ERR );
+}
+
+static inline int mvgetstr ( int y, int x, char *str ) {
+ return ( wmove ( stdscr, y, x ) == OK
+ ? wgetnstr ( stdscr, str, -1 ) : ERR );
+}
+
+static inline int mvhline ( int y, int x, chtype ch, int n ) {
+ return ( wmove ( stdscr, y, x ) == OK
+ ? whline ( stdscr, ch, n ) : ERR );
+}
+
+// OK, so maybe a few I did with macros...
+#define mvprintw( y, x, fmt, ... ) \
+ ( wmove(stdscr,(y),(x)) == OK \
+ ? wprintw( stdscr,(fmt), ## __VA_ARGS__ ) : ERR )
+
+static inline int mvvline ( int y, int x, chtype ch, int n ) {
+ return ( wmove ( stdscr, y, x ) == OK
+ ? wvline ( stdscr, ch, n ) : ERR );
+}
+
+static inline int mvwaddch ( WINDOW *win, int y, int x, const chtype ch ) {
+ return ( wmove( win, y, x ) == OK
+ ? waddch ( win, ch ) : ERR );
+}
+
+static inline int mvwaddchnstr ( WINDOW *win, int y, int x, const chtype *chstr, int n ) {
+ return ( wmove ( win, y, x ) == OK
+ ? waddchnstr ( win, chstr, n ) : ERR );
+}
+
+static inline int mvwaddchstr ( WINDOW *win, int y, int x, const chtype *chstr ) {
+ return ( wmove ( win, y, x ) == OK
+ ? waddchnstr ( win, chstr, -1 ) : ERR );
+}
+
+static inline int mvwaddnstr ( WINDOW *win, int y, int x, const char *str, int n ) {
+ return ( wmove ( win, y, x ) == OK
+ ? waddnstr ( win, str, n ) : ERR );
+}
+
+static inline int mvwaddstr ( WINDOW *win, int y, int x, const char *str ) {
+ return ( wmove ( win, y, x ) == OK
+ ? waddnstr ( win, str, -1 ) : ERR );
+}
+
+static inline int mvwdelch ( WINDOW *win, int y, int x ) {
+ return ( wmove ( win, y, x ) == OK
+ ? wdelch ( win ) : ERR );
+}
+
+static inline int mvwgetch ( WINDOW *win, int y, int x ) {
+ return ( wmove ( win, y, x ) == OK
+ ? wgetch ( win ) : ERR );
+}
+
+static inline int mvwgetnstr ( WINDOW *win, int y, int x, char *str, int n ) {
+ return ( wmove ( win, y, x ) == OK
+ ? wgetnstr ( win, str, n ) : ERR );
+}
+
+static inline int mvwgetstr ( WINDOW *win, int y, int x, char *str ) {
+ return ( wmove ( win, y, x ) == OK
+ ? wgetnstr ( win, str, -1 ) : ERR );
+}
+
+static inline int mvwhline ( WINDOW *win, int y, int x, chtype ch, int n ) {
+ return ( wmove ( win, y, x ) == OK
+ ? whline ( win, ch, n ) : ERR );
+}
+
+#define mvwprintw( win, y, x, fmt, ... ) \
+ ( wmove((win),(y),(x)) == OK \
+ ? wprintw((win),(fmt), ## __VA_ARGS__) : ERR )
+
+static inline int mvwvline ( WINDOW *win, int y, int x, chtype ch, int n ) {
+ return ( wmove ( win, y, x ) == OK
+ ? wvline ( win, ch, n ) : ERR );
+}
+
+#define printw( fmt, ... ) wprintw(stdscr,(fmt), ## __VA_ARGS__ )
+
+static inline int slk_refresh ( void ) {
+ if ( slk_clear() == OK )
+ return slk_restore();
+ else
+ return ERR;
+}
+
+#define standend() wstandend( stdscr )
+#define standout() wstandout( stdscr )
+
+static inline int start_colour ( void ) {
+ return OK;
+}
+
+static inline int vline ( chtype ch, int n ) {
+ return wvline ( stdscr, ch, n );
+}
+
+// marked for removal
+static inline int vwprintw ( WINDOW *win, const char *fmt, va_list varglist ) {
+ return vw_printw ( win, fmt, varglist );
+}
+
+static inline int waddchstr ( WINDOW *win, const chtype *chstr ) {
+ return waddchnstr ( win, chstr, -1 );
+}
+
+static inline int waddstr ( WINDOW *win, const char *str ) {
+ return waddnstr ( win, str, -1 );
+}
+
+static inline int wbkgdset ( WINDOW *win, chtype ch ) {
+ return wattrset( win, ch );
+}
+
+static inline int wgetstr ( WINDOW *win, char *str ) {
+ return wgetnstr ( win, str, -1 );
+}
+
+static inline int wstandend ( WINDOW *win ) {
+ return wattrset ( win, A_DEFAULT );
+}
+
+static inline int wstandout ( WINDOW *win ) {
+ return wattrset ( win, A_STANDOUT );
+}
+
+#endif /* CURSES_H */
diff --git a/gpxe/src/include/debug.h b/gpxe/src/include/debug.h
new file mode 100644
index 00000000..bb5d33f3
--- /dev/null
+++ b/gpxe/src/include/debug.h
@@ -0,0 +1,28 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+//#include <lib.h>
+extern int last_putchar;
+
+/* Defining DEBUG_THIS before including this file enables debug() macro
+ * for the file. DEBUG_ALL is for global control. */
+
+#if DEBUG_THIS || DEBUG_ALL
+#define DEBUG 1
+#else
+#undef DEBUG
+#endif
+
+#if DEBUG
+# define debug(...) \
+ ((last_putchar=='\n' ? printf("%s: ", __FUNCTION__) : 0), \
+ printf(__VA_ARGS__))
+# define debug_hexdump hexdump
+#else
+# define debug(...) /* nothing */
+# define debug_hexdump(...) /* nothing */
+#endif
+
+#define debugx debug
+
+#endif /* DEBUG_H */
diff --git a/gpxe/src/include/dhcp.h b/gpxe/src/include/dhcp.h
new file mode 100644
index 00000000..deba219b
--- /dev/null
+++ b/gpxe/src/include/dhcp.h
@@ -0,0 +1,12 @@
+#ifndef DHCP_H
+#define DHCP_H
+
+#include "stdint.h"
+
+struct dhcp_dev_id {
+ uint8_t bus_type;
+ uint16_t vendor_id;
+ uint16_t device_id;
+} __attribute__ (( packed ));
+
+#endif /* DHCP_H */
diff --git a/gpxe/src/include/elf.h b/gpxe/src/include/elf.h
new file mode 100644
index 00000000..a6eb5d9c
--- /dev/null
+++ b/gpxe/src/include/elf.h
@@ -0,0 +1,236 @@
+#ifndef ELF_H
+#define ELF_H
+
+#define EI_NIDENT 16 /* Size of e_ident array. */
+
+/* Values for e_type. */
+#define ET_NONE 0 /* No file type */
+#define ET_REL 1 /* Relocatable file */
+#define ET_EXEC 2 /* Executable file */
+#define ET_DYN 3 /* Shared object file */
+#define ET_CORE 4 /* Core file */
+
+/* Values for e_machine (architecute). */
+#define EM_NONE 0 /* No machine */
+#define EM_M32 1 /* AT&T WE 32100 */
+#define EM_SPARC 2 /* SUN SPARC */
+#define EM_386 3 /* Intel 80386+ */
+#define EM_68K 4 /* Motorola m68k family */
+#define EM_88K 5 /* Motorola m88k family */
+#define EM_486 6 /* Perhaps disused */
+#define EM_860 7 /* Intel 80860 */
+#define EM_MIPS 8 /* MIPS R3000 big-endian */
+#define EM_S370 9 /* IBM System/370 */
+#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */
+
+#define EM_PARISC 15 /* HPPA */
+#define EM_VPP500 17 /* Fujitsu VPP500 */
+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
+#define EM_960 19 /* Intel 80960 */
+#define EM_PPC 20 /* PowerPC */
+#define EM_PPC64 21 /* PowerPC 64-bit */
+#define EM_S390 22 /* IBM S390 */
+
+#define EM_V800 36 /* NEC V800 series */
+#define EM_FR20 37 /* Fujitsu FR20 */
+#define EM_RH32 38 /* TRW RH-32 */
+#define EM_RCE 39 /* Motorola RCE */
+#define EM_ARM 40 /* ARM */
+#define EM_FAKE_ALPHA 41 /* Digital Alpha */
+#define EM_SH 42 /* Hitachi SH */
+#define EM_SPARCV9 43 /* SPARC v9 64-bit */
+#define EM_TRICORE 44 /* Siemens Tricore */
+#define EM_ARC 45 /* Argonaut RISC Core */
+#define EM_H8_300 46 /* Hitachi H8/300 */
+#define EM_H8_300H 47 /* Hitachi H8/300H */
+#define EM_H8S 48 /* Hitachi H8S */
+#define EM_H8_500 49 /* Hitachi H8/500 */
+#define EM_IA_64 50 /* Intel Merced */
+#define EM_MIPS_X 51 /* Stanford MIPS-X */
+#define EM_COLDFIRE 52 /* Motorola Coldfire */
+#define EM_68HC12 53 /* Motorola M68HC12 */
+#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/
+#define EM_PCP 55 /* Siemens PCP */
+#define EM_NCPU 56 /* Sony nCPU embeeded RISC */
+#define EM_NDR1 57 /* Denso NDR1 microprocessor */
+#define EM_STARCORE 58 /* Motorola Start*Core processor */
+#define EM_ME16 59 /* Toyota ME16 processor */
+#define EM_ST100 60 /* STMicroelectronic ST100 processor */
+#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/
+#define EM_X86_64 62 /* AMD x86-64 architecture */
+#define EM_PDSP 63 /* Sony DSP Processor */
+
+#define EM_FX66 66 /* Siemens FX66 microcontroller */
+#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */
+#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */
+#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */
+#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */
+#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */
+#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */
+#define EM_SVX 73 /* Silicon Graphics SVx */
+#define EM_AT19 74 /* STMicroelectronics ST19 8 bit mc */
+#define EM_VAX 75 /* Digital VAX */
+#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */
+#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */
+#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */
+#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */
+#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */
+#define EM_HUANY 81 /* Harvard University machine-independent object files */
+#define EM_PRISM 82 /* SiTera Prism */
+#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */
+#define EM_FR30 84 /* Fujitsu FR30 */
+#define EM_D10V 85 /* Mitsubishi D10V */
+#define EM_D30V 86 /* Mitsubishi D30V */
+#define EM_V850 87 /* NEC v850 */
+#define EM_M32R 88 /* Mitsubishi M32R */
+#define EM_MN10300 89 /* Matsushita MN10300 */
+#define EM_MN10200 90 /* Matsushita MN10200 */
+#define EM_PJ 91 /* picoJava */
+#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */
+#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */
+#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */
+#define EM_NUM 95
+
+/* Values for p_type. */
+#define PT_NULL 0 /* Unused entry. */
+#define PT_LOAD 1 /* Loadable segment. */
+#define PT_DYNAMIC 2 /* Dynamic linking information segment. */
+#define PT_INTERP 3 /* Pathname of interpreter. */
+#define PT_NOTE 4 /* Auxiliary information. */
+#define PT_SHLIB 5 /* Reserved (not used). */
+#define PT_PHDR 6 /* Location of program header itself. */
+
+/* Values for p_flags. */
+#define PF_X 0x1 /* Executable. */
+#define PF_W 0x2 /* Writable. */
+#define PF_R 0x4 /* Readable. */
+
+
+#define ELF_PROGRAM_RETURNS_BIT 0x8000000 /* e_flags bit 31 */
+
+#define EI_MAG0 0
+#define ELFMAG0 0x7f
+
+#define EI_MAG1 1
+#define ELFMAG1 'E'
+
+#define EI_MAG2 2
+#define ELFMAG2 'L'
+
+#define EI_MAG3 3
+#define ELFMAG3 'F'
+
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+
+#define EI_CLASS 4 /* File class byte index */
+#define ELFCLASSNONE 0 /* Invalid class */
+#define ELFCLASS32 1 /* 32-bit objects */
+#define ELFCLASS64 2 /* 64-bit objects */
+
+#define EI_DATA 5 /* Data encodeing byte index */
+#define ELFDATANONE 0 /* Invalid data encoding */
+#define ELFDATA2LSB 1 /* 2's complement little endian */
+#define ELFDATA2MSB 2 /* 2's complement big endian */
+
+#define EI_VERSION 6 /* File version byte index */
+ /* Value must be EV_CURRENT */
+
+#define EV_NONE 0 /* Invalid ELF Version */
+#define EV_CURRENT 1 /* Current version */
+
+#define ELF32_PHDR_SIZE (8*4) /* Size of an elf program header */
+
+#ifndef ASSEMBLY
+
+#include <stdint.h>
+
+/*
+ * ELF definitions common to all 32-bit architectures.
+ */
+
+typedef uint32_t Elf32_Addr;
+typedef uint16_t Elf32_Half;
+typedef uint32_t Elf32_Off;
+typedef int32_t Elf32_Sword;
+typedef uint32_t Elf32_Word;
+typedef uint32_t Elf32_Size;
+
+typedef uint64_t Elf64_Addr;
+typedef uint16_t Elf64_Half;
+typedef uint64_t Elf64_Off;
+typedef int32_t Elf64_Sword;
+typedef uint32_t Elf64_Word;
+typedef uint64_t Elf64_Size;
+
+/*
+ * ELF header.
+ */
+typedef struct {
+ unsigned char e_ident[EI_NIDENT]; /* File identification. */
+ Elf32_Half e_type; /* File type. */
+ Elf32_Half e_machine; /* Machine architecture. */
+ Elf32_Word e_version; /* ELF format version. */
+ Elf32_Addr e_entry; /* Entry point. */
+ Elf32_Off e_phoff; /* Program header file offset. */
+ Elf32_Off e_shoff; /* Section header file offset. */
+ Elf32_Word e_flags; /* Architecture-specific flags. */
+ Elf32_Half e_ehsize; /* Size of ELF header in bytes. */
+ Elf32_Half e_phentsize; /* Size of program header entry. */
+ Elf32_Half e_phnum; /* Number of program header entries. */
+ Elf32_Half e_shentsize; /* Size of section header entry. */
+ Elf32_Half e_shnum; /* Number of section header entries. */
+ Elf32_Half e_shstrndx; /* Section name strings section. */
+} Elf32_Ehdr;
+
+typedef struct {
+ unsigned char e_ident[EI_NIDENT]; /* File identification. */
+ Elf64_Half e_type; /* File type. */
+ Elf64_Half e_machine; /* Machine architecture. */
+ Elf64_Word e_version; /* ELF format version. */
+ Elf64_Addr e_entry; /* Entry point. */
+ Elf64_Off e_phoff; /* Program header file offset. */
+ Elf64_Off e_shoff; /* Section header file offset. */
+ Elf64_Word e_flags; /* Architecture-specific flags. */
+ Elf64_Half e_ehsize; /* Size of ELF header in bytes. */
+ Elf64_Half e_phentsize; /* Size of program header entry. */
+ Elf64_Half e_phnum; /* Number of program header entries. */
+ Elf64_Half e_shentsize; /* Size of section header entry. */
+ Elf64_Half e_shnum; /* Number of section header entries. */
+ Elf64_Half e_shstrndx; /* Section name strings section. */
+} Elf64_Ehdr;
+
+/*
+ * Program header.
+ */
+typedef struct {
+ Elf32_Word p_type; /* Entry type. */
+ Elf32_Off p_offset; /* File offset of contents. */
+ Elf32_Addr p_vaddr; /* Virtual address (not used). */
+ Elf32_Addr p_paddr; /* Physical address. */
+ Elf32_Size p_filesz; /* Size of contents in file. */
+ Elf32_Size p_memsz; /* Size of contents in memory. */
+ Elf32_Word p_flags; /* Access permission flags. */
+ Elf32_Size p_align; /* Alignment in memory and file. */
+} Elf32_Phdr;
+
+typedef struct {
+ Elf64_Word p_type; /* Entry type. */
+ Elf64_Word p_flags; /* Access permission flags. */
+ Elf64_Off p_offset; /* File offset of contents. */
+ Elf64_Addr p_vaddr; /* Virtual address (not used). */
+ Elf64_Addr p_paddr; /* Physical address. */
+ Elf64_Size p_filesz; /* Size of contents in file. */
+ Elf64_Size p_memsz; /* Size of contents in memory. */
+ Elf64_Size p_align; /* Alignment in memory and file. */
+} Elf64_Phdr;
+
+/* Standardized Elf image notes for booting... The name for all of these is ELFBoot */
+
+
+/* ELF Defines for the current architecture */
+#include "bits/elf.h"
+
+#endif /* ASSEMBLY */
+
+#endif /* ELF_H */
diff --git a/gpxe/src/include/endian.h b/gpxe/src/include/endian.h
new file mode 100644
index 00000000..32006224
--- /dev/null
+++ b/gpxe/src/include/endian.h
@@ -0,0 +1,19 @@
+#ifndef ETHERBOOT_ENDIAN_H
+#define ETHERBOOT_ENDIAN_H
+
+/* Definitions for byte order, according to significance of bytes,
+ from low addresses to high addresses. The value is what you get by
+ putting '4' in the most significant byte, '3' in the second most
+ significant byte, '2' in the second least significant byte, and '1'
+ in the least significant byte, and then writing down one digit for
+ each byte, starting with the byte at the lowest address at the left,
+ and proceeding to the byte with the highest address at the right. */
+
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#define __PDP_ENDIAN 3412
+
+#include "bits/endian.h"
+
+
+#endif /* ETHERBOOT_ENDIAN_H */
diff --git a/gpxe/src/include/errno.h b/gpxe/src/include/errno.h
new file mode 100644
index 00000000..58dff1fd
--- /dev/null
+++ b/gpxe/src/include/errno.h
@@ -0,0 +1,508 @@
+#ifndef ERRNO_H
+#define ERRNO_H
+
+/** @file
+ *
+ * Error codes
+ *
+ * Return status codes as used within gPXE are designed to allow for
+ * maximum visibility into the source of an error even in an end-user
+ * build with no debugging. They are constructed as follows:
+ *
+ * Bits 7-0 : PXE error code
+ *
+ * This is the closest equivalent PXE error code
+ * (e.g. PXENV_STATUS_OUT_OF_RESOURCES), and is the only part of the
+ * error that will be returned via the PXE API, since PXE has
+ * predefined error codes.
+ *
+ * Bits 12-8 : Per-file disambiguator
+ *
+ * When the same error number can be generated from multiple points
+ * within a file, this field can be used to identify the unique
+ * instance.
+ *
+ * Bits 23-13 : File identifier
+ *
+ * This is a unique identifier for the file generating the error
+ * (e.g. ERRFILE_tcp for tcp.c).
+ *
+ * Bits 30-24 : POSIX error code
+ *
+ * This is the closest equivalent POSIX error code (e.g. ENOMEM).
+ *
+ * Bit 31 : Reserved
+ *
+ * Errors are usually return as negative error numbers (e.g. -EINVAL);
+ * bit 31 is therefore unusable.
+ *
+ *
+ * The convention within the code is that errors are negative and
+ * expressed using the POSIX error code and (optionally) a per-file
+ * disambiguator, e.g.
+ *
+ * return -EINVAL;
+ *
+ * or
+ *
+ * #define ETCP_BAD_CHECKSUM EUNIQ_02
+ * return -( EINVAL | ETCP_BAD_CHECKSUM )
+ *
+ * By various bits of preprocessor magic, the PXE error code and file
+ * identifier are already incorporated into the definition of the
+ * POSIX error code, which keeps the code relatively clean.
+ *
+ *
+ * Functions that wish to return failures should be declared as
+ * returning an integer @c rc "Return status code". A return value of
+ * zero indicates success, a non-zero value indicates failure. The
+ * return value can be passed directly to strerror() in order to
+ * generate a human-readable error message, e.g.
+ *
+ * if ( ( rc = some_function ( ... ) ) != 0 ) {
+ * DBG ( "Whatever I was trying to do failed: %s\n", strerror ( rc ) );
+ * return rc;
+ * }
+ *
+ * As illustrated in the above example, error returns should generally
+ * be directly propagated upward to the calling function.
+ *
+ */
+
+/* Get definitions for file identifiers */
+#include <gpxe/errfile.h>
+
+/* If we do not have a valid file identifier, generate a compiler
+ * warning upon usage of any error codes. (Don't just use a #warning,
+ * because some files include errno.h but don't ever actually use any
+ * error codes.)
+ */
+#if ! ERRFILE
+extern char missing_errfile_declaration[] __attribute__ (( deprecated ));
+#undef ERRFILE
+#define ERRFILE ( 0 * ( ( int ) missing_errfile_declaration ) )
+#endif
+
+/** Derive PXENV_STATUS code from gPXE error number */
+#define PXENV_STATUS( rc ) ( (-(rc)) & 0x00ff )
+
+/**
+ * @defgroup pxeerrors PXE error codes
+ *
+ * The names, meanings and values of these error codes are defined by
+ * the PXE specification.
+ *
+ * @{
+ */
+
+/* Generic errors */
+#define PXENV_STATUS_SUCCESS 0x0000
+#define PXENV_STATUS_FAILURE 0x0001
+#define PXENV_STATUS_BAD_FUNC 0x0002
+#define PXENV_STATUS_UNSUPPORTED 0x0003
+#define PXENV_STATUS_KEEP_UNDI 0x0004
+#define PXENV_STATUS_KEEP_ALL 0x0005
+#define PXENV_STATUS_OUT_OF_RESOURCES 0x0006
+
+/* ARP errors (0x0010 to 0x001f) */
+#define PXENV_STATUS_ARP_TIMEOUT 0x0011
+
+/* Base-Code state errors */
+#define PXENV_STATUS_UDP_CLOSED 0x0018
+#define PXENV_STATUS_UDP_OPEN 0x0019
+#define PXENV_STATUS_TFTP_CLOSED 0x001a
+#define PXENV_STATUS_TFTP_OPEN 0x001b
+
+/* BIOS/system errors (0x0020 to 0x002f) */
+#define PXENV_STATUS_MCOPY_PROBLEM 0x0020
+#define PXENV_STATUS_BIS_INTEGRITY_FAILURE 0x0021
+#define PXENV_STATUS_BIS_VALIDATE_FAILURE 0x0022
+#define PXENV_STATUS_BIS_INIT_FAILURE 0x0023
+#define PXENV_STATUS_BIS_SHUTDOWN_FAILURE 0x0024
+#define PXENV_STATUS_BIS_GBOA_FAILURE 0x0025
+#define PXENV_STATUS_BIS_FREE_FAILURE 0x0026
+#define PXENV_STATUS_BIS_GSI_FAILURE 0x0027
+#define PXENV_STATUS_BIS_BAD_CKSUM 0x0028
+
+/* TFTP/MTFTP errors (0x0030 to 0x003f) */
+#define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS 0x0030
+#define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x0032
+#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x0033
+#define PXENV_STATUS_TFTP_READ_TIMEOUT 0x0035
+#define PXENV_STATUS_TFTP_ERROR_OPCODE 0x0036
+#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION 0x0038
+#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION 0x0039
+#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES 0x003a
+#define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x003b
+#define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x003c
+#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x003d
+#define PXENV_STATUS_TFTP_NO_FILESIZE 0x003e
+#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE 0x003f
+
+/* Reserved errors 0x0040 to 0x004f) */
+
+/* DHCP/BOOTP errors (0x0050 to 0x005f) */
+#define PXENV_STATUS_DHCP_TIMEOUT 0x0051
+#define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x0052
+#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x0053
+#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x0054
+
+/* Driver errors (0x0060 to 0x006f) */
+#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x0060
+#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x0061
+#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x0062
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x0063
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x0064
+#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x0065
+#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x0066
+#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x0067
+#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM 0x0068
+#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x0069
+#define PXENV_STATUS_UNDI_INVALID_STATE 0x006a
+#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x006b
+#define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x006c
+
+/* ROM and NBP bootstrap errors (0x0070 to 0x007f) */
+#define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x0074
+#define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x0076
+#define PXENV_STATUS_BSTRAP_MISSING_LIST 0x0077
+#define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x0078
+#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x0079
+
+/* Environment NBP errors (0x0080 to 0x008f) */
+
+/* Reserved errors (0x0090 to 0x009f) */
+
+/* Miscellaneous errors (0x00a0 to 0x00af) */
+#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE 0x00a0
+#define PXENV_STATUS_BINL_NO_PXE_SERVER 0x00a1
+#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE 0x00a2
+#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE 0x00a3
+
+/* BUSD errors (0x00b0 to 0x00bf) */
+#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED 0x00b0
+
+/* Loader errors (0x00c0 to 0x00cf) */
+#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY 0x00c0
+#define PXENV_STATUS_LOADER_NO_BC_ROMID 0x00c1
+#define PXENV_STATUS_LOADER_BAD_BC_ROMID 0x00c2
+#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE 0x00c3
+#define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0x00c4
+#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0x00c5
+#define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE 0x00c6
+#define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0x00c8
+#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT 0x00c9
+#define PXENV_STATUS_LOADER_UNDI_START 0x00ca
+#define PXENV_STATUS_LOADER_BC_START 0x00cb
+
+/** @} */
+
+/**
+ * @defgroup posixerrors POSIX error codes
+ *
+ * The names and meanings (but not the values) of these error codes
+ * are defined by POSIX. We choose to assign unique values which
+ * incorporate the closest equivalent PXE error code, so that code may
+ * simply use ENOMEM, rather than having to use the cumbersome
+ * (ENOMEM|PXENV_STATUS_OUT_OF_RESOURCES).
+ *
+ * @{
+ */
+
+/** Operation completed successfully */
+#define ENOERR ( ERRFILE | PXENV_STATUS_SUCCESS | 0x00000000 )
+
+/** Arg list too long */
+#define E2BIG ( ERRFILE | PXENV_STATUS_BAD_FUNC | 0x01000000 )
+
+/** Permission denied */
+#define EACCES ( ERRFILE | PXENV_STATUS_TFTP_ACCESS_VIOLATION | 0x02000000 )
+
+/** Address in use */
+#define EADDRINUSE ( ERRFILE | PXENV_STATUS_UDP_OPEN | 0x03000000 )
+
+/** Address not available */
+#define EADDRNOTAVAIL ( ERRFILE | PXENV_STATUS_UDP_OPEN | 0x04000000 )
+
+/** Address family not supported */
+#define EAFNOSUPPORT ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x05000000 )
+
+/** Resource temporarily unavailable */
+#define EAGAIN ( ERRFILE | PXENV_STATUS_FAILURE | 0x06000000 )
+
+/** Connection already in progress */
+#define EALREADY ( ERRFILE | PXENV_STATUS_UDP_OPEN | 0x07000000 )
+
+/** Bad file descriptor */
+#define EBADF ( ERRFILE | PXENV_STATUS_TFTP_CLOSED | 0x08000000 )
+
+/** Bad message */
+#define EBADMSG ( ERRFILE | PXENV_STATUS_FAILURE | 0x09000000 )
+
+/** Resource busy */
+#define EBUSY ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x0a000000 )
+
+/** Operation canceled */
+#define ECANCELED \
+ ( ERRFILE | PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE | 0x0b000000 )
+
+/** No child processes */
+#define ECHILD ( ERRFILE | PXENV_STATUS_TFTP_FILE_NOT_FOUND | 0x0c000000 )
+
+/** Connection aborted */
+#define ECONNABORTED \
+ ( ERRFILE | PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION | 0x0d000000 )
+
+/** Connection refused */
+#define ECONNREFUSED \
+ ( ERRFILE | PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION | 0x0e000000 )
+
+/** Connection reset */
+#define ECONNRESET \
+ ( ERRFILE | PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION | 0x0f000000 )
+
+/** Resource deadlock avoided */
+#define EDEADLK ( ERRFILE | PXENV_STATUS_FAILURE | 0x10000000 )
+
+/** Destination address required */
+#define EDESTADDRREQ ( ERRFILE | PXENV_STATUS_BAD_FUNC | 0x11000000 )
+
+/** Domain error */
+#define EDOM ( ERRFILE | PXENV_STATUS_FAILURE | 0x12000000 )
+
+/** Reserved */
+#define EDQUOT ( ERRFILE | PXENV_STATUS_FAILURE | 0x13000000 )
+
+/** File exists */
+#define EEXIST ( ERRFILE | PXENV_STATUS_FAILURE | 0x14000000 )
+
+/** Bad address */
+#define EFAULT ( ERRFILE | PXENV_STATUS_MCOPY_PROBLEM | 0x15000000 )
+
+/** File too large */
+#define EFBIG ( ERRFILE | PXENV_STATUS_MCOPY_PROBLEM | 0x16000000 )
+
+/** Host is unreachable */
+#define EHOSTUNREACH ( ERRFILE | PXENV_STATUS_ARP_TIMEOUT | 0x17000000 )
+
+/** Identifier removed */
+#define EIDRM ( ERRFILE | PXENV_STATUS_FAILURE | 0x18000000 )
+
+/** Illegal byte sequence */
+#define EILSEQ ( ERRFILE | PXENV_STATUS_FAILURE | 0x19000000 )
+
+/** Operation in progress */
+#define EINPROGRESS ( ERRFILE | PXENV_STATUS_FAILURE | 0x1a000000 )
+
+/** Interrupted function call */
+#define EINTR ( ERRFILE | PXENV_STATUS_FAILURE | 0x1b000000 )
+
+/** Invalid argument */
+#define EINVAL ( ERRFILE | PXENV_STATUS_BAD_FUNC | 0x1c000000 )
+
+/** Input/output error */
+#define EIO \
+ ( ERRFILE | PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION | 0x1d000000 )
+
+/** Socket is connected */
+#define EISCONN ( ERRFILE | PXENV_STATUS_UDP_OPEN | 0x1e000000 )
+
+/** Is a directory */
+#define EISDIR ( ERRFILE | PXENV_STATUS_FAILURE | 0x1f000000 )
+
+/** Too many levels of symbolic links */
+#define ELOOP ( ERRFILE | PXENV_STATUS_FAILURE | 0x20000000 )
+
+/** Too many open files */
+#define EMFILE ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x21000000 )
+
+/** Too many links */
+#define EMLINK ( ERRFILE | PXENV_STATUS_FAILURE | 0x22000000 )
+
+/** Inappropriate message buffer length */
+#define EMSGSIZE ( ERRFILE | PXENV_STATUS_BAD_FUNC | 0x23000000 )
+
+/** Reserved */
+#define EMULTIHOP ( ERRFILE | PXENV_STATUS_FAILURE | 0x24000000 )
+
+/** Filename too long */
+#define ENAMETOOLONG ( ERRFILE | PXENV_STATUS_FAILURE | 0x25000000 )
+
+/** Network is down */
+#define ENETDOWN ( ERRFILE | PXENV_STATUS_ARP_TIMEOUT | 0x26000000 )
+
+/** Connection aborted by network */
+#define ENETRESET ( ERRFILE | PXENV_STATUS_FAILURE | 0x27000000 )
+
+/** Network unreachable */
+#define ENETUNREACH ( ERRFILE | PXENV_STATUS_ARP_TIMEOUT | 0x28000000 )
+
+/** Too many open files in system */
+#define ENFILE ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x29000000 )
+
+/** No buffer space available */
+#define ENOBUFS ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x2a000000 )
+
+/** No message is available on the STREAM head read queue */
+#define ENODATA ( ERRFILE | PXENV_STATUS_FAILURE | 0x2b000000 )
+
+/** No such device */
+#define ENODEV ( ERRFILE | PXENV_STATUS_TFTP_FILE_NOT_FOUND | 0x2c000000 )
+
+/** No such file or directory */
+#define ENOENT ( ERRFILE | PXENV_STATUS_TFTP_FILE_NOT_FOUND | 0x2d000000 )
+
+/** Exec format error */
+#define ENOEXEC ( ERRFILE | PXENV_STATUS_FAILURE | 0x2e000000 )
+
+/** No locks available */
+#define ENOLCK ( ERRFILE | PXENV_STATUS_FAILURE | 0x2f000000 )
+
+/** Reserved */
+#define ENOLINK ( ERRFILE | PXENV_STATUS_FAILURE | 0x30000000 )
+
+/** Not enough space */
+#define ENOMEM ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x31000000 )
+
+/** No message of the desired type */
+#define ENOMSG ( ERRFILE | PXENV_STATUS_FAILURE | 0x32000000 )
+
+/** Protocol not available */
+#define ENOPROTOOPT ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x33000000 )
+
+/** No space left on device */
+#define ENOSPC ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x34000000 )
+
+/** No STREAM resources */
+#define ENOSR ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x35000000 )
+
+/** Not a STREAM */
+#define ENOSTR ( ERRFILE | PXENV_STATUS_FAILURE | 0x36000000 )
+
+/** Function not implemented */
+#define ENOSYS ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x37000000 )
+
+/** The socket is not connected */
+#define ENOTCONN ( ERRFILE | PXENV_STATUS_FAILURE | 0x38000000 )
+
+/** Not a directory */
+#define ENOTDIR ( ERRFILE | PXENV_STATUS_FAILURE | 0x39000000 )
+
+/** Directory not empty */
+#define ENOTEMPTY ( ERRFILE | PXENV_STATUS_FAILURE | 0x3a000000 )
+
+/** Not a socket */
+#define ENOTSOCK ( ERRFILE | PXENV_STATUS_FAILURE | 0x3b000000 )
+
+/** Not supported */
+#define ENOTSUP ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x3c000000 )
+
+/** Inappropriate I/O control operation */
+#define ENOTTY ( ERRFILE | PXENV_STATUS_FAILURE | 0x3d000000 )
+
+/** No such device or address */
+#define ENXIO ( ERRFILE | PXENV_STATUS_TFTP_FILE_NOT_FOUND | 0x3e000000 )
+
+/** Operation not supported on socket */
+#define EOPNOTSUPP ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x3f000000 )
+
+/** Value too large to be stored in data type */
+#define EOVERFLOW ( ERRFILE | PXENV_STATUS_FAILURE | 0x40000000 )
+
+/** Operation not permitted */
+#define EPERM ( ERRFILE | PXENV_STATUS_TFTP_ACCESS_VIOLATION | 0x41000000 )
+
+/** Broken pipe */
+#define EPIPE ( ERRFILE | PXENV_STATUS_FAILURE | 0x42000000 )
+
+/** Protocol error */
+#define EPROTO ( ERRFILE | PXENV_STATUS_FAILURE | 0x43000000 )
+
+/** Protocol not supported */
+#define EPROTONOSUPPORT ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x44000000 )
+
+/** Protocol wrong type for socket */
+#define EPROTOTYPE ( ERRFILE | PXENV_STATUS_FAILURE | 0x45000000 )
+
+/** Result too large */
+#define ERANGE ( ERRFILE | PXENV_STATUS_FAILURE | 0x46000000 )
+
+/** Read-only file system */
+#define EROFS ( ERRFILE | PXENV_STATUS_FAILURE | 0x47000000 )
+
+/** Invalid seek */
+#define ESPIPE ( ERRFILE | PXENV_STATUS_FAILURE | 0x48000000 )
+
+/** No such process */
+#define ESRCH ( ERRFILE | PXENV_STATUS_TFTP_FILE_NOT_FOUND | 0x49000000 )
+
+/** Stale file handle */
+#define ESTALE ( ERRFILE | PXENV_STATUS_FAILURE | 0x4a000000 )
+
+/** STREAM ioctl() timeout */
+#define ETIME ( ERRFILE | PXENV_STATUS_FAILURE | 0x4b000000 )
+
+/** Operation timed out */
+#define ETIMEDOUT ( ERRFILE | PXENV_STATUS_TFTP_READ_TIMEOUT | 0x4c000000 )
+
+/** Text file busy */
+#define ETXTBSY ( ERRFILE | PXENV_STATUS_FAILURE | 0x4d000000 )
+
+/** Operation would block (different from EAGAIN!) */
+#define EWOULDBLOCK ( ERRFILE | PXENV_STATUS_TFTP_OPEN | 0x4e000000 )
+
+/** Improper link */
+#define EXDEV ( ERRFILE | PXENV_STATUS_FAILURE | 0x4f000000 )
+
+/** @} */
+
+/**
+ * @defgroup euniq Per-file error disambiguators
+ *
+ * Files which use the same error number multiple times should
+ * probably define their own error subspace using these
+ * disambiguators. For example:
+ *
+ * #define ETCP_HEADER_TOO_SHORT EUNIQ_01
+ * #define ETCP_BAD_CHECKSUM EUNIQ_02
+ *
+ * @{
+ */
+
+#define EUNIQ_01 0x00000100
+#define EUNIQ_02 0x00000200
+#define EUNIQ_03 0x00000300
+#define EUNIQ_04 0x00000400
+#define EUNIQ_05 0x00000500
+#define EUNIQ_06 0x00000600
+#define EUNIQ_07 0x00000700
+#define EUNIQ_08 0x00000800
+#define EUNIQ_09 0x00000900
+#define EUNIQ_0A 0x00000a00
+#define EUNIQ_0B 0x00000b00
+#define EUNIQ_0C 0x00000c00
+#define EUNIQ_0D 0x00000d00
+#define EUNIQ_0E 0x00000e00
+#define EUNIQ_0F 0x00000f00
+#define EUNIQ_10 0x00001000
+#define EUNIQ_11 0x00001100
+#define EUNIQ_12 0x00001200
+#define EUNIQ_13 0x00001300
+#define EUNIQ_14 0x00001400
+#define EUNIQ_15 0x00001500
+#define EUNIQ_16 0x00001600
+#define EUNIQ_17 0x00001700
+#define EUNIQ_18 0x00001800
+#define EUNIQ_19 0x00001900
+#define EUNIQ_1A 0x00001a00
+#define EUNIQ_1B 0x00001b00
+#define EUNIQ_1C 0x00001c00
+#define EUNIQ_1D 0x00001d00
+#define EUNIQ_1E 0x00001e00
+#define EUNIQ_1F 0x00001f00
+
+/** @} */
+
+extern int errno;
+
+#endif /* ERRNO_H */
diff --git a/gpxe/src/include/etherboot.h b/gpxe/src/include/etherboot.h
new file mode 100644
index 00000000..2a465954
--- /dev/null
+++ b/gpxe/src/include/etherboot.h
@@ -0,0 +1,42 @@
+#ifndef ETHERBOOT_H
+#define ETHERBOOT_H
+
+/*
+ * Standard includes that we always want
+ *
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <strings.h>
+#include <console.h>
+#include <gpxe/timer.h>
+#include <gpxe/if_arp.h>
+#include <gpxe/if_ether.h>
+
+typedef unsigned long Address;
+
+/*
+ * IMPORTANT!!!!!!!!!!!!!!
+ *
+ * Everything below this point is cruft left over from older versions
+ * of Etherboot. Do not add *anything* below this point. Things are
+ * gradually being moved to individual header files.
+ *
+ */
+
+/* Link configuration time in tenths of a second */
+#ifndef VALID_LINK_TIMEOUT
+#define VALID_LINK_TIMEOUT 100 /* 10.0 seconds */
+#endif
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
+#endif /* ETHERBOOT_H */
diff --git a/gpxe/src/include/fs.h b/gpxe/src/include/fs.h
new file mode 100644
index 00000000..1dfe8fd9
--- /dev/null
+++ b/gpxe/src/include/fs.h
@@ -0,0 +1,41 @@
+#ifndef FS_H
+#define FS_H
+
+#include <stdint.h>
+
+//typedef uint64_t sector_t;
+
+#ifdef IDE_DISK
+int ide_probe(int drive);
+int ide_read(int drive, sector_t sector, void *buffer);
+#endif
+
+#ifdef USB_DISK
+int usb_probe(int drive);
+int usb_read(int drive, sector_t sector, void *buffer);
+#endif
+
+#define DISK_IDE 1
+#define DISK_MEM 2
+#define DISK_USB 3
+
+int devopen(const char *name, int *reopen);
+int devread(unsigned long sector, unsigned long byte_offset,
+ unsigned long byte_len, void *buf);
+
+int file_open(const char *filename);
+int file_read(void *buf, unsigned long len);
+int file_seek(unsigned long offset);
+unsigned long file_size(void);
+
+#define PARTITION_UNKNOWN 0xbad6a7
+
+#ifdef ELTORITO
+int open_eltorito_image(int part, unsigned long *start, unsigned long *length);
+#else
+# define open_eltorito_image(x,y,z) PARTITION_UNKNOWN
+#endif
+
+extern int using_devsize;
+
+#endif /* FS_H */
diff --git a/gpxe/src/include/getopt.h b/gpxe/src/include/getopt.h
new file mode 100644
index 00000000..2505223e
--- /dev/null
+++ b/gpxe/src/include/getopt.h
@@ -0,0 +1,92 @@
+#ifndef _GETOPT_H
+#define _GETOPT_H
+
+/** @file
+ *
+ * Parse command-line options
+ *
+ */
+
+#include <stddef.h>
+
+enum getopt_argument_requirement {
+ /** Option does not take an argument */
+ no_argument = 0,
+ /** Option requires an argument */
+ required_argument = 1,
+ /** Option may have an argument */
+ optional_argument = 2,
+};
+
+/** A long option, as used for getopt_long() */
+struct option {
+ /** Long name of this option */
+ const char *name;
+ /** Option takes an argument
+ *
+ * Must be one of @c no_argument, @c required_argument, or @c
+ * optional_argument.
+ */
+ int has_arg;
+ /** Location into which to store @c val, or NULL.
+ *
+ * See the description for @c val for more details.
+ */
+ int *flag;
+ /** Value to return
+ *
+ * If @c flag is NULL, then this is the value that will be
+ * returned by getopt_long() when this option is found, and
+ * should therefore be set to the equivalent short option
+ * character.
+ *
+ * If @c flag is non-NULL, then this value will be written to
+ * the location pointed to by @flag, and getopt_long() will
+ * return 0.
+ */
+ int val;
+};
+
+extern char *optarg;
+extern int optind;
+extern int nextchar;
+extern int optopt;
+
+extern int getopt_long ( int argc, char * const argv[], const char *optstring,
+ const struct option *longopts, int *longindex );
+
+/**
+ * Parse command-line options
+ *
+ * @v argv Argument count
+ * @v argv Argument list
+ * @v optstring Option specification string
+ * @ret option Option found, or -1 for no more options
+ *
+ * See getopt_long() for full details.
+ */
+static inline int getopt ( int argc, char * const argv[],
+ const char *optstring ) {
+ static const struct option no_options[] = {
+ { NULL, 0, NULL, 0 }
+ };
+ return getopt_long ( argc, argv, optstring, no_options, NULL );
+}
+
+/**
+ * Reset getopt() internal state
+ *
+ * Due to a limitation of the POSIX getopt() API, it is necessary to
+ * add a call to reset_getopt() before each set of calls to getopt()
+ * or getopt_long(). This arises because POSIX assumes that each
+ * process will parse command line arguments no more than once; this
+ * assumption is not valid within Etherboot. We work around the
+ * limitation by arranging for execv() to call reset_getopt() before
+ * executing the command.
+ */
+static inline void reset_getopt ( void ) {
+ optind = 1;
+ nextchar = 0;
+}
+
+#endif /* _GETOPT_H */
diff --git a/gpxe/src/include/gpxe/abft.h b/gpxe/src/include/gpxe/abft.h
new file mode 100644
index 00000000..1c651ef1
--- /dev/null
+++ b/gpxe/src/include/gpxe/abft.h
@@ -0,0 +1,35 @@
+#ifndef _GPXE_ABFT_H
+#define _GPXE_ABFT_H
+
+/** @file
+ *
+ * AoE boot firmware table
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/acpi.h>
+#include <gpxe/if_ether.h>
+
+/** AoE boot firmware table signature */
+#define ABFT_SIG "aBFT"
+
+/**
+ * AoE Boot Firmware Table (aBFT)
+ */
+struct abft_table {
+ /** ACPI header */
+ struct acpi_description_header acpi;
+ /** AoE shelf */
+ uint16_t shelf;
+ /** AoE slot */
+ uint8_t slot;
+ /** Reserved */
+ uint8_t reserved_a;
+ /** MAC address */
+ uint8_t mac[ETH_ALEN];
+} __attribute__ (( packed ));
+
+extern void abft_fill_data ( struct aoe_session *aoe );
+
+#endif /* _GPXE_ABFT_H */
diff --git a/gpxe/src/include/gpxe/acpi.h b/gpxe/src/include/gpxe/acpi.h
new file mode 100644
index 00000000..33b1b2b7
--- /dev/null
+++ b/gpxe/src/include/gpxe/acpi.h
@@ -0,0 +1,41 @@
+#ifndef _GPXE_ACPI_H
+#define _GPXE_ACPI_H
+
+/** @file
+ *
+ * ACPI data structures
+ *
+ */
+
+#include <stdint.h>
+
+/**
+ * An ACPI description header
+ *
+ * This is the structure common to the start of all ACPI system
+ * description tables.
+ */
+struct acpi_description_header {
+ /** ACPI signature (4 ASCII characters) */
+ char signature[4];
+ /** Length of table, in bytes, including header */
+ uint32_t length;
+ /** ACPI Specification minor version number */
+ uint8_t revision;
+ /** To make sum of entire table == 0 */
+ uint8_t checksum;
+ /** OEM identification */
+ char oem_id[6];
+ /** OEM table identification */
+ char oem_table_id[8];
+ /** OEM revision number */
+ uint32_t oem_revision;
+ /** ASL compiler vendor ID */
+ char asl_compiler_id[4];
+ /** ASL compiler revision number */
+ uint32_t asl_compiler_revision;
+} __attribute__ (( packed ));
+
+extern void acpi_fix_checksum ( struct acpi_description_header *acpi );
+
+#endif /* _GPXE_ACPI_H */
diff --git a/gpxe/src/include/gpxe/aes.h b/gpxe/src/include/gpxe/aes.h
new file mode 100644
index 00000000..75cb4c44
--- /dev/null
+++ b/gpxe/src/include/gpxe/aes.h
@@ -0,0 +1,8 @@
+#ifndef _GPXE_AES_H
+#define _GPXE_AES_H
+
+struct crypto_algorithm;
+
+extern struct crypto_algorithm aes_algorithm;
+
+#endif /* _GPXE_AES_H */
diff --git a/gpxe/src/include/gpxe/ansiesc.h b/gpxe/src/include/gpxe/ansiesc.h
new file mode 100644
index 00000000..ccc4ca65
--- /dev/null
+++ b/gpxe/src/include/gpxe/ansiesc.h
@@ -0,0 +1,118 @@
+#ifndef _GPXE_ANSIESC_H
+#define _GPXE_ANSIESC_H
+
+/** @file
+ *
+ * ANSI escape sequences
+ *
+ * ANSI X3.64 (aka ECMA-48 or ISO/IEC 6429, available from
+ * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf)
+ * defines escape sequences consisting of:
+ *
+ * A Control Sequence Introducer (CSI)
+ *
+ * Zero or more Parameter Bytes (P)
+ *
+ * Zero or more Intermediate Bytes (I)
+ *
+ * A Final Byte (F)
+ *
+ * The CSI consists of ESC (0x1b) followed by "[" (0x5b). The
+ * Parameter Bytes, for a standardised (i.e. not private or
+ * experimental) sequence, consist of a list of ASCII decimal integers
+ * separated by semicolons. The Intermediate Bytes (in the range 0x20
+ * to 0x2f) and the Final Byte (in the range 0x40 to 0x4f) determine
+ * the control function.
+ *
+ */
+
+/** A handler for an escape sequence */
+struct ansiesc_handler {
+ /** The control function identifier
+ *
+ * The control function identifier consists of the
+ * Intermediate Bytes (if any) and the Final Byte. In
+ * practice, no more than one immediate byte is ever used, so
+ * the byte combination can be efficiently expressed as a
+ * single integer, in the obvious way (with the Final Byte
+ * being the least significant byte).
+ */
+ unsigned int function;
+ /** Handle escape sequence
+ *
+ * @v count Parameter count
+ * @v params Parameter list
+ *
+ * A negative parameter value indicates that the parameter was
+ * omitted and that the default value for this control
+ * function should be used.
+ *
+ * Since all parameters are optional, there is no way to
+ * distinguish between "zero parameters" and "single parameter
+ * omitted". Consequently, the parameter list will always
+ * contain at least one item.
+ */
+ void ( * handle ) ( unsigned int count, int params[] );
+};
+
+/** Maximum number of parameters within a single escape sequence */
+#define ANSIESC_MAX_PARAMS 4
+
+/**
+ * ANSI escape sequence context
+ *
+ * This provides temporary storage for processing escape sequences,
+ * and points to the list of escape sequence handlers.
+ */
+struct ansiesc_context {
+ /** Array of handlers
+ *
+ * Must be terminated by a handler with @c function set to
+ * zero.
+ */
+ struct ansiesc_handler *handlers;
+ /** Parameter count
+ *
+ * Will be zero when not currently in an escape sequence.
+ */
+ unsigned int count;
+ /** Parameter list */
+ int params[ANSIESC_MAX_PARAMS];
+ /** Control function identifier */
+ unsigned int function;
+};
+
+/** Escape character */
+#define ESC 0x1b
+
+/** Control Sequence Introducer */
+#define CSI "\033["
+
+/**
+ * @defgroup ansifuncs ANSI escape sequence function identifiers
+ * @{
+ */
+
+/** Cursor position */
+#define ANSIESC_CUP 'H'
+
+/** Erase in page */
+#define ANSIESC_ED 'J'
+
+/** Erase from cursor to end of page */
+#define ANSIESC_ED_TO_END 0
+
+/** Erase from start of page to cursor */
+#define ANSIESC_ED_FROM_START 1
+
+/** Erase whole page */
+#define ANSIESC_ED_ALL 2
+
+/** Select graphic rendition */
+#define ANSIESC_SGR 'm'
+
+/** @} */
+
+extern int ansiesc_process ( struct ansiesc_context *ctx, int c );
+
+#endif /* _GPXE_ANSIESC_H */
diff --git a/gpxe/src/include/gpxe/aoe.h b/gpxe/src/include/gpxe/aoe.h
new file mode 100644
index 00000000..85683384
--- /dev/null
+++ b/gpxe/src/include/gpxe/aoe.h
@@ -0,0 +1,126 @@
+#ifndef _GPXE_AOE_H
+#define _GPXE_AOE_H
+
+/** @file
+ *
+ * AoE protocol
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/list.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/retry.h>
+#include <gpxe/async.h>
+#include <gpxe/ata.h>
+
+/** An AoE ATA command */
+struct aoecmd {
+ /** AoE command flags */
+ uint8_t aflags;
+ /** ATA error/feature register */
+ uint8_t err_feat;
+ /** ATA sector count register */
+ uint8_t count;
+ /** ATA command/status register */
+ uint8_t cmd_stat;
+ /** Logical block address, in little-endian order */
+ union {
+ uint64_t u64;
+ uint8_t bytes[6];
+ } lba;
+ /** Data payload */
+ uint8_t data[0];
+} __attribute__ (( packed ));
+
+#define AOE_FL_EXTENDED 0x40 /**< LBA48 extended addressing */
+#define AOE_FL_DEV_HEAD 0x10 /**< Device/head flag */
+#define AOE_FL_ASYNC 0x02 /**< Asynchronous write */
+#define AOE_FL_WRITE 0x01 /**< Write command */
+
+/** An AoE header */
+struct aoehdr {
+ /** Protocol version number and flags */
+ uint8_t ver_flags;
+ /** Error code */
+ uint8_t error;
+ /** Major device number, in network byte order */
+ uint16_t major;
+ /** Minor device number */
+ uint8_t minor;
+ /** Command number */
+ uint8_t command;
+ /** Tag, in network byte order */
+ uint32_t tag;
+ /** Payload */
+ union {
+ /** ATA command */
+ struct aoecmd command[0];
+ } arg;
+} __attribute__ (( packed ));
+
+#define AOE_VERSION 0x10 /**< Version 1 */
+#define AOE_VERSION_MASK 0xf0 /**< Version part of ver_flags field */
+
+#define AOE_FL_RESPONSE 0x08 /**< Message is a response */
+#define AOE_FL_ERROR 0x04 /**< Command generated an error */
+
+#define AOE_MAJOR_BROADCAST 0xffff
+#define AOE_MINOR_BROADCAST 0xff
+
+#define AOE_CMD_ATA 0x00 /**< Issue ATA command */
+#define AOE_CMD_CONFIG 0x01 /**< Query Config Information */
+
+#define AOE_TAG_MAGIC 0xebeb0000
+
+#define AOE_ERR_BAD_COMMAND 1 /**< Unrecognised command code */
+#define AOE_ERR_BAD_PARAMETER 2 /**< Bad argument parameter */
+#define AOE_ERR_UNAVAILABLE 3 /**< Device unavailable */
+#define AOE_ERR_CONFIG_EXISTS 4 /**< Config string present */
+#define AOE_ERR_BAD_VERSION 5 /**< Unsupported version */
+
+/** An AoE session */
+struct aoe_session {
+ /** Reference counter */
+ struct refcnt refcnt;
+
+ /** List of all AoE sessions */
+ struct list_head list;
+
+ /** Network device */
+ struct net_device *netdev;
+
+ /** Major number */
+ uint16_t major;
+ /** Minor number */
+ uint8_t minor;
+ /** Target MAC address */
+ uint8_t target[ETH_ALEN];
+
+ /** Tag for current AoE command */
+ uint32_t tag;
+
+ /** Current ATA command */
+ struct ata_command *command;
+ /** Overall status of current ATA command */
+ unsigned int status;
+ /** Byte offset within command's data buffer */
+ unsigned int command_offset;
+ /** Return status code for command */
+ int rc;
+
+ /** Retransmission timer */
+ struct retry_timer timer;
+};
+
+#define AOE_STATUS_ERR_MASK 0x0f /**< Error portion of status code */
+#define AOE_STATUS_PENDING 0x80 /**< Command pending */
+
+/** Maximum number of sectors per packet */
+#define AOE_MAX_COUNT 2
+
+extern void aoe_detach ( struct ata_device *ata );
+extern int aoe_attach ( struct ata_device *ata, struct net_device *netdev,
+ const char *root_path );
+
+#endif /* _GPXE_AOE_H */
diff --git a/gpxe/src/include/gpxe/arp.h b/gpxe/src/include/gpxe/arp.h
new file mode 100644
index 00000000..6464ce0c
--- /dev/null
+++ b/gpxe/src/include/gpxe/arp.h
@@ -0,0 +1,41 @@
+#ifndef _GPXE_ARP_H
+#define _GPXE_ARP_H
+
+/** @file
+ *
+ * Address Resolution Protocol
+ *
+ */
+
+#include <gpxe/tables.h>
+
+struct net_device;
+struct net_protocol;
+
+/** A network-layer protocol that relies upon ARP */
+struct arp_net_protocol {
+ /** Network-layer protocol */
+ struct net_protocol *net_protocol;
+ /** Check existence of address
+ *
+ * @v netdev Network device
+ * @v net_addr Network-layer address
+ * @ret rc Return status code
+ */
+ int ( * check ) ( struct net_device *netdev,
+ const void *net_addr );
+};
+
+/** Declare an ARP protocol */
+#define __arp_net_protocol \
+ __table ( struct arp_net_protocol, arp_net_protocols, 01 )
+
+extern struct net_protocol arp_protocol;
+
+extern int arp_resolve ( struct net_device *netdev,
+ struct net_protocol *net_protocol,
+ const void *dest_net_addr,
+ const void *source_net_addr,
+ void *dest_ll_addr );
+
+#endif /* _GPXE_ARP_H */
diff --git a/gpxe/src/include/gpxe/asn1.h b/gpxe/src/include/gpxe/asn1.h
new file mode 100644
index 00000000..1ad90050
--- /dev/null
+++ b/gpxe/src/include/gpxe/asn1.h
@@ -0,0 +1,32 @@
+#ifndef _GPXE_ASN1_H
+#define _GPXE_ASN1_H
+
+/** @file
+ *
+ * ASN.1 encoding
+ *
+ */
+
+#define ASN1_INTEGER 0x02
+#define ASN1_BIT_STRING 0x03
+#define ASN1_OCTET_STRING 0x04
+#define ASN1_NULL 0x05
+#define ASN1_OID 0x06
+#define ASN1_SEQUENCE 0x30
+#define ASN1_IP_ADDRESS 0x40
+#define ASN1_EXPLICIT_TAG 0xa0
+
+/**
+ * A DER-encoded ASN.1 object cursor
+ */
+struct asn1_cursor {
+ /** Start of data */
+ uint8_t *data;
+ /** Length of data */
+ size_t len;
+};
+
+extern int asn1_enter_object ( struct asn1_cursor *cursor, unsigned int type );
+extern int asn1_skip_object ( struct asn1_cursor *cursor, unsigned int type );
+
+#endif /* _GPXE_ASN1_H */
diff --git a/gpxe/src/include/gpxe/async.h b/gpxe/src/include/gpxe/async.h
new file mode 100644
index 00000000..2d1d31a4
--- /dev/null
+++ b/gpxe/src/include/gpxe/async.h
@@ -0,0 +1,228 @@
+#ifndef _GPXE_ASYNC_H
+#define _GPXE_ASYNC_H
+
+/** @file
+ *
+ * Asynchronous operations
+ *
+ */
+
+#include <gpxe/list.h>
+
+struct async;
+
+/** An asynchronous operation ID
+ *
+ * Only positive identifiers are valid; negative values are used to
+ * indicate errors.
+ */
+typedef long aid_t;
+
+/** Signals that can be delivered to asynchronous operations */
+enum signal {
+ /** A child asynchronous operation has completed
+ *
+ * The parent should call async_wait() to reap the completed
+ * child. async_wait() will return the exit status and
+ * operation identifier of the child.
+ *
+ * The handler for this signal can be set to @c NULL; if it
+ * is, then the children will accumulate as zombies until
+ * async_wait() is called.
+ *
+ * The handler for this signal can also be set to @c SIG_IGN;
+ * if it is, then the children will automatically be reaped.
+ * Note that if you use @c SIG_IGN then you will not be able
+ * to retrieve the return status of the children; the call to
+ * async_wait() will simply return -ECHILD.
+ */
+ SIGCHLD = 0,
+ /** Cancel asynchronous operation
+ *
+ * This signal should trigger the asynchronous operation to
+ * cancel itself (including killing all its own children, if
+ * any), and then call async_done(). The asynchronous
+ * operation is allowed to not complete immediately.
+ *
+ * The handler for this signal can be set to @c NULL; if it
+ * is, then attempts to cancel the asynchronous operation will
+ * fail and the operation will complete normally. Anything
+ * waiting for the operation to cancel will block.
+ */
+ SIGKILL,
+ /** Update progress of asynchronous operation
+ *
+ * This signal should cause the asynchronous operation to
+ * immediately update the @c completed and @c total fields.
+ *
+ * The handler for this signal can be set to @c NULL; if it
+ * is, then the asynchronous operation is expected to keep its
+ * @c completed and @c total fields up to date at all times.
+ */
+ SIGUPDATE,
+ SIGMAX
+};
+
+/**
+ * A signal handler
+ *
+ * @v async Asynchronous operation
+ * @v signal Signal received
+ */
+typedef void ( * signal_handler_t ) ( struct async *async,
+ enum signal signal );
+
+/** Asynchronous operation operations */
+struct async_operations {
+ /** Reap asynchronous operation
+ *
+ * @v async Asynchronous operation
+ *
+ * Release all resources associated with the asynchronous
+ * operation. This will be called only after the asynchronous
+ * operation itself calls async_done(), so the only remaining
+ * resources will probably be the memory used by the struct
+ * async itself.
+ *
+ * This method can be set to @c NULL; if it is, then no
+ * resources will be freed. This may be suitable for
+ * asynchronous operations that consume no dynamically
+ * allocated memory.
+ */
+ void ( * reap ) ( struct async *async );
+ /** Handle signals */
+ signal_handler_t signal[SIGMAX];
+};
+
+/** An asynchronous operation */
+struct async {
+ /** Other asynchronous operations with the same parent */
+ struct list_head siblings;
+ /** Child asynchronous operations */
+ struct list_head children;
+ /** Parent asynchronous operation
+ *
+ * This field is optional; if left to NULL then the owner must
+ * never call async_done().
+ */
+ struct async *parent;
+ /** Asynchronous operation ID */
+ aid_t aid;
+ /** Final return status code */
+ int rc;
+
+ /** Amount of operation completed so far
+ *
+ * The units for this quantity are arbitrary. @c completed
+ * divded by @total should give something which approximately
+ * represents the progress through the operation. For a
+ * download operation, using byte counts would make sense.
+ *
+ * This progress indicator should also incorporate the status
+ * of any child asynchronous operations.
+ */
+ unsigned long completed;
+ /** Total operation size
+ *
+ * See @c completed. A zero value means "total size unknown"
+ * and is explcitly permitted; users should take this into
+ * account before calculating @c completed/total.
+ */
+ unsigned long total;
+
+ struct async_operations *aop;
+};
+
+extern struct async_operations default_async_operations;
+extern struct async_operations orphan_async_operations;
+
+extern aid_t async_init ( struct async *async, struct async_operations *aop,
+ struct async *parent );
+extern void async_uninit ( struct async *async );
+extern void async_ignore_signal ( struct async *async, enum signal signal );
+extern void async_signal ( struct async *async, enum signal signal );
+extern void async_signal_children ( struct async *async, enum signal signal );
+extern void async_done ( struct async *async, int rc );
+extern aid_t async_wait ( struct async *async, int *rc, int block );
+
+/** Default signal handler */
+#define SIG_DFL NULL
+
+/** Ignore signal */
+#define SIG_IGN async_ignore_signal
+
+/**
+ * Initialise orphan asynchronous operation
+ *
+ * @v async Asynchronous operation
+ * @ret aid Asynchronous operation ID
+ *
+ * An orphan asynchronous operation can act as a context for child
+ * operations. However, you must not call async_done() on such an
+ * operation, since this would attempt to send a signal to its
+ * (non-existent) parent. Instead, simply free the structure (after
+ * calling async_wait() to ensure that any child operations have
+ * completed).
+ */
+static inline aid_t async_init_orphan ( struct async *async ) {
+ return async_init ( async, &orphan_async_operations, NULL );
+}
+
+/**
+ * Execute and block on an asynchronous operation
+ *
+ * @v async_temp Temporary asynchronous operation structure to use
+ * @v START Code used to start the asynchronous operation
+ * @ret rc Return status code
+ *
+ * This is a notational shorthand for writing
+ *
+ * async_init_orphan ( &async_temp );
+ * if ( ( rc = START ) == 0 )
+ * async_wait ( &async_temp );
+ * if ( rc != 0 ) {
+ * ...handle failure...
+ * }
+ *
+ * and allows you instead to write
+ *
+ * if ( ( rc = async_block ( &async_temp, START ) ) != 0 ) {
+ * ...handle failure...
+ * }
+ *
+ * The argument START is a code snippet; it should initiate an
+ * asynchronous operation as a child of @c async_temp and return an
+ * error status code if it failed to do so (e.g. due to malloc()
+ * failure).
+ */
+#define async_block( async_temp, START ) ( { \
+ int rc; \
+ \
+ async_init_orphan ( async_temp ); \
+ if ( ( rc = START ) == 0 ) \
+ async_wait ( async_temp, &rc, 1 ); \
+ rc; \
+ } )
+
+/**
+ * Execute and block on an asynchronous operation, with progress indicator
+ *
+ * @v async_temp Temporary asynchronous operation structure to use
+ * @v START Code used to start the asynchronous operation
+ * @ret rc Return status code
+ *
+ * As for async_block(), the argument START is a code snippet; it
+ * should initiate an asynchronous operation as a child of @c
+ * async_temp and return an error status code if it failed to do so
+ * (e.g. due to malloc() failure).
+ */
+#define async_block_progress( async_temp, START ) ( { \
+ int rc; \
+ \
+ async_init_orphan ( async_temp ); \
+ if ( ( rc = START ) == 0 ) \
+ async_wait_progress ( async_temp, &rc );\
+ rc; \
+ } )
+
+#endif /* _GPXE_ASYNC_H */
diff --git a/gpxe/src/include/gpxe/ata.h b/gpxe/src/include/gpxe/ata.h
new file mode 100644
index 00000000..b6da3930
--- /dev/null
+++ b/gpxe/src/include/gpxe/ata.h
@@ -0,0 +1,205 @@
+#ifndef _GPXE_ATA_H
+#define _GPXE_ATA_H
+
+#include <stdint.h>
+#include <gpxe/blockdev.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/refcnt.h>
+
+/** @file
+ *
+ * ATA devices
+ *
+ */
+
+/**
+ * An ATA Logical Block Address
+ *
+ * ATA controllers have three byte-wide registers for specifying the
+ * block address: LBA Low, LBA Mid and LBA High. This allows for a
+ * 24-bit address. Some devices support the "48-bit address feature
+ * set" (LBA48), in which case each of these byte-wide registers is
+ * actually a two-entry FIFO, and the "previous" byte pushed into the
+ * FIFO is used as the corresponding high-order byte. So, to set up
+ * the 48-bit address 0x123456abcdef, you would issue
+ *
+ * 0x56 -> LBA Low register
+ * 0xef -> LBA Low register
+ * 0x34 -> LBA Mid register
+ * 0xcd -> LBA Mid register
+ * 0x12 -> LBA High register
+ * 0xab -> LBA High register
+ *
+ * This structure encapsulates this information by providing a single
+ * 64-bit integer in native byte order, unioned with bytes named so
+ * that the sequence becomes
+ *
+ * low_prev -> LBA Low register
+ * low_cur -> LBA Low register
+ * mid_prev -> LBA Mid register
+ * mid_cur -> LBA Mid register
+ * high_prev -> LBA High register
+ * high_cur -> LBA High register
+ *
+ * Just to complicate matters further, in non-LBA48 mode it is
+ * possible to have a 28-bit address, in which case bits 27:24 must be
+ * written into the low four bits of the Device register.
+ */
+union ata_lba {
+ /** LBA as a 64-bit integer in native-endian order */
+ uint64_t native;
+ /** ATA registers */
+ struct {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t low_cur;
+ uint8_t mid_cur;
+ uint8_t high_cur;
+ uint8_t low_prev;
+ uint8_t mid_prev;
+ uint8_t high_prev;
+ uint16_t pad;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint16_t pad;
+ uint8_t high_prev;
+ uint8_t mid_prev;
+ uint8_t low_prev;
+ uint8_t high_cur;
+ uint8_t mid_cur;
+ uint8_t low_cur;
+#else
+#error "I need a byte order"
+#endif
+ } bytes;
+};
+
+/** An ATA 2-byte FIFO register */
+union ata_fifo {
+ /** Value in native-endian order */
+ uint16_t native;
+ /** ATA registers */
+ struct {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t cur;
+ uint8_t prev;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t prev;
+ uint8_t cur;
+#else
+#error "I need a byte order"
+#endif
+ } bytes;
+};
+
+/** ATA command block */
+struct ata_cb {
+ /** Logical block address */
+ union ata_lba lba;
+ /** Sector count */
+ union ata_fifo count;
+ /** Error/feature register */
+ union ata_fifo err_feat;
+ /** Device register */
+ uint8_t device;
+ /** Command/status register */
+ uint8_t cmd_stat;
+ /** LBA48 addressing flag */
+ int lba48;
+};
+
+/** Obsolete bits in the ATA device register */
+#define ATA_DEV_OBSOLETE 0xa0
+
+/** LBA flag in the ATA device register */
+#define ATA_DEV_LBA 0x40
+
+/** Slave ("device 1") flag in the ATA device register */
+#define ATA_DEV_SLAVE 0x10
+
+/** Master ("device 0") flag in the ATA device register */
+#define ATA_DEV_MASTER 0x00
+
+/** Mask of non-LBA portion of device register */
+#define ATA_DEV_MASK 0xf0
+
+/** "Read sectors" command */
+#define ATA_CMD_READ 0x20
+
+/** "Read sectors (ext)" command */
+#define ATA_CMD_READ_EXT 0x24
+
+/** "Write sectors" command */
+#define ATA_CMD_WRITE 0x30
+
+/** "Write sectors (ext)" command */
+#define ATA_CMD_WRITE_EXT 0x34
+
+/** "Identify" command */
+#define ATA_CMD_IDENTIFY 0xec
+
+/** An ATA command */
+struct ata_command {
+ /** ATA command block */
+ struct ata_cb cb;
+ /** Data-out buffer (may be NULL)
+ *
+ * If non-NULL, this buffer must be ata_command::cb::count
+ * sectors in size.
+ */
+ userptr_t data_out;
+ /** Data-in buffer (may be NULL)
+ *
+ * If non-NULL, this buffer must be ata_command::cb::count
+ * sectors in size.
+ */
+ userptr_t data_in;
+};
+
+/**
+ * Structure returned by ATA IDENTIFY command
+ *
+ * This is a huge structure with many fields that we don't care about,
+ * so we implement only a few fields.
+ */
+struct ata_identity {
+ uint16_t ignore_a[60]; /* words 0-59 */
+ uint32_t lba_sectors; /* words 60-61 */
+ uint16_t ignore_b[21]; /* words 62-82 */
+ uint16_t supports_lba48; /* word 83 */
+ uint16_t ignore_c[16]; /* words 84-99 */
+ uint64_t lba48_sectors; /* words 100-103 */
+ uint16_t ignore_d[152]; /* words 104-255 */
+};
+
+/** Supports LBA48 flag */
+#define ATA_SUPPORTS_LBA48 ( 1 << 10 )
+
+/** ATA sector size */
+#define ATA_SECTOR_SIZE 512
+
+/** An ATA device */
+struct ata_device {
+ /** Block device interface */
+ struct block_device blockdev;
+ /** Device number
+ *
+ * Must be ATA_DEV_MASTER or ATA_DEV_SLAVE.
+ */
+ int device;
+ /** LBA48 extended addressing */
+ int lba48;
+ /**
+ * Issue ATA command
+ *
+ * @v ata ATA device
+ * @v command ATA command
+ * @ret rc Return status code
+ */
+ int ( * command ) ( struct ata_device *ata,
+ struct ata_command *command );
+ /** Backing device */
+ struct refcnt *backend;
+};
+
+extern int init_atadev ( struct ata_device *ata );
+
+#endif /* _GPXE_ATA_H */
diff --git a/gpxe/src/include/gpxe/bitbash.h b/gpxe/src/include/gpxe/bitbash.h
new file mode 100644
index 00000000..62bdce00
--- /dev/null
+++ b/gpxe/src/include/gpxe/bitbash.h
@@ -0,0 +1,50 @@
+#ifndef _GPXE_BITBASH_H
+#define _GPXE_BITBASH_H
+
+/** @file
+ *
+ * Bit-bashing interfaces
+ *
+ */
+
+struct bit_basher;
+
+/** Bit-bashing operations */
+struct bit_basher_operations {
+ /**
+ * Set/clear output bit
+ *
+ * @v basher Bit-bashing interface
+ * @v bit_id Bit number
+ * @v data Value to write
+ *
+ * @c data will be 0 if a logic 0 should be written (i.e. the
+ * bit should be cleared), or -1UL if a logic 1 should be
+ * written (i.e. the bit should be set). This is done so that
+ * the method may simply binary-AND @c data with the
+ * appropriate bit mask.
+ */
+ void ( * write ) ( struct bit_basher *basher, unsigned int bit_id,
+ unsigned long data );
+ /**
+ * Read input bit
+ *
+ * @v basher Bit-bashing interface
+ * @v bit_id Bit number
+ * @ret zero Input is a logic 0
+ * @ret non-zero Input is a logic 1
+ */
+ int ( * read ) ( struct bit_basher *basher, unsigned int bit_id );
+};
+
+/** A bit-bashing interface */
+struct bit_basher {
+ /** Bit-bashing operations */
+ struct bit_basher_operations *op;
+};
+
+extern void write_bit ( struct bit_basher *basher, unsigned int bit_id,
+ unsigned long data );
+extern int read_bit ( struct bit_basher *basher, unsigned int bit_id );
+
+#endif /* _GPXE_BITBASH_H */
diff --git a/gpxe/src/include/gpxe/bitmap.h b/gpxe/src/include/gpxe/bitmap.h
new file mode 100644
index 00000000..0c2f53c8
--- /dev/null
+++ b/gpxe/src/include/gpxe/bitmap.h
@@ -0,0 +1,83 @@
+#ifndef _GPXE_BITMAP_H
+#define _GPXE_BITMAP_H
+
+/** @file
+ *
+ * Bitmaps for multicast downloads
+ *
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+/** A single block of bits within a bitmap */
+typedef unsigned long bitmap_block_t;
+
+/** Size of a block of bits (in bits) */
+#define BITMAP_BLKSIZE ( sizeof ( bitmap_block_t ) * 8 )
+
+/**
+ * Block index within bitmap
+ *
+ * @v bit Bit index
+ * @ret index Block index
+ */
+#define BITMAP_INDEX( bit ) ( (bit) / BITMAP_BLKSIZE )
+
+/**
+ * Block mask within bitmap
+ *
+ * @v bit Bit index
+ * @ret mask Block mask
+ */
+#define BITMAP_MASK( bit ) ( 1 << ( (bit) % BITMAP_BLKSIZE ) )
+
+/** A bitmap */
+struct bitmap {
+ /** Bitmap data */
+ bitmap_block_t *blocks;
+ /** Length of the bitmap, in bits */
+ unsigned int length;
+ /** Index of first gap in the bitmap */
+ unsigned int first_gap;
+};
+
+extern int bitmap_resize ( struct bitmap *bitmap, unsigned int new_length );
+extern int bitmap_test ( struct bitmap *bitmap, unsigned int bit );
+extern void bitmap_set ( struct bitmap *bitmap, unsigned int bit );
+
+/**
+ * Free bitmap resources
+ *
+ * @v bitmap Bitmap
+ */
+static inline void bitmap_free ( struct bitmap *bitmap ) {
+ free ( bitmap->blocks );
+}
+
+/**
+ * Get first gap within bitmap
+ *
+ * @v bitmap Bitmap
+ * @ret first_gap First gap
+ *
+ * The first gap is the first unset bit within the bitmap.
+ */
+static inline unsigned int bitmap_first_gap ( struct bitmap *bitmap ) {
+ return bitmap->first_gap;
+}
+
+/**
+ * Check to see if bitmap is full
+ *
+ * @v bitmap Bitmap
+ * @ret is_full Bitmap is full
+ *
+ * The bitmap is full if it has no gaps (i.e. no unset bits).
+ */
+static inline int bitmap_full ( struct bitmap *bitmap ) {
+ return ( bitmap->first_gap == bitmap->length );
+}
+
+#endif /* _GPXE_BITMAP_H */
diff --git a/gpxe/src/include/gpxe/bitops.h b/gpxe/src/include/gpxe/bitops.h
new file mode 100644
index 00000000..f95af7d3
--- /dev/null
+++ b/gpxe/src/include/gpxe/bitops.h
@@ -0,0 +1,27 @@
+#ifndef _GPXE_BITOPS_H
+#define _GPXE_BITOPS_H
+
+/** @file
+ *
+ * Bit operations
+ */
+
+#include <stdint.h>
+
+static inline uint32_t rol32 ( uint32_t data, unsigned int rotation ) {
+ return ( ( data << rotation ) | ( data >> ( 32 - rotation ) ) );
+}
+
+static inline uint32_t ror32 ( uint32_t data, unsigned int rotation ) {
+ return ( ( data >> rotation ) | ( data << ( 32 - rotation ) ) );
+}
+
+static inline uint64_t rol64 ( uint64_t data, unsigned int rotation ) {
+ return ( ( data << rotation ) | ( data >> ( 64 - rotation ) ) );
+}
+
+static inline uint64_t ror64 ( uint64_t data, unsigned int rotation ) {
+ return ( ( data >> rotation ) | ( data << ( 64 - rotation ) ) );
+}
+
+#endif /* _GPXE_BITOPS_H */
diff --git a/gpxe/src/include/gpxe/blockdev.h b/gpxe/src/include/gpxe/blockdev.h
new file mode 100644
index 00000000..467ed1d9
--- /dev/null
+++ b/gpxe/src/include/gpxe/blockdev.h
@@ -0,0 +1,43 @@
+#ifndef _GPXE_BLOCKDEV_H
+#define _GPXE_BLOCKDEV_H
+
+/**
+ * @file
+ *
+ * Block devices
+ *
+ */
+
+#include <gpxe/uaccess.h>
+
+/** A block device */
+struct block_device {
+ /** Block size */
+ size_t blksize;
+ /** Total number of blocks */
+ uint64_t blocks;
+ /**
+ * Read block
+ *
+ * @v blockdev Block device
+ * @v block Block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+ int ( * read ) ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer );
+ /**
+ * Write block
+ *
+ * @v blockdev Block device
+ * @v block Block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+ int ( * write ) ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer );
+};
+
+#endif /* _GPXE_BLOCKDEV_H */
diff --git a/gpxe/src/include/gpxe/chap.h b/gpxe/src/include/gpxe/chap.h
new file mode 100644
index 00000000..0ea7ac5c
--- /dev/null
+++ b/gpxe/src/include/gpxe/chap.h
@@ -0,0 +1,51 @@
+#ifndef _GPXE_CHAP_H
+#define _GPXE_CHAP_H
+
+/** @file
+ *
+ * CHAP protocol
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/md5.h>
+
+struct crypto_algorithm;
+
+/** A CHAP challenge/response */
+struct chap_challenge {
+ /** Digest algorithm used for the response */
+ struct crypto_algorithm *digest;
+ /** Context used by the digest algorithm */
+ uint8_t *digest_context;
+ /** CHAP response */
+ uint8_t *response;
+ /** Length of CHAP response */
+ size_t response_len;
+};
+
+extern int chap_init ( struct chap_challenge *chap,
+ struct crypto_algorithm *digest );
+extern void chap_update ( struct chap_challenge *chap, const void *data,
+ size_t len );
+extern void chap_respond ( struct chap_challenge *chap );
+extern void chap_finish ( struct chap_challenge *chap );
+
+/**
+ * Add identifier data to the CHAP challenge
+ *
+ * @v chap CHAP challenge/response
+ * @v identifier CHAP identifier
+ *
+ * The CHAP identifier is the first byte of the CHAP challenge. This
+ * function is a notational convenience for calling chap_update() for
+ * the identifier byte.
+ */
+static inline void chap_set_identifier ( struct chap_challenge *chap,
+ unsigned int identifier ) {
+ uint8_t ident_byte = identifier;
+
+ chap_update ( chap, &ident_byte, sizeof ( ident_byte ) );
+}
+
+#endif /* _GPXE_CHAP_H */
diff --git a/gpxe/src/include/gpxe/command.h b/gpxe/src/include/gpxe/command.h
new file mode 100644
index 00000000..5d8057ab
--- /dev/null
+++ b/gpxe/src/include/gpxe/command.h
@@ -0,0 +1,22 @@
+#ifndef _GPXE_COMMAND_H
+#define _GPXE_COMMAND_H
+
+#include <gpxe/tables.h>
+
+/** A command-line command */
+struct command {
+ /** Name of the command */
+ const char *name;
+ /**
+ * Function implementing the command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Return status code
+ */
+ int ( * exec ) ( int argc, char **argv );
+};
+
+#define __command __table ( struct command, commands, 01 )
+
+#endif /* _GPXE_COMMAND_H */
diff --git a/gpxe/src/include/gpxe/cpio.h b/gpxe/src/include/gpxe/cpio.h
new file mode 100644
index 00000000..ba6f844a
--- /dev/null
+++ b/gpxe/src/include/gpxe/cpio.h
@@ -0,0 +1,51 @@
+#ifndef _GPXE_CPIO_H
+#define _GPXE_CPIO_H
+
+/** @file
+ *
+ * CPIO archives
+ *
+ */
+
+/** A CPIO archive header
+ *
+ * All field are hexadecimal ASCII numbers padded with '0' on the
+ * left to the full width of the field.
+ */
+struct cpio_header {
+ /** The string "070701" or "070702" */
+ char c_magic[6];
+ /** File inode number */
+ char c_ino[8];
+ /** File mode and permissions */
+ char c_mode[8];
+ /** File uid */
+ char c_uid[8];
+ /** File gid */
+ char c_gid[8];
+ /** Number of links */
+ char c_nlink[8];
+ /** Modification time */
+ char c_mtime[8];
+ /** Size of data field */
+ char c_filesize[8];
+ /** Major part of file device number */
+ char c_maj[8];
+ /** Minor part of file device number */
+ char c_min[8];
+ /** Major part of device node reference */
+ char c_rmaj[8];
+ /** Minor part of device node reference */
+ char c_rmin[8];
+ /** Length of filename, including final NUL */
+ char c_namesize[8];
+ /** Checksum of data field if c_magic is 070702, othersize zero */
+ char c_chksum[8];
+} __attribute__ (( packed ));
+
+/** CPIO magic */
+#define CPIO_MAGIC "070701"
+
+extern void cpio_set_field ( char *field, unsigned long value );
+
+#endif /* _GPXE_CPIO_H */
diff --git a/gpxe/src/include/gpxe/crypto.h b/gpxe/src/include/gpxe/crypto.h
new file mode 100644
index 00000000..95665acc
--- /dev/null
+++ b/gpxe/src/include/gpxe/crypto.h
@@ -0,0 +1,116 @@
+#ifndef _GPXE_CRYPTO_H
+#define _GPXE_CRYPTO_H
+
+/** @file
+ *
+ * Cryptographic API
+ *
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+/** A cryptographic algorithm */
+struct crypto_algorithm {
+ /** Algorithm name */
+ const char *name;
+ /** Context size */
+ size_t ctxsize;
+ /** Block size */
+ size_t blocksize;
+ /** Final output size */
+ size_t digestsize;
+ /** Initialise algorithm
+ *
+ * @v ctx Context
+ */
+ void ( * init ) ( void *ctx );
+ /** Set key
+ *
+ * @v ctx Context
+ * @v key Key
+ * @v keylen Key length
+ * @ret rc Return status code
+ */
+ int ( * setkey ) ( void *ctx, const void *key, size_t keylen );
+ /** Set initialisation vector
+ *
+ * @v ctx Context
+ * @v iv Initialisation vector
+ */
+ void ( *setiv ) ( void *ctx, const void *iv );
+ /** Encode data
+ *
+ * @v ctx Context
+ * @v src Data to encode
+ * @v dst Encoded data, or NULL
+ * @v len Length of data
+ * @ret rc Return status code
+ *
+ * For a cipher algorithm, the enciphered data should be
+ * placed in @c dst. For a digest algorithm, only the digest
+ * state should be updated, and @c dst will be NULL.
+ *
+ * @v len is guaranteed to be a multiple of @c blocksize.
+ */
+ void ( * encode ) ( void *ctx, const void *src, void *dst,
+ size_t len );
+ /** Decode data
+ *
+ * @v ctx Context
+ * @v src Data to decode
+ * @v dst Decoded data
+ * @v len Length of data
+ * @ret rc Return status code
+ *
+ * @v len is guaranteed to be a multiple of @c blocksize.
+ */
+ void ( * decode ) ( void *ctx, const void *src, void *dst,
+ size_t len );
+ /** Finalise algorithm
+ *
+ * @v ctx Context
+ * @v out Algorithm final output
+ */
+ void ( * final ) ( void *ctx, void *out );
+};
+
+static inline void digest_init ( struct crypto_algorithm *crypto,
+ void *ctx ) {
+ crypto->init ( ctx );
+}
+
+static inline void digest_update ( struct crypto_algorithm *crypto,
+ void *ctx, const void *data, size_t len ) {
+ crypto->encode ( ctx, data, NULL, len );
+}
+
+static inline void digest_final ( struct crypto_algorithm *crypto,
+ void *ctx, void *out ) {
+ crypto->final ( ctx, out );
+}
+
+static inline void cipher_setiv ( struct crypto_algorithm *crypto,
+ void *ctx, const void *iv ) {
+ crypto->setiv ( ctx, iv );
+}
+
+static inline int cipher_setkey ( struct crypto_algorithm *crypto,
+ void *ctx, const void *key, size_t keylen ) {
+ return crypto->setkey ( ctx, key, keylen );
+}
+
+static inline int is_stream_cipher ( struct crypto_algorithm *crypto ) {
+ return ( crypto->blocksize == 1 );
+}
+
+extern struct crypto_algorithm crypto_null;
+
+extern int cipher_encrypt ( struct crypto_algorithm *crypto,
+ void *ctx, const void *src, void *dst,
+ size_t len );
+extern int cipher_decrypt ( struct crypto_algorithm *crypto,
+ void *ctx, const void *src, void *dst,
+ size_t len );
+
+#endif /* _GPXE_CRYPTO_H */
diff --git a/gpxe/src/include/gpxe/device.h b/gpxe/src/include/gpxe/device.h
new file mode 100644
index 00000000..caabdae5
--- /dev/null
+++ b/gpxe/src/include/gpxe/device.h
@@ -0,0 +1,111 @@
+#ifndef _GPXE_DEVICE_H
+#define _GPXE_DEVICE_H
+
+/**
+ * @file
+ *
+ * Device model
+ *
+ */
+
+#include <gpxe/list.h>
+#include <gpxe/tables.h>
+
+/** A hardware device description */
+struct device_description {
+ /** Bus type
+ *
+ * This must be a BUS_TYPE_XXX constant.
+ */
+ unsigned int bus_type;
+ /** Location
+ *
+ * The interpretation of this field is bus-type-specific.
+ */
+ unsigned int location;
+ /** Vendor ID */
+ unsigned int vendor;
+ /** Device ID */
+ unsigned int device;
+ /** Device class */
+ unsigned long class;
+ /** I/O address */
+ unsigned long ioaddr;
+ /** IRQ */
+ unsigned int irq;
+};
+
+/** PCI bus type */
+#define BUS_TYPE_PCI 1
+
+/** ISAPnP bus type */
+#define BUS_TYPE_ISAPNP 2
+
+/** EISA bus type */
+#define BUS_TYPE_EISA 3
+
+/** MCA bus type */
+#define BUS_TYPE_MCA 4
+
+/** ISA bus type */
+#define BUS_TYPE_ISA 5
+
+/** A hardware device */
+struct device {
+ /** Name */
+ char name[16];
+ /** Device description */
+ struct device_description desc;
+ /** Devices on the same bus */
+ struct list_head siblings;
+ /** Devices attached to this device */
+ struct list_head children;
+ /** Bus device */
+ struct device *parent;
+};
+
+/**
+ * A root device
+ *
+ * Root devices are system buses such as PCI, EISA, etc.
+ *
+ */
+struct root_device {
+ /** Device chain
+ *
+ * A root device has a NULL parent field.
+ */
+ struct device dev;
+ /** Root device driver */
+ struct root_driver *driver;
+};
+
+/** A root device driver */
+struct root_driver {
+ /**
+ * Add root device
+ *
+ * @v rootdev Root device
+ * @ret rc Return status code
+ *
+ * Called from probe_devices() for all root devices in the build.
+ */
+ int ( * probe ) ( struct root_device *rootdev );
+ /**
+ * Remove root device
+ *
+ * @v rootdev Root device
+ *
+ * Called from remove_device() for all successfully-probed
+ * root devices.
+ */
+ void ( * remove ) ( struct root_device *rootdev );
+};
+
+/** Declare a root device */
+#define __root_device __table ( struct root_device, root_devices, 01 )
+
+extern int probe_devices ( void );
+extern void remove_devices ( void );
+
+#endif /* _GPXE_DEVICE_H */
diff --git a/gpxe/src/include/gpxe/dhcp.h b/gpxe/src/include/gpxe/dhcp.h
new file mode 100644
index 00000000..94cc2010
--- /dev/null
+++ b/gpxe/src/include/gpxe/dhcp.h
@@ -0,0 +1,455 @@
+#ifndef _GPXE_DHCP_H
+#define _GPXE_DHCP_H
+
+/** @file
+ *
+ * Dynamic Host Configuration Protocol
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/in.h>
+#include <gpxe/list.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/tables.h>
+
+struct net_device;
+struct job_interface;
+struct dhcp_options;
+struct dhcp_packet;
+
+/** BOOTP/DHCP server port */
+#define BOOTPS_PORT 67
+
+/** BOOTP/DHCP client port */
+#define BOOTPC_PORT 68
+
+/** Construct a tag value for an encapsulated option
+ *
+ * This tag value can be passed to Etherboot functions when searching
+ * for DHCP options in order to search for a tag within an
+ * encapsulated options block.
+ */
+#define DHCP_ENCAP_OPT( encapsulator, encapsulated ) \
+ ( ( (encapsulator) << 8 ) | (encapsulated) )
+/** Extract encapsulating option block tag from encapsulated tag value */
+#define DHCP_ENCAPSULATOR( encap_opt ) ( (encap_opt) >> 8 )
+/** Extract encapsulated option tag from encapsulated tag value */
+#define DHCP_ENCAPSULATED( encap_opt ) ( (encap_opt) & 0xff )
+/** Option is encapsulated */
+#define DHCP_IS_ENCAP_OPT( opt ) DHCP_ENCAPSULATOR( opt )
+
+/**
+ * @defgroup dhcpopts DHCP option tags
+ * @{
+ */
+
+/** Padding
+ *
+ * This tag does not have a length field; it is always only a single
+ * byte in length.
+ */
+#define DHCP_PAD 0
+
+/** Minimum normal DHCP option */
+#define DHCP_MIN_OPTION 1
+
+/** Subnet mask */
+#define DHCP_SUBNET_MASK 1
+
+/** Routers */
+#define DHCP_ROUTERS 3
+
+/** DNS servers */
+#define DHCP_DNS_SERVERS 6
+
+/** Syslog servers */
+#define DHCP_LOG_SERVERS 7
+
+/** Host name */
+#define DHCP_HOST_NAME 12
+
+/** Domain name */
+#define DHCP_DOMAIN_NAME 15
+
+/** Root path */
+#define DHCP_ROOT_PATH 17
+
+/** Vendor encapsulated options */
+#define DHCP_VENDOR_ENCAP 43
+
+/** Requested IP address */
+#define DHCP_REQUESTED_ADDRESS 50
+
+/** Lease time */
+#define DHCP_LEASE_TIME 51
+
+/** Option overloading
+ *
+ * The value of this option is the bitwise-OR of zero or more
+ * DHCP_OPTION_OVERLOAD_XXX constants.
+ */
+#define DHCP_OPTION_OVERLOAD 52
+
+/** The "file" field is overloaded to contain extra DHCP options */
+#define DHCP_OPTION_OVERLOAD_FILE 1
+
+/** The "sname" field is overloaded to contain extra DHCP options */
+#define DHCP_OPTION_OVERLOAD_SNAME 2
+
+/** DHCP message type */
+#define DHCP_MESSAGE_TYPE 53
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+/** DHCP server identifier */
+#define DHCP_SERVER_IDENTIFIER 54
+
+/** Parameter request list */
+#define DHCP_PARAMETER_REQUEST_LIST 55
+
+/** Maximum DHCP message size */
+#define DHCP_MAX_MESSAGE_SIZE 57
+
+/** Vendor class identifier */
+#define DHCP_VENDOR_CLASS_ID 60
+
+/** Client identifier */
+#define DHCP_CLIENT_ID 61
+
+/** TFTP server name
+ *
+ * This option replaces the fixed "sname" field, when that field is
+ * used to contain overloaded options.
+ */
+#define DHCP_TFTP_SERVER_NAME 66
+
+/** Bootfile name
+ *
+ * This option replaces the fixed "file" field, when that field is
+ * used to contain overloaded options.
+ */
+#define DHCP_BOOTFILE_NAME 67
+
+/** Client system architecture */
+#define DHCP_CLIENT_ARCHITECTURE 93
+
+/** Client network device interface */
+#define DHCP_CLIENT_NDI 94
+
+/** UUID client identifier */
+#define DHCP_CLIENT_UUID 97
+
+/** Etherboot-specific encapsulated options
+ *
+ * This encapsulated options field is used to contain all options
+ * specific to Etherboot (i.e. not assigned by IANA or other standards
+ * bodies).
+ */
+#define DHCP_EB_ENCAP 175
+
+/** Priority of this options block
+ *
+ * This is a signed 8-bit integer field indicating the priority of
+ * this block of options. It can be used to specify the relative
+ * priority of multiple option blocks (e.g. options from non-volatile
+ * storage versus options from a DHCP server).
+ */
+#define DHCP_EB_PRIORITY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 1 )
+
+/** "Your" IP address
+ *
+ * This option is used internally to contain the value of the "yiaddr"
+ * field, in order to provide a consistent approach to storing and
+ * processing options. It should never be present in a DHCP packet.
+ */
+#define DHCP_EB_YIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 2 )
+
+/** "Server" IP address
+ *
+ * This option is used internally to contain the value of the "siaddr"
+ * field, in order to provide a consistent approach to storing and
+ * processing options. It should never be present in a DHCP packet.
+ */
+#define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 3 )
+
+/*
+ * Tags in the range 0x10-0x7f are reserved for feature markers
+ *
+ */
+
+/** Ignore ProxyDHCP
+ *
+ * If set to a non-zero value, gPXE will not wait for ProxyDHCP offers
+ * and will ignore any ProxyDHCP offers that it receives.
+ */
+#define DHCP_EB_NO_PROXYDHCP DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb0 )
+
+/** Network device descriptor
+ *
+ * Byte 0 is the bus type ID; remaining bytes depend on the bus type.
+ *
+ * PCI devices:
+ * Byte 0 : 1 (PCI)
+ * Byte 1 : PCI vendor ID MSB
+ * Byte 2 : PCI vendor ID LSB
+ * Byte 3 : PCI device ID MSB
+ * Byte 4 : PCI device ID LSB
+ */
+#define DHCP_EB_BUS_ID DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb1 )
+
+/** BIOS drive number
+ *
+ * This is the drive number for a drive emulated via INT 13. 0x80 is
+ * the first hard disk, 0x81 is the second hard disk, etc.
+ */
+#define DHCP_EB_BIOS_DRIVE DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbd )
+
+/** Username
+ *
+ * This will be used as the username for any required authentication.
+ * It is expected that this option's value will be held in
+ * non-volatile storage, rather than transmitted as part of a DHCP
+ * packet.
+ */
+#define DHCP_EB_USERNAME DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbe )
+
+/** Password
+ *
+ * This will be used as the password for any required authentication.
+ * It is expected that this option's value will be held in
+ * non-volatile storage, rather than transmitted as part of a DHCP
+ * packet.
+ */
+#define DHCP_EB_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbf )
+
+/** iSCSI primary target IQN */
+#define DHCP_ISCSI_PRIMARY_TARGET_IQN 201
+
+/** iSCSI secondary target IQN */
+#define DHCP_ISCSI_SECONDARY_TARGET_IQN 202
+
+/** iSCSI initiator IQN */
+#define DHCP_ISCSI_INITIATOR_IQN 203
+
+/** Maximum normal DHCP option */
+#define DHCP_MAX_OPTION 254
+
+/** End of options
+ *
+ * This tag does not have a length field; it is always only a single
+ * byte in length.
+ */
+#define DHCP_END 255
+
+/** @} */
+
+/**
+ * Count number of arguments to a variadic macro
+ *
+ * This rather neat, non-iterative solution is courtesy of Laurent
+ * Deniau.
+ *
+ */
+#define _VA_ARG_COUNT( _1, _2, _3, _4, _5, _6, _7, _8, \
+ _9, _10, _11, _12, _13, _14, _15, _16, \
+ _17, _18, _19, _20, _21, _22, _23, _24, \
+ _25, _26, _27, _28, _29, _30, _31, _32, \
+ _33, _34, _35, _36, _37, _38, _39, _40, \
+ _41, _42, _43, _44, _45, _46, _47, _48, \
+ _49, _50, _51, _52, _53, _54, _55, _56, \
+ _57, _58, _59, _60, _61, _62, _63, N, ... ) N
+#define VA_ARG_COUNT( ... ) \
+ _VA_ARG_COUNT ( __VA_ARGS__, \
+ 63, 62, 61, 60, 59, 58, 57, 56, \
+ 55, 54, 53, 52, 51, 50, 49, 48, \
+ 47, 46, 45, 44, 43, 42, 41, 40, \
+ 39, 38, 37, 36, 35, 34, 33, 32, \
+ 31, 30, 29, 28, 27, 26, 25, 24, \
+ 23, 22, 21, 20, 19, 18, 17, 16, \
+ 15, 14, 13, 12, 11, 10, 9, 8, \
+ 7, 6, 5, 4, 3, 2, 1, 0 )
+
+/** Construct a DHCP option from a list of bytes */
+#define DHCP_OPTION( ... ) VA_ARG_COUNT ( __VA_ARGS__ ), __VA_ARGS__
+
+/** Construct a DHCP option from a list of characters */
+#define DHCP_STRING( ... ) DHCP_OPTION ( __VA_ARGS__ )
+
+/** Construct a byte-valued DHCP option */
+#define DHCP_BYTE( value ) DHCP_OPTION ( value )
+
+/** Construct a word-valued DHCP option */
+#define DHCP_WORD( value ) DHCP_OPTION ( ( ( (value) >> 8 ) & 0xff ), \
+ ( ( (value) >> 0 ) & 0xff ) )
+
+/** Construct a dword-valued DHCP option */
+#define DHCP_DWORD( value ) DHCP_OPTION ( ( ( (value) >> 24 ) & 0xff ), \
+ ( ( (value) >> 16 ) & 0xff ), \
+ ( ( (value) >> 8 ) & 0xff ), \
+ ( ( (value) >> 0 ) & 0xff ) )
+
+/** Construct a DHCP encapsulated options field */
+#define DHCP_ENCAP( ... ) DHCP_OPTION ( __VA_ARGS__, DHCP_END )
+
+/**
+ * A DHCP option
+ *
+ * DHCP options consist of a mandatory tag, a length field that is
+ * mandatory for all options except @c DHCP_PAD and @c DHCP_END, and a
+ * payload.
+ */
+struct dhcp_option {
+ /** Tag
+ *
+ * Must be a @c DHCP_XXX value.
+ */
+ uint8_t tag;
+ /** Length
+ *
+ * This is the length of the data field (i.e. excluding the
+ * tag and length fields). For the two tags @c DHCP_PAD and
+ * @c DHCP_END, the length field is implicitly zero and is
+ * also missing, i.e. these DHCP options are only a single
+ * byte in length.
+ */
+ uint8_t len;
+ /** Option data */
+ uint8_t data[0];
+} __attribute__ (( packed ));
+
+/**
+ * Length of a DHCP option header
+ *
+ * The header is the portion excluding the data, i.e. the tag and the
+ * length.
+ */
+#define DHCP_OPTION_HEADER_LEN ( offsetof ( struct dhcp_option, data ) )
+
+/** Maximum length for a single DHCP option */
+#define DHCP_MAX_LEN 0xff
+
+/**
+ * A DHCP header
+ *
+ */
+struct dhcphdr {
+ /** Operation
+ *
+ * This must be either @c BOOTP_REQUEST or @c BOOTP_REPLY.
+ */
+ uint8_t op;
+ /** Hardware address type
+ *
+ * This is an ARPHRD_XXX constant. Note that ARPHRD_XXX
+ * constants are nominally 16 bits wide; this could be
+ * considered to be a bug in the BOOTP/DHCP specification.
+ */
+ uint8_t htype;
+ /** Hardware address length */
+ uint8_t hlen;
+ /** Number of hops from server */
+ uint8_t hops;
+ /** Transaction ID */
+ uint32_t xid;
+ /** Seconds since start of acquisition */
+ uint16_t secs;
+ /** Flags */
+ uint16_t flags;
+ /** "Client" IP address
+ *
+ * This is filled in if the client already has an IP address
+ * assigned and can respond to ARP requests.
+ */
+ struct in_addr ciaddr;
+ /** "Your" IP address
+ *
+ * This is the IP address assigned by the server to the client.
+ */
+ struct in_addr yiaddr;
+ /** "Server" IP address
+ *
+ * This is the IP address of the next server to be used in the
+ * boot process.
+ */
+ struct in_addr siaddr;
+ /** "Gateway" IP address
+ *
+ * This is the IP address of the DHCP relay agent, if any.
+ */
+ struct in_addr giaddr;
+ /** Client hardware address */
+ uint8_t chaddr[16];
+ /** Server host name (null terminated)
+ *
+ * This field may be overridden and contain DHCP options
+ */
+ char sname[64];
+ /** Boot file name (null terminated)
+ *
+ * This field may be overridden and contain DHCP options
+ */
+ char file[128];
+ /** DHCP magic cookie
+ *
+ * Must have the value @c DHCP_MAGIC_COOKIE.
+ */
+ uint32_t magic;
+ /** DHCP options
+ *
+ * Variable length; extends to the end of the packet. Minimum
+ * length (for the sake of sanity) is 1, to allow for a single
+ * @c DHCP_END tag.
+ */
+ uint8_t options[0];
+};
+
+/** Opcode for a request from client to server */
+#define BOOTP_REQUEST 1
+
+/** Opcode for a reply from server to client */
+#define BOOTP_REPLY 2
+
+/** BOOTP reply must be broadcast
+ *
+ * Clients that cannot accept unicast BOOTP replies must set this
+ * flag.
+ */
+#define BOOTP_FL_BROADCAST 0x8000
+
+/** DHCP magic cookie */
+#define DHCP_MAGIC_COOKIE 0x63825363UL
+
+/** DHCP minimum packet length
+ *
+ * This is the mandated minimum packet length that a DHCP participant
+ * must be prepared to receive.
+ */
+#define DHCP_MIN_LEN 552
+
+/** Maximum time that we will wait for ProxyDHCP offers */
+#define PROXYDHCP_WAIT_TIME ( TICKS_PER_SEC * 1 )
+
+/** Settings block name used for DHCP responses */
+#define DHCP_SETTINGS_NAME "dhcp"
+
+/** Settings block name used for ProxyDHCP responses */
+#define PROXYDHCP_SETTINGS_NAME "proxydhcp"
+
+extern int create_dhcp_packet ( struct dhcp_packet *dhcppkt,
+ struct net_device *netdev, uint8_t msgtype,
+ struct dhcp_options *options,
+ void *data, size_t max_len );
+extern int create_dhcp_request ( struct dhcp_packet *dhcppkt,
+ struct net_device *netdev,
+ struct dhcp_packet *dhcpoffer,
+ void *data, size_t max_len );
+extern int start_dhcp ( struct job_interface *job, struct net_device *netdev );
+
+#endif /* _GPXE_DHCP_H */
diff --git a/gpxe/src/include/gpxe/dhcpopts.h b/gpxe/src/include/gpxe/dhcpopts.h
new file mode 100644
index 00000000..8391a9d4
--- /dev/null
+++ b/gpxe/src/include/gpxe/dhcpopts.h
@@ -0,0 +1,32 @@
+#ifndef _GPXE_DHCPOPTS_H
+#define _GPXE_DHCPOPTS_H
+
+/** @file
+ *
+ * DHCP options
+ *
+ */
+
+#include <stdint.h>
+
+/** A DHCP options block */
+struct dhcp_options {
+ /** Option block raw data */
+ void *data;
+ /** Option block length */
+ size_t len;
+ /** Option block maximum length */
+ size_t max_len;
+};
+
+extern int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
+ const void *data, size_t len );
+extern int dhcpopt_extensible_store ( struct dhcp_options *options,
+ unsigned int tag,
+ const void *data, size_t len );
+extern int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag,
+ void *data, size_t len );
+extern void dhcpopt_init ( struct dhcp_options *options,
+ void *data, size_t max_len );
+
+#endif /* _GPXE_DHCPOPTS_H */
diff --git a/gpxe/src/include/gpxe/dhcppkt.h b/gpxe/src/include/gpxe/dhcppkt.h
new file mode 100644
index 00000000..179be2f8
--- /dev/null
+++ b/gpxe/src/include/gpxe/dhcppkt.h
@@ -0,0 +1,35 @@
+#ifndef _GPXE_DHCPPKT_H
+#define _GPXE_DHCPPKT_H
+
+/** @file
+ *
+ * DHCP packets
+ *
+ */
+
+#include <gpxe/dhcp.h>
+#include <gpxe/dhcpopts.h>
+
+/**
+ * A DHCP packet
+ *
+ */
+struct dhcp_packet {
+ /** The DHCP packet contents */
+ struct dhcphdr *dhcphdr;
+ /** Maximum length of the DHCP packet buffer */
+ size_t max_len;
+ /** Used length of the DHCP packet buffer */
+ size_t len;
+ /** DHCP option blocks */
+ struct dhcp_options options;
+};
+
+extern int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag,
+ const void *data, size_t len );
+extern int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag,
+ void *data, size_t len );
+extern void dhcppkt_init ( struct dhcp_packet *dhcppkt,
+ void *data, size_t len );
+
+#endif /* _GPXE_DHCPPKT_H */
diff --git a/gpxe/src/include/gpxe/dns.h b/gpxe/src/include/gpxe/dns.h
new file mode 100644
index 00000000..3e3cff1b
--- /dev/null
+++ b/gpxe/src/include/gpxe/dns.h
@@ -0,0 +1,90 @@
+#ifndef _GPXE_DNS_H
+#define _GPXE_DNS_H
+
+/** @file
+ *
+ * DNS protocol
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/in.h>
+
+/*
+ * Constants
+ *
+ */
+
+#define DNS_TYPE_A 1
+#define DNS_TYPE_CNAME 5
+#define DNS_TYPE_ANY 255
+
+#define DNS_CLASS_IN 1
+#define DNS_CLASS_CS 2
+#define DNS_CLASS_CH 3
+#define DNS_CLASS_HS 4
+
+#define DNS_FLAG_QUERY ( 0x00 << 15 )
+#define DNS_FLAG_RESPONSE ( 0x01 << 15 )
+#define DNS_FLAG_QR(flags) ( (flags) & ( 0x01 << 15 ) )
+#define DNS_FLAG_OPCODE_QUERY ( 0x00 << 11 )
+#define DNS_FLAG_OPCODE_IQUERY ( 0x01 << 11 )
+#define DNS_FLAG_OPCODE_STATUS ( 0x02 << 11 )
+#define DNS_FLAG_OPCODE(flags) ( (flags) & ( 0x0f << 11 ) )
+#define DNS_FLAG_RD ( 0x01 << 8 )
+#define DNS_FLAG_RA ( 0x01 << 7 )
+#define DNS_FLAG_RCODE_OK ( 0x00 << 0 )
+#define DNS_FLAG_RCODE_NX ( 0x03 << 0 )
+#define DNS_FLAG_RCODE(flags) ( (flags) & ( 0x0f << 0 ) )
+
+#define DNS_PORT 53
+#define DNS_MAX_RETRIES 3
+#define DNS_MAX_CNAME_RECURSION 0x30
+
+/*
+ * DNS protocol structures
+ *
+ */
+struct dns_header {
+ uint16_t id;
+ uint16_t flags;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+} __attribute__ (( packed ));
+
+struct dns_query_info {
+ uint16_t qtype;
+ uint16_t qclass;
+} __attribute__ (( packed ));
+
+struct dns_query {
+ struct dns_header dns;
+ char payload[ 256 + sizeof ( struct dns_query_info ) ];
+} __attribute__ (( packed ));
+
+struct dns_rr_info_common {
+ uint16_t type;
+ uint16_t class;
+ uint32_t ttl;
+ uint16_t rdlength;
+} __attribute__ (( packed ));
+
+struct dns_rr_info_a {
+ struct dns_rr_info_common common;
+ struct in_addr in_addr;
+} __attribute__ (( packed ));
+
+struct dns_rr_info_cname {
+ struct dns_rr_info_common common;
+ char cname[0];
+} __attribute__ (( packed ));
+
+union dns_rr_info {
+ struct dns_rr_info_common common;
+ struct dns_rr_info_a a;
+ struct dns_rr_info_cname cname;
+};
+
+#endif /* _GPXE_DNS_H */
diff --git a/gpxe/src/include/gpxe/downloader.h b/gpxe/src/include/gpxe/downloader.h
new file mode 100644
index 00000000..33aa7692
--- /dev/null
+++ b/gpxe/src/include/gpxe/downloader.h
@@ -0,0 +1,17 @@
+#ifndef _GPXE_DOWNLOADER_H
+#define _GPXE_DOWNLOADER_H
+
+/** @file
+ *
+ * Image downloader
+ *
+ */
+
+struct job_interface;
+struct image;
+
+extern int create_downloader ( struct job_interface *job, struct image *image,
+ int ( * register_image ) ( struct image *image ),
+ int type, ... );
+
+#endif /* _GPXE_DOWNLOADER_H */
diff --git a/gpxe/src/include/gpxe/editbox.h b/gpxe/src/include/gpxe/editbox.h
new file mode 100644
index 00000000..47b07c25
--- /dev/null
+++ b/gpxe/src/include/gpxe/editbox.h
@@ -0,0 +1,51 @@
+#ifndef _GPXE_EDITBOX_H
+#define _GPXE_EDITBOX_H
+
+/** @file
+ *
+ * Editable text box widget
+ *
+ */
+
+#include <curses.h>
+#include <gpxe/editstring.h>
+
+/** An editable text box widget */
+struct edit_box {
+ /** Editable string */
+ struct edit_string string;
+ /** Containing window */
+ WINDOW *win;
+ /** Row */
+ unsigned int row;
+ /** Starting column */
+ unsigned int col;
+ /** Width */
+ unsigned int width;
+ /** First displayed character */
+ unsigned int first;
+};
+
+extern void init_editbox ( struct edit_box *box, char *buf, size_t len,
+ WINDOW *win, unsigned int row, unsigned int col,
+ unsigned int width )
+ __attribute__ (( nonnull (1, 2) ));
+extern void draw_editbox ( struct edit_box *box ) __nonnull;
+static inline int __pure edit_editbox ( struct edit_box *box, int key ) __nonnull;
+
+/**
+ * Edit text box widget
+ *
+ * @v box Editable text box widget
+ * @v key Key pressed by user
+ * @ret key Key returned to application, or zero
+ *
+ * You must call draw_editbox() to update the display after calling
+ * edit_editbox().
+ *
+ */
+static inline int edit_editbox ( struct edit_box *box, int key ) {
+ return edit_string ( &box->string, key );
+}
+
+#endif /* _GPXE_EDITBOX_H */
diff --git a/gpxe/src/include/gpxe/editstring.h b/gpxe/src/include/gpxe/editstring.h
new file mode 100644
index 00000000..fad8bd5a
--- /dev/null
+++ b/gpxe/src/include/gpxe/editstring.h
@@ -0,0 +1,31 @@
+#ifndef _GPXE_EDITSTRING_H
+#define _GPXE_EDITSTRING_H
+
+/** @file
+ *
+ * Editable strings
+ *
+ */
+
+/** An editable string */
+struct edit_string {
+ /** Buffer for string */
+ char *buf;
+ /** Size of buffer (including terminating NUL) */
+ size_t len;
+ /** Cursor position */
+ unsigned int cursor;
+
+ /* The following items are the edit history */
+
+ /** Last cursor position */
+ unsigned int last_cursor;
+ /** Start of modified portion of string */
+ unsigned int mod_start;
+ /** End of modified portion of string */
+ unsigned int mod_end;
+};
+
+extern int edit_string ( struct edit_string *string, int key ) __nonnull;
+
+#endif /* _GPXE_EDITSTRING_H */
diff --git a/gpxe/src/include/gpxe/eisa.h b/gpxe/src/include/gpxe/eisa.h
new file mode 100644
index 00000000..e9d890e1
--- /dev/null
+++ b/gpxe/src/include/gpxe/eisa.h
@@ -0,0 +1,125 @@
+#ifndef EISA_H
+#define EISA_H
+
+#include <stdint.h>
+#include <gpxe/isa_ids.h>
+#include <gpxe/device.h>
+#include <gpxe/tables.h>
+
+/*
+ * EISA constants
+ *
+ */
+
+#define EISA_MIN_SLOT (0x1)
+#define EISA_MAX_SLOT (0xf) /* Must be 2^n - 1 */
+#define EISA_SLOT_BASE( n ) ( 0x1000 * (n) )
+
+#define EISA_VENDOR_ID ( 0xc80 )
+#define EISA_PROD_ID ( 0xc82 )
+#define EISA_GLOBAL_CONFIG ( 0xc84 )
+
+#define EISA_CMD_RESET ( 1 << 2 )
+#define EISA_CMD_ENABLE ( 1 << 0 )
+
+/** An EISA device ID list entry */
+struct eisa_device_id {
+ /** Name */
+ const char *name;
+ /** Manufacturer ID */
+ uint16_t vendor_id;
+ /** Product ID */
+ uint16_t prod_id;
+};
+
+/** An EISA device */
+struct eisa_device {
+ /** Generic device */
+ struct device dev;
+ /** Slot number */
+ unsigned int slot;
+ /** I/O address */
+ uint16_t ioaddr;
+ /** Manufacturer ID */
+ uint16_t vendor_id;
+ /** Product ID */
+ uint16_t prod_id;
+ /** Driver for this device */
+ struct eisa_driver *driver;
+ /** Driver-private data
+ *
+ * Use eisa_set_drvdata() and eisa_get_drvdata() to access
+ * this field.
+ */
+ void *priv;
+ /** Driver name */
+ const char *driver_name;
+};
+
+/** An EISA driver */
+struct eisa_driver {
+ /** EISA ID table */
+ struct eisa_device_id *ids;
+ /** Number of entries in EISA ID table */
+ unsigned int id_count;
+ /**
+ * Probe device
+ *
+ * @v eisa EISA device
+ * @v id Matching entry in ID table
+ * @ret rc Return status code
+ */
+ int ( * probe ) ( struct eisa_device *eisa,
+ const struct eisa_device_id *id );
+ /**
+ * Remove device
+ *
+ * @v eisa EISA device
+ */
+ void ( * remove ) ( struct eisa_device *eisa );
+};
+
+/** Declare an EISA driver */
+#define __eisa_driver __table ( struct eisa_driver, eisa_drivers, 01 )
+
+extern void eisa_device_enabled ( struct eisa_device *eisa, int enabled );
+
+/**
+ * Enable EISA device
+ *
+ * @v eisa EISA device
+ */
+static inline void enable_eisa_device ( struct eisa_device *eisa ) {
+ eisa_device_enabled ( eisa, 1 );
+}
+
+/**
+ * Disable EISA device
+ *
+ * @v eisa EISA device
+ */
+static inline void disable_eisa_device ( struct eisa_device *eisa ) {
+ eisa_device_enabled ( eisa, 0 );
+}
+
+/**
+ * Set EISA driver-private data
+ *
+ * @v eisa EISA device
+ * @v priv Private data
+ */
+static inline void eisa_set_drvdata ( struct eisa_device *eisa, void *priv ) {
+ eisa->priv = priv;
+}
+
+/**
+ * Get EISA driver-private data
+ *
+ * @v eisa EISA device
+ * @ret priv Private data
+ */
+static inline void * eisa_get_drvdata ( struct eisa_device *eisa ) {
+ return eisa->priv;
+}
+
+#endif /* EISA_H */
diff --git a/gpxe/src/include/gpxe/elf.h b/gpxe/src/include/gpxe/elf.h
new file mode 100644
index 00000000..db28a60a
--- /dev/null
+++ b/gpxe/src/include/gpxe/elf.h
@@ -0,0 +1,15 @@
+#ifndef _GPXE_ELF_H
+#define _GPXE_ELF_H
+
+/**
+ * @file
+ *
+ * ELF image format
+ *
+ */
+
+#include <elf.h>
+
+extern int elf_load ( struct image *image );
+
+#endif /* _GPXE_ELF_H */
diff --git a/gpxe/src/include/gpxe/embedded.h b/gpxe/src/include/gpxe/embedded.h
new file mode 100644
index 00000000..ec457055
--- /dev/null
+++ b/gpxe/src/include/gpxe/embedded.h
@@ -0,0 +1,9 @@
+#ifndef _GPXE_EMBEDDED_H
+#define _GPXE_EMBEDDED_H
+
+#include <gpxe/image.h>
+
+struct image *embedded_image(void);
+
+#endif
+
diff --git a/gpxe/src/include/gpxe/errfile.h b/gpxe/src/include/gpxe/errfile.h
new file mode 100644
index 00000000..ae8b1480
--- /dev/null
+++ b/gpxe/src/include/gpxe/errfile.h
@@ -0,0 +1,157 @@
+#ifndef _GPXE_ERRFILE_H
+#define _GPXE_ERRFILE_H
+
+/** @file
+ *
+ * Error file identifiers
+ *
+ */
+
+#include <bits/errfile.h>
+
+/**
+ * @defgroup errfilecat Error file identifier categories
+ *
+ * @{
+ */
+
+#define ERRFILE_CORE 0x00002000 /**< Core code */
+#define ERRFILE_DRIVER 0x00004000 /**< Driver code */
+#define ERRFILE_NET 0x00006000 /**< Networking code */
+#define ERRFILE_IMAGE 0x00008000 /**< Image code */
+#define ERRFILE_OTHER 0x0000e000 /**< Any other code */
+
+/** @} */
+
+/** Flag for architecture-dependent error files */
+#define ERRFILE_ARCH 0x00800000
+
+/**
+ * @defgroup errfile Error file identifiers
+ *
+ * These values are automatically incorporated into the definitions
+ * for error numbers such as EINVAL.
+ *
+ * @{
+ */
+
+#define ERRFILE_asprintf ( ERRFILE_CORE | 0x00000000 )
+#define ERRFILE_downloader ( ERRFILE_CORE | 0x00010000 )
+#define ERRFILE_exec ( ERRFILE_CORE | 0x00020000 )
+#define ERRFILE_hw ( ERRFILE_CORE | 0x00030000 )
+#define ERRFILE_iobuf ( ERRFILE_CORE | 0x00040000 )
+#define ERRFILE_job ( ERRFILE_CORE | 0x00050000 )
+#define ERRFILE_linebuf ( ERRFILE_CORE | 0x00060000 )
+#define ERRFILE_monojob ( ERRFILE_CORE | 0x00070000 )
+#define ERRFILE_nvo ( ERRFILE_CORE | 0x00080000 )
+#define ERRFILE_open ( ERRFILE_CORE | 0x00090000 )
+#define ERRFILE_posix_io ( ERRFILE_CORE | 0x000a0000 )
+#define ERRFILE_resolv ( ERRFILE_CORE | 0x000b0000 )
+#define ERRFILE_settings ( ERRFILE_CORE | 0x000c0000 )
+#define ERRFILE_vsprintf ( ERRFILE_CORE | 0x000d0000 )
+#define ERRFILE_xfer ( ERRFILE_CORE | 0x000e0000 )
+#define ERRFILE_bitmap ( ERRFILE_CORE | 0x000f0000 )
+
+#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
+#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )
+#define ERRFILE_isapnp ( ERRFILE_DRIVER | 0x00020000 )
+#define ERRFILE_mca ( ERRFILE_DRIVER | 0x00030000 )
+#define ERRFILE_pci ( ERRFILE_DRIVER | 0x00040000 )
+
+#define ERRFILE_nvs ( ERRFILE_DRIVER | 0x00100000 )
+#define ERRFILE_spi ( ERRFILE_DRIVER | 0x00110000 )
+#define ERRFILE_i2c_bit ( ERRFILE_DRIVER | 0x00120000 )
+#define ERRFILE_spi_bit ( ERRFILE_DRIVER | 0x00130000 )
+
+#define ERRFILE_3c509 ( ERRFILE_DRIVER | 0x00200000 )
+#define ERRFILE_bnx2 ( ERRFILE_DRIVER | 0x00210000 )
+#define ERRFILE_cs89x0 ( ERRFILE_DRIVER | 0x00220000 )
+#define ERRFILE_eepro ( ERRFILE_DRIVER | 0x00230000 )
+#define ERRFILE_etherfabric ( ERRFILE_DRIVER | 0x00240000 )
+#define ERRFILE_legacy ( ERRFILE_DRIVER | 0x00250000 )
+#define ERRFILE_natsemi ( ERRFILE_DRIVER | 0x00260000 )
+#define ERRFILE_pnic ( ERRFILE_DRIVER | 0x00270000 )
+#define ERRFILE_prism2_pci ( ERRFILE_DRIVER | 0x00280000 )
+#define ERRFILE_prism2_plx ( ERRFILE_DRIVER | 0x00290000 )
+#define ERRFILE_rtl8139 ( ERRFILE_DRIVER | 0x002a0000 )
+#define ERRFILE_smc9000 ( ERRFILE_DRIVER | 0x002b0000 )
+#define ERRFILE_tg3 ( ERRFILE_DRIVER | 0x002c0000 )
+#define ERRFILE_3c509_eisa ( ERRFILE_DRIVER | 0x002d0000 )
+#define ERRFILE_3c515 ( ERRFILE_DRIVER | 0x002e0000 )
+#define ERRFILE_3c529 ( ERRFILE_DRIVER | 0x002f0000 )
+#define ERRFILE_3c595 ( ERRFILE_DRIVER | 0x00300000 )
+#define ERRFILE_3c5x9 ( ERRFILE_DRIVER | 0x00310000 )
+#define ERRFILE_3c90x ( ERRFILE_DRIVER | 0x00320000 )
+#define ERRFILE_amd8111e ( ERRFILE_DRIVER | 0x00330000 )
+#define ERRFILE_davicom ( ERRFILE_DRIVER | 0x00340000 )
+#define ERRFILE_depca ( ERRFILE_DRIVER | 0x00350000 )
+#define ERRFILE_dmfe ( ERRFILE_DRIVER | 0x00360000 )
+#define ERRFILE_eepro100 ( ERRFILE_DRIVER | 0x00380000 )
+#define ERRFILE_epic100 ( ERRFILE_DRIVER | 0x00390000 )
+#define ERRFILE_forcedeth ( ERRFILE_DRIVER | 0x003a0000 )
+#define ERRFILE_mtd80x ( ERRFILE_DRIVER | 0x003b0000 )
+#define ERRFILE_ns83820 ( ERRFILE_DRIVER | 0x003c0000 )
+#define ERRFILE_ns8390 ( ERRFILE_DRIVER | 0x003d0000 )
+#define ERRFILE_pcnet32 ( ERRFILE_DRIVER | 0x003e0000 )
+#define ERRFILE_r8169 ( ERRFILE_DRIVER | 0x003f0000 )
+#define ERRFILE_sis900 ( ERRFILE_DRIVER | 0x00400000 )
+#define ERRFILE_sundance ( ERRFILE_DRIVER | 0x00410000 )
+#define ERRFILE_tlan ( ERRFILE_DRIVER | 0x00420000 )
+#define ERRFILE_tulip ( ERRFILE_DRIVER | 0x00430000 )
+#define ERRFILE_via_rhine ( ERRFILE_DRIVER | 0x00440000 )
+#define ERRFILE_via_velocity ( ERRFILE_DRIVER | 0x00450000 )
+#define ERRFILE_w89c840 ( ERRFILE_DRIVER | 0x00460000 )
+#define ERRFILE_ipoib ( ERRFILE_DRIVER | 0x00470000 )
+#define ERRFILE_e1000 ( ERRFILE_DRIVER | 0x00480000 )
+#define ERRFILE_e1000_hw ( ERRFILE_DRIVER | 0x00490000 )
+
+#define ERRFILE_scsi ( ERRFILE_DRIVER | 0x00700000 )
+#define ERRFILE_arbel ( ERRFILE_DRIVER | 0x00710000 )
+#define ERRFILE_hermon ( ERRFILE_DRIVER | 0x00720000 )
+
+#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 )
+#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )
+#define ERRFILE_dhcpopts ( ERRFILE_NET | 0x00020000 )
+#define ERRFILE_ethernet ( ERRFILE_NET | 0x00030000 )
+#define ERRFILE_icmpv6 ( ERRFILE_NET | 0x00040000 )
+#define ERRFILE_ipv4 ( ERRFILE_NET | 0x00050000 )
+#define ERRFILE_ipv6 ( ERRFILE_NET | 0x00060000 )
+#define ERRFILE_ndp ( ERRFILE_NET | 0x00070000 )
+#define ERRFILE_netdevice ( ERRFILE_NET | 0x00080000 )
+#define ERRFILE_nullnet ( ERRFILE_NET | 0x00090000 )
+#define ERRFILE_tcp ( ERRFILE_NET | 0x000a0000 )
+#define ERRFILE_ftp ( ERRFILE_NET | 0x000b0000 )
+#define ERRFILE_http ( ERRFILE_NET | 0x000c0000 )
+#define ERRFILE_iscsi ( ERRFILE_NET | 0x000d0000 )
+#define ERRFILE_tcpip ( ERRFILE_NET | 0x000e0000 )
+#define ERRFILE_udp ( ERRFILE_NET | 0x000f0000 )
+#define ERRFILE_dhcp ( ERRFILE_NET | 0x00100000 )
+#define ERRFILE_dns ( ERRFILE_NET | 0x00110000 )
+#define ERRFILE_tftp ( ERRFILE_NET | 0x00120000 )
+#define ERRFILE_infiniband ( ERRFILE_NET | 0x00130000 )
+#define ERRFILE_netdev_settings ( ERRFILE_NET | 0x00140000 )
+#define ERRFILE_dhcppkt ( ERRFILE_NET | 0x00150000 )
+
+#define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 )
+#define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 )
+#define ERRFILE_script ( ERRFILE_IMAGE | 0x00020000 )
+#define ERRFILE_segment ( ERRFILE_IMAGE | 0x00030000 )
+
+#define ERRFILE_asn1 ( ERRFILE_OTHER | 0x00000000 )
+#define ERRFILE_chap ( ERRFILE_OTHER | 0x00010000 )
+#define ERRFILE_aoeboot ( ERRFILE_OTHER | 0x00020000 )
+#define ERRFILE_autoboot ( ERRFILE_OTHER | 0x00030000 )
+#define ERRFILE_dhcpmgmt ( ERRFILE_OTHER | 0x00040000 )
+#define ERRFILE_imgmgmt ( ERRFILE_OTHER | 0x00050000 )
+#define ERRFILE_pxe_tftp ( ERRFILE_OTHER | 0x00060000 )
+#define ERRFILE_pxe_udp ( ERRFILE_OTHER | 0x00070000 )
+#define ERRFILE_axtls_aes ( ERRFILE_OTHER | 0x00080000 )
+#define ERRFILE_cipher ( ERRFILE_OTHER | 0x00090000 )
+#define ERRFILE_image_cmd ( ERRFILE_OTHER | 0x000a0000 )
+#define ERRFILE_uri_test ( ERRFILE_OTHER | 0x000b0000 )
+#define ERRFILE_ibft ( ERRFILE_OTHER | 0x000c0000 )
+#define ERRFILE_tls ( ERRFILE_OTHER | 0x000d0000 )
+
+/** @} */
+
+#endif /* _GPXE_ERRFILE_H */
diff --git a/gpxe/src/include/gpxe/errortab.h b/gpxe/src/include/gpxe/errortab.h
new file mode 100644
index 00000000..e9a56768
--- /dev/null
+++ b/gpxe/src/include/gpxe/errortab.h
@@ -0,0 +1,19 @@
+#ifndef _GPXE_ERRORTAB_H
+#define _GPXE_ERRORTAB_H
+
+/** @file
+ *
+ * Error message tables
+ *
+ */
+
+#include <gpxe/tables.h>
+
+struct errortab {
+ int errno;
+ const char *text;
+};
+
+#define __errortab __table ( struct errortab, errortab, 01 )
+
+#endif /* _GPXE_ERRORTAB_H */
diff --git a/gpxe/src/include/gpxe/ethernet.h b/gpxe/src/include/gpxe/ethernet.h
new file mode 100644
index 00000000..158fe066
--- /dev/null
+++ b/gpxe/src/include/gpxe/ethernet.h
@@ -0,0 +1,33 @@
+#ifndef _GPXE_ETHERNET_H
+#define _GPXE_ETHERNET_H
+
+/** @file
+ *
+ * Ethernet protocol
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/netdevice.h>
+
+extern struct ll_protocol ethernet_protocol;
+
+extern const char * eth_ntoa ( const void *ll_addr );
+
+/**
+ * Allocate Ethernet device
+ *
+ * @v priv_size Size of driver private data
+ * @ret netdev Network device, or NULL
+ */
+static inline struct net_device * alloc_etherdev ( size_t priv_size ) {
+ struct net_device *netdev;
+
+ netdev = alloc_netdev ( priv_size );
+ if ( netdev ) {
+ netdev->ll_protocol = &ethernet_protocol;
+ }
+ return netdev;
+}
+
+#endif /* _GPXE_ETHERNET_H */
diff --git a/gpxe/src/include/gpxe/fakedhcp.h b/gpxe/src/include/gpxe/fakedhcp.h
new file mode 100644
index 00000000..990b56af
--- /dev/null
+++ b/gpxe/src/include/gpxe/fakedhcp.h
@@ -0,0 +1,21 @@
+#ifndef _GPXE_FAKEDHCP_H
+#define _GPXE_FAKEDHCP_H
+
+/** @file
+ *
+ * Fake DHCP packets
+ *
+ */
+
+#include <stdint.h>
+
+struct net_device;
+
+extern int create_fakedhcpdiscover ( struct net_device *netdev,
+ void *data, size_t max_len );
+extern int create_fakedhcpack ( struct net_device *netdev,
+ void *data, size_t max_len );
+extern int create_fakeproxydhcpack ( struct net_device *netdev,
+ void *data, size_t max_len );
+
+#endif /* _GPXE_FAKEDHCP_H */
diff --git a/gpxe/src/include/gpxe/features.h b/gpxe/src/include/gpxe/features.h
new file mode 100644
index 00000000..a520131f
--- /dev/null
+++ b/gpxe/src/include/gpxe/features.h
@@ -0,0 +1,88 @@
+#ifndef _GPXE_FEATURES_H
+#define _GPXE_FEATURES_H
+
+#include <stdint.h>
+#include <gpxe/tables.h>
+#include <gpxe/dhcp.h>
+
+/** @file
+ *
+ * Feature list
+ *
+ */
+
+/**
+ * @defgroup featurecat Feature categories
+ * @{
+ */
+
+#define FEATURE_PROTOCOL 01 /**< Network protocols */
+#define FEATURE_IMAGE 02 /**< Image formats */
+#define FEATURE_MISC 03 /**< Miscellaneous */
+
+/** @} */
+
+/**
+ * @defgroup dhcpfeatures DHCP feature option tags
+ *
+ * DHCP feature option tags are Etherboot encapsulated options in the
+ * range 0x10-0x7f.
+ *
+ * @{
+ */
+
+#define DHCP_EB_FEATURE_PXE_EXT 0x10 /**< PXE API extensions */
+#define DHCP_EB_FEATURE_ISCSI 0x11 /**< iSCSI protocol */
+#define DHCP_EB_FEATURE_AOE 0x12 /**< AoE protocol */
+#define DHCP_EB_FEATURE_HTTP 0x13 /**< HTTP protocol */
+#define DHCP_EB_FEATURE_HTTPS 0x14 /**< HTTPS protocol */
+#define DHCP_EB_FEATURE_TFTP 0x15 /**< TFTP protocol */
+#define DHCP_EB_FEATURE_FTP 0x16 /**< FTP protocol */
+#define DHCP_EB_FEATURE_DNS 0x17 /**< DNS protocol */
+#define DHCP_EB_FEATURE_BZIMAGE 0x18 /**< bzImage format */
+#define DHCP_EB_FEATURE_MULTIBOOT 0x19 /**< Multiboot format */
+#define DHCP_EB_FEATURE_NBI 0x20 /**< NBI format */
+#define DHCP_EB_FEATURE_PXE 0x21 /**< PXE format */
+
+/** @} */
+
+/** Declare a feature code for DHCP */
+#define __dhcp_feature( category ) \
+ __table ( uint8_t, dhcp_features, category )
+
+/** Construct a DHCP feature table entry */
+#define DHCP_FEATURE( category, feature_opt, version ) \
+ _DHCP_FEATURE ( category, OBJECT, feature_opt, version )
+#define _DHCP_FEATURE( category, _name, feature_opt, version ) \
+ __DHCP_FEATURE ( category, _name, feature_opt, version )
+#define __DHCP_FEATURE( category, _name, feature_opt, version ) \
+ uint8_t __dhcp_feature_ ## _name [] __dhcp_feature ( category ) = { \
+ feature_opt, DHCP_BYTE ( version ) \
+ };
+
+/** A named feature */
+struct feature {
+ /** Feature name */
+ char *name;
+};
+
+/** Declare a named feature */
+#define __feature_name( category ) \
+ __table ( struct feature, features, category )
+
+/** Construct a named feature */
+#define FEATURE_NAME( category, text ) \
+ _FEATURE_NAME ( category, OBJECT, text )
+#define _FEATURE_NAME( category, _name, text ) \
+ __FEATURE_NAME ( category, _name, text )
+#define __FEATURE_NAME( category, _name, text ) \
+ struct feature __feature_ ## _name __feature_name ( category ) = { \
+ .name = text, \
+ };
+
+/** Declare a feature */
+#define FEATURE( category, text, feature_opt, version ) \
+ FEATURE_NAME ( category, text ); \
+ DHCP_FEATURE ( category, feature_opt, version );
+
+#endif /* _GPXE_FEATURES_H */
diff --git a/gpxe/src/include/gpxe/filter.h b/gpxe/src/include/gpxe/filter.h
new file mode 100644
index 00000000..126f6347
--- /dev/null
+++ b/gpxe/src/include/gpxe/filter.h
@@ -0,0 +1,73 @@
+#ifndef _GPXE_FILTER_H
+#define _GPXE_FILTER_H
+
+/** @file
+ *
+ * Data transfer filters
+ *
+ */
+
+#include <stddef.h>
+#include <gpxe/xfer.h>
+
+/**
+ * Half of a data transfer filter
+ *
+ * Embed two of these structures within a structure implementing a
+ * data transfer filter, and intialise with filter_init(). You can
+ * then use the filter_xxx() methods as the data transfer interface
+ * methods as required.
+ */
+struct xfer_filter_half {
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+ /** Other half of the data transfer filter */
+ struct xfer_filter_half *other;
+};
+
+/**
+ * Get data transfer interface for the other half of a data transfer filter
+ *
+ * @v xfer Data transfer interface
+ * @ret other Other half's data transfer interface
+ */
+static inline __attribute__ (( always_inline )) struct xfer_interface *
+filter_other_half ( struct xfer_interface *xfer ) {
+ struct xfer_filter_half *half =
+ container_of ( xfer, struct xfer_filter_half, xfer );
+ return &half->other->xfer;
+}
+
+extern void filter_close ( struct xfer_interface *xfer, int rc );
+extern int filter_vredirect ( struct xfer_interface *xfer, int type,
+ va_list args );
+extern size_t filter_window ( struct xfer_interface *xfer );
+extern struct io_buffer * filter_alloc_iob ( struct xfer_interface *xfer,
+ size_t len );
+extern int filter_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta );
+extern int filter_deliver_raw ( struct xfer_interface *xfer, const void *data,
+ size_t len );
+
+/**
+ * Initialise a data transfer filter
+ *
+ * @v left "Left" half of the filter
+ * @v left_op Data transfer interface operations for "left" half
+ * @v right "Right" half of the filter
+ * @v right_op Data transfer interface operations for "right" half
+ * @v refcnt Containing object reference counter, or NULL
+ */
+static inline void filter_init ( struct xfer_filter_half *left,
+ struct xfer_interface_operations *left_op,
+ struct xfer_filter_half *right,
+ struct xfer_interface_operations *right_op,
+ struct refcnt *refcnt ) {
+ xfer_init ( &left->xfer, left_op, refcnt );
+ xfer_init ( &right->xfer, right_op, refcnt );
+ left->other = right;
+ right->other = left;
+}
+
+#endif /* _GPXE_FILTER_H */
diff --git a/gpxe/src/include/gpxe/ftp.h b/gpxe/src/include/gpxe/ftp.h
new file mode 100644
index 00000000..370285c6
--- /dev/null
+++ b/gpxe/src/include/gpxe/ftp.h
@@ -0,0 +1,13 @@
+#ifndef _GPXE_FTP_H
+#define _GPXE_FTP_H
+
+/** @file
+ *
+ * File transfer protocol
+ *
+ */
+
+/** FTP default port */
+#define FTP_PORT 21
+
+#endif /* _GPXE_FTP_H */
diff --git a/gpxe/src/include/gpxe/hidemem.h b/gpxe/src/include/gpxe/hidemem.h
new file mode 100644
index 00000000..547f8881
--- /dev/null
+++ b/gpxe/src/include/gpxe/hidemem.h
@@ -0,0 +1,23 @@
+#ifndef _GPXE_HIDEMEM_H
+#define _GPXE_HIDEMEM_H
+
+/**
+ * @file
+ *
+ * Hidden memory regions
+ *
+ */
+
+/**
+ * Unique IDs for hidden regions
+ */
+enum hidemem_region_id {
+ TEXT = 0,
+ BASEMEM,
+ EXTMEM,
+};
+
+extern void hide_region ( unsigned int region_id, physaddr_t start,
+ physaddr_t end );
+
+#endif /* _GPXE_HIDEMEM_H */
diff --git a/gpxe/src/include/gpxe/hmac.h b/gpxe/src/include/gpxe/hmac.h
new file mode 100644
index 00000000..fd34db04
--- /dev/null
+++ b/gpxe/src/include/gpxe/hmac.h
@@ -0,0 +1,30 @@
+#ifndef _GPXE_HMAC_H
+#define _GPXE_HMAC_H
+
+/** @file
+ *
+ * Keyed-Hashing for Message Authentication
+ */
+
+#include <gpxe/crypto.h>
+
+/**
+ * Update HMAC
+ *
+ * @v digest Digest algorithm to use
+ * @v digest_ctx Digest context
+ * @v data Data
+ * @v len Length of data
+ */
+static inline void hmac_update ( struct crypto_algorithm *digest,
+ void *digest_ctx, const void *data,
+ size_t len ) {
+ digest_update ( digest, digest_ctx, data, len );
+}
+
+extern void hmac_init ( struct crypto_algorithm *digest, void *digest_ctx,
+ void *key, size_t *key_len );
+extern void hmac_final ( struct crypto_algorithm *digest, void *digest_ctx,
+ void *key, size_t *key_len, void *hmac );
+
+#endif /* _GPXE_HMAC_H */
diff --git a/gpxe/src/include/gpxe/http.h b/gpxe/src/include/gpxe/http.h
new file mode 100644
index 00000000..fa92a950
--- /dev/null
+++ b/gpxe/src/include/gpxe/http.h
@@ -0,0 +1,21 @@
+#ifndef _GPXE_HTTP_H
+#define _GPXE_HTTP_H
+
+/** @file
+ *
+ * Hyper Text Transport Protocol
+ *
+ */
+
+/** HTTP default port */
+#define HTTP_PORT 80
+
+/** HTTPS default port */
+#define HTTPS_PORT 443
+
+extern int http_open_filter ( struct xfer_interface *xfer, struct uri *uri,
+ unsigned int default_port,
+ int ( * filter ) ( struct xfer_interface *,
+ struct xfer_interface ** ) );
+
+#endif /* _GPXE_HTTP_H */
diff --git a/gpxe/src/include/gpxe/i2c.h b/gpxe/src/include/gpxe/i2c.h
new file mode 100644
index 00000000..bfaee8fb
--- /dev/null
+++ b/gpxe/src/include/gpxe/i2c.h
@@ -0,0 +1,111 @@
+#ifndef _GPXE_I2C_H
+#define _GPXE_I2C_H
+
+/** @file
+ *
+ * I2C interface
+ *
+ */
+
+#include <stdint.h>
+
+/** An I2C device
+ *
+ * An I2C device represents a specific slave device on an I2C bus. It
+ * is accessed via an I2C interface.
+ */
+struct i2c_device {
+ /** Address of this device */
+ unsigned int address;
+ /** Flag indicating a ten-bit address format */
+ int tenbit;
+};
+
+/** An I2C interface
+ *
+ * An I2C interface provides access to an I2C bus, via which I2C
+ * devices may be reached.
+ */
+struct i2c_interface {
+ /**
+ * Read data from I2C device
+ *
+ * @v i2c I2C interface
+ * @v i2cdev I2C device
+ * @v offset Starting offset within the device
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+ int ( * read ) ( struct i2c_interface *i2c, struct i2c_device *i2cdev,
+ unsigned int offset, uint8_t *data,
+ unsigned int len );
+ /**
+ * Write data to I2C device
+ *
+ * @v i2c I2C interface
+ * @v i2cdev I2C device
+ * @v offset Starting offset within the device
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+ int ( * write ) ( struct i2c_interface *i2c, struct i2c_device *i2cdev,
+ unsigned int offset, const uint8_t *data,
+ unsigned int len );
+};
+
+/** A bit-bashing I2C interface
+ *
+ * This provides a standardised way to construct I2C buses via a
+ * bit-bashing interface.
+ */
+struct i2c_bit_basher {
+ /** I2C interface */
+ struct i2c_interface i2c;
+ /** Bit-bashing interface */
+ struct bit_basher basher;
+};
+
+/** Ten-bit address marker
+ *
+ * This value is ORed with the I2C device address to indicate a
+ * ten-bit address format on the bus.
+ */
+#define I2C_TENBIT_ADDRESS 0x7800
+
+/** An I2C write command */
+#define I2C_WRITE 0
+
+/** An I2C read command */
+#define I2C_READ 1
+
+/** Bit indices used for I2C bit-bashing interface */
+enum {
+ /** Serial clock */
+ I2C_BIT_SCL = 0,
+ /** Serial data */
+ I2C_BIT_SDA,
+};
+
+/** Delay required for bit-bashing operation */
+#define I2C_UDELAY 5
+
+/**
+ * Check presence of I2C device
+ *
+ * @v i2c I2C interface
+ * @v i2cdev I2C device
+ * @ret rc Return status code
+ *
+ * Checks for the presence of the device on the I2C bus by attempting
+ * a zero-length write.
+ */
+static inline int i2c_check_presence ( struct i2c_interface *i2c,
+ struct i2c_device *i2cdev ) {
+ return i2c->write ( i2c, i2cdev, 0, NULL, 0 );
+}
+
+extern void init_i2c_bit_basher ( struct i2c_bit_basher *i2cbit );
+
+#endif /* _GPXE_I2C_H */
diff --git a/gpxe/src/include/gpxe/ibft.h b/gpxe/src/include/gpxe/ibft.h
new file mode 100644
index 00000000..5eef547b
--- /dev/null
+++ b/gpxe/src/include/gpxe/ibft.h
@@ -0,0 +1,300 @@
+#ifndef _GPXE_IBFT_H
+#define _GPXE_IBFT_H
+
+/*
+ * Copyright Fen Systems Ltd. 2007. Portions of this code are derived
+ * from IBM Corporation Sample Programs. Copyright IBM Corporation
+ * 2004, 2007. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+/** @file
+ *
+ * iSCSI boot firmware table
+ *
+ * The information in this file is derived from the document "iSCSI
+ * Boot Firmware Table (iBFT)" as published by IBM at
+ *
+ * ftp://ftp.software.ibm.com/systems/support/system_x_pdf/ibm_iscsi_boot_firmware_table_v1.02.pdf
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/acpi.h>
+#include <gpxe/in.h>
+
+/** iSCSI Boot Firmware Table signature */
+#define IBFT_SIG "iBFT"
+
+/** An offset from the start of the iBFT */
+typedef uint16_t ibft_off_t;
+
+/** Length of a string within the iBFT (excluding terminating NUL) */
+typedef uint16_t ibft_size_t;
+
+/** A string within the iBFT */
+struct ibft_string {
+ /** Length of string */
+ ibft_size_t length;
+ /** Offset to string */
+ ibft_off_t offset;
+} __attribute__ (( packed ));
+
+/** An IP address within the iBFT */
+struct ibft_ipaddr {
+ /** Reserved; must be zero */
+ uint16_t zeroes[5];
+ /** Must be 0xffff if IPv4 address is present, otherwise zero */
+ uint16_t ones;
+ /** The IPv4 address, or zero if not present */
+ struct in_addr in;
+} __attribute__ (( packed ));
+
+/**
+ * iBFT structure header
+ *
+ * This structure is common to several sections within the iBFT.
+ */
+struct ibft_header {
+ /** Structure ID
+ *
+ * This is an IBFT_STRUCTURE_ID_XXX constant
+ */
+ uint8_t structure_id;
+ /** Version (always 1) */
+ uint8_t version;
+ /** Length, including this header */
+ uint16_t length;
+ /** Index
+ *
+ * This is the number of the NIC or Target, when applicable.
+ */
+ uint8_t index;
+ /** Flags */
+ uint8_t flags;
+} __attribute__ (( packed ));
+
+/**
+ * iBFT Control structure
+ *
+ */
+struct ibft_control {
+ /** Common header */
+ struct ibft_header header;
+ /** Extensions */
+ uint16_t extensions;
+ /** Offset to Initiator structure */
+ ibft_off_t initiator;
+ /** Offset to NIC structure for NIC 0 */
+ ibft_off_t nic_0;
+ /** Offset to Target structure for target 0 */
+ ibft_off_t target_0;
+ /** Offset to NIC structure for NIC 1 */
+ ibft_off_t nic_1;
+ /** Offset to Target structure for target 1 */
+ ibft_off_t target_1;
+} __attribute__ (( packed ));
+
+/** Structure ID for Control section */
+#define IBFT_STRUCTURE_ID_CONTROL 0x01
+
+/** Attempt login only to specified target
+ *
+ * If this flag is not set, all targets will be logged in to.
+ */
+#define IBFT_FL_CONTROL_SINGLE_LOGIN_ONLY 0x01
+
+/**
+ * iBFT Initiator structure
+ *
+ */
+struct ibft_initiator {
+ /** Common header */
+ struct ibft_header header;
+ /** iSNS server */
+ struct ibft_ipaddr isns_server;
+ /** SLP server */
+ struct ibft_ipaddr slp_server;
+ /** Primary and secondary Radius servers */
+ struct ibft_ipaddr radius[2];
+ /** Initiator name */
+ struct ibft_string initiator_name;
+} __attribute__ (( packed ));
+
+/** Structure ID for Initiator section */
+#define IBFT_STRUCTURE_ID_INITIATOR 0x02
+
+/** Initiator block valid */
+#define IBFT_FL_INITIATOR_BLOCK_VALID 0x01
+
+/** Initiator firmware boot selected */
+#define IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED 0x02
+
+/**
+ * iBFT NIC structure
+ *
+ */
+struct ibft_nic {
+ /** Common header */
+ struct ibft_header header;
+ /** IP address */
+ struct ibft_ipaddr ip_address;
+ /** Subnet mask
+ *
+ * This is the length of the subnet mask in bits (e.g. /24).
+ */
+ uint8_t subnet_mask_prefix;
+ /** Origin */
+ uint8_t origin;
+ /** Default gateway */
+ struct ibft_ipaddr gateway;
+ /** Primary and secondary DNS servers */
+ struct ibft_ipaddr dns[2];
+ /** DHCP server */
+ struct ibft_ipaddr dhcp;
+ /** VLAN tag */
+ uint16_t vlan;
+ /** MAC address */
+ uint8_t mac_address[6];
+ /** PCI bus:dev:fn */
+ uint16_t pci_bus_dev_func;
+ /** Hostname */
+ struct ibft_string hostname;
+} __attribute__ (( packed ));
+
+/** Structure ID for NIC section */
+#define IBFT_STRUCTURE_ID_NIC 0x03
+
+/** NIC block valid */
+#define IBFT_FL_NIC_BLOCK_VALID 0x01
+
+/** NIC firmware boot selected */
+#define IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED 0x02
+
+/** NIC global / link local */
+#define IBFT_FL_NIC_GLOBAL 0x04
+
+/**
+ * iBFT Target structure
+ *
+ */
+struct ibft_target {
+ /** Common header */
+ struct ibft_header header;
+ /** IP address */
+ struct ibft_ipaddr ip_address;
+ /** TCP port */
+ uint16_t socket;
+ /** Boot LUN */
+ uint64_t boot_lun;
+ /** CHAP type
+ *
+ * This is an IBFT_CHAP_XXX constant.
+ */
+ uint8_t chap_type;
+ /** NIC association */
+ uint8_t nic_association;
+ /** Target name */
+ struct ibft_string target_name;
+ /** CHAP name */
+ struct ibft_string chap_name;
+ /** CHAP secret */
+ struct ibft_string chap_secret;
+ /** Reverse CHAP name */
+ struct ibft_string reverse_chap_name;
+ /** Reverse CHAP secret */
+ struct ibft_string reverse_chap_secret;
+} __attribute__ (( packed ));
+
+/** Structure ID for Target section */
+#define IBFT_STRUCTURE_ID_TARGET 0x04
+
+/** Target block valid */
+#define IBFT_FL_TARGET_BLOCK_VALID 0x01
+
+/** Target firmware boot selected */
+#define IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED 0x02
+
+/** Target use Radius CHAP */
+#define IBFT_FL_TARGET_USE_CHAP 0x04
+
+/** Target use Radius rCHAP */
+#define IBFT_FL_TARGET_USE_RCHAP 0x08
+
+/* Values for chap_type */
+#define IBFT_CHAP_NONE 0 /**< No CHAP authentication */
+#define IBFT_CHAP_ONE_WAY 1 /**< One-way CHAP */
+#define IBFT_CHAP_MUTUAL 2 /**< Mutual CHAP */
+
+/**
+ * iSCSI Boot Firmware Table (iBFT)
+ */
+struct ibft_table {
+ /** ACPI header */
+ struct acpi_description_header acpi;
+ /** Reserved */
+ uint8_t reserved[12];
+ /** Control structure */
+ struct ibft_control control;
+} __attribute__ (( packed ));
+
+/**
+ * iSCSI string block descriptor
+ *
+ * This is an internal structure that we use to keep track of the
+ * allocation of string data.
+ */
+struct ibft_string_block {
+ /** The iBFT containing these strings */
+ struct ibft_table *table;
+ /** Offset of first free byte within iBFT */
+ unsigned int offset;
+};
+
+/** Amount of space reserved for strings in a gPXE iBFT */
+#define IBFT_STRINGS_SIZE 384
+
+/**
+ * An iBFT created by gPXE
+ *
+ */
+struct gpxe_ibft {
+ /** The fixed section */
+ struct ibft_table table;
+ /** The Initiator section */
+ struct ibft_initiator initiator __attribute__ (( aligned ( 16 ) ));
+ /** The NIC section */
+ struct ibft_nic nic __attribute__ (( aligned ( 16 ) ));
+ /** The Target section */
+ struct ibft_target target __attribute__ (( aligned ( 16 ) ));
+ /** Strings block */
+ char strings[IBFT_STRINGS_SIZE];
+} __attribute__ (( packed, aligned ( 16 ) ));
+
+struct net_device;
+struct iscsi_session;
+
+extern int ibft_fill_data ( struct net_device *netdev,
+ struct iscsi_session *iscsi );
+
+#endif /* _GPXE_IBFT_H */
diff --git a/gpxe/src/include/gpxe/icmp6.h b/gpxe/src/include/gpxe/icmp6.h
new file mode 100644
index 00000000..34093616
--- /dev/null
+++ b/gpxe/src/include/gpxe/icmp6.h
@@ -0,0 +1,57 @@
+#ifndef _GPXE_ICMP6_H
+#define _GPXE_ICMP6_H
+
+/** @file
+ *
+ * ICMP6 protocol
+ *
+ */
+
+#include <gpxe/ip6.h>
+#include <gpxe/ndp.h>
+
+#define ICMP6_NSOLICIT 135
+#define ICMP6_NADVERT 136
+
+extern struct tcpip_protocol icmp6_protocol;
+
+struct icmp6_header {
+ uint8_t type;
+ uint8_t code;
+ uint16_t csum;
+ /* Message body */
+};
+
+struct neighbour_solicit {
+ uint8_t type;
+ uint8_t code;
+ uint16_t csum;
+ uint32_t reserved;
+ struct in6_addr target;
+ /* "Compulsory" options */
+ uint8_t opt_type;
+ uint8_t opt_len;
+ /* FIXME: hack alert */
+ uint8_t opt_ll_addr[6];
+};
+
+struct neighbour_advert {
+ uint8_t type;
+ uint8_t code;
+ uint16_t csum;
+ uint8_t flags;
+ uint8_t reserved;
+ struct in6_addr target;
+ uint8_t opt_type;
+ uint8_t opt_len;
+ /* FIXME: hack alert */
+ uint8_t opt_ll_addr[6];
+};
+
+#define ICMP6_FLAGS_ROUTER 0x80
+#define ICMP6_FLAGS_SOLICITED 0x40
+#define ICMP6_FLAGS_OVERRIDE 0x20
+
+int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src, struct in6_addr *dest );
+
+#endif /* _GPXE_ICMP6_H */
diff --git a/gpxe/src/include/gpxe/if_arp.h b/gpxe/src/include/gpxe/if_arp.h
new file mode 100644
index 00000000..5b250337
--- /dev/null
+++ b/gpxe/src/include/gpxe/if_arp.h
@@ -0,0 +1,100 @@
+#ifndef _GPXE_IF_ARP_H
+#define _GPXE_IF_ARP_H
+
+/** @file
+ *
+ * Address Resolution Protocol constants and types
+ *
+ */
+
+#include <stdint.h>
+
+/* ARP protocol HARDWARE identifiers. */
+#define ARPHRD_NETROM 0 /**< from KA9Q: NET/ROM pseudo */
+#define ARPHRD_ETHER 1 /**< Ethernet 10Mbps */
+#define ARPHRD_EETHER 2 /**< Experimental Ethernet */
+#define ARPHRD_AX25 3 /**< AX.25 Level 2 */
+#define ARPHRD_PRONET 4 /**< PROnet token ring */
+#define ARPHRD_CHAOS 5 /**< Chaosnet */
+#define ARPHRD_IEEE802 6 /**< IEEE 802.2 Ethernet/TR/TB */
+#define ARPHRD_ARCNET 7 /**< ARCnet */
+#define ARPHRD_APPLETLK 8 /**< APPLEtalk */
+#define ARPHRD_DLCI 15 /**< Frame Relay DLCI */
+#define ARPHRD_ATM 19 /**< ATM */
+#define ARPHRD_METRICOM 23 /**< Metricom STRIP (new IANA id) */
+#define ARPHRD_IEEE1394 24 /**< IEEE 1394 IPv4 - RFC 2734 */
+#define ARPHRD_EUI64 27 /**< EUI-64 */
+#define ARPHRD_INFINIBAND 32 /**< InfiniBand */
+
+/* ARP protocol opcodes. */
+#define ARPOP_REQUEST 1 /**< ARP request */
+#define ARPOP_REPLY 2 /**< ARP reply */
+#define ARPOP_RREQUEST 3 /**< RARP request */
+#define ARPOP_RREPLY 4 /**< RARP reply */
+#define ARPOP_InREQUEST 8 /**< InARP request */
+#define ARPOP_InREPLY 9 /**< InARP reply */
+#define ARPOP_NAK 10 /**< (ATM)ARP NAK */
+
+/**
+ * An ARP header
+ *
+ * This contains only the fixed-size portions of an ARP header; for
+ * other fields use the arp_{sender,target}_{ha,pa} family of
+ * functions.
+ */
+struct arphdr {
+ /** Link-layer protocol
+ *
+ * This is an ARPHRD_XXX constant
+ */
+ uint16_t ar_hrd;
+ /** Network-layer protocol
+ *
+ * This is, for Ethernet, an ETH_P_XXX constant.
+ */
+ uint16_t ar_pro;
+ /** Link-layer address length */
+ uint8_t ar_hln;
+ /** Network-layer address length */
+ uint8_t ar_pln;
+ /** ARP opcode */
+ uint16_t ar_op;
+} __attribute__ (( packed ));
+
+/** ARP packet sender hardware address
+ *
+ * @v arphdr ARP header
+ * @ret ar_sha Sender hardware address
+ */
+static inline void * arp_sender_ha ( struct arphdr *arphdr ) {
+ return ( ( ( void * ) arphdr ) + sizeof ( *arphdr ) );
+}
+
+/** ARP packet sender protocol address
+ *
+ * @v arphdr ARP header
+ * @ret ar_spa Sender protocol address
+ */
+static inline void * arp_sender_pa ( struct arphdr *arphdr ) {
+ return ( arp_sender_ha ( arphdr ) + arphdr->ar_hln );
+}
+
+/** ARP packet target hardware address
+ *
+ * @v arphdr ARP header
+ * @ret ar_tha Target hardware address
+ */
+static inline void * arp_target_ha ( struct arphdr *arphdr ) {
+ return ( arp_sender_pa ( arphdr ) + arphdr->ar_pln );
+}
+
+/** ARP packet target protocol address
+ *
+ * @v arphdr ARP header
+ * @ret ar_tpa Target protocol address
+ */
+static inline void * arp_target_pa ( struct arphdr *arphdr ) {
+ return ( arp_target_ha ( arphdr ) + arphdr->ar_hln );
+}
+
+#endif /* _GPXE_IF_ARP_H */
diff --git a/gpxe/src/include/gpxe/if_ether.h b/gpxe/src/include/gpxe/if_ether.h
new file mode 100644
index 00000000..2f3f33d4
--- /dev/null
+++ b/gpxe/src/include/gpxe/if_ether.h
@@ -0,0 +1,33 @@
+#ifndef _GPXE_IF_ETHER_H
+#define _GPXE_IF_ETHER_H
+
+#include <stdint.h>
+
+#define ETH_ALEN 6 /* Size of Ethernet address */
+#define ETH_HLEN 14 /* Size of ethernet header */
+#define ETH_ZLEN 60 /* Minimum packet */
+#define ETH_FRAME_LEN 1514 /* Maximum packet */
+#define ETH_DATA_ALIGN 2 /* Amount needed to align the data after an ethernet header */
+#ifndef ETH_MAX_MTU
+#define ETH_MAX_MTU (ETH_FRAME_LEN-ETH_HLEN)
+#endif
+
+#define ETH_P_RAW 0x0000 /* Raw packet */
+#define ETH_P_IP 0x0800 /* Internet Protocl Packet */
+#define ETH_P_ARP 0x0806 /* Address Resolution Protocol */
+#define ETH_P_RARP 0x8035 /* Reverse Address resolution Protocol */
+#define ETH_P_IPV6 0x86DD /* IPv6 over blueblook */
+#define ETH_P_SLOW 0x8809 /* Ethernet slow protocols */
+#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */
+
+/** An Ethernet link-layer header */
+struct ethhdr {
+ /** Destination MAC address */
+ uint8_t h_dest[ETH_ALEN];
+ /** Source MAC address */
+ uint8_t h_source[ETH_ALEN];
+ /** Protocol ID */
+ uint16_t h_protocol;
+} __attribute__ ((packed));
+
+#endif /* _GPXE_IF_ETHER_H */
diff --git a/gpxe/src/include/gpxe/image.h b/gpxe/src/include/gpxe/image.h
new file mode 100644
index 00000000..76dc3b8f
--- /dev/null
+++ b/gpxe/src/include/gpxe/image.h
@@ -0,0 +1,162 @@
+#ifndef _GPXE_IMAGE_H
+#define _GPXE_IMAGE_H
+
+/**
+ * @file
+ *
+ * Executable/loadable images
+ *
+ */
+
+#include <gpxe/tables.h>
+#include <gpxe/list.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/refcnt.h>
+
+struct uri;
+struct image_type;
+
+/** An executable or loadable image */
+struct image {
+ /** Reference count */
+ struct refcnt refcnt;
+
+ /** List of registered images */
+ struct list_head list;
+
+ /** URI of image */
+ struct uri *uri;
+ /** Name */
+ char name[16];
+ /** Flags */
+ unsigned int flags;
+
+ /** Command line to pass to image */
+ char *cmdline;
+ /** Raw file image */
+ userptr_t data;
+ /** Length of raw file image */
+ size_t len;
+
+ /** Image type, if known */
+ struct image_type *type;
+ /** Image type private data */
+ union {
+ physaddr_t phys;
+ userptr_t user;
+ unsigned long ul;
+ } priv;
+};
+
+/** Image is loaded */
+#define IMAGE_LOADED 0x0001
+
+/** An executable or loadable image type */
+struct image_type {
+ /** Name of this image type */
+ char *name;
+ /**
+ * Load image into memory
+ *
+ * @v image Executable/loadable image
+ * @ret rc Return status code
+ *
+ * Load the image into memory at the correct location as
+ * determined by the file format.
+ *
+ * If the file image is in the correct format, the method must
+ * update @c image->type to point to its own type (unless @c
+ * image->type is already set). This allows the autoloading
+ * code to disambiguate between "this is not my image format"
+ * and "there is something wrong with this image". In
+ * particular, setting @c image->type and then returning an
+ * error will cause image_autoload() to abort and return an
+ * error, rather than continuing to the next image type.
+ */
+ int ( * load ) ( struct image *image );
+ /**
+ * Execute loaded image
+ *
+ * @v image Loaded image
+ * @ret rc Return status code
+ */
+ int ( * exec ) ( struct image *image );
+};
+
+/**
+ * Multiboot image probe priority
+ *
+ * Multiboot images are also valid executables in another format
+ * (e.g. ELF), so we must perform the multiboot probe first.
+ */
+#define PROBE_MULTIBOOT 01
+
+/**
+ * Normal image probe priority
+ */
+#define PROBE_NORMAL 02
+
+/**
+ * PXE image probe priority
+ *
+ * PXE images have no signature checks, so will claim all image files.
+ * They must therefore be tried last in the probe order list.
+ */
+#define PROBE_PXE 03
+
+/** An executable or loadable image type */
+#define __image_type( probe_order ) \
+ __table ( struct image_type, image_types, probe_order )
+
+extern struct list_head images;
+
+/** Iterate over all registered images */
+#define for_each_image( image ) \
+ list_for_each_entry ( (image), &images, list )
+
+extern struct image * alloc_image ( void );
+extern int image_set_uri ( struct image *image, struct uri *uri );
+extern int image_set_cmdline ( struct image *image, const char *cmdline );
+extern int register_image ( struct image *image );
+extern void unregister_image ( struct image *image );
+extern void promote_image ( struct image *image );
+struct image * find_image ( const char *name );
+extern int image_load ( struct image *image );
+extern int image_autoload ( struct image *image );
+extern int image_exec ( struct image *image );
+extern int register_and_autoload_image ( struct image *image );
+extern int register_and_autoexec_image ( struct image *image );
+
+/**
+ * Increment reference count on an image
+ *
+ * @v image Image
+ * @ret image Image
+ */
+static inline struct image * image_get ( struct image *image ) {
+ ref_get ( &image->refcnt );
+ return image;
+}
+
+/**
+ * Decrement reference count on an image
+ *
+ * @v image Image
+ */
+static inline void image_put ( struct image *image ) {
+ ref_put ( &image->refcnt );
+}
+
+/**
+ * Set image name
+ *
+ * @v image Image
+ * @v name New image name
+ * @ret rc Return status code
+ */
+static inline int image_set_name ( struct image *image, const char *name ) {
+ strncpy ( image->name, name, ( sizeof ( image->name ) - 1 ) );
+ return 0;
+}
+
+#endif /* _GPXE_IMAGE_H */
diff --git a/gpxe/src/include/gpxe/in.h b/gpxe/src/include/gpxe/in.h
new file mode 100644
index 00000000..89530a55
--- /dev/null
+++ b/gpxe/src/include/gpxe/in.h
@@ -0,0 +1,94 @@
+#ifndef _GPXE_IN_H
+#define _GPXE_IN_H
+
+#include <stdint.h>
+#include <gpxe/socket.h>
+
+/* Protocol numbers */
+
+#define IP_ICMP 1
+#define IP_IGMP 2
+#define IP_TCP 6
+#define IP_UDP 17
+#define IP_ICMP6 58
+
+/* IP address constants */
+
+#define INADDR_NONE 0xffffffff
+
+#define INADDR_BROADCAST 0xffffffff
+
+#define IN_CLASSA(addr) ( ( (addr) & 0x80000000 ) == 0x00000000 )
+#define IN_CLASSA_NET 0xff000000
+#define IN_CLASSB(addr) ( ( (addr) & 0xc0000000 ) == 0x80000000 )
+#define IN_CLASSB_NET 0xffff0000
+#define IN_CLASSC(addr) ( ( (addr) & 0xe0000000 ) == 0xc0000000 )
+#define IN_CLASSC_NET 0xffffff00
+#define IN_MULTICAST(addr) ( ( (addr) & 0xf0000000 ) == 0xe0000000 )
+
+/**
+ * IP address structure
+ */
+struct in_addr {
+ uint32_t s_addr;
+};
+
+typedef struct in_addr in_addr;
+
+/**
+ * IP6 address structure
+ */
+struct in6_addr {
+ union {
+ uint8_t u6_addr8[16];
+ uint16_t u6_addr16[8];
+ uint32_t u6_addr32[4];
+ } in6_u;
+#define s6_addr in6_u.u6_addr8
+#define s6_addr16 in6_u.u6_addr16
+#define s6_addr32 in6_u.u6_addr32
+};
+
+/**
+ * IPv4 socket address
+ */
+struct sockaddr_in {
+ /** Socket address family (part of struct @c sockaddr)
+ *
+ * Always set to @c AF_INET for IPv4 addresses
+ */
+ sa_family_t sin_family;
+ /** TCP/IP port (part of struct @c sockaddr_tcpip) */
+ uint16_t sin_port;
+ /** IPv4 address */
+ struct in_addr sin_addr;
+};
+
+/**
+ * IPv6 socket address
+ */
+struct sockaddr_in6 {
+ /** Socket address family (part of struct @c sockaddr)
+ *
+ * Always set to @c AF_INET6 for IPv6 addresses
+ */
+ sa_family_t sin_family;
+ /** TCP/IP port (part of struct @c sockaddr_tcpip) */
+ uint16_t sin_port;
+ uint32_t sin6_flowinfo; /* Flow number */
+ struct in6_addr sin6_addr; /* 128-bit destination address */
+ uint32_t sin6_scope_id; /* Scope ID */
+};
+
+extern int inet_aton ( const char *cp, struct in_addr *inp );
+extern char * inet_ntoa ( struct in_addr in );
+
+/* Adding the following for IP6 support
+ *
+
+extern int inet6_aton ( const char *cp, struct in6_addr *inp );
+extern char * inet6_ntoa ( struct in_addr in );
+
+ */
+
+#endif /* _GPXE_IN_H */
diff --git a/gpxe/src/include/gpxe/infiniband.h b/gpxe/src/include/gpxe/infiniband.h
new file mode 100644
index 00000000..354dc579
--- /dev/null
+++ b/gpxe/src/include/gpxe/infiniband.h
@@ -0,0 +1,751 @@
+#ifndef _GPXE_INFINIBAND_H
+#define _GPXE_INFINIBAND_H
+
+/** @file
+ *
+ * Infiniband protocol
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/device.h>
+
+/** Subnet administrator QPN */
+#define IB_SA_QPN 1
+
+/** Broadcast QPN */
+#define IB_BROADCAST_QPN 0xffffffUL
+
+/** Subnet administrator queue key */
+#define IB_GLOBAL_QKEY 0x80010000UL
+
+/** An Infiniband Global Identifier */
+struct ib_gid {
+ union {
+ uint8_t bytes[16];
+ uint16_t words[8];
+ uint32_t dwords[4];
+ } u;
+};
+
+/** An Infiniband Global Route Header */
+struct ib_global_route_header {
+ /** IP version, traffic class, and flow label
+ *
+ * 4 bits : Version of the GRH
+ * 8 bits : Traffic class
+ * 20 bits : Flow label
+ */
+ uint32_t ipver_tclass_flowlabel;
+ /** Payload length */
+ uint16_t paylen;
+ /** Next header */
+ uint8_t nxthdr;
+ /** Hop limit */
+ uint8_t hoplmt;
+ /** Source GID */
+ struct ib_gid sgid;
+ /** Destiniation GID */
+ struct ib_gid dgid;
+} __attribute__ (( packed ));
+
+struct ib_device;
+struct ib_queue_pair;
+struct ib_completion_queue;
+
+/** An Infiniband Work Queue */
+struct ib_work_queue {
+ /** Containing queue pair */
+ struct ib_queue_pair *qp;
+ /** "Is a send queue" flag */
+ int is_send;
+ /** Associated completion queue */
+ struct ib_completion_queue *cq;
+ /** List of work queues on this completion queue */
+ struct list_head list;
+ /** Number of work queue entries */
+ unsigned int num_wqes;
+ /** Next work queue entry index
+ *
+ * This is the index of the next entry to be filled (i.e. the
+ * first empty entry). This value is not bounded by num_wqes;
+ * users must logical-AND with (num_wqes-1) to generate an
+ * array index.
+ */
+ unsigned long next_idx;
+ /** I/O buffers assigned to work queue */
+ struct io_buffer **iobufs;
+ /** Driver private data */
+ void *drv_priv;
+};
+
+/** An Infiniband Queue Pair */
+struct ib_queue_pair {
+ /** Queue Pair Number */
+ unsigned long qpn;
+ /** Queue key */
+ unsigned long qkey;
+ /** Send queue */
+ struct ib_work_queue send;
+ /** Receive queue */
+ struct ib_work_queue recv;
+ /** Driver private data */
+ void *drv_priv;
+ /** Queue owner private data */
+ void *owner_priv;
+};
+
+/** An Infiniband Completion Queue */
+struct ib_completion_queue {
+ /** Completion queue number */
+ unsigned long cqn;
+ /** Number of completion queue entries */
+ unsigned int num_cqes;
+ /** Next completion queue entry index
+ *
+ * This is the index of the next entry to be filled (i.e. the
+ * first empty entry). This value is not bounded by num_wqes;
+ * users must logical-AND with (num_wqes-1) to generate an
+ * array index.
+ */
+ unsigned long next_idx;
+ /** List of work queues completing to this queue */
+ struct list_head work_queues;
+ /** Driver private data */
+ void *drv_priv;
+};
+
+/** An Infiniband completion */
+struct ib_completion {
+ /** Syndrome
+ *
+ * If non-zero, then the completion is in error.
+ */
+ unsigned int syndrome;
+ /** Length */
+ size_t len;
+};
+
+/** An Infiniband completion handler
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v completion Completion
+ * @v iobuf I/O buffer
+ */
+typedef void ( * ib_completer_t ) ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct ib_completion *completion,
+ struct io_buffer *iobuf );
+
+/** An Infiniband Address Vector */
+struct ib_address_vector {
+ /** Destination Queue Pair */
+ unsigned int dest_qp;
+ /** Queue key */
+ unsigned long qkey;
+ /** Destination Local ID */
+ unsigned int dlid;
+ /** Rate */
+ unsigned int rate;
+ /** Service level */
+ unsigned int sl;
+ /** GID is present */
+ unsigned int gid_present;
+ /** GID */
+ struct ib_gid gid;
+};
+
+struct ib_mad_hdr;
+
+/**
+ * Infiniband device operations
+ *
+ * These represent a subset of the Infiniband Verbs.
+ */
+struct ib_device_operations {
+ /** Create completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ * @ret rc Return status code
+ */
+ int ( * create_cq ) ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq );
+ /** Destroy completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ */
+ void ( * destroy_cq ) ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq );
+ /** Create queue pair
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @ret rc Return status code
+ */
+ int ( * create_qp ) ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp );
+ /** Destroy queue pair
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ */
+ void ( * destroy_qp ) ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp );
+ /** Post send work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v av Address vector
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ *
+ * If this method returns success, the I/O buffer remains
+ * owned by the queue pair. If this method returns failure,
+ * the I/O buffer is immediately released; the failure is
+ * interpreted as "failure to enqueue buffer".
+ */
+ int ( * post_send ) ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct ib_address_vector *av,
+ struct io_buffer *iobuf );
+ /** Post receive work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ *
+ * If this method returns success, the I/O buffer remains
+ * owned by the queue pair. If this method returns failure,
+ * the I/O buffer is immediately released; the failure is
+ * interpreted as "failure to enqueue buffer".
+ */
+ int ( * post_recv ) ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct io_buffer *iobuf );
+ /** Poll completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ * @v complete_send Send completion handler
+ * @v complete_recv Receive completion handler
+ *
+ * The completion handler takes ownership of the I/O buffer.
+ */
+ void ( * poll_cq ) ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq,
+ ib_completer_t complete_send,
+ ib_completer_t complete_recv );
+ /**
+ * Open port
+ *
+ * @v ibdev Infiniband device
+ * @ret rc Return status code
+ */
+ int ( * open ) ( struct ib_device *ibdev );
+ /**
+ * Close port
+ *
+ * @v ibdev Infiniband device
+ */
+ void ( * close ) ( struct ib_device *ibdev );
+ /** Attach to multicast group
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v gid Multicast GID
+ * @ret rc Return status code
+ */
+ int ( * mcast_attach ) ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct ib_gid *gid );
+ /** Detach from multicast group
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v gid Multicast GID
+ */
+ void ( * mcast_detach ) ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct ib_gid *gid );
+ /**
+ * Issue management datagram
+ *
+ * @v ibdev Infiniband device
+ * @v mad Management datagram
+ * @v len Length of management datagram
+ * @ret rc Return status code
+ */
+ int ( * mad ) ( struct ib_device *ibdev, struct ib_mad_hdr *mad,
+ size_t len );
+};
+
+/** An Infiniband device */
+struct ib_device {
+ /** Underlying device */
+ struct device *dev;
+ /** Infiniband operations */
+ struct ib_device_operations *op;
+ /** Port number */
+ unsigned int port;
+ /** Port GID */
+ struct ib_gid port_gid;
+ /** Subnet manager LID */
+ unsigned long sm_lid;
+ /** Partition key */
+ unsigned int pkey;
+ /** Driver private data */
+ void *drv_priv;
+ /** Owner private data */
+ void *owner_priv;
+};
+
+extern struct ib_completion_queue * ib_create_cq ( struct ib_device *ibdev,
+ unsigned int num_cqes );
+extern void ib_destroy_cq ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq );
+extern struct ib_queue_pair *
+ib_create_qp ( struct ib_device *ibdev, unsigned int num_send_wqes,
+ struct ib_completion_queue *send_cq, unsigned int num_recv_wqes,
+ struct ib_completion_queue *recv_cq, unsigned long qkey );
+extern void ib_destroy_qp ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp );
+extern struct ib_work_queue * ib_find_wq ( struct ib_completion_queue *cq,
+ unsigned long qpn, int is_send );
+extern struct ib_device * alloc_ibdev ( size_t priv_size );
+extern int register_ibdev ( struct ib_device *ibdev );
+extern void unregister_ibdev ( struct ib_device *ibdev );
+extern void free_ibdev ( struct ib_device *ibdev );
+
+/**
+ * Post send work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v av Address vector
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+ struct ib_address_vector *av, struct io_buffer *iobuf ) {
+ return ibdev->op->post_send ( ibdev, qp, av, iobuf );
+}
+
+/**
+ * Post receive work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+ struct io_buffer *iobuf ) {
+ return ibdev->op->post_recv ( ibdev, qp, iobuf );
+}
+
+/**
+ * Poll completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ * @v complete_send Send completion handler
+ * @v complete_recv Receive completion handler
+ */
+static inline __attribute__ (( always_inline )) void
+ib_poll_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq,
+ ib_completer_t complete_send, ib_completer_t complete_recv ) {
+ ibdev->op->poll_cq ( ibdev, cq, complete_send, complete_recv );
+}
+
+/**
+ * Open port
+ *
+ * @v ibdev Infiniband device
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+ib_open ( struct ib_device *ibdev ) {
+ return ibdev->op->open ( ibdev );
+}
+
+/**
+ * Close port
+ *
+ * @v ibdev Infiniband device
+ */
+static inline __attribute__ (( always_inline )) void
+ib_close ( struct ib_device *ibdev ) {
+ ibdev->op->close ( ibdev );
+}
+
+/**
+ * Attach to multicast group
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v gid Multicast GID
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+ib_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+ struct ib_gid *gid ) {
+ return ibdev->op->mcast_attach ( ibdev, qp, gid );
+}
+
+/**
+ * Detach from multicast group
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v gid Multicast GID
+ */
+static inline __attribute__ (( always_inline )) void
+ib_mcast_detach ( struct ib_device *ibdev, struct ib_queue_pair *qp,
+ struct ib_gid *gid ) {
+ ibdev->op->mcast_detach ( ibdev, qp, gid );
+}
+
+/**
+ * Issue management datagram
+ *
+ * @v ibdev Infiniband device
+ * @v mad Management datagram
+ * @v len Length of management datagram
+ * @ret rc Return status code
+ */
+static inline __attribute__ (( always_inline )) int
+ib_mad ( struct ib_device *ibdev, struct ib_mad_hdr *mad, size_t len ) {
+ return ibdev->op->mad ( ibdev, mad, len );
+}
+
+/**
+ * Set Infiniband work queue driver-private data
+ *
+ * @v wq Work queue
+ * @v priv Private data
+ */
+static inline __attribute__ (( always_inline )) void
+ib_wq_set_drvdata ( struct ib_work_queue *wq, void *priv ) {
+ wq->drv_priv = priv;
+}
+
+/**
+ * Get Infiniband work queue driver-private data
+ *
+ * @v wq Work queue
+ * @ret priv Private data
+ */
+static inline __attribute__ (( always_inline )) void *
+ib_wq_get_drvdata ( struct ib_work_queue *wq ) {
+ return wq->drv_priv;
+}
+
+/**
+ * Set Infiniband queue pair driver-private data
+ *
+ * @v qp Queue pair
+ * @v priv Private data
+ */
+static inline __attribute__ (( always_inline )) void
+ib_qp_set_drvdata ( struct ib_queue_pair *qp, void *priv ) {
+ qp->drv_priv = priv;
+}
+
+/**
+ * Get Infiniband queue pair driver-private data
+ *
+ * @v qp Queue pair
+ * @ret priv Private data
+ */
+static inline __attribute__ (( always_inline )) void *
+ib_qp_get_drvdata ( struct ib_queue_pair *qp ) {
+ return qp->drv_priv;
+}
+
+/**
+ * Set Infiniband queue pair owner-private data
+ *
+ * @v qp Queue pair
+ * @v priv Private data
+ */
+static inline __attribute__ (( always_inline )) void
+ib_qp_set_ownerdata ( struct ib_queue_pair *qp, void *priv ) {
+ qp->owner_priv = priv;
+}
+
+/**
+ * Get Infiniband queue pair owner-private data
+ *
+ * @v qp Queue pair
+ * @ret priv Private data
+ */
+static inline __attribute__ (( always_inline )) void *
+ib_qp_get_ownerdata ( struct ib_queue_pair *qp ) {
+ return qp->owner_priv;
+}
+
+/**
+ * Set Infiniband completion queue driver-private data
+ *
+ * @v cq Completion queue
+ * @v priv Private data
+ */
+static inline __attribute__ (( always_inline )) void
+ib_cq_set_drvdata ( struct ib_completion_queue *cq, void *priv ) {
+ cq->drv_priv = priv;
+}
+
+/**
+ * Get Infiniband completion queue driver-private data
+ *
+ * @v cq Completion queue
+ * @ret priv Private data
+ */
+static inline __attribute__ (( always_inline )) void *
+ib_cq_get_drvdata ( struct ib_completion_queue *cq ) {
+ return cq->drv_priv;
+}
+
+/**
+ * Set Infiniband device driver-private data
+ *
+ * @v ibdev Infiniband device
+ * @v priv Private data
+ */
+static inline __attribute__ (( always_inline )) void
+ib_set_drvdata ( struct ib_device *ibdev, void *priv ) {
+ ibdev->drv_priv = priv;
+}
+
+/**
+ * Get Infiniband device driver-private data
+ *
+ * @v ibdev Infiniband device
+ * @ret priv Private data
+ */
+static inline __attribute__ (( always_inline )) void *
+ib_get_drvdata ( struct ib_device *ibdev ) {
+ return ibdev->drv_priv;
+}
+
+/**
+ * Set Infiniband device owner-private data
+ *
+ * @v ibdev Infiniband device
+ * @v priv Private data
+ */
+static inline __attribute__ (( always_inline )) void
+ib_set_ownerdata ( struct ib_device *ibdev, void *priv ) {
+ ibdev->owner_priv = priv;
+}
+
+/**
+ * Get Infiniband device owner-private data
+ *
+ * @v ibdev Infiniband device
+ * @ret priv Private data
+ */
+static inline __attribute__ (( always_inline )) void *
+ib_get_ownerdata ( struct ib_device *ibdev ) {
+ return ibdev->owner_priv;
+}
+
+/*****************************************************************************
+ *
+ * Management datagrams
+ *
+ * Portions Copyright (c) 2004 Mellanox Technologies Ltd. All rights
+ * reserved.
+ *
+ */
+
+/* Management base version */
+#define IB_MGMT_BASE_VERSION 1
+
+/* Management classes */
+#define IB_MGMT_CLASS_SUBN_LID_ROUTED 0x01
+#define IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE 0x81
+#define IB_MGMT_CLASS_SUBN_ADM 0x03
+#define IB_MGMT_CLASS_PERF_MGMT 0x04
+#define IB_MGMT_CLASS_BM 0x05
+#define IB_MGMT_CLASS_DEVICE_MGMT 0x06
+#define IB_MGMT_CLASS_CM 0x07
+#define IB_MGMT_CLASS_SNMP 0x08
+#define IB_MGMT_CLASS_VENDOR_RANGE2_START 0x30
+#define IB_MGMT_CLASS_VENDOR_RANGE2_END 0x4F
+
+/* Management methods */
+#define IB_MGMT_METHOD_GET 0x01
+#define IB_MGMT_METHOD_SET 0x02
+#define IB_MGMT_METHOD_GET_RESP 0x81
+#define IB_MGMT_METHOD_SEND 0x03
+#define IB_MGMT_METHOD_TRAP 0x05
+#define IB_MGMT_METHOD_REPORT 0x06
+#define IB_MGMT_METHOD_REPORT_RESP 0x86
+#define IB_MGMT_METHOD_TRAP_REPRESS 0x07
+#define IB_MGMT_METHOD_DELETE 0x15
+#define IB_MGMT_METHOD_RESP 0x80
+
+/* Subnet management attributes */
+#define IB_SMP_ATTR_NOTICE 0x0002
+#define IB_SMP_ATTR_NODE_DESC 0x0010
+#define IB_SMP_ATTR_NODE_INFO 0x0011
+#define IB_SMP_ATTR_SWITCH_INFO 0x0012
+#define IB_SMP_ATTR_GUID_INFO 0x0014
+#define IB_SMP_ATTR_PORT_INFO 0x0015
+#define IB_SMP_ATTR_PKEY_TABLE 0x0016
+#define IB_SMP_ATTR_SL_TO_VL_TABLE 0x0017
+#define IB_SMP_ATTR_VL_ARB_TABLE 0x0018
+#define IB_SMP_ATTR_LINEAR_FORWARD_TABLE 0x0019
+#define IB_SMP_ATTR_RANDOM_FORWARD_TABLE 0x001A
+#define IB_SMP_ATTR_MCAST_FORWARD_TABLE 0x001B
+#define IB_SMP_ATTR_SM_INFO 0x0020
+#define IB_SMP_ATTR_VENDOR_DIAG 0x0030
+#define IB_SMP_ATTR_LED_INFO 0x0031
+#define IB_SMP_ATTR_VENDOR_MASK 0xFF00
+
+#define IB_SA_ATTR_MC_MEMBER_REC 0x38
+#define IB_SA_ATTR_PATH_REC 0x35
+
+#define IB_SA_MCMEMBER_REC_MGID (1<<0)
+#define IB_SA_MCMEMBER_REC_PORT_GID (1<<1)
+#define IB_SA_MCMEMBER_REC_QKEY (1<<2)
+#define IB_SA_MCMEMBER_REC_MLID (1<<3)
+#define IB_SA_MCMEMBER_REC_MTU_SELECTOR (1<<4)
+#define IB_SA_MCMEMBER_REC_MTU (1<<5)
+#define IB_SA_MCMEMBER_REC_TRAFFIC_CLASS (1<<6)
+#define IB_SA_MCMEMBER_REC_PKEY (1<<7)
+#define IB_SA_MCMEMBER_REC_RATE_SELECTOR (1<<8)
+#define IB_SA_MCMEMBER_REC_RATE (1<<9)
+#define IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME_SELECTOR (1<<10)
+#define IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME (1<<11)
+#define IB_SA_MCMEMBER_REC_SL (1<<12)
+#define IB_SA_MCMEMBER_REC_FLOW_LABEL (1<<13)
+#define IB_SA_MCMEMBER_REC_HOP_LIMIT (1<<14)
+#define IB_SA_MCMEMBER_REC_SCOPE (1<<15)
+#define IB_SA_MCMEMBER_REC_JOIN_STATE (1<<16)
+#define IB_SA_MCMEMBER_REC_PROXY_JOIN (1<<17)
+
+#define IB_SA_PATH_REC_DGID (1<<2)
+#define IB_SA_PATH_REC_SGID (1<<3)
+
+struct ib_mad_hdr {
+ uint8_t base_version;
+ uint8_t mgmt_class;
+ uint8_t class_version;
+ uint8_t method;
+ uint16_t status;
+ uint16_t class_specific;
+ uint32_t tid[2];
+ uint16_t attr_id;
+ uint16_t resv;
+ uint32_t attr_mod;
+} __attribute__ (( packed ));
+
+struct ib_sa_hdr {
+ uint32_t sm_key[2];
+ uint16_t reserved;
+ uint16_t attrib_offset;
+ uint32_t comp_mask[2];
+} __attribute__ (( packed ));
+
+struct ib_rmpp_hdr {
+ uint32_t raw[3];
+} __attribute__ (( packed ));
+
+struct ib_mad_data {
+ struct ib_mad_hdr mad_hdr;
+ uint8_t data[232];
+} __attribute__ (( packed ));
+
+struct ib_mad_guid_info {
+ struct ib_mad_hdr mad_hdr;
+ uint32_t mkey[2];
+ uint32_t reserved[8];
+ uint8_t gid_local[8];
+} __attribute__ (( packed ));
+
+struct ib_mad_port_info {
+ struct ib_mad_hdr mad_hdr;
+ uint32_t mkey[2];
+ uint32_t reserved[8];
+ uint32_t mkey2[2];
+ uint8_t gid_prefix[8];
+ uint16_t lid;
+ uint16_t mastersm_lid;
+ uint32_t cap_mask;
+ uint16_t diag_code;
+ uint16_t mkey_lease_period;
+ uint8_t local_port_num;
+ uint8_t link_width_enabled;
+ uint8_t link_width_supported;
+ uint8_t link_width_active;
+ uint8_t port_state__link_speed_supported;
+ uint8_t link_down_def_state__port_phys_state;
+ uint8_t lmc__r1__mkey_prot_bits;
+ uint8_t link_speed_enabled__link_speed_active;
+} __attribute__ (( packed ));
+
+struct ib_mad_pkey_table {
+ struct ib_mad_hdr mad_hdr;
+ uint32_t mkey[2];
+ uint32_t reserved[8];
+ uint16_t pkey[16][2];
+} __attribute__ (( packed ));
+
+struct ib_mad_path_record {
+ struct ib_mad_hdr mad_hdr;
+ struct ib_rmpp_hdr rmpp_hdr;
+ struct ib_sa_hdr sa_hdr;
+ uint32_t reserved0[2];
+ struct ib_gid dgid;
+ struct ib_gid sgid;
+ uint16_t dlid;
+ uint16_t slid;
+ uint32_t hop_limit__flow_label__raw_traffic;
+ uint32_t pkey__numb_path__reversible__tclass;
+ uint8_t reserved1;
+ uint8_t reserved__sl;
+ uint8_t mtu_selector__mtu;
+ uint8_t rate_selector__rate;
+ uint32_t preference__packet_lifetime__packet_lifetime_selector;
+ uint32_t reserved2[35];
+} __attribute__ (( packed ));
+
+struct ib_mad_mc_member_record {
+ struct ib_mad_hdr mad_hdr;
+ struct ib_rmpp_hdr rmpp_hdr;
+ struct ib_sa_hdr sa_hdr;
+ struct ib_gid mgid;
+ struct ib_gid port_gid;
+ uint32_t qkey;
+ uint16_t mlid;
+ uint8_t mtu_selector__mtu;
+ uint8_t tclass;
+ uint16_t pkey;
+ uint8_t rate_selector__rate;
+ uint8_t packet_lifetime_selector__packet_lifetime;
+ uint32_t sl__flow_label__hop_limit;
+ uint8_t scope__join_state;
+ uint8_t proxy_join__reserved;
+ uint16_t reserved0;
+ uint32_t reserved1[37];
+} __attribute__ (( packed ));
+
+union ib_mad {
+ struct ib_mad_hdr mad_hdr;
+ struct ib_mad_data data;
+ struct ib_mad_guid_info guid_info;
+ struct ib_mad_port_info port_info;
+ struct ib_mad_pkey_table pkey_table;
+ struct ib_mad_path_record path_record;
+ struct ib_mad_mc_member_record mc_member_record;
+} __attribute__ (( packed ));
+
+#endif /* _GPXE_INFINIBAND_H */
diff --git a/gpxe/src/include/gpxe/init.h b/gpxe/src/include/gpxe/init.h
new file mode 100644
index 00000000..d83aa5e5
--- /dev/null
+++ b/gpxe/src/include/gpxe/init.h
@@ -0,0 +1,62 @@
+#ifndef _GPXE_INIT_H
+#define _GPXE_INIT_H
+
+#include <gpxe/tables.h>
+
+/**
+ * An initialisation function
+ *
+ * Initialisation functions are called exactly once, as part of the
+ * call to initialise().
+ */
+struct init_fn {
+ void ( * initialise ) ( void );
+};
+
+/** Declare an initialisation functon */
+#define __init_fn( init_order ) \
+ __table ( struct init_fn, init_fns, init_order )
+
+/** @defgroup initfn_order Initialisation function ordering
+ * @{
+ */
+
+#define INIT_EARLY 01 /**< Early initialisation */
+#define INIT_CONSOLE 02 /**< Console initialisation */
+#define INIT_NORMAL 03 /**< Normal initialisation */
+
+/** @} */
+
+/**
+ * A startup/shutdown function
+ *
+ * Startup and shutdown functions may be called multiple times, as
+ * part of the calls to startup() and shutdown().
+ */
+struct startup_fn {
+ void ( * startup ) ( void );
+ void ( * shutdown ) ( void );
+};
+
+/** Declare a startup/shutdown function */
+#define __startup_fn( startup_order ) \
+ __table ( struct startup_fn, startup_fns, startup_order )
+
+/** @defgroup startfn_order Startup/shutdown function ordering
+ *
+ * Shutdown functions are called in the reverse order to startup
+ * functions.
+ *
+ * @{
+ */
+
+#define STARTUP_EARLY 01 /**< Early startup */
+#define STARTUP_NORMAL 02 /**< Normal startup */
+
+/** @} */
+
+extern void initialise ( void );
+extern void startup ( void );
+extern void shutdown ( void );
+
+#endif /* _GPXE_INIT_H */
diff --git a/gpxe/src/include/gpxe/initrd.h b/gpxe/src/include/gpxe/initrd.h
new file mode 100644
index 00000000..1871bf78
--- /dev/null
+++ b/gpxe/src/include/gpxe/initrd.h
@@ -0,0 +1,14 @@
+#ifndef _GPXE_INITRD_H
+#define _GPXE_INITRD_H
+
+/**
+ * @file
+ *
+ * Linux initrd image format
+ *
+ */
+
+#include <gpxe/image.h>
+extern struct image_type initrd_image_type __image_type ( PROBE_NORMAL );
+
+#endif /* _GPXE_INITRD_H */
diff --git a/gpxe/src/include/gpxe/interface.h b/gpxe/src/include/gpxe/interface.h
new file mode 100644
index 00000000..94c711a9
--- /dev/null
+++ b/gpxe/src/include/gpxe/interface.h
@@ -0,0 +1,56 @@
+#ifndef _GPXE_INTERFACE_H
+#define _GPXE_INTERFACE_H
+
+/** @file
+ *
+ * Object communication interfaces
+ *
+ */
+
+#include <gpxe/refcnt.h>
+
+/** An object communication interface */
+struct interface {
+ /** Destination interface
+ *
+ * When messages are sent via this interface, they will be
+ * delivered to the destination interface.
+ *
+ * This pointer may never be NULL. When the interface is
+ * unplugged, it should point to a null interface.
+ */
+ struct interface *dest;
+ /** Reference counter
+ *
+ * If this interface is not part of a reference-counted
+ * object, this field may be NULL.
+ */
+ struct refcnt *refcnt;
+};
+
+/**
+ * Increment reference count on an interface
+ *
+ * @v intf Interface
+ * @ret intf Interface
+ */
+static inline __attribute__ (( always_inline )) struct interface *
+intf_get ( struct interface *intf ) {
+ ref_get ( intf->refcnt );
+ return intf;
+}
+
+/**
+ * Decrement reference count on an interface
+ *
+ * @v intf Interface
+ */
+static inline __attribute__ (( always_inline )) void
+intf_put ( struct interface *intf ) {
+ ref_put ( intf->refcnt );
+}
+
+extern void plug ( struct interface *intf, struct interface *dest );
+extern void plug_plug ( struct interface *a, struct interface *b );
+
+#endif /* _GPXE_INTERFACE_H */
diff --git a/gpxe/src/include/gpxe/iobuf.h b/gpxe/src/include/gpxe/iobuf.h
new file mode 100644
index 00000000..ff787754
--- /dev/null
+++ b/gpxe/src/include/gpxe/iobuf.h
@@ -0,0 +1,189 @@
+#ifndef _GPXE_IOBUF_H
+#define _GPXE_IOBUF_H
+
+/** @file
+ *
+ * I/O buffers
+ *
+ */
+
+#include <stdint.h>
+#include <assert.h>
+#include <gpxe/list.h>
+
+/**
+ * I/O buffer alignment
+ *
+ * I/O buffers allocated via alloc_iob() are guaranteed to be
+ * physically aligned to this boundary. Some cards cannot DMA across
+ * a 4kB boundary. With a standard Ethernet MTU, aligning to a 2kB
+ * boundary is sufficient to guarantee no 4kB boundary crossings. For
+ * a jumbo Ethernet MTU, a packet may be larger than 4kB anyway.
+ */
+#define IOB_ALIGN 2048
+
+/**
+ * Minimum I/O buffer length
+ *
+ * alloc_iob() will round up the allocated length to this size if
+ * necessary. This is used on behalf of hardware that is not capable
+ * of auto-padding.
+ */
+#define IOB_ZLEN 64
+
+/**
+ * A persistent I/O buffer
+ *
+ * This data structure encapsulates a long-lived I/O buffer. The
+ * buffer may be passed between multiple owners, queued for possible
+ * retransmission, etc.
+ */
+struct io_buffer {
+ /** List of which this buffer is a member
+ *
+ * The list must belong to the current owner of the buffer.
+ * Different owners may maintain different lists (e.g. a
+ * retransmission list for TCP).
+ */
+ struct list_head list;
+
+ /** Start of the buffer */
+ void *head;
+ /** Start of data */
+ void *data;
+ /** End of data */
+ void *tail;
+ /** End of the buffer */
+ void *end;
+};
+
+/**
+ * Reserve space at start of I/O buffer
+ *
+ * @v iobuf I/O buffer
+ * @v len Length to reserve
+ * @ret data Pointer to new start of buffer
+ */
+static inline void * iob_reserve ( struct io_buffer *iobuf, size_t len ) {
+ iobuf->data += len;
+ iobuf->tail += len;
+ return iobuf->data;
+}
+#define iob_reserve( iobuf, len ) ( { \
+ void *__result; \
+ __result = iob_reserve ( (iobuf), (len) ); \
+ assert ( (iobuf)->tail <= (iobuf)->end ); \
+ __result; } )
+
+/**
+ * Add data to start of I/O buffer
+ *
+ * @v iobuf I/O buffer
+ * @v len Length to add
+ * @ret data Pointer to new start of buffer
+ */
+static inline void * iob_push ( struct io_buffer *iobuf, size_t len ) {
+ iobuf->data -= len;
+ return iobuf->data;
+}
+#define iob_push( iobuf, len ) ( { \
+ void *__result; \
+ __result = iob_push ( (iobuf), (len) ); \
+ assert ( (iobuf)->data >= (iobuf)->head ); \
+ __result; } )
+
+/**
+ * Remove data from start of I/O buffer
+ *
+ * @v iobuf I/O buffer
+ * @v len Length to remove
+ * @ret data Pointer to new start of buffer
+ */
+static inline void * iob_pull ( struct io_buffer *iobuf, size_t len ) {
+ iobuf->data += len;
+ assert ( iobuf->data <= iobuf->tail );
+ return iobuf->data;
+}
+#define iob_pull( iobuf, len ) ( { \
+ void *__result; \
+ __result = iob_pull ( (iobuf), (len) ); \
+ assert ( (iobuf)->data <= (iobuf)->tail ); \
+ __result; } )
+
+/**
+ * Add data to end of I/O buffer
+ *
+ * @v iobuf I/O buffer
+ * @v len Length to add
+ * @ret data Pointer to newly added space
+ */
+static inline void * iob_put ( struct io_buffer *iobuf, size_t len ) {
+ void *old_tail = iobuf->tail;
+ iobuf->tail += len;
+ return old_tail;
+}
+#define iob_put( iobuf, len ) ( { \
+ void *__result; \
+ __result = iob_put ( (iobuf), (len) ); \
+ assert ( (iobuf)->tail <= (iobuf)->end ); \
+ __result; } )
+
+/**
+ * Remove data from end of I/O buffer
+ *
+ * @v iobuf I/O buffer
+ * @v len Length to remove
+ */
+static inline void iob_unput ( struct io_buffer *iobuf, size_t len ) {
+ iobuf->tail -= len;
+}
+#define iob_unput( iobuf, len ) do { \
+ iob_unput ( (iobuf), (len) ); \
+ assert ( (iobuf)->tail >= (iobuf)->data ); \
+ } while ( 0 )
+
+/**
+ * Empty an I/O buffer
+ *
+ * @v iobuf I/O buffer
+ */
+static inline void iob_empty ( struct io_buffer *iobuf ) {
+ iobuf->tail = iobuf->data;
+}
+
+/**
+ * Calculate length of data in an I/O buffer
+ *
+ * @v iobuf I/O buffer
+ * @ret len Length of data in buffer
+ */
+static inline size_t iob_len ( struct io_buffer *iobuf ) {
+ return ( iobuf->tail - iobuf->data );
+}
+
+/**
+ * Calculate available space at start of an I/O buffer
+ *
+ * @v iobuf I/O buffer
+ * @ret len Length of data available at start of buffer
+ */
+static inline size_t iob_headroom ( struct io_buffer *iobuf ) {
+ return ( iobuf->data - iobuf->head );
+}
+
+/**
+ * Calculate available space at end of an I/O buffer
+ *
+ * @v iobuf I/O buffer
+ * @ret len Length of data available at end of buffer
+ */
+static inline size_t iob_tailroom ( struct io_buffer *iobuf ) {
+ return ( iobuf->end - iobuf->tail );
+}
+
+extern struct io_buffer * __malloc alloc_iob ( size_t len );
+extern void free_iob ( struct io_buffer *iobuf );
+extern void iob_pad ( struct io_buffer *iobuf, size_t min_len );
+extern int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len );
+
+#endif /* _GPXE_IOBUF_H */
diff --git a/gpxe/src/include/gpxe/ip.h b/gpxe/src/include/gpxe/ip.h
new file mode 100644
index 00000000..b1b0c48c
--- /dev/null
+++ b/gpxe/src/include/gpxe/ip.h
@@ -0,0 +1,95 @@
+#ifndef _GPXE_IP_H
+#define _GPXE_IP_H
+
+/** @file
+ *
+ * IP protocol
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/in.h>
+#include <gpxe/list.h>
+#include <gpxe/retry.h>
+
+struct io_buffer;
+struct net_device;
+struct net_protocol;
+
+/* IP constants */
+
+#define IP_VER 0x40U
+#define IP_MASK_VER 0xf0U
+#define IP_MASK_HLEN 0x0fU
+#define IP_MASK_OFFSET 0x1fffU
+#define IP_MASK_DONOTFRAG 0x4000U
+#define IP_MASK_MOREFRAGS 0x2000U
+#define IP_PSHLEN 12
+
+/* IP header defaults */
+#define IP_TOS 0
+#define IP_TTL 64
+
+#define IP_FRAG_IOB_SIZE 1500
+#define IP_FRAG_TIMEOUT 50
+
+/** An IPv4 packet header */
+struct iphdr {
+ uint8_t verhdrlen;
+ uint8_t service;
+ uint16_t len;
+ uint16_t ident;
+ uint16_t frags;
+ uint8_t ttl;
+ uint8_t protocol;
+ uint16_t chksum;
+ struct in_addr src;
+ struct in_addr dest;
+} __attribute__ (( packed ));
+
+/** An IPv4 pseudo header */
+struct ipv4_pseudo_header {
+ struct in_addr src;
+ struct in_addr dest;
+ uint8_t zero_padding;
+ uint8_t protocol;
+ uint16_t len;
+};
+
+/** An IPv4 address/routing table entry */
+struct ipv4_miniroute {
+ /** List of miniroutes */
+ struct list_head list;
+
+ /** Network device */
+ struct net_device *netdev;
+
+ /** IPv4 address */
+ struct in_addr address;
+ /** Subnet mask */
+ struct in_addr netmask;
+ /** Gateway address */
+ struct in_addr gateway;
+};
+
+/* Fragment reassembly buffer */
+struct frag_buffer {
+ /* Identification number */
+ uint16_t ident;
+ /* Source network address */
+ struct in_addr src;
+ /* Destination network address */
+ struct in_addr dest;
+ /* Reassembled I/O buffer */
+ struct io_buffer *frag_iob;
+ /* Reassembly timer */
+ struct retry_timer frag_timer;
+ /* List of fragment reassembly buffers */
+ struct list_head list;
+};
+
+extern struct list_head ipv4_miniroutes;
+
+extern struct net_protocol ipv4_protocol;
+
+#endif /* _GPXE_IP_H */
diff --git a/gpxe/src/include/gpxe/ip6.h b/gpxe/src/include/gpxe/ip6.h
new file mode 100644
index 00000000..dc0ae31f
--- /dev/null
+++ b/gpxe/src/include/gpxe/ip6.h
@@ -0,0 +1,78 @@
+#ifndef _GPXE_IP6_H
+#define _GPXE_IP6_H
+
+/** @file
+ *
+ * IP6 protocol
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/in.h>
+
+/* IP6 constants */
+
+#define IP6_VERSION 0x6
+#define IP6_HOP_LIMIT 255
+
+/**
+ * I/O buffer contents
+ * This is duplicated in tcp.h and here. Ideally it should go into iobuf.h
+ */
+#define MAX_HDR_LEN 100
+#define MAX_IOB_LEN 1500
+#define MIN_IOB_LEN MAX_HDR_LEN + 100 /* To account for padding by LL */
+
+#define IP6_EQUAL( in6_addr1, in6_addr2 ) \
+ ( memcmp ( ( char* ) &( in6_addr1 ), ( char* ) &( in6_addr2 ),\
+ sizeof ( struct in6_addr ) ) == 0 )
+
+#define IS_UNSPECIFIED( addr ) \
+ ( ( (addr).in6_u.u6_addr32[0] == 0x00000000 ) && \
+ ( (addr).in6_u.u6_addr32[1] == 0x00000000 ) && \
+ ( (addr).in6_u.u6_addr32[2] == 0x00000000 ) && \
+ ( (addr).in6_u.u6_addr32[3] == 0x00000000 ) )
+/* IP6 header */
+struct ip6_header {
+ uint32_t ver_traffic_class_flow_label;
+ uint16_t payload_len;
+ uint8_t nxt_hdr;
+ uint8_t hop_limit;
+ struct in6_addr src;
+ struct in6_addr dest;
+};
+
+/* IP6 pseudo header */
+struct ipv6_pseudo_header {
+ struct in6_addr src;
+ struct in6_addr dest;
+ uint8_t zero_padding;
+ uint8_t nxt_hdr;
+ uint16_t len;
+};
+
+/* Next header numbers */
+#define IP6_HOPBYHOP 0x00
+#define IP6_ROUTING 0x43
+#define IP6_FRAGMENT 0x44
+#define IP6_AUTHENTICATION 0x51
+#define IP6_DEST_OPTS 0x60
+#define IP6_ESP 0x50
+#define IP6_ICMP6 0x58
+#define IP6_NO_HEADER 0x59
+
+struct io_buffer;
+struct net_device;
+struct net_protocol;
+
+extern struct net_protocol ipv6_protocol;
+extern struct tcpip_net_protocol ipv6_tcpip_protocol;
+extern char * inet6_ntoa ( struct in6_addr in6 );
+
+extern int add_ipv6_address ( struct net_device *netdev,
+ struct in6_addr prefix, int prefix_len,
+ struct in6_addr address,
+ struct in6_addr gateway );
+extern void del_ipv6_address ( struct net_device *netdev );
+
+#endif /* _GPXE_IP6_H */
diff --git a/gpxe/src/include/gpxe/ipoib.h b/gpxe/src/include/gpxe/ipoib.h
new file mode 100644
index 00000000..0551687d
--- /dev/null
+++ b/gpxe/src/include/gpxe/ipoib.h
@@ -0,0 +1,78 @@
+#ifndef _GPXE_IPOIB_H
+#define _GPXE_IPOIB_H
+
+/** @file
+ *
+ * IP over Infiniband
+ */
+
+#include <gpxe/infiniband.h>
+
+/** IPoIB MAC address length */
+#define IPOIB_ALEN 20
+
+/** An IPoIB MAC address */
+struct ipoib_mac {
+ /** Queue pair number
+ *
+ * MSB must be zero; QPNs are only 24-bit.
+ */
+ uint32_t qpn;
+ /** Port GID */
+ struct ib_gid gid;
+} __attribute__ (( packed ));
+
+/** IPoIB link-layer header length */
+#define IPOIB_HLEN 24
+
+/**
+ * IPoIB link-layer header pseudo portion
+ *
+ * This part doesn't actually exist on the wire, but it provides a
+ * convenient way to fit into the typical network device model.
+ */
+struct ipoib_pseudo_hdr {
+ /** Peer address */
+ struct ipoib_mac peer;
+} __attribute__ (( packed ));
+
+/** IPoIB link-layer header real portion */
+struct ipoib_real_hdr {
+ /** Network-layer protocol */
+ uint16_t proto;
+ /** Reserved, must be zero */
+ uint16_t reserved;
+} __attribute__ (( packed ));
+
+/** An IPoIB link-layer header */
+struct ipoib_hdr {
+ /** Pseudo portion */
+ struct ipoib_pseudo_hdr pseudo;
+ /** Real portion */
+ struct ipoib_real_hdr real;
+} __attribute__ (( packed ));
+
+extern struct ll_protocol ipoib_protocol;
+
+extern const char * ipoib_ntoa ( const void *ll_addr );
+
+/**
+ * Allocate IPoIB device
+ *
+ * @v priv_size Size of driver private data
+ * @ret netdev Network device, or NULL
+ */
+static inline struct net_device * alloc_ipoibdev ( size_t priv_size ) {
+ struct net_device *netdev;
+
+ netdev = alloc_netdev ( priv_size );
+ if ( netdev ) {
+ netdev->ll_protocol = &ipoib_protocol;
+ }
+ return netdev;
+}
+
+extern int ipoib_probe ( struct ib_device *ibdev );
+extern void ipoib_remove ( struct ib_device *ibdev );
+
+#endif /* _GPXE_IPOIB_H */
diff --git a/gpxe/src/include/gpxe/isa.h b/gpxe/src/include/gpxe/isa.h
new file mode 100644
index 00000000..bb25dbce
--- /dev/null
+++ b/gpxe/src/include/gpxe/isa.h
@@ -0,0 +1,92 @@
+#ifndef ISA_H
+#define ISA_H
+
+#include <stdint.h>
+#include <gpxe/isa_ids.h>
+#include <gpxe/device.h>
+#include <gpxe/tables.h>
+
+/** An ISA device */
+struct isa_device {
+ /** Generic device */
+ struct device dev;
+ /** I/O address */
+ uint16_t ioaddr;
+ /** Driver for this device */
+ struct isa_driver *driver;
+ /** Driver-private data
+ *
+ * Use isa_set_drvdata() and isa_get_drvdata() to access
+ * this field.
+ */
+ void *priv;
+ /** Driver name */
+ const char *driver_name;
+};
+
+/*
+ * An individual ISA device, identified by probe address
+ *
+ */
+typedef uint16_t isa_probe_addr_t;
+
+/** An ISA driver */
+struct isa_driver {
+ /** Name */
+ const char *name;
+ /** Probe address list */
+ isa_probe_addr_t *probe_addrs;
+ /** Number of entries in probe address list */
+ unsigned int addr_count;
+ /** Manufacturer ID to be assumed for this device */
+ uint16_t vendor_id;
+ /** Product ID to be assumed for this device */
+ uint16_t prod_id;
+ /**
+ * Probe device
+ *
+ * @v isa ISA device
+ * @v id Matching entry in ID table
+ * @ret rc Return status code
+ */
+ int ( * probe ) ( struct isa_device *isa );
+ /**
+ * Remove device
+ *
+ * @v isa ISA device
+ */
+ void ( * remove ) ( struct isa_device *isa );
+};
+
+/** Declare an ISA driver */
+#define __isa_driver __table ( struct isa_driver, isa_drivers, 01 )
+
+/**
+ * Set ISA driver-private data
+ *
+ * @v isa ISA device
+ * @v priv Private data
+ */
+static inline void isa_set_drvdata ( struct isa_device *isa, void *priv ) {
+ isa->priv = priv;
+}
+
+/**
+ * Get ISA driver-private data
+ *
+ * @v isa ISA device
+ * @ret priv Private data
+ */
+static inline void * isa_get_drvdata ( struct isa_device *isa ) {
+ return isa->priv;
+}
+
+/*
+ * ISA_ROM is parsed by parserom.pl to generate Makefile rules and
+ * files for rom-o-matic.
+ *
+ */
+#define ISA_ROM( IMAGE, DESCRIPTION )
+
+#endif /* ISA_H */
+
diff --git a/gpxe/src/include/gpxe/isa_ids.h b/gpxe/src/include/gpxe/isa_ids.h
new file mode 100644
index 00000000..bf3f1015
--- /dev/null
+++ b/gpxe/src/include/gpxe/isa_ids.h
@@ -0,0 +1,49 @@
+#ifndef ISA_IDS_H
+#define ISA_IDS_H
+
+/*
+ * This file defines IDs as used by ISAPnP and EISA devices. These
+ * IDs have the format:
+ *
+ * vendor byte 0 bit 7 must be zero
+ * bits 6-2 first vendor char in compressed ASCII
+ * bits 1-0 second vendor char in compressed ASCII (bits 4-3)
+ * byte 1 bits 7-5 second vendor char in compressed ASCII (bits 2-0)
+ * bits 4-0 third vendor char in compressed ASCII
+ * product byte 0 bits 7-4 first hex digit of product number
+ * bits 3-0 second hex digit of product number
+ * byte 1 bits 7-4 third hex digit of product number
+ * bits 3-0 hex digit of revision level
+ *
+ * ISA IDs are always expressed in little-endian order, even though
+ * the underlying "meaning" is big-endian.
+ */
+
+#include <byteswap.h>
+
+/*
+ * Construct a vendor ID from three ASCII characters
+ *
+ */
+#define ISA_VENDOR( a, b, c ) \
+ bswap_16 ( ( ( ( (a) - 'A' + 1 ) & 0x1f ) << 10 ) | \
+ ( ( ( (b) - 'A' + 1 ) & 0x1f ) << 5 ) | \
+ ( ( ( (c) - 'A' + 1 ) & 0x1f ) << 0 ) )
+
+#define ISAPNP_VENDOR( a, b, c ) ISA_VENDOR ( a, b, c )
+#define EISA_VENDOR( a, b, c ) ISA_VENDOR ( a, b, c )
+
+#define GENERIC_ISAPNP_VENDOR ISAPNP_VENDOR ( 'P','N','P' )
+
+/*
+ * Extract product ID and revision from combined product field
+ *
+ */
+#define ISA_PROD_ID_MASK ( 0xf0ff )
+#define ISA_PROD_ID(product) ( (product) & ISA_PROD_ID_MASK )
+#define ISA_PROD_REV(product) ( ( (product) & ~ISA_PROD_ID_MASK ) >> 8 )
+
+/* Functions in isa_ids.c */
+extern char * isa_id_string ( unsigned int vendor, unsigned int product );
+
+#endif /* ISA_IDS_H */
diff --git a/gpxe/src/include/gpxe/isapnp.h b/gpxe/src/include/gpxe/isapnp.h
new file mode 100644
index 00000000..07797a99
--- /dev/null
+++ b/gpxe/src/include/gpxe/isapnp.h
@@ -0,0 +1,273 @@
+/**************************************************************************
+*
+* isapnp.h -- Etherboot isapnp support for the 3Com 3c515
+* Written 2002-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code:
+* Copyright (C) 2001 P.J.H.Fox (fox@roestock.demon.co.uk)
+*
+*
+*
+* REVISION HISTORY:
+* ================
+* Version 0.1 April 26, 2002 TJL
+* Version 0.2 01/08/2003 TJL Renamed from 3c515_isapnp.h
+*
+*
+* Generalised into an ISAPnP bus that can be used by more than just
+* the 3c515 by Michael Brown <mbrown@fensystems.co.uk>
+*
+***************************************************************************/
+
+#ifndef ISAPNP_H
+#define ISAPNP_H
+
+#include <stdint.h>
+#include <gpxe/isa_ids.h>
+#include <gpxe/device.h>
+#include <gpxe/tables.h>
+
+/*
+ * ISAPnP constants
+ *
+ */
+
+/* Port addresses */
+#define ISAPNP_ADDRESS 0x279
+#define ISAPNP_WRITE_DATA 0xa79
+#define ISAPNP_READ_PORT_MIN 0x203
+#define ISAPNP_READ_PORT_START 0x213 /* ISAPnP spec says 0x203, but
+ * Linux ISAPnP starts at
+ * 0x213 with no explanatory
+ * comment. 0x203 probably
+ * clashes with something. */
+#define ISAPNP_READ_PORT_MAX 0x3ff
+#define ISAPNP_READ_PORT_STEP 0x10 /* Can be any multiple of 4
+ * according to the spec, but
+ * since ISA I/O addresses are
+ * allocated in blocks of 16,
+ * it makes no sense to use
+ * any value less than 16.
+ */
+
+/* Card select numbers */
+#define ISAPNP_CSN_MIN 0x01
+#define ISAPNP_CSN_MAX 0x0f
+
+/* Registers */
+#define ISAPNP_READPORT 0x00
+#define ISAPNP_SERIALISOLATION 0x01
+#define ISAPNP_CONFIGCONTROL 0x02
+#define ISAPNP_WAKE 0x03
+#define ISAPNP_RESOURCEDATA 0x04
+#define ISAPNP_STATUS 0x05
+#define ISAPNP_CARDSELECTNUMBER 0x06
+#define ISAPNP_LOGICALDEVICENUMBER 0x07
+#define ISAPNP_ACTIVATE 0x30
+#define ISAPNP_IORANGECHECK 0x31
+#define ISAPNP_IOBASE(n) ( 0x60 + ( (n) * 2 ) )
+#define ISAPNP_IRQNO(n) ( 0x70 + ( (n) * 2 ) )
+#define ISAPNP_IRQTYPE(n) ( 0x71 + ( (n) * 2 ) )
+
+/* Bits in the CONFIGCONTROL register */
+#define ISAPNP_CONFIG_RESET ( 1 << 0 )
+#define ISAPNP_CONFIG_WAIT_FOR_KEY ( 1 << 1 )
+#define ISAPNP_CONFIG_RESET_CSN ( 1 << 2 )
+#define ISAPNP_CONFIG_RESET_DRV ( ISAPNP_CONFIG_RESET | \
+ ISAPNP_CONFIG_WAIT_FOR_KEY | \
+ ISAPNP_CONFIG_RESET_CSN )
+
+/* The LFSR used for the initiation key and for checksumming */
+#define ISAPNP_LFSR_SEED 0x6a
+
+/* Small tags */
+#define ISAPNP_IS_SMALL_TAG(tag) ( ! ( (tag) & 0x80 ) )
+#define ISAPNP_SMALL_TAG_NAME(tag) ( ( (tag) >> 3 ) & 0xf )
+#define ISAPNP_SMALL_TAG_LEN(tag) ( ( (tag) & 0x7 ) )
+#define ISAPNP_TAG_PNPVERNO 0x01
+#define ISAPNP_TAG_LOGDEVID 0x02
+#define ISAPNP_TAG_COMPATDEVID 0x03
+#define ISAPNP_TAG_IRQ 0x04
+#define ISAPNP_TAG_DMA 0x05
+#define ISAPNP_TAG_STARTDEP 0x06
+#define ISAPNP_TAG_ENDDEP 0x07
+#define ISAPNP_TAG_IOPORT 0x08
+#define ISAPNP_TAG_FIXEDIO 0x09
+#define ISAPNP_TAG_RSVDSHORTA 0x0A
+#define ISAPNP_TAG_RSVDSHORTB 0x0B
+#define ISAPNP_TAG_RSVDSHORTC 0x0C
+#define ISAPNP_TAG_RSVDSHORTD 0x0D
+#define ISAPNP_TAG_VENDORSHORT 0x0E
+#define ISAPNP_TAG_END 0x0F
+/* Large tags */
+#define ISAPNP_IS_LARGE_TAG(tag) ( ( (tag) & 0x80 ) )
+#define ISAPNP_LARGE_TAG_NAME(tag) (tag)
+#define ISAPNP_TAG_MEMRANGE 0x81
+#define ISAPNP_TAG_ANSISTR 0x82
+#define ISAPNP_TAG_UNICODESTR 0x83
+#define ISAPNP_TAG_VENDORLONG 0x84
+#define ISAPNP_TAG_MEM32RANGE 0x85
+#define ISAPNP_TAG_FIXEDMEM32RANGE 0x86
+#define ISAPNP_TAG_RSVDLONG0 0xF0
+#define ISAPNP_TAG_RSVDLONG1 0xF1
+#define ISAPNP_TAG_RSVDLONG2 0xF2
+#define ISAPNP_TAG_RSVDLONG3 0xF3
+#define ISAPNP_TAG_RSVDLONG4 0xF4
+#define ISAPNP_TAG_RSVDLONG5 0xF5
+#define ISAPNP_TAG_RSVDLONG6 0xF6
+#define ISAPNP_TAG_RSVDLONG7 0xF7
+#define ISAPNP_TAG_RSVDLONG8 0xF8
+#define ISAPNP_TAG_RSVDLONG9 0xF9
+#define ISAPNP_TAG_RSVDLONGA 0xFA
+#define ISAPNP_TAG_RSVDLONGB 0xFB
+#define ISAPNP_TAG_RSVDLONGC 0xFC
+#define ISAPNP_TAG_RSVDLONGD 0xFD
+#define ISAPNP_TAG_RSVDLONGE 0xFE
+#define ISAPNP_TAG_RSVDLONGF 0xFF
+#define ISAPNP_TAG_PSEUDO_NEWBOARD 0x100
+
+/** An ISAPnP serial identifier */
+struct isapnp_identifier {
+ /** Vendor ID */
+ uint16_t vendor_id;
+ /** Product ID */
+ uint16_t prod_id;
+ /** Serial number */
+ uint32_t serial;
+ /** Checksum */
+ uint8_t checksum;
+} __attribute__ (( packed ));
+
+/** An ISAPnP logical device ID structure */
+struct isapnp_logdevid {
+ /** Vendor ID */
+ uint16_t vendor_id;
+ /** Product ID */
+ uint16_t prod_id;
+ /** Flags */
+ uint16_t flags;
+} __attribute__ (( packed ));
+
+/** An ISAPnP device ID list entry */
+struct isapnp_device_id {
+ /** Name */
+ const char *name;
+ /** Vendor ID */
+ uint16_t vendor_id;
+ /** Product ID */
+ uint16_t prod_id;
+};
+
+/** An ISAPnP device */
+struct isapnp_device {
+ /** Generic device */
+ struct device dev;
+ /** Vendor ID */
+ uint16_t vendor_id;
+ /** Product ID */
+ uint16_t prod_id;
+ /** I/O address */
+ uint16_t ioaddr;
+ /** Interrupt number */
+ uint8_t irqno;
+ /** Card Select Number */
+ uint8_t csn;
+ /** Logical Device ID */
+ uint8_t logdev;
+ /** Driver for this device */
+ struct isapnp_driver *driver;
+ /** Driver-private data
+ *
+ * Use isapnp_set_drvdata() and isapnp_get_drvdata() to access
+ * this field.
+ */
+ void *priv;
+ /** Driver name */
+ const char *driver_name;
+};
+
+/** An ISAPnP driver */
+struct isapnp_driver {
+ /** ISAPnP ID table */
+ struct isapnp_device_id *ids;
+ /** Number of entries in ISAPnP ID table */
+ unsigned int id_count;
+ /**
+ * Probe device
+ *
+ * @v isapnp ISAPnP device
+ * @v id Matching entry in ID table
+ * @ret rc Return status code
+ */
+ int ( * probe ) ( struct isapnp_device *isapnp,
+ const struct isapnp_device_id *id );
+ /**
+ * Remove device
+ *
+ * @v isapnp ISAPnP device
+ */
+ void ( * remove ) ( struct isapnp_device *isapnp );
+};
+
+/** Declare an ISAPnP driver */
+#define __isapnp_driver __table ( struct isapnp_driver, isapnp_drivers, 01 )
+
+extern uint16_t isapnp_read_port;
+
+extern void isapnp_device_activation ( struct isapnp_device *isapnp,
+ int activation );
+
+/**
+ * Activate ISAPnP device
+ *
+ * @v isapnp ISAPnP device
+ */
+static inline void activate_isapnp_device ( struct isapnp_device *isapnp ) {
+ isapnp_device_activation ( isapnp, 1 );
+}
+
+/**
+ * Deactivate ISAPnP device
+ *
+ * @v isapnp ISAPnP device
+ */
+static inline void deactivate_isapnp_device ( struct isapnp_device *isapnp ) {
+ isapnp_device_activation ( isapnp, 0 );
+}
+
+/**
+ * Set ISAPnP driver-private data
+ *
+ * @v isapnp ISAPnP device
+ * @v priv Private data
+ */
+static inline void isapnp_set_drvdata ( struct isapnp_device *isapnp,
+ void *priv ) {
+ isapnp->priv = priv;
+}
+
+/**
+ * Get ISAPnP driver-private data
+ *
+ * @v isapnp ISAPnP device
+ * @ret priv Private data
+ */
+static inline void * isapnp_get_drvdata ( struct isapnp_device *isapnp ) {
+ return isapnp->priv;
+}
+
+#endif /* ISAPNP_H */
diff --git a/gpxe/src/include/gpxe/iscsi.h b/gpxe/src/include/gpxe/iscsi.h
new file mode 100644
index 00000000..e4df6849
--- /dev/null
+++ b/gpxe/src/include/gpxe/iscsi.h
@@ -0,0 +1,654 @@
+#ifndef _GPXE_ISCSI_H
+#define _GPXE_ISCSI_H
+
+/** @file
+ *
+ * iSCSI protocol
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/socket.h>
+#include <gpxe/scsi.h>
+#include <gpxe/chap.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/xfer.h>
+#include <gpxe/process.h>
+
+/** Default iSCSI port */
+#define ISCSI_PORT 3260
+
+/**
+ * iSCSI segment lengths
+ *
+ * iSCSI uses an icky structure with one one-byte field (a dword
+ * count) and one three-byte field (a byte count). This structure,
+ * and the accompanying macros, relieve some of the pain.
+ */
+union iscsi_segment_lengths {
+ struct {
+ /** The AHS length (measured in dwords) */
+ uint8_t ahs_len;
+ /** The data length (measured in bytes), in network
+ * byte order
+ */
+ uint8_t data_len[3];
+ } bytes;
+ /** Ths data length (measured in bytes), in network byte
+ * order, with ahs_len as the first byte.
+ */
+ uint32_t ahs_and_data_len;
+};
+
+/** The length of the additional header segment, in dwords */
+#define ISCSI_AHS_LEN( segment_lengths ) \
+ ( (segment_lengths).bytes.ahs_len )
+
+/** The length of the data segment, in bytes, excluding any padding */
+#define ISCSI_DATA_LEN( segment_lengths ) \
+ ( ntohl ( (segment_lengths).ahs_and_data_len ) & 0xffffff )
+
+/** The padding of the data segment, in bytes */
+#define ISCSI_DATA_PAD_LEN( segment_lengths ) \
+ ( ( 0 - (segment_lengths).bytes.data_len[2] ) & 0x03 )
+
+/** Set additional header and data segment lengths */
+#define ISCSI_SET_LENGTHS( segment_lengths, ahs_len, data_len ) do { \
+ (segment_lengths).ahs_and_data_len = \
+ htonl ( data_len | ( ahs_len << 24 ) ); \
+ } while ( 0 )
+
+/**
+ * iSCSI basic header segment common fields
+ *
+ */
+struct iscsi_bhs_common {
+ /** Opcode */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Fields specific to the PDU type */
+ uint8_t other_a[2];
+ /** Segment lengths */
+ union iscsi_segment_lengths lengths;
+ /** Fields specific to the PDU type */
+ uint8_t other_b[8];
+ /** Initiator Task Tag */
+ uint32_t itt;
+ /** Fields specific to the PDU type */
+ uint8_t other_c[28];
+};
+
+/** Opcode mask */
+#define ISCSI_OPCODE_MASK 0x3f
+
+/** Immediate delivery */
+#define ISCSI_FLAG_IMMEDIATE 0x40
+
+/** Final PDU of a sequence */
+#define ISCSI_FLAG_FINAL 0x80
+
+/**
+ * iSCSI basic header segment common request fields
+ *
+ */
+struct iscsi_bhs_common_response {
+ /** Opcode */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Fields specific to the PDU type */
+ uint8_t other_a[2];
+ /** Segment lengths */
+ union iscsi_segment_lengths lengths;
+ /** Fields specific to the PDU type */
+ uint8_t other_b[8];
+ /** Initiator Task Tag */
+ uint32_t itt;
+ /** Fields specific to the PDU type */
+ uint8_t other_c[4];
+ /** Status sequence number */
+ uint32_t statsn;
+ /** Expected command sequence number */
+ uint32_t expcmdsn;
+ /** Fields specific to the PDU type */
+ uint8_t other_d[16];
+};
+
+/**
+ * iSCSI login request basic header segment
+ *
+ */
+struct iscsi_bhs_login_request {
+ /** Opcode */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Maximum supported version number */
+ uint8_t version_max;
+ /** Minimum supported version number */
+ uint8_t version_min;
+ /** Segment lengths */
+ union iscsi_segment_lengths lengths;
+ /** Initiator session ID (IANA format) enterprise number and flags */
+ uint32_t isid_iana_en;
+ /** Initiator session ID (IANA format) qualifier */
+ uint16_t isid_iana_qual;
+ /** Target session identifying handle */
+ uint16_t tsih;
+ /** Initiator Task Tag */
+ uint32_t itt;
+ /** Connection ID */
+ uint16_t cid;
+ /** Reserved */
+ uint16_t reserved_a;
+ /** Command sequence number */
+ uint32_t cmdsn;
+ /** Expected status sequence number */
+ uint32_t expstatsn;
+ /** Reserved */
+ uint8_t reserved_b[16];
+};
+
+/** Login request opcode */
+#define ISCSI_OPCODE_LOGIN_REQUEST 0x03
+
+/** Willingness to transition to next stage */
+#define ISCSI_LOGIN_FLAG_TRANSITION 0x80
+
+/** Key=value pairs continued in subsequent request */
+#define ISCSI_LOGIN_FLAG_CONTINUE 0x40
+
+/* Current stage values and mask */
+#define ISCSI_LOGIN_CSG_MASK 0x0c
+#define ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION 0x00
+#define ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION 0x04
+#define ISCSI_LOGIN_CSG_FULL_FEATURE_PHASE 0x0c
+
+/* Next stage values and mask */
+#define ISCSI_LOGIN_NSG_MASK 0x03
+#define ISCSI_LOGIN_NSG_SECURITY_NEGOTIATION 0x00
+#define ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION 0x01
+#define ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE 0x03
+
+/** ISID IANA format marker */
+#define ISCSI_ISID_IANA 0x40000000
+
+/** Fen Systems Ltd. IANA enterprise number
+ *
+ * Permission is hereby granted to use Fen Systems Ltd.'s IANA
+ * enterprise number with this iSCSI implementation.
+ */
+#define IANA_EN_FEN_SYSTEMS 10019
+
+/**
+ * iSCSI login response basic header segment
+ *
+ */
+struct iscsi_bhs_login_response {
+ /** Opcode */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Maximum supported version number */
+ uint8_t version_max;
+ /** Minimum supported version number */
+ uint8_t version_min;
+ /** Segment lengths */
+ union iscsi_segment_lengths lengths;
+ /** Initiator session ID (IANA format) enterprise number and flags */
+ uint32_t isid_iana_en;
+ /** Initiator session ID (IANA format) qualifier */
+ uint16_t isid_iana_qual;
+ /** Target session identifying handle */
+ uint16_t tsih;
+ /** Initiator Task Tag */
+ uint32_t itt;
+ /** Reserved */
+ uint32_t reserved_a;
+ /** Status sequence number */
+ uint32_t statsn;
+ /** Expected command sequence number */
+ uint32_t expcmdsn;
+ /** Maximum command sequence number */
+ uint32_t maxcmdsn;
+ /** Status class */
+ uint8_t status_class;
+ /** Status detail */
+ uint8_t status_detail;
+ /** Reserved */
+ uint8_t reserved_b[10];
+};
+
+/** Login response opcode */
+#define ISCSI_OPCODE_LOGIN_RESPONSE 0x23
+
+/* Login response status codes */
+#define ISCSI_STATUS_SUCCESS 0x00
+#define ISCSI_STATUS_REDIRECT 0x01
+#define ISCSI_STATUS_INITIATOR_ERROR 0x02
+#define ISCSI_STATUS_TARGET_ERROR 0x03
+
+/**
+ * iSCSI SCSI command basic header segment
+ *
+ */
+struct iscsi_bhs_scsi_command {
+ /** Opcode */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Reserved */
+ uint16_t reserved_a;
+ /** Segment lengths */
+ union iscsi_segment_lengths lengths;
+ /** SCSI Logical Unit Number */
+ uint64_t lun;
+ /** Initiator Task Tag */
+ uint32_t itt;
+ /** Expected data transfer length */
+ uint32_t exp_len;
+ /** Command sequence number */
+ uint32_t cmdsn;
+ /** Expected status sequence number */
+ uint32_t expstatsn;
+ /** SCSI Command Descriptor Block (CDB) */
+ union scsi_cdb cdb;
+};
+
+/** SCSI command opcode */
+#define ISCSI_OPCODE_SCSI_COMMAND 0x01
+
+/** Command will read data */
+#define ISCSI_COMMAND_FLAG_READ 0x40
+
+/** Command will write data */
+#define ISCSI_COMMAND_FLAG_WRITE 0x20
+
+/* Task attributes */
+#define ISCSI_COMMAND_ATTR_UNTAGGED 0x00
+#define ISCSI_COMMAND_ATTR_SIMPLE 0x01
+#define ISCSI_COMMAND_ATTR_ORDERED 0x02
+#define ISCSI_COMMAND_ATTR_HEAD_OF_QUEUE 0x03
+#define ISCSI_COMMAND_ATTR_ACA 0x04
+
+/**
+ * iSCSI SCSI response basic header segment
+ *
+ */
+struct iscsi_bhs_scsi_response {
+ /** Opcode */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Response code */
+ uint8_t response;
+ /** SCSI status code */
+ uint8_t status;
+ /** Segment lengths */
+ union iscsi_segment_lengths lengths;
+ /** Reserved */
+ uint8_t reserved_a[8];
+ /** Initiator Task Tag */
+ uint32_t itt;
+ /** SNACK tag */
+ uint32_t snack;
+ /** Status sequence number */
+ uint32_t statsn;
+ /** Expected command sequence number */
+ uint32_t expcmdsn;
+ /** Maximum command sequence number */
+ uint32_t maxcmdsn;
+ /** Expected data sequence number */
+ uint32_t expdatasn;
+ /** Reserved */
+ uint8_t reserved_b[8];
+};
+
+/** SCSI response opcode */
+#define ISCSI_OPCODE_SCSI_RESPONSE 0x21
+
+/** SCSI command completed at target */
+#define ISCSI_RESPONSE_COMMAND_COMPLETE 0x00
+
+/** SCSI target failure */
+#define ISCSI_RESPONSE_TARGET_FAILURE 0x01
+
+/** SCSI sense response code offset
+ *
+ * The SCSI response may contain unsolicited sense data in the data
+ * segment. If it does, this is the offset to the sense response code
+ * byte, which is the only byte we care about.
+ */
+#define ISCSI_SENSE_RESPONSE_CODE_OFFSET 2
+
+/**
+ * iSCSI data-in basic header segment
+ *
+ */
+struct iscsi_bhs_data_in {
+ /** Opcode */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Reserved */
+ uint8_t reserved_a;
+ /** SCSI status code */
+ uint8_t status;
+ /** Segment lengths */
+ union iscsi_segment_lengths lengths;
+ /** Logical Unit Number */
+ uint64_t lun;
+ /** Initiator Task Tag */
+ uint32_t itt;
+ /** Target Transfer Tag */
+ uint32_t ttt;
+ /** Status sequence number */
+ uint32_t statsn;
+ /** Expected command sequence number */
+ uint32_t expcmdsn;
+ /** Maximum command sequence number */
+ uint32_t maxcmdsn;
+ /** Data sequence number */
+ uint32_t datasn;
+ /** Buffer offset */
+ uint32_t offset;
+ /** Residual count */
+ uint32_t residual_count;
+};
+
+/** Data-in opcode */
+#define ISCSI_OPCODE_DATA_IN 0x25
+
+/** Data requires acknowledgement */
+#define ISCSI_DATA_FLAG_ACKNOWLEDGE 0x40
+
+/** Data overflow occurred */
+#define ISCSI_DATA_FLAG_OVERFLOW 0x04
+
+/** Data underflow occurred */
+#define ISCSI_DATA_FLAG_UNDERFLOW 0x02
+
+/** SCSI status code and overflow/underflow flags are valid */
+#define ISCSI_DATA_FLAG_STATUS 0x01
+
+/**
+ * iSCSI data-out basic header segment
+ *
+ */
+struct iscsi_bhs_data_out {
+ /** Opcode */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Reserved */
+ uint16_t reserved_a;
+ /** Segment lengths */
+ union iscsi_segment_lengths lengths;
+ /** Logical Unit Number */
+ uint64_t lun;
+ /** Initiator Task Tag */
+ uint32_t itt;
+ /** Target Transfer Tag */
+ uint32_t ttt;
+ /** Reserved */
+ uint32_t reserved_b;
+ /** Expected status sequence number */
+ uint32_t expstatsn;
+ /** Reserved */
+ uint32_t reserved_c;
+ /** Data sequence number */
+ uint32_t datasn;
+ /** Buffer offset */
+ uint32_t offset;
+ /** Reserved */
+ uint32_t reserved_d;
+};
+
+/** Data-out opcode */
+#define ISCSI_OPCODE_DATA_OUT 0x05
+
+/**
+ * iSCSI request to transfer basic header segment
+ *
+ */
+struct iscsi_bhs_r2t {
+ /** Opcode */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Reserved */
+ uint16_t reserved_a;
+ /** Segment lengths */
+ union iscsi_segment_lengths lengths;
+ /** Logical Unit Number */
+ uint64_t lun;
+ /** Initiator Task Tag */
+ uint32_t itt;
+ /** Target Transfer Tag */
+ uint32_t ttt;
+ /** Status sequence number */
+ uint32_t statsn;
+ /** Expected command sequence number */
+ uint32_t expcmdsn;
+ /** Maximum command sequence number */
+ uint32_t maxcmdsn;
+ /** R2T sequence number */
+ uint32_t r2tsn;
+ /** Buffer offset */
+ uint32_t offset;
+ /** Desired data transfer length */
+ uint32_t len;
+};
+
+/** R2T opcode */
+#define ISCSI_OPCODE_R2T 0x31
+
+/**
+ * An iSCSI basic header segment
+ */
+union iscsi_bhs {
+ struct iscsi_bhs_common common;
+ struct iscsi_bhs_common_response common_response;
+ struct iscsi_bhs_login_request login_request;
+ struct iscsi_bhs_login_response login_response;
+ struct iscsi_bhs_scsi_command scsi_command;
+ struct iscsi_bhs_scsi_response scsi_response;
+ struct iscsi_bhs_data_in data_in;
+ struct iscsi_bhs_data_out data_out;
+ struct iscsi_bhs_r2t r2t;
+ unsigned char bytes[ sizeof ( struct iscsi_bhs_common ) ];
+};
+
+/** State of an iSCSI TX engine */
+enum iscsi_tx_state {
+ /** Nothing to send */
+ ISCSI_TX_IDLE = 0,
+ /** Sending the basic header segment */
+ ISCSI_TX_BHS,
+ /** Sending the additional header segment */
+ ISCSI_TX_AHS,
+ /** Sending the data segment */
+ ISCSI_TX_DATA,
+ /** Sending the data segment padding */
+ ISCSI_TX_DATA_PADDING,
+};
+
+/** State of an iSCSI RX engine */
+enum iscsi_rx_state {
+ /** Receiving the basic header segment */
+ ISCSI_RX_BHS = 0,
+ /** Receiving the additional header segment */
+ ISCSI_RX_AHS,
+ /** Receiving the data segment */
+ ISCSI_RX_DATA,
+ /** Receiving the data segment padding */
+ ISCSI_RX_DATA_PADDING,
+};
+
+/** An iSCSI session */
+struct iscsi_session {
+ /** Reference counter */
+ struct refcnt refcnt;
+
+ /** Transport-layer socket */
+ struct xfer_interface socket;
+
+ /** Target address */
+ char *target_address;
+ /** Target port */
+ unsigned int target_port;
+ /** Target IQN */
+ char *target_iqn;
+ /** Logical Unit Number (LUN) */
+ uint64_t lun;
+ /** Target socket address (recorded only for iBFT) */
+ struct sockaddr target_sockaddr;
+
+ /** Session status
+ *
+ * This is the bitwise-OR of zero or more ISCSI_STATUS_XXX
+ * constants.
+ */
+ int status;
+ /** Retry count
+ *
+ * Number of times that the connection has been retried.
+ * Reset upon a successful connection.
+ */
+ int retry_count;
+
+ /** Username (if any) */
+ char *username;
+ /** Password (if any) */
+ char *password;
+ /** CHAP challenge/response */
+ struct chap_challenge chap;
+
+ /** Target session identifying handle
+ *
+ * This is assigned by the target when we first log in, and
+ * must be reused on subsequent login attempts.
+ */
+ uint16_t tsih;
+ /** Initiator task tag
+ *
+ * This is the tag of the current command. It is incremented
+ * whenever a new command is started.
+ */
+ uint32_t itt;
+ /** Target transfer tag
+ *
+ * This is the tag attached to a sequence of data-out PDUs in
+ * response to an R2T.
+ */
+ uint32_t ttt;
+ /**
+ * Transfer offset
+ *
+ * This is the offset for an in-progress sequence of data-out
+ * PDUs in response to an R2T.
+ */
+ uint32_t transfer_offset;
+ /**
+ * Transfer length
+ *
+ * This is the length for an in-progress sequence of data-out
+ * PDUs in response to an R2T.
+ */
+ uint32_t transfer_len;
+ /** Command sequence number
+ *
+ * This is the sequence number of the current command, used to
+ * fill out the CmdSN field in iSCSI request PDUs. It is
+ * updated with the value of the ExpCmdSN field whenever we
+ * receive an iSCSI response PDU containing such a field.
+ */
+ uint32_t cmdsn;
+ /** Status sequence number
+ *
+ * This is the most recent status sequence number present in
+ * the StatSN field of an iSCSI response PDU containing such a
+ * field. Whenever we send an iSCSI request PDU, we fill out
+ * the ExpStatSN field with this value plus one.
+ */
+ uint32_t statsn;
+
+ /** Basic header segment for current TX PDU */
+ union iscsi_bhs tx_bhs;
+ /** State of the TX engine */
+ enum iscsi_tx_state tx_state;
+ /** TX process */
+ struct process process;
+
+ /** Basic header segment for current RX PDU */
+ union iscsi_bhs rx_bhs;
+ /** State of the RX engine */
+ enum iscsi_rx_state rx_state;
+ /** Byte offset within the current RX state */
+ size_t rx_offset;
+ /** Length of the current RX state */
+ size_t rx_len;
+ /** Buffer for received data (not always used) */
+ void *rx_buffer;
+
+ /** Current SCSI command
+ *
+ * Set to NULL when command is complete.
+ */
+ struct scsi_command *command;
+ /** SCSI command return code
+ *
+ * Set to -EINPROGRESS while command is processing.
+ */
+ int rc;
+ /** Instant return code
+ *
+ * Set to a non-zero value if all requests should return
+ * immediately. This can be used to e.g. avoid retrying
+ * logins that are doomed to fail authentication.
+ */
+ int instant_rc;
+};
+
+/** iSCSI session is currently in the security negotiation phase */
+#define ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE \
+ ( ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION | \
+ ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION )
+
+/** iSCSI session is currently in the operational parameter
+ * negotiation phase
+ */
+#define ISCSI_STATUS_OPERATIONAL_NEGOTIATION_PHASE \
+ ( ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION | \
+ ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE )
+
+/** iSCSI session is currently in the full feature phase */
+#define ISCSI_STATUS_FULL_FEATURE_PHASE ISCSI_LOGIN_CSG_FULL_FEATURE_PHASE
+
+/** Mask for all iSCSI session phases */
+#define ISCSI_STATUS_PHASE_MASK ( ISCSI_LOGIN_CSG_MASK | ISCSI_LOGIN_NSG_MASK )
+
+/** iSCSI session needs to send the initial security negotiation strings */
+#define ISCSI_STATUS_STRINGS_SECURITY 0x0100
+
+/** iSCSI session needs to send the CHAP_A string */
+#define ISCSI_STATUS_STRINGS_CHAP_ALGORITHM 0x0200
+
+/** iSCSI session needs to send the CHAP response */
+#define ISCSI_STATUS_STRINGS_CHAP_RESPONSE 0x0400
+
+/** iSCSI session needs to send the operational negotiation strings */
+#define ISCSI_STATUS_STRINGS_OPERATIONAL 0x0800
+
+/** Mask for all iSCSI "needs to send" flags */
+#define ISCSI_STATUS_STRINGS_MASK 0xff00
+
+/** Maximum number of retries at connecting */
+#define ISCSI_MAX_RETRIES 2
+
+extern int iscsi_attach ( struct scsi_device *scsi, const char *root_path );
+extern void iscsi_detach ( struct scsi_device *scsi );
+extern const char * iscsi_initiator_iqn ( void );
+
+#endif /* _GPXE_ISCSI_H */
diff --git a/gpxe/src/include/gpxe/job.h b/gpxe/src/include/gpxe/job.h
new file mode 100644
index 00000000..8e11aa33
--- /dev/null
+++ b/gpxe/src/include/gpxe/job.h
@@ -0,0 +1,165 @@
+#ifndef _GPXE_JOB_H
+#define _GPXE_JOB_H
+
+/** @file
+ *
+ * Job control interfaces
+ *
+ */
+
+#include <stddef.h>
+#include <gpxe/interface.h>
+
+/** Job progress */
+struct job_progress {
+ /** Amount of operation completed so far
+ *
+ * The units for this quantity are arbitrary. @c completed
+ * divded by @total should give something which approximately
+ * represents the progress through the operation. For a
+ * download operation, using byte counts would make sense.
+ */
+ unsigned long completed;
+ /** Total operation size
+ *
+ * See @c completed. A zero value means "total size unknown"
+ * and is explcitly permitted; users should take this into
+ * account before calculating @c completed/total.
+ */
+ unsigned long total;
+};
+
+struct job_interface;
+
+/** Job control interface operations */
+struct job_interface_operations {
+ /** Job completed
+ *
+ * @v job Job control interface
+ * @v rc Overall job status code
+ */
+ void ( * done ) ( struct job_interface *job, int rc );
+ /** Abort job
+ *
+ * @v job Job control interface
+ */
+ void ( * kill ) ( struct job_interface *job );
+ /** Get job progress
+ *
+ * @v job Job control interface
+ * @v progress Progress data to fill in
+ */
+ void ( * progress ) ( struct job_interface *job,
+ struct job_progress *progress );
+};
+
+/** A job control interface */
+struct job_interface {
+ /** Generic object communication interface */
+ struct interface intf;
+ /** Operations for received messages */
+ struct job_interface_operations *op;
+};
+
+extern struct job_interface null_job;
+extern struct job_interface_operations null_job_ops;
+
+extern void job_done ( struct job_interface *job, int rc );
+extern void job_kill ( struct job_interface *job );
+
+extern void ignore_job_done ( struct job_interface *job, int rc );
+extern void ignore_job_kill ( struct job_interface *job );
+extern void ignore_job_progress ( struct job_interface *job,
+ struct job_progress *progress );
+
+/**
+ * Initialise a job control interface
+ *
+ * @v job Job control interface
+ * @v op Job control interface operations
+ * @v refcnt Containing object reference counter, or NULL
+ */
+static inline void job_init ( struct job_interface *job,
+ struct job_interface_operations *op,
+ struct refcnt *refcnt ) {
+ job->intf.dest = &null_job.intf;
+ job->intf.refcnt = refcnt;
+ job->op = op;
+}
+
+/**
+ * Get job control interface from generic object communication interface
+ *
+ * @v intf Generic object communication interface
+ * @ret job Job control interface
+ */
+static inline __attribute__ (( always_inline )) struct job_interface *
+intf_to_job ( struct interface *intf ) {
+ return container_of ( intf, struct job_interface, intf );
+}
+
+/**
+ * Get reference to destination job control interface
+ *
+ * @v job Job control interface
+ * @ret dest Destination interface
+ */
+static inline __attribute__ (( always_inline )) struct job_interface *
+job_get_dest ( struct job_interface *job ) {
+ return intf_to_job ( intf_get ( job->intf.dest ) );
+}
+
+/**
+ * Drop reference to job control interface
+ *
+ * @v job Job control interface
+ */
+static inline __attribute__ (( always_inline )) void
+job_put ( struct job_interface *job ) {
+ intf_put ( &job->intf );
+}
+
+/**
+ * Plug a job control interface into a new destination interface
+ *
+ * @v job Job control interface
+ * @v dest New destination interface
+ */
+static inline void job_plug ( struct job_interface *job,
+ struct job_interface *dest ) {
+ plug ( &job->intf, &dest->intf );
+}
+
+/**
+ * Plug two job control interfaces together
+ *
+ * @v a Job control interface A
+ * @v b Job control interface B
+ */
+static inline void job_plug_plug ( struct job_interface *a,
+ struct job_interface *b ) {
+ plug_plug ( &a->intf, &b->intf );
+}
+
+/**
+ * Unplug a job control interface
+ *
+ * @v job Job control interface
+ */
+static inline void job_unplug ( struct job_interface *job ) {
+ plug ( &job->intf, &null_job.intf );
+}
+
+/**
+ * Stop using a job control interface
+ *
+ * @v job Job control interface
+ *
+ * After calling this method, no further messages will be received via
+ * the interface.
+ */
+static inline void job_nullify ( struct job_interface *job ) {
+ job->op = &null_job_ops;
+};
+
+#endif /* _GPXE_JOB_H */
diff --git a/gpxe/src/include/gpxe/keys.h b/gpxe/src/include/gpxe/keys.h
new file mode 100644
index 00000000..7a0b5101
--- /dev/null
+++ b/gpxe/src/include/gpxe/keys.h
@@ -0,0 +1,82 @@
+#ifndef _GPXE_KEYS_H
+#define _GPXE_KEYS_H
+
+/** @file
+ *
+ * Key definitions
+ *
+ */
+
+/*
+ * Symbolic names for some standard ASCII characters
+ *
+ */
+
+#define NUL 0x00
+#define CTRL_A 0x01
+#define CTRL_B 0x02
+#define CTRL_C 0x03
+#define CTRL_D 0x04
+#define CTRL_E 0x05
+#define CTRL_F 0x06
+#define CTRL_G 0x07
+#define CTRL_H 0x08
+#define CTRL_I 0x09
+#define CTRL_J 0x0a
+#define CTRL_K 0x0b
+#define CTRL_L 0x0c
+#define CTRL_M 0x0d
+#define CTRL_N 0x0e
+#define CTRL_O 0x0f
+#define CTRL_P 0x10
+#define CTRL_Q 0x11
+#define CTRL_R 0x12
+#define CTRL_S 0x13
+#define CTRL_T 0x14
+#define CTRL_U 0x15
+#define CTRL_V 0x16
+#define CTRL_W 0x17
+#define CTRL_X 0x18
+#define CTRL_Y 0x19
+#define CTRL_Z 0x1a
+
+#define BACKSPACE CTRL_H
+#define TAB CTRL_I
+#define LF CTRL_J
+#define CR CTRL_M
+#define ESC 0x1b
+
+/*
+ * Special keys outside the normal ASCII range
+ *
+ *
+ * The names are chosen to match those used by curses. The values are
+ * chosen to facilitate easy conversion from a received ANSI escape
+ * sequence to a KEY_XXX constant. The KEY_XXX constant is simply
+ * 0x100 plus the first byte following CSI in the ANSI escape
+ * sequence. For example, KEY_LEFT is 0x144, since a left cursor key
+ * is transmitted as the ANSI sequence "^[[D".
+ */
+
+#define KEY_ANSI( character ) ( 0x100 + (character) )
+
+#define KEY_MIN 0x101
+#define KEY_UP KEY_ANSI ( 'A' ) /**< Up arrow */
+#define KEY_DOWN KEY_ANSI ( 'B' ) /**< Down arrow */
+#define KEY_RIGHT KEY_ANSI ( 'C' ) /**< Right arrow */
+#define KEY_LEFT KEY_ANSI ( 'D' ) /**< Left arrow */
+#define KEY_END KEY_ANSI ( 'F' ) /**< End */
+#define KEY_HOME KEY_ANSI ( 'H' ) /**< Home */
+#define KEY_IC KEY_ANSI ( '2' ) /**< Insert */
+#define KEY_DC KEY_ANSI ( '3' ) /**< Delete */
+#define KEY_PPAGE KEY_ANSI ( '5' ) /**< Page up */
+#define KEY_NPAGE KEY_ANSI ( '6' ) /**< Page down */
+#define KEY_MAX 0x1ff
+
+/* Not in the [KEY_MIN,KEY_MAX] range; terminals seem to send these as
+ * normal ASCII values.
+ */
+#define KEY_BACKSPACE BACKSPACE
+#define KEY_ENTER LF
+
+#endif /* _GPXE_KEYS_H */
diff --git a/gpxe/src/include/gpxe/linebuf.h b/gpxe/src/include/gpxe/linebuf.h
new file mode 100644
index 00000000..676731a9
--- /dev/null
+++ b/gpxe/src/include/gpxe/linebuf.h
@@ -0,0 +1,28 @@
+#ifndef _GPXE_LINEBUF_H
+#define _GPXE_LINEBUF_H
+
+/** @file
+ *
+ * Line buffering
+ *
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+/** A line buffer */
+struct line_buffer {
+ /** Current string in the buffer */
+ char *data;
+ /** Length of current string, excluding the terminating NUL */
+ size_t len;
+ /** String is ready to read */
+ int ready;
+};
+
+extern char * buffered_line ( struct line_buffer *linebuf );
+extern ssize_t line_buffer ( struct line_buffer *linebuf,
+ const char *data, size_t len );
+extern void empty_line_buffer ( struct line_buffer *linebuf );
+
+#endif /* _GPXE_LINEBUF_H */
diff --git a/gpxe/src/include/gpxe/linux_compat.h b/gpxe/src/include/gpxe/linux_compat.h
new file mode 100644
index 00000000..2c3cbbde
--- /dev/null
+++ b/gpxe/src/include/gpxe/linux_compat.h
@@ -0,0 +1,25 @@
+#ifndef _GPXE_LINUX_COMPAT_H
+#define _GPXE_LINUX_COMPAT_H
+
+/** @file
+ *
+ * Linux code compatibility
+ *
+ * This file exists to ease the building of Linux source code within
+ * gPXE. This is intended to facilitate quick testing; it is not
+ * intended to be a substitute for proper porting.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <byteswap.h>
+#include <gpxe/bitops.h>
+
+#define __init
+#define __exit
+#define __initdata
+#define __exitdata
+#define printk printf
+
+#endif /* _GPXE_LINUX_COMPAT_H */
diff --git a/gpxe/src/include/gpxe/list.h b/gpxe/src/include/gpxe/list.h
new file mode 100644
index 00000000..602382be
--- /dev/null
+++ b/gpxe/src/include/gpxe/list.h
@@ -0,0 +1,178 @@
+#ifndef _GPXE_LIST_H
+#define _GPXE_LIST_H
+
+/** @file
+ *
+ * Linked lists
+ *
+ * This linked list handling code is based on the Linux kernel's
+ * list.h.
+ */
+
+#include <stddef.h>
+#include <assert.h>
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next;
+ struct list_head *prev;
+};
+
+#define LIST_HEAD_INIT( name ) { &(name), &(name) }
+
+#define LIST_HEAD( name ) \
+ struct list_head name = LIST_HEAD_INIT ( name )
+
+#define INIT_LIST_HEAD( ptr ) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while ( 0 )
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add ( struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next ) {
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * Add a new entry to the head of a list
+ *
+ * @v new New entry to be added
+ * @v head List head to add it after
+ *
+ * Insert a new entry after the specified head. This is good for
+ * implementing stacks.
+ */
+static inline void list_add ( struct list_head *new, struct list_head *head ) {
+ __list_add ( new, head, head->next );
+}
+#define list_add( new, head ) do { \
+ assert ( (head)->next->prev == (head) ); \
+ assert ( (head)->prev->next == (head) ); \
+ list_add ( (new), (head) ); \
+ } while ( 0 )
+
+/**
+ * Add a new entry to the tail of a list
+ *
+ * @v new New entry to be added
+ * @v head List head to add it before
+ *
+ * Insert a new entry before the specified head. This is useful for
+ * implementing queues.
+ */
+static inline void list_add_tail ( struct list_head *new,
+ struct list_head *head ) {
+ __list_add ( new, head->prev, head );
+}
+#define list_add_tail( new, head ) do { \
+ assert ( (head)->next->prev == (head) ); \
+ assert ( (head)->prev->next == (head) ); \
+ list_add_tail ( (new), (head) ); \
+ } while ( 0 )
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del ( struct list_head * prev,
+ struct list_head * next ) {
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * Delete an entry from a list
+ *
+ * @v entry Element to delete from the list
+ *
+ * Note that list_empty() on entry does not return true after this;
+ * the entry is in an undefined state.
+ */
+static inline void list_del ( struct list_head *entry ) {
+ __list_del ( entry->prev, entry->next );
+}
+#define list_del( entry ) do { \
+ assert ( (entry)->prev != NULL ); \
+ assert ( (entry)->next != NULL ); \
+ assert ( (entry)->next->prev == (entry) ); \
+ assert ( (entry)->prev->next == (entry) ); \
+ list_del ( (entry) ); \
+ } while ( 0 )
+
+/**
+ * Test whether a list is empty
+ *
+ * @v head List to test.
+ */
+static inline int list_empty ( const struct list_head *head ) {
+ return head->next == head;
+}
+
+/**
+ * Get the containing struct for this entry
+ *
+ * @v ptr The struct list_head pointer
+ * @v type The type of the struct this is embedded in
+ * @v member The name of the list_struct within the struct
+ */
+#define list_entry( ptr, type, member ) \
+ container_of ( ptr, type, member )
+
+/**
+ * Iterate over a list
+ *
+ * @v pos The &struct list_head to use as a loop counter
+ * @v head The head for your list
+ */
+#define list_for_each( pos, head ) \
+ for ( pos = (head)->next; pos != (head); pos = pos->next )
+
+/**
+ * Iterate over entries in a list
+ *
+ * @v pos The type * to use as a loop counter
+ * @v head The head for your list
+ * @v member The name of the list_struct within the struct
+ */
+#define list_for_each_entry( pos, head, member ) \
+ for ( pos = list_entry ( (head)->next, typeof ( *pos ), member ); \
+ &pos->member != (head); \
+ pos = list_entry ( pos->member.next, typeof ( *pos ), member ) )
+
+/**
+ * Iterate over entries in a list, safe against deletion of entries
+ *
+ * @v pos The type * to use as a loop counter
+ * @v tmp Another type * to use for temporary storage
+ * @v head The head for your list
+ * @v member The name of the list_struct within the struct
+ */
+#define list_for_each_entry_safe( pos, tmp, head, member ) \
+ for ( pos = list_entry ( (head)->next, typeof ( *pos ), member ), \
+ tmp = list_entry ( pos->member.next, typeof ( *tmp ), member ); \
+ &pos->member != (head); \
+ pos = tmp, \
+ tmp = list_entry ( tmp->member.next, typeof ( *tmp ), member ) )
+
+#endif /* _GPXE_LIST_H */
diff --git a/gpxe/src/include/gpxe/malloc.h b/gpxe/src/include/gpxe/malloc.h
new file mode 100644
index 00000000..cce5d1d3
--- /dev/null
+++ b/gpxe/src/include/gpxe/malloc.h
@@ -0,0 +1,57 @@
+#ifndef _GPXE_MALLOC_H
+#define _GPXE_MALLOC_H
+
+#include <stdint.h>
+
+/** @file
+ *
+ * Dynamic memory allocation
+ *
+ */
+
+/*
+ * Prototypes for the standard functions (malloc() et al) are in
+ * stdlib.h. Include <gpxe/malloc.h> only if you need the
+ * non-standard functions, such as malloc_dma().
+ *
+ */
+#include <stdlib.h>
+
+extern size_t freemem;
+
+extern void * __malloc alloc_memblock ( size_t size, size_t align );
+extern void free_memblock ( void *ptr, size_t size );
+extern void mpopulate ( void *start, size_t len );
+extern void mdumpfree ( void );
+
+/**
+ * Allocate memory for DMA
+ *
+ * @v size Requested size
+ * @v align Physical alignment
+ * @ret ptr Memory, or NULL
+ *
+ * Allocates physically-aligned memory for DMA.
+ *
+ * @c align must be a power of two. @c size may not be zero.
+ */
+static inline void * __malloc malloc_dma ( size_t size, size_t phys_align ) {
+ return alloc_memblock ( size, phys_align );
+}
+
+/**
+ * Free memory allocated with malloc_dma()
+ *
+ * @v ptr Memory allocated by malloc_dma(), or NULL
+ * @v size Size of memory, as passed to malloc_dma()
+ *
+ * Memory allocated with malloc_dma() can only be freed with
+ * free_dma(); it cannot be freed with the standard free().
+ *
+ * If @c ptr is NULL, no action is taken.
+ */
+static inline void free_dma ( void *ptr, size_t size ) {
+ free_memblock ( ptr, size );
+}
+
+#endif /* _GPXE_MALLOC_H */
diff --git a/gpxe/src/include/gpxe/mca.h b/gpxe/src/include/gpxe/mca.h
new file mode 100644
index 00000000..21f9e74d
--- /dev/null
+++ b/gpxe/src/include/gpxe/mca.h
@@ -0,0 +1,103 @@
+/*
+ * MCA bus driver code
+ *
+ * Abstracted from 3c509.c.
+ *
+ */
+
+#ifndef MCA_H
+#define MCA_H
+
+#include <gpxe/isa_ids.h>
+#include <gpxe/device.h>
+#include <gpxe/tables.h>
+
+/*
+ * MCA constants
+ *
+ */
+#define MCA_MOTHERBOARD_SETUP_REG 0x94
+#define MCA_ADAPTER_SETUP_REG 0x96
+#define MCA_MAX_SLOT_NR 0x07 /* Must be 2^n - 1 */
+#define MCA_POS_REG(n) (0x100+(n))
+
+/* Is there a standard that would define this? */
+#define GENERIC_MCA_VENDOR ISA_VENDOR ( 'M', 'C', 'A' )
+
+/** An MCA device ID list entry */
+struct mca_device_id {
+ /** Name */
+ const char *name;
+ /** Device ID */
+ uint16_t id;
+};
+
+/** An MCA device */
+struct mca_device {
+ /** Generic device */
+ struct device dev;
+ /** Slot number */
+ unsigned int slot;
+ /** POS register values */
+ unsigned char pos[8];
+ /** Driver for this device */
+ struct mca_driver *driver;
+ /** Driver-private data
+ *
+ * Use mca_set_drvdata() and mca_get_drvdata() to access
+ * this field.
+ */
+ void *priv;
+ /** Driver name */
+ const char *driver_name;
+};
+
+#define MCA_ID(mca) ( ( (mca)->pos[1] << 8 ) + (mca)->pos[0] )
+
+/** An MCA driver */
+struct mca_driver {
+ /** MCA ID table */
+ struct mca_device_id *ids;
+ /** Number of entries in MCA ID table */
+ unsigned int id_count;
+ /**
+ * Probe device
+ *
+ * @v mca MCA device
+ * @v id Matching entry in ID table
+ * @ret rc Return status code
+ */
+ int ( * probe ) ( struct mca_device *mca,
+ const struct mca_device_id *id );
+ /**
+ * Remove device
+ *
+ * @v mca MCA device
+ */
+ void ( * remove ) ( struct mca_device *mca );
+};
+
+/** Declare an MCA driver */
+#define __mca_driver __table ( struct mca_driver, mca_drivers, 01 )
+
+/**
+ * Set MCA driver-private data
+ *
+ * @v mca MCA device
+ * @v priv Private data
+ */
+static inline void mca_set_drvdata ( struct mca_device *mca, void *priv ) {
+ mca->priv = priv;
+}
+
+/**
+ * Get MCA driver-private data
+ *
+ * @v mca MCA device
+ * @ret priv Private data
+ */
+static inline void * mca_get_drvdata ( struct mca_device *mca ) {
+ return mca->priv;
+}
+
+#endif
diff --git a/gpxe/src/include/gpxe/md5.h b/gpxe/src/include/gpxe/md5.h
new file mode 100644
index 00000000..304a0e64
--- /dev/null
+++ b/gpxe/src/include/gpxe/md5.h
@@ -0,0 +1,22 @@
+#ifndef _GPXE_MD5_H
+#define _GPXE_MD5_H
+
+struct crypto_algorithm;
+
+#include <stdint.h>
+
+#define MD5_DIGEST_SIZE 16
+#define MD5_BLOCK_WORDS 16
+#define MD5_HASH_WORDS 4
+
+struct md5_ctx {
+ u32 hash[MD5_HASH_WORDS];
+ u32 block[MD5_BLOCK_WORDS];
+ u64 byte_count;
+};
+
+#define MD5_CTX_SIZE sizeof ( struct md5_ctx )
+
+extern struct crypto_algorithm md5_algorithm;
+
+#endif /* _GPXE_MD5_H */
diff --git a/gpxe/src/include/gpxe/memmap.h b/gpxe/src/include/gpxe/memmap.h
new file mode 100644
index 00000000..836a1b92
--- /dev/null
+++ b/gpxe/src/include/gpxe/memmap.h
@@ -0,0 +1,34 @@
+#ifndef _GPXE_MEMMAP_H
+#define _GPXE_MEMMAP_H
+
+#include <stdint.h>
+
+/**
+ * @file
+ *
+ * Memory mapping
+ *
+ */
+
+/** A usable memory region */
+struct memory_region {
+ /** Physical start address */
+ uint64_t start;
+ /** Physical end address */
+ uint64_t end;
+};
+
+/** Maximum number of memory regions we expect to encounter */
+#define MAX_MEMORY_REGIONS 8
+
+/** A memory map */
+struct memory_map {
+ /** Memory regions */
+ struct memory_region regions[MAX_MEMORY_REGIONS];
+ /** Number of used regions */
+ unsigned int count;
+};
+
+extern void get_memmap ( struct memory_map *memmap );
+
+#endif /* _GPXE_MEMMAP_H */
diff --git a/gpxe/src/include/gpxe/monojob.h b/gpxe/src/include/gpxe/monojob.h
new file mode 100644
index 00000000..aaa38d03
--- /dev/null
+++ b/gpxe/src/include/gpxe/monojob.h
@@ -0,0 +1,15 @@
+#ifndef _GPXE_MONOJOB_H
+#define _GPXE_MONOJOB_H
+
+/** @file
+ *
+ * Single foreground job
+ *
+ */
+
+struct job_interface;
+
+extern struct job_interface monojob;
+extern int monojob_wait ( const char *string );
+
+#endif /* _GPXE_MONOJOB_H */
diff --git a/gpxe/src/include/gpxe/ndp.h b/gpxe/src/include/gpxe/ndp.h
new file mode 100644
index 00000000..db32b0c8
--- /dev/null
+++ b/gpxe/src/include/gpxe/ndp.h
@@ -0,0 +1,21 @@
+#include <stdint.h>
+#include <byteswap.h>
+#include <string.h>
+#include <gpxe/icmp6.h>
+#include <gpxe/ip6.h>
+#include <gpxe/in.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/tcpip.h>
+
+#define NDP_STATE_INVALID 0
+#define NDP_STATE_INCOMPLETE 1
+#define NDP_STATE_REACHABLE 2
+#define NDP_STATE_DELAY 3
+#define NDP_STATE_PROBE 4
+#define NDP_STATE_STALE 5
+
+int ndp_resolve ( struct net_device *netdev, struct in6_addr *src,
+ struct in6_addr *dest, void *dest_ll_addr );
+int ndp_process_advert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
+ struct sockaddr_tcpip *st_dest );
diff --git a/gpxe/src/include/gpxe/netdevice.h b/gpxe/src/include/gpxe/netdevice.h
new file mode 100644
index 00000000..d8cb84d0
--- /dev/null
+++ b/gpxe/src/include/gpxe/netdevice.h
@@ -0,0 +1,404 @@
+#ifndef _GPXE_NETDEVICE_H
+#define _GPXE_NETDEVICE_H
+
+/** @file
+ *
+ * Network device management
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/list.h>
+#include <gpxe/tables.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/settings.h>
+
+struct io_buffer;
+struct net_device;
+struct net_protocol;
+struct ll_protocol;
+struct device;
+
+/** Maximum length of a link-layer address */
+#define MAX_LL_ADDR_LEN 20
+
+/** Maximum length of a link-layer header */
+#define MAX_LL_HEADER_LEN 32
+
+/** Maximum length of a network-layer address */
+#define MAX_NET_ADDR_LEN 4
+
+/**
+ * A network-layer protocol
+ *
+ */
+struct net_protocol {
+ /** Protocol name */
+ const char *name;
+ /**
+ * Process received packet
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v ll_source Link-layer source address
+ *
+ * This method takes ownership of the I/O buffer.
+ */
+ int ( * rx ) ( struct io_buffer *iobuf, struct net_device *netdev,
+ const void *ll_source );
+ /**
+ * Transcribe network-layer address
+ *
+ * @v net_addr Network-layer address
+ * @ret string Human-readable transcription of address
+ *
+ * This method should convert the network-layer address into a
+ * human-readable format (e.g. dotted quad notation for IPv4).
+ *
+ * The buffer used to hold the transcription is statically
+ * allocated.
+ */
+ const char * ( *ntoa ) ( const void * net_addr );
+ /** Network-layer protocol
+ *
+ * This is an ETH_P_XXX constant, in network-byte order
+ */
+ uint16_t net_proto;
+ /** Network-layer address length */
+ uint8_t net_addr_len;
+};
+
+/**
+ * A link-layer protocol
+ *
+ */
+struct ll_protocol {
+ /** Protocol name */
+ const char *name;
+ /**
+ * Transmit network-layer packet via network device
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v net_protocol Network-layer protocol
+ * @v ll_dest Link-layer destination address
+ * @ret rc Return status code
+ *
+ * This method should prepend in the link-layer header
+ * (e.g. the Ethernet DIX header) and transmit the packet.
+ * This method takes ownership of the I/O buffer.
+ */
+ int ( * tx ) ( struct io_buffer *iobuf, struct net_device *netdev,
+ struct net_protocol *net_protocol,
+ const void *ll_dest );
+ /**
+ * Handle received packet
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ *
+ * This method should strip off the link-layer header
+ * (e.g. the Ethernet DIX header) and pass the packet to
+ * net_rx(). This method takes ownership of the packet
+ * buffer.
+ */
+ int ( * rx ) ( struct io_buffer *iobuf, struct net_device *netdev );
+ /**
+ * Transcribe link-layer address
+ *
+ * @v ll_addr Link-layer address
+ * @ret string Human-readable transcription of address
+ *
+ * This method should convert the link-layer address into a
+ * human-readable format.
+ *
+ * The buffer used to hold the transcription is statically
+ * allocated.
+ */
+ const char * ( * ntoa ) ( const void * ll_addr );
+ /** Link-layer protocol
+ *
+ * This is an ARPHRD_XXX constant, in network byte order.
+ */
+ uint16_t ll_proto;
+ /** Link-layer address length */
+ uint8_t ll_addr_len;
+ /** Link-layer header length */
+ uint8_t ll_header_len;
+ /** Link-layer broadcast address */
+ const uint8_t *ll_broadcast;
+};
+
+/** Network device operations */
+struct net_device_operations {
+ /** Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ *
+ * This method should allocate RX I/O buffers and enable
+ * the hardware to start transmitting and receiving packets.
+ */
+ int ( * open ) ( struct net_device *netdev );
+ /** Close network device
+ *
+ * @v netdev Network device
+ *
+ * This method should stop the flow of packets, and free up
+ * any packets that are currently in the device's TX queue.
+ */
+ void ( * close ) ( struct net_device *netdev );
+ /** Transmit packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ *
+ * This method should cause the hardware to initiate
+ * transmission of the I/O buffer.
+ *
+ * If this method returns success, the I/O buffer remains
+ * owned by the net device's TX queue, and the net device must
+ * eventually call netdev_tx_complete() to free the buffer.
+ * If this method returns failure, the I/O buffer is
+ * immediately released; the failure is interpreted as
+ * "failure to enqueue buffer".
+ *
+ * This method is guaranteed to be called only when the device
+ * is open.
+ */
+ int ( * transmit ) ( struct net_device *netdev,
+ struct io_buffer *iobuf );
+ /** Poll for completed and received packets
+ *
+ * @v netdev Network device
+ *
+ * This method should cause the hardware to check for
+ * completed transmissions and received packets. Any received
+ * packets should be delivered via netdev_rx().
+ *
+ * This method is guaranteed to be called only when the device
+ * is open.
+ */
+ void ( * poll ) ( struct net_device *netdev );
+ /** Enable or disable interrupts
+ *
+ * @v netdev Network device
+ * @v enable Interrupts should be enabled
+ */
+ void ( * irq ) ( struct net_device *netdev, int enable );
+};
+
+/** Network device statistics */
+struct net_device_stats {
+ /** Count of successfully completed transmissions */
+ unsigned int tx_ok;
+ /** Count of transmission errors */
+ unsigned int tx_err;
+ /** Count of successfully received packets */
+ unsigned int rx_ok;
+ /** Count of reception errors */
+ unsigned int rx_err;
+};
+
+/**
+ * A network device
+ *
+ * This structure represents a piece of networking hardware. It has
+ * properties such as a link-layer address and methods for
+ * transmitting and receiving raw packets.
+ *
+ * Note that this structure must represent a generic network device,
+ * not just an Ethernet device.
+ */
+struct net_device {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** List of network devices */
+ struct list_head list;
+ /** Name of this network device */
+ char name[8];
+ /** Underlying hardware device */
+ struct device *dev;
+
+ /** Network device operations */
+ struct net_device_operations *op;
+
+ /** Link-layer protocol */
+ struct ll_protocol *ll_protocol;
+ /** Link-layer address
+ *
+ * For Ethernet, this is the MAC address.
+ */
+ uint8_t ll_addr[MAX_LL_ADDR_LEN];
+
+ /** Current device state
+ *
+ * This is the bitwise-OR of zero or more NETDEV_XXX constants.
+ */
+ unsigned int state;
+ /** TX packet queue */
+ struct list_head tx_queue;
+ /** RX packet queue */
+ struct list_head rx_queue;
+ /** Device statistics */
+ struct net_device_stats stats;
+
+ /** Configuration settings applicable to this device */
+ struct simple_settings settings;
+
+ /** Driver private data */
+ void *priv;
+};
+
+/** Network device is open */
+#define NETDEV_OPEN 0x0001
+
+/** Declare a link-layer protocol */
+#define __ll_protocol __table ( struct ll_protocol, ll_protocols, 01 )
+
+/** Declare a network-layer protocol */
+#define __net_protocol __table ( struct net_protocol, net_protocols, 01 )
+
+extern struct list_head net_devices;
+extern struct net_device_operations null_netdev_operations;
+
+/**
+ * Initialise a network device
+ *
+ * @v netdev Network device
+ * @v op Network device operations
+ */
+static inline void netdev_init ( struct net_device *netdev,
+ struct net_device_operations *op ) {
+ netdev->op = op;
+}
+
+/**
+ * Stop using a network device
+ *
+ * @v netdev Network device
+ *
+ * Drivers should call this method immediately before the final call
+ * to netdev_put().
+ */
+static inline void netdev_nullify ( struct net_device *netdev ) {
+ netdev->op = &null_netdev_operations;
+}
+
+/**
+ * Get printable network device hardware address
+ *
+ * @v netdev Network device
+ * @ret name Hardware address
+ */
+static inline const char * netdev_hwaddr ( struct net_device *netdev ) {
+ return netdev->ll_protocol->ntoa ( netdev->ll_addr );
+}
+
+/** Iterate over all network devices */
+#define for_each_netdev( netdev ) \
+ list_for_each_entry ( (netdev), &net_devices, list )
+
+/** There exist some network devices
+ *
+ * @ret existence Existence of network devices
+ */
+static inline int have_netdevs ( void ) {
+ return ( ! list_empty ( &net_devices ) );
+}
+
+/**
+ * Get reference to network device
+ *
+ * @v netdev Network device
+ * @ret netdev Network device
+ */
+static inline __attribute__ (( always_inline )) struct net_device *
+netdev_get ( struct net_device *netdev ) {
+ ref_get ( &netdev->refcnt );
+ return netdev;
+}
+
+/**
+ * Drop reference to network device
+ *
+ * @v netdev Network device
+ */
+static inline __attribute__ (( always_inline )) void
+netdev_put ( struct net_device *netdev ) {
+ ref_put ( &netdev->refcnt );
+}
+
+/**
+ * Get driver private area for this network device
+ *
+ * @v netdev Network device
+ * @ret priv Driver private area for this network device
+ */
+static inline __attribute__ (( always_inline )) void *
+netdev_priv ( struct net_device *netdev ) {
+ return netdev->priv;
+}
+
+/**
+ * Get per-netdevice configuration settings block
+ *
+ * @v netdev Network device
+ * @ret settings Settings block
+ */
+static inline __attribute__ (( always_inline )) struct settings *
+netdev_settings ( struct net_device *netdev ) {
+ return &netdev->settings.settings;
+}
+
+extern int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf );
+extern void netdev_tx_complete_err ( struct net_device *netdev,
+ struct io_buffer *iobuf, int rc );
+extern void netdev_tx_complete_next_err ( struct net_device *netdev, int rc );
+extern void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf );
+extern void netdev_rx_err ( struct net_device *netdev,
+ struct io_buffer *iobuf, int rc );
+extern void netdev_poll ( struct net_device *netdev );
+extern struct io_buffer * netdev_rx_dequeue ( struct net_device *netdev );
+extern struct net_device * alloc_netdev ( size_t priv_size );
+extern int register_netdev ( struct net_device *netdev );
+extern int netdev_open ( struct net_device *netdev );
+extern void netdev_close ( struct net_device *netdev );
+extern void unregister_netdev ( struct net_device *netdev );
+extern void netdev_irq ( struct net_device *netdev, int enable );
+extern struct net_device * find_netdev ( const char *name );
+extern struct net_device * find_netdev_by_location ( unsigned int bus_type,
+ unsigned int location );
+extern int net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
+ struct net_protocol *net_protocol, const void *ll_dest );
+extern int net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
+ uint16_t net_proto, const void *ll_source );
+
+extern struct settings_operations netdev_settings_operations;
+
+/**
+ * Complete network transmission
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ *
+ * The packet must currently be in the network device's TX queue.
+ */
+static inline void netdev_tx_complete ( struct net_device *netdev,
+ struct io_buffer *iobuf ) {
+ netdev_tx_complete_err ( netdev, iobuf, 0 );
+}
+
+/**
+ * Complete network transmission
+ *
+ * @v netdev Network device
+ *
+ * Completes the oldest outstanding packet in the TX queue.
+ */
+static inline void netdev_tx_complete_next ( struct net_device *netdev ) {
+ netdev_tx_complete_next_err ( netdev, 0 );
+}
+
+#endif /* _GPXE_NETDEVICE_H */
diff --git a/gpxe/src/include/gpxe/nvo.h b/gpxe/src/include/gpxe/nvo.h
new file mode 100644
index 00000000..28068f4b
--- /dev/null
+++ b/gpxe/src/include/gpxe/nvo.h
@@ -0,0 +1,53 @@
+#ifndef _GPXE_NVO_H
+#define _GPXE_NVO_H
+
+/** @file
+ *
+ * Non-volatile stored options
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/dhcpopts.h>
+#include <gpxe/settings.h>
+
+struct nvs_device;
+struct refcnt;
+
+/**
+ * A fragment of a non-volatile storage device used for stored options
+ */
+struct nvo_fragment {
+ /** Starting address of fragment within NVS device */
+ unsigned int address;
+ /** Length of fragment */
+ size_t len;
+};
+
+/**
+ * A block of non-volatile stored options
+ */
+struct nvo_block {
+ /** Settings block */
+ struct settings settings;
+ /** Underlying non-volatile storage device */
+ struct nvs_device *nvs;
+ /** List of option-containing fragments
+ *
+ * The list is terminated by a fragment with a length of zero.
+ */
+ struct nvo_fragment *fragments;
+ /** Total length of option-containing fragments */
+ size_t total_len;
+ /** Option-containing data */
+ void *data;
+ /** DHCP options block */
+ struct dhcp_options dhcpopts;
+};
+
+extern void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs,
+ struct nvo_fragment *fragments, struct refcnt *refcnt );
+extern int register_nvo ( struct nvo_block *nvo, struct settings *parent );
+extern void unregister_nvo ( struct nvo_block *nvo );
+
+#endif /* _GPXE_NVO_H */
diff --git a/gpxe/src/include/gpxe/nvs.h b/gpxe/src/include/gpxe/nvs.h
new file mode 100644
index 00000000..b026dd46
--- /dev/null
+++ b/gpxe/src/include/gpxe/nvs.h
@@ -0,0 +1,66 @@
+#ifndef _GPXE_NVS_H
+#define _GPXE_NVS_H
+
+/** @file
+ *
+ * Non-volatile storage
+ *
+ */
+
+#include <stdint.h>
+
+/** A non-volatile storage device */
+struct nvs_device {
+ /** Word length
+ *
+ * This is expressed as the base-2 logarithm of the word
+ * length in bytes. A value of 0 therefore translates as
+ * 8-bit words, and a value of 1 translates as 16-bit words.
+ */
+ unsigned int word_len_log2;
+ /** Device size (in words) */
+ unsigned int size;
+ /** Data block size (in words)
+ *
+ * This is the block size used by the device. It must be a
+ * power of two. Data reads and writes must not cross a block
+ * boundary.
+ *
+ * Many devices allow reads to cross a block boundary, and
+ * restrict only writes. For the sake of simplicity, we
+ * assume that the same restriction applies to both reads and
+ * writes.
+ */
+ unsigned int block_size;
+ /** Read data from device
+ *
+ * @v nvs NVS device
+ * @v address Address from which to read
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ *
+ * Reads may not cross a block boundary.
+ */
+ int ( * read ) ( struct nvs_device *nvs, unsigned int address,
+ void *data, size_t len );
+ /** Write data to device
+ *
+ * @v nvs NVS device
+ * @v address Address to which to write
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ *
+ * Writes may not cross a block boundary.
+ */
+ int ( * write ) ( struct nvs_device *nvs, unsigned int address,
+ const void *data, size_t len );
+};
+
+extern int nvs_read ( struct nvs_device *nvs, unsigned int address,
+ void *data, size_t len );
+extern int nvs_write ( struct nvs_device *nvs, unsigned int address,
+ const void *data, size_t len );
+
+#endif /* _GPXE_NVS_H */
diff --git a/gpxe/src/include/gpxe/open.h b/gpxe/src/include/gpxe/open.h
new file mode 100644
index 00000000..beab0a1f
--- /dev/null
+++ b/gpxe/src/include/gpxe/open.h
@@ -0,0 +1,95 @@
+#ifndef _GPXE_OPEN_H
+#define _GPXE_OPEN_H
+
+/** @file
+ *
+ * Data transfer interface opening
+ *
+ */
+
+#include <stdarg.h>
+#include <gpxe/tables.h>
+
+struct xfer_interface;
+struct uri;
+struct sockaddr;
+
+/** Location types */
+enum {
+ /** Location is a URI
+ *
+ * Parameter list for open() is:
+ *
+ * struct uri *uri;
+ */
+ LOCATION_URI = 1,
+ /** Location is a URI string
+ *
+ * Parameter list for open() is:
+ *
+ * const char *uri_string;
+ */
+ LOCATION_URI_STRING,
+ /** Location is a socket
+ *
+ * Parameter list for open() is:
+ *
+ * int semantics;
+ * struct sockaddr *peer;
+ * struct sockaddr *local;
+ */
+ LOCATION_SOCKET,
+};
+
+/** A URI opener */
+struct uri_opener {
+ /** URI protocol name
+ *
+ * This is the "scheme" portion of the URI, e.g. "http" or
+ * "file".
+ */
+ const char *scheme;
+ /** Open URI
+ *
+ * @v xfer Data transfer interface
+ * @v uri URI
+ * @ret rc Return status code
+ */
+ int ( * open ) ( struct xfer_interface *xfer, struct uri *uri );
+};
+
+/** Register a URI opener */
+#define __uri_opener __table ( struct uri_opener, uri_openers, 01 )
+
+/** A socket opener */
+struct socket_opener {
+ /** Communication semantics (e.g. SOCK_STREAM) */
+ int semantics;
+ /** Address family (e.g. AF_INET) */
+ int family;
+ /** Open socket
+ *
+ * @v xfer Data transfer interface
+ * @v peer Peer socket address
+ * @v local Local socket address, or NULL
+ * @ret rc Return status code
+ */
+ int ( * open ) ( struct xfer_interface *xfer, struct sockaddr *peer,
+ struct sockaddr *local );
+};
+
+/** Register a socket opener */
+#define __socket_opener __table ( struct socket_opener, socket_openers, 01 )
+
+extern int xfer_open_uri ( struct xfer_interface *xfer, struct uri *uri );
+extern int xfer_open_uri_string ( struct xfer_interface *xfer,
+ const char *uri_string );
+extern int xfer_open_named_socket ( struct xfer_interface *xfer,
+ int semantics, struct sockaddr *peer,
+ const char *name, struct sockaddr *local );
+extern int xfer_open_socket ( struct xfer_interface *xfer, int semantics,
+ struct sockaddr *peer, struct sockaddr *local );
+extern int xfer_vopen ( struct xfer_interface *xfer, int type, va_list args );
+extern int xfer_open ( struct xfer_interface *xfer, int type, ... );
+
+#endif /* _GPXE_OPEN_H */
diff --git a/gpxe/src/include/gpxe/pci.h b/gpxe/src/include/gpxe/pci.h
new file mode 100644
index 00000000..b5c417c9
--- /dev/null
+++ b/gpxe/src/include/gpxe/pci.h
@@ -0,0 +1,356 @@
+#ifndef _GPXE_PCI_H
+#define _GPXE_PCI_H
+
+/*
+ * Support for NE2000 PCI clones added David Monro June 1997
+ * Generalised for other PCI NICs by Ken Yap July 1997
+ * PCI support rewritten by Michael Brown 2006
+ *
+ * Most of this is taken from /usr/src/linux/include/linux/pci.h.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#include <stdint.h>
+#include <gpxe/device.h>
+#include <gpxe/tables.h>
+#include <pci_io.h>
+#include "pci_ids.h"
+
+/*
+ * PCI constants
+ *
+ */
+
+#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */
+#define PCI_COMMAND_MEM 0x2 /* Enable response in mem space */
+#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */
+#define PCI_LATENCY_TIMER 0x0d /* 8 bits */
+#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */
+#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */
+#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */
+#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */
+#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */
+#define PCI_COMMAND_SERR 0x100 /* Enable SERR */
+#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */
+
+
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+
+#define PCI_STATUS 0x06 /* 16 bits */
+#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */
+#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */
+#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */
+#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */
+#define PCI_STATUS_PARITY 0x100 /* Detected parity error */
+#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */
+#define PCI_STATUS_DEVSEL_FAST 0x000
+#define PCI_STATUS_DEVSEL_MEDIUM 0x200
+#define PCI_STATUS_DEVSEL_SLOW 0x400
+#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */
+#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */
+#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */
+#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */
+#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */
+
+#define PCI_REVISION 0x08 /* 8 bits */
+#define PCI_REVISION_ID 0x08 /* 8 bits */
+#define PCI_CLASS_REVISION 0x08 /* 32 bits */
+#define PCI_CLASS_CODE 0x0b /* 8 bits */
+#define PCI_SUBCLASS_CODE 0x0a /* 8 bits */
+#define PCI_HEADER_TYPE 0x0e /* 8 bits */
+#define PCI_HEADER_TYPE_NORMAL 0
+#define PCI_HEADER_TYPE_BRIDGE 1
+#define PCI_HEADER_TYPE_CARDBUS 2
+
+
+/* Header type 0 (normal devices) */
+#define PCI_CARDBUS_CIS 0x28
+#define PCI_SUBSYSTEM_VENDOR_ID 0x2c
+#define PCI_SUBSYSTEM_ID 0x2e
+
+#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */
+#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits */
+#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits */
+#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */
+#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */
+#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */
+
+#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */
+#define PCI_BASE_ADDRESS_SPACE_IO 0x01
+#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00
+
+#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
+#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */
+#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */
+#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */
+#define PCI_BASE_ADDRESS_MEM_MASK (~0x0f)
+#define PCI_BASE_ADDRESS_IO_MASK (~0x03)
+#define PCI_ROM_ADDRESS 0x30 /* 32 bits */
+#define PCI_ROM_ADDRESS_ENABLE 0x01 /* Write 1 to enable ROM,
+ bits 31..11 are address,
+ 10..2 are reserved */
+
+#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */
+
+#define PCI_INTERRUPT_LINE 0x3c /* IRQ number (0-15) */
+#define PCI_INTERRUPT_PIN 0x3d /* IRQ pin on PCI bus (A-D) */
+
+/* Header type 1 (PCI-to-PCI bridges) */
+#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */
+#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */
+#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */
+#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */
+#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */
+#define PCI_IO_LIMIT 0x1d
+#define PCI_IO_RANGE_TYPE_MASK 0x0f /* I/O bridging type */
+#define PCI_IO_RANGE_TYPE_16 0x00
+#define PCI_IO_RANGE_TYPE_32 0x01
+#define PCI_IO_RANGE_MASK ~0x0f
+#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */
+#define PCI_MEMORY_BASE 0x20 /* Memory range behind */
+#define PCI_MEMORY_LIMIT 0x22
+#define PCI_MEMORY_RANGE_TYPE_MASK 0x0f
+#define PCI_MEMORY_RANGE_MASK ~0x0f
+#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */
+#define PCI_PREF_MEMORY_LIMIT 0x26
+#define PCI_PREF_RANGE_TYPE_MASK 0x0f
+#define PCI_PREF_RANGE_TYPE_32 0x00
+#define PCI_PREF_RANGE_TYPE_64 0x01
+#define PCI_PREF_RANGE_MASK ~0x0f
+#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */
+#define PCI_PREF_LIMIT_UPPER32 0x2c
+#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */
+#define PCI_IO_LIMIT_UPPER16 0x32
+/* 0x34 same as for htype 0 */
+/* 0x35-0x3b is reserved */
+#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_BRIDGE_CONTROL 0x3e
+#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */
+#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */
+#define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */
+#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */
+#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */
+#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */
+#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */
+
+#define PCI_CB_CAPABILITY_LIST 0x14
+
+/* Capability lists */
+
+#define PCI_CAP_LIST_ID 0 /* Capability ID */
+#define PCI_CAP_ID_PM 0x01 /* Power Management */
+#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */
+#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */
+#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */
+#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */
+#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */
+#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */
+#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */
+#define PCI_CAP_SIZEOF 4
+
+/* Power Management Registers */
+
+#define PCI_PM_PMC 2 /* PM Capabilities Register */
+#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */
+#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */
+#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */
+#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */
+#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */
+#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */
+#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */
+#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */
+#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */
+#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */
+#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */
+#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */
+#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */
+#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */
+#define PCI_PM_CTRL 4 /* PM control and status register */
+#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */
+#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */
+#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */
+#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */
+#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */
+#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */
+#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */
+#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */
+#define PCI_PM_DATA_REGISTER 7 /* (??) */
+#define PCI_PM_SIZEOF 8
+
+/* AGP registers */
+
+#define PCI_AGP_VERSION 2 /* BCD version number */
+#define PCI_AGP_RFU 3 /* Rest of capability flags */
+#define PCI_AGP_STATUS 4 /* Status register */
+#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */
+#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */
+#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */
+#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */
+#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */
+#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */
+#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */
+#define PCI_AGP_COMMAND 8 /* Control register */
+#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */
+#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */
+#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */
+#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */
+#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */
+#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */
+#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */
+#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */
+#define PCI_AGP_SIZEOF 12
+
+/* Slot Identification */
+
+#define PCI_SID_ESR 2 /* Expansion Slot Register */
+#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */
+#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */
+#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */
+
+/* Message Signalled Interrupts registers */
+
+#define PCI_MSI_FLAGS 2 /* Various flags */
+#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */
+#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */
+#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */
+#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */
+#define PCI_MSI_RFU 3 /* Rest of capability flags */
+#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */
+#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */
+#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */
+#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
+
+/** A PCI device ID list entry */
+struct pci_device_id {
+ /** Name */
+ const char *name;
+ /** PCI vendor ID */
+ uint16_t vendor;
+ /** PCI device ID */
+ uint16_t device;
+};
+
+/** Match-anything ID */
+#define PCI_ANY_ID 0xffff
+
+/** A PCI device */
+struct pci_device {
+ /** Generic device */
+ struct device dev;
+ /** Memory base
+ *
+ * This is the physical address of the first valid memory BAR.
+ */
+ unsigned long membase;
+ /**
+ * I/O address
+ *
+ * This is the physical address of the first valid I/O BAR.
+ */
+ unsigned long ioaddr;
+ /** Vendor ID */
+ uint16_t vendor;
+ /** Device ID */
+ uint16_t device;
+ /** Device class */
+ uint32_t class;
+ /** Interrupt number */
+ uint8_t irq;
+ /** Bus number */
+ uint8_t bus;
+ /** Device and function number */
+ uint8_t devfn;
+ /** Driver for this device */
+ struct pci_driver *driver;
+ /** Driver-private data
+ *
+ * Use pci_set_drvdata() and pci_get_drvdata() to access this
+ * field.
+ */
+ void *priv;
+ /** Driver name */
+ const char *driver_name;
+};
+
+/** A PCI driver */
+struct pci_driver {
+ /** PCI ID table */
+ struct pci_device_id *ids;
+ /** Number of entries in PCI ID table */
+ unsigned int id_count;
+ /**
+ * Probe device
+ *
+ * @v pci PCI device
+ * @v id Matching entry in ID table
+ * @ret rc Return status code
+ */
+ int ( * probe ) ( struct pci_device *pci,
+ const struct pci_device_id *id );
+ /**
+ * Remove device
+ *
+ * @v pci PCI device
+ */
+ void ( * remove ) ( struct pci_device *pci );
+};
+
+/** Declare a PCI driver */
+#define __pci_driver __table ( struct pci_driver, pci_drivers, 01 )
+
+#define PCI_DEVFN( slot, func ) ( ( (slot) << 3 ) | (func) )
+#define PCI_SLOT( devfn ) ( ( (devfn) >> 3 ) & 0x1f )
+#define PCI_FUNC( devfn ) ( (devfn) & 0x07 )
+#define PCI_BUSDEVFN( bus, devfn ) ( ( (bus) << 8 ) | (devfn) )
+
+#define PCI_BASE_CLASS( class ) ( (class) >> 16 )
+#define PCI_SUB_CLASS( class ) ( ( (class) >> 8 ) & 0xff )
+#define PCI_PROG_INTF( class ) ( (class) & 0xff )
+
+/*
+ * PCI_ROM is used to build up entries in a struct pci_id array. It
+ * is also parsed by parserom.pl to generate Makefile rules and files
+ * for rom-o-matic.
+ */
+#define PCI_ROM( _vendor, _device, _name, _description ) { \
+ .vendor = _vendor, \
+ .device = _device, \
+ .name = _name, \
+}
+
+extern void adjust_pci_device ( struct pci_device *pci );
+extern unsigned long pci_bar_start ( struct pci_device *pci,
+ unsigned int reg );
+extern int pci_find_capability ( struct pci_device *pci, int capability );
+extern unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg );
+
+/**
+ * Set PCI driver-private data
+ *
+ * @v pci PCI device
+ * @v priv Private data
+ */
+static inline void pci_set_drvdata ( struct pci_device *pci, void *priv ) {
+ pci->priv = priv;
+}
+
+/**
+ * Get PCI driver-private data
+ *
+ * @v pci PCI device
+ * @ret priv Private data
+ */
+static inline void * pci_get_drvdata ( struct pci_device *pci ) {
+ return pci->priv;
+}
+
+#endif /* _GPXE_PCI_H */
diff --git a/gpxe/src/include/gpxe/pci_ids.h b/gpxe/src/include/gpxe/pci_ids.h
new file mode 100644
index 00000000..075ff96b
--- /dev/null
+++ b/gpxe/src/include/gpxe/pci_ids.h
@@ -0,0 +1,348 @@
+#ifndef _GPXE_PCI_IDS_H
+#define _GPXE_PCI_IDS_H
+
+/*
+ * PCI Class, Vendor and Device IDs
+ *
+ * Please keep sorted.
+ */
+
+/* Device classes and subclasses */
+
+#define PCI_CLASS_NOT_DEFINED 0x0000
+#define PCI_CLASS_NOT_DEFINED_VGA 0x0001
+
+#define PCI_BASE_CLASS_STORAGE 0x01
+#define PCI_CLASS_STORAGE_SCSI 0x0100
+#define PCI_CLASS_STORAGE_IDE 0x0101
+#define PCI_CLASS_STORAGE_FLOPPY 0x0102
+#define PCI_CLASS_STORAGE_IPI 0x0103
+#define PCI_CLASS_STORAGE_RAID 0x0104
+#define PCI_CLASS_STORAGE_OTHER 0x0180
+
+#define PCI_BASE_CLASS_NETWORK 0x02
+#define PCI_CLASS_NETWORK_ETHERNET 0x0200
+#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201
+#define PCI_CLASS_NETWORK_FDDI 0x0202
+#define PCI_CLASS_NETWORK_ATM 0x0203
+#define PCI_CLASS_NETWORK_OTHER 0x0280
+
+#define PCI_BASE_CLASS_DISPLAY 0x03
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_DISPLAY_XGA 0x0301
+#define PCI_CLASS_DISPLAY_3D 0x0302
+#define PCI_CLASS_DISPLAY_OTHER 0x0380
+
+#define PCI_BASE_CLASS_MULTIMEDIA 0x04
+#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400
+#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401
+#define PCI_CLASS_MULTIMEDIA_PHONE 0x0402
+#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480
+
+#define PCI_BASE_CLASS_MEMORY 0x05
+#define PCI_CLASS_MEMORY_RAM 0x0500
+#define PCI_CLASS_MEMORY_FLASH 0x0501
+#define PCI_CLASS_MEMORY_OTHER 0x0580
+
+#define PCI_BASE_CLASS_BRIDGE 0x06
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+#define PCI_CLASS_BRIDGE_ISA 0x0601
+#define PCI_CLASS_BRIDGE_EISA 0x0602
+#define PCI_CLASS_BRIDGE_MC 0x0603
+#define PCI_CLASS_BRIDGE_PCI 0x0604
+#define PCI_CLASS_BRIDGE_PCMCIA 0x0605
+#define PCI_CLASS_BRIDGE_NUBUS 0x0606
+#define PCI_CLASS_BRIDGE_CARDBUS 0x0607
+#define PCI_CLASS_BRIDGE_RACEWAY 0x0608
+#define PCI_CLASS_BRIDGE_OTHER 0x0680
+
+#define PCI_BASE_CLASS_COMMUNICATION 0x07
+#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700
+#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701
+#define PCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702
+#define PCI_CLASS_COMMUNICATION_MODEM 0x0703
+#define PCI_CLASS_COMMUNICATION_OTHER 0x0780
+
+#define PCI_BASE_CLASS_SYSTEM 0x08
+#define PCI_CLASS_SYSTEM_PIC 0x0800
+#define PCI_CLASS_SYSTEM_DMA 0x0801
+#define PCI_CLASS_SYSTEM_TIMER 0x0802
+#define PCI_CLASS_SYSTEM_RTC 0x0803
+#define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804
+#define PCI_CLASS_SYSTEM_OTHER 0x0880
+
+#define PCI_BASE_CLASS_INPUT 0x09
+#define PCI_CLASS_INPUT_KEYBOARD 0x0900
+#define PCI_CLASS_INPUT_PEN 0x0901
+#define PCI_CLASS_INPUT_MOUSE 0x0902
+#define PCI_CLASS_INPUT_SCANNER 0x0903
+#define PCI_CLASS_INPUT_GAMEPORT 0x0904
+#define PCI_CLASS_INPUT_OTHER 0x0980
+
+#define PCI_BASE_CLASS_DOCKING 0x0a
+#define PCI_CLASS_DOCKING_GENERIC 0x0a00
+#define PCI_CLASS_DOCKING_OTHER 0x0a80
+
+#define PCI_BASE_CLASS_PROCESSOR 0x0b
+#define PCI_CLASS_PROCESSOR_386 0x0b00
+#define PCI_CLASS_PROCESSOR_486 0x0b01
+#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02
+#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10
+#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20
+#define PCI_CLASS_PROCESSOR_MIPS 0x0b30
+#define PCI_CLASS_PROCESSOR_CO 0x0b40
+
+#define PCI_BASE_CLASS_SERIAL 0x0c
+#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00
+#define PCI_CLASS_SERIAL_ACCESS 0x0c01
+#define PCI_CLASS_SERIAL_SSA 0x0c02
+#define PCI_CLASS_SERIAL_USB 0x0c03
+#define PCI_CLASS_SERIAL_FIBER 0x0c04
+#define PCI_CLASS_SERIAL_SMBUS 0x0c05
+
+#define PCI_BASE_CLASS_INTELLIGENT 0x0e
+#define PCI_CLASS_INTELLIGENT_I2O 0x0e00
+
+#define PCI_BASE_CLASS_SATELLITE 0x0f
+#define PCI_CLASS_SATELLITE_TV 0x0f00
+#define PCI_CLASS_SATELLITE_AUDIO 0x0f01
+#define PCI_CLASS_SATELLITE_VOICE 0x0f03
+#define PCI_CLASS_SATELLITE_DATA 0x0f04
+
+#define PCI_BASE_CLASS_CRYPT 0x10
+#define PCI_CLASS_CRYPT_NETWORK 0x1000
+#define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1001
+#define PCI_CLASS_CRYPT_OTHER 0x1080
+
+#define PCI_BASE_CLASS_SIGNAL_PROCESSING 0x11
+#define PCI_CLASS_SP_DPIO 0x1100
+#define PCI_CLASS_SP_OTHER 0x1180
+
+#define PCI_CLASS_OTHERS 0xff
+
+/* Vendors */
+
+#define PCI_VENDOR_ID_DYNALINK 0x0675
+#define PCI_VENDOR_ID_BERKOM 0x0871
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+#define PCI_VENDOR_ID_NCR 0x1000
+#define PCI_VENDOR_ID_LSI_LOGIC 0x1000
+#define PCI_VENDOR_ID_ATI 0x1002
+#define PCI_VENDOR_ID_VLSI 0x1004
+#define PCI_VENDOR_ID_ADL 0x1005
+#define PCI_VENDOR_ID_NS 0x100b
+#define PCI_VENDOR_ID_TSENG 0x100c
+#define PCI_VENDOR_ID_WEITEK 0x100e
+#define PCI_VENDOR_ID_DEC 0x1011
+#define PCI_VENDOR_ID_CIRRUS 0x1013
+#define PCI_VENDOR_ID_IBM 0x1014
+#define PCI_VENDOR_ID_COMPEX2 0x101a
+/* pci.ids says "AT&T GIS (NCR)" */
+#define PCI_VENDOR_ID_WD 0x101c
+#define PCI_VENDOR_ID_AMI 0x101e
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_VENDOR_ID_TRIDENT 0x1023
+#define PCI_VENDOR_ID_AI 0x1025
+#define PCI_VENDOR_ID_DELL 0x1028
+#define PCI_VENDOR_ID_MATROX 0x102B
+#define PCI_VENDOR_ID_CT 0x102c
+#define PCI_VENDOR_ID_MIRO 0x1031
+#define PCI_VENDOR_ID_NEC 0x1033
+#define PCI_VENDOR_ID_FD 0x1036
+#define PCI_VENDOR_ID_SIS 0x1039
+#define PCI_VENDOR_ID_SI 0x1039
+#define PCI_VENDOR_ID_HP 0x103c
+#define PCI_VENDOR_ID_PCTECH 0x1042
+#define PCI_VENDOR_ID_ASUSTEK 0x1043
+#define PCI_VENDOR_ID_DPT 0x1044
+#define PCI_VENDOR_ID_OPTI 0x1045
+#define PCI_VENDOR_ID_ELSA 0x1048
+#define PCI_VENDOR_ID_ELSA 0x1048
+#define PCI_VENDOR_ID_SGS 0x104a
+#define PCI_VENDOR_ID_BUSLOGIC 0x104B
+#define PCI_VENDOR_ID_TI 0x104c
+#define PCI_VENDOR_ID_SONY 0x104d
+#define PCI_VENDOR_ID_OAK 0x104e
+/* Winbond have two vendor IDs! See 0x10ad as well */
+#define PCI_VENDOR_ID_WINBOND2 0x1050
+#define PCI_VENDOR_ID_ANIGMA 0x1051
+#define PCI_VENDOR_ID_EFAR 0x1055
+#define PCI_VENDOR_ID_MOTOROLA 0x1057
+#define PCI_VENDOR_ID_MOTOROLA_OOPS 0x1507
+#define PCI_VENDOR_ID_PROMISE 0x105a
+#define PCI_VENDOR_ID_N9 0x105d
+#define PCI_VENDOR_ID_UMC 0x1060
+#define PCI_VENDOR_ID_X 0x1061
+#define PCI_VENDOR_ID_MYLEX 0x1069
+#define PCI_VENDOR_ID_PICOP 0x1066
+#define PCI_VENDOR_ID_APPLE 0x106b
+#define PCI_VENDOR_ID_YAMAHA 0x1073
+#define PCI_VENDOR_ID_NEXGEN 0x1074
+#define PCI_VENDOR_ID_QLOGIC 0x1077
+#define PCI_VENDOR_ID_CYRIX 0x1078
+#define PCI_VENDOR_ID_LEADTEK 0x107d
+#define PCI_VENDOR_ID_INTERPHASE 0x107e
+#define PCI_VENDOR_ID_CONTAQ 0x1080
+#define PCI_VENDOR_ID_FOREX 0x1083
+#define PCI_VENDOR_ID_OLICOM 0x108d
+#define PCI_VENDOR_ID_SUN 0x108e
+#define PCI_VENDOR_ID_CMD 0x1095
+#define PCI_VENDOR_ID_VISION 0x1098
+#define PCI_VENDOR_ID_BROOKTREE 0x109e
+#define PCI_VENDOR_ID_SIERRA 0x10a8
+#define PCI_VENDOR_ID_SGI 0x10a9
+#define PCI_VENDOR_ID_ACC 0x10aa
+#define PCI_VENDOR_ID_WINBOND 0x10ad
+#define PCI_VENDOR_ID_DATABOOK 0x10b3
+#define PCI_VENDOR_ID_PLX 0x10b5
+#define PCI_VENDOR_ID_MADGE 0x10b6
+#define PCI_VENDOR_ID_3COM 0x10b7
+#define PCI_VENDOR_ID_SMC 0x10b8
+#define PCI_VENDOR_ID_SUNDANCE 0x13F0
+#define PCI_VENDOR_ID_AL 0x10b9
+#define PCI_VENDOR_ID_MITSUBISHI 0x10ba
+#define PCI_VENDOR_ID_SURECOM 0x10bd
+#define PCI_VENDOR_ID_NEOMAGIC 0x10c8
+#define PCI_VENDOR_ID_ASP 0x10cd
+#define PCI_VENDOR_ID_MACRONIX 0x10d9
+#define PCI_VENDOR_ID_TCONRAD 0x10da
+#define PCI_VENDOR_ID_CERN 0x10dc
+#define PCI_VENDOR_ID_NVIDIA 0x10de
+#define PCI_VENDOR_ID_IMS 0x10e0
+#define PCI_VENDOR_ID_TEKRAM2 0x10e1
+#define PCI_VENDOR_ID_TUNDRA 0x10e3
+#define PCI_VENDOR_ID_AMCC 0x10e8
+#define PCI_VENDOR_ID_INTERG 0x10ea
+#define PCI_VENDOR_ID_REALTEK 0x10ec
+#define PCI_VENDOR_ID_XILINX 0x10ee
+#define PCI_VENDOR_ID_TRUEVISION 0x10fa
+#define PCI_VENDOR_ID_INIT 0x1101
+#define PCI_VENDOR_ID_CREATIVE 0x1102
+/* duplicate: ECTIVA */
+#define PCI_VENDOR_ID_ECTIVA 0x1102
+/* duplicate: CREATIVE */
+#define PCI_VENDOR_ID_TTI 0x1103
+#define PCI_VENDOR_ID_VIA 0x1106
+#define PCI_VENDOR_ID_VIATEC 0x1106
+#define PCI_VENDOR_ID_SIEMENS 0x110A
+#define PCI_VENDOR_ID_SMC2 0x1113
+#define PCI_VENDOR_ID_VORTEX 0x1119
+#define PCI_VENDOR_ID_EF 0x111a
+#define PCI_VENDOR_ID_IDT 0x111d
+#define PCI_VENDOR_ID_FORE 0x1127
+#define PCI_VENDOR_ID_IMAGINGTECH 0x112f
+#define PCI_VENDOR_ID_PHILIPS 0x1131
+#define PCI_VENDOR_ID_EICON 0x1133
+#define PCI_VENDOR_ID_CYCLONE 0x113c
+#define PCI_VENDOR_ID_ALLIANCE 0x1142
+#define PCI_VENDOR_ID_SYSKONNECT 0x1148
+#define PCI_VENDOR_ID_VMIC 0x114a
+#define PCI_VENDOR_ID_DIGI 0x114f
+#define PCI_VENDOR_ID_MUTECH 0x1159
+#define PCI_VENDOR_ID_XIRCOM 0x115d
+#define PCI_VENDOR_ID_RENDITION 0x1163
+#define PCI_VENDOR_ID_SERVERWORKS 0x1166
+#define PCI_VENDOR_ID_SBE 0x1176
+#define PCI_VENDOR_ID_TOSHIBA 0x1179
+#define PCI_VENDOR_ID_RICOH 0x1180
+#define PCI_VENDOR_ID_DLINK 0x1186
+#define PCI_VENDOR_ID_ARTOP 0x1191
+#define PCI_VENDOR_ID_ZEITNET 0x1193
+#define PCI_VENDOR_ID_OMEGA 0x119b
+#define PCI_VENDOR_ID_FUJITSU_ME 0x119e
+#define PCI_SUBVENDOR_ID_KEYSPAN 0x11a9
+#define PCI_VENDOR_ID_GALILEO 0x11ab
+#define PCI_VENDOR_ID_LINKSYS 0x11ad
+#define PCI_VENDOR_ID_LITEON 0x11ad
+#define PCI_VENDOR_ID_V3 0x11b0
+#define PCI_VENDOR_ID_NP 0x11bc
+#define PCI_VENDOR_ID_ATT 0x11c1
+#define PCI_VENDOR_ID_SPECIALIX 0x11cb
+#define PCI_VENDOR_ID_AURAVISION 0x11d1
+#define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4
+#define PCI_VENDOR_ID_IKON 0x11d5
+#define PCI_VENDOR_ID_ZORAN 0x11de
+#define PCI_VENDOR_ID_KINETIC 0x11f4
+#define PCI_VENDOR_ID_COMPEX 0x11f6
+#define PCI_VENDOR_ID_RP 0x11fe
+#define PCI_VENDOR_ID_CYCLADES 0x120e
+#define PCI_VENDOR_ID_ESSENTIAL 0x120f
+#define PCI_VENDOR_ID_O2 0x1217
+#define PCI_VENDOR_ID_3DFX 0x121a
+#define PCI_VENDOR_ID_SIGMADES 0x1236
+#define PCI_VENDOR_ID_CCUBE 0x123f
+#define PCI_VENDOR_ID_AVM 0x1244
+#define PCI_VENDOR_ID_DIPIX 0x1246
+#define PCI_VENDOR_ID_STALLION 0x124d
+#define PCI_VENDOR_ID_OPTIBASE 0x1255
+#define PCI_VENDOR_ID_ESS 0x125d
+#define PCI_VENDOR_ID_HARRIS 0x1260
+#define PCI_VENDOR_ID_SATSAGEM 0x1267
+#define PCI_VENDOR_ID_HUGHES 0x1273
+#define PCI_VENDOR_ID_ENSONIQ 0x1274
+#define PCI_VENDOR_ID_ROCKWELL 0x127A
+#define PCI_VENDOR_ID_DAVICOM 0x1282
+#define PCI_VENDOR_ID_ITE 0x1283
+/* formerly Platform Tech */
+#define PCI_VENDOR_ID_ESS_OLD 0x1285
+#define PCI_VENDOR_ID_ALTEON 0x12ae
+#define PCI_VENDOR_ID_USR 0x12B9
+#define PCI_VENDOR_ID_HOLTEK 0x12c3
+#define PCI_SUBVENDOR_ID_CONNECT_TECH 0x12c4
+#define PCI_VENDOR_ID_PICTUREL 0x12c5
+#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2
+#define PCI_SUBVENDOR_ID_CHASE_PCIFAST 0x12E0
+#define PCI_SUBVENDOR_ID_CHASE_PCIRAS 0x124D
+#define PCI_VENDOR_ID_AUREAL 0x12eb
+#define PCI_VENDOR_ID_CBOARDS 0x1307
+#define PCI_VENDOR_ID_SIIG 0x131f
+#define PCI_VENDOR_ID_ADMTEK 0x1317
+#define PCI_VENDOR_ID_DOMEX 0x134a
+#define PCI_VENDOR_ID_QUATECH 0x135C
+#define PCI_VENDOR_ID_SEALEVEL 0x135e
+#define PCI_VENDOR_ID_HYPERCOPE 0x1365
+#define PCI_VENDOR_ID_KAWASAKI 0x136b
+#define PCI_VENDOR_ID_LMC 0x1376
+#define PCI_VENDOR_ID_NETGEAR 0x1385
+#define PCI_VENDOR_ID_APPLICOM 0x1389
+#define PCI_VENDOR_ID_MOXA 0x1393
+#define PCI_VENDOR_ID_CCD 0x1397
+#define PCI_VENDOR_ID_MICROGATE 0x13c0
+#define PCI_VENDOR_ID_3WARE 0x13C1
+#define PCI_VENDOR_ID_ABOCOM 0x13D1
+#define PCI_VENDOR_ID_CMEDIA 0x13f6
+#define PCI_VENDOR_ID_LAVA 0x1407
+#define PCI_VENDOR_ID_TIMEDIA 0x1409
+#define PCI_VENDOR_ID_OXSEMI 0x1415
+#define PCI_VENDOR_ID_AIRONET 0x14b9
+#define PCI_VENDOR_ID_TITAN 0x14D2
+#define PCI_VENDOR_ID_PANACOM 0x14d4
+#define PCI_VENDOR_ID_BROADCOM 0x14e4
+#define PCI_VENDOR_ID_SYBA 0x1592
+#define PCI_VENDOR_ID_MORETON 0x15aa
+#define PCI_VENDOR_ID_ZOLTRIX 0x15b0
+#define PCI_VENDOR_ID_PDC 0x15e9
+#define PCI_VENDOR_ID_FSC 0x1734
+#define PCI_VENDOR_ID_SYMPHONY 0x1c1c
+#define PCI_VENDOR_ID_TEKRAM 0x1de1
+#define PCI_VENDOR_ID_3DLABS 0x3d3d
+#define PCI_VENDOR_ID_AVANCE 0x4005
+#define PCI_VENDOR_ID_AKS 0x416c
+#define PCI_VENDOR_ID_NETVIN 0x4a14
+#define PCI_VENDOR_ID_S3 0x5333
+#define PCI_VENDOR_ID_DCI 0x6666
+#define PCI_VENDOR_ID_GENROCO 0x5555
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_VENDOR_ID_COMPUTONE 0x8e0e
+#define PCI_SUBVENDOR_ID_COMPUTONE 0x8e0e
+#define PCI_VENDOR_ID_KTI 0x8e2e
+#define PCI_VENDOR_ID_ADAPTEC 0x9004
+#define PCI_VENDOR_ID_ADAPTEC2 0x9005
+#define PCI_VENDOR_ID_ATRONICS 0x907f
+#define PCI_VENDOR_ID_HOLTEK2 0x9412
+#define PCI_VENDOR_ID_NETMOS 0x9710
+#define PCI_SUBVENDOR_ID_EXSYS 0xd84d
+#define PCI_VENDOR_ID_TIGERJET 0xe159
+#define PCI_VENDOR_ID_ARK 0xedd8
+
+#endif /* _GPXE_PCI_IDS_H */
diff --git a/gpxe/src/include/gpxe/posix_io.h b/gpxe/src/include/gpxe/posix_io.h
new file mode 100644
index 00000000..9984db00
--- /dev/null
+++ b/gpxe/src/include/gpxe/posix_io.h
@@ -0,0 +1,85 @@
+#ifndef _GPXE_POSIX_IO_H
+#define _GPXE_POSIX_IO_H
+
+/** @file
+ *
+ * POSIX-like I/O
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/uaccess.h>
+
+/** Minimum file descriptor that will ever be allocated */
+#define POSIX_FD_MIN ( 1 )
+
+/** Maximum file descriptor that will ever be allocated */
+#define POSIX_FD_MAX ( 31 )
+
+/** File descriptor set as used for select() */
+typedef uint32_t fd_set;
+
+extern int open ( const char *uri_string );
+extern ssize_t read_user ( int fd, userptr_t buffer,
+ off_t offset, size_t len );
+extern int select ( fd_set *readfds, int wait );
+extern ssize_t fsize ( int fd );
+extern int close ( int fd );
+
+/**
+ * Zero a file descriptor set
+ *
+ * @v set File descriptor set
+ */
+static inline __attribute__ (( always_inline )) void
+FD_ZERO ( fd_set *set ) {
+ *set = 0;
+}
+
+/**
+ * Set a bit within a file descriptor set
+ *
+ * @v fd File descriptor
+ * @v set File descriptor set
+ */
+static inline __attribute__ (( always_inline )) void
+FD_SET ( int fd, fd_set *set ) {
+ *set |= ( 1 << fd );
+}
+
+/**
+ * Clear a bit within a file descriptor set
+ *
+ * @v fd File descriptor
+ * @v set File descriptor set
+ */
+static inline __attribute__ (( always_inline )) void
+FD_CLR ( int fd, fd_set *set ) {
+ *set &= ~( 1 << fd );
+}
+
+/**
+ * Test a bit within a file descriptor set
+ *
+ * @v fd File descriptor
+ * @v set File descriptor set
+ * @ret is_set Corresponding bit is set
+ */
+static inline __attribute__ (( always_inline )) int
+FD_ISSET ( int fd, fd_set *set ) {
+ return ( *set & ( 1 << fd ) );
+}
+
+/**
+ * Read data from file
+ *
+ * @v fd File descriptor
+ * @v buf Data buffer
+ * @v len Maximum length to read
+ * @ret len Actual length read, or negative error number
+ */
+static inline ssize_t read ( int fd, void *buf, size_t len ) {
+ return read_user ( fd, virt_to_user ( buf ), 0, len );
+}
+
+#endif /* _GPXE_POSIX_IO_H */
diff --git a/gpxe/src/include/gpxe/process.h b/gpxe/src/include/gpxe/process.h
new file mode 100644
index 00000000..8d9b109a
--- /dev/null
+++ b/gpxe/src/include/gpxe/process.h
@@ -0,0 +1,75 @@
+#ifndef _GPXE_PROCESS_H
+#define _GPXE_PROCESS_H
+
+/** @file
+ *
+ * Processes
+ *
+ */
+
+#include <gpxe/list.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/tables.h>
+
+/** A process */
+struct process {
+ /** List of processes */
+ struct list_head list;
+ /**
+ * Single-step the process
+ *
+ * This method should execute a single step of the process.
+ * Returning from this method is isomorphic to yielding the
+ * CPU to another process.
+ */
+ void ( * step ) ( struct process *process );
+ /** Reference counter
+ *
+ * If this interface is not part of a reference-counted
+ * object, this field may be NULL.
+ */
+ struct refcnt *refcnt;
+};
+
+extern void process_add ( struct process *process );
+extern void process_del ( struct process *process );
+extern void step ( void );
+
+/**
+ * Initialise process without adding to process list
+ *
+ * @v process Process
+ * @v step Process' step() method
+ */
+static inline __attribute__ (( always_inline )) void
+process_init_stopped ( struct process *process,
+ void ( * step ) ( struct process *process ),
+ struct refcnt *refcnt ) {
+ process->step = step;
+ process->refcnt = refcnt;
+}
+
+/**
+ * Initialise process and add to process list
+ *
+ * @v process Process
+ * @v step Process' step() method
+ */
+static inline __attribute__ (( always_inline )) void
+process_init ( struct process *process,
+ void ( * step ) ( struct process *process ),
+ struct refcnt *refcnt ) {
+ process_init_stopped ( process, step, refcnt );
+ process_add ( process );
+}
+
+/**
+ * Declare a permanent process
+ *
+ * Permanent processes will be automatically added to the process list
+ * at initialisation time.
+ */
+#define __permanent_process \
+ __table ( struct process, processes, 01 )
+
+#endif /* _GPXE_PROCESS_H */
diff --git a/gpxe/src/include/gpxe/profile.h b/gpxe/src/include/gpxe/profile.h
new file mode 100644
index 00000000..d46ca05a
--- /dev/null
+++ b/gpxe/src/include/gpxe/profile.h
@@ -0,0 +1,78 @@
+#ifndef _GPXE_PROFILE_H
+#define _GPXE_PROFILE_H
+
+/** @file
+ *
+ * Profiling
+ *
+ */
+
+#include <stdint.h>
+
+/**
+ * A data structure for storing profiling information
+ */
+union profiler {
+ /** Timestamp (in CPU-specific "ticks") */
+ uint64_t timestamp;
+ /** Registers returned by rdtsc.
+ *
+ * This part should really be architecture-specific code.
+ */
+ struct {
+ uint32_t eax;
+ uint32_t edx;
+ } rdtsc;
+};
+
+/**
+ * Static per-object profiler, for use with simple_profile()
+ */
+static union profiler simple_profiler;
+
+/**
+ * Perform profiling
+ *
+ * @v profiler Profiler data structure
+ * @ret delta Elapsed ticks since last call to profile().
+ *
+ * Call profile() both before and after the code you wish to measure.
+ * The "after" call will return the measurement. For example:
+ *
+ * @code
+ *
+ * profile ( &profiler );
+ * ... do something here ...
+ * printf ( "It took %ld ticks to execute\n", profile ( &profiler ) );
+ *
+ * @endcode
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+profile ( union profiler *profiler ) {
+ uint64_t last_timestamp = profiler->timestamp;
+
+ __asm__ __volatile__ ( "rdtsc" :
+ "=a" ( profiler->rdtsc.eax ),
+ "=d" ( profiler->rdtsc.edx ) );
+ return ( profiler->timestamp - last_timestamp );
+}
+
+/**
+ * Perform profiling
+ *
+ * @ret delta Elapsed ticks since last call to profile().
+ *
+ * When you only need one profiler, you can avoid the hassle of
+ * creating your own @c profiler data structure by using
+ * simple_profile() instead.
+ *
+ * simple_profile() is equivalent to profile(&simple_profiler), where
+ * @c simple_profiler is a @c profiler data structure that is static
+ * to each object which includes @c profile.h.
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+simple_profile ( void ) {
+ return profile ( &simple_profiler );
+}
+
+#endif /* _GPXE_PROFILE_H */
diff --git a/gpxe/src/include/gpxe/ramdisk.h b/gpxe/src/include/gpxe/ramdisk.h
new file mode 100644
index 00000000..4a77f05e
--- /dev/null
+++ b/gpxe/src/include/gpxe/ramdisk.h
@@ -0,0 +1,22 @@
+#ifndef _GPXE_RAMDISK_H
+#define _GPXE_RAMDISK_H
+
+/**
+ * @file
+ *
+ * RAM disks
+ *
+ */
+
+#include <gpxe/uaccess.h>
+#include <gpxe/blockdev.h>
+
+struct ramdisk {
+ struct block_device blockdev;
+ userptr_t data;
+};
+
+extern int init_ramdisk ( struct ramdisk *ramdisk, userptr_t data, size_t len,
+ unsigned int blksize );
+
+#endif /* _GPXE_RAMDISK_H */
diff --git a/gpxe/src/include/gpxe/rarp.h b/gpxe/src/include/gpxe/rarp.h
new file mode 100644
index 00000000..81e03bde
--- /dev/null
+++ b/gpxe/src/include/gpxe/rarp.h
@@ -0,0 +1,14 @@
+#ifndef _GPXE_RARP_H
+#define _GPXE_RARP_H
+
+/** @file
+ *
+ * Reverse Address Resolution Protocol
+ *
+ */
+
+struct net_protocol;
+
+extern struct net_protocol rarp_protocol;
+
+#endif /* _GPXE_RARP_H */
diff --git a/gpxe/src/include/gpxe/refcnt.h b/gpxe/src/include/gpxe/refcnt.h
new file mode 100644
index 00000000..68e0fd4b
--- /dev/null
+++ b/gpxe/src/include/gpxe/refcnt.h
@@ -0,0 +1,44 @@
+#ifndef _GPXE_REFCNT_H
+#define _GPXE_REFCNT_H
+
+/** @file
+ *
+ * Reference counting
+ *
+ */
+
+/**
+ * A reference counter
+ *
+ * This data structure is designed to be embedded within a
+ * reference-counted object.
+ *
+ * Reference-counted objects are freed when their reference count
+ * drops below zero. This means that a freshly allocated-and-zeroed
+ * reference-counted object will be freed on the first call to
+ * ref_put().
+ */
+struct refcnt {
+ /** Current reference count
+ *
+ * When this count is decremented below zero, the free()
+ * method will be called.
+ */
+ int refcnt;
+ /** Free containing object
+ *
+ * This method is called when the reference count is
+ * decremented below zero.
+ *
+ * If this method is left NULL, the standard library free()
+ * function will be called. The upshot of this is that you
+ * may omit the free() method if the @c refcnt object is the
+ * first element of your reference-counted struct.
+ */
+ void ( * free ) ( struct refcnt *refcnt );
+};
+
+extern struct refcnt * ref_get ( struct refcnt *refcnt );
+extern void ref_put ( struct refcnt *refcnt );
+
+#endif /* _GPXE_REFCNT_H */
diff --git a/gpxe/src/include/gpxe/resolv.h b/gpxe/src/include/gpxe/resolv.h
new file mode 100644
index 00000000..7c1ee6e2
--- /dev/null
+++ b/gpxe/src/include/gpxe/resolv.h
@@ -0,0 +1,166 @@
+#ifndef _GPXE_RESOLV_H
+#define _GPXE_RESOLV_H
+
+/** @file
+ *
+ * Name resolution
+ *
+ */
+
+#include <gpxe/refcnt.h>
+#include <gpxe/interface.h>
+#include <gpxe/tables.h>
+
+struct sockaddr;
+struct resolv_interface;
+
+/** Name resolution interface operations */
+struct resolv_interface_operations {
+ /** Name resolution completed
+ *
+ * @v resolv Name resolution interface
+ * @v sa Completed socket address (if successful)
+ * @v rc Final status code
+ */
+ void ( * done ) ( struct resolv_interface *resolv,
+ struct sockaddr *sa, int rc );
+};
+
+/** A name resolution interface */
+struct resolv_interface {
+ /** Generic object communication interface */
+ struct interface intf;
+ /** Operations for received messages */
+ struct resolv_interface_operations *op;
+};
+
+extern struct resolv_interface null_resolv;
+extern struct resolv_interface_operations null_resolv_ops;
+
+/**
+ * Initialise a name resolution interface
+ *
+ * @v resolv Name resolution interface
+ * @v op Name resolution interface operations
+ * @v refcnt Containing object reference counter, or NULL
+ */
+static inline void resolv_init ( struct resolv_interface *resolv,
+ struct resolv_interface_operations *op,
+ struct refcnt *refcnt ) {
+ resolv->intf.dest = &null_resolv.intf;
+ resolv->intf.refcnt = refcnt;
+ resolv->op = op;
+}
+
+/**
+ * Get name resolution interface from generic object communication interface
+ *
+ * @v intf Generic object communication interface
+ * @ret resolv Name resolution interface
+ */
+static inline __attribute__ (( always_inline )) struct resolv_interface *
+intf_to_resolv ( struct interface *intf ) {
+ return container_of ( intf, struct resolv_interface, intf );
+}
+
+/**
+ * Get reference to destination name resolution interface
+ *
+ * @v resolv Name resolution interface
+ * @ret dest Destination interface
+ */
+static inline __attribute__ (( always_inline )) struct resolv_interface *
+resolv_get_dest ( struct resolv_interface *resolv ) {
+ return intf_to_resolv ( intf_get ( resolv->intf.dest ) );
+}
+
+/**
+ * Drop reference to name resolution interface
+ *
+ * @v resolv name resolution interface
+ */
+static inline __attribute__ (( always_inline )) void
+resolv_put ( struct resolv_interface *resolv ) {
+ intf_put ( &resolv->intf );
+}
+
+/**
+ * Plug a name resolution interface into a new destination interface
+ *
+ * @v resolv Name resolution interface
+ * @v dest New destination interface
+ */
+static inline __attribute__ (( always_inline )) void
+resolv_plug ( struct resolv_interface *resolv, struct resolv_interface *dest ) {
+ plug ( &resolv->intf, &dest->intf );
+}
+
+/**
+ * Plug two name resolution interfaces together
+ *
+ * @v a Name resolution interface A
+ * @v b Name resolution interface B
+ */
+static inline __attribute__ (( always_inline )) void
+resolv_plug_plug ( struct resolv_interface *a, struct resolv_interface *b ) {
+ plug_plug ( &a->intf, &b->intf );
+}
+
+/**
+ * Unplug a name resolution interface
+ *
+ * @v resolv Name resolution interface
+ */
+static inline __attribute__ (( always_inline )) void
+resolv_unplug ( struct resolv_interface *resolv ) {
+ plug ( &resolv->intf, &null_resolv.intf );
+}
+
+/**
+ * Stop using a name resolution interface
+ *
+ * @v resolv Name resolution interface
+ *
+ * After calling this method, no further messages will be received via
+ * the interface.
+ */
+static inline void resolv_nullify ( struct resolv_interface *resolv ) {
+ resolv->op = &null_resolv_ops;
+};
+
+/** A name resolver */
+struct resolver {
+ /** Name of this resolver (e.g. "DNS") */
+ const char *name;
+ /** Start name resolution
+ *
+ * @v resolv Name resolution interface
+ * @v name Name to resolve
+ * @v sa Socket address to complete
+ * @ret rc Return status code
+ */
+ int ( * resolv ) ( struct resolv_interface *resolv, const char *name,
+ struct sockaddr *sa );
+};
+
+/** Numeric resolver priority */
+#define RESOLV_NUMERIC 01
+
+/** Normal resolver priority */
+#define RESOLV_NORMAL 02
+
+/** Register as a name resolver */
+#define __resolver( resolv_order ) \
+ __table ( struct resolver, resolvers, resolv_order )
+
+extern void resolv_done ( struct resolv_interface *resolv,
+ struct sockaddr *sa, int rc );
+extern void ignore_resolv_done ( struct resolv_interface *resolv,
+ struct sockaddr *sa, int rc );
+extern struct resolv_interface_operations null_resolv_ops;
+extern struct resolv_interface null_resolv;
+
+extern int resolv ( struct resolv_interface *resolv, const char *name,
+ struct sockaddr *sa );
+
+#endif /* _GPXE_RESOLV_H */
diff --git a/gpxe/src/include/gpxe/retry.h b/gpxe/src/include/gpxe/retry.h
new file mode 100644
index 00000000..e71e7b3b
--- /dev/null
+++ b/gpxe/src/include/gpxe/retry.h
@@ -0,0 +1,52 @@
+#ifndef _GPXE_RETRY_H
+#define _GPXE_RETRY_H
+
+/** @file
+ *
+ * Retry timers
+ *
+ */
+
+#include <gpxe/list.h>
+
+/** A retry timer */
+struct retry_timer {
+ /** List of active timers */
+ struct list_head list;
+ /** Timeout value (in ticks) */
+ unsigned long timeout;
+ /** Start time (in ticks)
+ *
+ * A start time of zero indicates a stopped timer.
+ */
+ unsigned long start;
+ /** Retry count */
+ unsigned int count;
+ /** Timer expired callback
+ *
+ * @v timer Retry timer
+ * @v fail Failure indicator
+ *
+ * The timer will already be stopped when this method is
+ * called. The failure indicator will be True if the retry
+ * timeout has already exceeded @c MAX_TIMEOUT.
+ */
+ void ( * expired ) ( struct retry_timer *timer, int over );
+};
+
+extern void start_timer ( struct retry_timer *timer );
+extern void start_timer_nodelay ( struct retry_timer *timer );
+extern void stop_timer ( struct retry_timer *timer );
+
+/**
+ * Test to see if timer is currently running
+ *
+ * @v timer Retry timer
+ * @ret running Non-zero if timer is running
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+timer_running ( struct retry_timer *timer ) {
+ return ( timer->start );
+}
+
+#endif /* _GPXE_RETRY_H */
diff --git a/gpxe/src/include/gpxe/rsa.h b/gpxe/src/include/gpxe/rsa.h
new file mode 100644
index 00000000..ce15cfa0
--- /dev/null
+++ b/gpxe/src/include/gpxe/rsa.h
@@ -0,0 +1,10 @@
+#ifndef _GPXE_RSA_H
+#define _GPXE_RSA_H
+
+struct crypto_algorithm;
+
+extern struct crypto_algorithm rsa_algorithm;
+
+#include "crypto/axtls/crypto.h"
+
+#endif /* _GPXE_RSA_H */
diff --git a/gpxe/src/include/gpxe/scsi.h b/gpxe/src/include/gpxe/scsi.h
new file mode 100644
index 00000000..e820117b
--- /dev/null
+++ b/gpxe/src/include/gpxe/scsi.h
@@ -0,0 +1,270 @@
+#ifndef _GPXE_SCSI_H
+#define _GPXE_SCSI_H
+
+#include <stdint.h>
+#include <gpxe/blockdev.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/refcnt.h>
+
+/** @file
+ *
+ * SCSI devices
+ *
+ */
+
+/**
+ * @defgroup scsiops SCSI operation codes
+ * @{
+ */
+
+#define SCSI_OPCODE_READ_10 0x28 /**< READ (10) */
+#define SCSI_OPCODE_READ_16 0x88 /**< READ (16) */
+#define SCSI_OPCODE_WRITE_10 0x2a /**< WRITE (10) */
+#define SCSI_OPCODE_WRITE_16 0x8a /**< WRITE (16) */
+#define SCSI_OPCODE_READ_CAPACITY_10 0x25 /**< READ CAPACITY (10) */
+#define SCSI_OPCODE_SERVICE_ACTION_IN 0x9e /**< SERVICE ACTION IN */
+#define SCSI_SERVICE_ACTION_READ_CAPACITY_16 0x10 /**< READ CAPACITY (16) */
+
+/** @} */
+
+/**
+ * @defgroup scsiflags SCSI flags
+ * @{
+ */
+
+#define SCSI_FL_FUA_NV 0x02 /**< Force unit access to NVS */
+#define SCSI_FL_FUA 0x08 /**< Force unit access */
+#define SCSI_FL_DPO 0x10 /**< Disable cache page out */
+
+/** @} */
+
+/**
+ * @defgroup scsicdbs SCSI command data blocks
+ * @{
+ */
+
+/** A SCSI "READ (10)" CDB */
+struct scsi_cdb_read_10 {
+ /** Opcode (0x28) */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Start address
+ *
+ * This is a logical block number, in big-endian order.
+ */
+ uint32_t lba;
+ /** Group number */
+ uint8_t group;
+ /** Transfer length
+ *
+ * This is a logical block count, in big-endian order.
+ */
+ uint16_t len;
+ /** Control byte */
+ uint8_t control;
+} __attribute__ (( packed ));
+
+/** A SCSI "READ (16)" CDB */
+struct scsi_cdb_read_16 {
+ /** Opcode (0x88) */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Start address
+ *
+ * This is a logical block number, in big-endian order.
+ */
+ uint64_t lba;
+ /** Transfer length
+ *
+ * This is a logical block count, in big-endian order.
+ */
+ uint32_t len;
+ /** Group number */
+ uint8_t group;
+ /** Control byte */
+ uint8_t control;
+} __attribute__ (( packed ));
+
+/** A SCSI "WRITE (10)" CDB */
+struct scsi_cdb_write_10 {
+ /** Opcode (0x2a) */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Start address
+ *
+ * This is a logical block number, in big-endian order.
+ */
+ uint32_t lba;
+ /** Group number */
+ uint8_t group;
+ /** Transfer length
+ *
+ * This is a logical block count, in big-endian order.
+ */
+ uint16_t len;
+ /** Control byte */
+ uint8_t control;
+} __attribute__ (( packed ));
+
+/** A SCSI "WRITE (16)" CDB */
+struct scsi_cdb_write_16 {
+ /** Opcode (0x8a) */
+ uint8_t opcode;
+ /** Flags */
+ uint8_t flags;
+ /** Start address
+ *
+ * This is a logical block number, in big-endian order.
+ */
+ uint64_t lba;
+ /** Transfer length
+ *
+ * This is a logical block count, in big-endian order.
+ */
+ uint32_t len;
+ /** Group number */
+ uint8_t group;
+ /** Control byte */
+ uint8_t control;
+} __attribute__ (( packed ));
+
+/** A SCSI "READ CAPACITY (10)" CDB */
+struct scsi_cdb_read_capacity_10 {
+ /** Opcode (0x25) */
+ uint8_t opcode;
+ /** Reserved */
+ uint8_t reserved_a;
+ /** Logical block address
+ *
+ * Applicable only if the PMI bit is set.
+ */
+ uint32_t lba;
+ /** Reserved */
+ uint8_t reserved_b[3];
+ /** Control byte */
+ uint8_t control;
+} __attribute__ (( packed ));
+
+/** SCSI "READ CAPACITY (10)" parameter data */
+struct scsi_capacity_10 {
+ /** Maximum logical block number */
+ uint32_t lba;
+ /** Block length in bytes */
+ uint32_t blksize;
+} __attribute__ (( packed ));
+
+/** A SCSI "READ CAPACITY (16)" CDB */
+struct scsi_cdb_read_capacity_16 {
+ /** Opcode (0x9e) */
+ uint8_t opcode;
+ /** Service action */
+ uint8_t service_action;
+ /** Logical block address
+ *
+ * Applicable only if the PMI bit is set.
+ */
+ uint64_t lba;
+ /** Transfer length
+ *
+ * This is the size of the data-in buffer, in bytes.
+ */
+ uint32_t len;
+ /** Reserved */
+ uint8_t reserved;
+ /** Control byte */
+ uint8_t control;
+} __attribute__ (( packed ));
+
+/** SCSI "READ CAPACITY (16)" parameter data */
+struct scsi_capacity_16 {
+ /** Maximum logical block number */
+ uint64_t lba;
+ /** Block length in bytes */
+ uint32_t blksize;
+ /** Reserved */
+ uint8_t reserved[20];
+} __attribute__ (( packed ));
+
+/** A SCSI Command Data Block */
+union scsi_cdb {
+ struct scsi_cdb_read_10 read10;
+ struct scsi_cdb_read_16 read16;
+ struct scsi_cdb_write_10 write10;
+ struct scsi_cdb_write_16 write16;
+ struct scsi_cdb_read_capacity_10 readcap10;
+ struct scsi_cdb_read_capacity_16 readcap16;
+ unsigned char bytes[16];
+};
+
+/** printf() format for dumping a scsi_cdb */
+#define SCSI_CDB_FORMAT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:" \
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
+
+/** printf() parameters for dumping a scsi_cdb */
+#define SCSI_CDB_DATA(cdb) \
+ (cdb).bytes[0], (cdb).bytes[1], (cdb).bytes[2], (cdb).bytes[3], \
+ (cdb).bytes[4], (cdb).bytes[5], (cdb).bytes[6], (cdb).bytes[7], \
+ (cdb).bytes[8], (cdb).bytes[9], (cdb).bytes[10], (cdb).bytes[11], \
+ (cdb).bytes[12], (cdb).bytes[13], (cdb).bytes[14], (cdb).bytes[15]
+
+/** @} */
+
+/** A SCSI command */
+struct scsi_command {
+ /** CDB for this command */
+ union scsi_cdb cdb;
+ /** Data-out buffer (may be NULL) */
+ userptr_t data_out;
+ /** Data-out buffer length
+ *
+ * Must be zero if @c data_out is NULL
+ */
+ size_t data_out_len;
+ /** Data-in buffer (may be NULL) */
+ userptr_t data_in;
+ /** Data-in buffer length
+ *
+ * Must be zero if @c data_in is NULL
+ */
+ size_t data_in_len;
+ /** SCSI status code */
+ uint8_t status;
+ /** SCSI sense response code */
+ uint8_t sense_response;
+};
+
+/** A SCSI device */
+struct scsi_device {
+ /** Block device interface */
+ struct block_device blockdev;
+ /** Logical unit number (LUN)
+ *
+ * This is a four-level LUN as specified by SAM-2, in
+ * big-endian order.
+ */
+ uint64_t lun;
+ /**
+ * Issue SCSI command
+ *
+ * @v scsi SCSI device
+ * @v command SCSI command
+ * @ret rc Return status code
+ *
+ * Note that a successful return status code indicates only
+ * that the SCSI command completed. The caller must check the
+ * status field in the command structure to see if, for
+ * example, the device returned CHECK CONDITION or some other
+ * non-success status code.
+ */
+ int ( * command ) ( struct scsi_device *scsi,
+ struct scsi_command *command );
+ /** Backing device */
+ struct refcnt *backend;
+};
+
+extern int init_scsidev ( struct scsi_device *scsi );
+
+#endif /* _GPXE_SCSI_H */
diff --git a/gpxe/src/include/gpxe/segment.h b/gpxe/src/include/gpxe/segment.h
new file mode 100644
index 00000000..5ab91699
--- /dev/null
+++ b/gpxe/src/include/gpxe/segment.h
@@ -0,0 +1,15 @@
+#ifndef _GPXE_SEGMENT_H
+#define _GPXE_SEGMENT_H
+
+/**
+ * @file
+ *
+ * Executable image segments
+ *
+ */
+
+#include <gpxe/uaccess.h>
+
+extern int prep_segment ( userptr_t segment, size_t filesz, size_t memsz );
+
+#endif /* _GPXE_SEGMENT_H */
diff --git a/gpxe/src/include/gpxe/settings.h b/gpxe/src/include/gpxe/settings.h
new file mode 100644
index 00000000..40825698
--- /dev/null
+++ b/gpxe/src/include/gpxe/settings.h
@@ -0,0 +1,285 @@
+#ifndef _GPXE_SETTINGS_H
+#define _GPXE_SETTINGS_H
+
+/** @file
+ *
+ * Configuration settings
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/tables.h>
+#include <gpxe/list.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/dhcpopts.h>
+
+struct settings;
+struct in_addr;
+
+/** A setting */
+struct setting {
+ /** Name
+ *
+ * This is the human-readable name for the setting.
+ */
+ const char *name;
+ /** Description */
+ const char *description;
+ /** Setting type
+ *
+ * This identifies the type of setting (e.g. string, IPv4
+ * address, etc.).
+ */
+ struct setting_type *type;
+ /** DHCP option number, if applicable */
+ unsigned int tag;
+};
+
+/** Declare a configuration setting */
+#define __setting __table ( struct setting, settings, 01 )
+
+/** Settings block operations */
+struct settings_operations {
+ /** Store value of setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+ int ( * store ) ( struct settings *settings, struct setting *setting,
+ const void *data, size_t len );
+ /** Fetch value of setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ *
+ * The actual length of the setting will be returned even if
+ * the buffer was too small.
+ */
+ int ( * fetch ) ( struct settings *settings, struct setting *setting,
+ void *data, size_t len );
+};
+
+/** A settings block */
+struct settings {
+ /** Reference counter */
+ struct refcnt *refcnt;
+ /** Name */
+ const char *name;
+ /** Parent settings block */
+ struct settings *parent;
+ /** Sibling settings blocks */
+ struct list_head siblings;
+ /** Child settings blocks */
+ struct list_head children;
+ /** Settings block operations */
+ struct settings_operations *op;
+};
+
+/**
+ * A setting type
+ *
+ * This represents a type of setting (e.g. string, IPv4 address,
+ * etc.).
+ */
+struct setting_type {
+ /** Name
+ *
+ * This is the name exposed to the user (e.g. "string").
+ */
+ const char *name;
+ /** Parse and set value of setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v value Formatted setting data
+ * @ret rc Return status code
+ */
+ int ( * storef ) ( struct settings *settings, struct setting *setting,
+ const char *value );
+ /** Fetch and format value of setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to fetch
+ * @v buf Buffer to contain formatted value
+ * @v len Length of buffer
+ * @ret len Length of formatted value, or negative error
+ */
+ int ( * fetchf ) ( struct settings *settings, struct setting *setting,
+ char *buf, size_t len );
+};
+
+/** Declare a configuration setting type */
+#define __setting_type \
+ __table ( struct setting_type, setting_types, 01 )
+
+/**
+ * A settings applicator
+ *
+ */
+struct settings_applicator {
+ /** Apply updated settings
+ *
+ * @ret rc Return status code
+ */
+ int ( * apply ) ( void );
+};
+
+/** Declare a settings applicator */
+#define __settings_applicator \
+ __table ( struct settings_applicator, settings_applicators, 01 )
+
+/**
+ * A simple settings block
+ *
+ */
+struct simple_settings {
+ /** Settings block */
+ struct settings settings;
+ /** DHCP options */
+ struct dhcp_options dhcpopts;
+};
+
+extern struct settings_operations simple_settings_operations;
+extern int simple_settings_store ( struct settings *settings,
+ struct setting *setting,
+ const void *data, size_t len );
+extern int simple_settings_fetch ( struct settings *settings,
+ struct setting *setting,
+ void *data, size_t len );
+
+extern int register_settings ( struct settings *settings,
+ struct settings *parent );
+extern void unregister_settings ( struct settings *settings );
+
+extern int store_setting ( struct settings *settings, struct setting *setting,
+ const void *data, size_t len );
+extern int fetch_setting ( struct settings *settings, struct setting *setting,
+ void *data, size_t len );
+extern int fetch_setting_len ( struct settings *settings,
+ struct setting *setting );
+extern int fetch_string_setting ( struct settings *settings,
+ struct setting *setting,
+ char *data, size_t len );
+extern int fetch_ipv4_setting ( struct settings *settings,
+ struct setting *setting, struct in_addr *inp );
+extern int fetch_int_setting ( struct settings *settings,
+ struct setting *setting, long *value );
+extern int fetch_uint_setting ( struct settings *settings,
+ struct setting *setting,
+ unsigned long *value );
+extern long fetch_intz_setting ( struct settings *settings,
+ struct setting *setting );
+extern unsigned long fetch_uintz_setting ( struct settings *settings,
+ struct setting *setting );
+extern int setting_cmp ( struct setting *a, struct setting *b );
+
+extern struct settings * find_child_settings ( struct settings *parent,
+ const char *name );
+extern struct settings * find_settings ( const char *name );
+
+extern int storef_setting ( struct settings *settings,
+ struct setting *setting,
+ const char *value );
+extern int storef_named_setting ( const char *name, const char *value );
+extern int fetchf_named_setting ( const char *name, char *buf, size_t len );
+
+extern struct setting_type setting_type_string __setting_type;
+extern struct setting_type setting_type_ipv4 __setting_type;
+extern struct setting_type setting_type_int8 __setting_type;
+extern struct setting_type setting_type_int16 __setting_type;
+extern struct setting_type setting_type_int32 __setting_type;
+extern struct setting_type setting_type_uint8 __setting_type;
+extern struct setting_type setting_type_uint16 __setting_type;
+extern struct setting_type setting_type_uint32 __setting_type;
+extern struct setting_type setting_type_hex __setting_type;
+
+extern struct setting ip_setting __setting;
+extern struct setting netmask_setting __setting;
+extern struct setting gateway_setting __setting;
+extern struct setting dns_setting __setting;
+extern struct setting hostname_setting __setting;
+extern struct setting filename_setting __setting;
+extern struct setting root_path_setting __setting;
+extern struct setting username_setting __setting;
+extern struct setting password_setting __setting;
+extern struct setting priority_setting __setting;
+extern struct setting bios_drive_setting __setting;
+
+/**
+ * Initialise a settings block
+ *
+ * @v settings Settings block
+ * @v op Settings block operations
+ * @v refcnt Containing object reference counter, or NULL
+ * @v name Settings block name
+ */
+static inline void settings_init ( struct settings *settings,
+ struct settings_operations *op,
+ struct refcnt *refcnt,
+ const char *name ) {
+ INIT_LIST_HEAD ( &settings->siblings );
+ INIT_LIST_HEAD ( &settings->children );
+ settings->op = op;
+ settings->refcnt = refcnt;
+ settings->name = name;
+}
+
+/**
+ * Initialise a settings block
+ *
+ * @v simple Simple settings block
+ * @v refcnt Containing object reference counter, or NULL
+ * @v name Settings block name
+ */
+static inline void simple_settings_init ( struct simple_settings *simple,
+ struct refcnt *refcnt,
+ const char *name ) {
+ settings_init ( &simple->settings, &simple_settings_operations,
+ refcnt, name );
+}
+
+/**
+ * Delete setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to delete
+ * @ret rc Return status code
+ */
+static inline int delete_setting ( struct settings *settings,
+ struct setting *setting ) {
+ return store_setting ( settings, setting, NULL, 0 );
+}
+
+/**
+ * Fetch and format value of setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v type Settings type
+ * @v buf Buffer to contain formatted value
+ * @v len Length of buffer
+ * @ret len Length of formatted value, or negative error
+ */
+static inline int fetchf_setting ( struct settings *settings,
+ struct setting *setting,
+ char *buf, size_t len ) {
+ return setting->type->fetchf ( settings, setting, buf, len );
+}
+
+/**
+ * Delete named setting
+ *
+ * @v name Name of setting
+ * @ret rc Return status code
+ */
+static inline int delete_named_setting ( const char *name ) {
+ return storef_named_setting ( name, NULL );
+}
+
+#endif /* _GPXE_SETTINGS_H */
diff --git a/gpxe/src/include/gpxe/settings_ui.h b/gpxe/src/include/gpxe/settings_ui.h
new file mode 100644
index 00000000..48548fd5
--- /dev/null
+++ b/gpxe/src/include/gpxe/settings_ui.h
@@ -0,0 +1,14 @@
+#ifndef _GPXE_SETTINGS_UI_H
+#define _GPXE_SETTINGS_UI_H
+
+/** @file
+ *
+ * Option configuration console
+ *
+ */
+
+struct settings;
+
+extern int settings_ui ( struct settings *settings ) __nonnull;
+
+#endif /* _GPXE_SETTINGS_UI_H */
diff --git a/gpxe/src/include/gpxe/sha1.h b/gpxe/src/include/gpxe/sha1.h
new file mode 100644
index 00000000..2d6e90dd
--- /dev/null
+++ b/gpxe/src/include/gpxe/sha1.h
@@ -0,0 +1,13 @@
+#ifndef _GPXE_SHA1_H
+#define _GPXE_SHA1_H
+
+#include "crypto/axtls/crypto.h"
+
+struct crypto_algorithm;
+
+#define SHA1_CTX_SIZE sizeof ( SHA1_CTX )
+#define SHA1_DIGEST_SIZE SHA1_SIZE
+
+extern struct crypto_algorithm sha1_algorithm;
+
+#endif /* _GPXE_SHA1_H */
diff --git a/gpxe/src/include/gpxe/shell.h b/gpxe/src/include/gpxe/shell.h
new file mode 100644
index 00000000..c353fc4d
--- /dev/null
+++ b/gpxe/src/include/gpxe/shell.h
@@ -0,0 +1,12 @@
+#ifndef _GPXE_SHELL_H
+#define _GPXE_SHELL_H
+
+/** @file
+ *
+ * Minimal command shell
+ *
+ */
+
+extern void shell ( void );
+
+#endif /* _GPXE_SHELL_H */
diff --git a/gpxe/src/include/gpxe/shell_banner.h b/gpxe/src/include/gpxe/shell_banner.h
new file mode 100644
index 00000000..f8e92a4d
--- /dev/null
+++ b/gpxe/src/include/gpxe/shell_banner.h
@@ -0,0 +1,12 @@
+#ifndef _GPXE_SHELL_BANNER_H
+#define _GPXE_SHELL_BANNER_H
+
+/** @file
+ *
+ * Shell startup banner
+ *
+ */
+
+extern int shell_banner ( void );
+
+#endif /* _GPXE_SHELL_BANNER_H */
diff --git a/gpxe/src/include/gpxe/socket.h b/gpxe/src/include/gpxe/socket.h
new file mode 100644
index 00000000..9c7afb87
--- /dev/null
+++ b/gpxe/src/include/gpxe/socket.h
@@ -0,0 +1,95 @@
+#ifndef _GPXE_SOCKET_H
+#define _GPXE_SOCKET_H
+
+/** @file
+ *
+ * Socket addresses
+ *
+ */
+
+/**
+ * @defgroup commtypes Communication semantics
+ *
+ * @{
+ */
+
+/** Connection-based, reliable streams */
+#define SOCK_STREAM ( ( int ) TCP_SOCK_STREAM )
+extern char TCP_SOCK_STREAM[];
+
+/** Connectionless, unreliable streams */
+#define SOCK_DGRAM ( ( int ) UDP_SOCK_DGRAM )
+extern char UDP_SOCK_DGRAM[];
+
+/** @} */
+
+/**
+ * Name communication semantics
+ *
+ * @v semantics Communication semantics (e.g. SOCK_STREAM)
+ * @ret name Name of communication semantics
+ */
+static inline __attribute__ (( always_inline )) const char *
+socket_semantics_name ( int semantics ) {
+ /* Cannot use a switch() because of the {TCP_UDP}_SOCK_XXX hack */
+ if ( semantics == SOCK_STREAM ) {
+ return "SOCK_STREAM";
+ } else if ( semantics == SOCK_DGRAM ) {
+ return "SOCK_DGRAM";
+ } else {
+ return "SOCK_UNKNOWN";
+ }
+}
+
+/**
+ * @defgroup addrfam Address families
+ *
+ * @{
+ */
+#define AF_INET 1 /**< IPv4 Internet addresses */
+#define AF_INET6 2 /**< IPv6 Internet addresses */
+/** @} */
+
+/**
+ * Name address family
+ *
+ * @v family Address family (e.g. AF_INET)
+ * @ret name Name of address family
+ */
+static inline __attribute__ (( always_inline )) const char *
+socket_family_name ( int family ) {
+ switch ( family ) {
+ case AF_INET: return "AF_INET";
+ case AF_INET6: return "AF_INET6";
+ default: return "AF_UNKNOWN";
+ }
+}
+
+/** A socket address family */
+typedef uint16_t sa_family_t;
+
+/** Length of a @c struct @c sockaddr */
+#define SA_LEN 32
+
+/**
+ * Generalized socket address structure
+ *
+ * This contains the fields common to socket addresses for all address
+ * families.
+ */
+struct sockaddr {
+ /** Socket address family
+ *
+ * This is an AF_XXX constant.
+ */
+ sa_family_t sa_family;
+ /** Padding
+ *
+ * This ensures that a struct @c sockaddr_tcpip is large
+ * enough to hold a socket address for any TCP/IP address
+ * family.
+ */
+ char pad[ SA_LEN - sizeof ( sa_family_t ) ];
+};
+
+#endif /* _GPXE_SOCKET_H */
diff --git a/gpxe/src/include/gpxe/spi.h b/gpxe/src/include/gpxe/spi.h
new file mode 100644
index 00000000..fe38cb6b
--- /dev/null
+++ b/gpxe/src/include/gpxe/spi.h
@@ -0,0 +1,239 @@
+#ifndef _GPXE_SPI_H
+#define _GPXE_SPI_H
+
+/** @file
+ *
+ * SPI interface
+ *
+ */
+
+#include <gpxe/nvs.h>
+
+/**
+ * @defgroup spicmds SPI commands
+ * @{
+ */
+
+/** Write status register */
+#define SPI_WRSR 0x01
+
+/** Write data to memory array */
+#define SPI_WRITE 0x02
+
+/** Read data from memory array */
+#define SPI_READ 0x03
+
+/** Reset write enable latch */
+#define SPI_WRDI 0x04
+
+/** Read status register */
+#define SPI_RDSR 0x05
+
+/** Set write enable latch */
+#define SPI_WREN 0x06
+
+/**
+ * @defgroup atmelcmds Atmel-specific SPI commands
+ * @{
+ */
+
+/** Erase one sector in memory array (Not supported on all devices) */
+#define ATMEL_SECTOR_ERASE 0x52
+
+/** Erase all sections in memory array (Not supported on all devices) */
+#define ATMEL_CHIP_ERASE 0x62
+
+/** Read manufacturer and product ID (Not supported on all devices) */
+#define ATMEL_RDID 0x15
+
+/** @} */
+
+/** @} */
+
+/**
+ * @defgroup spistatus SPI status register bits (not present on all devices)
+ * @{
+ */
+
+/** Write-protect pin enabled */
+#define SPI_STATUS_WPEN 0x80
+
+/** Block protection bit 2 */
+#define SPI_STATUS_BP2 0x10
+
+/** Block protection bit 1 */
+#define SPI_STATUS_BP1 0x08
+
+/** Block protection bit 0 */
+#define SPI_STATUS_BP0 0x04
+
+/** State of the write enable latch */
+#define SPI_STATUS_WEN 0x02
+
+/** Device busy flag */
+#define SPI_STATUS_NRDY 0x01
+
+/** @} */
+
+/**
+ * An SPI device
+ *
+ * This data structure represents a physical SPI device attached to an
+ * SPI bus.
+ */
+struct spi_device {
+ /** NVS device */
+ struct nvs_device nvs;
+ /** SPI bus to which device is attached */
+ struct spi_bus *bus;
+ /** Slave number */
+ unsigned int slave;
+ /** Command length, in bits */
+ unsigned int command_len;
+ /** Address length, in bits */
+ unsigned int address_len;
+ /** Address is munged
+ *
+ * Some devices with 9-bit addresses (e.g. AT25040A EEPROM)
+ * use bit 3 of the command byte as address bit A8, rather
+ * than having a two-byte address. If this flag is set, then
+ * commands should be munged in this way.
+ */
+ unsigned int munge_address : 1;
+};
+
+static inline __attribute__ (( always_inline )) struct spi_device *
+nvs_to_spi ( struct nvs_device *nvs ) {
+ return container_of ( nvs, struct spi_device, nvs );
+}
+
+/**
+ * An SPI bus
+ *
+ * This data structure represents an SPI bus controller capable of
+ * issuing commands to attached SPI devices.
+ */
+struct spi_bus {
+ /** SPI interface mode
+ *
+ * This is the bitwise OR of zero or more of @c SPI_MODE_CPHA
+ * and @c SPI_MODE_CPOL. It is also the number conventionally
+ * used to describe the SPI interface mode. For example, SPI
+ * mode 1 is the mode in which CPOL=0 and CPHA=1, which
+ * therefore corresponds to a mode value of (0|SPI_MODE_CPHA)
+ * which, happily, equals 1.
+ */
+ unsigned int mode;
+ /**
+ * Read/write data via SPI bus
+ *
+ * @v bus SPI bus
+ * @v device SPI device
+ * @v command Command
+ * @v address Address to read/write (<0 for no address)
+ * @v data_out TX data buffer (or NULL)
+ * @v data_in RX data buffer (or NULL)
+ * @v len Length of data buffer(s)
+ *
+ * This issues the specified command and optional address to
+ * the SPI device, then reads and/or writes data to/from the
+ * data buffers.
+ */
+ int ( * rw ) ( struct spi_bus *bus, struct spi_device *device,
+ unsigned int command, int address,
+ const void *data_out, void *data_in, size_t len );
+};
+
+/** Clock phase (CPHA) mode bit
+ *
+ * Phase 0 is sample on rising edge, shift data on falling edge.
+ *
+ * Phase 1 is shift data on rising edge, sample data on falling edge.
+ */
+#define SPI_MODE_CPHA 0x01
+
+/** Clock polarity (CPOL) mode bit
+ *
+ * This bit reflects the idle state of the clock line (SCLK).
+ */
+#define SPI_MODE_CPOL 0x02
+
+/** Slave select polarity mode bit
+ *
+ * This bit reflects that active state of the slave select lines. It
+ * is not part of the normal SPI mode number (which covers only @c
+ * SPI_MODE_CPOL and @c SPI_MODE_CPHA), but is included here for
+ * convenience.
+ */
+#define SPI_MODE_SSPOL 0x10
+
+/** Microwire-compatible mode
+ *
+ * This is SPI mode 1 (i.e. CPOL=0, CPHA=1), and is compatible with
+ * the original Microwire protocol.
+ */
+#define SPI_MODE_MICROWIRE 1
+
+/** Microwire/Plus-compatible mode
+ *
+ * This is SPI mode 0 (i.e. CPOL=0, CPHA=0), and is compatible with
+ * the Microwire/Plus protocol
+ */
+#define SPI_MODE_MICROWIRE_PLUS 0
+
+/** Threewire-compatible mode
+ *
+ * This mode is compatible with Atmel's series of "three-wire"
+ * interfaces.
+ */
+#define SPI_MODE_THREEWIRE ( SPI_MODE_MICROWIRE_PLUS | SPI_MODE_SSPOL )
+
+extern int spi_read ( struct nvs_device *nvs, unsigned int address,
+ void *data, size_t len );
+extern int spi_write ( struct nvs_device *nvs, unsigned int address,
+ const void *data, size_t len );
+
+/**
+ * @defgroup spidevs SPI device types
+ * @{
+ */
+
+static inline __attribute__ (( always_inline )) void
+init_spi ( struct spi_device *device ) {
+ device->nvs.word_len_log2 = 0;
+ device->command_len = 8,
+ device->nvs.read = spi_read;
+ device->nvs.write = spi_write;
+}
+
+/** Atmel AT25F1024 serial flash */
+static inline __attribute__ (( always_inline )) void
+init_at25f1024 ( struct spi_device *device ) {
+ device->address_len = 24;
+ device->nvs.size = ( 128 * 1024 );
+ device->nvs.block_size = 256;
+ init_spi ( device );
+}
+
+/** Atmel 25040 serial EEPROM */
+static inline __attribute__ (( always_inline )) void
+init_at25040 ( struct spi_device *device ) {
+ device->address_len = 8;
+ device->munge_address = 1;
+ device->nvs.size = 512;
+ device->nvs.block_size = 8;
+ init_spi ( device );
+}
+
+/** Microchip 25XX640 serial EEPROM */
+static inline __attribute__ (( always_inline )) void
+init_mc25xx640 ( struct spi_device *device ) {
+ device->address_len = 16;
+ device->nvs.size = ( 8 * 1024 );
+ device->nvs.block_size = 32;
+ init_spi ( device );
+}
+
+/** @} */
+
+#endif /* _GPXE_SPI_H */
diff --git a/gpxe/src/include/gpxe/spi_bit.h b/gpxe/src/include/gpxe/spi_bit.h
new file mode 100644
index 00000000..ced85ceb
--- /dev/null
+++ b/gpxe/src/include/gpxe/spi_bit.h
@@ -0,0 +1,61 @@
+#ifndef _GPXE_SPI_BIT_H
+#define _GPXE_SPI_BIT_H
+
+/** @file
+ *
+ * SPI bit-bashing interface
+ *
+ */
+
+#include <gpxe/spi.h>
+#include <gpxe/bitbash.h>
+
+/** A bit-bashing SPI bus */
+struct spi_bit_basher {
+ /** SPI bus */
+ struct spi_bus bus;
+ /** Bit-bashing interface */
+ struct bit_basher basher;
+ /** Endianness of data
+ *
+ * SPI commands and addresses are always big-endian (i.e. MSB
+ * transmitted first on the wire), but some cards
+ * (e.g. natsemi) choose to regard the data stored in the
+ * EEPROM as little-endian (i.e. LSB transmitted first on the
+ * wire).
+ */
+ int endianness;
+};
+
+/** Bit indices used for SPI bit-bashing interface */
+enum {
+ /** Serial clock */
+ SPI_BIT_SCLK = 0,
+ /** Master Out Slave In */
+ SPI_BIT_MOSI,
+ /** Master In Slave Out */
+ SPI_BIT_MISO,
+ /** Slave 0 select */
+ SPI_BIT_SS0,
+};
+
+/**
+ * Determine bit index for a particular slave
+ *
+ * @v slave Slave number
+ * @ret index Bit index (i.e. SPI_BIT_SSN, where N=slave)
+ */
+#define SPI_BIT_SS( slave ) ( SPI_BIT_SS0 + (slave) )
+
+/** Delay between SCLK transitions */
+#define SPI_BIT_UDELAY 1
+
+/** SPI bit basher treats data as big-endian */
+#define SPI_BIT_BIG_ENDIAN 0
+
+/** SPI bit basher treats data as little-endian */
+#define SPI_BIT_LITTLE_ENDIAN 1
+
+extern void init_spi_bit_basher ( struct spi_bit_basher *spibit );
+
+#endif /* _GPXE_SPI_BIT_H */
diff --git a/gpxe/src/include/gpxe/tables.h b/gpxe/src/include/gpxe/tables.h
new file mode 100644
index 00000000..b2c56ab6
--- /dev/null
+++ b/gpxe/src/include/gpxe/tables.h
@@ -0,0 +1,229 @@
+#ifndef _GPXE_TABLES_H
+#define _GPXE_TABLES_H
+
+/** @page ifdef_harmful #ifdef considered harmful
+ *
+ * Overuse of @c #ifdef has long been a problem in Etherboot.
+ * Etherboot provides a rich array of features, but all these features
+ * take up valuable space in a ROM image. The traditional solution to
+ * this problem has been for each feature to have its own @c #ifdef
+ * option, allowing the feature to be compiled in only if desired.
+ *
+ * The problem with this is that it becomes impossible to compile, let
+ * alone test, all possible versions of Etherboot. Code that is not
+ * typically used tends to suffer from bit-rot over time. It becomes
+ * extremely difficult to predict which combinations of compile-time
+ * options will result in code that can even compile and link
+ * correctly.
+ *
+ * To solve this problem, we have adopted a new approach from
+ * Etherboot 5.5 onwards. @c #ifdef is now "considered harmful", and
+ * its use should be minimised. Separate features should be
+ * implemented in separate @c .c files, and should \b always be
+ * compiled (i.e. they should \b not be guarded with a @c #ifdef @c
+ * MY_PET_FEATURE statement). By making (almost) all code always
+ * compile, we avoid the problem of bit-rot in rarely-used code.
+ *
+ * The file config.h, in combination with the @c make command line,
+ * specifies the objects that will be included in any particular build
+ * of Etherboot. For example, suppose that config.h includes the line
+ *
+ * @code
+ *
+ * #define CONSOLE_SERIAL
+ * #define DOWNLOAD_PROTO_TFTP
+ *
+ * @endcode
+ *
+ * When a particular Etherboot image (e.g. @c bin/rtl8139.zdsk) is
+ * built, the options specified in config.h are used to drag in the
+ * relevant objects at link-time. For the above example, serial.o and
+ * tftp.o would be linked in.
+ *
+ * There remains one problem to solve: how do these objects get used?
+ * Traditionally, we had code such as
+ *
+ * @code
+ *
+ * #ifdef CONSOLE_SERIAL
+ * serial_init();
+ * #endif
+ *
+ * @endcode
+ *
+ * in main.c, but this reintroduces @c #ifdef and so is a Bad Idea.
+ * We cannot simply remove the @c #ifdef and make it
+ *
+ * @code
+ *
+ * serial_init();
+ *
+ * @endcode
+ *
+ * because then serial.o would end up always being linked in.
+ *
+ * The solution is to use @link tables.h linker tables @endlink.
+ *
+ */
+
+/** @file
+ *
+ * Linker tables
+ *
+ * Read @ref ifdef_harmful first for some background on the motivation
+ * for using linker tables.
+ *
+ * This file provides macros for dealing with linker-generated tables
+ * of fixed-size symbols. We make fairly extensive use of these in
+ * order to avoid @c #ifdef spaghetti and/or linker symbol pollution.
+ * For example, instead of having code such as
+ *
+ * @code
+ *
+ * #ifdef CONSOLE_SERIAL
+ * serial_init();
+ * #endif
+ *
+ * @endcode
+ *
+ * we make serial.c generate an entry in the initialisation function
+ * table, and then have a function call_init_fns() that simply calls
+ * all functions present in this table. If and only if serial.o gets
+ * linked in, then its initialisation function will be called. We
+ * avoid linker symbol pollution (i.e. always dragging in serial.o
+ * just because of a call to serial_init()) and we also avoid @c
+ * #ifdef spaghetti (having to conditionalise every reference to
+ * functions in serial.c).
+ *
+ * The linker script takes care of assembling the tables for us. All
+ * our table sections have names of the format @c .tbl.NAME.NN where
+ * @c NAME designates the data structure stored in the table (e.g. @c
+ * init_fn) and @c NN is a two-digit decimal number used to impose an
+ * ordering upon the tables if required. @c NN=00 is reserved for the
+ * symbol indicating "table start", and @c NN=99 is reserved for the
+ * symbol indicating "table end".
+ *
+ * As an example, suppose that we want to create a "frobnicator"
+ * feature framework, and allow for several independent modules to
+ * provide frobnicating services. Then we would create a frob.h
+ * header file containing e.g.
+ *
+ * @code
+ *
+ * struct frobnicator {
+ * const char *name; // Name of the frobnicator
+ * void ( *frob ) ( void ); // The frobnicating function itself
+ * };
+ *
+ * #define __frobnicator __table ( frobnicators, 01 )
+ *
+ * @endcode
+ *
+ * Any module providing frobnicating services would look something
+ * like
+ *
+ * @code
+ *
+ * #include "frob.h"
+ *
+ * static void my_frob ( void ) {
+ * // Do my frobnicating
+ * ...
+ * }
+ *
+ * struct frob my_frobnicator __frobnicator = {
+ * .name = "my_frob",
+ * .frob = my_frob,
+ * };
+ *
+ * @endcode
+ *
+ * The central frobnicator code (frob.c) would use the frobnicating
+ * modules as follows
+ *
+ * @code
+ *
+ * #include "frob.h"
+ *
+ * static struct frob frob_start[0] __table_start ( frobnicators );
+ * static struct frob frob_end[0] __table_end ( frobnicators );
+ *
+ * // Call all linked-in frobnicators
+ * void frob_all ( void ) {
+ * struct frob *frob;
+ *
+ * for ( frob = frob_start ; frob < frob_end ; frob++ ) {
+ * printf ( "Calling frobnicator \"%s\"\n", frob->name );
+ * frob->frob ();
+ * }
+ * }
+ *
+ * @endcode
+ *
+ * See init.h and init.c for a real-life example.
+ *
+ */
+
+#ifdef DOXYGEN
+#define __attribute__( x )
+#endif
+
+#define __table_str( x ) #x
+#define __table_section( table, idx ) \
+ __section__ ( ".tbl." __table_str ( table ) "." __table_str ( idx ) )
+
+#define __table_section_start( table ) __table_section ( table, 00 )
+#define __table_section_end( table ) __table_section ( table, 99 )
+
+#define __natural_alignment( type ) __aligned__ ( __alignof__ ( type ) )
+
+/**
+ * Linker table entry.
+ *
+ * Declares a data structure to be part of a linker table. Use as
+ * e.g.
+ *
+ * @code
+ *
+ * struct my_foo __table ( foo, 01 ) = {
+ * ...
+ * };
+ *
+ * @endcode
+ *
+ */
+#define __table( type, table, idx ) \
+ __attribute__ (( __table_section ( table, idx ), \
+ __natural_alignment ( type ) ))
+
+/**
+ * Linker table start marker.
+ *
+ * Declares a data structure (usually an empty data structure) to be
+ * the start of a linker table. Use as e.g.
+ *
+ * @code
+ *
+ * static struct foo_start[0] __table_start ( foo );
+ *
+ * @endcode
+ *
+ */
+#define __table_start( type, table ) __table ( type, table, 00 )
+
+/**
+ * Linker table end marker.
+ *
+ * Declares a data structure (usually an empty data structure) to be
+ * the end of a linker table. Use as e.g.
+ *
+ * @code
+ *
+ * static struct foo_end[0] __table_end ( foo );
+ *
+ * @endcode
+ *
+ */
+#define __table_end( type, table ) __table ( type, table, 99 )
+
+#endif /* _GPXE_TABLES_H */
diff --git a/gpxe/src/include/gpxe/tcp.h b/gpxe/src/include/gpxe/tcp.h
new file mode 100644
index 00000000..264ec29b
--- /dev/null
+++ b/gpxe/src/include/gpxe/tcp.h
@@ -0,0 +1,306 @@
+#ifndef _GPXE_TCP_H
+#define _GPXE_TCP_H
+
+/** @file
+ *
+ * TCP protocol
+ *
+ * This file defines the gPXE TCP API.
+ *
+ */
+
+#include <gpxe/tcpip.h>
+
+/**
+ * A TCP header
+ */
+struct tcp_header {
+ uint16_t src; /* Source port */
+ uint16_t dest; /* Destination port */
+ uint32_t seq; /* Sequence number */
+ uint32_t ack; /* Acknowledgement number */
+ uint8_t hlen; /* Header length (4), Reserved (4) */
+ uint8_t flags; /* Reserved (2), Flags (6) */
+ uint16_t win; /* Advertised window */
+ uint16_t csum; /* Checksum */
+ uint16_t urg; /* Urgent pointer */
+};
+
+/** @defgroup tcpopts TCP options
+ * @{
+ */
+
+/** End of TCP options list */
+#define TCP_OPTION_END 0
+
+/** TCP option pad */
+#define TCP_OPTION_NOP 1
+
+/** Generic TCP option */
+struct tcp_option {
+ uint8_t kind;
+ uint8_t length;
+} __attribute__ (( packed ));
+
+/** TCP MSS option */
+struct tcp_mss_option {
+ uint8_t kind;
+ uint8_t length;
+ uint16_t mss;
+} __attribute__ (( packed ));
+
+/** Code for the TCP MSS option */
+#define TCP_OPTION_MSS 2
+
+/** TCP timestamp option */
+struct tcp_timestamp_option {
+ uint8_t kind;
+ uint8_t length;
+ uint32_t tsval;
+ uint32_t tsecr;
+} __attribute__ (( packed ));
+
+/** Padded TCP timestamp option (used for sending) */
+struct tcp_timestamp_padded_option {
+ uint8_t nop[2];
+ struct tcp_timestamp_option tsopt;
+} __attribute__ (( packed ));
+
+/** Code for the TCP timestamp option */
+#define TCP_OPTION_TS 8
+
+/** Parsed TCP options */
+struct tcp_options {
+ /** MSS option, if present */
+ const struct tcp_mss_option *mssopt;
+ /** Timestampe option, if present */
+ const struct tcp_timestamp_option *tsopt;
+};
+
+/** @} */
+
+/*
+ * TCP flags
+ */
+#define TCP_CWR 0x80
+#define TCP_ECE 0x40
+#define TCP_URG 0x20
+#define TCP_ACK 0x10
+#define TCP_PSH 0x08
+#define TCP_RST 0x04
+#define TCP_SYN 0x02
+#define TCP_FIN 0x01
+
+/**
+* @defgroup tcpstates TCP states
+*
+* The TCP state is defined by a combination of the flags that have
+* been sent to the peer, the flags that have been acknowledged by the
+* peer, and the flags that have been received from the peer.
+*
+* @{
+*/
+
+/** TCP flags that have been sent in outgoing packets */
+#define TCP_STATE_SENT(flags) ( (flags) << 0 )
+#define TCP_FLAGS_SENT(state) ( ( (state) >> 0 ) & 0xff )
+
+/** TCP flags that have been acknowledged by the peer
+ *
+ * Note that this applies only to SYN and FIN.
+ */
+#define TCP_STATE_ACKED(flags) ( (flags) << 8 )
+#define TCP_FLAGS_ACKED(state) ( ( (state) >> 8 ) & 0xff )
+
+/** TCP flags that have been received from the peer
+ *
+ * Note that this applies only to SYN and FIN, and that once SYN has
+ * been received, we should always be sending ACK.
+ */
+#define TCP_STATE_RCVD(flags) ( (flags) << 16 )
+#define TCP_FLAGS_RCVD(state) ( ( (state) >> 16 ) & 0xff )
+
+/** TCP flags that are currently being sent in outgoing packets */
+#define TCP_FLAGS_SENDING(state) \
+ ( TCP_FLAGS_SENT ( state ) & ~TCP_FLAGS_ACKED ( state ) )
+
+/** CLOSED
+ *
+ * The connection has not yet been used for anything.
+ */
+#define TCP_CLOSED TCP_RST
+
+/** LISTEN
+ *
+ * Not currently used as a state; we have no support for listening
+ * connections. Given a unique value to avoid compiler warnings.
+ */
+#define TCP_LISTEN 0
+
+/** SYN_SENT
+ *
+ * SYN has been sent, nothing has yet been received or acknowledged.
+ */
+#define TCP_SYN_SENT ( TCP_STATE_SENT ( TCP_SYN ) )
+
+/** SYN_RCVD
+ *
+ * SYN has been sent but not acknowledged, SYN has been received.
+ */
+#define TCP_SYN_RCVD ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK ) | \
+ TCP_STATE_RCVD ( TCP_SYN ) )
+
+/** ESTABLISHED
+ *
+ * SYN has been sent and acknowledged, SYN has been received.
+ */
+#define TCP_ESTABLISHED ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK ) | \
+ TCP_STATE_ACKED ( TCP_SYN ) | \
+ TCP_STATE_RCVD ( TCP_SYN ) )
+
+/** FIN_WAIT_1
+ *
+ * SYN has been sent and acknowledged, SYN has been received, FIN has
+ * been sent but not acknowledged, FIN has not been received.
+ *
+ * RFC 793 shows that we can enter FIN_WAIT_1 without have had SYN
+ * acknowledged, i.e. if the application closes the connection after
+ * sending and receiving SYN, but before having had SYN acknowledged.
+ * However, we have to *pretend* that SYN has been acknowledged
+ * anyway, otherwise we end up sending SYN and FIN in the same
+ * sequence number slot. Therefore, when we transition from SYN_RCVD
+ * to FIN_WAIT_1, we have to remember to set TCP_STATE_ACKED(TCP_SYN)
+ * and increment our sequence number.
+ */
+#define TCP_FIN_WAIT_1 ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK | TCP_FIN ) | \
+ TCP_STATE_ACKED ( TCP_SYN ) | \
+ TCP_STATE_RCVD ( TCP_SYN ) )
+
+/** FIN_WAIT_2
+ *
+ * SYN has been sent and acknowledged, SYN has been received, FIN has
+ * been sent and acknowledged, FIN ha not been received.
+ */
+#define TCP_FIN_WAIT_2 ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK | TCP_FIN ) | \
+ TCP_STATE_ACKED ( TCP_SYN | TCP_FIN ) | \
+ TCP_STATE_RCVD ( TCP_SYN ) )
+
+/** CLOSING / LAST_ACK
+ *
+ * SYN has been sent and acknowledged, SYN has been received, FIN has
+ * been sent but not acknowledged, FIN has been received.
+ *
+ * This state actually encompasses both CLOSING and LAST_ACK; they are
+ * identical with the definition of state that we use. I don't
+ * *believe* that they need to be distinguished.
+ */
+#define TCP_CLOSING_OR_LAST_ACK \
+ ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK | TCP_FIN ) | \
+ TCP_STATE_ACKED ( TCP_SYN ) | \
+ TCP_STATE_RCVD ( TCP_SYN | TCP_FIN ) )
+
+/** TIME_WAIT
+ *
+ * SYN has been sent and acknowledged, SYN has been received, FIN has
+ * been sent and acknowledged, FIN has been received.
+ */
+#define TCP_TIME_WAIT ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK | TCP_FIN ) | \
+ TCP_STATE_ACKED ( TCP_SYN | TCP_FIN ) | \
+ TCP_STATE_RCVD ( TCP_SYN | TCP_FIN ) )
+
+/** CLOSE_WAIT
+ *
+ * SYN has been sent and acknowledged, SYN has been received, FIN has
+ * been received.
+ */
+#define TCP_CLOSE_WAIT ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK ) | \
+ TCP_STATE_ACKED ( TCP_SYN ) | \
+ TCP_STATE_RCVD ( TCP_SYN | TCP_FIN ) )
+
+/** Can send data in current state
+ *
+ * We can send data if and only if we have had our SYN acked and we
+ * have not yet sent our FIN.
+ */
+#define TCP_CAN_SEND_DATA(state) \
+ ( ( (state) & ( TCP_STATE_ACKED ( TCP_SYN ) | \
+ TCP_STATE_SENT ( TCP_FIN ) ) ) \
+ == TCP_STATE_ACKED ( TCP_SYN ) )
+
+/** Have closed gracefully
+ *
+ * We have closed gracefully if we have both received a FIN and had
+ * our own FIN acked.
+ */
+#define TCP_CLOSED_GRACEFULLY(state) \
+ ( ( (state) & ( TCP_STATE_ACKED ( TCP_FIN ) | \
+ TCP_STATE_RCVD ( TCP_FIN ) ) ) \
+ == ( TCP_STATE_ACKED ( TCP_FIN ) | TCP_STATE_RCVD ( TCP_FIN ) ) )
+
+/** @} */
+
+/** Mask for TCP header length field */
+#define TCP_MASK_HLEN 0xf0
+
+/** Smallest port number on which a TCP connection can listen */
+#define TCP_MIN_PORT 1
+
+/* Some IOB constants */
+#define MAX_HDR_LEN 100
+#define MAX_IOB_LEN 1500
+#define MIN_IOB_LEN MAX_HDR_LEN + 100 /* To account for padding by LL */
+
+/**
+ * Maxmimum advertised TCP window size
+ *
+ * We estimate the TCP window size as the amount of free memory we
+ * have. This is not strictly accurate (since it ignores any space
+ * already allocated as RX buffers), but it will do for now.
+ *
+ * Since we don't store out-of-order received packets, the
+ * retransmission penalty is that the whole window contents must be
+ * resent. This suggests keeping the window size small, but bear in
+ * mind that the maximum bandwidth on any link is limited to
+ *
+ * max_bandwidth = ( tcp_window / round_trip_time )
+ *
+ * With a 48kB window, which probably accurately reflects our amount
+ * of free memory, and a WAN RTT of say 200ms, this gives a maximum
+ * bandwidth of 240kB/s. This is sufficiently close to realistic that
+ * we will need to be careful that our advertised window doesn't end
+ * up limiting WAN download speeds.
+ *
+ * Finally, since the window goes into a 16-bit field and we cannot
+ * actually use 65536, we use a window size of (65536-4) to ensure
+ * that payloads remain dword-aligned.
+ */
+//#define TCP_MAX_WINDOW_SIZE ( 65536 - 4 )
+#define TCP_MAX_WINDOW_SIZE 4096
+
+/**
+ * Path MTU
+ *
+ * We really ought to implement Path MTU discovery. Until we do,
+ * anything with a path MTU greater than this may fail.
+ */
+#define TCP_PATH_MTU 1460
+
+/**
+ * Advertised TCP MSS
+ *
+ * We currently hardcode this to a reasonable value and hope that the
+ * sender uses path MTU discovery. The alternative is breaking the
+ * abstraction layer so that we can find out the MTU from the IP layer
+ * (which would have to find out from the net device layer).
+ */
+#define TCP_MSS 1460
+
+/** TCP maximum segment lifetime
+ *
+ * Currently set to 2 minutes, as per RFC 793.
+ */
+#define TCP_MSL ( 2 * 60 * TICKS_PER_SEC )
+
+extern struct tcpip_protocol tcp_protocol;
+
+#endif /* _GPXE_TCP_H */
diff --git a/gpxe/src/include/gpxe/tcpip.h b/gpxe/src/include/gpxe/tcpip.h
new file mode 100644
index 00000000..c0fadd23
--- /dev/null
+++ b/gpxe/src/include/gpxe/tcpip.h
@@ -0,0 +1,118 @@
+#ifndef _GPXE_TCPIP_H
+#define _GPXE_TCPIP_H
+
+/** @file
+ *
+ * Transport-network layer interface
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/socket.h>
+#include <gpxe/in.h>
+#include <gpxe/tables.h>
+
+struct io_buffer;
+struct net_device;
+
+/** Empty checksum value
+ *
+ * This is the TCP/IP checksum over a zero-length block of data.
+ */
+#define TCPIP_EMPTY_CSUM 0xffff
+
+/**
+ * TCP/IP socket address
+ *
+ * This contains the fields common to socket addresses for all TCP/IP
+ * address families.
+ */
+struct sockaddr_tcpip {
+ /** Socket address family (part of struct @c sockaddr) */
+ sa_family_t st_family;
+ /** TCP/IP port */
+ uint16_t st_port;
+ /** Padding
+ *
+ * This ensures that a struct @c sockaddr_tcpip is large
+ * enough to hold a socket address for any TCP/IP address
+ * family.
+ */
+ char pad[ sizeof ( struct sockaddr ) -
+ ( sizeof ( sa_family_t ) + sizeof ( uint16_t ) ) ];
+};
+
+/**
+ * A transport-layer protocol of the TCP/IP stack (eg. UDP, TCP, etc)
+ */
+struct tcpip_protocol {
+ /** Protocol name */
+ const char *name;
+ /**
+ * Process received packet
+ *
+ * @v iobuf I/O buffer
+ * @v st_src Partially-filled source address
+ * @v st_dest Partially-filled destination address
+ * @v pshdr_csum Pseudo-header checksum
+ * @ret rc Return status code
+ *
+ * This method takes ownership of the I/O buffer.
+ */
+ int ( * rx ) ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
+ struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum );
+ /**
+ * Transport-layer protocol number
+ *
+ * This is a constant of the type IP_XXX
+ */
+ uint8_t tcpip_proto;
+};
+
+/**
+ * A network-layer protocol of the TCP/IP stack (eg. IPV4, IPv6, etc)
+ */
+struct tcpip_net_protocol {
+ /** Protocol name */
+ const char *name;
+ /** Network address family */
+ sa_family_t sa_family;
+ /**
+ * Transmit packet
+ *
+ * @v iobuf I/O buffer
+ * @v tcpip_protocol Transport-layer protocol
+ * @v st_dest Destination address
+ * @v netdev Network device (or NULL to route automatically)
+ * @v trans_csum Transport-layer checksum to complete, or NULL
+ * @ret rc Return status code
+ *
+ * This function takes ownership of the I/O buffer.
+ */
+ int ( * tx ) ( struct io_buffer *iobuf,
+ struct tcpip_protocol *tcpip_protocol,
+ struct sockaddr_tcpip *st_dest,
+ struct net_device *netdev,
+ uint16_t *trans_csum );
+};
+
+/** Declare a TCP/IP transport-layer protocol */
+#define __tcpip_protocol \
+ __table ( struct tcpip_protocol, tcpip_protocols, 01 )
+
+/** Declare a TCP/IP network-layer protocol */
+#define __tcpip_net_protocol \
+ __table ( struct tcpip_net_protocol, tcpip_net_protocols, 01 )
+
+extern int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto,
+ struct sockaddr_tcpip *st_src,
+ struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum );
+extern int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip,
+ struct sockaddr_tcpip *st_dest,
+ struct net_device *netdev,
+ uint16_t *trans_csum );
+extern uint16_t tcpip_continue_chksum ( uint16_t partial,
+ const void *data, size_t len );
+extern uint16_t tcpip_chksum ( const void *data, size_t len );
+
+#endif /* _GPXE_TCPIP_H */
diff --git a/gpxe/src/include/gpxe/tftp.h b/gpxe/src/include/gpxe/tftp.h
new file mode 100644
index 00000000..0177a95a
--- /dev/null
+++ b/gpxe/src/include/gpxe/tftp.h
@@ -0,0 +1,83 @@
+#ifndef _GPXE_TFTP_H
+#define _GPXE_TFTP_H
+
+/** @file
+ *
+ * TFTP protocol
+ *
+ */
+
+#include <stdint.h>
+
+#define TFTP_PORT 69 /**< Default TFTP server port */
+#define TFTP_DEFAULT_BLKSIZE 512 /**< Default TFTP data block size */
+#define TFTP_MAX_BLKSIZE 1432
+
+#define TFTP_RRQ 1 /**< Read request opcode */
+#define TFTP_WRQ 2 /**< Write request opcode */
+#define TFTP_DATA 3 /**< Data block opcode */
+#define TFTP_ACK 4 /**< Data block acknowledgement opcode */
+#define TFTP_ERROR 5 /**< Error opcode */
+#define TFTP_OACK 6 /**< Options acknowledgement opcode */
+
+#define TFTP_ERR_FILE_NOT_FOUND 1 /**< File not found */
+#define TFTP_ERR_ACCESS_DENIED 2 /**< Access violation */
+#define TFTP_ERR_DISK_FULL 3 /**< Disk full or allocation exceeded */
+#define TFTP_ERR_ILLEGAL_OP 4 /**< Illegal TFTP operation */
+#define TFTP_ERR_UNKNOWN_TID 5 /**< Unknown transfer ID */
+#define TFTP_ERR_FILE_EXISTS 6 /**< File already exists */
+#define TFTP_ERR_UNKNOWN_USER 7 /**< No such user */
+#define TFTP_ERR_BAD_OPTS 8 /**< Option negotiation failed */
+
+#define MTFTP_PORT 1759 /**< Default MTFTP server port */
+
+/** A TFTP read request (RRQ) packet */
+struct tftp_rrq {
+ uint16_t opcode;
+ char data[0];
+} __attribute__ (( packed ));
+
+/** A TFTP data (DATA) packet */
+struct tftp_data {
+ uint16_t opcode;
+ uint16_t block;
+ uint8_t data[0];
+} __attribute__ (( packed ));
+
+/** A TFTP acknowledgement (ACK) packet */
+struct tftp_ack {
+ uint16_t opcode;
+ uint16_t block;
+} __attribute__ (( packed ));
+
+/** A TFTP error (ERROR) packet */
+struct tftp_error {
+ uint16_t opcode;
+ uint16_t errcode;
+ char errmsg[0];
+} __attribute__ (( packed ));
+
+/** A TFTP options acknowledgement (OACK) packet */
+struct tftp_oack {
+ uint16_t opcode;
+ char data[0];
+} __attribute__ (( packed ));
+
+/** The common header of all TFTP packets */
+struct tftp_common {
+ uint16_t opcode;
+} __attribute__ (( packed ));
+
+/** A union encapsulating all TFTP packet types */
+union tftp_any {
+ struct tftp_common common;
+ struct tftp_rrq rrq;
+ struct tftp_data data;
+ struct tftp_ack ack;
+ struct tftp_error error;
+ struct tftp_oack oack;
+};
+
+extern void tftp_set_request_blksize ( unsigned int blksize );
+
+#endif /* _GPXE_TFTP_H */
diff --git a/gpxe/src/include/gpxe/threewire.h b/gpxe/src/include/gpxe/threewire.h
new file mode 100644
index 00000000..865fc25d
--- /dev/null
+++ b/gpxe/src/include/gpxe/threewire.h
@@ -0,0 +1,89 @@
+#ifndef _GPXE_THREEWIRE_H
+#define _GPXE_THREEWIRE_H
+
+/** @file
+ *
+ * Three-wire serial interface
+ *
+ * The Atmel three-wire interface is a subset of the (newer) SPI
+ * interface, and is implemented here as a layer on top of the SPI
+ * support.
+ */
+
+#include <gpxe/spi.h>
+#include <limits.h>
+
+/**
+ * @defgroup tcmds Three-wire commands
+ * @{
+ */
+
+/** Read data from memory array */
+#define THREEWIRE_READ 0x6
+
+/** Write data to memory array */
+#define THREEWIRE_WRITE 0x5
+
+/** Write enable */
+#define THREEWIRE_EWEN 0x4
+
+/** Address to be used for write enable command */
+#define THREEWIRE_EWEN_ADDRESS INT_MAX
+
+/** Time to wait for write cycles to complete
+ *
+ * This is sufficient for AT93C46/AT93C56 devices, but may need to be
+ * increased in future when other devices are added.
+ */
+#define THREEWIRE_WRITE_MDELAY 10
+
+/** @} */
+
+extern int threewire_read ( struct nvs_device *nvs, unsigned int address,
+ void *data, size_t len );
+extern int threewire_write ( struct nvs_device *nvs, unsigned int address,
+ const void *data, size_t len );
+
+/**
+ * @defgroup tdevs Three-wire device types
+ * @{
+ */
+
+static inline __attribute__ (( always_inline )) void
+init_at93cx6 ( struct spi_device *device, unsigned int organisation ) {
+ device->nvs.word_len_log2 = ( ( organisation == 8 ) ? 0 : 1 );
+ device->nvs.block_size = 1;
+ device->command_len = 3,
+ device->nvs.read = threewire_read;
+ device->nvs.write = threewire_write;
+}
+
+/**
+ * Initialise Atmel AT93C46 serial EEPROM
+ *
+ * @v device SPI device
+ * @v organisation Word organisation (8 or 16)
+ */
+static inline __attribute__ (( always_inline )) void
+init_at93c46 ( struct spi_device *device, unsigned int organisation ) {
+ device->nvs.size = ( 1024 / organisation );
+ device->address_len = ( ( organisation == 8 ) ? 7 : 6 );
+ init_at93cx6 ( device, organisation );
+}
+
+/**
+ * Initialise Atmel AT93C56 serial EEPROM
+ *
+ * @v device SPI device
+ * @v organisation Word organisation (8 or 16)
+ */
+static inline __attribute__ (( always_inline )) void
+init_at93c56 ( struct spi_device *device, unsigned int organisation ) {
+ device->nvs.size = ( 2048 / organisation );
+ device->address_len = ( ( organisation == 8 ) ? 9 : 8 );
+ init_at93cx6 ( device, organisation );
+}
+
+/** @} */
+
+#endif /* _GPXE_THREEWIRE_H */
diff --git a/gpxe/src/include/gpxe/timer.h b/gpxe/src/include/gpxe/timer.h
new file mode 100644
index 00000000..b7057225
--- /dev/null
+++ b/gpxe/src/include/gpxe/timer.h
@@ -0,0 +1,41 @@
+#ifndef GPXE_TIMER_H
+#define GPXE_TIMER_H
+
+#include <stddef.h>
+#include <gpxe/tables.h>
+
+typedef unsigned long tick_t;
+
+#define MSECS_IN_SEC (1000)
+#define USECS_IN_SEC (1000*1000)
+#define USECS_IN_MSEC (1000)
+
+#define TICKS_PER_SEC USECS_IN_SEC
+
+extern tick_t currticks ( void );
+
+extern void generic_currticks_udelay ( unsigned int usecs );
+
+/** A timer */
+struct timer {
+ /** Initialise timer
+ *
+ * @ret rc Return status code
+ */
+ int ( * init ) ( void );
+ /** Read current time
+ *
+ * @ret ticks Current time, in ticks
+ */
+ tick_t ( * currticks ) ( void );
+ /** Delay
+ *
+ * @v usecs Time to delay, in microseconds
+ */
+ void ( * udelay ) ( unsigned int usecs );
+};
+
+#define __timer( order ) __table ( struct timer, timers, order )
+
+#endif /* GPXE_TIMER_H */
+
diff --git a/gpxe/src/include/gpxe/tls.h b/gpxe/src/include/gpxe/tls.h
new file mode 100644
index 00000000..a8cf16ef
--- /dev/null
+++ b/gpxe/src/include/gpxe/tls.h
@@ -0,0 +1,171 @@
+#ifndef _GPXE_TLS_H
+#define _GPXE_TLS_H
+
+/**
+ * @file
+ *
+ * Transport Layer Security Protocol
+ */
+
+#include <stdint.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/filter.h>
+#include <gpxe/process.h>
+#include <gpxe/crypto.h>
+#include <gpxe/md5.h>
+#include <gpxe/sha1.h>
+
+/** A TLS header */
+struct tls_header {
+ /** Content type
+ *
+ * This is a TLS_TYPE_XXX constant
+ */
+ uint8_t type;
+ /** Protocol version
+ *
+ * This is a TLS_VERSION_XXX constant
+ */
+ uint16_t version;
+ /** Length of payload */
+ uint16_t length;
+} __attribute__ (( packed ));
+
+/** TLS version 1.0 */
+#define TLS_VERSION_TLS_1_0 0x0301
+
+/** TLS version 1.1 */
+#define TLS_VERSION_TLS_1_1 0x0302
+
+/** Change cipher content type */
+#define TLS_TYPE_CHANGE_CIPHER 20
+
+/** Alert content type */
+#define TLS_TYPE_ALERT 21
+
+/** Handshake content type */
+#define TLS_TYPE_HANDSHAKE 22
+
+/** Application data content type */
+#define TLS_TYPE_DATA 23
+
+/* Handshake message types */
+#define TLS_HELLO_REQUEST 0
+#define TLS_CLIENT_HELLO 1
+#define TLS_SERVER_HELLO 2
+#define TLS_CERTIFICATE 11
+#define TLS_SERVER_KEY_EXCHANGE 12
+#define TLS_CERTIFICATE_REQUEST 13
+#define TLS_SERVER_HELLO_DONE 14
+#define TLS_CERTIFICATE_VERIFY 15
+#define TLS_CLIENT_KEY_EXCHANGE 16
+#define TLS_FINISHED 20
+
+/* TLS alert levels */
+#define TLS_ALERT_WARNING 1
+#define TLS_ALERT_FATAL 2
+
+/* TLS cipher specifications */
+#define TLS_RSA_WITH_NULL_MD5 0x0001
+#define TLS_RSA_WITH_NULL_SHA 0x0002
+#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002f
+#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035
+
+/** TLS RX state machine state */
+enum tls_rx_state {
+ TLS_RX_HEADER = 0,
+ TLS_RX_DATA,
+};
+
+/** TLS TX state machine state */
+enum tls_tx_state {
+ TLS_TX_NONE = 0,
+ TLS_TX_CLIENT_HELLO,
+ TLS_TX_CLIENT_KEY_EXCHANGE,
+ TLS_TX_CHANGE_CIPHER,
+ TLS_TX_FINISHED,
+ TLS_TX_DATA
+};
+
+/** A TLS cipher specification */
+struct tls_cipherspec {
+ /** Public-key encryption algorithm */
+ struct crypto_algorithm *pubkey;
+ /** Bulk encryption cipher algorithm */
+ struct crypto_algorithm *cipher;
+ /** MAC digest algorithm */
+ struct crypto_algorithm *digest;
+ /** Key length */
+ size_t key_len;
+ /** Dynamically-allocated storage */
+ void *dynamic;
+ /** Public key encryption context */
+ void *pubkey_ctx;
+ /** Bulk encryption cipher context */
+ void *cipher_ctx;
+ /** Next bulk encryption cipher context (TX only) */
+ void *cipher_next_ctx;
+ /** MAC secret */
+ void *mac_secret;
+};
+
+/** A TLS session */
+struct tls_session {
+ /** Reference counter */
+ struct refcnt refcnt;
+
+ /** Plaintext stream */
+ struct xfer_filter_half plainstream;
+ /** Ciphertext stream */
+ struct xfer_filter_half cipherstream;
+
+ /** Current TX cipher specification */
+ struct tls_cipherspec tx_cipherspec;
+ /** Next TX cipher specification */
+ struct tls_cipherspec tx_cipherspec_pending;
+ /** Current RX cipher specification */
+ struct tls_cipherspec rx_cipherspec;
+ /** Next RX cipher specification */
+ struct tls_cipherspec rx_cipherspec_pending;
+ /** Premaster secret */
+ uint8_t pre_master_secret[48];
+ /** Master secret */
+ uint8_t master_secret[48];
+ /** Server random bytes */
+ uint8_t server_random[32];
+ /** Client random bytes */
+ uint8_t client_random[32];
+ /** MD5 context for handshake verification */
+ uint8_t handshake_md5_ctx[MD5_CTX_SIZE];
+ /** SHA1 context for handshake verification */
+ uint8_t handshake_sha1_ctx[SHA1_CTX_SIZE];
+
+ /** Hack: server RSA public key */
+ uint8_t *rsa_mod;
+ size_t rsa_mod_len;
+ uint8_t *rsa_pub_exp;
+ size_t rsa_pub_exp_len;
+
+ /** TX sequence number */
+ uint64_t tx_seq;
+ /** TX state */
+ enum tls_tx_state tx_state;
+ /** TX process */
+ struct process process;
+
+ /** RX sequence number */
+ uint64_t rx_seq;
+ /** RX state */
+ enum tls_rx_state rx_state;
+ /** Offset within current RX state */
+ size_t rx_rcvd;
+ /** Current received record header */
+ struct tls_header rx_header;
+ /** Current received raw data buffer */
+ void *rx_data;
+};
+
+extern int add_tls ( struct xfer_interface *xfer,
+ struct xfer_interface **next );
+
+#endif /* _GPXE_TLS_H */
diff --git a/gpxe/src/include/gpxe/uaccess.h b/gpxe/src/include/gpxe/uaccess.h
new file mode 100644
index 00000000..05f89e03
--- /dev/null
+++ b/gpxe/src/include/gpxe/uaccess.h
@@ -0,0 +1,27 @@
+#ifndef _GPXE_UACCESS_H
+#define _GPXE_UACCESS_H
+
+/**
+ * @file
+ *
+ * Access to external ("user") memory
+ *
+ * gPXE often needs to transfer data between internal and external
+ * buffers. On i386, the external buffers may require access via a
+ * different segment, and the buffer address cannot be encoded into a
+ * simple void * pointer. The @c userptr_t type encapsulates the
+ * information needed to identify an external buffer, and the
+ * copy_to_user() and copy_from_user() functions provide methods for
+ * transferring data between internal and external buffers.
+ *
+ * Note that userptr_t is an opaque type; in particular, performing
+ * arithmetic upon a userptr_t is not allowed.
+ *
+ */
+
+#include <bits/uaccess.h>
+
+/** Equivalent of NULL for user pointers */
+#define UNULL ( ( userptr_t ) 0 )
+
+#endif /* _GPXE_UACCESS_H */
diff --git a/gpxe/src/include/gpxe/udp.h b/gpxe/src/include/gpxe/udp.h
new file mode 100644
index 00000000..cb0e44eb
--- /dev/null
+++ b/gpxe/src/include/gpxe/udp.h
@@ -0,0 +1,47 @@
+#ifndef _GPXE_UDP_H
+#define _GPXE_UDP_H
+
+/** @file
+ *
+ * UDP protocol
+ *
+ * This file defines the gPXE UDP API.
+ *
+ */
+
+#include <stddef.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/if_ether.h>
+
+struct xfer_interface;
+struct sockaddr;
+
+/**
+ * UDP constants
+ */
+
+#define UDP_MAX_HLEN 72
+#define UDP_MAX_TXIOB ETH_MAX_MTU
+#define UDP_MIN_TXIOB ETH_ZLEN
+
+/**
+ * A UDP header
+ */
+struct udp_header {
+ /** Source port */
+ uint16_t src;
+ /** Destination port */
+ uint16_t dest;
+ /** Length */
+ uint16_t len;
+ /** Checksum */
+ uint16_t chksum;
+};
+
+extern int udp_open_promisc ( struct xfer_interface *xfer );
+extern int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
+ struct sockaddr *local );
+
+#endif /* _GPXE_UDP_H */
+
diff --git a/gpxe/src/include/gpxe/umalloc.h b/gpxe/src/include/gpxe/umalloc.h
new file mode 100644
index 00000000..49ec22b4
--- /dev/null
+++ b/gpxe/src/include/gpxe/umalloc.h
@@ -0,0 +1,17 @@
+#ifndef _GPXE_UMALLOC_H
+#define _GPXE_UMALLOC_H
+
+/**
+ * @file
+ *
+ * User memory allocation
+ *
+ */
+
+#include <gpxe/uaccess.h>
+
+extern userptr_t umalloc ( size_t size );
+extern userptr_t urealloc ( userptr_t ptr, size_t new_size );
+extern void ufree ( userptr_t ptr );
+
+#endif /* _GPXE_UMALLOC_H */
diff --git a/gpxe/src/include/gpxe/uri.h b/gpxe/src/include/gpxe/uri.h
new file mode 100644
index 00000000..514bc479
--- /dev/null
+++ b/gpxe/src/include/gpxe/uri.h
@@ -0,0 +1,139 @@
+#ifndef _GPXE_URI_H
+#define _GPXE_URI_H
+
+/** @file
+ *
+ * Uniform Resource Identifiers
+ *
+ */
+
+#include <stdlib.h>
+#include <gpxe/refcnt.h>
+
+/** A Uniform Resource Identifier
+ *
+ * Terminology for this data structure is as per uri(7), except that
+ * "path" is defined to include the leading '/' for an absolute path.
+ *
+ * Note that all fields within a URI are optional and may be NULL.
+ *
+ * Some examples are probably helpful:
+ *
+ * http://www.etherboot.org/wiki :
+ *
+ * scheme = "http", host = "www.etherboot.org", path = "/wiki"
+ *
+ * /var/lib/tftpboot :
+ *
+ * path = "/var/lib/tftpboot"
+ *
+ * mailto:bob@nowhere.com :
+ *
+ * scheme = "mailto", opaque = "bob@nowhere.com"
+ *
+ * ftp://joe:secret@insecure.org:8081/hidden/path/to?what=is#this
+ *
+ * scheme = "ftp", user = "joe", password = "secret",
+ * host = "insecure.org", port = "8081", path = "/hidden/path/to",
+ * query = "what=is", fragment = "this"
+ */
+struct uri {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** Scheme */
+ const char *scheme;
+ /** Opaque part */
+ const char *opaque;
+ /** User name */
+ const char *user;
+ /** Password */
+ const char *password;
+ /** Host name */
+ const char *host;
+ /** Port number */
+ const char *port;
+ /** Path */
+ const char *path;
+ /** Query */
+ const char *query;
+ /** Fragment */
+ const char *fragment;
+};
+
+/**
+ * URI is an absolute URI
+ *
+ * @v uri URI
+ * @ret is_absolute URI is absolute
+ *
+ * An absolute URI begins with a scheme, e.g. "http:" or "mailto:".
+ * Note that this is a separate concept from a URI with an absolute
+ * path.
+ */
+static inline int uri_is_absolute ( struct uri *uri ) {
+ return ( uri->scheme != NULL );
+}
+
+/**
+ * URI has an absolute path
+ *
+ * @v uri URI
+ * @ret has_absolute_path URI has an absolute path
+ *
+ * An absolute path begins with a '/'. Note that this is a separate
+ * concept from an absolute URI. Note also that a URI may not have a
+ * path at all.
+ */
+static inline int uri_has_absolute_path ( struct uri *uri ) {
+ return ( uri->path && ( uri->path[0] == '/' ) );
+}
+
+/**
+ * URI has a relative path
+ *
+ * @v uri URI
+ * @ret has_relative_path URI has a relative path
+ *
+ * A relative path begins with something other than a '/'. Note that
+ * this is a separate concept from a relative URI. Note also that a
+ * URI may not have a path at all.
+ */
+static inline int uri_has_relative_path ( struct uri *uri ) {
+ return ( uri->path && ( uri->path[0] != '/' ) );
+}
+
+/**
+ * Increment URI reference count
+ *
+ * @v uri URI, or NULL
+ * @ret uri URI as passed in
+ */
+static inline __attribute__ (( always_inline )) struct uri *
+uri_get ( struct uri *uri ) {
+ ref_get ( &uri->refcnt );
+ return uri;
+}
+
+/**
+ * Decrement URI reference count
+ *
+ * @v uri URI, or NULL
+ */
+static inline __attribute__ (( always_inline )) void
+uri_put ( struct uri *uri ) {
+ ref_put ( &uri->refcnt );
+}
+
+extern struct uri *cwuri;
+
+extern struct uri * parse_uri ( const char *uri_string );
+extern unsigned int uri_port ( struct uri *uri, unsigned int default_port );
+extern int unparse_uri ( char *buf, size_t size, struct uri *uri );
+extern struct uri * uri_dup ( struct uri *uri );
+extern char * resolve_path ( const char *base_path,
+ const char *relative_path );
+extern struct uri * resolve_uri ( struct uri *base_uri,
+ struct uri *relative_uri );
+extern void churi ( struct uri *uri );
+
+#endif /* _GPXE_URI_H */
diff --git a/gpxe/src/include/gpxe/uuid.h b/gpxe/src/include/gpxe/uuid.h
new file mode 100644
index 00000000..a62735c9
--- /dev/null
+++ b/gpxe/src/include/gpxe/uuid.h
@@ -0,0 +1,34 @@
+#ifndef _GPXE_UUID_H
+#define _GPXE_UUID_H
+
+/** @file
+ *
+ * Universally unique IDs
+ */
+
+#include <stdint.h>
+
+union uuid;
+#include <bits/uuid.h>
+
+/** A universally unique ID */
+union uuid {
+ /** Canonical form (00000000-0000-0000-0000-000000000000) */
+ struct {
+ /** 8 hex digits, little-endian */
+ uint32_t a;
+ /** 2 hex digits, little-endian */
+ uint16_t b;
+ /** 2 hex digits, little-endian */
+ uint16_t c;
+ /** 2 hex digits, big-endian */
+ uint16_t d;
+ /** 12 hex digits, big-endian */
+ uint8_t e[6];
+ } canonical;
+ uint8_t raw[16];
+};
+
+extern char * uuid_ntoa ( union uuid *uuid );
+
+#endif /* _GPXE_UUID_H */
diff --git a/gpxe/src/include/gpxe/vsprintf.h b/gpxe/src/include/gpxe/vsprintf.h
new file mode 100644
index 00000000..9360f29b
--- /dev/null
+++ b/gpxe/src/include/gpxe/vsprintf.h
@@ -0,0 +1,71 @@
+#ifndef _GPXE_VSPRINTF_H
+#define _GPXE_VSPRINTF_H
+
+/** @file
+ *
+ * printf() and friends
+ *
+ * Etherboot's printf() functions understand the following subset of
+ * the standard C printf()'s format specifiers:
+ *
+ * - Flag characters
+ * - '#' - Alternate form (i.e. "0x" prefix)
+ * - '0' - Zero-pad
+ * - Field widths
+ * - Length modifiers
+ * - 'hh' - Signed / unsigned char
+ * - 'h' - Signed / unsigned short
+ * - 'l' - Signed / unsigned long
+ * - 'll' - Signed / unsigned long long
+ * - 'z' - Signed / unsigned size_t
+ * - Conversion specifiers
+ * - 'd' - Signed decimal
+ * - 'x','X' - Unsigned hexadecimal
+ * - 'c' - Character
+ * - 's' - String
+ * - 'p' - Pointer
+ *
+ * Hexadecimal numbers are always zero-padded to the specified field
+ * width (if any); decimal numbers are always space-padded. Decimal
+ * long longs are not supported.
+ *
+ */
+
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+/**
+ * A printf context
+ *
+ * Contexts are used in order to be able to share code between
+ * vprintf() and vsnprintf(), without requiring the allocation of a
+ * buffer for vprintf().
+ */
+struct printf_context {
+ /**
+ * Character handler
+ *
+ * @v ctx Context
+ * @v c Character
+ *
+ * This method is called for each character written to the
+ * formatted string.
+ */
+ void ( * handler ) ( struct printf_context *ctx, unsigned int c );
+ /** Length of formatted string
+ *
+ * When handler() is called, @len will be set to the number of
+ * characters written so far (i.e. zero for the first call to
+ * handler()).
+ */
+ size_t len;
+};
+
+extern size_t vcprintf ( struct printf_context *ctx, const char *fmt,
+ va_list args );
+extern int vssnprintf ( char *buf, ssize_t ssize, const char *fmt,
+ va_list args );
+extern int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... );
+
+#endif /* _GPXE_VSPRINTF_H */
diff --git a/gpxe/src/include/gpxe/xfer.h b/gpxe/src/include/gpxe/xfer.h
new file mode 100644
index 00000000..9575bf69
--- /dev/null
+++ b/gpxe/src/include/gpxe/xfer.h
@@ -0,0 +1,275 @@
+#ifndef _GPXE_XFER_H
+#define _GPXE_XFER_H
+
+/** @file
+ *
+ * Data transfer interfaces
+ *
+ */
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <gpxe/interface.h>
+#include <gpxe/iobuf.h>
+
+struct xfer_interface;
+struct xfer_metadata;
+
+/** Data transfer interface operations */
+struct xfer_interface_operations {
+ /** Close interface
+ *
+ * @v xfer Data transfer interface
+ * @v rc Reason for close
+ */
+ void ( * close ) ( struct xfer_interface *xfer, int rc );
+ /** Redirect to new location
+ *
+ * @v xfer Data transfer interface
+ * @v type New location type
+ * @v args Remaining arguments depend upon location type
+ * @ret rc Return status code
+ */
+ int ( * vredirect ) ( struct xfer_interface *xfer, int type,
+ va_list args );
+ /** Check flow control window
+ *
+ * @v xfer Data transfer interface
+ * @ret len Length of window
+ *
+ * Flow control is regarded as advisory but not mandatory.
+ * Users who have control over their own rate of data
+ * generation should perform a flow control check before
+ * generating new data. Users who have no control (such as
+ * NIC drivers or filter layers) are not obliged to check.
+ *
+ * Data transfer interfaces must be prepared to accept
+ * datagrams even if they are advertising a window of zero
+ * bytes.
+ */
+ size_t ( * window ) ( struct xfer_interface *xfer );
+ /** Allocate I/O buffer
+ *
+ * @v xfer Data transfer interface
+ * @v len I/O buffer payload length
+ * @ret iobuf I/O buffer
+ */
+ struct io_buffer * ( * alloc_iob ) ( struct xfer_interface *xfer,
+ size_t len );
+ /** Deliver datagram as I/O buffer with metadata
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf Datagram I/O buffer
+ * @v meta Data transfer metadata
+ * @ret rc Return status code
+ *
+ * A data transfer interface that wishes to support only raw
+ * data delivery should set this method to
+ * xfer_deliver_as_raw().
+ */
+ int ( * deliver_iob ) ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta );
+ /** Deliver datagram as raw data
+ *
+ * @v xfer Data transfer interface
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ *
+ * A data transfer interface that wishes to support only I/O
+ * buffer delivery should set this method to
+ * xfer_deliver_as_iob().
+ */
+ int ( * deliver_raw ) ( struct xfer_interface *xfer,
+ const void *data, size_t len );
+};
+
+/** A data transfer interface */
+struct xfer_interface {
+ /** Generic object communication interface */
+ struct interface intf;
+ /** Operations for received messages */
+ struct xfer_interface_operations *op;
+};
+
+/** Basis positions for seek() events */
+enum seek_whence {
+ SEEK_CUR = 0,
+ SEEK_SET,
+};
+
+/** Data transfer metadata */
+struct xfer_metadata {
+ /** Position of data within stream */
+ off_t offset;
+ /** Basis for data position
+ *
+ * Must be one of @c SEEK_CUR or @c SEEK_SET.
+ */
+ int whence;
+ /** Source socket address, or NULL */
+ struct sockaddr *src;
+ /** Destination socket address, or NULL */
+ struct sockaddr *dest;
+ /** Network device, or NULL */
+ struct net_device *netdev;
+};
+
+/**
+ * Describe seek basis
+ *
+ * @v whence Basis for new position
+ */
+static inline __attribute__ (( always_inline )) const char *
+whence_text ( int whence ) {
+ switch ( whence ) {
+ case SEEK_CUR: return "CUR";
+ case SEEK_SET: return "SET";
+ default: return "INVALID";
+ }
+}
+
+extern struct xfer_interface null_xfer;
+extern struct xfer_interface_operations null_xfer_ops;
+
+extern void xfer_close ( struct xfer_interface *xfer, int rc );
+extern int xfer_vredirect ( struct xfer_interface *xfer, int type,
+ va_list args );
+extern int xfer_redirect ( struct xfer_interface *xfer, int type, ... );
+extern size_t xfer_window ( struct xfer_interface *xfer );
+extern struct io_buffer * xfer_alloc_iob ( struct xfer_interface *xfer,
+ size_t len );
+extern int xfer_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf );
+extern int xfer_deliver_iob_meta ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta );
+extern int xfer_deliver_raw ( struct xfer_interface *xfer,
+ const void *data, size_t len );
+extern int xfer_vprintf ( struct xfer_interface *xfer,
+ const char *format, va_list args );
+extern int xfer_printf ( struct xfer_interface *xfer,
+ const char *format, ... );
+extern int xfer_seek ( struct xfer_interface *xfer, off_t offset, int whence );
+
+extern void ignore_xfer_close ( struct xfer_interface *xfer, int rc );
+extern int ignore_xfer_vredirect ( struct xfer_interface *xfer,
+ int type, va_list args );
+extern size_t unlimited_xfer_window ( struct xfer_interface *xfer );
+extern size_t no_xfer_window ( struct xfer_interface *xfer );
+extern struct io_buffer * default_xfer_alloc_iob ( struct xfer_interface *xfer,
+ size_t len );
+extern int xfer_deliver_as_raw ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta );
+extern int xfer_deliver_as_iob ( struct xfer_interface *xfer,
+ const void *data, size_t len );
+extern int ignore_xfer_deliver_raw ( struct xfer_interface *xfer,
+ const void *data __unused, size_t len );
+
+/**
+ * Initialise a data transfer interface
+ *
+ * @v xfer Data transfer interface
+ * @v op Data transfer interface operations
+ * @v refcnt Containing object reference counter, or NULL
+ */
+static inline void xfer_init ( struct xfer_interface *xfer,
+ struct xfer_interface_operations *op,
+ struct refcnt *refcnt ) {
+ xfer->intf.dest = &null_xfer.intf;
+ xfer->intf.refcnt = refcnt;
+ xfer->op = op;
+}
+
+/**
+ * Initialise a static data transfer interface
+ *
+ * @v operations Data transfer interface operations
+ */
+#define XFER_INIT( operations ) { \
+ .intf = { \
+ .dest = &null_xfer.intf, \
+ .refcnt = NULL, \
+ }, \
+ .op = operations, \
+ }
+
+/**
+ * Get data transfer interface from generic object communication interface
+ *
+ * @v intf Generic object communication interface
+ * @ret xfer Data transfer interface
+ */
+static inline __attribute__ (( always_inline )) struct xfer_interface *
+intf_to_xfer ( struct interface *intf ) {
+ return container_of ( intf, struct xfer_interface, intf );
+}
+
+/**
+ * Get reference to destination data transfer interface
+ *
+ * @v xfer Data transfer interface
+ * @ret dest Destination interface
+ */
+static inline __attribute__ (( always_inline )) struct xfer_interface *
+xfer_get_dest ( struct xfer_interface *xfer ) {
+ return intf_to_xfer ( intf_get ( xfer->intf.dest ) );
+}
+
+/**
+ * Drop reference to data transfer interface
+ *
+ * @v xfer Data transfer interface
+ */
+static inline __attribute__ (( always_inline )) void
+xfer_put ( struct xfer_interface *xfer ) {
+ intf_put ( &xfer->intf );
+}
+
+/**
+ * Plug a data transfer interface into a new destination interface
+ *
+ * @v xfer Data transfer interface
+ * @v dest New destination interface
+ */
+static inline __attribute__ (( always_inline )) void
+xfer_plug ( struct xfer_interface *xfer, struct xfer_interface *dest ) {
+ plug ( &xfer->intf, &dest->intf );
+}
+
+/**
+ * Plug two data transfer interfaces together
+ *
+ * @v a Data transfer interface A
+ * @v b Data transfer interface B
+ */
+static inline __attribute__ (( always_inline )) void
+xfer_plug_plug ( struct xfer_interface *a, struct xfer_interface *b ) {
+ plug_plug ( &a->intf, &b->intf );
+}
+
+/**
+ * Unplug a data transfer interface
+ *
+ * @v xfer Data transfer interface
+ */
+static inline __attribute__ (( always_inline )) void
+xfer_unplug ( struct xfer_interface *xfer ) {
+ plug ( &xfer->intf, &null_xfer.intf );
+}
+
+/**
+ * Stop using a data transfer interface
+ *
+ * @v xfer Data transfer interface
+ *
+ * After calling this method, no further messages will be received via
+ * the interface.
+ */
+static inline void xfer_nullify ( struct xfer_interface *xfer ) {
+ xfer->op = &null_xfer_ops;
+};
+
+#endif /* _GPXE_XFER_H */
diff --git a/gpxe/src/include/i82365.h b/gpxe/src/include/i82365.h
new file mode 100644
index 00000000..3b0e00ca
--- /dev/null
+++ b/gpxe/src/include/i82365.h
@@ -0,0 +1,450 @@
+/*
+ * i82365.h 1.15 1999/10/25 20:03:34
+ *
+ * The contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL").
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ */
+
+#ifndef _LINUX_I82365_H
+#define _LINUX_I82365_H
+
+/* register definitions for the Intel 82365SL PCMCIA controller */
+
+/* Offsets for PCIC registers */
+#define I365_IDENT 0x00 /* Identification and revision */
+#define I365_STATUS 0x01 /* Interface status */
+#define I365_POWER 0x02 /* Power and RESETDRV control */
+#define I365_INTCTL 0x03 /* Interrupt and general control */
+#define I365_CSC 0x04 /* Card status change */
+#define I365_CSCINT 0x05 /* Card status change interrupt control */
+#define I365_ADDRWIN 0x06 /* Address window enable */
+#define I365_IOCTL 0x07 /* I/O control */
+#define I365_GENCTL 0x16 /* Card detect and general control */
+#define I365_GBLCTL 0x1E /* Global control register */
+
+/* Offsets for I/O and memory window registers */
+#define I365_IO(map) (0x08+((map)<<2))
+#define I365_MEM(map) (0x10+((map)<<3))
+#define I365_W_START 0
+#define I365_W_STOP 2
+#define I365_W_OFF 4
+
+/* Flags for I365_STATUS */
+#define I365_CS_BVD1 0x01
+#define I365_CS_STSCHG 0x01
+#define I365_CS_BVD2 0x02
+#define I365_CS_SPKR 0x02
+#define I365_CS_DETECT 0x0C
+#define I365_CS_WRPROT 0x10
+#define I365_CS_READY 0x20 /* Inverted */
+#define I365_CS_POWERON 0x40
+#define I365_CS_GPI 0x80
+
+/* Flags for I365_POWER */
+#define I365_PWR_OFF 0x00 /* Turn off the socket */
+#define I365_PWR_OUT 0x80 /* Output enable */
+#define I365_PWR_NORESET 0x40 /* Disable RESETDRV on resume */
+#define I365_PWR_AUTO 0x20 /* Auto pwr switch enable */
+#define I365_VCC_MASK 0x18 /* Mask for turning off Vcc */
+/* There are different layouts for B-step and DF-step chips: the B
+ step has independent Vpp1/Vpp2 control, and the DF step has only
+ Vpp1 control, plus 3V control */
+#define I365_VCC_5V 0x10 /* Vcc = 5.0v */
+#define I365_VCC_3V 0x18 /* Vcc = 3.3v */
+#define I365_VPP2_MASK 0x0c /* Mask for turning off Vpp2 */
+#define I365_VPP2_5V 0x04 /* Vpp2 = 5.0v */
+#define I365_VPP2_12V 0x08 /* Vpp2 = 12.0v */
+#define I365_VPP1_MASK 0x03 /* Mask for turning off Vpp1 */
+#define I365_VPP1_5V 0x01 /* Vpp2 = 5.0v */
+#define I365_VPP1_12V 0x02 /* Vpp2 = 12.0v */
+
+/* Flags for I365_INTCTL */
+#define I365_RING_ENA 0x80
+#define I365_PC_RESET 0x40
+#define I365_PC_IOCARD 0x20
+#define I365_INTR_ENA 0x10
+#define I365_IRQ_MASK 0x0F
+
+/* Flags for I365_CSC and I365_CSCINT*/
+#define I365_CSC_BVD1 0x01
+#define I365_CSC_STSCHG 0x01
+#define I365_CSC_BVD2 0x02
+#define I365_CSC_READY 0x04
+#define I365_CSC_DETECT 0x08
+#define I365_CSC_ANY 0x0F
+#define I365_CSC_GPI 0x10
+
+/* Flags for I365_ADDRWIN */
+#define I365_ENA_IO(map) (0x40 << (map))
+#define I365_ENA_MEM(map) (0x01 << (map))
+
+/* Flags for I365_IOCTL */
+#define I365_IOCTL_MASK(map) (0x0F << (map<<2))
+#define I365_IOCTL_WAIT(map) (0x08 << (map<<2))
+#define I365_IOCTL_0WS(map) (0x04 << (map<<2))
+#define I365_IOCTL_IOCS16(map) (0x02 << (map<<2))
+#define I365_IOCTL_16BIT(map) (0x01 << (map<<2))
+
+/* Flags for I365_GENCTL */
+#define I365_CTL_16DELAY 0x01
+#define I365_CTL_RESET 0x02
+#define I365_CTL_GPI_ENA 0x04
+#define I365_CTL_GPI_CTL 0x08
+#define I365_CTL_RESUME 0x10
+#define I365_CTL_SW_IRQ 0x20
+
+/* Flags for I365_GBLCTL */
+#define I365_GBL_PWRDOWN 0x01
+#define I365_GBL_CSC_LEV 0x02
+#define I365_GBL_WRBACK 0x04
+#define I365_GBL_IRQ_0_LEV 0x08
+#define I365_GBL_IRQ_1_LEV 0x10
+
+/* Flags for memory window registers */
+#define I365_MEM_16BIT 0x8000 /* In memory start high byte */
+#define I365_MEM_0WS 0x4000
+#define I365_MEM_WS1 0x8000 /* In memory stop high byte */
+#define I365_MEM_WS0 0x4000
+#define I365_MEM_WRPROT 0x8000 /* In offset high byte */
+#define I365_MEM_REG 0x4000
+
+#define I365_REG(slot, reg) (((slot) << 6) + reg)
+
+#endif /* _LINUX_I82365_H */
+
+//*****************************************************************************
+//*****************************************************************************
+//*****************************************************************************
+//*****************************************************************************
+//*****************************************************************************
+// Beginning vg468.h (for VADEM chipset)
+
+#ifndef _LINUX_VG468_H
+#define _LINUX_VG468_H
+
+/* Special bit in I365_IDENT used for Vadem chip detection */
+#define I365_IDENT_VADEM 0x08
+
+/* Special definitions in I365_POWER */
+#define VG468_VPP2_MASK 0x0c
+#define VG468_VPP2_5V 0x04
+#define VG468_VPP2_12V 0x08
+
+/* Unique Vadem registers */
+#define VG469_VSENSE 0x1f /* Card voltage sense */
+#define VG469_VSELECT 0x2f /* Card voltage select */
+#define VG468_CTL 0x38 /* Control register */
+#define VG468_TIMER 0x39 /* Timer control */
+#define VG468_MISC 0x3a /* Miscellaneous */
+#define VG468_GPIO_CFG 0x3b /* GPIO configuration */
+#define VG469_EXT_MODE 0x3c /* Extended mode register */
+#define VG468_SELECT 0x3d /* Programmable chip select */
+#define VG468_SELECT_CFG 0x3e /* Chip select configuration */
+#define VG468_ATA 0x3f /* ATA control */
+
+/* Flags for VG469_VSENSE */
+#define VG469_VSENSE_A_VS1 0x01
+#define VG469_VSENSE_A_VS2 0x02
+#define VG469_VSENSE_B_VS1 0x04
+#define VG469_VSENSE_B_VS2 0x08
+
+/* Flags for VG469_VSELECT */
+#define VG469_VSEL_VCC 0x03
+#define VG469_VSEL_5V 0x00
+#define VG469_VSEL_3V 0x03
+#define VG469_VSEL_MAX 0x0c
+#define VG469_VSEL_EXT_STAT 0x10
+#define VG469_VSEL_EXT_BUS 0x20
+#define VG469_VSEL_MIXED 0x40
+#define VG469_VSEL_ISA 0x80
+
+/* Flags for VG468_CTL */
+#define VG468_CTL_SLOW 0x01 /* 600ns memory timing */
+#define VG468_CTL_ASYNC 0x02 /* Asynchronous bus clocking */
+#define VG468_CTL_TSSI 0x08 /* Tri-state some outputs */
+#define VG468_CTL_DELAY 0x10 /* Card detect debounce */
+#define VG468_CTL_INPACK 0x20 /* Obey INPACK signal? */
+#define VG468_CTL_POLARITY 0x40 /* VCCEN polarity */
+#define VG468_CTL_COMPAT 0x80 /* Compatibility stuff */
+
+#define VG469_CTL_WS_COMPAT 0x04 /* Wait state compatibility */
+#define VG469_CTL_STRETCH 0x10 /* LED stretch */
+
+/* Flags for VG468_TIMER */
+#define VG468_TIMER_ZEROPWR 0x10 /* Zero power control */
+#define VG468_TIMER_SIGEN 0x20 /* Power up */
+#define VG468_TIMER_STATUS 0x40 /* Activity timer status */
+#define VG468_TIMER_RES 0x80 /* Timer resolution */
+#define VG468_TIMER_MASK 0x0f /* Activity timer timeout */
+
+/* Flags for VG468_MISC */
+#define VG468_MISC_GPIO 0x04 /* General-purpose IO */
+#define VG468_MISC_DMAWSB 0x08 /* DMA wait state control */
+#define VG469_MISC_LEDENA 0x10 /* LED enable */
+#define VG468_MISC_VADEMREV 0x40 /* Vadem revision control */
+#define VG468_MISC_UNLOCK 0x80 /* Unique register lock */
+
+/* Flags for VG469_EXT_MODE_A */
+#define VG469_MODE_VPPST 0x03 /* Vpp steering control */
+#define VG469_MODE_INT_SENSE 0x04 /* Internal voltage sense */
+#define VG469_MODE_CABLE 0x08
+#define VG469_MODE_COMPAT 0x10 /* i82365sl B or DF step */
+#define VG469_MODE_TEST 0x20
+#define VG469_MODE_RIO 0x40 /* Steer RIO to INTR? */
+
+/* Flags for VG469_EXT_MODE_B */
+#define VG469_MODE_B_3V 0x01 /* 3.3v for socket B */
+
+#endif /* _LINUX_VG468_H */
+
+
+//*****************************************************************************
+//*****************************************************************************
+//*****************************************************************************
+//*****************************************************************************
+//*****************************************************************************
+// Beginning ricoh.h (RICOH chipsets)
+
+#ifndef _LINUX_RICOH_H
+#define _LINUX_RICOH_H
+
+
+#define RF5C_MODE_CTL 0x1f /* Mode control */
+#define RF5C_PWR_CTL 0x2f /* Mixed voltage control */
+#define RF5C_CHIP_ID 0x3a /* Chip identification */
+#define RF5C_MODE_CTL_3 0x3b /* Mode control 3 */
+
+/* I/O window address offset */
+#define RF5C_IO_OFF(w) (0x36+((w)<<1))
+
+/* Flags for RF5C_MODE_CTL */
+#define RF5C_MODE_ATA 0x01 /* ATA mode */
+#define RF5C_MODE_LED_ENA 0x02 /* IRQ 12 is LED */
+#define RF5C_MODE_CA21 0x04
+#define RF5C_MODE_CA22 0x08
+#define RF5C_MODE_CA23 0x10
+#define RF5C_MODE_CA24 0x20
+#define RF5C_MODE_CA25 0x40
+#define RF5C_MODE_3STATE_BIT7 0x80
+
+/* Flags for RF5C_PWR_CTL */
+#define RF5C_PWR_VCC_3V 0x01
+#define RF5C_PWR_IREQ_HIGH 0x02
+#define RF5C_PWR_INPACK_ENA 0x04
+#define RF5C_PWR_5V_DET 0x08
+#define RF5C_PWR_TC_SEL 0x10 /* Terminal Count: irq 11 or 15 */
+#define RF5C_PWR_DREQ_LOW 0x20
+#define RF5C_PWR_DREQ_OFF 0x00 /* DREQ steering control */
+#define RF5C_PWR_DREQ_INPACK 0x40
+#define RF5C_PWR_DREQ_SPKR 0x80
+#define RF5C_PWR_DREQ_IOIS16 0xc0
+
+/* Values for RF5C_CHIP_ID */
+#define RF5C_CHIP_RF5C296 0x32
+#define RF5C_CHIP_RF5C396 0xb2
+
+/* Flags for RF5C_MODE_CTL_3 */
+#define RF5C_MCTL3_DISABLE 0x01 /* Disable PCMCIA interface */
+#define RF5C_MCTL3_DMA_ENA 0x02
+
+/* Register definitions for Ricoh PCI-to-CardBus bridges */
+
+/* Extra bits in CB_BRIDGE_CONTROL */
+#define RL5C46X_BCR_3E0_ENA 0x0800
+#define RL5C46X_BCR_3E2_ENA 0x1000
+
+/* Bridge Configuration Register */
+#define RL5C4XX_CONFIG 0x80 /* 16 bit */
+#define RL5C4XX_CONFIG_IO_1_MODE 0x0200
+#define RL5C4XX_CONFIG_IO_0_MODE 0x0100
+#define RL5C4XX_CONFIG_PREFETCH 0x0001
+
+
+/* Misc Control Register */
+#define RL5C4XX_MISC 0x0082 /* 16 bit */
+#define RL5C4XX_MISC_HW_SUSPEND_ENA 0x0002
+#define RL5C4XX_MISC_VCCEN_POL 0x0100
+#define RL5C4XX_MISC_VPPEN_POL 0x0200
+#define RL5C46X_MISC_SUSPEND 0x0001
+#define RL5C46X_MISC_PWR_SAVE_2 0x0004
+#define RL5C46X_MISC_IFACE_BUSY 0x0008
+#define RL5C46X_MISC_B_LOCK 0x0010
+#define RL5C46X_MISC_A_LOCK 0x0020
+#define RL5C46X_MISC_PCI_LOCK 0x0040
+#define RL5C47X_MISC_IFACE_BUSY 0x0004
+#define RL5C47X_MISC_PCI_INT_MASK 0x0018
+#define RL5C47X_MISC_PCI_INT_DIS 0x0020
+#define RL5C47X_MISC_SUBSYS_WR 0x0040
+#define RL5C47X_MISC_SRIRQ_ENA 0x0080
+#define RL5C47X_MISC_5V_DISABLE 0x0400
+#define RL5C47X_MISC_LED_POL 0x0800
+
+/* 16-bit Interface Control Register */
+#define RL5C4XX_16BIT_CTL 0x0084 /* 16 bit */
+#define RL5C4XX_16CTL_IO_TIMING 0x0100
+#define RL5C4XX_16CTL_MEM_TIMING 0x0200
+#define RL5C46X_16CTL_LEVEL_1 0x0010
+#define RL5C46X_16CTL_LEVEL_2 0x0020
+
+/* 16-bit IO and memory timing registers */
+#define RL5C4XX_16BIT_IO_0 0x0088 /* 16 bit */
+#define RL5C4XX_16BIT_MEM_0 0x0088 /* 16 bit */
+#define RL5C4XX_SETUP_MASK 0x0007
+#define RL5C4XX_SETUP_SHIFT 0
+#define RL5C4XX_CMD_MASK 0x01f0
+#define RL5C4XX_CMD_SHIFT 4
+#define RL5C4XX_HOLD_MASK 0x1c00
+#define RL5C4XX_HOLD_SHIFT 10
+#define RL5C4XX_MISC_CONTROL 0x2F /* 8 bit */
+#define RL5C4XX_ZV_ENABLE 0x08
+
+#endif /* _LINUX_RICOH_H */
+
+
+//*****************************************************************************
+//*****************************************************************************
+//*****************************************************************************
+//*****************************************************************************
+//*****************************************************************************
+// Beginning cirrus.h (CIRRUS chipsets)
+
+#ifndef _LINUX_CIRRUS_H
+#define _LINUX_CIRRUS_H
+
+#ifndef PCI_VENDOR_ID_CIRRUS
+#define PCI_VENDOR_ID_CIRRUS 0x1013
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_6729
+#define PCI_DEVICE_ID_CIRRUS_6729 0x1100
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_6832
+#define PCI_DEVICE_ID_CIRRUS_6832 0x1110
+#endif
+
+#define PD67_MISC_CTL_1 0x16 /* Misc control 1 */
+#define PD67_FIFO_CTL 0x17 /* FIFO control */
+#define PD67_MISC_CTL_2 0x1E /* Misc control 2 */
+#define PD67_CHIP_INFO 0x1f /* Chip information */
+#define PD67_ATA_CTL 0x026 /* 6730: ATA control */
+#define PD67_EXT_INDEX 0x2e /* Extension index */
+#define PD67_EXT_DATA 0x2f /* Extension data */
+
+/* PD6722 extension registers -- indexed in PD67_EXT_INDEX */
+#define PD67_DATA_MASK0 0x01 /* Data mask 0 */
+#define PD67_DATA_MASK1 0x02 /* Data mask 1 */
+#define PD67_DMA_CTL 0x03 /* DMA control */
+
+/* PD6730 extension registers -- indexed in PD67_EXT_INDEX */
+#define PD67_EXT_CTL_1 0x03 /* Extension control 1 */
+#define PD67_MEM_PAGE(n) ((n)+5) /* PCI window bits 31:24 */
+#define PD67_EXTERN_DATA 0x0a
+#define PD67_MISC_CTL_3 0x25
+#define PD67_SMB_PWR_CTL 0x26
+
+/* I/O window address offset */
+#define PD67_IO_OFF(w) (0x36+((w)<<1))
+
+/* Timing register sets */
+#define PD67_TIME_SETUP(n) (0x3a + 3*(n))
+#define PD67_TIME_CMD(n) (0x3b + 3*(n))
+#define PD67_TIME_RECOV(n) (0x3c + 3*(n))
+
+/* Flags for PD67_MISC_CTL_1 */
+#define PD67_MC1_5V_DET 0x01 /* 5v detect */
+#define PD67_MC1_MEDIA_ENA 0x01 /* 6730: Multimedia enable */
+#define PD67_MC1_VCC_3V 0x02 /* 3.3v Vcc */
+#define PD67_MC1_PULSE_MGMT 0x04
+#define PD67_MC1_PULSE_IRQ 0x08
+#define PD67_MC1_SPKR_ENA 0x10
+#define PD67_MC1_INPACK_ENA 0x80
+
+/* Flags for PD67_FIFO_CTL */
+#define PD67_FIFO_EMPTY 0x80
+
+/* Flags for PD67_MISC_CTL_2 */
+#define PD67_MC2_FREQ_BYPASS 0x01
+#define PD67_MC2_DYNAMIC_MODE 0x02
+#define PD67_MC2_SUSPEND 0x04
+#define PD67_MC2_5V_CORE 0x08
+#define PD67_MC2_LED_ENA 0x10 /* IRQ 12 is LED enable */
+#define PD67_MC2_FAST_PCI 0x10 /* 6729: PCI bus > 25 MHz */
+#define PD67_MC2_3STATE_BIT7 0x20 /* Floppy change bit */
+#define PD67_MC2_DMA_MODE 0x40
+#define PD67_MC2_IRQ15_RI 0x80 /* IRQ 15 is ring enable */
+
+/* Flags for PD67_CHIP_INFO */
+#define PD67_INFO_SLOTS 0x20 /* 0 = 1 slot, 1 = 2 slots */
+#define PD67_INFO_CHIP_ID 0xc0
+#define PD67_INFO_REV 0x1c
+
+/* Fields in PD67_TIME_* registers */
+#define PD67_TIME_SCALE 0xc0
+#define PD67_TIME_SCALE_1 0x00
+#define PD67_TIME_SCALE_16 0x40
+#define PD67_TIME_SCALE_256 0x80
+#define PD67_TIME_SCALE_4096 0xc0
+#define PD67_TIME_MULT 0x3f
+
+/* Fields in PD67_DMA_CTL */
+#define PD67_DMA_MODE 0xc0
+#define PD67_DMA_OFF 0x00
+#define PD67_DMA_DREQ_INPACK 0x40
+#define PD67_DMA_DREQ_WP 0x80
+#define PD67_DMA_DREQ_BVD2 0xc0
+#define PD67_DMA_PULLUP 0x20 /* Disable socket pullups? */
+
+/* Fields in PD67_EXT_CTL_1 */
+#define PD67_EC1_VCC_PWR_LOCK 0x01
+#define PD67_EC1_AUTO_PWR_CLEAR 0x02
+#define PD67_EC1_LED_ENA 0x04
+#define PD67_EC1_INV_CARD_IRQ 0x08
+#define PD67_EC1_INV_MGMT_IRQ 0x10
+#define PD67_EC1_PULLUP_CTL 0x20
+
+/* Fields in PD67_MISC_CTL_3 */
+#define PD67_MC3_IRQ_MASK 0x03
+#define PD67_MC3_IRQ_PCPCI 0x00
+#define PD67_MC3_IRQ_EXTERN 0x01
+#define PD67_MC3_IRQ_PCIWAY 0x02
+#define PD67_MC3_IRQ_PCI 0x03
+#define PD67_MC3_PWR_MASK 0x0c
+#define PD67_MC3_PWR_SERIAL 0x00
+#define PD67_MC3_PWR_TI2202 0x08
+#define PD67_MC3_PWR_SMB 0x0c
+
+/* Register definitions for Cirrus PD6832 PCI-to-CardBus bridge */
+
+/* PD6832 extension registers -- indexed in PD67_EXT_INDEX */
+#define PD68_EXT_CTL_2 0x0b
+#define PD68_PCI_SPACE 0x22
+#define PD68_PCCARD_SPACE 0x23
+#define PD68_WINDOW_TYPE 0x24
+#define PD68_EXT_CSC 0x2e
+#define PD68_MISC_CTL_4 0x2f
+#define PD68_MISC_CTL_5 0x30
+#define PD68_MISC_CTL_6 0x31
+
+/* Extra flags in PD67_MISC_CTL_3 */
+#define PD68_MC3_HW_SUSP 0x10
+#define PD68_MC3_MM_EXPAND 0x40
+#define PD68_MC3_MM_ARM 0x80
+
+/* Bridge Control Register */
+#define PD6832_BCR_MGMT_IRQ_ENA 0x0800
+
+/* Socket Number Register */
+#define PD6832_SOCKET_NUMBER 0x004c /* 8 bit */
+
+#endif /* _LINUX_CIRRUS_H */
+
+
+
diff --git a/gpxe/src/include/igmp.h b/gpxe/src/include/igmp.h
new file mode 100644
index 00000000..8b3292f2
--- /dev/null
+++ b/gpxe/src/include/igmp.h
@@ -0,0 +1,42 @@
+#ifndef IGMP_H
+#define IGMP_H
+
+#include "stdint.h"
+#include <gpxe/in.h>
+
+#define IGMP_QUERY 0x11
+#define IGMPv1_REPORT 0x12
+#define IGMPv2_REPORT 0x16
+#define IGMP_LEAVE 0x17
+#define GROUP_ALL_HOSTS 0xe0000001 /* 224.0.0.1 Host byte order */
+
+#define MULTICAST_MASK 0xf0000000
+#define MULTICAST_NETWORK 0xe0000000
+
+enum {
+ IGMP_SERVER,
+ MAX_IGMP
+};
+
+struct igmp {
+ uint8_t type;
+ uint8_t response_time;
+ uint16_t chksum;
+ struct in_addr group;
+} PACKED;
+
+struct igmp_ip_t { /* Format of an igmp ip packet */
+ struct iphdr ip;
+ uint8_t router_alert[4]; /* Router alert option */
+ struct igmp igmp;
+} PACKED;
+
+struct igmptable_t {
+ struct in_addr group;
+ unsigned long time;
+} PACKED;
+
+extern void join_group ( int slot, unsigned long group );
+extern void leave_group ( int slot );
+
+#endif /* IGMP_H */
diff --git a/gpxe/src/include/lib.h b/gpxe/src/include/lib.h
new file mode 100644
index 00000000..400ea468
--- /dev/null
+++ b/gpxe/src/include/lib.h
@@ -0,0 +1,42 @@
+#ifndef LIB_H
+#define LIB_H
+
+#include <stdint.h>
+
+int getline(char *buf, int max);
+
+extern struct pci_device *dev_list;
+extern int n_devs;
+
+extern void pci_init(void);
+extern struct pci_device *pci_find_device(int vendor, int device, int devclass,
+int prog_if, int index);
+
+void *calloc(size_t nmemb, size_t size);
+void *realloc(void *ptr, size_t size);
+
+char *strdup(const char *s);
+
+int isspace(int c);
+
+unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base);
+unsigned long long strtoull_with_suffix(const char *cp,char **endp,unsigned int base);
+
+unsigned int get_le32(const unsigned char *);
+unsigned int get_le16(const unsigned char *);
+void hexdump(const void *p, unsigned int len);
+
+long long simple_strtoll(const char *cp,char **endp,unsigned int base);
+
+#define LOADER_NOT_SUPPORT 0xbadf11e
+
+struct sys_info;
+int elf_load(struct sys_info *, const char *filename, const char *cmdline);
+
+#if LINUX_LOADER
+int linux_load(struct sys_info *, const char *filename, const char *cmdline);
+#else
+#define linux_load(x,y,z) LOADER_NOT_SUPPORT /* nop */
+#endif
+
+#endif /* LIB_H */
diff --git a/gpxe/src/include/libgen.h b/gpxe/src/include/libgen.h
new file mode 100644
index 00000000..56a2f760
--- /dev/null
+++ b/gpxe/src/include/libgen.h
@@ -0,0 +1,7 @@
+#ifndef _LIBGEN_H
+#define _LIBGEN_H
+
+extern char * basename ( char *path );
+extern char * dirname ( char *path );
+
+#endif /* _LIBGEN_H */
diff --git a/gpxe/src/include/little_bswap.h b/gpxe/src/include/little_bswap.h
new file mode 100644
index 00000000..e4f83753
--- /dev/null
+++ b/gpxe/src/include/little_bswap.h
@@ -0,0 +1,33 @@
+#ifndef ETHERBOOT_LITTLE_BSWAP_H
+#define ETHERBOOT_LITTLE_BSWAP_H
+
+#define ntohl(x) __bswap_32(x)
+#define htonl(x) __bswap_32(x)
+#define ntohs(x) __bswap_16(x)
+#define htons(x) __bswap_16(x)
+#define cpu_to_le64(x) (x)
+#define cpu_to_le32(x) (x)
+#define cpu_to_le16(x) (x)
+#define cpu_to_be64(x) __bswap_64(x)
+#define cpu_to_be32(x) __bswap_32(x)
+#define cpu_to_be16(x) __bswap_16(x)
+#define le64_to_cpu(x) (x)
+#define le32_to_cpu(x) (x)
+#define le16_to_cpu(x) (x)
+#define be64_to_cpu(x) __bswap_64(x)
+#define be32_to_cpu(x) __bswap_32(x)
+#define be16_to_cpu(x) __bswap_16(x)
+#define cpu_to_le64s(x) do {} while (0)
+#define cpu_to_le32s(x) do {} while (0)
+#define cpu_to_le16s(x) do {} while (0)
+#define cpu_to_be64s(x) __bswap_64s(x)
+#define cpu_to_be32s(x) __bswap_32s(x)
+#define cpu_to_be16s(x) __bswap_16s(x)
+#define le64_to_cpus(x) do {} while (0)
+#define le32_to_cpus(x) do {} while (0)
+#define le16_to_cpus(x) do {} while (0)
+#define be64_to_cpus(x) __bswap_64s(x)
+#define be32_to_cpus(x) __bswap_32s(x)
+#define be16_to_cpus(x) __bswap_16s(x)
+
+#endif /* ETHERBOOT_LITTLE_BSWAP_H */
diff --git a/gpxe/src/include/mii.h b/gpxe/src/include/mii.h
new file mode 100644
index 00000000..34c1ca9b
--- /dev/null
+++ b/gpxe/src/include/mii.h
@@ -0,0 +1,105 @@
+/*
+ * linux/mii.h: definitions for MII-compatible transceivers
+ * Originally drivers/net/sunhme.h.
+ *
+ * Copyright (C) 1996, 1999, 2001 David S. Miller (davem@redhat.com)
+ *
+ * Copied Form Linux 2.4.25 an unneeded items removed by:
+ * Timothy Legge (timlegge at etherboot dot org)
+ *
+ * 03/26/2004
+ */
+
+/* Generic MII registers. */
+
+#define MII_BMCR 0x00 /* Basic mode control register */
+#define MII_BMSR 0x01 /* Basic mode status register */
+#define MII_PHYSID1 0x02 /* PHYS ID 1 */
+#define MII_PHYSID2 0x03 /* PHYS ID 2 */
+#define MII_ADVERTISE 0x04 /* Advertisement control reg */
+#define MII_LPA 0x05 /* Link partner ability reg */
+#define MII_EXPANSION 0x06 /* Expansion register */
+#define MII_DCOUNTER 0x12 /* Disconnect counter */
+#define MII_FCSCOUNTER 0x13 /* False carrier counter */
+#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
+#define MII_RERRCOUNTER 0x15 /* Receive error counter */
+#define MII_SREVISION 0x16 /* Silicon revision */
+#define MII_RESV1 0x17 /* Reserved... */
+#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */
+#define MII_PHYADDR 0x19 /* PHY address */
+#define MII_RESV2 0x1a /* Reserved... */
+#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
+#define MII_NCONFIG 0x1c /* Network interface config */
+
+/* Basic mode control register. */
+#define BMCR_RESV 0x007f /* Unused... */
+#define BMCR_CTST 0x0080 /* Collision test */
+#define BMCR_FULLDPLX 0x0100 /* Full duplex */
+#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
+#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */
+#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */
+#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */
+#define BMCR_SPEED100 0x2000 /* Select 100Mbps */
+#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */
+#define BMCR_RESET 0x8000 /* Reset the DP83840 */
+
+/* Basic mode status register. */
+#define BMSR_ERCAP 0x0001 /* Ext-reg capability */
+#define BMSR_JCD 0x0002 /* Jabber detected */
+#define BMSR_LSTATUS 0x0004 /* Link status */
+#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */
+#define BMSR_RFAULT 0x0010 /* Remote fault detected */
+#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
+#define BMSR_RESV 0x07c0 /* Unused... */
+#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */
+#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */
+#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */
+#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */
+#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */
+
+/* Advertisement control register. */
+#define ADVERTISE_SLCT 0x001f /* Selector bits */
+#define ADVERTISE_CSMA 0x0001 /* Only selector supported */
+#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
+#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
+#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */
+#define ADVERTISE_RESV 0x1c00 /* Unused... */
+#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */
+#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */
+#define ADVERTISE_NPAGE 0x8000 /* Next page bit */
+
+#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
+ ADVERTISE_CSMA)
+#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
+ ADVERTISE_100HALF | ADVERTISE_100FULL)
+
+/* Link partner ability register. */
+#define LPA_SLCT 0x001f /* Same as advertise selector */
+#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */
+#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */
+#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */
+#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */
+#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */
+#define LPA_RESV 0x1c00 /* Unused... */
+#define LPA_RFAULT 0x2000 /* Link partner faulted */
+#define LPA_LPACK 0x4000 /* Link partner acked us */
+#define LPA_NPAGE 0x8000 /* Next page bit */
+
+#define LPA_DUPLEX (LPA_10FULL | LPA_100FULL)
+#define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4)
+
+/* Expansion register for auto-negotiation. */
+#define EXPANSION_NWAY 0x0001 /* Can do N-way auto-nego */
+#define EXPANSION_LCWP 0x0002 /* Got new RX page code word */
+#define EXPANSION_ENABLENPAGE 0x0004 /* This enables npage words */
+#define EXPANSION_NPCAPABLE 0x0008 /* Link partner supports npage */
+#define EXPANSION_MFAULTS 0x0010 /* Multiple faults detected */
+#define EXPANSION_RESV 0xffe0 /* Unused... */
+
+/* N-way test register. */
+#define NWAYTEST_RESV1 0x00ff /* Unused... */
+#define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */
+#define NWAYTEST_RESV2 0xfe00 /* Unused... */
+
diff --git a/gpxe/src/include/nfs.h b/gpxe/src/include/nfs.h
new file mode 100644
index 00000000..0877bb66
--- /dev/null
+++ b/gpxe/src/include/nfs.h
@@ -0,0 +1,63 @@
+#ifndef _NFS_H
+#define _NFS_H
+
+#define SUNRPC_PORT 111
+
+#define PROG_PORTMAP 100000
+#define PROG_NFS 100003
+#define PROG_MOUNT 100005
+
+#define MSG_CALL 0
+#define MSG_REPLY 1
+
+#define PORTMAP_GETPORT 3
+
+#define MOUNT_ADDENTRY 1
+#define MOUNT_UMOUNTALL 4
+
+#define NFS_LOOKUP 4
+#define NFS_READLINK 5
+#define NFS_READ 6
+
+#define NFS_FHSIZE 32
+
+#define NFSERR_PERM 1
+#define NFSERR_NOENT 2
+#define NFSERR_ACCES 13
+#define NFSERR_ISDIR 21
+#define NFSERR_INVAL 22
+
+/* Block size used for NFS read accesses. A RPC reply packet (including all
+ * headers) must fit within a single Ethernet frame to avoid fragmentation.
+ * Chosen to be a power of two, as most NFS servers are optimized for this. */
+#define NFS_READ_SIZE 1024
+
+#define NFS_MAXLINKDEPTH 16
+
+struct rpc_t {
+ struct iphdr ip;
+ struct udphdr udp;
+ union {
+ uint8_t data[300]; /* longest RPC call must fit!!!! */
+ struct {
+ uint32_t id;
+ uint32_t type;
+ uint32_t rpcvers;
+ uint32_t prog;
+ uint32_t vers;
+ uint32_t proc;
+ uint32_t data[1];
+ } call;
+ struct {
+ uint32_t id;
+ uint32_t type;
+ uint32_t rstatus;
+ uint32_t verifier;
+ uint32_t v2;
+ uint32_t astatus;
+ uint32_t data[1];
+ } reply;
+ } u;
+} PACKED;
+
+#endif /* _NFS_H */
diff --git a/gpxe/src/include/nic.h b/gpxe/src/include/nic.h
new file mode 100644
index 00000000..65e4be71
--- /dev/null
+++ b/gpxe/src/include/nic.h
@@ -0,0 +1,272 @@
+ /*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#ifndef NIC_H
+#define NIC_H
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <byteswap.h>
+#include <gpxe/pci.h>
+#include <gpxe/isapnp.h>
+#include <gpxe/isa.h>
+#include <gpxe/eisa.h>
+#include <gpxe/mca.h>
+#include "dhcp.h"
+
+typedef enum {
+ DISABLE = 0,
+ ENABLE,
+ FORCE
+} irq_action_t;
+
+typedef enum duplex {
+ HALF_DUPLEX = 1,
+ FULL_DUPLEX
+} duplex_t;
+
+/*
+ * Structure returned from eth_probe and passed to other driver
+ * functions.
+ */
+struct nic {
+ struct nic_operations *nic_op;
+ int flags; /* driver specific flags */
+ unsigned char *node_addr;
+ unsigned char *packet;
+ unsigned int packetlen;
+ unsigned int ioaddr;
+ unsigned char irqno;
+ unsigned int mbps;
+ duplex_t duplex;
+ struct dhcp_dev_id dhcp_dev_id;
+ void *priv_data; /* driver private data */
+};
+
+struct nic_operations {
+ int ( *connect ) ( struct nic * );
+ int ( *poll ) ( struct nic *, int retrieve );
+ void ( *transmit ) ( struct nic *, const char *,
+ unsigned int, unsigned int, const char * );
+ void ( *irq ) ( struct nic *, irq_action_t );
+};
+
+extern struct nic nic;
+
+static inline int eth_poll ( int retrieve ) {
+ return nic.nic_op->poll ( &nic, retrieve );
+}
+
+static inline void eth_transmit ( const char *dest, unsigned int type,
+ unsigned int size, const void *packet ) {
+ nic.nic_op->transmit ( &nic, dest, type, size, packet );
+}
+
+/*
+ * Function prototypes
+ *
+ */
+extern int dummy_connect ( struct nic *nic );
+extern void dummy_irq ( struct nic *nic, irq_action_t irq_action );
+extern int legacy_probe ( void *hwdev,
+ void ( * set_drvdata ) ( void *hwdev, void *priv ),
+ struct device *dev,
+ int ( * probe ) ( struct nic *nic, void *hwdev ),
+ void ( * disable ) ( struct nic *nic, void *hwdev ));
+void legacy_remove ( void *hwdev,
+ void * ( * get_drvdata ) ( void *hwdev ),
+ void ( * disable ) ( struct nic *nic, void *hwdev ) );
+
+#define PCI_DRIVER(_name,_ids,_class) \
+ static inline int \
+ _name ## _pci_legacy_probe ( struct pci_device *pci, \
+ const struct pci_device_id *id ); \
+ static inline void \
+ _name ## _pci_legacy_remove ( struct pci_device *pci ); \
+ struct pci_driver _name __pci_driver = { \
+ .ids = _ids, \
+ .id_count = sizeof ( _ids ) / sizeof ( _ids[0] ), \
+ .probe = _name ## _pci_legacy_probe, \
+ .remove = _name ## _pci_legacy_remove, \
+ }; \
+ REQUIRE_OBJECT ( pci );
+
+static inline void legacy_pci_set_drvdata ( void *hwdev, void *priv ) {
+ pci_set_drvdata ( hwdev, priv );
+}
+static inline void * legacy_pci_get_drvdata ( void *hwdev ) {
+ return pci_get_drvdata ( hwdev );
+}
+
+#define ISAPNP_DRIVER(_name,_ids) \
+ static inline int \
+ _name ## _isapnp_legacy_probe ( struct isapnp_device *isapnp, \
+ const struct isapnp_device_id *id ); \
+ static inline void \
+ _name ## _isapnp_legacy_remove ( struct isapnp_device *isapnp ); \
+ struct isapnp_driver _name __isapnp_driver = { \
+ .ids = _ids, \
+ .id_count = sizeof ( _ids ) / sizeof ( _ids[0] ), \
+ .probe = _name ## _isapnp_legacy_probe, \
+ .remove = _name ## _isapnp_legacy_remove, \
+ }; \
+ REQUIRE_OBJECT ( isapnp );
+
+static inline void legacy_isapnp_set_drvdata ( void *hwdev, void *priv ) {
+ isapnp_set_drvdata ( hwdev, priv );
+}
+static inline void * legacy_isapnp_get_drvdata ( void *hwdev ) {
+ return isapnp_get_drvdata ( hwdev );
+}
+
+#define EISA_DRIVER(_name,_ids) \
+ static inline int \
+ _name ## _eisa_legacy_probe ( struct eisa_device *eisa, \
+ const struct eisa_device_id *id ); \
+ static inline void \
+ _name ## _eisa_legacy_remove ( struct eisa_device *eisa ); \
+ struct eisa_driver _name __eisa_driver = { \
+ .ids = _ids, \
+ .id_count = sizeof ( _ids ) / sizeof ( _ids[0] ), \
+ .probe = _name ## _eisa_legacy_probe, \
+ .remove = _name ## _eisa_legacy_remove, \
+ }; \
+ REQUIRE_OBJECT ( eisa );
+
+static inline void legacy_eisa_set_drvdata ( void *hwdev, void *priv ) {
+ eisa_set_drvdata ( hwdev, priv );
+}
+static inline void * legacy_eisa_get_drvdata ( void *hwdev ) {
+ return eisa_get_drvdata ( hwdev );
+}
+
+#define MCA_DRIVER(_name,_ids) \
+ static inline int \
+ _name ## _mca_legacy_probe ( struct mca_device *mca, \
+ const struct mca_device_id *id ); \
+ static inline void \
+ _name ## _mca_legacy_remove ( struct mca_device *mca ); \
+ struct mca_driver _name __mca_driver = { \
+ .ids = _ids, \
+ .id_count = sizeof ( _ids ) / sizeof ( _ids[0] ), \
+ .probe = _name ## _mca_legacy_probe, \
+ .remove = _name ## _mca_legacy_remove, \
+ }; \
+ REQUIRE_OBJECT ( mca );
+
+static inline void legacy_mca_set_drvdata ( void *hwdev, void *priv ) {
+ mca_set_drvdata ( hwdev, priv );
+}
+static inline void * legacy_mca_get_drvdata ( void *hwdev ) {
+ return mca_get_drvdata ( hwdev );
+}
+
+#define ISA_DRIVER(_name,_probe_addrs,_probe_addr,_vendor_id,_prod_id) \
+ static inline int \
+ _name ## _isa_legacy_probe ( struct isa_device *isa ); \
+ static inline int \
+ _name ## _isa_legacy_probe_at_addr ( struct isa_device *isa ) { \
+ if ( ! _probe_addr ( isa->ioaddr ) ) \
+ return -ENODEV; \
+ return _name ## _isa_legacy_probe ( isa ); \
+ } \
+ static inline void \
+ _name ## _isa_legacy_remove ( struct isa_device *isa ); \
+ static const char _name ## _text[]; \
+ struct isa_driver _name __isa_driver = { \
+ .name = _name ## _text, \
+ .probe_addrs = _probe_addrs, \
+ .addr_count = ( sizeof ( _probe_addrs ) / \
+ sizeof ( _probe_addrs[0] ) ), \
+ .vendor_id = _vendor_id, \
+ .prod_id = _prod_id, \
+ .probe = _name ## _isa_legacy_probe_at_addr, \
+ .remove = _name ## _isa_legacy_remove, \
+ }; \
+ REQUIRE_OBJECT ( isa );
+
+static inline void legacy_isa_set_drvdata ( void *hwdev, void *priv ) {
+ isa_set_drvdata ( hwdev, priv );
+}
+static inline void * legacy_isa_get_drvdata ( void *hwdev ) {
+ return isa_get_drvdata ( hwdev );
+}
+
+#undef DRIVER
+#define DRIVER(_name_text,_unused2,_unused3,_name,_probe,_disable) \
+ static const char _name ## _text[] = _name_text; \
+ static inline int \
+ _name ## _probe ( struct nic *nic, void *hwdev ) { \
+ return _probe ( nic, hwdev ); \
+ } \
+ static inline void \
+ _name ## _disable ( struct nic *nic, void *hwdev ) { \
+ void ( * _unsafe_disable ) () = _disable; \
+ _unsafe_disable ( nic, hwdev ); \
+ } \
+ static inline int \
+ _name ## _pci_legacy_probe ( struct pci_device *pci, \
+ const struct pci_device_id *id __unused ) { \
+ return legacy_probe ( pci, legacy_pci_set_drvdata, \
+ &pci->dev, _name ## _probe, \
+ _name ## _disable ); \
+ } \
+ static inline void \
+ _name ## _pci_legacy_remove ( struct pci_device *pci ) { \
+ return legacy_remove ( pci, legacy_pci_get_drvdata, \
+ _name ## _disable ); \
+ } \
+ static inline int \
+ _name ## _isapnp_legacy_probe ( struct isapnp_device *isapnp, \
+ const struct isapnp_device_id *id __unused ) { \
+ return legacy_probe ( isapnp, legacy_isapnp_set_drvdata, \
+ &isapnp->dev, _name ## _probe, \
+ _name ## _disable ); \
+ } \
+ static inline void \
+ _name ## _isapnp_legacy_remove ( struct isapnp_device *isapnp ) { \
+ return legacy_remove ( isapnp, legacy_isapnp_get_drvdata, \
+ _name ## _disable ); \
+ } \
+ static inline int \
+ _name ## _eisa_legacy_probe ( struct eisa_device *eisa, \
+ const struct eisa_device_id *id __unused ) { \
+ return legacy_probe ( eisa, legacy_eisa_set_drvdata, \
+ &eisa->dev, _name ## _probe, \
+ _name ## _disable ); \
+ } \
+ static inline void \
+ _name ## _eisa_legacy_remove ( struct eisa_device *eisa ) { \
+ return legacy_remove ( eisa, legacy_eisa_get_drvdata, \
+ _name ## _disable ); \
+ } \
+ static inline int \
+ _name ## _mca_legacy_probe ( struct mca_device *mca, \
+ const struct mca_device_id *id __unused ) { \
+ return legacy_probe ( mca, legacy_mca_set_drvdata, \
+ &mca->dev, _name ## _probe, \
+ _name ## _disable ); \
+ } \
+ static inline void \
+ _name ## _mca_legacy_remove ( struct mca_device *mca ) { \
+ return legacy_remove ( mca, legacy_mca_get_drvdata, \
+ _name ## _disable ); \
+ } \
+ static inline int \
+ _name ## _isa_legacy_probe ( struct isa_device *isa ) { \
+ return legacy_probe ( isa, legacy_isa_set_drvdata, \
+ &isa->dev, _name ## _probe, \
+ _name ## _disable ); \
+ } \
+ static inline void \
+ _name ## _isa_legacy_remove ( struct isa_device *isa ) { \
+ return legacy_remove ( isa, legacy_isa_get_drvdata, \
+ _name ## _disable ); \
+ }
+
+#endif /* NIC_H */
diff --git a/gpxe/src/include/nmb.h b/gpxe/src/include/nmb.h
new file mode 100644
index 00000000..3e551ffd
--- /dev/null
+++ b/gpxe/src/include/nmb.h
@@ -0,0 +1,22 @@
+#ifndef NMB_H
+#define NMB_H
+
+#include <gpxe/dns.h>
+
+/*
+ * NetBIOS name query packets are basically the same as DNS packets,
+ * though the resource record format is different.
+ *
+ */
+
+#define DNS_TYPE_NB 0x20
+#define DNS_FLAG_BROADCAST ( 0x01 << 4 )
+#define NBNS_UDP_PORT 137
+
+struct dns_rr_info_nb {
+ struct dns_rr_info info;
+ uint16_t nb_flags;
+ struct in_addr nb_address;
+} __attribute__ (( packed ));
+
+#endif /* NMB_H */
diff --git a/gpxe/src/include/old_tcp.h b/gpxe/src/include/old_tcp.h
new file mode 100644
index 00000000..93e1485e
--- /dev/null
+++ b/gpxe/src/include/old_tcp.h
@@ -0,0 +1,37 @@
+#ifndef _TCP_H
+#define _TCP_H
+
+#define TCP_INITIAL_TIMEOUT (3*TICKS_PER_SEC)
+#define TCP_MAX_TIMEOUT (60*TICKS_PER_SEC)
+#define TCP_MIN_TIMEOUT (TICKS_PER_SEC)
+#define TCP_MAX_RETRY 10
+#define TCP_MAX_HEADER ((int)sizeof(struct iphdr)+64)
+#define TCP_MIN_WINDOW (1500-TCP_MAX_HEADER)
+#define TCP_MAX_WINDOW (65535-TCP_MAX_HEADER)
+
+#define FIN 1
+#define SYN 2
+#define RST 4
+#define PSH 8
+#define ACK 16
+#define URG 32
+
+
+struct tcphdr {
+ uint16_t src;
+ uint16_t dst;
+ int32_t seq;
+ int32_t ack;
+ uint16_t ctrl;
+ uint16_t window;
+ uint16_t chksum;
+ uint16_t urgent;
+};
+
+extern int tcp_transaction ( unsigned long destip, unsigned int destsock,
+ void *ptr,
+ int (*send)(int len, void *buf, void *ptr),
+ int (*recv)(int len, const void *buf, void *ptr));
+
+
+#endif /* _TCP_H */
diff --git a/gpxe/src/include/pc_kbd.h b/gpxe/src/include/pc_kbd.h
new file mode 100644
index 00000000..c125efa0
--- /dev/null
+++ b/gpxe/src/include/pc_kbd.h
@@ -0,0 +1,7 @@
+#ifndef _PC_KBD_H
+#define _PC_KBD_H
+
+int kbd_ischar(void);
+
+int kbd_getc(void);
+#endif
diff --git a/gpxe/src/include/pcmcia-opts.h b/gpxe/src/include/pcmcia-opts.h
new file mode 100644
index 00000000..70dc0921
--- /dev/null
+++ b/gpxe/src/include/pcmcia-opts.h
@@ -0,0 +1,23 @@
+// pcmcia-opts.h
+// special options file for development time. Later this could end in Config(?)
+#ifndef __pcmciaopts
+#define __pcmciaopts
+
+ #define _yes_ 1
+ #define _no_ 0
+
+ #define SUPPORT_I82365 (_yes_)
+// #define SUPPORT_YENTA (_no_)
+// #define SUPPORT_SOME_DRIVER (_no_)
+
+ #define PCMCIA_SHUTDOWN (_yes_)
+ #define MAP_ATTRMEM_TO 0xd0000
+ #define MAP_ATTRMEM_LEN 0x02000
+
+ #define PDEBUG 3
+ // The higher the more output you get, 0..3
+ // Not fully implemented though, but for the future...
+
+ #undef _yes_
+ #undef _no_
+#endif
diff --git a/gpxe/src/include/pcmcia.h b/gpxe/src/include/pcmcia.h
new file mode 100644
index 00000000..d528bea5
--- /dev/null
+++ b/gpxe/src/include/pcmcia.h
@@ -0,0 +1,156 @@
+// pcmcia.h - Header file for PCMCIA support
+
+#ifndef PCMCIA_H
+#define PCMCIA_H
+
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned int u_int;
+typedef unsigned long u_long;
+
+typedef u_short ioaddr_t;
+extern int sockets;
+
+#define MAXPCCSOCKS 8
+#define MAXPCCCONFIGS 8
+
+typedef enum ebpdriver_t { I82365, SOMEDRIVER } ebpdriver_t;
+typedef enum interface_func_t { INIT, SHUTDOWN, MAPATTRMEM, UNMAPATTRMEM, SELECTCONFIG } interface_func_t;
+typedef enum ebpstatus_t { EMPTY, HASCARD, INITIALIZED, SUSPENDED, OTHERDEVICE, UNKNOWN } ebpstatus_t;
+
+struct driver_interact_t {
+ ebpdriver_t id;
+ int (*f)(interface_func_t,int,int,int,int);
+ char *name;
+};
+struct pccsock_t {
+ ebpdriver_t device;
+ int drivernum;
+ ebpstatus_t status;
+ // Internal usage of the drivers:
+ int internalid;
+ int flags;
+ int ioaddr;
+ int type;
+ int configoffset;
+ int possibleconfignum;
+ int stringoffset;
+ u_int stringlength;
+ int rmask0;
+};
+
+extern struct pccsock_t pccsock[MAXPCCSOCKS];
+extern u_int pccsocks;
+
+struct pcc_config_t {
+ u_char index;
+ u_char irq;
+ int iowin;
+ int iolen;
+};
+
+
+int i82365_interfacer(interface_func_t,int,int,int,void *);
+void sleepticks(int);
+
+#define EINVAL 22
+
+
+//*********************************************************** cc.h:
+/* Definitions for card status flags for GetStatus */
+#define SS_WRPROT 0x0001
+#define SS_CARDLOCK 0x0002
+#define SS_EJECTION 0x0004
+#define SS_INSERTION 0x0008
+#define SS_BATDEAD 0x0010
+#define SS_BATWARN 0x0020
+#define SS_READY 0x0040
+#define SS_DETECT 0x0080
+#define SS_POWERON 0x0100
+#define SS_GPI 0x0200
+#define SS_STSCHG 0x0400
+#define SS_CARDBUS 0x0800
+#define SS_3VCARD 0x1000
+#define SS_XVCARD 0x2000
+#define SS_PENDING 0x4000
+
+/* cc.h: for InquireSocket */
+typedef struct socket_cap_t {
+ u_int features;
+ u_int irq_mask;
+ u_int map_size;
+ ioaddr_t io_offset;
+ u_char pci_irq;
+ //struct pci_dev *cb_dev;
+ //struct bus_operations *bus;
+ void *cb_dev;
+ void *bus;
+} socket_cap_t;
+/* InquireSocket capabilities */
+#define SS_CAP_PAGE_REGS 0x0001
+#define SS_CAP_VIRTUAL_BUS 0x0002
+#define SS_CAP_MEM_ALIGN 0x0004
+#define SS_CAP_STATIC_MAP 0x0008
+#define SS_CAP_PCCARD 0x4000
+#define SS_CAP_CARDBUS 0x8000
+
+/* for GetSocket, SetSocket */
+typedef struct socket_state_t {
+ u_int flags;
+ u_int csc_mask;
+ u_char Vcc, Vpp;
+ u_char io_irq;
+} socket_state_t;
+
+extern socket_state_t dead_socket;
+
+/* Socket configuration flags */
+#define SS_PWR_AUTO 0x0010
+#define SS_IOCARD 0x0020
+#define SS_RESET 0x0040
+#define SS_DMA_MODE 0x0080
+#define SS_SPKR_ENA 0x0100
+#define SS_OUTPUT_ENA 0x0200
+#define SS_DEBOUNCED 0x0400 /* Tell driver that the debounce delay has ended */
+#define SS_ZVCARD 0x0800
+
+/* Flags for I/O port and memory windows */
+#define MAP_ACTIVE 0x01
+#define MAP_16BIT 0x02
+#define MAP_AUTOSZ 0x04
+#define MAP_0WS 0x08
+#define MAP_WRPROT 0x10
+#define MAP_ATTRIB 0x20
+#define MAP_USE_WAIT 0x40
+#define MAP_PREFETCH 0x80
+
+/* Use this just for bridge windows */
+#define MAP_IOSPACE 0x20
+
+typedef struct pccard_io_map {
+ u_char map;
+ u_char flags;
+ u_short speed;
+ ioaddr_t start, stop;
+} pccard_io_map;
+
+
+typedef struct pccard_mem_map {
+ u_char map;
+ u_char flags;
+ u_short speed;
+ u_long sys_start, sys_stop;
+ u_int card_start;
+} pccard_mem_map;
+
+typedef struct cb_bridge_map {
+ u_char map;
+ u_char flags;
+ u_int start, stop;
+} cb_bridge_map;
+// need the global function pointer struct? *TODO*
+//************************************* end cc.h
+
+
+
+#endif /* PCMCIA_H */
diff --git a/gpxe/src/include/pxe.h b/gpxe/src/include/pxe.h
new file mode 100644
index 00000000..6d332ac7
--- /dev/null
+++ b/gpxe/src/include/pxe.h
@@ -0,0 +1,151 @@
+#ifndef PXE_H
+#define PXE_H
+
+#include "pxe_types.h"
+#include "pxe_api.h"
+#include <gpxe/device.h>
+
+/* Parameter block for pxenv_unknown() */
+struct s_PXENV_UNKNOWN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNKNOWN PXENV_UNKNOWN_t;
+
+/* Union used for PXE API calls; we don't know the type of the
+ * structure until we interpret the opcode. Also, Status is available
+ * in the same location for any opcode, and it's convenient to have
+ * non-specific access to it.
+ */
+union u_PXENV_ANY {
+ /* Make it easy to read status for any operation */
+ PXENV_STATUS_t Status;
+ struct s_PXENV_UNKNOWN unknown;
+ struct s_PXENV_UNLOAD_STACK unload_stack;
+ struct s_PXENV_GET_CACHED_INFO get_cached_info;
+ struct s_PXENV_TFTP_READ_FILE restart_tftp;
+ struct s_PXENV_START_UNDI start_undi;
+ struct s_PXENV_STOP_UNDI stop_undi;
+ struct s_PXENV_START_BASE start_base;
+ struct s_PXENV_STOP_BASE stop_base;
+ struct s_PXENV_TFTP_OPEN tftp_open;
+ struct s_PXENV_TFTP_CLOSE tftp_close;
+ struct s_PXENV_TFTP_READ tftp_read;
+ struct s_PXENV_TFTP_READ_FILE tftp_read_file;
+ struct s_PXENV_TFTP_GET_FSIZE tftp_get_fsize;
+ struct s_PXENV_UDP_OPEN udp_open;
+ struct s_PXENV_UDP_CLOSE udp_close;
+ struct s_PXENV_UDP_WRITE udp_write;
+ struct s_PXENV_UDP_READ udp_read;
+ struct s_PXENV_UNDI_STARTUP undi_startup;
+ struct s_PXENV_UNDI_CLEANUP undi_cleanup;
+ struct s_PXENV_UNDI_INITIALIZE undi_initialize;
+ struct s_PXENV_UNDI_RESET undi_reset_adapter;
+ struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
+ struct s_PXENV_UNDI_OPEN undi_open;
+ struct s_PXENV_UNDI_CLOSE undi_close;
+ struct s_PXENV_UNDI_TRANSMIT undi_transmit;
+ struct s_PXENV_UNDI_SET_MCAST_ADDRESS undi_set_mcast_address;
+ struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_station_address;
+ struct s_PXENV_UNDI_SET_PACKET_FILTER undi_set_packet_filter;
+ struct s_PXENV_UNDI_GET_INFORMATION undi_get_information;
+ struct s_PXENV_UNDI_GET_STATISTICS undi_get_statistics;
+ struct s_PXENV_UNDI_CLEAR_STATISTICS undi_clear_statistics;
+ struct s_PXENV_UNDI_INITIATE_DIAGS undi_initiate_diags;
+ struct s_PXENV_UNDI_FORCE_INTERRUPT undi_force_interrupt;
+ struct s_PXENV_UNDI_GET_MCAST_ADDRESS undi_get_mcast_address;
+ struct s_PXENV_UNDI_GET_NIC_TYPE undi_get_nic_type;
+ struct s_PXENV_UNDI_GET_IFACE_INFO undi_get_iface_info;
+ struct s_PXENV_UNDI_GET_STATE undi_get_state;
+ struct s_PXENV_UNDI_ISR undi_isr;
+ struct s_PXENV_FILE_OPEN file_open;
+ struct s_PXENV_FILE_CLOSE file_close;
+ struct s_PXENV_FILE_SELECT file_select;
+ struct s_PXENV_FILE_READ file_read;
+ struct s_PXENV_GET_FILE_SIZE get_file_size;
+ struct s_PXENV_FILE_EXEC file_exec;
+ struct s_PXENV_FILE_API_CHECK file_api_check;
+};
+
+typedef union u_PXENV_ANY PXENV_ANY_t;
+
+/** An UNDI expansion ROM header */
+struct undi_rom_header {
+ /** Signature
+ *
+ * Must be equal to @c ROM_SIGNATURE
+ */
+ UINT16_t Signature;
+ /** ROM length in 512-byte blocks */
+ UINT8_t ROMLength;
+ /** Unused */
+ UINT8_t unused[0x13];
+ /** Offset of the PXE ROM ID structure */
+ UINT16_t PXEROMID;
+ /** Offset of the PCI ROM structure */
+ UINT16_t PCIRHeader;
+} PACKED;
+
+/** Signature for an expansion ROM */
+#define ROM_SIGNATURE 0xaa55
+
+/** An UNDI ROM ID structure */
+struct undi_rom_id {
+ /** Signature
+ *
+ * Must be equal to @c UNDI_ROM_ID_SIGNATURE
+ */
+ UINT32_t Signature;
+ /** Length of structure */
+ UINT8_t StructLength;
+ /** Checksum */
+ UINT8_t StructCksum;
+ /** Structure revision
+ *
+ * Must be zero.
+ */
+ UINT8_t StructRev;
+ /** UNDI revision
+ *
+ * Version 2.1.0 is encoded as the byte sequence 0x00, 0x01, 0x02.
+ */
+ UINT8_t UNDIRev[3];
+ /** Offset to UNDI loader */
+ UINT16_t UNDILoader;
+ /** Minimum required stack segment size */
+ UINT16_t StackSize;
+ /** Minimum required data segment size */
+ UINT16_t DataSize;
+ /** Minimum required code segment size */
+ UINT16_t CodeSize;
+} PACKED;
+
+/** Signature for an UNDI ROM ID structure */
+#define UNDI_ROM_ID_SIGNATURE \
+ ( ( 'U' << 0 ) + ( 'N' << 8 ) + ( 'D' << 16 ) + ( 'I' << 24 ) )
+
+/** A PCI expansion header */
+struct pcir_header {
+ /** Signature
+ *
+ * Must be equal to @c PCIR_SIGNATURE
+ */
+ uint32_t signature;
+ /** PCI vendor ID */
+ uint16_t vendor_id;
+ /** PCI device ID */
+ uint16_t device_id;
+} PACKED;
+
+/** Signature for an UNDI ROM ID structure */
+#define PCIR_SIGNATURE \
+ ( ( 'P' << 0 ) + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) )
+
+
+extern struct net_device *pxe_netdev;
+
+extern void pxe_set_netdev ( struct net_device *netdev );
+
+extern void pxe_set_cached_filename ( const unsigned char *filename );
+
+#endif /* PXE_H */
diff --git a/gpxe/src/include/pxe_api.h b/gpxe/src/include/pxe_api.h
new file mode 100644
index 00000000..b3d4bca8
--- /dev/null
+++ b/gpxe/src/include/pxe_api.h
@@ -0,0 +1,1841 @@
+#ifndef PXE_API_H
+#define PXE_API_H
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/** @file
+ *
+ * Preboot eXecution Environment (PXE) API
+ *
+ */
+
+#include "pxe_types.h"
+
+/** @addtogroup pxe Preboot eXecution Environment (PXE) API
+ * @{
+ */
+
+/** @defgroup pxe_api_call PXE entry points
+ *
+ * PXE entry points and calling conventions
+ *
+ * @{
+ */
+
+/** The PXENV+ structure */
+struct s_PXENV {
+ /** Signature
+ *
+ * Contains the bytes 'P', 'X', 'E', 'N', 'V', '+'.
+ */
+ UINT8_t Signature[6];
+ /** PXE API version
+ *
+ * MSB is major version number, LSB is minor version number.
+ * If the API version number is 0x0201 or greater, the !PXE
+ * structure pointed to by #PXEPtr should be used instead of
+ * this data structure.
+ */
+ UINT16_t Version;
+ UINT8_t Length; /**< Length of this structure */
+ /** Checksum
+ *
+ * The byte checksum of this structure (using the length in
+ * #Length) must be zero.
+ */
+ UINT8_t Checksum;
+ SEGOFF16_t RMEntry; /**< Real-mode PXENV+ entry point */
+ /** Protected-mode PXENV+ entry point offset
+ *
+ * PXE 2.1 deprecates this entry point. For protected-mode
+ * API calls, use the !PXE structure pointed to by #PXEPtr
+ * instead.
+ */
+ UINT32_t PMOffset;
+ /** Protected-mode PXENV+ entry point segment selector
+ *
+ * PXE 2.1 deprecates this entry point. For protected-mode
+ * API calls, use the !PXE structure pointed to by #PXEPtr
+ * instead.
+ */
+ SEGSEL_t PMSelector;
+ SEGSEL_t StackSeg; /**< Stack segment selector */
+ UINT16_t StackSize; /**< Stack segment size */
+ SEGSEL_t BC_CodeSeg; /**< Base-code code segment selector */
+ UINT16_t BC_CodeSize; /**< Base-code code segment size */
+ SEGSEL_t BC_DataSeg; /**< Base-code data segment selector */
+ UINT16_t BC_DataSize; /**< Base-code data segment size */
+ SEGSEL_t UNDIDataSeg; /**< UNDI data segment selector */
+ UINT16_t UNDIDataSize; /**< UNDI data segment size */
+ SEGSEL_t UNDICodeSeg; /**< UNDI code segment selector */
+ UINT16_t UNDICodeSize; /**< UNDI code segment size */
+ /** Address of the !PXE structure
+ *
+ * This field is present only if #Version is 0x0201 or
+ * greater. If present, it points to a struct s_PXE.
+ */
+ SEGOFF16_t PXEPtr;
+} PACKED;
+
+typedef struct s_PXENV PXENV_t;
+
+/** The !PXE structure */
+struct s_PXE {
+ /** Signature
+ *
+ * Contains the bytes '!', 'P', 'X', 'E'.
+ */
+ UINT8_t Signature[4];
+ UINT8_t StructLength; /**< Length of this structure */
+ /** Checksum
+ *
+ * The byte checksum of this structure (using the length in
+ * #StructLength) must be zero.
+ */
+ UINT8_t StructCksum;
+ /** Revision of this structure
+ *
+ * For PXE version 2.1, this field must be zero.
+ */
+ UINT8_t StructRev;
+ UINT8_t reserved_1; /**< Must be zero */
+ /** Address of the UNDI ROM ID structure
+ *
+ * This is a pointer to a struct s_UNDI_ROM_ID.
+ */
+ SEGOFF16_t UNDIROMID;
+ /** Address of the Base Code ROM ID structure
+ *
+ * This is a pointer to a struct s_BC_ROM_ID.
+ */
+ SEGOFF16_t BaseROMID;
+ /** 16-bit !PXE entry point
+ *
+ * This is the entry point for either real mode, or protected
+ * mode with a 16-bit stack segment.
+ */
+ SEGOFF16_t EntryPointSP;
+ /** 32-bit !PXE entry point
+ *
+ * This is the entry point for protected mode with a 32-bit
+ * stack segment.
+ */
+ SEGOFF16_t EntryPointESP;
+ /** Status call-out function
+ *
+ * @v 0 (if in a time-out loop)
+ * @v n Number of a received TFTP packet
+ * @ret 0 Continue operation
+ * @ret 1 Cancel operation
+ *
+ * This function will be called whenever the PXE stack is in
+ * protected mode, is waiting for an event (e.g. a DHCP reply)
+ * and wishes to allow the user to cancel the operation.
+ * Parameters are passed in register %ax; the return value
+ * must also be placed in register %ax. All other registers
+ * and flags @b must be preserved.
+ *
+ * In real mode, an internal function (that checks for a
+ * keypress) will be used.
+ *
+ * If this field is set to -1, no status call-out function
+ * will be used and consequently the user will not be allowed
+ * to interrupt operations.
+ *
+ * @note The PXE specification version 2.1 defines the
+ * StatusCallout field, mentions it 11 times, but nowhere
+ * defines what it actually does or how it gets called.
+ * Fortunately, the WfM specification version 1.1a deigns to
+ * inform us of such petty details.
+ */
+ SEGOFF16_t StatusCallout;
+ UINT8_t reserved_2; /**< Must be zero */
+ /** Number of segment descriptors
+ *
+ * If this number is greater than 7, the remaining descriptors
+ * follow immediately after #BC_CodeWrite.
+ */
+ UINT8_t SegDescCnt;
+ /** First protected-mode selector
+ *
+ * This is the segment selector value for the first segment
+ * assigned to PXE. Protected-mode selectors must be
+ * consecutive, according to the PXE 2.1 specification, though
+ * no reason is given. Each #SEGDESC_t includes a field for
+ * the segment selector, so this information is entirely
+ * redundant.
+ */
+ SEGSEL_t FirstSelector;
+ /** Stack segment descriptor */
+ SEGDESC_t Stack;
+ /** UNDI data segment descriptor */
+ SEGDESC_t UNDIData;
+ /** UNDI code segment descriptor */
+ SEGDESC_t UNDICode;
+ /** UNDI writable code segment descriptor */
+ SEGDESC_t UNDICodeWrite;
+ /** Base-code data segment descriptor */
+ SEGDESC_t BC_Data;
+ /** Base-code code segment descriptor */
+ SEGDESC_t BC_Code;
+ /** Base-code writable code segment descriptor */
+ SEGDESC_t BC_CodeWrite;
+} PACKED;
+
+typedef struct s_PXE PXE_t;
+
+/** @} */ /* pxe_api_call */
+
+/** @defgroup pxe_preboot_api PXE Preboot API
+ *
+ * General high-level functions: #PXENV_UNLOAD_STACK, #PXENV_START_UNDI etc.
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_unload_stack PXENV_UNLOAD_STACK
+ *
+ * UNLOAD BASE CODE STACK
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_unload_stack() */
+#define PXENV_UNLOAD_STACK 0x0070
+
+/** Parameter block for pxenv_unload_stack() */
+struct s_PXENV_UNLOAD_STACK {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT8_t reserved[10]; /**< Must be zero */
+} PACKED;
+
+typedef struct s_PXENV_UNLOAD_STACK PXENV_UNLOAD_STACK_t;
+
+extern PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK
+ *unload_stack );
+
+/** @} */ /* pxenv_unload_stack */
+
+/** @defgroup pxenv_get_cached_info PXENV_GET_CACHED_INFO
+ *
+ * GET CACHED INFO
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_get_cached_info() */
+#define PXENV_GET_CACHED_INFO 0x0071
+
+/** The client's DHCPDISCOVER packet */
+#define PXENV_PACKET_TYPE_DHCP_DISCOVER 1
+
+/** The DHCP server's DHCPACK packet */
+#define PXENV_PACKET_TYPE_DHCP_ACK 2
+
+/** The Boot Server's Discover Reply packet
+ *
+ * This packet contains DHCP option 60 set to "PXEClient", a valid
+ * boot file name, and may or may not contain MTFTP options.
+ */
+#define PXENV_PACKET_TYPE_CACHED_REPLY 3
+
+/** Parameter block for pxenv_get_cached_info() */
+struct s_PXENV_GET_CACHED_INFO {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Packet type.
+ *
+ * Valid values are #PXENV_PACKET_TYPE_DHCP_DISCOVER,
+ * #PXENV_PACKET_TYPE_DHCP_ACK or #PXENV_PACKET_TYPE_CACHED_REPLY
+ */
+ UINT16_t PacketType;
+ UINT16_t BufferSize; /**< Buffer size */
+ SEGOFF16_t Buffer; /**< Buffer address */
+ UINT16_t BufferLimit; /**< Maximum buffer size */
+} PACKED;
+
+typedef struct s_PXENV_GET_CACHED_INFO PXENV_GET_CACHED_INFO_t;
+
+#define BOOTP_REQ 1 /**< A BOOTP request packet */
+#define BOOTP_REP 2 /**< A BOOTP reply packet */
+
+/** DHCP broadcast flag
+ *
+ * Request a broadcast response (DHCPOFFER or DHCPACK) from the DHCP
+ * server.
+ */
+#define BOOTP_BCAST 0x8000
+
+#define VM_RFC1048 0x63825363L /**< DHCP magic cookie */
+
+/** Maximum length of DHCP options */
+#define BOOTP_DHCPVEND 1024
+
+/** Format of buffer filled in by pxenv_get_cached_info()
+ *
+ * This somewhat convoluted data structure simply describes the layout
+ * of a DHCP packet. Refer to RFC2131 section 2 for a full
+ * description.
+ */
+struct bootph {
+ /** Message opcode.
+ *
+ * Valid values are #BOOTP_REQ and #BOOTP_REP.
+ */
+ UINT8_t opcode;
+ /** NIC hardware type.
+ *
+ * Valid values are as for s_PXENV_UNDI_GET_INFORMATION::HwType.
+ */
+ UINT8_t Hardware;
+ UINT8_t Hardlen; /**< MAC address length */
+ /** Gateway hops
+ *
+ * Zero in packets sent by the client. May be non-zero in
+ * replies from the DHCP server, if the reply comes via a DHCP
+ * relay agent.
+ */
+ UINT8_t Gatehops;
+ UINT32_t ident; /**< DHCP transaction id (xid) */
+ /** Elapsed time
+ *
+ * Number of seconds since the client began the DHCP
+ * transaction.
+ */
+ UINT16_t seconds;
+ /** Flags
+ *
+ * This is the bitwise-OR of any of the following values:
+ * #BOOTP_BCAST.
+ */
+ UINT16_t Flags;
+ /** Client IP address
+ *
+ * Set only if the client already has an IP address.
+ */
+ IP4_t cip;
+ /** Your IP address
+ *
+ * This is the IP address that the server assigns to the
+ * client.
+ */
+ IP4_t yip;
+ /** Server IP address
+ *
+ * This is the IP address of the BOOTP/DHCP server.
+ */
+ IP4_t sip;
+ /** Gateway IP address
+ *
+ * This is the IP address of the BOOTP/DHCP relay agent, if
+ * any. It is @b not (necessarily) the address of the default
+ * gateway for routing purposes.
+ */
+ IP4_t gip;
+ MAC_ADDR_t CAddr; /**< Client MAC address */
+ UINT8_t Sname[64]; /**< Server host name */
+ UINT8_t bootfile[128]; /**< Boot file name */
+ /** DHCP options
+ *
+ * Don't ask. Just laugh. Then burn a copy of the PXE
+ * specification and send Intel an e-mail asking them if
+ * they've figured out what a "union" does in C yet.
+ */
+ union bootph_vendor {
+ UINT8_t d[BOOTP_DHCPVEND]; /**< DHCP options */
+ /** DHCP options */
+ struct bootph_vendor_v {
+ /** DHCP magic cookie
+ *
+ * Should have the value #VM_RFC1048.
+ */
+ UINT8_t magic[4];
+ UINT32_t flags; /**< BOOTP flags/opcodes */
+ /** "End of BOOTP vendor extensions"
+ *
+ * Abandon hope, all ye who consider the
+ * purpose of this field.
+ */
+ UINT8_t pad[56];
+ } v;
+ } vendor;
+} PACKED;
+
+typedef struct bootph BOOTPLAYER_t;
+
+extern PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
+ *get_cached_info );
+
+/** @} */ /* pxenv_get_cached_info */
+
+/** @defgroup pxenv_restart_tftp PXENV_RESTART_TFTP
+ *
+ * RESTART TFTP
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_restart_tftp() */
+#define PXENV_RESTART_TFTP 0x0073
+
+/** Parameter block for pxenv_restart_tftp() */
+struct s_PXENV_TFTP_READ_FILE;
+
+typedef struct s_PXENV_RESTART_TFTP PXENV_RESTART_TFTP_t;
+
+extern PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE
+ *restart_tftp );
+
+/** @} */ /* pxenv_restart_tftp */
+
+/** @defgroup pxenv_start_undi PXENV_START_UNDI
+ *
+ * START UNDI
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_start_undi() */
+#define PXENV_START_UNDI 0x0000
+
+/** Parameter block for pxenv_start_undi() */
+struct s_PXENV_START_UNDI {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** %ax register as passed to the Option ROM initialisation routine.
+ *
+ * For a PCI device, this should contain the bus:dev:fn value
+ * that uniquely identifies the PCI device in the system. For
+ * a non-PCI device, this field is not defined.
+ */
+ UINT16_t AX;
+ /** %bx register as passed to the Option ROM initialisation routine.
+ *
+ * For an ISAPnP device, this should contain the Card Select
+ * Number assigned to the ISAPnP card. For non-ISAPnP
+ * devices, this should contain 0xffff.
+ */
+ UINT16_t BX;
+ /** %dx register as passed to the Option ROM initialisation routine.
+ *
+ * For an ISAPnP device, this should contain the ISAPnP Read
+ * Port address as currently set in all ISAPnP cards. If
+ * there are no ISAPnP cards, this should contain 0xffff. (If
+ * this is a non-ISAPnP device, but there are ISAPnP cards in
+ * the system, this value is not well defined.)
+ */
+ UINT16_t DX;
+ /** %di register as passed to the Option ROM initialisation routine.
+ *
+ * This contains the #OFF16_t portion of a struct #s_SEGOFF16
+ * that points to the System BIOS Plug and Play Installation
+ * Check Structure. (Refer to section 4.4 of the Plug and
+ * Play BIOS specification for a description of this
+ * structure.)
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #OFF16_t and #UINT16_t are
+ * equivalent anyway; for other architectures #OFF16_t makes
+ * more sense.
+ */
+ OFF16_t DI;
+ /** %es register as passed to the Option ROM initialisation routine.
+ *
+ * This contains the #SEGSEL_t portion of a struct #s_SEGOFF16
+ * that points to the System BIOS Plug and Play Installation
+ * Check Structure. (Refer to section 4.4 of the Plug and
+ * Play BIOS specification for a description of this
+ * structure.)
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are
+ * equivalent anyway; for other architectures #SEGSEL_t makes
+ * more sense.
+ */
+ SEGSEL_t ES;
+} PACKED;
+
+typedef struct s_PXENV_START_UNDI PXENV_START_UNDI_t;
+
+extern PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi );
+
+/** @} */ /* pxenv_start_undi */
+
+/** @defgroup pxenv_stop_undi PXENV_STOP_UNDI
+ *
+ * STOP UNDI
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_stop_undi() */
+#define PXENV_STOP_UNDI 0x0015
+
+/** Parameter block for pxenv_stop_undi() */
+struct s_PXENV_STOP_UNDI {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_STOP_UNDI PXENV_STOP_UNDI_t;
+
+extern PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi );
+
+/** @} */ /* pxenv_stop_undi */
+
+/** @defgroup pxenv_start_base PXENV_START_BASE
+ *
+ * START BASE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_start_base() */
+#define PXENV_START_BASE 0x0075
+
+/** Parameter block for pxenv_start_base() */
+struct s_PXENV_START_BASE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_START_BASE PXENV_START_BASE_t;
+
+extern PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base );
+
+/** @} */ /* pxenv_start_base */
+
+/** @defgroup pxenv_stop_base PXENV_STOP_BASE
+ *
+ * STOP BASE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_stop_base() */
+#define PXENV_STOP_BASE 0x0076
+
+/** Parameter block for pxenv_stop_base() */
+struct s_PXENV_STOP_BASE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_STOP_BASE PXENV_STOP_BASE_t;
+
+extern PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base );
+
+/** @} */ /* pxenv_stop_base */
+
+/** @} */ /* pxe_preboot_api */
+
+/** @defgroup pxe_tftp_api PXE TFTP API
+ *
+ * Download files via TFTP or MTFTP
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_tftp_open PXENV_TFTP_OPEN
+ *
+ * TFTP OPEN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_open() */
+#define PXENV_TFTP_OPEN 0x0020
+
+/** Parameter block for pxenv_tftp_open() */
+struct s_PXENV_TFTP_OPEN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t ServerIPAddress; /**< TFTP server IP address */
+ IP4_t GatewayIPAddress; /**< Relay agent IP address */
+ UINT8_t FileName[128]; /**< File name */
+ UDP_PORT_t TFTPPort; /**< TFTP server UDP port */
+ /** Requested size of TFTP packets
+ *
+ * This is the TFTP "blksize" option. This must be at least
+ * 512, since servers that do not support TFTP options cannot
+ * negotiate blocksizes smaller than this.
+ */
+ UINT16_t PacketSize;
+} PACKED;
+
+typedef struct s_PXENV_TFTP_OPEN PXENV_TFTP_OPEN_t;
+
+extern PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open );
+
+/** @} */ /* pxenv_tftp_open */
+
+/** @defgroup pxenv_tftp_close PXENV_TFTP_CLOSE
+ *
+ * TFTP CLOSE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_close() */
+#define PXENV_TFTP_CLOSE 0x0021
+
+/** Parameter block for pxenv_tftp_close() */
+struct s_PXENV_TFTP_CLOSE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_TFTP_CLOSE PXENV_TFTP_CLOSE_t;
+
+extern PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close );
+
+/** @} */ /* pxenv_tftp_close */
+
+/** @defgroup pxenv_tftp_read PXENV_TFTP_READ
+ *
+ * TFTP READ
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_read() */
+#define PXENV_TFTP_READ 0x0022
+
+/** Parameter block for pxenv_tftp_read() */
+struct s_PXENV_TFTP_READ {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t PacketNumber; /**< TFTP packet number */
+ UINT16_t BufferSize; /**< Size of data buffer */
+ SEGOFF16_t Buffer; /**< Address of data buffer */
+} PACKED;
+
+typedef struct s_PXENV_TFTP_READ PXENV_TFTP_READ_t;
+
+extern PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read );
+
+/** @} */ /* pxenv_tftp_read */
+
+/** @defgroup pxenv_tftp_read_file PXENV_TFTP_READ_FILE
+ *
+ * TFTP/MTFTP READ FILE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_read_file() */
+#define PXENV_TFTP_READ_FILE 0x0023
+
+/** Parameter block for pxenv_tftp_read_file() */
+struct s_PXENV_TFTP_READ_FILE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT8_t FileName[128]; /**< File name */
+ UINT32_t BufferSize; /**< Size of data buffer */
+ ADDR32_t Buffer; /**< Address of data buffer */
+ IP4_t ServerIPAddress; /**< TFTP server IP address */
+ IP4_t GatewayIPAddress; /**< Relay agent IP address */
+ /** File multicast IP address */
+ IP4_t McastIPAddress;
+ /** Client multicast listening port */
+ UDP_PORT_t TFTPClntPort;
+ /** Server multicast listening port */
+ UDP_PORT_t TFTPSrvPort;
+ /** TFTP open timeout.
+ *
+ * This is the timeout for receiving the first DATA or ACK
+ * packets during the MTFTP Listen phase.
+ */
+ UINT16_t TFTPOpenTimeOut;
+ /** TFTP reopen timeout.
+ *
+ * This is the timeout for receiving an ACK packet while in
+ * the MTFTP Listen phase (when at least one ACK packet has
+ * already been seen).
+ */
+ UINT16_t TFTPReopenDelay;
+} PACKED;
+
+typedef struct s_PXENV_TFTP_READ_FILE PXENV_TFTP_READ_FILE_t;
+
+extern PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
+ *tftp_read_file );
+
+/** @} */ /* pxenv_tftp_read_file */
+
+/** @defgroup pxenv_tftp_get_fsize PXENV_TFTP_GET_FSIZE
+ *
+ * TFTP GET FILE SIZE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_tftp_get_fsize() */
+#define PXENV_TFTP_GET_FSIZE 0x0025
+
+/** Parameter block for pxenv_tftp_get_fsize() */
+struct s_PXENV_TFTP_GET_FSIZE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t ServerIPAddress; /**< TFTP server IP address */
+ IP4_t GatewayIPAddress; /**< Relay agent IP address */
+ UINT8_t FileName[128]; /**< File name */
+ UINT32_t FileSize; /**< Size of the file */
+} PACKED;
+
+typedef struct s_PXENV_TFTP_GET_FSIZE PXENV_TFTP_GET_FSIZE_t;
+
+extern PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
+ *get_fsize );
+
+/** @} */ /* pxenv_tftp_get_fsize */
+
+/** @} */ /* pxe_tftp_api */
+
+/** @defgroup pxe_udp_api PXE UDP API
+ *
+ * Transmit and receive UDP packets
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_udp_open PXENV_UDP_OPEN
+ *
+ * UDP OPEN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_open() */
+#define PXENV_UDP_OPEN 0x0030
+
+/** Parameter block for pxenv_udp_open() */
+struct s_PXENV_UDP_OPEN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t src_ip; /**< IP address of this station */
+} PACKED;
+
+typedef struct s_PXENV_UDP_OPEN PXENV_UDP_OPEN_t;
+
+extern PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open );
+
+/** @} */ /* pxenv_udp_open */
+
+/** @defgroup pxenv_udp_close PXENV_UDP_CLOSE
+ *
+ * UDP CLOSE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_close() */
+#define PXENV_UDP_CLOSE 0x0031
+
+/** Parameter block for pxenv_udp_close() */
+struct s_PXENV_UDP_CLOSE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UDP_CLOSE PXENV_UDP_CLOSE_t;
+
+extern PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close );
+
+/** @} */ /* pxenv_udp_close */
+
+/** @defgroup pxenv_udp_write PXENV_UDP_WRITE
+ *
+ * UDP WRITE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_write() */
+#define PXENV_UDP_WRITE 0x0033
+
+/** Parameter block for pxenv_udp_write() */
+struct s_PXENV_UDP_WRITE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t ip; /**< Destination IP address */
+ IP4_t gw; /**< Relay agent IP address */
+ UDP_PORT_t src_port; /**< Source UDP port */
+ UDP_PORT_t dst_port; /**< Destination UDP port */
+ UINT16_t buffer_size; /**< UDP payload buffer size */
+ SEGOFF16_t buffer; /**< UDP payload buffer address */
+} PACKED;
+
+typedef struct s_PXENV_UDP_WRITE PXENV_UDP_WRITE_t;
+
+extern PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *udp_write );
+
+/** @} */ /* pxenv_udp_write */
+
+/** @defgroup pxenv_udp_read PXENV_UDP_READ
+ *
+ * UDP READ
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_udp_read() */
+#define PXENV_UDP_READ 0x0032
+
+/** Parameter block for pxenv_udp_read() */
+struct s_PXENV_UDP_READ {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t src_ip; /**< Source IP address */
+ IP4_t dest_ip; /**< Destination IP address */
+ UDP_PORT_t s_port; /**< Source UDP port */
+ UDP_PORT_t d_port; /**< Destination UDP port */
+ UINT16_t buffer_size; /**< UDP payload buffer size */
+ SEGOFF16_t buffer; /**< UDP payload buffer address */
+} PACKED;
+
+typedef struct s_PXENV_UDP_READ PXENV_UDP_READ_t;
+
+extern PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *udp_read );
+
+/** @} */ /* pxenv_udp_read */
+
+/** @} */ /* pxe_udp_api */
+
+/** @defgroup pxe_undi_api PXE UNDI API
+ *
+ * Direct control of the network interface card
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_undi_startup PXENV_UNDI_STARTUP
+ *
+ * UNDI STARTUP
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_startup() */
+#define PXENV_UNDI_STARTUP 0x0001
+
+#define PXENV_BUS_ISA 0 /**< ISA bus type */
+#define PXENV_BUS_EISA 1 /**< EISA bus type */
+#define PXENV_BUS_MCA 2 /**< MCA bus type */
+#define PXENV_BUS_PCI 3 /**< PCI bus type */
+#define PXENV_BUS_VESA 4 /**< VESA bus type */
+#define PXENV_BUS_PCMCIA 5 /**< PCMCIA bus type */
+
+/** Parameter block for pxenv_undi_startup() */
+struct s_PXENV_UNDI_STARTUP {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_STARTUP PXENV_UNDI_STARTUP_t;
+
+extern PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP
+ *undi_startup );
+
+/** @} */ /* pxenv_undi_startup */
+
+/** @defgroup pxenv_undi_cleanup PXENV_UNDI_CLEANUP
+ *
+ * UNDI CLEANUP
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_cleanup() */
+#define PXENV_UNDI_CLEANUP 0x0002
+
+/** Parameter block for pxenv_undi_cleanup() */
+struct s_PXENV_UNDI_CLEANUP {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_CLEANUP PXENV_UNDI_CLEANUP_t;
+
+extern PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP
+ *undi_cleanup );
+
+/** @} */ /* pxenv_undi_cleanup */
+
+/** @defgroup pxenv_undi_initialize PXENV_UNDI_INITIALIZE
+ *
+ * UNDI INITIALIZE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_initialize() */
+#define PXENV_UNDI_INITIALIZE 0x0003
+
+/** Parameter block for pxenv_undi_initialize() */
+struct s_PXENV_UNDI_INITIALIZE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** NDIS 2.0 configuration information, or NULL
+ *
+ * This is a pointer to the data structure returned by the
+ * NDIS 2.0 GetProtocolManagerInfo() API call. The data
+ * structure is documented, in a rather haphazard way, in
+ * section 4-17 of the NDIS 2.0 specification.
+ */
+ ADDR32_t ProtocolIni;
+ UINT8_t reserved[8]; /**< Must be zero */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_INITIALIZE PXENV_UNDI_INITIALIZE_t;
+
+extern PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
+ *undi_initialize );
+
+/** @} */ /* pxenv_undi_initialize */
+
+/** @defgroup pxenv_undi_reset_adapter PXENV_UNDI_RESET_ADAPTER
+ *
+ * UNDI RESET ADAPTER
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_reset_adapter() */
+#define PXENV_UNDI_RESET_ADAPTER 0x0004
+
+/** Maximum number of multicast MAC addresses */
+#define MAXNUM_MCADDR 8
+
+/** List of multicast MAC addresses */
+struct s_PXENV_UNDI_MCAST_ADDRESS {
+ /** Number of multicast MAC addresses */
+ UINT16_t MCastAddrCount;
+ /** List of up to #MAXNUM_MCADDR multicast MAC addresses */
+ MAC_ADDR_t McastAddr[MAXNUM_MCADDR];
+} PACKED;
+
+typedef struct s_PXENV_UNDI_MCAST_ADDRESS PXENV_UNDI_MCAST_ADDRESS_t;
+
+/** Parameter block for pxenv_undi_reset_adapter() */
+struct s_PXENV_UNDI_RESET {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Multicast MAC addresses */
+ struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_RESET PXENV_UNDI_RESET_t;
+
+extern PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
+ *undi_reset_adapter );
+
+/** @} */ /* pxenv_undi_reset_adapter */
+
+/** @defgroup pxenv_undi_shutdown PXENV_UNDI_SHUTDOWN
+ *
+ * UNDI SHUTDOWN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_shutdown() */
+#define PXENV_UNDI_SHUTDOWN 0x0005
+
+/** Parameter block for pxenv_undi_shutdown() */
+struct s_PXENV_UNDI_SHUTDOWN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_SHUTDOWN PXENV_UNDI_SHUTDOWN_t;
+
+extern PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
+ *undi_shutdown );
+
+/** @} */ /* pxenv_undi_shutdown */
+
+/** @defgroup pxenv_undi_open PXENV_UNDI_OPEN
+ *
+ * UNDI OPEN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_open() */
+#define PXENV_UNDI_OPEN 0x0006
+
+/** Accept "directed" packets
+ *
+ * These are packets addresses to either this adapter's MAC address or
+ * to any of the configured multicast MAC addresses (see
+ * #s_PXENV_UNDI_MCAST_ADDRESS).
+ */
+#define FLTR_DIRECTED 0x0001
+/** Accept broadcast packets */
+#define FLTR_BRDCST 0x0002
+/** Accept all packets; listen in promiscuous mode */
+#define FLTR_PRMSCS 0x0004
+/** Accept source-routed packets */
+#define FLTR_SRC_RTG 0x0008
+
+/** Parameter block for pxenv_undi_open() */
+struct s_PXENV_UNDI_OPEN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Open flags as defined in NDIS 2.0
+ *
+ * This is the OpenOptions field as passed to the NDIS 2.0
+ * OpenAdapter() API call. It is defined to be "adapter
+ * specific", though 0 is guaranteed to be a valid value.
+ */
+ UINT16_t OpenFlag;
+ /** Receive packet filter
+ *
+ * This is the bitwise-OR of any of the following flags:
+ * #FLTR_DIRECTED, #FLTR_BRDCST, #FLTR_PRMSCS and
+ * #FLTR_SRC_RTG.
+ */
+ UINT16_t PktFilter;
+ /** Multicast MAC addresses */
+ struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_OPEN PXENV_UNDI_OPEN_t;
+
+extern PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open );
+
+/** @} */ /* pxenv_undi_open */
+
+/** @defgroup pxenv_undi_close PXENV_UNDI_CLOSE
+ *
+ * UNDI CLOSE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_close() */
+#define PXENV_UNDI_CLOSE 0x0007
+
+/** Parameter block for pxenv_undi_close() */
+struct s_PXENV_UNDI_CLOSE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_CLOSE PXENV_UNDI_CLOSE_t;
+
+extern PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close );
+
+/** @} */ /* pxenv_undi_close */
+
+/** @defgroup pxenv_undi_transmit PXENV_UNDI_TRANSMIT
+ *
+ * UNDI TRANSMIT PACKET
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_transmit() */
+#define PXENV_UNDI_TRANSMIT 0x0008
+
+#define P_UNKNOWN 0 /**< Media header already filled in */
+#define P_IP 1 /**< IP protocol */
+#define P_ARP 2 /**< ARP protocol */
+#define P_RARP 3 /**< RARP protocol */
+#define P_OTHER 4 /**< Other protocol */
+
+#define XMT_DESTADDR 0x0000 /**< Unicast packet */
+#define XMT_BROADCAST 0x0001 /**< Broadcast packet */
+
+/** Maximum number of data blocks in a transmit buffer descriptor */
+#define MAX_DATA_BLKS 8
+
+/** A transmit buffer descriptor, as pointed to by s_PXENV_UNDI_TRANSMIT::TBD
+ */
+struct s_PXENV_UNDI_TBD {
+ UINT16_t ImmedLength; /**< Length of the transmit buffer */
+ SEGOFF16_t Xmit; /**< Address of the transmit buffer */
+ UINT16_t DataBlkCount;
+ /** Array of up to #MAX_DATA_BLKS additional transmit buffers */
+ struct DataBlk {
+ /** Always 1
+ *
+ * A value of 0 would indicate that #TDDataPtr were an
+ * #ADDR32_t rather than a #SEGOFF16_t. The PXE
+ * specification version 2.1 explicitly states that
+ * this is not supported; #TDDataPtr will always be a
+ * #SEGOFF16_t.
+ */
+ UINT8_t TDPtrType;
+ UINT8_t TDRsvdByte; /**< Must be zero */
+ UINT16_t TDDataLen; /**< Length of this transmit buffer */
+ SEGOFF16_t TDDataPtr; /**< Address of this transmit buffer */
+ } DataBlock[MAX_DATA_BLKS];
+} PACKED;
+
+typedef struct s_PXENV_UNDI_TBD PXENV_UNDI_TBD_t;
+
+/** Parameter block for pxenv_undi_transmit() */
+struct s_PXENV_UNDI_TRANSMIT {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Protocol
+ *
+ * Valid values are #P_UNKNOWN, #P_IP, #P_ARP or #P_RARP. If
+ * the caller has already filled in the media header, this
+ * field must be set to #P_UNKNOWN.
+ */
+ UINT8_t Protocol;
+ /** Unicast/broadcast flag
+ *
+ * Valid values are #XMT_DESTADDR or #XMT_BROADCAST.
+ */
+ UINT8_t XmitFlag;
+ SEGOFF16_t DestAddr; /**< Destination MAC address */
+ /** Address of the Transmit Buffer Descriptor
+ *
+ * This is a pointer to a struct s_PXENV_UNDI_TBD.
+ */
+ SEGOFF16_t TBD;
+ UINT32_t Reserved[2]; /**< Must be zero */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_TRANSMIT PXENV_UNDI_TRANSMIT_t;
+
+extern PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
+ *undi_transmit );
+
+/** @} */ /* pxenv_undi_transmit */
+
+/** @defgroup pxenv_undi_set_mcast_address PXENV_UNDI_SET_MCAST_ADDRESS
+ *
+ * UNDI SET MULTICAST ADDRESS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_set_mcast_address() */
+#define PXENV_UNDI_SET_MCAST_ADDRESS 0x0009
+
+/** Parameter block for pxenv_undi_set_mcast_address() */
+struct s_PXENV_UNDI_SET_MCAST_ADDRESS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** List of multicast addresses */
+ struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_SET_MCAST_ADDRESS PXENV_UNDI_SET_MCAST_ADDRESS_t;
+
+extern PXENV_EXIT_t pxenv_undi_set_mcast_address (
+ struct s_PXENV_UNDI_SET_MCAST_ADDRESS *undi_set_mcast_address );
+
+/** @} */ /* pxenv_undi_set_mcast_address */
+
+/** @defgroup pxenv_undi_set_station_address PXENV_UNDI_SET_STATION_ADDRESS
+ *
+ * UNDI SET STATION ADDRESS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_set_station_address() */
+#define PXENV_UNDI_SET_STATION_ADDRESS 0x000a
+
+/** Parameter block for pxenv_undi_set_station_address() */
+struct s_PXENV_UNDI_SET_STATION_ADDRESS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ MAC_ADDR_t StationAddress; /**< Station MAC address */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_SET_STATION_ADDRESS PXENV_UNDI_SET_STATION_ADDRESS_t;
+
+extern PXENV_EXIT_t pxenv_undi_set_station_address (
+ struct s_PXENV_UNDI_SET_STATION_ADDRESS *undi_set_station_address );
+
+/** @} */ /* pxenv_undi_set_station_address */
+
+/** @defgroup pxenv_undi_set_packet_filter PXENV_UNDI_SET_PACKET_FILTER
+ *
+ * UNDI SET PACKET FILTER
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_set_packet_filter() */
+#define PXENV_UNDI_SET_PACKET_FILTER 0x000b
+
+/** Parameter block for pxenv_undi_set_packet_filter() */
+struct s_PXENV_UNDI_SET_PACKET_FILTER {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Receive packet filter
+ *
+ * This field takes the same values as
+ * s_PXENV_UNDI_OPEN::PktFilter.
+ *
+ * @note Yes, this field is a different size to
+ * s_PXENV_UNDI_OPEN::PktFilter. Blame "the managers at Intel
+ * who apparently let a consultant come up with the spec
+ * without any kind of adult supervision" (quote from hpa).
+ */
+ UINT8_t filter;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_SET_PACKET_FILTER PXENV_UNDI_SET_PACKET_FILTER_t;
+
+extern PXENV_EXIT_t pxenv_undi_set_packet_filter (
+ struct s_PXENV_UNDI_SET_PACKET_FILTER *undi_set_packet_filter );
+
+/** @} */ /* pxenv_undi_set_packet_filter */
+
+/** @defgroup pxenv_undi_get_information PXENV_UNDI_GET_INFORMATION
+ *
+ * UNDI GET INFORMATION
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_information() */
+#define PXENV_UNDI_GET_INFORMATION 0x000c
+
+#define ETHER_TYPE 1 /**< Ethernet (10Mb) */
+#define EXP_ETHER_TYPE 2 /**< Experimental Ethernet (3Mb) */
+#define AX25_TYPE 3 /**< Amateur Radio AX.25 */
+#define TOKEN_RING_TYPE 4 /**< Proteon ProNET Token Ring */
+#define CHAOS_TYPE 5 /**< Chaos */
+#define IEEE_TYPE 6 /**< IEEE 802 Networks */
+#define ARCNET_TYPE 7 /**< ARCNET */
+
+/** Parameter block for pxenv_undi_get_information() */
+struct s_PXENV_UNDI_GET_INFORMATION {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t BaseIo; /**< I/O base address */
+ UINT16_t IntNumber; /**< IRQ number */
+ UINT16_t MaxTranUnit; /**< Adapter MTU */
+ /** Hardware type
+ *
+ * Valid values are defined in RFC1010 ("Assigned numbers"),
+ * and are #ETHER_TYPE, #EXP_ETHER_TYPE, #AX25_TYPE,
+ * #TOKEN_RING_TYPE, #CHAOS_TYPE, #IEEE_TYPE or #ARCNET_TYPE.
+ */
+ UINT16_t HwType;
+ UINT16_t HwAddrLen; /**< MAC address length */
+ MAC_ADDR_t CurrentNodeAddress; /**< Current MAC address */
+ MAC_ADDR_t PermNodeAddress; /**< Permanent (EEPROM) MAC address */
+ SEGSEL_t ROMAddress; /**< Real-mode ROM segment address */
+ UINT16_t RxBufCt; /**< Receive queue length */
+ UINT16_t TxBufCt; /**< Transmit queue length */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_INFORMATION PXENV_UNDI_GET_INFORMATION_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_information (
+ struct s_PXENV_UNDI_GET_INFORMATION *undi_get_information );
+
+/** @} */ /* pxenv_undi_get_information */
+
+/** @defgroup pxenv_undi_get_statistics PXENV_UNDI_GET_STATISTICS
+ *
+ * UNDI GET STATISTICS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_statistics() */
+#define PXENV_UNDI_GET_STATISTICS 0x000d
+
+/** Parameter block for pxenv_undi_get_statistics() */
+struct s_PXENV_UNDI_GET_STATISTICS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT32_t XmtGoodFrames; /**< Successful transmission count */
+ UINT32_t RcvGoodFrames; /**< Successful reception count */
+ UINT32_t RcvCRCErrors; /**< Receive CRC error count */
+ UINT32_t RcvResourceErrors; /**< Receive queue overflow count */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_STATISTICS PXENV_UNDI_GET_STATISTICS_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_statistics (
+ struct s_PXENV_UNDI_GET_STATISTICS *undi_get_statistics );
+
+/** @} */ /* pxenv_undi_get_statistics */
+
+/** @defgroup pxenv_undi_clear_statistics PXENV_UNDI_CLEAR_STATISTICS
+ *
+ * UNDI CLEAR STATISTICS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_clear_statistics() */
+#define PXENV_UNDI_CLEAR_STATISTICS 0x000e
+
+/** Parameter block for pxenv_undi_clear_statistics() */
+struct s_PXENV_UNDI_CLEAR_STATISTICS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_CLEAR_STATISTICS PXENV_UNDI_CLEAR_STATISTICS_t;
+
+extern PXENV_EXIT_t pxenv_undi_clear_statistics (
+ struct s_PXENV_UNDI_CLEAR_STATISTICS *undi_clear_statistics );
+
+/** @} */ /* pxenv_undi_clear_statistics */
+
+/** @defgroup pxenv_undi_initiate_diags PXENV_UNDI_INITIATE_DIAGS
+ *
+ * UNDI INITIATE DIAGS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_initiate_diags() */
+#define PXENV_UNDI_INITIATE_DIAGS 0x000f
+
+/** Parameter block for pxenv_undi_initiate_diags() */
+struct s_PXENV_UNDI_INITIATE_DIAGS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_INITIATE_DIAGS PXENV_UNDI_INITIATE_DIAGS_t;
+
+extern PXENV_EXIT_t pxenv_undi_initiate_diags (
+ struct s_PXENV_UNDI_INITIATE_DIAGS *undi_initiate_diags );
+
+/** @} */ /* pxenv_undi_initiate_diags */
+
+/** @defgroup pxenv_undi_force_interrupt PXENV_UNDI_FORCE_INTERRUPT
+ *
+ * UNDI FORCE INTERRUPT
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_force_interrupt() */
+#define PXENV_UNDI_FORCE_INTERRUPT 0x0010
+
+/** Parameter block for pxenv_undi_force_interrupt() */
+struct s_PXENV_UNDI_FORCE_INTERRUPT {
+ PXENV_STATUS_t Status; /**< PXE status code */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_FORCE_INTERRUPT PXENV_UNDI_FORCE_INTERRUPT_t;
+
+extern PXENV_EXIT_t pxenv_undi_force_interrupt (
+ struct s_PXENV_UNDI_FORCE_INTERRUPT *undi_force_interrupt );
+
+/** @} */ /* pxenv_undi_force_interrupt */
+
+/** @defgroup pxenv_undi_get_mcast_address PXENV_UNDI_GET_MCAST_ADDRESS
+ *
+ * UNDI GET MULTICAST ADDRESS
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_mcast_address() */
+#define PXENV_UNDI_GET_MCAST_ADDRESS 0x0011
+
+/** Parameter block for pxenv_undi_get_mcast_address() */
+struct s_PXENV_UNDI_GET_MCAST_ADDRESS {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ IP4_t InetAddr; /**< Multicast IP address */
+ MAC_ADDR_t MediaAddr; /**< Multicast MAC address */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_MCAST_ADDRESS PXENV_UNDI_GET_MCAST_ADDRESS_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_mcast_address (
+ struct s_PXENV_UNDI_GET_MCAST_ADDRESS *undi_get_mcast_address );
+
+/** @} */ /* pxenv_undi_get_mcast_address */
+
+/** @defgroup pxenv_undi_get_nic_type PXENV_UNDI_GET_NIC_TYPE
+ *
+ * UNDI GET NIC TYPE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_nic_type() */
+#define PXENV_UNDI_GET_NIC_TYPE 0x0012
+
+#define PCI_NIC 2 /**< PCI network card */
+#define PnP_NIC 3 /**< ISAPnP network card */
+#define CardBus_NIC 4 /**< CardBus network card */
+
+/** Information for a PCI or equivalent NIC */
+struct pci_nic_info {
+ UINT16_t Vendor_ID; /**< PCI vendor ID */
+ UINT16_t Dev_ID; /**< PCI device ID */
+ UINT8_t Base_Class; /**< PCI base class */
+ UINT8_t Sub_Class; /**< PCI sub class */
+ UINT8_t Prog_Intf; /**< PCI programming interface */
+ UINT8_t Rev; /**< PCI revision */
+ UINT16_t BusDevFunc; /**< PCI bus:dev:fn address */
+ UINT16_t SubVendor_ID; /**< PCI subvendor ID */
+ UINT16_t SubDevice_ID; /**< PCI subdevice ID */
+} PACKED;
+
+/** Information for an ISAPnP or equivalent NIC */
+struct pnp_nic_info {
+ UINT32_t EISA_Dev_ID; /**< EISA device ID */
+ UINT8_t Base_Class; /**< Base class */
+ UINT8_t Sub_Class; /**< Sub class */
+ UINT8_t Prog_Intf; /**< Programming interface */
+ /** Card Select Number assigned to card */
+ UINT16_t CardSelNum;
+} PACKED;
+
+/** Parameter block for pxenv_undi_get_nic_type() */
+struct s_PXENV_UNDI_GET_NIC_TYPE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** NIC type
+ *
+ * Valid values are #PCI_NIC, #PnP_NIC or #CardBus_NIC.
+ */
+ UINT8_t NicType;
+ /** NIC information */
+ union nic_type_info {
+ /** NIC information (if #NicType==#PCI_NIC) */
+ struct pci_nic_info pci;
+ /** NIC information (if #NicType==#CardBus_NIC) */
+ struct pci_nic_info cardbus;
+ /** NIC information (if #NicType==#PnP_NIC) */
+ struct pnp_nic_info pnp;
+ } info;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_NIC_TYPE PXENV_UNDI_GET_NIC_TYPE_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_nic_type (
+ struct s_PXENV_UNDI_GET_NIC_TYPE *undi_get_nic_type );
+
+/** @} */ /* pxenv_undi_get_nic_type */
+
+/** @defgroup pxenv_undi_get_iface_info PXENV_UNDI_GET_IFACE_INFO
+ *
+ * UNDI GET IFACE INFO
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_iface_info() */
+#define PXENV_UNDI_GET_IFACE_INFO 0x0013
+
+/** Parameter block for pxenv_undi_get_iface_info() */
+struct s_PXENV_UNDI_GET_IFACE_INFO {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Interface type
+ *
+ * This is defined in the NDIS 2.0 specification to be one of
+ * the strings "802.3", "802.4", "802.5", "802.6", "DIX",
+ * "DIX+802.3", "APPLETALK", "ARCNET", "FDDI", "SDLC", "BSC",
+ * "HDLC", or "ISDN".
+ *
+ * "Normal" Ethernet, for various historical reasons, is
+ * "DIX+802.3".
+ */
+ UINT8_t IfaceType[16];
+ UINT32_t LinkSpeed; /**< Link speed, in bits per second */
+ /** Service flags
+ *
+ * These are the "service flags" defined in the "MAC
+ * Service-Specific Characteristics" table in the NDIS 2.0
+ * specification. Almost all of them are irrelevant to PXE.
+ */
+ UINT32_t ServiceFlags;
+ UINT32_t Reserved[4]; /**< Must be zero */
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_IFACE_INFO PXENV_UNDI_GET_IFACE_INFO_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_iface_info (
+ struct s_PXENV_UNDI_GET_IFACE_INFO *undi_get_iface_info );
+
+/** @} */ /* pxenv_undi_get_iface_info */
+
+/** @defgroup pxenv_undi_get_state PXENV_UNDI_GET_STATE
+ *
+ * UNDI GET STATE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_get_state() */
+#define PXENV_UNDI_GET_STATE 0x0015
+
+/** pxenv_start_undi() has been called */
+#define PXE_UNDI_GET_STATE_STARTED 1
+/** pxenv_undi_initialize() has been called */
+#define PXE_UNDI_GET_STATE_INITIALIZED 2
+/** pxenv_undi_open() has been called */
+#define PXE_UNDI_GET_STATE_OPENED 3
+
+/** Parameter block for pxenv_undi_get_state() */
+struct s_PXENV_UNDI_GET_STATE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Current state of the UNDI driver
+ *
+ * Valid values are #PXE_UNDI_GET_STATE_STARTED,
+ * #PXE_UNDI_GET_STATE_INITIALIZED or
+ * #PXE_UNDI_GET_STATE_OPENED.
+ */
+ UINT8_t UNDIstate;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_GET_STATE PXENV_UNDI_GET_STATE_t;
+
+extern PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
+ *undi_get_state );
+
+/** @} */ /* pxenv_undi_get_state */
+
+/** @defgroup pxenv_undi_isr PXENV_UNDI_ISR
+ *
+ * UNDI ISR
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_undi_isr() */
+#define PXENV_UNDI_ISR 0x0014
+
+/** Determine whether or not this is our interrupt */
+#define PXENV_UNDI_ISR_IN_START 1
+/** Start processing interrupt */
+#define PXENV_UNDI_ISR_IN_PROCESS 2
+/** Continue processing interrupt */
+#define PXENV_UNDI_ISR_IN_GET_NEXT 3
+/** This interrupt was ours */
+#define PXENV_UNDI_ISR_OUT_OURS 0
+/** This interrupt was not ours */
+#define PXENV_UNDI_ISR_OUT_NOT_OURS 1
+/** Finished processing interrupt */
+#define PXENV_UNDI_ISR_OUT_DONE 0
+/** A packet transmission has completed */
+#define PXENV_UNDI_ISR_OUT_TRANSMIT 2
+/** A packet has been received */
+#define PXENV_UNDI_ISR_OUT_RECEIVE 3
+/** We are already in the middle of processing an interrupt */
+#define PXENV_UNDI_ISR_OUT_BUSY 4
+
+/** Unicast packet (or packet captured in promiscuous mode) */
+#define P_DIRECTED 0
+/** Broadcast packet */
+#define P_BROADCAST 1
+/** Multicast packet */
+#define P_MULTICAST 2
+
+/** Parameter block for pxenv_undi_isr() */
+struct s_PXENV_UNDI_ISR {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ /** Function flag
+ *
+ * Valid values are #PXENV_UNDI_ISR_IN_START,
+ * #PXENV_UNDI_ISR_IN_PROCESS, #PXENV_UNDI_ISR_IN_GET_NEXT,
+ * #PXENV_UNDI_ISR_OUT_OURS, #PXENV_UNDI_ISR_OUT_NOT_OURS,
+ * #PXENV_UNDI_ISR_OUT_DONE, #PXENV_UNDI_ISR_OUT_TRANSMIT,
+ * #PXENV_UNDI_ISR_OUT_RECEIVE or #PXENV_UNDI_ISR_OUT_BUSY.
+ */
+ UINT16_t FuncFlag;
+ UINT16_t BufferLength; /**< Data buffer length */
+ UINT16_t FrameLength; /**< Total frame length */
+ UINT16_t FrameHeaderLength; /**< Frame header length */
+ SEGOFF16_t Frame; /**< Data buffer address */
+ /** Protocol type
+ *
+ * Valid values are #P_IP, #P_ARP, #P_RARP or #P_OTHER.
+ */
+ UINT8_t ProtType;
+ /** Packet type
+ *
+ * Valid values are #P_DIRECTED, #P_BROADCAST or #P_MULTICAST.
+ */
+ UINT8_t PktType;
+} PACKED;
+
+typedef struct s_PXENV_UNDI_ISR PXENV_UNDI_ISR_t;
+
+extern PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr );
+
+/** @} */ /* pxenv_undi_isr */
+
+/** @} */ /* pxe_undi_api */
+
+/** @defgroup pxe_file_api PXE FILE API
+ *
+ * POSIX-like file operations
+ *
+ * @{
+ */
+
+/** @defgroup pxenv_file_open PXENV_FILE_OPEN
+ *
+ * FILE OPEN
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_open() */
+#define PXENV_FILE_OPEN 0x00e0
+
+/** Parameter block for pxenv_file_open() */
+struct s_PXENV_FILE_OPEN {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t FileHandle; /**< File handle */
+ SEGOFF16_t FileName; /**< File URL */
+ UINT32_t Reserved; /**< Reserved */
+} PACKED;
+
+typedef struct s_PXENV_FILE_OPEN PXENV_FILE_OPEN_t;
+
+extern PXENV_EXIT_t pxenv_file_open ( struct s_PXENV_FILE_OPEN *file_open );
+
+/** @} */ /* pxenv_file_open */
+
+/** @defgroup pxenv_file_close PXENV_FILE_CLOSE
+ *
+ * FILE CLOSE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_close() */
+#define PXENV_FILE_CLOSE 0x00e1
+
+/** Parameter block for pxenv_file_close() */
+struct s_PXENV_FILE_CLOSE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t FileHandle; /**< File handle */
+} PACKED;
+
+typedef struct s_PXENV_FILE_CLOSE PXENV_FILE_CLOSE_t;
+
+extern PXENV_EXIT_t pxenv_file_close ( struct s_PXENV_FILE_CLOSE
+ *file_close );
+
+/** @} */ /* pxenv_file_close */
+
+/** @defgroup pxenv_file_select PXENV_FILE_SELECT
+ *
+ * FILE SELECT
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_select() */
+#define PXENV_FILE_SELECT 0x00e2
+
+/** File is ready for reading */
+#define RDY_READ 0x0001
+
+/** Parameter block for pxenv_file_select() */
+struct s_PXENV_FILE_SELECT {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t FileHandle; /**< File handle */
+ UINT16_t Ready; /**< Indication of readiness */
+} PACKED;
+
+typedef struct s_PXENV_FILE_SELECT PXENV_FILE_SELECT_t;
+
+extern PXENV_EXIT_t pxenv_file_select ( struct s_PXENV_FILE_SELECT
+ *file_select );
+
+/** @} */ /* pxenv_file_select */
+
+/** @defgroup pxenv_file_read PXENV_FILE_READ
+ *
+ * FILE READ
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_read() */
+#define PXENV_FILE_READ 0x00e3
+
+/** Parameter block for pxenv_file_read() */
+struct s_PXENV_FILE_READ {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t FileHandle; /**< File handle */
+ UINT16_t BufferSize; /**< Data buffer size */
+ SEGOFF16_t Buffer; /**< Data buffer */
+} PACKED;
+
+typedef struct s_PXENV_FILE_READ PXENV_FILE_READ_t;
+
+extern PXENV_EXIT_t pxenv_file_read ( struct s_PXENV_FILE_READ *file_read );
+
+/** @} */ /* pxenv_file_read */
+
+/** @defgroup pxenv_get_file_size PXENV_GET_FILE_SIZE
+ *
+ * GET FILE SIZE
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_get_file_size() */
+#define PXENV_GET_FILE_SIZE 0x00e4
+
+/** Parameter block for pxenv_get_file_size() */
+struct s_PXENV_GET_FILE_SIZE {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t FileHandle; /**< File handle */
+ UINT32_t FileSize; /**< File size */
+} PACKED;
+
+typedef struct s_PXENV_GET_FILE_SIZE PXENV_GET_FILE_SIZE_t;
+
+extern PXENV_EXIT_t pxenv_get_file_size ( struct s_PXENV_GET_FILE_SIZE
+ *get_file_size );
+
+/** @} */ /* pxenv_get_file_size */
+
+/** @defgroup pxenv_file_exec PXENV_FILE_EXEC
+ *
+ * FILE EXEC
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_exec() */
+#define PXENV_FILE_EXEC 0x00e5
+
+/** Parameter block for pxenv_file_exec() */
+struct s_PXENV_FILE_EXEC {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ SEGOFF16_t Command; /**< Command to execute */
+} PACKED;
+
+typedef struct s_PXENV_FILE_EXEC PXENV_FILE_EXEC_t;
+
+extern PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec );
+
+/** @} */ /* pxenv_file_exec */
+
+/** @defgroup pxenv_file_api_check PXENV_FILE_API_CHECK
+ *
+ * FILE API CHECK
+ *
+ * @{
+ */
+
+/** PXE API function code for pxenv_file_api_check() */
+#define PXENV_FILE_API_CHECK 0x00e6
+
+/** Parameter block for pxenv_file_api_check() */
+struct s_PXENV_FILE_API_CHECK {
+ PXENV_STATUS_t Status; /**< PXE status code */
+ UINT16_t Size; /**< Size of structure */
+ UINT32_t Magic; /**< Magic number */
+ UINT32_t Provider; /**< Implementation identifier */
+ UINT32_t APIMask; /**< Supported API functions */
+ UINT32_t Flags; /**< Reserved for the future */
+} PACKED;
+
+typedef struct s_PXENV_FILE_API_CHECK PXENV_FILE_API_CHECK_t;
+
+extern PXENV_EXIT_t pxenv_file_api_check ( struct s_PXENV_FILE_API_CHECK *file_api_check );
+
+/** @} */ /* pxenv_file_api_check */
+
+/** @} */ /* pxe_file_api */
+
+/** @defgroup pxe_loader_api PXE Loader API
+ *
+ * The UNDI ROM loader API
+ *
+ * @{
+ */
+
+/** Parameter block for undi_loader() */
+struct s_UNDI_LOADER {
+ /** PXE status code */
+ PXENV_STATUS_t Status;
+ /** %ax register as for PXENV_START_UNDI */
+ UINT16_t AX;
+ /** %bx register as for PXENV_START_UNDI */
+ UINT16_t BX;
+ /** %dx register as for PXENV_START_UNDI */
+ UINT16_t DX;
+ /** %di register as for PXENV_START_UNDI */
+ OFF16_t DI;
+ /** %es register as for PXENV_START_UNDI */
+ SEGSEL_t ES;
+ /** UNDI data segment
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are
+ * equivalent anyway; for other architectures #SEGSEL_t makes
+ * more sense.
+ */
+ SEGSEL_t UNDI_DS;
+ /** UNDI code segment
+ *
+ * @note The PXE specification defines the type of this field
+ * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are
+ * equivalent anyway; for other architectures #SEGSEL_t makes
+ * more sense.
+ */
+ SEGSEL_t UNDI_CS;
+ /** Address of the !PXE structure (a struct s_PXE) */
+ SEGOFF16_t PXEptr;
+ /** Address of the PXENV+ structure (a struct s_PXENV) */
+ SEGOFF16_t PXENVptr;
+} PACKED;
+
+typedef struct s_UNDI_LOADER UNDI_LOADER_t;
+
+extern PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader );
+
+/** @} */ /* pxe_loader_api */
+
+/** @} */ /* pxe */
+
+/** @page pxe_notes Etherboot PXE implementation notes
+
+@section pxe_routing IP routing
+
+Several PXE API calls (e.g. pxenv_tftp_open() and pxenv_udp_write())
+allow for the caller to specify a "relay agent IP address", often in a
+field called "gateway" or similar. The PXE specification states that
+"The IP layer should provide space for a minimum of four routing
+entries obtained from the default router and static route DHCP option
+tags in the DHCPACK message, plus any non-zero giaddr field from the
+DHCPOFFER message(s) accepted by the client".
+
+The DHCP static route option ("option static-routes" in dhcpd.conf)
+works only for classed IP routing (i.e. it provides no way to specify
+a subnet mask). Since virtually everything now uses classless IP
+routing, the DHCP static route option is almost totally useless, and
+is (according to the dhcp-options man page) not implemented by any of
+the popular DHCP clients.
+
+This leaves the caller-specified "relay agent IP address", the giaddr
+field from the DHCPOFFER message(s) and the default gateway(s)
+provided via the routers option ("option routers" in dhcpd.conf) in
+the DHCPACK message. Each of these is a default gateway address.
+It's a fair bet that the routers option should take priority over the
+giaddr field, since the routers option has to be explicitly specified
+by the DHCP server operator. Similarly, it's fair to assume that the
+caller-specified "relay agent IP address", if present, should take
+priority over any other routing table entries.
+
+@bug Etherboot currently ignores all potential sources of routing
+information other than the first router provided to it by a DHCP
+routers option.
+
+@section pxe_x86_modes x86 processor mode restrictions
+
+On the x86 platform, different PXE API calls have different
+restrictions on the processor modes (real or protected) that can be
+used. See the individual API call descriptions for the restrictions
+that apply to any particular call.
+
+@subsection pxe_x86_pmode16 Real mode, or protected-mode with 16-bit stack
+
+The PXE specification states that the API function can be called in
+protected mode only if the s_PXE::StatusCallout field is set to a
+non-zero value, and that the API function cannot be called with a
+32-bit stack segment.
+
+Etherboot does not enforce either of these restrictions; they seem (as
+with so much of the PXE specification) to be artifacts of the Intel
+implementation.
+
+*/
+
+#endif /* PXE_API_H */
diff --git a/gpxe/src/include/pxe_types.h b/gpxe/src/include/pxe_types.h
new file mode 100644
index 00000000..e31af062
--- /dev/null
+++ b/gpxe/src/include/pxe_types.h
@@ -0,0 +1,126 @@
+#ifndef PXE_TYPES_H
+#define PXE_TYPES_H
+
+/** @file
+ *
+ * PXE data types
+ *
+ */
+
+#include "stdint.h"
+#include "pxe_addr.h" /* Architecture-specific PXE definitions */
+#include "errno.h" /* PXE status codes */
+
+/** @addtogroup pxe Preboot eXecution Environment (PXE) API
+ * @{
+ */
+
+/** @defgroup pxe_types PXE data types
+ *
+ * Basic PXE data types such as #UINT16_t, #ADDR32_t, #SEGSEL_t etc.
+ *
+ * These definitions are based on Table 1-1 ("Data Type Definitions")
+ * in the Intel PXE specification version 2.1. They have been
+ * generalised to non-x86 architectures where possible.
+ *
+ * @{
+ */
+
+/** An 8-bit unsigned integer */
+typedef uint8_t UINT8_t;
+
+/** A 16-bit unsigned integer */
+typedef uint16_t UINT16_t;
+
+/** A 32-bit unsigned integer */
+typedef uint32_t UINT32_t;
+
+/** A PXE exit code.
+ *
+ * Permitted values are #PXENV_EXIT_SUCCESS and #PXENV_EXIT_FAILURE.
+ *
+ */
+typedef UINT16_t PXENV_EXIT_t;
+#define PXENV_EXIT_SUCCESS 0x0000 /**< No error occurred */
+#define PXENV_EXIT_FAILURE 0x0001 /**< An error occurred */
+
+/** A PXE status code.
+ *
+ * Status codes are defined in errno.h.
+ *
+ */
+typedef UINT16_t PXENV_STATUS_t;
+
+/** An IPv4 address.
+ *
+ * @note This data type is in network (big-endian) byte order.
+ *
+ */
+typedef UINT32_t IP4_t;
+
+/** A UDP port.
+ *
+ * @note This data type is in network (big-endian) byte order.
+ *
+ */
+typedef UINT16_t UDP_PORT_t;
+
+/** Maximum length of a MAC address */
+#define MAC_ADDR_LEN 16
+
+/** A MAC address */
+typedef UINT8_t MAC_ADDR_t[MAC_ADDR_LEN];
+
+#ifndef HAVE_ARCH_ADDR32
+/** A physical address.
+ *
+ * For x86, this is a 32-bit physical address, and is therefore
+ * limited to the low 4GB.
+ *
+ */
+typedef UINT32_t ADDR32_t;
+#endif
+
+#ifndef HAVE_ARCH_SEGSEL
+/** A segment selector.
+ *
+ * For x86, this is a real mode segment (0x0000-0xffff), or a
+ * protected-mode segment selector, such as could be loaded into a
+ * segment register.
+ *
+ */
+typedef UINT16_t SEGSEL_t;
+#endif
+
+#ifndef HAVE_ARCH_OFF16
+/** An offset within a segment identified by #SEGSEL
+ *
+ * For x86, this is a 16-bit offset.
+ *
+ */
+typedef UINT16_t OFF16_t;
+#endif
+
+/** A segment:offset address
+ *
+ * For x86, this is a 16-bit real-mode or protected-mode
+ * segment:offset address.
+ *
+ */
+typedef struct s_SEGOFF16 {
+ OFF16_t offset; /**< Offset within the segment */
+ SEGSEL_t segment; /**< Segment selector */
+} PACKED SEGOFF16_t;
+
+/** A segment descriptor */
+typedef struct s_SEGDESC {
+ SEGSEL_t segment_address; /**< Segment selector */
+ ADDR32_t Physical_address; /**< Segment base address */
+ OFF16_t Seg_size; /**< Size of the segment */
+} PACKED SEGDESC_t;
+
+/** @} */ /* pxe_types */
+
+/** @} */ /* pxe */
+
+#endif /* PXE_TYPES_H */
diff --git a/gpxe/src/include/readline/readline.h b/gpxe/src/include/readline/readline.h
new file mode 100644
index 00000000..1a03b483
--- /dev/null
+++ b/gpxe/src/include/readline/readline.h
@@ -0,0 +1,12 @@
+#ifndef _READLINE_H
+#define _READLINE_H
+
+/** @file
+ *
+ * Minmal readline
+ *
+ */
+
+extern char * __malloc readline ( const char *prompt );
+
+#endif /* _READLINE_H */
diff --git a/gpxe/src/include/stdarg.h b/gpxe/src/include/stdarg.h
new file mode 100644
index 00000000..a4eb711d
--- /dev/null
+++ b/gpxe/src/include/stdarg.h
@@ -0,0 +1,10 @@
+#ifndef _STDARG_H
+#define _STDARG_H
+
+typedef __builtin_va_list va_list;
+#define va_start( ap, last ) __builtin_va_start ( ap, last )
+#define va_arg( ap, type ) __builtin_va_arg ( ap, type )
+#define va_end( ap ) __builtin_va_end ( ap )
+#define va_copy( dest, src ) __builtin_va_copy ( dest, src )
+
+#endif /* _STDARG_H */
diff --git a/gpxe/src/include/stddef.h b/gpxe/src/include/stddef.h
new file mode 100644
index 00000000..6f91d219
--- /dev/null
+++ b/gpxe/src/include/stddef.h
@@ -0,0 +1,18 @@
+#ifndef STDDEF_H
+#define STDDEF_H
+
+/* for size_t */
+#include <stdint.h>
+
+#undef NULL
+#define NULL ((void *)0)
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#undef container_of
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#endif /* STDDEF_H */
diff --git a/gpxe/src/include/stdint.h b/gpxe/src/include/stdint.h
new file mode 100644
index 00000000..4b0e44f2
--- /dev/null
+++ b/gpxe/src/include/stdint.h
@@ -0,0 +1,24 @@
+#ifndef _STDINT_H
+#define _STDINT_H
+
+#include <bits/stdint.h>
+
+typedef int8_t s8;
+typedef uint8_t u8;
+typedef int16_t s16;
+typedef uint16_t u16;
+typedef int32_t s32;
+typedef uint32_t u32;
+typedef int64_t s64;
+typedef uint64_t u64;
+
+typedef int8_t int8;
+typedef uint8_t uint8;
+typedef int16_t int16;
+typedef uint16_t uint16;
+typedef int32_t int32;
+typedef uint32_t uint32;
+typedef int64_t int64;
+typedef uint64_t uint64;
+
+#endif /* _STDINT_H */
diff --git a/gpxe/src/include/stdio.h b/gpxe/src/include/stdio.h
new file mode 100644
index 00000000..82077e20
--- /dev/null
+++ b/gpxe/src/include/stdio.h
@@ -0,0 +1,45 @@
+#ifndef _STDIO_H
+#define _STDIO_H
+
+#include <stdint.h>
+#include <stdarg.h>
+
+extern int __attribute__ (( format ( printf, 1, 2 ) ))
+printf ( const char *fmt, ... );
+
+extern int __attribute__ (( format ( printf, 3, 4 ) ))
+snprintf ( char *buf, size_t size, const char *fmt, ... );
+
+extern int __attribute__ (( format ( printf, 2, 3 ) ))
+asprintf ( char **strp, const char *fmt, ... );
+
+extern int vprintf ( const char *fmt, va_list args );
+
+extern int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args );
+
+extern int vasprintf ( char **strp, const char *fmt, va_list args );
+
+/**
+ * Write a formatted string to a buffer
+ *
+ * @v buf Buffer into which to write the string
+ * @v fmt Format string
+ * @v ... Arguments corresponding to the format string
+ * @ret len Length of formatted string
+ */
+#define sprintf( buf, fmt, ... ) \
+ snprintf ( (buf), ~( ( size_t ) 0 ), (fmt), ## __VA_ARGS__ )
+
+/**
+ * Write a formatted string to a buffer
+ *
+ * @v buf Buffer into which to write the string
+ * @v fmt Format string
+ * @v args Arguments corresponding to the format string
+ * @ret len Length of formatted string
+ */
+static inline int vsprintf ( char *buf, const char *fmt, va_list args ) {
+ return vsnprintf ( buf, ~( ( size_t ) 0 ), fmt, args );
+}
+
+#endif /* _STDIO_H */
diff --git a/gpxe/src/include/stdlib.h b/gpxe/src/include/stdlib.h
new file mode 100644
index 00000000..ae996962
--- /dev/null
+++ b/gpxe/src/include/stdlib.h
@@ -0,0 +1,72 @@
+#ifndef STDLIB_H
+#define STDLIB_H
+
+#include <stdint.h>
+#include <assert.h>
+
+/*****************************************************************************
+ *
+ * Numeric parsing
+ *
+ ****************************************************************************
+ */
+
+extern unsigned long strtoul ( const char *p, char **endp, int base );
+
+/*****************************************************************************
+ *
+ * Memory allocation
+ *
+ ****************************************************************************
+ */
+
+extern void * __malloc malloc ( size_t size );
+extern void * realloc ( void *old_ptr, size_t new_size );
+extern void free ( void *ptr );
+extern void * __malloc zalloc ( size_t len );
+
+/**
+ * Allocate cleared memory
+ *
+ * @v nmemb Number of members
+ * @v size Size of each member
+ * @ret ptr Allocated memory
+ *
+ * Allocate memory as per malloc(), and zero it.
+ *
+ * This is implemented as a static inline, with the body of the
+ * function in zalloc(), since in most cases @c nmemb will be 1 and
+ * doing the multiply is just wasteful.
+ */
+static inline void * __malloc calloc ( size_t nmemb, size_t size ) {
+ return zalloc ( nmemb * size );
+}
+
+/*****************************************************************************
+ *
+ * Random number generation
+ *
+ ****************************************************************************
+ */
+
+extern long int random ( void );
+extern void srandom ( unsigned int seed );
+
+static inline int rand ( void ) {
+ return random();
+}
+
+static inline void srand ( unsigned int seed ) {
+ srandom ( seed );
+}
+
+/*****************************************************************************
+ *
+ * Miscellaneous
+ *
+ ****************************************************************************
+ */
+
+extern int system ( const char *command );
+
+#endif /* STDLIB_H */
diff --git a/gpxe/src/include/string.h b/gpxe/src/include/string.h
new file mode 100644
index 00000000..a2894a3a
--- /dev/null
+++ b/gpxe/src/include/string.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * string handling functions
+ * based on linux/include/linux/ctype.h
+ * and linux/include/linux/string.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef ETHERBOOT_STRING_H
+#define ETHERBOOT_STRING_H
+
+#include <stddef.h>
+#include <bits/string.h>
+
+int __pure strnicmp(const char *s1, const char *s2, size_t len) __nonnull;
+char * strcpy(char * dest,const char *src) __nonnull;
+char * strncpy(char * dest,const char *src,size_t count) __nonnull;
+char * strcat(char * dest, const char * src) __nonnull;
+char * strncat(char *dest, const char *src, size_t count) __nonnull;
+int __pure strcmp(const char * cs,const char * ct) __nonnull;
+int __pure strncmp(const char * cs,const char * ct,
+ size_t count) __nonnull;
+char * __pure strchr(const char * s, int c) __nonnull;
+char * __pure strrchr(const char * s, int c) __nonnull;
+size_t __pure strlen(const char * s) __nonnull;
+size_t __pure strnlen(const char * s, size_t count) __nonnull;
+size_t __pure strspn(const char *s, const char *accept) __nonnull;
+size_t __pure strcspn(const char *s, const char *reject) __nonnull;
+char * __pure strpbrk(const char * cs,const char * ct) __nonnull;
+char * strtok(char * s,const char * ct) __nonnull;
+char * strsep(char **s, const char *ct) __nonnull;
+void * memset(void * s,int c,size_t count) __nonnull;
+void * memmove(void * dest,const void *src,size_t count) __nonnull;
+int __pure memcmp(const void * cs,const void * ct,
+ size_t count) __nonnull;
+void * __pure memscan(const void * addr, int c, size_t size) __nonnull;
+char * __pure strstr(const char * s1,const char * s2) __nonnull;
+void * __pure memchr(const void *s, int c, size_t n) __nonnull;
+char * __malloc strdup(const char *s) __nonnull;
+char * __malloc strndup(const char *s, size_t n) __nonnull;
+
+extern const char * __pure strerror ( int errno );
+
+#endif /* ETHERBOOT_STRING */
diff --git a/gpxe/src/include/strings.h b/gpxe/src/include/strings.h
new file mode 100644
index 00000000..968a7c11
--- /dev/null
+++ b/gpxe/src/include/strings.h
@@ -0,0 +1,63 @@
+#ifndef _STRINGS_H
+#define _STRINGS_H
+
+#include <limits.h>
+#include <string.h>
+
+static inline __attribute__ (( always_inline )) int
+__constant_flsl ( unsigned long x ) {
+ int r = 0;
+
+#if ULONG_MAX > 0xffffffff
+ if ( x & 0xffffffff00000000UL ) {
+ x >>= 32;
+ r += 32;
+ }
+#endif
+ if ( x & 0xffff0000UL ) {
+ x >>= 16;
+ r += 16;
+ }
+ if ( x & 0xff00 ) {
+ x >>= 8;
+ r += 8;
+ }
+ if ( x & 0xf0 ) {
+ x >>= 4;
+ r += 4;
+ }
+ if ( x & 0xc ) {
+ x >>= 2;
+ r += 2;
+ }
+ if ( x & 0x2 ) {
+ x >>= 1;
+ r += 1;
+ }
+ if ( x & 0x1 ) {
+ r += 1;
+ }
+ return r;
+}
+
+/* We don't actually have these functions yet */
+extern int __flsl ( long x );
+
+#define flsl( x ) \
+ ( __builtin_constant_p ( x ) ? __constant_flsl ( x ) : __flsl ( x ) )
+
+#define fls( x ) flsl ( x )
+
+extern int strcasecmp ( const char *s1, const char *s2 );
+
+static inline __attribute__ (( always_inline )) void
+bcopy ( const void *src, void *dest, size_t n ) {
+ memmove ( dest, src, n );
+}
+
+static inline __attribute__ (( always_inline )) void
+bzero ( void *s, size_t n ) {
+ memset ( s, 0, n );
+}
+
+#endif /* _STRINGS_H */
diff --git a/gpxe/src/include/sys/time.h b/gpxe/src/include/sys/time.h
new file mode 100644
index 00000000..21fb7e99
--- /dev/null
+++ b/gpxe/src/include/sys/time.h
@@ -0,0 +1,20 @@
+#ifndef _SYS_TIME_H
+#define _SYS_TIME_H
+
+#include <time.h>
+
+typedef unsigned long suseconds_t;
+
+struct timeval {
+ time_t tv_sec; /* seconds */
+ suseconds_t tv_usec; /* microseconds */
+};
+
+struct timezone {
+ int tz_minuteswest; /* minutes W of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+
+extern int gettimeofday ( struct timeval *tv, struct timezone *tz );
+
+#endif /* _SYS_TIME_H */
diff --git a/gpxe/src/include/sys_info.h b/gpxe/src/include/sys_info.h
new file mode 100644
index 00000000..7127c643
--- /dev/null
+++ b/gpxe/src/include/sys_info.h
@@ -0,0 +1,33 @@
+#ifndef SYS_INFO_H
+#define SYS_INFO_H
+
+/* Information collected from firmware/bootloader */
+
+struct sys_info {
+ /* Values passed by bootloader */
+ unsigned long boot_type;
+ unsigned long boot_data;
+ unsigned long boot_arg;
+
+ char *firmware; /* "PCBIOS", "LinuxBIOS", etc. */
+ char *command_line; /* command line given to us */
+#if 0
+//By LYH
+//Will use meminfo in Etherboot
+ /* memory map */
+ int n_memranges;
+ struct memrange {
+ unsigned long long base;
+ unsigned long long size;
+ } *memrange;
+#endif
+};
+
+void collect_sys_info(struct sys_info *info);
+void collect_elfboot_info(struct sys_info *info);
+void collect_linuxbios_info(struct sys_info *info);
+
+/* Our name and version. I want to see single instance of these in the image */
+extern const char *program_name, *program_version;
+
+#endif /* SYS_INFO_H */
diff --git a/gpxe/src/include/time.h b/gpxe/src/include/time.h
new file mode 100644
index 00000000..6ea927c3
--- /dev/null
+++ b/gpxe/src/include/time.h
@@ -0,0 +1,22 @@
+#ifndef _TIME_H
+#define _TIME_H
+
+typedef unsigned long time_t;
+
+struct tm {
+ int tm_sec; /* seconds */
+ int tm_min; /* minutes */
+ int tm_hour; /* hours */
+ int tm_mday; /* day of the month */
+ int tm_mon; /* month */
+ int tm_year; /* year */
+ int tm_wday; /* day of the week */
+ int tm_yday; /* day in the year */
+ int tm_isdst; /* daylight saving time */
+};
+
+extern time_t time ( time_t *t );
+
+extern time_t mktime ( struct tm *tm );
+
+#endif /* _TIME_H */
diff --git a/gpxe/src/include/unistd.h b/gpxe/src/include/unistd.h
new file mode 100644
index 00000000..7c44a0ce
--- /dev/null
+++ b/gpxe/src/include/unistd.h
@@ -0,0 +1,31 @@
+#ifndef _UNISTD_H
+#define _UNISTD_H
+
+#include <stddef.h>
+#include <stdarg.h>
+
+unsigned int sleep ( unsigned int seconds );
+extern int execv ( const char *command, char * const argv[] );
+
+/**
+ * Execute command
+ *
+ * @v command Command name
+ * @v arg ... Argument list (starting with argv[0])
+ * @ret rc Command exit status
+ *
+ * This is a front end to execv().
+ */
+#define execl( command, arg, ... ) ( { \
+ char * const argv[] = { (arg), ## __VA_ARGS__ }; \
+ int rc = execv ( (command), argv ); \
+ rc; \
+ } )
+
+void udelay(unsigned int usecs);
+void mdelay(unsigned int msecs);
+
+#define usleep(x) udelay(x)
+
+
+#endif /* _UNISTD_H */
diff --git a/gpxe/src/include/usr/aoeboot.h b/gpxe/src/include/usr/aoeboot.h
new file mode 100644
index 00000000..0421ebcc
--- /dev/null
+++ b/gpxe/src/include/usr/aoeboot.h
@@ -0,0 +1,6 @@
+#ifndef _USR_AOEBOOT_H
+#define _USR_AOEBOOT_H
+
+extern int aoeboot ( const char *root_path );
+
+#endif /* _USR_AOEBOOT_H */
diff --git a/gpxe/src/include/usr/autoboot.h b/gpxe/src/include/usr/autoboot.h
new file mode 100644
index 00000000..b451a8c1
--- /dev/null
+++ b/gpxe/src/include/usr/autoboot.h
@@ -0,0 +1,13 @@
+#ifndef _USR_AUTOBOOT_H
+#define _USR_AUTOBOOT_H
+
+/** @file
+ *
+ * Automatic booting
+ *
+ */
+
+extern void autoboot ( void );
+extern int boot_root_path ( const char *root_path );
+
+#endif /* _USR_AUTOBOOT_H */
diff --git a/gpxe/src/include/usr/dhcpmgmt.h b/gpxe/src/include/usr/dhcpmgmt.h
new file mode 100644
index 00000000..2757a1c1
--- /dev/null
+++ b/gpxe/src/include/usr/dhcpmgmt.h
@@ -0,0 +1,14 @@
+#ifndef _USR_DHCPMGMT_H
+#define _USR_DHCPMGMT_H
+
+/** @file
+ *
+ * DHCP management
+ *
+ */
+
+struct net_device;
+
+int dhcp ( struct net_device *netdev );
+
+#endif /* _USR_DHCPMGMT_H */
diff --git a/gpxe/src/include/usr/ifmgmt.h b/gpxe/src/include/usr/ifmgmt.h
new file mode 100644
index 00000000..c7d35da8
--- /dev/null
+++ b/gpxe/src/include/usr/ifmgmt.h
@@ -0,0 +1,16 @@
+#ifndef _USR_IFMGMT_H
+#define _USR_IFMGMT_H
+
+/** @file
+ *
+ * Network interface management
+ *
+ */
+
+struct net_device;
+
+extern int ifopen ( struct net_device *netdev );
+extern void ifclose ( struct net_device *netdev );
+extern void ifstat ( struct net_device *netdev );
+
+#endif /* _USR_IFMGMT_H */
diff --git a/gpxe/src/include/usr/imgmgmt.h b/gpxe/src/include/usr/imgmgmt.h
new file mode 100644
index 00000000..438af003
--- /dev/null
+++ b/gpxe/src/include/usr/imgmgmt.h
@@ -0,0 +1,20 @@
+#ifndef _USR_IMGMGMT_H
+#define _USR_IMGMGMT_H
+
+/** @file
+ *
+ * Image management
+ *
+ */
+
+struct image;
+
+extern int imgfetch ( struct image *image, const char *uri_string,
+ int ( * image_register ) ( struct image *image ) );
+extern int imgload ( struct image *image );
+extern int imgexec ( struct image *image );
+extern struct image * imgautoselect ( void );
+extern void imgstat ( struct image *image );
+extern void imgfree ( struct image *image );
+
+#endif /* _USR_IMGMGMT_H */
diff --git a/gpxe/src/include/usr/iscsiboot.h b/gpxe/src/include/usr/iscsiboot.h
new file mode 100644
index 00000000..b17951d5
--- /dev/null
+++ b/gpxe/src/include/usr/iscsiboot.h
@@ -0,0 +1,6 @@
+#ifndef _USR_ISCSIBOOT_H
+#define _USR_ISCSIBOOT_H
+
+extern int iscsiboot ( const char *root_path );
+
+#endif /* _USR_ISCSIBOOT_H */
diff --git a/gpxe/src/include/usr/route.h b/gpxe/src/include/usr/route.h
new file mode 100644
index 00000000..fc855892
--- /dev/null
+++ b/gpxe/src/include/usr/route.h
@@ -0,0 +1,12 @@
+#ifndef _USR_ROUTE_H
+#define _USR_ROUTE_H
+
+/** @file
+ *
+ * Routing table management
+ *
+ */
+
+extern void route ( void );
+
+#endif /* _USR_ROUTE_H */
diff --git a/gpxe/src/interface/pxe/pxe_errors.c b/gpxe/src/interface/pxe/pxe_errors.c
new file mode 100644
index 00000000..f884ef8a
--- /dev/null
+++ b/gpxe/src/interface/pxe/pxe_errors.c
@@ -0,0 +1,103 @@
+#include <errno.h>
+#include <gpxe/errortab.h>
+
+/*
+ * This table was generated from the relevant section of errno.h using
+ *
+ * perl -ne 'if ( /(PXENV_STATUS_(\S+))/ ) {
+ * $code = $1; $msg = $2;
+ * $msg =~ s/_/ /g; $msg = ucfirst lc $msg;
+ * $msg =~ s/(tftp|udp|arp|undi|bis|binl|pxenv|pxe|dhcp)/uc $1/ieg;
+ * print "\t{ $code, \"$msg\" },\n";
+ * }'
+ *
+ * followed by a little manual tweaking.
+ *
+ */
+struct errortab pxe_errortab[] __errortab = {
+ { PXENV_STATUS_SUCCESS, "Success" },
+ { PXENV_STATUS_FAILURE, "Failure" },
+ { PXENV_STATUS_BAD_FUNC, "Bad function" },
+ { PXENV_STATUS_UNSUPPORTED, "Unsupported function" },
+ { PXENV_STATUS_KEEP_UNDI, "Keep UNDI" },
+ { PXENV_STATUS_KEEP_ALL, "Keep all" },
+ { PXENV_STATUS_OUT_OF_RESOURCES, "Out of resources" },
+ { PXENV_STATUS_ARP_TIMEOUT, "ARP timeout" },
+ { PXENV_STATUS_UDP_CLOSED, "UDP closed" },
+ { PXENV_STATUS_UDP_OPEN, "UDP open" },
+ { PXENV_STATUS_TFTP_CLOSED, "TFTP closed" },
+ { PXENV_STATUS_TFTP_OPEN, "TFTP open" },
+ { PXENV_STATUS_MCOPY_PROBLEM, "Memory copy problem" },
+ { PXENV_STATUS_BIS_INTEGRITY_FAILURE, "BIS integrity failure" },
+ { PXENV_STATUS_BIS_VALIDATE_FAILURE, "BIS validation failure" },
+ { PXENV_STATUS_BIS_INIT_FAILURE, "BIS init failure" },
+ { PXENV_STATUS_BIS_SHUTDOWN_FAILURE, "BIS shutdown failure" },
+ { PXENV_STATUS_BIS_GBOA_FAILURE, "BIS GBOA failure" },
+ { PXENV_STATUS_BIS_FREE_FAILURE, "BIS free failure" },
+ { PXENV_STATUS_BIS_GSI_FAILURE, "BIS GSI failure" },
+ { PXENV_STATUS_BIS_BAD_CKSUM, "BIS bad checksum" },
+ { PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS, "TFTP cannot ARP address" },
+ { PXENV_STATUS_TFTP_OPEN_TIMEOUT, "TFTP open timeout" },
+ { PXENV_STATUS_TFTP_UNKNOWN_OPCODE, "TFTP unknown opcode" },
+ { PXENV_STATUS_TFTP_READ_TIMEOUT, "TFTP read timeout" },
+ { PXENV_STATUS_TFTP_ERROR_OPCODE, "TFTP error opcode" },
+ { PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION,
+ "TFTP cannot open connection" },
+ { PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION,
+ "TFTP cannot read from connection" },
+ { PXENV_STATUS_TFTP_TOO_MANY_PACKAGES, "TFTP too many packages" },
+ { PXENV_STATUS_TFTP_FILE_NOT_FOUND, "TFTP file not found" },
+ { PXENV_STATUS_TFTP_ACCESS_VIOLATION, "TFTP access violation" },
+ { PXENV_STATUS_TFTP_NO_MCAST_ADDRESS, "TFTP no mcast address" },
+ { PXENV_STATUS_TFTP_NO_FILESIZE, "TFTP no filesize" },
+ { PXENV_STATUS_TFTP_INVALID_PACKET_SIZE, "TFTP invalid packet size" },
+ { PXENV_STATUS_DHCP_TIMEOUT, "DHCP timeout" },
+ { PXENV_STATUS_DHCP_NO_IP_ADDRESS, "DHCP no ip address" },
+ { PXENV_STATUS_DHCP_NO_BOOTFILE_NAME, "DHCP no bootfile name" },
+ { PXENV_STATUS_DHCP_BAD_IP_ADDRESS, "DHCP bad ip address" },
+ { PXENV_STATUS_UNDI_INVALID_FUNCTION, "UNDI invalid function" },
+ { PXENV_STATUS_UNDI_MEDIATEST_FAILED, "UNDI mediatest failed" },
+ { PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST,
+ "UNDI cannot initialise NIC for multicast" },
+ { PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC,
+ "UNDI cannot initialise NIC" },
+ { PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY,
+ "UNDI cannot initialise PHY" },
+ { PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA,
+ "UNDI cannot read config data" },
+ { PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA,
+ "UNDI cannot read init data" },
+ { PXENV_STATUS_UNDI_BAD_MAC_ADDRESS, "UNDI bad MAC address" },
+ { PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM, "UNDI bad EEPROM checksum" },
+ { PXENV_STATUS_UNDI_ERROR_SETTING_ISR, "UNDI error setting ISR" },
+ { PXENV_STATUS_UNDI_INVALID_STATE, "UNDI invalid state" },
+ { PXENV_STATUS_UNDI_TRANSMIT_ERROR, "UNDI transmit error" },
+ { PXENV_STATUS_UNDI_INVALID_PARAMETER, "UNDI invalid parameter" },
+ { PXENV_STATUS_BSTRAP_PROMPT_MENU, "Bootstrap prompt menu" },
+ { PXENV_STATUS_BSTRAP_MCAST_ADDR, "Bootstrap mcast addr" },
+ { PXENV_STATUS_BSTRAP_MISSING_LIST, "Bootstrap missing list" },
+ { PXENV_STATUS_BSTRAP_NO_RESPONSE, "Bootstrap no response" },
+ { PXENV_STATUS_BSTRAP_FILE_TOO_BIG, "Bootstrap file too big" },
+ { PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE,
+ "BINL canceled by keystroke" },
+ { PXENV_STATUS_BINL_NO_PXE_SERVER, "BINL no PXE server" },
+ { PXENV_STATUS_NOT_AVAILABLE_IN_PMODE,
+ "Not available in protected mode" },
+ { PXENV_STATUS_NOT_AVAILABLE_IN_RMODE, "Not available in real mode" },
+ { PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED,
+ "BUSD device not supported" },
+ { PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY,
+ "Loader no free base memory" },
+ { PXENV_STATUS_LOADER_NO_BC_ROMID, "Loader no Base Code ROM ID" },
+ { PXENV_STATUS_LOADER_BAD_BC_ROMID, "Loader bad Base Code ROM ID" },
+ { PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE,
+ "Loader bad Base Code runtime image" },
+ { PXENV_STATUS_LOADER_NO_UNDI_ROMID, "Loader no UNDI ROM ID" },
+ { PXENV_STATUS_LOADER_BAD_UNDI_ROMID, "Loader bad UNDI ROM ID" },
+ { PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE,
+ "Loader bad UNDI driver image" },
+ { PXENV_STATUS_LOADER_NO_PXE_STRUCT, "Loader no !PXE struct" },
+ { PXENV_STATUS_LOADER_NO_PXENV_STRUCT, "Loader no PXENV+ struct" },
+ { PXENV_STATUS_LOADER_UNDI_START, "Loader UNDI start" },
+ { PXENV_STATUS_LOADER_BC_START, "Loader Base Code start" },
+};
diff --git a/gpxe/src/interface/pxe/pxe_file.c b/gpxe/src/interface/pxe/pxe_file.c
new file mode 100644
index 00000000..41674588
--- /dev/null
+++ b/gpxe/src/interface/pxe/pxe_file.c
@@ -0,0 +1,264 @@
+/** @file
+ *
+ * PXE FILE API
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/posix_io.h>
+#include <gpxe/features.h>
+#include <pxe.h>
+
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FEATURE ( FEATURE_MISC, "PXEXT", DHCP_EB_FEATURE_PXE_EXT, 2 );
+
+/**
+ * FILE OPEN
+ *
+ * @v file_open Pointer to a struct s_PXENV_FILE_OPEN
+ * @v s_PXENV_FILE_OPEN::FileName URL of file to open
+ * @ret #PXENV_EXIT_SUCCESS File was opened
+ * @ret #PXENV_EXIT_FAILURE File was not opened
+ * @ret s_PXENV_FILE_OPEN::Status PXE status code
+ * @ret s_PXENV_FILE_OPEN::FileHandle Handle of opened file
+ *
+ */
+PXENV_EXIT_t pxenv_file_open ( struct s_PXENV_FILE_OPEN *file_open ) {
+ userptr_t filename;
+ size_t filename_len;
+ int fd;
+
+ DBG ( "PXENV_FILE_OPEN" );
+
+ /* Copy name from external program, and open it */
+ filename = real_to_user ( file_open->FileName.segment,
+ file_open->FileName.offset );
+ filename_len = strlen_user ( filename, 0 );
+ {
+ char uri_string[ filename_len + 1 ];
+
+ copy_from_user ( uri_string, filename, 0,
+ sizeof ( uri_string ) );
+ DBG ( " %s", uri_string );
+ fd = open ( uri_string );
+ }
+
+ if ( fd < 0 ) {
+ file_open->Status = PXENV_STATUS ( fd );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBG ( " as file %d", fd );
+
+ file_open->FileHandle = fd;
+ file_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE CLOSE
+ *
+ * @v file_close Pointer to a struct s_PXENV_FILE_CLOSE
+ * @v s_PXENV_FILE_CLOSE::FileHandle File handle
+ * @ret #PXENV_EXIT_SUCCESS File was closed
+ * @ret #PXENV_EXIT_FAILURE File was not closed
+ * @ret s_PXENV_FILE_CLOSE::Status PXE status code
+ *
+ */
+PXENV_EXIT_t pxenv_file_close ( struct s_PXENV_FILE_CLOSE *file_close ) {
+
+ DBG ( "PXENV_FILE_CLOSE %d", file_close->FileHandle );
+
+ close ( file_close->FileHandle );
+ file_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE SELECT
+ *
+ * @v file_select Pointer to a struct s_PXENV_FILE_SELECT
+ * @v s_PXENV_FILE_SELECT::FileHandle File handle
+ * @ret #PXENV_EXIT_SUCCESS File has been checked for readiness
+ * @ret #PXENV_EXIT_FAILURE File has not been checked for readiness
+ * @ret s_PXENV_FILE_SELECT::Status PXE status code
+ * @ret s_PXENV_FILE_SELECT::Ready Indication of readiness
+ *
+ */
+PXENV_EXIT_t pxenv_file_select ( struct s_PXENV_FILE_SELECT *file_select ) {
+ fd_set fdset;
+ int ready;
+
+ DBG ( "PXENV_FILE_SELECT %d", file_select->FileHandle );
+
+ FD_ZERO ( &fdset );
+ FD_SET ( file_select->FileHandle, &fdset );
+ if ( ( ready = select ( &fdset, 0 ) ) < 0 ) {
+ file_select->Status = PXENV_STATUS ( ready );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ file_select->Ready = ( ready ? RDY_READ : 0 );
+ file_select->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE READ
+ *
+ * @v file_read Pointer to a struct s_PXENV_FILE_READ
+ * @v s_PXENV_FILE_READ::FileHandle File handle
+ * @v s_PXENV_FILE_READ::BufferSize Size of data buffer
+ * @v s_PXENV_FILE_READ::Buffer Data buffer
+ * @ret #PXENV_EXIT_SUCCESS Data has been read from file
+ * @ret #PXENV_EXIT_FAILURE Data has not been read from file
+ * @ret s_PXENV_FILE_READ::Status PXE status code
+ * @ret s_PXENV_FILE_READ::Ready Indication of readiness
+ * @ret s_PXENV_FILE_READ::BufferSize Length of data read
+ *
+ */
+PXENV_EXIT_t pxenv_file_read ( struct s_PXENV_FILE_READ *file_read ) {
+ userptr_t buffer;
+ ssize_t len;
+
+ DBG ( "PXENV_FILE_READ %d to %04x:%04x+%04x", file_read->FileHandle,
+ file_read->Buffer.segment, file_read->Buffer.offset,
+ file_read->BufferSize );
+
+ buffer = real_to_user ( file_read->Buffer.segment,
+ file_read->Buffer.offset );
+ if ( ( len = read_user ( file_read->FileHandle, buffer, 0,
+ file_read->BufferSize ) ) < 0 ) {
+ file_read->Status = PXENV_STATUS ( len );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBG ( " read %04zx", ( ( size_t ) len ) );
+
+ file_read->BufferSize = len;
+ file_read->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * GET FILE SIZE
+ *
+ * @v get_file_size Pointer to a struct s_PXENV_GET_FILE_SIZE
+ * @v s_PXENV_GET_FILE_SIZE::FileHandle File handle
+ * @ret #PXENV_EXIT_SUCCESS File size has been determined
+ * @ret #PXENV_EXIT_FAILURE File size has not been determined
+ * @ret s_PXENV_GET_FILE_SIZE::Status PXE status code
+ * @ret s_PXENV_GET_FILE_SIZE::FileSize Size of file
+ */
+PXENV_EXIT_t pxenv_get_file_size ( struct s_PXENV_GET_FILE_SIZE
+ *get_file_size ) {
+ ssize_t filesize;
+
+ DBG ( "PXENV_GET_FILE_SIZE %d", get_file_size->FileHandle );
+
+ filesize = fsize ( get_file_size->FileHandle );
+ if ( filesize < 0 ) {
+ get_file_size->Status = PXENV_STATUS ( filesize );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ DBG ( " is %zd", ( ( size_t ) filesize ) );
+
+ get_file_size->FileSize = filesize;
+ get_file_size->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE EXEC
+ *
+ * @v file_exec Pointer to a struct s_PXENV_FILE_EXEC
+ * @v s_PXENV_FILE_EXEC::Command Command to execute
+ * @ret #PXENV_EXIT_SUCCESS Command was executed successfully
+ * @ret #PXENV_EXIT_FAILURE Command was not executed successfully
+ * @ret s_PXENV_FILE_EXEC::Status PXE status code
+ *
+ */
+PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec ) {
+ userptr_t command;
+ size_t command_len;
+ int rc;
+
+ DBG ( "PXENV_FILE_EXEC" );
+
+ /* Copy name from external program, and exec it */
+ command = real_to_user ( file_exec->Command.segment,
+ file_exec->Command.offset );
+ command_len = strlen_user ( command, 0 );
+ {
+ char command_string[ command_len + 1 ];
+
+ copy_from_user ( command_string, command, 0,
+ sizeof ( command_string ) );
+ DBG ( " %s", command_string );
+
+ if ( ( rc = system ( command_string ) ) != 0 ) {
+ file_exec->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+ }
+
+ file_exec->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * FILE API CHECK
+ *
+ * @v file_exec Pointer to a struct s_PXENV_FILE_API_CHECK
+ * @v s_PXENV_FILE_API_CHECK::Magic Inbound magic number (0x91d447b2)
+ * @ret #PXENV_EXIT_SUCCESS Command was executed successfully
+ * @ret #PXENV_EXIT_FAILURE Command was not executed successfully
+ * @ret s_PXENV_FILE_API_CHECK::Status PXE status code
+ * @ret s_PXENV_FILE_API_CHECK::Magic Outbound magic number (0xe9c17b20)
+ * @ret s_PXENV_FILE_API_CHECK::Provider "gPXE" (0x45585067)
+ * @ret s_PXENV_FILE_API_CHECK::APIMask API function bitmask
+ * @ret s_PXENV_FILE_API_CHECK::Flags Reserved
+ *
+ */
+PXENV_EXIT_t pxenv_file_api_check ( struct s_PXENV_FILE_API_CHECK *file_api_check ) {
+ DBG ( "PXENV_FILE_API_CHECK" );
+
+ if ( file_api_check->Magic != 0x91d447b2 ) {
+ file_api_check->Status = PXENV_STATUS_BAD_FUNC;
+ return PXENV_EXIT_FAILURE;
+ } else if ( file_api_check->Size <
+ sizeof(struct s_PXENV_FILE_API_CHECK) ) {
+ file_api_check->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ } else {
+ file_api_check->Status = PXENV_STATUS_SUCCESS;
+ file_api_check->Size = sizeof(struct s_PXENV_FILE_API_CHECK);
+ file_api_check->Magic = 0xe9c17b20;
+ file_api_check->Provider = 0x45585067; /* "gPXE" */
+ file_api_check->APIMask = 0x0000007f; /* Functions e0-e6 */
+ file_api_check->Flags = 0; /* None defined */
+ return PXENV_EXIT_SUCCESS;
+ }
+}
diff --git a/gpxe/src/interface/pxe/pxe_loader.c b/gpxe/src/interface/pxe/pxe_loader.c
new file mode 100644
index 00000000..708d203a
--- /dev/null
+++ b/gpxe/src/interface/pxe/pxe_loader.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/init.h>
+#include "pxe.h"
+#include "pxe_call.h"
+
+/** @file
+ *
+ * PXE UNDI loader
+ *
+ */
+
+/* PXENV_UNDI_LOADER
+ *
+ */
+PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ) {
+
+ DBG ( "[PXENV_UNDI_LOADER to CS %04x DS %04x]",
+ undi_loader->UNDI_CS, undi_loader->UNDI_DS );
+
+ /* Perform one-time initialisation (e.g. heap) */
+ initialise();
+
+ /* Set up PXE data structures */
+ pxe_init_structures();
+
+ /* Fill in UNDI loader structure */
+ undi_loader->PXEptr.segment = rm_cs;
+ undi_loader->PXEptr.offset =
+ ( ( unsigned ) & __from_text16 ( ppxe ) );
+ undi_loader->PXENVptr.segment = rm_cs;
+ undi_loader->PXENVptr.offset =
+ ( ( unsigned ) & __from_text16 ( pxenv ) );
+
+ undi_loader->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
diff --git a/gpxe/src/interface/pxe/pxe_preboot.c b/gpxe/src/interface/pxe/pxe_preboot.c
new file mode 100644
index 00000000..302953eb
--- /dev/null
+++ b/gpxe/src/interface/pxe/pxe_preboot.c
@@ -0,0 +1,353 @@
+/** @file
+ *
+ * PXE Preboot API
+ *
+ */
+
+/* PXE API interface for Etherboot.
+ *
+ * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/fakedhcp.h>
+#include <gpxe/device.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/isapnp.h>
+#include <gpxe/init.h>
+#include <gpxe/if_ether.h>
+#include <basemem_packet.h>
+#include "pxe.h"
+#include "pxe_call.h"
+
+/* Avoid dragging in isapnp.o unnecessarily */
+uint16_t isapnp_read_port;
+
+/** Zero-based versions of PXENV_GET_CACHED_INFO::PacketType */
+enum pxe_cached_info_indices {
+ CACHED_INFO_DHCPDISCOVER = ( PXENV_PACKET_TYPE_DHCP_DISCOVER - 1 ),
+ CACHED_INFO_DHCPACK = ( PXENV_PACKET_TYPE_DHCP_ACK - 1 ),
+ CACHED_INFO_BINL = ( PXENV_PACKET_TYPE_CACHED_REPLY - 1 ),
+ NUM_CACHED_INFOS
+};
+
+/** A cached DHCP packet */
+union pxe_cached_info {
+ struct dhcphdr dhcphdr;
+ /* This buffer must be *exactly* the size of a BOOTPLAYER_t
+ * structure, otherwise WinPE will die horribly. It takes the
+ * size of *our* buffer and feeds it in to us as the size of
+ * one of *its* buffers. If our buffer is larger than it
+ * expects, we therefore end up overwriting part of its data
+ * segment, since it tells us to do so. (D'oh!)
+ *
+ * Note that a BOOTPLAYER_t is not necessarily large enough to
+ * hold a DHCP packet; this is a flaw in the PXE spec.
+ */
+ BOOTPLAYER_t packet;
+} __attribute__ (( packed ));
+
+/** A PXE DHCP packet creator */
+struct pxe_dhcp_packet_creator {
+ /** Create DHCP packet
+ *
+ * @v netdev Network device
+ * @v data Buffer for DHCP packet
+ * @v max_len Size of DHCP packet buffer
+ * @ret rc Return status code
+ */
+ int ( * create ) ( struct net_device *netdev, void *data,
+ size_t max_len );
+};
+
+/** PXE DHCP packet creators */
+static struct pxe_dhcp_packet_creator pxe_dhcp_packet_creators[] = {
+ [CACHED_INFO_DHCPDISCOVER] = { create_fakedhcpdiscover },
+ [CACHED_INFO_DHCPACK] = { create_fakedhcpack },
+ [CACHED_INFO_BINL] = { create_fakeproxydhcpack },
+};
+
+/* The case in which the caller doesn't supply a buffer is really
+ * awkward to support given that we have multiple sources of options,
+ * and that we don't actually store the DHCP packets. (We may not
+ * even have performed DHCP; we may have obtained all configuration
+ * from non-volatile stored options or from the command line.)
+ *
+ * Some NBPs rely on the buffers we provide being persistent, so we
+ * can't just use the temporary packet buffer. 4.5kB of base memory
+ * always wasted just because some clients are too lazy to provide
+ * their own buffers...
+ */
+static union pxe_cached_info __bss16_array ( cached_info, [NUM_CACHED_INFOS] );
+#define cached_info __use_data16 ( cached_info )
+
+/**
+ * Set PXE cached TFTP filename
+ *
+ * @v filename TFTP filename
+ *
+ * This is a bug-for-bug compatibility hack needed in order to work
+ * with Microsoft Remote Installation Services (RIS). The filename
+ * used in a call to PXENV_RESTART_TFTP or PXENV_TFTP_READ_FILE must
+ * be returned as the DHCP filename in subsequent calls to
+ * PXENV_GET_CACHED_INFO.
+ */
+void pxe_set_cached_filename ( const unsigned char *filename ) {
+ memcpy ( cached_info[CACHED_INFO_DHCPACK].dhcphdr.file, filename,
+ sizeof ( cached_info[CACHED_INFO_DHCPACK].dhcphdr.file ) );
+ memcpy ( cached_info[CACHED_INFO_BINL].dhcphdr.file, filename,
+ sizeof ( cached_info[CACHED_INFO_BINL].dhcphdr.file ) );
+}
+
+/**
+ * UNLOAD BASE CODE STACK
+ *
+ * @v None -
+ * @ret ...
+ *
+ */
+PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
+ DBG ( "PXENV_UNLOAD_STACK" );
+
+ unload_stack->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_GET_CACHED_INFO
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
+ *get_cached_info ) {
+ struct pxe_dhcp_packet_creator *creator;
+ union pxe_cached_info *info;
+ unsigned int idx;
+ size_t len;
+ userptr_t buffer;
+ int rc;
+
+ DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
+
+ DBG ( " to %04x:%04x+%x", get_cached_info->Buffer.segment,
+ get_cached_info->Buffer.offset, get_cached_info->BufferSize );
+
+ /* Sanity check */
+ idx = ( get_cached_info->PacketType - 1 );
+ if ( idx >= NUM_CACHED_INFOS ) {
+ DBG ( " bad PacketType" );
+ goto err;
+ }
+ info = &cached_info[idx];
+
+ /* Construct cached version of packet, if not already constructed. */
+ if ( ! info->dhcphdr.op ) {
+ /* Construct DHCP packet */
+ creator = &pxe_dhcp_packet_creators[idx];
+ if ( ( rc = creator->create ( pxe_netdev, info,
+ sizeof ( *info ) ) ) != 0 ) {
+ DBG ( " failed to build packet" );
+ goto err;
+ }
+ }
+
+ len = get_cached_info->BufferSize;
+ if ( len == 0 ) {
+ /* Point client at our cached buffer.
+ *
+ * To add to the fun, Intel decided at some point in
+ * the evolution of the PXE specification to add the
+ * BufferLimit field, which we are meant to fill in
+ * with the length of our packet buffer, so that the
+ * caller can safely modify the boot server reply
+ * packet stored therein. However, this field was not
+ * present in earlier versions of the PXE spec, and
+ * there is at least one PXE NBP (Altiris) which
+ * allocates only exactly enough space for this
+ * earlier, shorter version of the structure. If we
+ * actually fill in the BufferLimit field, we
+ * therefore risk trashing random areas of the
+ * caller's memory. If we *don't* fill it in, then
+ * the caller is at liberty to assume that whatever
+ * random value happened to be in that location
+ * represents the length of the buffer we've just
+ * passed back to it.
+ *
+ * Since older PXE stacks won't fill this field in
+ * anyway, it's probably safe to assume that no
+ * callers actually rely on it, so we choose to not
+ * fill it in.
+ */
+ get_cached_info->Buffer.segment = rm_ds;
+ get_cached_info->Buffer.offset =
+ ( unsigned ) ( __from_data16 ( info ) );
+ get_cached_info->BufferSize = sizeof ( *info );
+ DBG ( " returning %04x:%04x+%04x['%x']",
+ get_cached_info->Buffer.segment,
+ get_cached_info->Buffer.offset,
+ get_cached_info->BufferSize,
+ get_cached_info->BufferLimit );
+ } else {
+ /* Copy packet to client buffer */
+ if ( len > sizeof ( *info ) )
+ len = sizeof ( *info );
+ if ( len < sizeof ( *info ) )
+ DBG ( " buffer may be too short" );
+ buffer = real_to_user ( get_cached_info->Buffer.segment,
+ get_cached_info->Buffer.offset );
+ copy_to_user ( buffer, 0, info, len );
+ get_cached_info->BufferSize = len;
+ }
+
+ get_cached_info->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+
+ err:
+ get_cached_info->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_RESTART_TFTP
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE
+ *restart_tftp ) {
+ PXENV_EXIT_t tftp_exit;
+
+ DBG ( "PXENV_RESTART_TFTP " );
+
+ /* Intel bug-for-bug hack */
+ pxe_set_cached_filename ( restart_tftp->FileName );
+
+ /* Words cannot describe the complete mismatch between the PXE
+ * specification and any possible version of reality...
+ */
+ restart_tftp->Buffer = PXE_LOAD_PHYS; /* Fixed by spec, apparently */
+ restart_tftp->BufferSize = ( 0xa0000 - PXE_LOAD_PHYS ); /* Near enough */
+ tftp_exit = pxenv_tftp_read_file ( restart_tftp );
+ if ( tftp_exit != PXENV_EXIT_SUCCESS )
+ return tftp_exit;
+
+ /* Fire up the new NBP */
+ restart_tftp->Status = pxe_start_nbp();
+
+ /* Not sure what "SUCCESS" actually means, since we can only
+ * return if the new NBP failed to boot...
+ */
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_START_UNDI
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
+ unsigned int bus_type;
+ unsigned int location;
+ struct net_device *netdev;
+
+ DBG ( "PXENV_START_UNDI %04x:%04x:%04x",
+ start_undi->AX, start_undi->BX, start_undi->DX );
+
+ /* Determine bus type and location. Use a heuristic to decide
+ * whether we are PCI or ISAPnP
+ */
+ if ( ( start_undi->DX >= ISAPNP_READ_PORT_MIN ) &&
+ ( start_undi->DX <= ISAPNP_READ_PORT_MAX ) &&
+ ( start_undi->BX >= ISAPNP_CSN_MIN ) &&
+ ( start_undi->BX <= ISAPNP_CSN_MAX ) ) {
+ bus_type = BUS_TYPE_ISAPNP;
+ location = start_undi->BX;
+ /* Record ISAPnP read port for use by isapnp.c */
+ isapnp_read_port = start_undi->DX;
+ } else {
+ bus_type = BUS_TYPE_PCI;
+ location = start_undi->AX;
+ }
+
+ /* Probe for devices, etc. */
+ startup();
+
+ /* Look for a matching net device */
+ netdev = find_netdev_by_location ( bus_type, location );
+ if ( ! netdev ) {
+ DBG ( " no net device found" );
+ start_undi->Status = PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC;
+ return PXENV_EXIT_FAILURE;
+ }
+ DBG ( " using netdev %s", netdev->name );
+
+ /* Save as PXE net device */
+ pxe_set_netdev ( netdev );
+
+ /* Hook INT 1A */
+ pxe_hook_int1a();
+
+ start_undi->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_STOP_UNDI
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) {
+ DBG ( "PXENV_STOP_UNDI" );
+
+ /* Unhook INT 1A */
+ pxe_unhook_int1a();
+
+ /* Clear PXE net device */
+ pxe_set_netdev ( NULL );
+
+ /* Prepare for unload */
+ shutdown();
+
+ stop_undi->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_START_BASE
+ *
+ * Status: won't implement (requires major structural changes)
+ */
+PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ) {
+ DBG ( "PXENV_START_BASE" );
+
+ start_base->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_STOP_BASE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) {
+ DBG ( "PXENV_STOP_BASE" );
+
+ /* The only time we will be called is when the NBP is trying
+ * to shut down the PXE stack. There's nothing we need to do
+ * in this call.
+ */
+
+ stop_base->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
diff --git a/gpxe/src/interface/pxe/pxe_tftp.c b/gpxe/src/interface/pxe/pxe_tftp.c
new file mode 100644
index 00000000..976298a8
--- /dev/null
+++ b/gpxe/src/interface/pxe/pxe_tftp.c
@@ -0,0 +1,584 @@
+/** @file
+ *
+ * PXE TFTP API
+ *
+ */
+
+/*
+ * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/in.h>
+#include <gpxe/tftp.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/process.h>
+#include <pxe.h>
+
+/** A PXE TFTP connection */
+struct pxe_tftp_connection {
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+ /** Data buffer */
+ userptr_t buffer;
+ /** Size of data buffer */
+ size_t size;
+ /** Starting offset of data buffer */
+ size_t start;
+ /** File position */
+ size_t offset;
+ /** Maximum file position */
+ size_t max_offset;
+ /** Block size */
+ size_t blksize;
+ /** Block index */
+ unsigned int blkidx;
+ /** Overall return status code */
+ int rc;
+};
+
+/** The PXE TFTP connection */
+static struct pxe_tftp_connection pxe_tftp = {
+ .xfer = XFER_INIT ( &null_xfer_ops ),
+};
+
+/**
+ * Close PXE TFTP connection
+ *
+ * @v rc Final status code
+ */
+static void pxe_tftp_close ( int rc ) {
+ xfer_nullify ( &pxe_tftp.xfer );
+ xfer_close ( &pxe_tftp.xfer, rc );
+ pxe_tftp.rc = rc;
+}
+
+/**
+ * Receive new data
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf I/O buffer
+ * @v meta Transfer metadata
+ * @ret rc Return status code
+ */
+static int pxe_tftp_xfer_deliver_iob ( struct xfer_interface *xfer __unused,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ size_t len = iob_len ( iobuf );
+ int rc = 0;
+
+ /* Calculate new buffer position */
+ if ( meta->whence != SEEK_CUR )
+ pxe_tftp.offset = 0;
+ pxe_tftp.offset += meta->offset;
+
+ /* Copy data block to buffer */
+ if ( len == 0 ) {
+ /* No data (pure seek); treat as success */
+ } else if ( pxe_tftp.offset < pxe_tftp.start ) {
+ DBG ( " buffer underrun at %zx (min %zx)",
+ pxe_tftp.offset, pxe_tftp.start );
+ rc = -ENOBUFS;
+ } else if ( ( pxe_tftp.offset + len ) >
+ ( pxe_tftp.start + pxe_tftp.size ) ) {
+ DBG ( " buffer overrun at %zx (max %zx)",
+ ( pxe_tftp.offset + len ),
+ ( pxe_tftp.start + pxe_tftp.size ) );
+ rc = -ENOBUFS;
+ } else {
+ copy_to_user ( pxe_tftp.buffer,
+ ( pxe_tftp.offset - pxe_tftp.start ),
+ iobuf->data, len );
+ }
+
+ /* Calculate new buffer position */
+ pxe_tftp.offset += len;
+
+ /* Mildly ugly hack; assume that the first non-zero seek
+ * indicates the block size.
+ */
+ if ( pxe_tftp.blksize == 0 )
+ pxe_tftp.blksize = pxe_tftp.offset;
+
+ /* Record maximum offset as the file size */
+ if ( pxe_tftp.max_offset < pxe_tftp.offset )
+ pxe_tftp.max_offset = pxe_tftp.offset;
+
+ /* Terminate transfer on error */
+ if ( rc != 0 )
+ pxe_tftp_close ( rc );
+
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * Handle close() event
+ *
+ * @v xfer Data transfer interface
+ * @v rc Reason for close
+ */
+static void pxe_tftp_xfer_close ( struct xfer_interface *xfer __unused,
+ int rc ) {
+ pxe_tftp_close ( rc );
+}
+
+static struct xfer_interface_operations pxe_tftp_xfer_ops = {
+ .close = pxe_tftp_xfer_close,
+ .vredirect = xfer_vopen,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = pxe_tftp_xfer_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/**
+ * Maximum length of a PXE TFTP URI
+ *
+ * The PXE TFTP API provides 128 characters for the filename; the
+ * extra 128 bytes allow for the remainder of the URI.
+ */
+#define PXE_TFTP_URI_LEN 256
+
+/**
+ * Open PXE TFTP connection
+ *
+ * @v ipaddress IP address
+ * @v port TFTP server port
+ * @v filename File name
+ * @v blksize Requested block size
+ * @ret rc Return status code
+ */
+static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port,
+ const unsigned char *filename, size_t blksize ) {
+ char uri_string[PXE_TFTP_URI_LEN];
+ struct in_addr address;
+ int rc;
+
+ /* Intel bug-for-bug hack */
+ pxe_set_cached_filename ( filename );
+
+ /* Reset PXE TFTP connection structure */
+ memset ( &pxe_tftp, 0, sizeof ( pxe_tftp ) );
+ xfer_init ( &pxe_tftp.xfer, &pxe_tftp_xfer_ops, NULL );
+ pxe_tftp.rc = -EINPROGRESS;
+
+ /* Construct URI string */
+ address.s_addr = ipaddress;
+ if ( ! port )
+ port = htons ( TFTP_PORT );
+ if ( blksize < TFTP_DEFAULT_BLKSIZE )
+ blksize = TFTP_DEFAULT_BLKSIZE;
+ snprintf ( uri_string, sizeof ( uri_string ),
+ "tftp://%s:%d%s%s?blksize=%d",
+ inet_ntoa ( address ), ntohs ( port ),
+ ( ( filename[0] == '/' ) ? "" : "/" ), filename, blksize );
+ DBG ( " %s", uri_string );
+
+ /* Open PXE TFTP connection */
+ if ( ( rc = xfer_open_uri_string ( &pxe_tftp.xfer,
+ uri_string ) ) != 0 ) {
+ DBG ( " could not open (%s)\n", strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * TFTP OPEN
+ *
+ * @v tftp_open Pointer to a struct s_PXENV_TFTP_OPEN
+ * @v s_PXENV_TFTP_OPEN::ServerIPAddress TFTP server IP address
+ * @v s_PXENV_TFTP_OPEN::GatewayIPAddress Relay agent IP address, or 0.0.0.0
+ * @v s_PXENV_TFTP_OPEN::FileName Name of file to open
+ * @v s_PXENV_TFTP_OPEN::TFTPPort TFTP server UDP port
+ * @v s_PXENV_TFTP_OPEN::PacketSize TFTP blksize option to request
+ * @ret #PXENV_EXIT_SUCCESS File was opened
+ * @ret #PXENV_EXIT_FAILURE File was not opened
+ * @ret s_PXENV_TFTP_OPEN::Status PXE status code
+ * @ret s_PXENV_TFTP_OPEN::PacketSize Negotiated blksize
+ * @err #PXENV_STATUS_TFTP_INVALID_PACKET_SIZE Requested blksize too small
+ *
+ * Opens a TFTP connection for downloading a file a block at a time
+ * using pxenv_tftp_read().
+ *
+ * If s_PXENV_TFTP_OPEN::GatewayIPAddress is 0.0.0.0, normal IP
+ * routing will take place. See the relevant
+ * @ref pxe_routing "implementation note" for more details.
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note According to the PXE specification version 2.1, this call
+ * "opens a file for reading/writing", though how writing is to be
+ * achieved without the existence of an API call %pxenv_tftp_write()
+ * is not made clear.
+ *
+ * @note Despite the existence of the numerous statements within the
+ * PXE specification of the form "...if a TFTP/MTFTP or UDP connection
+ * is active...", you cannot use pxenv_tftp_open() and
+ * pxenv_tftp_read() to read a file via MTFTP; only via plain old
+ * TFTP. If you want to use MTFTP, use pxenv_tftp_read_file()
+ * instead. Astute readers will note that, since
+ * pxenv_tftp_read_file() is an atomic operation from the point of
+ * view of the PXE API, it is conceptually impossible to issue any
+ * other PXE API call "if an MTFTP connection is active".
+ */
+PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_OPEN" );
+
+ /* Guard against callers that fail to close before re-opening */
+ pxe_tftp_close ( 0 );
+
+ /* Open connection */
+ if ( ( rc = pxe_tftp_open ( tftp_open->ServerIPAddress,
+ tftp_open->TFTPPort,
+ tftp_open->FileName,
+ tftp_open->PacketSize ) ) != 0 ) {
+ tftp_open->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Wait for OACK to arrive so that we have the block size */
+ while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
+ ( pxe_tftp.blksize == 0 ) ) {
+ step();
+ }
+ tftp_open->PacketSize = pxe_tftp.blksize;
+
+ /* EINPROGRESS is normal; we don't wait for the whole transfer */
+ if ( rc == -EINPROGRESS )
+ rc = 0;
+
+ tftp_open->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
+
+/**
+ * TFTP CLOSE
+ *
+ * @v tftp_close Pointer to a struct s_PXENV_TFTP_CLOSE
+ * @ret #PXENV_EXIT_SUCCESS File was closed successfully
+ * @ret #PXENV_EXIT_FAILURE File was not closed
+ * @ret s_PXENV_TFTP_CLOSE::Status PXE status code
+ * @err None -
+ *
+ * Close a connection previously opened with pxenv_tftp_open(). You
+ * must have previously opened a connection with pxenv_tftp_open().
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ */
+PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) {
+ DBG ( "PXENV_TFTP_CLOSE" );
+
+ pxe_tftp_close ( 0 );
+ tftp_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * TFTP READ
+ *
+ * @v tftp_read Pointer to a struct s_PXENV_TFTP_READ
+ * @v s_PXENV_TFTP_READ::Buffer Address of data buffer
+ * @ret #PXENV_EXIT_SUCCESS Data was read successfully
+ * @ret #PXENV_EXIT_FAILURE Data was not read
+ * @ret s_PXENV_TFTP_READ::Status PXE status code
+ * @ret s_PXENV_TFTP_READ::PacketNumber TFTP packet number
+ * @ret s_PXENV_TFTP_READ::BufferSize Length of data written into buffer
+ *
+ * Reads a single packet from a connection previously opened with
+ * pxenv_tftp_open() into the data buffer pointed to by
+ * s_PXENV_TFTP_READ::Buffer. You must have previously opened a
+ * connection with pxenv_tftp_open(). The data written into
+ * s_PXENV_TFTP_READ::Buffer is just the file data; the various
+ * network headers have already been removed.
+ *
+ * The buffer must be large enough to contain a packet of the size
+ * negotiated via the s_PXENV_TFTP_OPEN::PacketSize field in the
+ * pxenv_tftp_open() call. It is worth noting that the PXE
+ * specification does @b not require the caller to fill in
+ * s_PXENV_TFTP_READ::BufferSize before calling pxenv_tftp_read(), so
+ * the PXE stack is free to ignore whatever value the caller might
+ * place there and just assume that the buffer is large enough. That
+ * said, it may be worth the caller always filling in
+ * s_PXENV_TFTP_READ::BufferSize to guard against PXE stacks that
+ * mistake it for an input parameter.
+ *
+ * The length of the TFTP data packet will be returned via
+ * s_PXENV_TFTP_READ::BufferSize. If this length is less than the
+ * blksize negotiated via s_PXENV_TFTP_OPEN::PacketSize in the call to
+ * pxenv_tftp_open(), this indicates that the block is the last block
+ * in the file. Note that zero is a valid length for
+ * s_PXENV_TFTP_READ::BufferSize, and will occur when the length of
+ * the file is a multiple of the blksize.
+ *
+ * The PXE specification doesn't actually state that calls to
+ * pxenv_tftp_read() will return the data packets in strict sequential
+ * order, though most PXE stacks will probably do so. The sequence
+ * number of the packet will be returned in
+ * s_PXENV_TFTP_READ::PacketNumber. The first packet in the file has
+ * a sequence number of one, not zero.
+ *
+ * To guard against flawed PXE stacks, the caller should probably set
+ * s_PXENV_TFTP_READ::PacketNumber to one less than the expected
+ * returned value (i.e. set it to zero for the first call to
+ * pxenv_tftp_read() and then re-use the returned s_PXENV_TFTP_READ
+ * parameter block for subsequent calls without modifying
+ * s_PXENV_TFTP_READ::PacketNumber between calls). The caller should
+ * also guard against potential problems caused by flawed
+ * implementations returning the occasional duplicate packet, by
+ * checking that the value returned in s_PXENV_TFTP_READ::PacketNumber
+ * is as expected (i.e. one greater than that returned from the
+ * previous call to pxenv_tftp_read()).
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ */
+PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_READ to %04x:%04x",
+ tftp_read->Buffer.segment, tftp_read->Buffer.offset );
+
+ /* Read single block into buffer */
+ pxe_tftp.buffer = real_to_user ( tftp_read->Buffer.segment,
+ tftp_read->Buffer.offset );
+ pxe_tftp.size = pxe_tftp.blksize;
+ pxe_tftp.start = pxe_tftp.offset;
+ while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
+ ( pxe_tftp.offset == pxe_tftp.start ) )
+ step();
+ pxe_tftp.buffer = UNULL;
+ tftp_read->BufferSize = ( pxe_tftp.offset - pxe_tftp.start );
+ tftp_read->PacketNumber = ++pxe_tftp.blkidx;
+
+ /* EINPROGRESS is normal if we haven't reached EOF yet */
+ if ( rc == -EINPROGRESS )
+ rc = 0;
+
+ tftp_read->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
+
+/**
+ * TFTP/MTFTP read file
+ *
+ * @v tftp_read_file Pointer to a struct s_PXENV_TFTP_READ_FILE
+ * @v s_PXENV_TFTP_READ_FILE::FileName File name
+ * @v s_PXENV_TFTP_READ_FILE::BufferSize Size of the receive buffer
+ * @v s_PXENV_TFTP_READ_FILE::Buffer Address of the receive buffer
+ * @v s_PXENV_TFTP_READ_FILE::ServerIPAddress TFTP server IP address
+ * @v s_PXENV_TFTP_READ_FILE::GatewayIPAddress Relay agent IP address
+ * @v s_PXENV_TFTP_READ_FILE::McastIPAddress File's multicast IP address
+ * @v s_PXENV_TFTP_READ_FILE::TFTPClntPort Client multicast UDP port
+ * @v s_PXENV_TFTP_READ_FILE::TFTPSrvPort Server multicast UDP port
+ * @v s_PXENV_TFTP_READ_FILE::TFTPOpenTimeOut Time to wait for first packet
+ * @v s_PXENV_TFTP_READ_FILE::TFTPReopenDelay MTFTP inactivity timeout
+ * @ret #PXENV_EXIT_SUCCESS File downloaded successfully
+ * @ret #PXENV_EXIT_FAILURE File not downloaded
+ * @ret s_PXENV_TFTP_READ_FILE::Status PXE status code
+ * @ret s_PXENV_TFTP_READ_FILE::BufferSize Length of downloaded file
+ *
+ * Downloads an entire file via either TFTP or MTFTP into the buffer
+ * pointed to by s_PXENV_TFTP_READ_FILE::Buffer.
+ *
+ * The PXE specification does not make it clear how the caller
+ * requests that MTFTP be used rather than TFTP (or vice versa). One
+ * reasonable guess is that setting
+ * s_PXENV_TFTP_READ_FILE::McastIPAddress to 0.0.0.0 would cause TFTP
+ * to be used instead of MTFTP, though it is conceivable that some PXE
+ * stacks would interpret that as "use the DHCP-provided multicast IP
+ * address" instead. Some PXE stacks will not implement MTFTP at all,
+ * and will always use TFTP.
+ *
+ * It is not specified whether or not
+ * s_PXENV_TFTP_READ_FILE::TFTPSrvPort will be used as the TFTP server
+ * port for TFTP (rather than MTFTP) downloads. Callers should assume
+ * that the only way to access a TFTP server on a non-standard port is
+ * to use pxenv_tftp_open() and pxenv_tftp_read().
+ *
+ * If s_PXENV_TFTP_READ_FILE::GatewayIPAddress is 0.0.0.0, normal IP
+ * routing will take place. See the relevant
+ * @ref pxe_routing "implementation note" for more details.
+ *
+ * It is interesting to note that s_PXENV_TFTP_READ_FILE::Buffer is an
+ * #ADDR32_t type, i.e. nominally a flat physical address. Some PXE
+ * NBPs (e.g. NTLDR) are known to call pxenv_tftp_read_file() in real
+ * mode with s_PXENV_TFTP_READ_FILE::Buffer set to an address above
+ * 1MB. This means that PXE stacks must be prepared to write to areas
+ * outside base memory. Exactly how this is to be achieved is not
+ * specified, though using INT 15,87 is as close to a standard method
+ * as any, and should probably be used. Switching to protected-mode
+ * in order to access high memory will fail if pxenv_tftp_read_file()
+ * is called in V86 mode; it is reasonably to expect that a V86
+ * monitor would intercept the relatively well-defined INT 15,87 if it
+ * wants the PXE stack to be able to write to high memory.
+ *
+ * Things get even more interesting if pxenv_tftp_read_file() is
+ * called in protected mode, because there is then absolutely no way
+ * for the PXE stack to write to an absolute physical address. You
+ * can't even get around the problem by creating a special "access
+ * everything" segment in the s_PXE data structure, because the
+ * #SEGDESC_t descriptors are limited to 64kB in size.
+ *
+ * Previous versions of the PXE specification (e.g. WfM 1.1a) provide
+ * a separate API call, %pxenv_tftp_read_file_pmode(), specifically to
+ * work around this problem. The s_PXENV_TFTP_READ_FILE_PMODE
+ * parameter block splits s_PXENV_TFTP_READ_FILE::Buffer into
+ * s_PXENV_TFTP_READ_FILE_PMODE::BufferSelector and
+ * s_PXENV_TFTP_READ_FILE_PMODE::BufferOffset, i.e. it provides a
+ * protected-mode segment:offset address for the data buffer. This
+ * API call is no longer present in version 2.1 of the PXE
+ * specification.
+ *
+ * Etherboot makes the assumption that s_PXENV_TFTP_READ_FILE::Buffer
+ * is an offset relative to the caller's data segment, when
+ * pxenv_tftp_read_file() is called in protected mode.
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note Microsoft's NTLDR assumes that the filename passed in via
+ * s_PXENV_TFTP_READ_FILE::FileName will be stored in the "file" field
+ * of the stored DHCPACK packet, whence it will be returned via any
+ * subsequent calls to pxenv_get_cached_info(). Though this is
+ * essentially a bug in the Intel PXE implementation (not, for once,
+ * in the specification!), it is a bug that Microsoft relies upon, and
+ * so we implement this bug-for-bug compatibility by overwriting the
+ * filename stored DHCPACK packet with the filename passed in
+ * s_PXENV_TFTP_READ_FILE::FileName.
+ *
+ */
+PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
+ *tftp_read_file ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_READ_FILE to %08lx+%lx", tftp_read_file->Buffer,
+ tftp_read_file->BufferSize );
+
+ /* Open TFTP file */
+ if ( ( rc = pxe_tftp_open ( tftp_read_file->ServerIPAddress, 0,
+ tftp_read_file->FileName, 0 ) ) != 0 ) {
+ tftp_read_file->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Read entire file */
+ pxe_tftp.buffer = phys_to_user ( tftp_read_file->Buffer );
+ pxe_tftp.size = tftp_read_file->BufferSize;
+ while ( ( rc = pxe_tftp.rc ) == -EINPROGRESS )
+ step();
+ pxe_tftp.buffer = UNULL;
+ tftp_read_file->BufferSize = pxe_tftp.max_offset;
+
+ /* Close TFTP file */
+ pxe_tftp_close ( rc );
+
+ tftp_read_file->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
+
+/**
+ * TFTP GET FILE SIZE
+ *
+ * @v tftp_get_fsize Pointer to a struct s_PXENV_TFTP_GET_FSIZE
+ * @v s_PXENV_TFTP_GET_FSIZE::ServerIPAddress TFTP server IP address
+ * @v s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress Relay agent IP address
+ * @v s_PXENV_TFTP_GET_FSIZE::FileName File name
+ * @ret #PXENV_EXIT_SUCCESS File size was determined successfully
+ * @ret #PXENV_EXIT_FAILURE File size was not determined
+ * @ret s_PXENV_TFTP_GET_FSIZE::Status PXE status code
+ * @ret s_PXENV_TFTP_GET_FSIZE::FileSize File size
+ *
+ * Determine the size of a file on a TFTP server. This uses the
+ * "tsize" TFTP option, and so will not work with a TFTP server that
+ * does not support TFTP options, or that does not support the "tsize"
+ * option.
+ *
+ * The PXE specification states that this API call will @b not open a
+ * TFTP connection for subsequent use with pxenv_tftp_read(). (This
+ * is somewhat daft, since the only way to obtain the file size via
+ * the "tsize" option involves issuing a TFTP open request, but that's
+ * life.)
+ *
+ * You cannot call pxenv_tftp_get_fsize() while a TFTP or UDP
+ * connection is open.
+ *
+ * If s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress is 0.0.0.0, normal IP
+ * routing will take place. See the relevant
+ * @ref pxe_routing "implementation note" for more details.
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note There is no way to specify the TFTP server port with this API
+ * call. Though you can open a file using a non-standard TFTP server
+ * port (via s_PXENV_TFTP_OPEN::TFTPPort or, potentially,
+ * s_PXENV_TFTP_READ_FILE::TFTPSrvPort), you can only get the size of
+ * a file from a TFTP server listening on the standard TFTP port.
+ * "Consistency" is not a word in Intel's vocabulary.
+ */
+PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
+ *tftp_get_fsize ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_GET_FSIZE" );
+
+ /* Open TFTP file */
+ if ( ( rc = pxe_tftp_open ( tftp_get_fsize->ServerIPAddress, 0,
+ tftp_get_fsize->FileName, 0 ) ) != 0 ) {
+ tftp_get_fsize->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Wait for initial seek to arrive, and record size */
+ while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
+ ( pxe_tftp.max_offset == 0 ) ) {
+ step();
+ }
+ tftp_get_fsize->FileSize = pxe_tftp.max_offset;
+
+ /* EINPROGRESS is normal; we don't wait for the whole transfer */
+ if ( rc == -EINPROGRESS )
+ rc = 0;
+
+ /* Close TFTP file */
+ pxe_tftp_close ( rc );
+
+ tftp_get_fsize->Status = PXENV_STATUS ( rc );
+ return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
+}
diff --git a/gpxe/src/interface/pxe/pxe_udp.c b/gpxe/src/interface/pxe/pxe_udp.c
new file mode 100644
index 00000000..40c2b2e5
--- /dev/null
+++ b/gpxe/src/interface/pxe/pxe_udp.c
@@ -0,0 +1,390 @@
+/** @file
+ *
+ * PXE UDP API
+ *
+ */
+
+#include <string.h>
+#include <byteswap.h>
+#include <gpxe/xfer.h>
+#include <gpxe/udp.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/process.h>
+#include <pxe.h>
+
+/*
+ * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/** A PXE UDP connection */
+struct pxe_udp_connection {
+ /** Data transfer interface to UDP stack */
+ struct xfer_interface xfer;
+ /** Local address */
+ struct sockaddr_in local;
+ /** Current PXENV_UDP_READ parameter block */
+ struct s_PXENV_UDP_READ *pxenv_udp_read;
+};
+
+/**
+ * Receive PXE UDP data
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf I/O buffer
+ * @v meta Data transfer metadata
+ * @ret rc Return status code
+ *
+ * Receives a packet as part of the current pxenv_udp_read()
+ * operation.
+ */
+static int pxe_udp_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct pxe_udp_connection *pxe_udp =
+ container_of ( xfer, struct pxe_udp_connection, xfer );
+ struct s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read;
+ struct sockaddr_in *sin_src;
+ struct sockaddr_in *sin_dest;
+ userptr_t buffer;
+ size_t len;
+ int rc = 0;
+
+ if ( ! pxenv_udp_read ) {
+ DBG ( "PXE discarded UDP packet\n" );
+ rc = -ENOBUFS;
+ goto done;
+ }
+
+ /* Copy packet to buffer and record length */
+ buffer = real_to_user ( pxenv_udp_read->buffer.segment,
+ pxenv_udp_read->buffer.offset );
+ len = iob_len ( iobuf );
+ if ( len > pxenv_udp_read->buffer_size )
+ len = pxenv_udp_read->buffer_size;
+ copy_to_user ( buffer, 0, iobuf->data, len );
+ pxenv_udp_read->buffer_size = len;
+
+ /* Fill in source/dest information */
+ assert ( meta );
+ sin_src = ( struct sockaddr_in * ) meta->src;
+ assert ( sin_src );
+ assert ( sin_src->sin_family == AF_INET );
+ pxenv_udp_read->src_ip = sin_src->sin_addr.s_addr;
+ pxenv_udp_read->s_port = sin_src->sin_port;
+ sin_dest = ( struct sockaddr_in * ) meta->dest;
+ assert ( sin_dest );
+ assert ( sin_dest->sin_family == AF_INET );
+ pxenv_udp_read->dest_ip = sin_dest->sin_addr.s_addr;
+ pxenv_udp_read->d_port = sin_dest->sin_port;
+
+ /* Mark as received */
+ pxe_udp->pxenv_udp_read = NULL;
+
+ done:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/** PXE UDP data transfer interface operations */
+static struct xfer_interface_operations pxe_udp_xfer_operations = {
+ .close = ignore_xfer_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = pxe_udp_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/** The PXE UDP connection */
+static struct pxe_udp_connection pxe_udp = {
+ .xfer = XFER_INIT ( &pxe_udp_xfer_operations ),
+ .local = {
+ .sin_family = AF_INET,
+ },
+};
+
+/**
+ * UDP OPEN
+ *
+ * @v pxenv_udp_open Pointer to a struct s_PXENV_UDP_OPEN
+ * @v s_PXENV_UDP_OPEN::src_ip IP address of this station, or 0.0.0.0
+ * @ret #PXENV_EXIT_SUCCESS Always
+ * @ret s_PXENV_UDP_OPEN::Status PXE status code
+ * @err #PXENV_STATUS_UDP_OPEN UDP connection already open
+ * @err #PXENV_STATUS_OUT_OF_RESOURCES Could not open connection
+ *
+ * Prepares the PXE stack for communication using pxenv_udp_write()
+ * and pxenv_udp_read().
+ *
+ * The IP address supplied in s_PXENV_UDP_OPEN::src_ip will be
+ * recorded and used as the local station's IP address for all further
+ * communication, including communication by means other than
+ * pxenv_udp_write() and pxenv_udp_read(). (If
+ * s_PXENV_UDP_OPEN::src_ip is 0.0.0.0, the local station's IP address
+ * will remain unchanged.)
+ *
+ * You can only have one open UDP connection at a time. This is not a
+ * meaningful restriction, since pxenv_udp_write() and
+ * pxenv_udp_read() allow you to specify arbitrary local and remote
+ * ports and an arbitrary remote address for each packet. According
+ * to the PXE specifiation, you cannot have a UDP connection open at
+ * the same time as a TFTP connection; this restriction does not apply
+ * to Etherboot.
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note The PXE specification does not make it clear whether the IP
+ * address supplied in s_PXENV_UDP_OPEN::src_ip should be used only
+ * for this UDP connection, or retained for all future communication.
+ * The latter seems more consistent with typical PXE stack behaviour.
+ *
+ * @note Etherboot currently ignores the s_PXENV_UDP_OPEN::src_ip
+ * parameter.
+ *
+ */
+PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) {
+ int rc;
+
+ DBG ( "PXENV_UDP_OPEN" );
+
+ /* Record source IP address */
+ pxe_udp.local.sin_addr.s_addr = pxenv_udp_open->src_ip;
+
+ /* Open promiscuous UDP connection */
+ xfer_close ( &pxe_udp.xfer, 0 );
+ if ( ( rc = udp_open_promisc ( &pxe_udp.xfer ) ) != 0 ) {
+ pxenv_udp_open->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ pxenv_udp_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * UDP CLOSE
+ *
+ * @v pxenv_udp_close Pointer to a struct s_PXENV_UDP_CLOSE
+ * @ret #PXENV_EXIT_SUCCESS Always
+ * @ret s_PXENV_UDP_CLOSE::Status PXE status code
+ * @err None -
+ *
+ * Closes a UDP connection opened with pxenv_udp_open().
+ *
+ * You can only have one open UDP connection at a time. You cannot
+ * have a UDP connection open at the same time as a TFTP connection.
+ * You cannot use pxenv_udp_close() to close a TFTP connection; use
+ * pxenv_tftp_close() instead.
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ */
+PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) {
+ DBG ( "PXENV_UDP_CLOSE" );
+
+ /* Close UDP connection */
+ xfer_close ( &pxe_udp.xfer, 0 );
+
+ pxenv_udp_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * UDP WRITE
+ *
+ * @v pxenv_udp_write Pointer to a struct s_PXENV_UDP_WRITE
+ * @v s_PXENV_UDP_WRITE::ip Destination IP address
+ * @v s_PXENV_UDP_WRITE::gw Relay agent IP address, or 0.0.0.0
+ * @v s_PXENV_UDP_WRITE::src_port Source UDP port, or 0
+ * @v s_PXENV_UDP_WRITE::dst_port Destination UDP port
+ * @v s_PXENV_UDP_WRITE::buffer_size Length of the UDP payload
+ * @v s_PXENV_UDP_WRITE::buffer Address of the UDP payload
+ * @ret #PXENV_EXIT_SUCCESS Packet was transmitted successfully
+ * @ret #PXENV_EXIT_FAILURE Packet could not be transmitted
+ * @ret s_PXENV_UDP_WRITE::Status PXE status code
+ * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open
+ * @err #PXENV_STATUS_UNDI_TRANSMIT_ERROR Could not transmit packet
+ *
+ * Transmits a single UDP packet. A valid IP and UDP header will be
+ * prepended to the payload in s_PXENV_UDP_WRITE::buffer; the buffer
+ * should not contain precomputed IP and UDP headers, nor should it
+ * contain space allocated for these headers. The first byte of the
+ * buffer will be transmitted as the first byte following the UDP
+ * header.
+ *
+ * If s_PXENV_UDP_WRITE::gw is 0.0.0.0, normal IP routing will take
+ * place. See the relevant @ref pxe_routing "implementation note" for
+ * more details.
+ *
+ * If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used.
+ *
+ * You must have opened a UDP connection with pxenv_udp_open() before
+ * calling pxenv_udp_write().
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note Etherboot currently ignores the s_PXENV_UDP_WRITE::gw
+ * parameter.
+ *
+ */
+PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) {
+ struct sockaddr_in dest;
+ struct xfer_metadata meta = {
+ .src = ( struct sockaddr * ) &pxe_udp.local,
+ .dest = ( struct sockaddr * ) &dest,
+ };
+ size_t len;
+ struct io_buffer *iobuf;
+ userptr_t buffer;
+ int rc;
+
+ DBG ( "PXENV_UDP_WRITE" );
+
+ /* Construct destination socket address */
+ memset ( &dest, 0, sizeof ( dest ) );
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = pxenv_udp_write->ip;
+ dest.sin_port = pxenv_udp_write->dst_port;
+
+ /* Set local (source) port. PXE spec says source port is 2069
+ * if not specified. Really, this ought to be set at UDP open
+ * time but hey, we didn't design this API.
+ */
+ pxe_udp.local.sin_port = pxenv_udp_write->src_port;
+ if ( ! pxe_udp.local.sin_port )
+ pxe_udp.local.sin_port = htons ( 2069 );
+
+ /* FIXME: we ignore the gateway specified, since we're
+ * confident of being able to do our own routing. We should
+ * probably allow for multiple gateways.
+ */
+
+ /* Allocate and fill data buffer */
+ len = pxenv_udp_write->buffer_size;
+ iobuf = xfer_alloc_iob ( &pxe_udp.xfer, len );
+ if ( ! iobuf ) {
+ pxenv_udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ }
+ buffer = real_to_user ( pxenv_udp_write->buffer.segment,
+ pxenv_udp_write->buffer.offset );
+ copy_from_user ( iob_put ( iobuf, len ), buffer, 0, len );
+
+ DBG ( " %04x:%04x+%x %d->%s:%d", pxenv_udp_write->buffer.segment,
+ pxenv_udp_write->buffer.offset, pxenv_udp_write->buffer_size,
+ ntohs ( pxenv_udp_write->src_port ),
+ inet_ntoa ( dest.sin_addr ),
+ ntohs ( pxenv_udp_write->dst_port ) );
+
+ /* Transmit packet */
+ if ( ( rc = xfer_deliver_iob_meta ( &pxe_udp.xfer, iobuf,
+ &meta ) ) != 0 ) {
+ pxenv_udp_write->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ pxenv_udp_write->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/**
+ * UDP READ
+ *
+ * @v pxenv_udp_read Pointer to a struct s_PXENV_UDP_READ
+ * @v s_PXENV_UDP_READ::dest_ip Destination IP address, or 0.0.0.0
+ * @v s_PXENV_UDP_READ::d_port Destination UDP port, or 0
+ * @v s_PXENV_UDP_READ::buffer_size Size of the UDP payload buffer
+ * @v s_PXENV_UDP_READ::buffer Address of the UDP payload buffer
+ * @ret #PXENV_EXIT_SUCCESS A packet has been received
+ * @ret #PXENV_EXIT_FAILURE No packet has been received
+ * @ret s_PXENV_UDP_READ::Status PXE status code
+ * @ret s_PXENV_UDP_READ::src_ip Source IP address
+ * @ret s_PXENV_UDP_READ::dest_ip Destination IP address
+ * @ret s_PXENV_UDP_READ::s_port Source UDP port
+ * @ret s_PXENV_UDP_READ::d_port Destination UDP port
+ * @ret s_PXENV_UDP_READ::buffer_size Length of UDP payload
+ * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open
+ * @err #PXENV_STATUS_FAILURE No packet was ready to read
+ *
+ * Receive a single UDP packet. This is a non-blocking call; if no
+ * packet is ready to read, the call will return instantly with
+ * s_PXENV_UDP_READ::Status==PXENV_STATUS_FAILURE.
+ *
+ * If s_PXENV_UDP_READ::dest_ip is 0.0.0.0, UDP packets addressed to
+ * any IP address will be accepted and may be returned to the caller.
+ *
+ * If s_PXENV_UDP_READ::d_port is 0, UDP packets addressed to any UDP
+ * port will be accepted and may be returned to the caller.
+ *
+ * You must have opened a UDP connection with pxenv_udp_open() before
+ * calling pxenv_udp_read().
+ *
+ * On x86, you must set the s_PXE::StatusCallout field to a nonzero
+ * value before calling this function in protected mode. You cannot
+ * call this function with a 32-bit stack segment. (See the relevant
+ * @ref pxe_x86_pmode16 "implementation note" for more details.)
+ *
+ * @note The PXE specification (version 2.1) does not state that we
+ * should fill in s_PXENV_UDP_READ::dest_ip and
+ * s_PXENV_UDP_READ::d_port, but Microsoft Windows' NTLDR program
+ * expects us to do so, and will fail if we don't.
+ *
+ */
+PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) {
+ struct in_addr dest_ip = { .s_addr = pxenv_udp_read->dest_ip };
+ uint16_t d_port = pxenv_udp_read->d_port;
+
+ DBG ( "PXENV_UDP_READ" );
+
+ /* Try receiving a packet */
+ pxe_udp.pxenv_udp_read = pxenv_udp_read;
+ step();
+ if ( pxe_udp.pxenv_udp_read ) {
+ /* No packet received */
+ pxe_udp.pxenv_udp_read = NULL;
+ goto no_packet;
+ }
+
+ /* Filter on destination address and/or port */
+ if ( dest_ip.s_addr && ( dest_ip.s_addr != pxenv_udp_read->dest_ip ) )
+ goto no_packet;
+ if ( d_port && ( d_port != pxenv_udp_read->d_port ) )
+ goto no_packet;
+
+ DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment,
+ pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size,
+ inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) ));
+ DBG ( "%d<-%s:%d", ntohs ( pxenv_udp_read->s_port ),
+ inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ),
+ ntohs ( pxenv_udp_read->d_port ) );
+
+ pxenv_udp_read->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+
+ no_packet:
+ pxenv_udp_read->Status = PXENV_STATUS_FAILURE;
+ return PXENV_EXIT_FAILURE;
+}
diff --git a/gpxe/src/interface/pxe/pxe_undi.c b/gpxe/src/interface/pxe/pxe_undi.c
new file mode 100644
index 00000000..76b55df9
--- /dev/null
+++ b/gpxe/src/interface/pxe/pxe_undi.c
@@ -0,0 +1,620 @@
+/** @file
+ *
+ * PXE UNDI API
+ *
+ */
+
+/*
+ * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <byteswap.h>
+#include <basemem_packet.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/device.h>
+#include <gpxe/pci.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ip.h>
+#include <gpxe/arp.h>
+#include <gpxe/rarp.h>
+#include "pxe.h"
+
+/**
+ * Count of outstanding transmitted packets
+ *
+ * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
+ * decremented each time that PXENV_UNDI_ISR is called with the TX
+ * queue empty, stopping when the count reaches zero. This allows us
+ * to provide a pessimistic approximation of TX completion events to
+ * the PXE NBP simply by monitoring the netdev's TX queue.
+ */
+static int undi_tx_count = 0;
+
+struct net_device *pxe_netdev = NULL;
+
+/**
+ * Set network device as current PXE network device
+ *
+ * @v netdev Network device, or NULL
+ */
+void pxe_set_netdev ( struct net_device *netdev ) {
+ if ( pxe_netdev )
+ netdev_put ( pxe_netdev );
+ pxe_netdev = NULL;
+ if ( netdev )
+ pxe_netdev = netdev_get ( netdev );
+}
+
+/**
+ * Open PXE network device
+ *
+ * @ret rc Return status code
+ */
+static int pxe_netdev_open ( void ) {
+ int rc;
+
+ if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 )
+ return rc;
+
+ netdev_irq ( pxe_netdev, 1 );
+ return 0;
+}
+
+/**
+ * Close PXE network device
+ *
+ */
+static void pxe_netdev_close ( void ) {
+ netdev_irq ( pxe_netdev, 0 );
+ netdev_close ( pxe_netdev );
+ undi_tx_count = 0;
+}
+
+/* PXENV_UNDI_STARTUP
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
+ DBG ( "PXENV_UNDI_STARTUP" );
+
+ undi_startup->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_CLEANUP
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
+ DBG ( "PXENV_UNDI_CLEANUP" );
+
+ pxe_netdev_close();
+
+ undi_cleanup->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_INITIALIZE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
+ *undi_initialize ) {
+ DBG ( "PXENV_UNDI_INITIALIZE" );
+
+ undi_initialize->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_RESET_ADAPTER
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
+ *undi_reset_adapter ) {
+ int rc;
+
+ DBG ( "PXENV_UNDI_RESET_ADAPTER" );
+
+ pxe_netdev_close();
+ if ( ( rc = pxe_netdev_open() ) != 0 ) {
+ undi_reset_adapter->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SHUTDOWN
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
+ *undi_shutdown ) {
+ DBG ( "PXENV_UNDI_SHUTDOWN" );
+
+ pxe_netdev_close();
+
+ undi_shutdown->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_OPEN
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
+ int rc;
+
+ DBG ( "PXENV_UNDI_OPEN" );
+
+ if ( ( rc = pxe_netdev_open() ) != 0 ) {
+ undi_open->Status = PXENV_STATUS ( rc );
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_CLOSE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
+ DBG ( "PXENV_UNDI_CLOSE" );
+
+ pxe_netdev_close();
+
+ undi_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_TRANSMIT
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
+ *undi_transmit ) {
+ struct s_PXENV_UNDI_TBD tbd;
+ struct DataBlk *datablk;
+ struct io_buffer *iobuf;
+ struct net_protocol *net_protocol;
+ char destaddr[MAX_LL_ADDR_LEN];
+ const void *ll_dest;
+ size_t ll_hlen = pxe_netdev->ll_protocol->ll_header_len;
+ size_t len;
+ unsigned int i;
+ int rc;
+
+ DBG ( "PXENV_UNDI_TRANSMIT" );
+
+ /* Identify network-layer protocol */
+ switch ( undi_transmit->Protocol ) {
+ case P_IP: net_protocol = &ipv4_protocol; break;
+ case P_ARP: net_protocol = &arp_protocol; break;
+ case P_RARP: net_protocol = &rarp_protocol; break;
+ case P_UNKNOWN:
+ net_protocol = NULL;
+ ll_hlen = 0;
+ break;
+ default:
+ undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+ return PXENV_EXIT_FAILURE;
+ }
+ DBG ( " %s", ( net_protocol ? net_protocol->name : "UNKNOWN" ) );
+
+ /* Calculate total packet length */
+ copy_from_real ( &tbd, undi_transmit->TBD.segment,
+ undi_transmit->TBD.offset, sizeof ( tbd ) );
+ len = tbd.ImmedLength;
+ DBG ( " %d", tbd.ImmedLength );
+ for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
+ datablk = &tbd.DataBlock[i];
+ len += datablk->TDDataLen;
+ DBG ( "+%d", datablk->TDDataLen );
+ }
+
+ /* Allocate and fill I/O buffer */
+ iobuf = alloc_iob ( ll_hlen + len );
+ if ( ! iobuf ) {
+ undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ }
+ iob_reserve ( iobuf, ll_hlen );
+ copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
+ tbd.Xmit.offset, tbd.ImmedLength );
+ for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
+ datablk = &tbd.DataBlock[i];
+ copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
+ datablk->TDDataPtr.segment,
+ datablk->TDDataPtr.offset,
+ datablk->TDDataLen );
+ }
+
+ /* Transmit packet */
+ if ( net_protocol == NULL ) {
+ /* Link-layer header already present */
+ rc = netdev_tx ( pxe_netdev, iobuf );
+ } else {
+ /* Calculate destination address */
+ if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
+ copy_from_real ( destaddr,
+ undi_transmit->DestAddr.segment,
+ undi_transmit->DestAddr.offset,
+ pxe_netdev->ll_protocol->ll_addr_len );
+ ll_dest = destaddr;
+ } else {
+ ll_dest = pxe_netdev->ll_protocol->ll_broadcast;
+ }
+ rc = net_tx ( iobuf, pxe_netdev, net_protocol, ll_dest );
+ }
+
+ /* Flag transmission as in-progress */
+ undi_tx_count++;
+
+ undi_transmit->Status = PXENV_STATUS ( rc );
+ return ( ( rc == 0 ) ? PXENV_EXIT_SUCCESS : PXENV_EXIT_FAILURE );
+}
+
+/* PXENV_UNDI_SET_MCAST_ADDRESS
+ *
+ * Status: stub (no PXE multicast support)
+ */
+PXENV_EXIT_t
+pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
+ *undi_set_mcast_address ) {
+ DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
+
+ undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_SET_STATION_ADDRESS
+ *
+ * Status: working
+ */
+PXENV_EXIT_t
+pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
+ *undi_set_station_address ) {
+
+ DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
+
+ /* If adapter is open, the change will have no effect; return
+ * an error
+ */
+ if ( pxe_netdev->state & NETDEV_OPEN ) {
+ undi_set_station_address->Status =
+ PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Update MAC address */
+ memcpy ( pxe_netdev->ll_addr,
+ &undi_set_station_address->StationAddress,
+ pxe_netdev->ll_protocol->ll_addr_len );
+
+ undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SET_PACKET_FILTER
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t
+pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
+ *undi_set_packet_filter ) {
+ DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
+
+ undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_GET_INFORMATION
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
+ *undi_get_information ) {
+ struct device *dev = pxe_netdev->dev;
+ struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
+
+ DBG ( "PXENV_UNDI_GET_INFORMATION" );
+
+ undi_get_information->BaseIo = dev->desc.ioaddr;
+ undi_get_information->IntNumber = dev->desc.irq;
+ /* Cheat: assume all cards can cope with this */
+ undi_get_information->MaxTranUnit = ETH_MAX_MTU;
+ undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
+ undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
+ /* Cheat: assume card is always configured with its permanent
+ * node address. This is a valid assumption within Etherboot
+ * at the time of writing.
+ */
+ memcpy ( &undi_get_information->CurrentNodeAddress,
+ pxe_netdev->ll_addr,
+ sizeof ( undi_get_information->CurrentNodeAddress ) );
+ memcpy ( &undi_get_information->PermNodeAddress,
+ pxe_netdev->ll_addr,
+ sizeof ( undi_get_information->PermNodeAddress ) );
+ undi_get_information->ROMAddress = 0;
+ /* nic.rom_info->rom_segment; */
+ /* We only provide the ability to receive or transmit a single
+ * packet at a time. This is a bootloader, not an OS.
+ */
+ undi_get_information->RxBufCt = 1;
+ undi_get_information->TxBufCt = 1;
+
+ undi_get_information->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_STATISTICS
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
+ *undi_get_statistics ) {
+ DBG ( "PXENV_UNDI_GET_STATISTICS" );
+
+ undi_get_statistics->XmtGoodFrames = pxe_netdev->stats.tx_ok;
+ undi_get_statistics->RcvGoodFrames = pxe_netdev->stats.rx_ok;
+ undi_get_statistics->RcvCRCErrors = pxe_netdev->stats.rx_err;
+ undi_get_statistics->RcvResourceErrors = pxe_netdev->stats.rx_err;
+
+ undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_CLEAR_STATISTICS
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
+ *undi_clear_statistics ) {
+ DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
+
+ memset ( &pxe_netdev->stats, 0, sizeof ( pxe_netdev->stats ) );
+
+ undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_INITIATE_DIAGS
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
+ *undi_initiate_diags ) {
+ DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
+
+ undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_FORCE_INTERRUPT
+ *
+ * Status: won't implement (would require driver API changes for no
+ * perceptible benefit)
+ */
+PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
+ *undi_force_interrupt ) {
+ DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
+
+ undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_GET_MCAST_ADDRESS
+ *
+ * Status: stub (no PXE multicast support)
+ */
+PXENV_EXIT_t
+pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
+ *undi_get_mcast_address ) {
+ DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
+
+ undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_GET_NIC_TYPE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
+ *undi_get_nic_type ) {
+ struct device *dev = pxe_netdev->dev;
+
+ DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
+
+ memset ( &undi_get_nic_type->info, 0,
+ sizeof ( undi_get_nic_type->info ) );
+
+ switch ( dev->desc.bus_type ) {
+ case BUS_TYPE_PCI: {
+ struct pci_nic_info *info = &undi_get_nic_type->info.pci;
+
+ undi_get_nic_type->NicType = PCI_NIC;
+ info->Vendor_ID = dev->desc.vendor;
+ info->Dev_ID = dev->desc.device;
+ info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
+ info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
+ info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
+ info->BusDevFunc = dev->desc.location;
+ /* Cheat: remaining fields are probably unnecessary,
+ * and would require adding extra code to pci.c.
+ */
+ undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
+ undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
+ break; }
+ case BUS_TYPE_ISAPNP: {
+ struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
+
+ undi_get_nic_type->NicType = PnP_NIC;
+ info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
+ dev->desc.device );
+ info->CardSelNum = dev->desc.location;
+ /* Cheat: remaining fields are probably unnecessary,
+ * and would require adding extra code to isapnp.c.
+ */
+ break; }
+ default:
+ undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_IFACE_INFO
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
+ *undi_get_iface_info ) {
+ DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
+
+ /* Just hand back some info, doesn't really matter what it is.
+ * Most PXE stacks seem to take this approach.
+ */
+ snprintf ( ( char * ) undi_get_iface_info->IfaceType,
+ sizeof ( undi_get_iface_info->IfaceType ), "gPXE" );
+ undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
+ undi_get_iface_info->ServiceFlags = 0;
+ memset ( undi_get_iface_info->Reserved, 0,
+ sizeof(undi_get_iface_info->Reserved) );
+
+ undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_STATE
+ *
+ * Status: impossible
+ */
+PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
+ *undi_get_state ) {
+ DBG ( "PXENV_UNDI_GET_STATE" );
+
+ undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+};
+
+/* PXENV_UNDI_ISR
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
+ struct io_buffer *iobuf;
+ size_t len;
+
+ DBG ( "PXENV_UNDI_ISR" );
+
+ /* Just in case some idiot actually looks at these fields when
+ * we weren't meant to fill them in...
+ */
+ undi_isr->BufferLength = 0;
+ undi_isr->FrameLength = 0;
+ undi_isr->FrameHeaderLength = 0;
+ undi_isr->ProtType = 0;
+ undi_isr->PktType = 0;
+
+ switch ( undi_isr->FuncFlag ) {
+ case PXENV_UNDI_ISR_IN_START :
+ DBG ( " START" );
+
+ /* Call poll(). This should acknowledge the device
+ * interrupt and queue up any received packet.
+ */
+ netdev_poll ( pxe_netdev );
+
+ /* Disable interrupts to avoid interrupt storm */
+ netdev_irq ( pxe_netdev, 0 );
+
+ /* Always say it was ours for the sake of simplicity */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
+ break;
+ case PXENV_UNDI_ISR_IN_PROCESS :
+ case PXENV_UNDI_ISR_IN_GET_NEXT :
+ DBG ( " PROCESS/GET_NEXT" );
+
+ /* If we have not yet marked a TX as complete, and the
+ * netdev TX queue is empty, report the TX completion.
+ */
+ if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
+ undi_tx_count--;
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
+ break;
+ }
+
+ /* Remove first packet from netdev RX queue */
+ iobuf = netdev_rx_dequeue ( pxe_netdev );
+ if ( ! iobuf ) {
+ /* No more packets remaining */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+ /* Re-enable interrupts */
+ netdev_irq ( pxe_netdev, 1 );
+ break;
+ }
+
+ /* Copy packet to base memory buffer */
+ len = iob_len ( iobuf );
+ DBG ( " RECEIVE %zd", len );
+ if ( len > sizeof ( basemem_packet ) ) {
+ /* Should never happen */
+ len = sizeof ( basemem_packet );
+ }
+ memcpy ( basemem_packet, iobuf->data, len );
+
+ /* Fill in UNDI_ISR structure */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
+ undi_isr->BufferLength = len;
+ undi_isr->FrameLength = len;
+ undi_isr->FrameHeaderLength =
+ pxe_netdev->ll_protocol->ll_header_len;
+ undi_isr->Frame.segment = rm_ds;
+ undi_isr->Frame.offset =
+ ( ( unsigned ) & __from_data16 ( basemem_packet ) );
+ /* Probably ought to fill in packet type */
+ undi_isr->ProtType = P_UNKNOWN;
+ undi_isr->PktType = XMT_DESTADDR;
+
+ /* Free packet */
+ free_iob ( iobuf );
+ break;
+ default :
+ DBG ( " INVALID(%04x)", undi_isr->FuncFlag );
+
+ /* Should never happen */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+ undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_isr->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
diff --git a/gpxe/src/libgcc/__divdi3.c b/gpxe/src/libgcc/__divdi3.c
new file mode 100644
index 00000000..36f0b37f
--- /dev/null
+++ b/gpxe/src/libgcc/__divdi3.c
@@ -0,0 +1,26 @@
+/*
+ * arch/i386/libgcc/__divdi3.c
+ */
+
+#include "libgcc.h"
+
+LIBGCC int64_t __divdi3(int64_t num, int64_t den)
+{
+ int minus = 0;
+ int64_t v;
+
+ if ( num < 0 ) {
+ num = -num;
+ minus = 1;
+ }
+ if ( den < 0 ) {
+ den = -den;
+ minus ^= 1;
+ }
+
+ v = __udivmoddi4(num, den, NULL);
+ if ( minus )
+ v = -v;
+
+ return v;
+}
diff --git a/gpxe/src/libgcc/__moddi3.c b/gpxe/src/libgcc/__moddi3.c
new file mode 100644
index 00000000..eb7784b7
--- /dev/null
+++ b/gpxe/src/libgcc/__moddi3.c
@@ -0,0 +1,26 @@
+/*
+ * arch/i386/libgcc/__moddi3.c
+ */
+
+#include "libgcc.h"
+
+LIBGCC int64_t __moddi3(int64_t num, int64_t den)
+{
+ int minus = 0;
+ int64_t v;
+
+ if ( num < 0 ) {
+ num = -num;
+ minus = 1;
+ }
+ if ( den < 0 ) {
+ den = -den;
+ minus ^= 1;
+ }
+
+ (void) __udivmoddi4(num, den, (uint64_t *)&v);
+ if ( minus )
+ v = -v;
+
+ return v;
+}
diff --git a/gpxe/src/libgcc/__udivdi3.c b/gpxe/src/libgcc/__udivdi3.c
new file mode 100644
index 00000000..9ae0c3dc
--- /dev/null
+++ b/gpxe/src/libgcc/__udivdi3.c
@@ -0,0 +1,10 @@
+/*
+ * arch/i386/libgcc/__divdi3.c
+ */
+
+#include "libgcc.h"
+
+LIBGCC uint64_t __udivdi3(uint64_t num, uint64_t den)
+{
+ return __udivmoddi4(num, den, NULL);
+}
diff --git a/gpxe/src/libgcc/__udivmoddi4.c b/gpxe/src/libgcc/__udivmoddi4.c
new file mode 100644
index 00000000..59966edb
--- /dev/null
+++ b/gpxe/src/libgcc/__udivmoddi4.c
@@ -0,0 +1,32 @@
+#include "libgcc.h"
+
+LIBGCC uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem_p)
+{
+ uint64_t quot = 0, qbit = 1;
+
+ if ( den == 0 ) {
+ return 1/((unsigned)den); /* Intentional divide by zero, without
+ triggering a compiler warning which
+ would abort the build */
+ }
+
+ /* Left-justify denominator and count shift */
+ while ( (int64_t)den >= 0 ) {
+ den <<= 1;
+ qbit <<= 1;
+ }
+
+ while ( qbit ) {
+ if ( den <= num ) {
+ num -= den;
+ quot += qbit;
+ }
+ den >>= 1;
+ qbit >>= 1;
+ }
+
+ if ( rem_p )
+ *rem_p = num;
+
+ return quot;
+}
diff --git a/gpxe/src/libgcc/__umoddi3.c b/gpxe/src/libgcc/__umoddi3.c
new file mode 100644
index 00000000..f6c76cb6
--- /dev/null
+++ b/gpxe/src/libgcc/__umoddi3.c
@@ -0,0 +1,13 @@
+/*
+ * arch/i386/libgcc/__umoddi3.c
+ */
+
+#include "libgcc.h"
+
+LIBGCC uint64_t __umoddi3(uint64_t num, uint64_t den)
+{
+ uint64_t v;
+
+ (void) __udivmoddi4(num, den, &v);
+ return v;
+}
diff --git a/gpxe/src/libgcc/libgcc.h b/gpxe/src/libgcc/libgcc.h
new file mode 100644
index 00000000..5b4a6244
--- /dev/null
+++ b/gpxe/src/libgcc/libgcc.h
@@ -0,0 +1,26 @@
+#ifndef _LIBGCC_H
+#define _LIBGCC_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+ * It seems as though gcc expects its implicit arithmetic functions to
+ * be cdecl, even if -mrtd is specified. This is somewhat
+ * inconsistent; for example, if -mregparm=3 is used then the implicit
+ * functions do become regparm(3).
+ *
+ * The implicit calls to memcpy() and memset() which gcc can generate
+ * do not seem to have this inconsistency; -mregparm and -mrtd affect
+ * them in the same way as any other function.
+ *
+ */
+#define LIBGCC __attribute__ (( cdecl ))
+
+extern LIBGCC uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem);
+extern LIBGCC uint64_t __udivdi3(uint64_t num, uint64_t den);
+extern LIBGCC uint64_t __umoddi3(uint64_t num, uint64_t den);
+extern LIBGCC int64_t __divdi3(int64_t num, int64_t den);
+extern LIBGCC int64_t __moddi3(int64_t num, int64_t den);
+
+#endif /* _LIBGCC_H */
diff --git a/gpxe/src/libgcc/memcpy.c b/gpxe/src/libgcc/memcpy.c
new file mode 100644
index 00000000..e98b7838
--- /dev/null
+++ b/gpxe/src/libgcc/memcpy.c
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * gcc sometimes likes to insert implicit calls to memcpy().
+ * Unfortunately, there doesn't seem to be any way to prevent it from
+ * doing this, or to force it to use the optimised memcpy() as seen by
+ * C code; it insists on inserting a symbol reference to "memcpy". We
+ * therefore include wrapper functions just to keep gcc happy.
+ *
+ */
+
+#include <string.h>
+
+void * gcc_implicit_memcpy ( void *dest, const void *src,
+ size_t len ) asm ( "memcpy" );
+
+void * gcc_implicit_memcpy ( void *dest, const void *src, size_t len ) {
+ return memcpy ( dest, src, len );
+}
diff --git a/gpxe/src/net/aoe.c b/gpxe/src/net/aoe.c
new file mode 100644
index 00000000..e3f84e5a
--- /dev/null
+++ b/gpxe/src/net/aoe.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <gpxe/list.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/ata.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/process.h>
+#include <gpxe/features.h>
+#include <gpxe/aoe.h>
+
+/** @file
+ *
+ * AoE protocol
+ *
+ */
+
+FEATURE ( FEATURE_PROTOCOL, "AoE", DHCP_EB_FEATURE_AOE, 1 );
+
+struct net_protocol aoe_protocol;
+
+/** List of all AoE sessions */
+static LIST_HEAD ( aoe_sessions );
+
+static void aoe_free ( struct refcnt *refcnt ) {
+ struct aoe_session *aoe =
+ container_of ( refcnt, struct aoe_session, refcnt );
+
+ netdev_put ( aoe->netdev );
+ free ( aoe );
+}
+
+/**
+ * Mark current AoE command complete
+ *
+ * @v aoe AoE session
+ * @v rc Return status code
+ */
+static void aoe_done ( struct aoe_session *aoe, int rc ) {
+
+ /* Record overall command status */
+ aoe->command->cb.cmd_stat = aoe->status;
+ aoe->command = NULL;
+
+ /* Mark operation as complete */
+ aoe->rc = rc;
+}
+
+/**
+ * Send AoE command
+ *
+ * @v aoe AoE session
+ * @ret rc Return status code
+ *
+ * This transmits an AoE command packet. It does not wait for a
+ * response.
+ */
+static int aoe_send_command ( struct aoe_session *aoe ) {
+ struct ata_command *command = aoe->command;
+ struct io_buffer *iobuf;
+ struct aoehdr *aoehdr;
+ struct aoecmd *aoecmd;
+ unsigned int count;
+ unsigned int data_out_len;
+
+ /* Fail immediately if we have no netdev to send on */
+ if ( ! aoe->netdev ) {
+ aoe_done ( aoe, -ENETUNREACH );
+ return -ENETUNREACH;
+ }
+
+ /* Calculate count and data_out_len for this subcommand */
+ count = command->cb.count.native;
+ if ( count > AOE_MAX_COUNT )
+ count = AOE_MAX_COUNT;
+ data_out_len = ( command->data_out ? ( count * ATA_SECTOR_SIZE ) : 0 );
+
+ /* Create outgoing I/O buffer */
+ iobuf = alloc_iob ( ETH_HLEN + sizeof ( *aoehdr ) + sizeof ( *aoecmd ) +
+ data_out_len );
+ if ( ! iobuf )
+ return -ENOMEM;
+ iob_reserve ( iobuf, ETH_HLEN );
+ aoehdr = iob_put ( iobuf, sizeof ( *aoehdr ) );
+ aoecmd = iob_put ( iobuf, sizeof ( *aoecmd ) );
+ memset ( aoehdr, 0, ( sizeof ( *aoehdr ) + sizeof ( *aoecmd ) ) );
+
+ /* Fill AoE header */
+ aoehdr->ver_flags = AOE_VERSION;
+ aoehdr->major = htons ( aoe->major );
+ aoehdr->minor = aoe->minor;
+ aoehdr->tag = htonl ( ++aoe->tag );
+
+ /* Fill AoE command */
+ linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE, __fix_ata_h__ );
+ aoecmd->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 ) |
+ ( command->cb.device & ATA_DEV_SLAVE ) |
+ ( data_out_len ? AOE_FL_WRITE : 0 ) );
+ aoecmd->err_feat = command->cb.err_feat.bytes.cur;
+ aoecmd->count = count;
+ aoecmd->cmd_stat = command->cb.cmd_stat;
+ aoecmd->lba.u64 = cpu_to_le64 ( command->cb.lba.native );
+ if ( ! command->cb.lba48 )
+ aoecmd->lba.bytes[3] |= ( command->cb.device & ATA_DEV_MASK );
+
+ /* Fill data payload */
+ copy_from_user ( iob_put ( iobuf, data_out_len ), command->data_out,
+ aoe->command_offset, data_out_len );
+
+ /* Send packet */
+ start_timer ( &aoe->timer );
+ return net_tx ( iobuf, aoe->netdev, &aoe_protocol, aoe->target );
+}
+
+/**
+ * Handle AoE retry timer expiry
+ *
+ * @v timer AoE retry timer
+ * @v fail Failure indicator
+ */
+static void aoe_timer_expired ( struct retry_timer *timer, int fail ) {
+ struct aoe_session *aoe =
+ container_of ( timer, struct aoe_session, timer );
+
+ if ( fail ) {
+ aoe_done ( aoe, -ETIMEDOUT );
+ } else {
+ aoe_send_command ( aoe );
+ }
+}
+
+/**
+ * Handle AoE response
+ *
+ * @v aoe AoE session
+ * @v aoehdr AoE header
+ * @ret rc Return status code
+ */
+static int aoe_rx_response ( struct aoe_session *aoe, struct aoehdr *aoehdr,
+ unsigned int len ) {
+ struct aoecmd *aoecmd = aoehdr->arg.command;
+ struct ata_command *command = aoe->command;
+ unsigned int rx_data_len;
+ unsigned int count;
+ unsigned int data_len;
+
+ /* Sanity check */
+ if ( len < ( sizeof ( *aoehdr ) + sizeof ( *aoecmd ) ) ) {
+ /* Ignore packet; allow timer to trigger retransmit */
+ return -EINVAL;
+ }
+ rx_data_len = ( len - sizeof ( *aoehdr ) - sizeof ( *aoecmd ) );
+
+ /* Stop retry timer. After this point, every code path must
+ * either terminate the AoE operation via aoe_done(), or
+ * transmit a new packet.
+ */
+ stop_timer ( &aoe->timer );
+
+ /* Check for fatal errors */
+ if ( aoehdr->ver_flags & AOE_FL_ERROR ) {
+ aoe_done ( aoe, -EIO );
+ return 0;
+ }
+
+ /* Calculate count and data_len for this subcommand */
+ count = command->cb.count.native;
+ if ( count > AOE_MAX_COUNT )
+ count = AOE_MAX_COUNT;
+ data_len = count * ATA_SECTOR_SIZE;
+
+ /* Merge into overall ATA status */
+ aoe->status |= aoecmd->cmd_stat;
+
+ /* Copy data payload */
+ if ( command->data_in ) {
+ if ( rx_data_len > data_len )
+ rx_data_len = data_len;
+ copy_to_user ( command->data_in, aoe->command_offset,
+ aoecmd->data, rx_data_len );
+ }
+
+ /* Update ATA command and offset */
+ aoe->command_offset += data_len;
+ command->cb.lba.native += count;
+ command->cb.count.native -= count;
+
+ /* Check for operation complete */
+ if ( ! command->cb.count.native ) {
+ aoe_done ( aoe, 0 );
+ return 0;
+ }
+
+ /* Transmit next portion of request */
+ aoe_send_command ( aoe );
+
+ return 0;
+}
+
+/**
+ * Process incoming AoE packets
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v ll_source Link-layer source address
+ * @ret rc Return status code
+ *
+ */
+static int aoe_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused,
+ const void *ll_source ) {
+ struct aoehdr *aoehdr = iobuf->data;
+ unsigned int len = iob_len ( iobuf );
+ struct aoe_session *aoe;
+ int rc = 0;
+
+ /* Sanity checks */
+ if ( len < sizeof ( *aoehdr ) ) {
+ rc = -EINVAL;
+ goto done;
+ }
+ if ( ( aoehdr->ver_flags & AOE_VERSION_MASK ) != AOE_VERSION ) {
+ rc = -EPROTONOSUPPORT;
+ goto done;
+ }
+ if ( ! ( aoehdr->ver_flags & AOE_FL_RESPONSE ) ) {
+ /* Ignore AoE requests that we happen to see */
+ goto done;
+ }
+
+ /* Demultiplex amongst active AoE sessions */
+ list_for_each_entry ( aoe, &aoe_sessions, list ) {
+ if ( ntohs ( aoehdr->major ) != aoe->major )
+ continue;
+ if ( aoehdr->minor != aoe->minor )
+ continue;
+ if ( ntohl ( aoehdr->tag ) != aoe->tag )
+ continue;
+ memcpy ( aoe->target, ll_source, sizeof ( aoe->target ) );
+ rc = aoe_rx_response ( aoe, aoehdr, len );
+ break;
+ }
+
+ done:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/** AoE protocol */
+struct net_protocol aoe_protocol __net_protocol = {
+ .name = "AoE",
+ .net_proto = htons ( ETH_P_AOE ),
+ .rx = aoe_rx,
+};
+
+/**
+ * Issue ATA command via an open AoE session
+ *
+ * @v ata ATA device
+ * @v command ATA command
+ * @ret rc Return status code
+ */
+static int aoe_command ( struct ata_device *ata,
+ struct ata_command *command ) {
+ struct aoe_session *aoe =
+ container_of ( ata->backend, struct aoe_session, refcnt );
+ int rc;
+
+ aoe->command = command;
+ aoe->status = 0;
+ aoe->command_offset = 0;
+ aoe_send_command ( aoe );
+
+ aoe->rc = -EINPROGRESS;
+ while ( aoe->rc == -EINPROGRESS )
+ step();
+ rc = aoe->rc;
+
+ return rc;
+}
+
+static int aoe_detached_command ( struct ata_device *ata __unused,
+ struct ata_command *command __unused ) {
+ return -ENODEV;
+}
+
+void aoe_detach ( struct ata_device *ata ) {
+ struct aoe_session *aoe =
+ container_of ( ata->backend, struct aoe_session, refcnt );
+
+ stop_timer ( &aoe->timer );
+ ata->command = aoe_detached_command;
+ list_del ( &aoe->list );
+ ref_put ( ata->backend );
+ ata->backend = NULL;
+}
+
+static int aoe_parse_root_path ( struct aoe_session *aoe,
+ const char *root_path ) {
+ char *ptr;
+
+ if ( strncmp ( root_path, "aoe:", 4 ) != 0 )
+ return -EINVAL;
+ ptr = ( ( char * ) root_path + 4 );
+
+ if ( *ptr++ != 'e' )
+ return -EINVAL;
+
+ aoe->major = strtoul ( ptr, &ptr, 10 );
+ if ( *ptr++ != '.' )
+ return -EINVAL;
+
+ aoe->minor = strtoul ( ptr, &ptr, 10 );
+ if ( *ptr )
+ return -EINVAL;
+
+ return 0;
+}
+
+int aoe_attach ( struct ata_device *ata, struct net_device *netdev,
+ const char *root_path ) {
+ struct aoe_session *aoe;
+ int rc;
+
+ /* Allocate and initialise structure */
+ aoe = zalloc ( sizeof ( *aoe ) );
+ if ( ! aoe )
+ return -ENOMEM;
+ aoe->refcnt.free = aoe_free;
+ aoe->netdev = netdev_get ( netdev );
+ memcpy ( aoe->target, ethernet_protocol.ll_broadcast,
+ sizeof ( aoe->target ) );
+ aoe->tag = AOE_TAG_MAGIC;
+ aoe->timer.expired = aoe_timer_expired;
+
+ /* Parse root path */
+ if ( ( rc = aoe_parse_root_path ( aoe, root_path ) ) != 0 )
+ goto err;
+
+ /* Attach parent interface, transfer reference to connection
+ * list, and return
+ */
+ ata->backend = ref_get ( &aoe->refcnt );
+ ata->command = aoe_command;
+ list_add ( &aoe->list, &aoe_sessions );
+ return 0;
+
+ err:
+ ref_put ( &aoe->refcnt );
+ return rc;
+}
diff --git a/gpxe/src/net/arp.c b/gpxe/src/net/arp.c
new file mode 100644
index 00000000..011d4fef
--- /dev/null
+++ b/gpxe/src/net/arp.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/if_arp.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/arp.h>
+
+/** @file
+ *
+ * Address Resolution Protocol
+ *
+ * This file implements the address resolution protocol as defined in
+ * RFC826. The implementation is media-independent and
+ * protocol-independent; it is not limited to Ethernet or to IPv4.
+ *
+ */
+
+/** Registered ARP protocols */
+static struct arp_net_protocol arp_net_protocols[0]
+ __table_start ( struct arp_net_protocol, arp_net_protocols );
+static struct arp_net_protocol arp_net_protocols_end[0]
+ __table_end ( struct arp_net_protocol, arp_net_protocols );
+
+/** An ARP cache entry */
+struct arp_entry {
+ /** Network-layer protocol */
+ struct net_protocol *net_protocol;
+ /** Link-layer protocol */
+ struct ll_protocol *ll_protocol;
+ /** Network-layer address */
+ uint8_t net_addr[MAX_NET_ADDR_LEN];
+ /** Link-layer address */
+ uint8_t ll_addr[MAX_LL_ADDR_LEN];
+};
+
+/** Number of entries in the ARP cache
+ *
+ * This is a global cache, covering all network interfaces,
+ * network-layer protocols and link-layer protocols.
+ */
+#define NUM_ARP_ENTRIES 4
+
+/** The ARP cache */
+static struct arp_entry arp_table[NUM_ARP_ENTRIES];
+#define arp_table_end &arp_table[NUM_ARP_ENTRIES]
+
+static unsigned int next_new_arp_entry = 0;
+
+struct net_protocol arp_protocol;
+
+/**
+ * Find entry in the ARP cache
+ *
+ * @v ll_protocol Link-layer protocol
+ * @v net_protocol Network-layer protocol
+ * @v net_addr Network-layer address
+ * @ret arp ARP cache entry, or NULL if not found
+ *
+ */
+static struct arp_entry *
+arp_find_entry ( struct ll_protocol *ll_protocol,
+ struct net_protocol *net_protocol,
+ const void *net_addr ) {
+ struct arp_entry *arp;
+
+ for ( arp = arp_table ; arp < arp_table_end ; arp++ ) {
+ if ( ( arp->ll_protocol == ll_protocol ) &&
+ ( arp->net_protocol == net_protocol ) &&
+ ( memcmp ( arp->net_addr, net_addr,
+ net_protocol->net_addr_len ) == 0 ) )
+ return arp;
+ }
+ return NULL;
+}
+
+/**
+ * Look up media-specific link-layer address in the ARP cache
+ *
+ * @v netdev Network device
+ * @v net_protocol Network-layer protocol
+ * @v dest_net_addr Destination network-layer address
+ * @v source_net_addr Source network-layer address
+ * @ret dest_ll_addr Destination link layer address
+ * @ret rc Return status code
+ *
+ * This function will use the ARP cache to look up the link-layer
+ * address for the link-layer protocol associated with the network
+ * device and the given network-layer protocol and addresses. If
+ * found, the destination link-layer address will be filled in in @c
+ * dest_ll_addr.
+ *
+ * If no address is found in the ARP cache, an ARP request will be
+ * transmitted on the specified network device and -ENOENT will be
+ * returned.
+ */
+int arp_resolve ( struct net_device *netdev, struct net_protocol *net_protocol,
+ const void *dest_net_addr, const void *source_net_addr,
+ void *dest_ll_addr ) {
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ const struct arp_entry *arp;
+ struct io_buffer *iobuf;
+ struct arphdr *arphdr;
+ int rc;
+
+ /* Look for existing entry in ARP table */
+ arp = arp_find_entry ( ll_protocol, net_protocol, dest_net_addr );
+ if ( arp ) {
+ DBG ( "ARP cache hit: %s %s => %s %s\n",
+ net_protocol->name, net_protocol->ntoa ( arp->net_addr ),
+ ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) );
+ memcpy ( dest_ll_addr, arp->ll_addr, ll_protocol->ll_addr_len);
+ return 0;
+ }
+ DBG ( "ARP cache miss: %s %s\n", net_protocol->name,
+ net_protocol->ntoa ( dest_net_addr ) );
+
+ /* Allocate ARP packet */
+ iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *arphdr ) +
+ 2 * ( MAX_LL_ADDR_LEN + MAX_NET_ADDR_LEN ) );
+ if ( ! iobuf )
+ return -ENOMEM;
+ iob_reserve ( iobuf, MAX_LL_HEADER_LEN );
+
+ /* Build up ARP request */
+ arphdr = iob_put ( iobuf, sizeof ( *arphdr ) );
+ arphdr->ar_hrd = ll_protocol->ll_proto;
+ arphdr->ar_hln = ll_protocol->ll_addr_len;
+ arphdr->ar_pro = net_protocol->net_proto;
+ arphdr->ar_pln = net_protocol->net_addr_len;
+ arphdr->ar_op = htons ( ARPOP_REQUEST );
+ memcpy ( iob_put ( iobuf, ll_protocol->ll_addr_len ),
+ netdev->ll_addr, ll_protocol->ll_addr_len );
+ memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ),
+ source_net_addr, net_protocol->net_addr_len );
+ memset ( iob_put ( iobuf, ll_protocol->ll_addr_len ),
+ 0, ll_protocol->ll_addr_len );
+ memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ),
+ dest_net_addr, net_protocol->net_addr_len );
+
+ /* Transmit ARP request */
+ if ( ( rc = net_tx ( iobuf, netdev, &arp_protocol,
+ ll_protocol->ll_broadcast ) ) != 0 )
+ return rc;
+
+ return -ENOENT;
+}
+
+/**
+ * Identify ARP protocol
+ *
+ * @v net_proto Network-layer protocol, in network-endian order
+ * @ret arp_net_protocol ARP protocol, or NULL
+ *
+ */
+static struct arp_net_protocol * arp_find_protocol ( uint16_t net_proto ) {
+ struct arp_net_protocol *arp_net_protocol;
+
+ for ( arp_net_protocol = arp_net_protocols ;
+ arp_net_protocol < arp_net_protocols_end ; arp_net_protocol++ ) {
+ if ( arp_net_protocol->net_protocol->net_proto == net_proto ) {
+ return arp_net_protocol;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Process incoming ARP packets
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v ll_source Link-layer source address
+ * @ret rc Return status code
+ *
+ * This handles ARP requests and responses as detailed in RFC826. The
+ * method detailed within the RFC is pretty optimised, handling
+ * requests and responses with basically a single code path and
+ * avoiding the need for extraneous ARP requests; read the RFC for
+ * details.
+ */
+static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
+ const void *ll_source __unused ) {
+ struct arphdr *arphdr = iobuf->data;
+ struct arp_net_protocol *arp_net_protocol;
+ struct net_protocol *net_protocol;
+ struct ll_protocol *ll_protocol;
+ struct arp_entry *arp;
+ int merge = 0;
+
+ /* Identify network-layer and link-layer protocols */
+ arp_net_protocol = arp_find_protocol ( arphdr->ar_pro );
+ if ( ! arp_net_protocol )
+ goto done;
+ net_protocol = arp_net_protocol->net_protocol;
+ ll_protocol = netdev->ll_protocol;
+
+ /* Sanity checks */
+ if ( ( arphdr->ar_hrd != ll_protocol->ll_proto ) ||
+ ( arphdr->ar_hln != ll_protocol->ll_addr_len ) ||
+ ( arphdr->ar_pln != net_protocol->net_addr_len ) )
+ goto done;
+
+ /* See if we have an entry for this sender, and update it if so */
+ arp = arp_find_entry ( ll_protocol, net_protocol,
+ arp_sender_pa ( arphdr ) );
+ if ( arp ) {
+ memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
+ arphdr->ar_hln );
+ merge = 1;
+ DBG ( "ARP cache update: %s %s => %s %s\n",
+ net_protocol->name, net_protocol->ntoa ( arp->net_addr ),
+ ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) );
+ }
+
+ /* See if we own the target protocol address */
+ if ( arp_net_protocol->check ( netdev, arp_target_pa ( arphdr ) ) != 0)
+ goto done;
+
+ /* Create new ARP table entry if necessary */
+ if ( ! merge ) {
+ arp = &arp_table[next_new_arp_entry++ % NUM_ARP_ENTRIES];
+ arp->ll_protocol = ll_protocol;
+ arp->net_protocol = net_protocol;
+ memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
+ arphdr->ar_hln );
+ memcpy ( arp->net_addr, arp_sender_pa ( arphdr ),
+ arphdr->ar_pln);
+ DBG ( "ARP cache add: %s %s => %s %s\n",
+ net_protocol->name, net_protocol->ntoa ( arp->net_addr ),
+ ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) );
+ }
+
+ /* If it's not a request, there's nothing more to do */
+ if ( arphdr->ar_op != htons ( ARPOP_REQUEST ) )
+ goto done;
+
+ /* Change request to a reply */
+ DBG ( "ARP reply: %s %s => %s %s\n", net_protocol->name,
+ net_protocol->ntoa ( arp_target_pa ( arphdr ) ),
+ ll_protocol->name, ll_protocol->ntoa ( netdev->ll_addr ) );
+ arphdr->ar_op = htons ( ARPOP_REPLY );
+ memswap ( arp_sender_ha ( arphdr ), arp_target_ha ( arphdr ),
+ arphdr->ar_hln + arphdr->ar_pln );
+ memcpy ( arp_sender_ha ( arphdr ), netdev->ll_addr, arphdr->ar_hln );
+
+ /* Send reply */
+ net_tx ( iobuf, netdev, &arp_protocol, arp_target_ha (arphdr ) );
+ iobuf = NULL;
+
+ done:
+ free_iob ( iobuf );
+ return 0;
+}
+
+/**
+ * Transcribe ARP address
+ *
+ * @v net_addr ARP address
+ * @ret string "<ARP>"
+ *
+ * This operation is meaningless for the ARP protocol.
+ */
+static const char * arp_ntoa ( const void *net_addr __unused ) {
+ return "<ARP>";
+}
+
+/** ARP protocol */
+struct net_protocol arp_protocol __net_protocol = {
+ .name = "ARP",
+ .net_proto = htons ( ETH_P_ARP ),
+ .rx = arp_rx,
+ .ntoa = arp_ntoa,
+};
diff --git a/gpxe/src/net/dhcpopts.c b/gpxe/src/net/dhcpopts.c
new file mode 100644
index 00000000..1898011a
--- /dev/null
+++ b/gpxe/src/net/dhcpopts.c
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/dhcpopts.h>
+
+/** @file
+ *
+ * DHCP options
+ *
+ */
+
+/**
+ * Obtain printable version of a DHCP option tag
+ *
+ * @v tag DHCP option tag
+ * @ret name String representation of the tag
+ *
+ */
+static inline char * dhcp_tag_name ( unsigned int tag ) {
+ static char name[8];
+
+ if ( DHCP_IS_ENCAP_OPT ( tag ) ) {
+ snprintf ( name, sizeof ( name ), "%d.%d",
+ DHCP_ENCAPSULATOR ( tag ),
+ DHCP_ENCAPSULATED ( tag ) );
+ } else {
+ snprintf ( name, sizeof ( name ), "%d", tag );
+ }
+ return name;
+}
+
+/**
+ * Get pointer to DHCP option
+ *
+ * @v options DHCP options block
+ * @v offset Offset within options block
+ * @ret option DHCP option
+ */
+static inline __attribute__ (( always_inline )) struct dhcp_option *
+dhcp_option ( struct dhcp_options *options, unsigned int offset ) {
+ return ( ( struct dhcp_option * ) ( options->data + offset ) );
+}
+
+/**
+ * Get offset of a DHCP option
+ *
+ * @v options DHCP options block
+ * @v option DHCP option
+ * @ret offset Offset within options block
+ */
+static inline __attribute__ (( always_inline )) int
+dhcp_option_offset ( struct dhcp_options *options,
+ struct dhcp_option *option ) {
+ return ( ( ( void * ) option ) - options->data );
+}
+
+/**
+ * Calculate length of any DHCP option
+ *
+ * @v option DHCP option
+ * @ret len Length (including tag and length field)
+ */
+static unsigned int dhcp_option_len ( struct dhcp_option *option ) {
+ if ( ( option->tag == DHCP_END ) || ( option->tag == DHCP_PAD ) ) {
+ return 1;
+ } else {
+ return ( option->len + DHCP_OPTION_HEADER_LEN );
+ }
+}
+
+/**
+ * Find DHCP option within DHCP options block, and its encapsulator (if any)
+ *
+ * @v options DHCP options block
+ * @v tag DHCP option tag to search for
+ * @ret encap_offset Offset of encapsulating DHCP option
+ * @ret offset Offset of DHCP option, or negative error
+ *
+ * Searches for the DHCP option matching the specified tag within the
+ * DHCP option block. Encapsulated options may be searched for by
+ * using DHCP_ENCAP_OPT() to construct the tag value.
+ *
+ * If the option is encapsulated, and @c encapsulator is non-NULL, it
+ * will be filled in with the offset of the encapsulating option.
+ *
+ * This routine is designed to be paranoid. It does not assume that
+ * the option data is well-formatted, and so must guard against flaws
+ * such as options missing a @c DHCP_END terminator, or options whose
+ * length would take them beyond the end of the data block.
+ */
+static int find_dhcp_option_with_encap ( struct dhcp_options *options,
+ unsigned int tag,
+ int *encap_offset ) {
+ unsigned int original_tag __attribute__ (( unused )) = tag;
+ struct dhcp_option *option;
+ int offset = 0;
+ ssize_t remaining = options->len;
+ unsigned int option_len;
+
+ /* Sanity check */
+ if ( tag == DHCP_PAD )
+ return -ENOENT;
+
+ /* Search for option */
+ while ( remaining ) {
+ /* Calculate length of this option. Abort processing
+ * if the length is malformed (i.e. takes us beyond
+ * the end of the data block).
+ */
+ option = dhcp_option ( options, offset );
+ option_len = dhcp_option_len ( option );
+ remaining -= option_len;
+ if ( remaining < 0 )
+ break;
+ /* Check for explicit end marker */
+ if ( option->tag == DHCP_END )
+ break;
+ /* Check for matching tag */
+ if ( option->tag == tag ) {
+ DBGC ( options, "DHCPOPT %p found %s (length %d)\n",
+ options, dhcp_tag_name ( original_tag ),
+ option_len );
+ return offset;
+ }
+ /* Check for start of matching encapsulation block */
+ if ( DHCP_IS_ENCAP_OPT ( tag ) &&
+ ( option->tag == DHCP_ENCAPSULATOR ( tag ) ) ) {
+ if ( encap_offset )
+ *encap_offset = offset;
+ /* Continue search within encapsulated option block */
+ tag = DHCP_ENCAPSULATED ( tag );
+ remaining = option_len;
+ offset += DHCP_OPTION_HEADER_LEN;
+ continue;
+ }
+ offset += option_len;
+ }
+
+ return -ENOENT;
+}
+
+/**
+ * Resize a DHCP option
+ *
+ * @v options DHCP option block
+ * @v offset Offset of option to resize
+ * @v encap_offset Offset of encapsulating offset (or -ve for none)
+ * @v old_len Old length (including header)
+ * @v new_len New length (including header)
+ * @v can_realloc Can reallocate options data if necessary
+ * @ret rc Return status code
+ */
+static int resize_dhcp_option ( struct dhcp_options *options,
+ int offset, int encap_offset,
+ size_t old_len, size_t new_len,
+ int can_realloc ) {
+ struct dhcp_option *encapsulator;
+ struct dhcp_option *option;
+ ssize_t delta = ( new_len - old_len );
+ size_t new_options_len;
+ size_t new_encapsulator_len;
+ void *new_data;
+ void *source;
+ void *dest;
+ void *end;
+
+ /* Check for sufficient space, and update length fields */
+ if ( new_len > DHCP_MAX_LEN ) {
+ DBGC ( options, "DHCPOPT %p overlength option\n", options );
+ return -ENOSPC;
+ }
+ new_options_len = ( options->len + delta );
+ if ( new_options_len > options->max_len ) {
+ /* Reallocate options block if allowed to do so. */
+ if ( can_realloc ) {
+ new_data = realloc ( options->data, new_options_len );
+ if ( ! new_data ) {
+ DBGC ( options, "DHCPOPT %p could not "
+ "reallocate to %zd bytes\n", options,
+ new_options_len );
+ return -ENOMEM;
+ }
+ options->data = new_data;
+ options->max_len = new_options_len;
+ } else {
+ DBGC ( options, "DHCPOPT %p out of space\n", options );
+ return -ENOMEM;
+ }
+ }
+ if ( encap_offset >= 0 ) {
+ encapsulator = dhcp_option ( options, encap_offset );
+ new_encapsulator_len = ( encapsulator->len + delta );
+ if ( new_encapsulator_len > DHCP_MAX_LEN ) {
+ DBGC ( options, "DHCPOPT %p overlength encapsulator\n",
+ options );
+ return -ENOSPC;
+ }
+ encapsulator->len = new_encapsulator_len;
+ }
+ options->len = new_options_len;
+
+ /* Move remainder of option data */
+ option = dhcp_option ( options, offset );
+ source = ( ( ( void * ) option ) + old_len );
+ dest = ( ( ( void * ) option ) + new_len );
+ end = ( options->data + options->max_len );
+ memmove ( dest, source, ( end - dest ) );
+
+ return 0;
+}
+
+/**
+ * Set value of DHCP option
+ *
+ * @v options DHCP option block
+ * @v tag DHCP option tag
+ * @v data New value for DHCP option
+ * @v len Length of value, in bytes
+ * @v can_realloc Can reallocate options data if necessary
+ * @ret offset Offset of DHCP option, or negative error
+ *
+ * Sets the value of a DHCP option within the options block. The
+ * option may or may not already exist. Encapsulators will be created
+ * (and deleted) as necessary.
+ *
+ * This call may fail due to insufficient space in the options block.
+ * If it does fail, and the option existed previously, the option will
+ * be left with its original value.
+ */
+static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
+ const void *data, size_t len,
+ int can_realloc ) {
+ static const uint8_t empty_encapsulator[] = { DHCP_END };
+ int offset;
+ int encap_offset = -1;
+ int creation_offset = 0;
+ struct dhcp_option *option;
+ unsigned int encap_tag = DHCP_ENCAPSULATOR ( tag );
+ size_t old_len = 0;
+ size_t new_len = ( len ? ( len + DHCP_OPTION_HEADER_LEN ) : 0 );
+ int rc;
+
+ /* Sanity check */
+ if ( tag == DHCP_PAD )
+ return -ENOTTY;
+
+ /* Find old instance of this option, if any */
+ offset = find_dhcp_option_with_encap ( options, tag, &encap_offset );
+ if ( offset >= 0 ) {
+ old_len = dhcp_option_len ( dhcp_option ( options, offset ) );
+ DBGC ( options, "DHCPOPT %p resizing %s from %zd to %zd\n",
+ options, dhcp_tag_name ( tag ), old_len, new_len );
+ } else {
+ DBGC ( options, "DHCPOPT %p creating %s (length %zd)\n",
+ options, dhcp_tag_name ( tag ), new_len );
+ }
+
+ /* Ensure that encapsulator exists, if required */
+ if ( encap_tag ) {
+ if ( encap_offset < 0 )
+ encap_offset = set_dhcp_option ( options, encap_tag,
+ empty_encapsulator, 1,
+ can_realloc );
+ if ( encap_offset < 0 )
+ return encap_offset;
+ creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN );
+ }
+
+ /* Create new option if necessary */
+ if ( offset < 0 )
+ offset = creation_offset;
+
+ /* Resize option to fit new data */
+ if ( ( rc = resize_dhcp_option ( options, offset, encap_offset,
+ old_len, new_len,
+ can_realloc ) ) != 0 )
+ return rc;
+
+ /* Copy new data into option, if applicable */
+ if ( len ) {
+ option = dhcp_option ( options, offset );
+ option->tag = tag;
+ option->len = len;
+ memcpy ( &option->data, data, len );
+ }
+
+ /* Delete encapsulator if there's nothing else left in it */
+ if ( encap_offset >= 0 ) {
+ option = dhcp_option ( options, encap_offset );
+ if ( option->len <= 1 )
+ set_dhcp_option ( options, encap_tag, NULL, 0, 0 );
+ }
+
+ return offset;
+}
+
+/**
+ * Store value of DHCP option setting
+ *
+ * @v options DHCP option block
+ * @v tag Setting tag number
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+int dhcpopt_store ( struct dhcp_options *options, unsigned int tag,
+ const void *data, size_t len ) {
+ int offset;
+
+ offset = set_dhcp_option ( options, tag, data, len, 0 );
+ if ( offset < 0 )
+ return offset;
+ return 0;
+}
+
+/**
+ * Store value of DHCP option setting, extending options block if necessary
+ *
+ * @v options DHCP option block
+ * @v tag Setting tag number
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+int dhcpopt_extensible_store ( struct dhcp_options *options, unsigned int tag,
+ const void *data, size_t len ) {
+ int offset;
+
+ offset = set_dhcp_option ( options, tag, data, len, 1 );
+ if ( offset < 0 )
+ return offset;
+ return 0;
+}
+
+/**
+ * Fetch value of DHCP option setting
+ *
+ * @v options DHCP option block
+ * @v tag Setting tag number
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag,
+ void *data, size_t len ) {
+ int offset;
+ struct dhcp_option *option;
+ size_t option_len;
+
+ offset = find_dhcp_option_with_encap ( options, tag, NULL );
+ if ( offset < 0 )
+ return offset;
+
+ option = dhcp_option ( options, offset );
+ option_len = option->len;
+ if ( len > option_len )
+ len = option_len;
+ memcpy ( data, option->data, len );
+
+ return option_len;
+}
+
+/**
+ * Recalculate length of DHCP options block
+ *
+ * @v options Uninitialised DHCP option block
+ *
+ * The "used length" field will be updated based on scanning through
+ * the block to find the end of the options.
+ */
+static void dhcpopt_update_len ( struct dhcp_options *options ) {
+ struct dhcp_option *option;
+ int offset = 0;
+ ssize_t remaining = options->max_len;
+ unsigned int option_len;
+
+ /* Find last non-pad option */
+ options->len = 0;
+ while ( remaining ) {
+ option = dhcp_option ( options, offset );
+ option_len = dhcp_option_len ( option );
+ remaining -= option_len;
+ if ( remaining < 0 )
+ break;
+ offset += option_len;
+ if ( option->tag != DHCP_PAD )
+ options->len = offset;
+ }
+}
+
+/**
+ * Initialise prepopulated block of DHCP options
+ *
+ * @v options Uninitialised DHCP option block
+ * @v data Memory for DHCP option data
+ * @v max_len Length of memory for DHCP option data
+ *
+ * The memory content must already be filled with valid DHCP options.
+ * A zeroed block counts as a block of valid DHCP options.
+ */
+void dhcpopt_init ( struct dhcp_options *options, void *data,
+ size_t max_len ) {
+
+ /* Fill in fields */
+ options->data = data;
+ options->max_len = max_len;
+
+ /* Update length */
+ dhcpopt_update_len ( options );
+
+ DBGC ( options, "DHCPOPT %p created (data %p len %#zx max_len %#zx)\n",
+ options, options->data, options->len, options->max_len );
+}
diff --git a/gpxe/src/net/dhcppkt.c b/gpxe/src/net/dhcppkt.c
new file mode 100644
index 00000000..1cf99d8d
--- /dev/null
+++ b/gpxe/src/net/dhcppkt.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/dhcpopts.h>
+#include <gpxe/dhcppkt.h>
+
+/** @file
+ *
+ * DHCP packets
+ *
+ */
+
+/** A dedicated field within a DHCP packet */
+struct dhcp_packet_field {
+ /** Settings tag number */
+ unsigned int tag;
+ /** Offset within DHCP packet */
+ uint16_t offset;
+ /** Length of field */
+ uint16_t len;
+};
+
+/** Declare a dedicated field within a DHCP packet
+ *
+ * @v _tag Settings tag number
+ * @v _field Field name
+ */
+#define DHCP_PACKET_FIELD( _tag, _field ) { \
+ .tag = (_tag), \
+ .offset = offsetof ( struct dhcphdr, _field ), \
+ .len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ), \
+ }
+
+/** Dedicated fields within a DHCP packet */
+static struct dhcp_packet_field dhcp_packet_fields[] = {
+ DHCP_PACKET_FIELD ( DHCP_EB_YIADDR, yiaddr ),
+ DHCP_PACKET_FIELD ( DHCP_EB_SIADDR, siaddr ),
+ DHCP_PACKET_FIELD ( DHCP_TFTP_SERVER_NAME, sname ),
+ DHCP_PACKET_FIELD ( DHCP_BOOTFILE_NAME, file ),
+};
+
+/**
+ * Get address of a DHCP packet field
+ *
+ * @v dhcphdr DHCP packet header
+ * @v field DHCP packet field
+ * @ret data Packet field data
+ */
+static inline void * dhcp_packet_field ( struct dhcphdr *dhcphdr,
+ struct dhcp_packet_field *field ) {
+ return ( ( ( void * ) dhcphdr ) + field->offset );
+}
+
+/**
+ * Find DHCP packet field corresponding to settings tag number
+ *
+ * @v tag Settings tag number
+ * @ret field DHCP packet field, or NULL
+ */
+static struct dhcp_packet_field *
+find_dhcp_packet_field ( unsigned int tag ) {
+ struct dhcp_packet_field *field;
+ unsigned int i;
+
+ for ( i = 0 ; i < ( sizeof ( dhcp_packet_fields ) /
+ sizeof ( dhcp_packet_fields[0] ) ) ; i++ ) {
+ field = &dhcp_packet_fields[i];
+ if ( field->tag == tag )
+ return field;
+ }
+ return NULL;
+}
+
+/**
+ * Store value of DHCP packet setting
+ *
+ * @v dhcppkt DHCP packet
+ * @v tag Setting tag number
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag,
+ const void *data, size_t len ) {
+ struct dhcp_packet_field *field;
+ int rc;
+
+ /* If this is a special field, fill it in */
+ if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
+ if ( len > field->len )
+ return -ENOSPC;
+ memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ),
+ data, len );
+ return 0;
+ }
+
+ /* Otherwise, use the generic options block */
+ rc = dhcpopt_store ( &dhcppkt->options, tag, data, len );
+
+ /* Update our used-length field */
+ dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
+ dhcppkt->options.len );
+
+ return rc;
+}
+
+/**
+ * Fetch value of DHCP packet setting
+ *
+ * @v dhcppkt DHCP packet
+ * @v tag Setting tag number
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag,
+ void *data, size_t len ) {
+ struct dhcp_packet_field *field;
+
+ /* If this is a special field, return it */
+ if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) {
+ if ( len > field->len )
+ len = field->len;
+ memcpy ( data,
+ dhcp_packet_field ( dhcppkt->dhcphdr, field ), len );
+ return field->len;
+ }
+
+ /* Otherwise, use the generic options block */
+ return dhcpopt_fetch ( &dhcppkt->options, tag, data, len );
+}
+
+/**
+ * Initialise prepopulated DHCP packet
+ *
+ * @v dhcppkt Uninitialised DHCP packet
+ * @v data Memory for DHCP packet data
+ * @v max_len Length of memory for DHCP packet data
+ *
+ * The memory content must already be filled with valid DHCP options.
+ * A zeroed block counts as a block of valid DHCP options.
+ */
+void dhcppkt_init ( struct dhcp_packet *dhcppkt, void *data, size_t len ) {
+ dhcppkt->dhcphdr = data;
+ dhcppkt->max_len = len;
+ dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
+ ( len - offsetof ( struct dhcphdr, options ) ) );
+ dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
+ dhcppkt->options.len );
+}
diff --git a/gpxe/src/net/ethernet.c b/gpxe/src/net/ethernet.c
new file mode 100644
index 00000000..55035de5
--- /dev/null
+++ b/gpxe/src/net/ethernet.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <assert.h>
+#include <gpxe/if_arp.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/ethernet.h>
+
+/** @file
+ *
+ * Ethernet protocol
+ *
+ */
+
+/** Ethernet broadcast MAC address */
+static uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+/**
+ * Transmit Ethernet packet
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v net_protocol Network-layer protocol
+ * @v ll_dest Link-layer destination address
+ *
+ * Prepends the Ethernet link-layer header and transmits the packet.
+ */
+static int eth_tx ( struct io_buffer *iobuf, struct net_device *netdev,
+ struct net_protocol *net_protocol, const void *ll_dest ) {
+ struct ethhdr *ethhdr = iob_push ( iobuf, sizeof ( *ethhdr ) );
+
+ /* Build Ethernet header */
+ memcpy ( ethhdr->h_dest, ll_dest, ETH_ALEN );
+ memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN );
+ ethhdr->h_protocol = net_protocol->net_proto;
+
+ /* Hand off to network device */
+ return netdev_tx ( netdev, iobuf );
+}
+
+/**
+ * Process received Ethernet packet
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ *
+ * Strips off the Ethernet link-layer header and passes up to the
+ * network-layer protocol.
+ */
+static int eth_rx ( struct io_buffer *iobuf, struct net_device *netdev ) {
+ struct ethhdr *ethhdr = iobuf->data;
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *ethhdr ) ) {
+ DBG ( "Ethernet packet too short (%zd bytes)\n",
+ iob_len ( iobuf ) );
+ free_iob ( iobuf );
+ return -EINVAL;
+ }
+
+ /* Strip off Ethernet header */
+ iob_pull ( iobuf, sizeof ( *ethhdr ) );
+
+ /* Hand off to network-layer protocol */
+ return net_rx ( iobuf, netdev, ethhdr->h_protocol, ethhdr->h_source );
+}
+
+/**
+ * Transcribe Ethernet address
+ *
+ * @v ll_addr Link-layer address
+ * @ret string Link-layer address in human-readable format
+ */
+const char * eth_ntoa ( const void *ll_addr ) {
+ static char buf[18]; /* "00:00:00:00:00:00" */
+ const uint8_t *eth_addr = ll_addr;
+
+ sprintf ( buf, "%02x:%02x:%02x:%02x:%02x:%02x",
+ eth_addr[0], eth_addr[1], eth_addr[2],
+ eth_addr[3], eth_addr[4], eth_addr[5] );
+ return buf;
+}
+
+/** Ethernet protocol */
+struct ll_protocol ethernet_protocol __ll_protocol = {
+ .name = "Ethernet",
+ .ll_proto = htons ( ARPHRD_ETHER ),
+ .ll_addr_len = ETH_ALEN,
+ .ll_header_len = ETH_HLEN,
+ .ll_broadcast = eth_broadcast,
+ .tx = eth_tx,
+ .rx = eth_rx,
+ .ntoa = eth_ntoa,
+};
diff --git a/gpxe/src/net/fakedhcp.c b/gpxe/src/net/fakedhcp.c
new file mode 100644
index 00000000..c3054db1
--- /dev/null
+++ b/gpxe/src/net/fakedhcp.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <gpxe/settings.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/dhcppkt.h>
+#include <gpxe/fakedhcp.h>
+
+/** @file
+ *
+ * Fake DHCP packets
+ *
+ */
+
+/**
+ * Copy settings to DHCP packet
+ *
+ * @v dest Destination DHCP packet
+ * @v source Source settings block
+ * @v encapsulator Encapsulating setting tag number, or zero
+ * @ret rc Return status code
+ */
+static int copy_encap_settings ( struct dhcp_packet *dest,
+ struct settings *source,
+ unsigned int encapsulator ) {
+ struct setting setting = { .name = "" };
+ unsigned int subtag;
+ unsigned int tag;
+ int len;
+ int check_len;
+ int rc;
+
+ for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) {
+ tag = DHCP_ENCAP_OPT ( encapsulator, subtag );
+ switch ( tag ) {
+ case DHCP_EB_ENCAP:
+ case DHCP_VENDOR_ENCAP:
+ /* Process encapsulated settings */
+ if ( ( rc = copy_encap_settings ( dest, source,
+ tag ) ) != 0 )
+ return rc;
+ break;
+ default:
+ /* Copy setting, if present */
+ setting.tag = tag;
+ len = fetch_setting_len ( source, &setting );
+ if ( len < 0 )
+ break;
+ {
+ char buf[len];
+
+ check_len = fetch_setting ( source, &setting,
+ buf, sizeof (buf));
+ assert ( check_len == len );
+ if ( ( rc = dhcppkt_store ( dest, tag, buf,
+ sizeof(buf) )) !=0)
+ return rc;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Copy settings to DHCP packet
+ *
+ * @v dest Destination DHCP packet
+ * @v source Source settings block
+ * @ret rc Return status code
+ */
+static int copy_settings ( struct dhcp_packet *dest,
+ struct settings *source ) {
+ return copy_encap_settings ( dest, source, 0 );
+}
+
+/**
+ * Create fake DHCPDISCOVER packet
+ *
+ * @v netdev Network device
+ * @v data Buffer for DHCP packet
+ * @v max_len Size of DHCP packet buffer
+ * @ret rc Return status code
+ *
+ * Used by external code.
+ */
+int create_fakedhcpdiscover ( struct net_device *netdev,
+ void *data, size_t max_len ) {
+ struct dhcp_packet dhcppkt;
+ int rc;
+
+ if ( ( rc = create_dhcp_request ( &dhcppkt, netdev, NULL, data,
+ max_len ) ) != 0 ) {
+ DBG ( "Could not create DHCPDISCOVER: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Create fake DHCPACK packet
+ *
+ * @v netdev Network device
+ * @v data Buffer for DHCP packet
+ * @v max_len Size of DHCP packet buffer
+ * @ret rc Return status code
+ *
+ * Used by external code.
+ */
+int create_fakedhcpack ( struct net_device *netdev,
+ void *data, size_t max_len ) {
+ struct dhcp_packet dhcppkt;
+ int rc;
+
+ /* Create base DHCPACK packet */
+ if ( ( rc = create_dhcp_packet ( &dhcppkt, netdev, DHCPACK, NULL,
+ data, max_len ) ) != 0 ) {
+ DBG ( "Could not create DHCPACK: %s\n", strerror ( rc ) );
+ return rc;
+ }
+
+ /* Merge in globally-scoped settings, then netdev-specific
+ * settings. Do it in this order so that netdev-specific
+ * settings take precedence regardless of stated priorities.
+ */
+ if ( ( rc = copy_settings ( &dhcppkt, NULL ) ) != 0 ) {
+ DBG ( "Could not set DHCPACK global settings: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ if ( ( rc = copy_settings ( &dhcppkt,
+ netdev_settings ( netdev ) ) ) != 0 ) {
+ DBG ( "Could not set DHCPACK netdev settings: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Create ProxyDHCPACK packet
+ *
+ * @v netdev Network device
+ * @v data Buffer for DHCP packet
+ * @v max_len Size of DHCP packet buffer
+ * @ret rc Return status code
+ *
+ * Used by external code.
+ */
+int create_fakeproxydhcpack ( struct net_device *netdev,
+ void *data, size_t max_len ) {
+ struct dhcp_packet dhcppkt;
+ struct settings *settings;
+ int rc;
+
+ /* Identify ProxyDHCP settings */
+ settings = find_settings ( PROXYDHCP_SETTINGS_NAME );
+
+ /* No ProxyDHCP settings => return empty block */
+ if ( ! settings ) {
+ memset ( data, 0, max_len );
+ return 0;
+ }
+
+ /* Create base DHCPACK packet */
+ if ( ( rc = create_dhcp_packet ( &dhcppkt, netdev, DHCPACK, NULL,
+ data, max_len ) ) != 0 ) {
+ DBG ( "Could not create ProxyDHCPACK: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Merge in ProxyDHCP options */
+ if ( ( rc = copy_settings ( &dhcppkt, settings ) ) != 0 ) {
+ DBG ( "Could not set ProxyDHCPACK settings: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/gpxe/src/net/icmpv6.c b/gpxe/src/net/icmpv6.c
new file mode 100644
index 00000000..7b7146c2
--- /dev/null
+++ b/gpxe/src/net/icmpv6.c
@@ -0,0 +1,128 @@
+#include <stdint.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/in.h>
+#include <gpxe/ip6.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/ndp.h>
+#include <gpxe/icmp6.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/netdevice.h>
+
+struct tcpip_protocol icmp6_protocol;
+
+/**
+ * Send neighbour solicitation packet
+ *
+ * @v netdev Network device
+ * @v src Source address
+ * @v dest Destination address
+ *
+ * This function prepares a neighbour solicitation packet and sends it to the
+ * network layer.
+ */
+int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unused,
+ struct in6_addr *dest ) {
+ union {
+ struct sockaddr_in6 sin6;
+ struct sockaddr_tcpip st;
+ } st_dest;
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ struct neighbour_solicit *nsolicit;
+ struct io_buffer *iobuf = alloc_iob ( sizeof ( *nsolicit ) + MIN_IOB_LEN );
+ iob_reserve ( iobuf, MAX_HDR_LEN );
+ nsolicit = iob_put ( iobuf, sizeof ( *nsolicit ) );
+
+ /* Fill up the headers */
+ memset ( nsolicit, 0, sizeof ( *nsolicit ) );
+ nsolicit->type = ICMP6_NSOLICIT;
+ nsolicit->code = 0;
+ nsolicit->target = *dest;
+ nsolicit->opt_type = 1;
+ nsolicit->opt_len = ( 2 + ll_protocol->ll_addr_len ) / 8;
+ memcpy ( nsolicit->opt_ll_addr, netdev->ll_addr,
+ netdev->ll_protocol->ll_addr_len );
+ /* Partial checksum */
+ nsolicit->csum = 0;
+ nsolicit->csum = tcpip_chksum ( nsolicit, sizeof ( *nsolicit ) );
+
+ /* Solicited multicast address */
+ st_dest.sin6.sin_family = AF_INET6;
+ st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff;
+ st_dest.sin6.sin6_addr.in6_u.u6_addr8[2] = 0x02;
+ st_dest.sin6.sin6_addr.in6_u.u6_addr16[1] = 0x0000;
+ st_dest.sin6.sin6_addr.in6_u.u6_addr32[1] = 0x00000000;
+ st_dest.sin6.sin6_addr.in6_u.u6_addr16[4] = 0x0000;
+ st_dest.sin6.sin6_addr.in6_u.u6_addr16[5] = 0x0001;
+ st_dest.sin6.sin6_addr.in6_u.u6_addr32[3] = dest->in6_u.u6_addr32[3];
+ st_dest.sin6.sin6_addr.in6_u.u6_addr8[13] = 0xff;
+
+ /* Send packet over IP6 */
+ return tcpip_tx ( iobuf, &icmp6_protocol, &st_dest.st,
+ NULL, &nsolicit->csum );
+}
+
+/**
+ * Process ICMP6 headers
+ *
+ * @v iobuf I/O buffer
+ * @v st_src Source address
+ * @v st_dest Destination address
+ */
+static int icmp6_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
+ struct sockaddr_tcpip *st_dest, __unused uint16_t pshdr_csum ) {
+ struct icmp6_header *icmp6hdr = iobuf->data;
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *icmp6hdr ) ) {
+ DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
+ free_iob ( iobuf );
+ return -EINVAL;
+ }
+
+ /* TODO: Verify checksum */
+
+ /* Process the ICMP header */
+ switch ( icmp6hdr->type ) {
+ case ICMP6_NADVERT:
+ return ndp_process_advert ( iobuf, st_src, st_dest );
+ }
+ return -ENOSYS;
+}
+
+#if 0
+void icmp6_test_nadvert (struct net_device *netdev, struct sockaddr_in6 *server_p, char *ll_addr) {
+
+ struct sockaddr_in6 server;
+ memcpy ( &server, server_p, sizeof ( server ) );
+ struct io_buffer *rxiobuf = alloc_iob ( 500 );
+ iob_reserve ( rxiobuf, MAX_HDR_LEN );
+ struct neighbour_advert *nadvert = iob_put ( rxiobuf, sizeof ( *nadvert ) );
+ nadvert->type = 136;
+ nadvert->code = 0;
+ nadvert->flags = ICMP6_FLAGS_SOLICITED;
+ nadvert->csum = 0xffff;
+ nadvert->target = server.sin6_addr;
+ nadvert->opt_type = 2;
+ nadvert->opt_len = 1;
+ memcpy ( nadvert->opt_ll_addr, ll_addr, 6 );
+ struct ip6_header *ip6hdr = iob_push ( rxiobuf, sizeof ( *ip6hdr ) );
+ ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );
+ ip6hdr->hop_limit = 255;
+ ip6hdr->nxt_hdr = 58;
+ ip6hdr->payload_len = htons ( sizeof ( *nadvert ) );
+ ip6hdr->src = server.sin6_addr;
+ ip6hdr->dest = server.sin6_addr;
+ hex_dump ( rxiobuf->data, iob_len ( rxiobuf ) );
+ net_rx ( rxiobuf, netdev, htons ( ETH_P_IPV6 ), ll_addr );
+}
+#endif
+
+/** ICMP6 protocol */
+struct tcpip_protocol icmp6_protocol __tcpip_protocol = {
+ .name = "ICMP6",
+ .rx = icmp6_rx,
+ .tcpip_proto = IP_ICMP6, // 58
+};
diff --git a/gpxe/src/net/infiniband.c b/gpxe/src/net/infiniband.c
new file mode 100644
index 00000000..39d11285
--- /dev/null
+++ b/gpxe/src/net/infiniband.c
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <assert.h>
+#include <gpxe/list.h>
+#include <gpxe/if_arp.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/ipoib.h>
+#include <gpxe/infiniband.h>
+
+/** @file
+ *
+ * Infiniband protocol
+ *
+ */
+
+/**
+ * Create completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v num_cqes Number of completion queue entries
+ * @ret cq New completion queue
+ */
+struct ib_completion_queue * ib_create_cq ( struct ib_device *ibdev,
+ unsigned int num_cqes ) {
+ struct ib_completion_queue *cq;
+ int rc;
+
+ DBGC ( ibdev, "IBDEV %p creating completion queue\n", ibdev );
+
+ /* Allocate and initialise data structure */
+ cq = zalloc ( sizeof ( *cq ) );
+ if ( ! cq )
+ return NULL;
+ cq->num_cqes = num_cqes;
+ INIT_LIST_HEAD ( &cq->work_queues );
+
+ /* Perform device-specific initialisation and get CQN */
+ if ( ( rc = ibdev->op->create_cq ( ibdev, cq ) ) != 0 ) {
+ DBGC ( ibdev, "IBDEV %p could not initialise completion "
+ "queue: %s\n", ibdev, strerror ( rc ) );
+ free ( cq );
+ return NULL;
+ }
+
+ DBGC ( ibdev, "IBDEV %p created %d-entry completion queue %p (%p) "
+ "with CQN %#lx\n", ibdev, num_cqes, cq,
+ ib_cq_get_drvdata ( cq ), cq->cqn );
+ return cq;
+}
+
+/**
+ * Destroy completion queue
+ *
+ * @v ibdev Infiniband device
+ * @v cq Completion queue
+ */
+void ib_destroy_cq ( struct ib_device *ibdev,
+ struct ib_completion_queue *cq ) {
+ DBGC ( ibdev, "IBDEV %p destroying completion queue %#lx\n",
+ ibdev, cq->cqn );
+ assert ( list_empty ( &cq->work_queues ) );
+ ibdev->op->destroy_cq ( ibdev, cq );
+ free ( cq );
+}
+
+/**
+ * Create queue pair
+ *
+ * @v ibdev Infiniband device
+ * @v num_send_wqes Number of send work queue entries
+ * @v send_cq Send completion queue
+ * @v num_recv_wqes Number of receive work queue entries
+ * @v recv_cq Receive completion queue
+ * @v qkey Queue key
+ * @ret qp Queue pair
+ */
+struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev,
+ unsigned int num_send_wqes,
+ struct ib_completion_queue *send_cq,
+ unsigned int num_recv_wqes,
+ struct ib_completion_queue *recv_cq,
+ unsigned long qkey ) {
+ struct ib_queue_pair *qp;
+ size_t total_size;
+ int rc;
+
+ DBGC ( ibdev, "IBDEV %p creating queue pair\n", ibdev );
+
+ /* Allocate and initialise data structure */
+ total_size = ( sizeof ( *qp ) +
+ ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) ) +
+ ( num_recv_wqes * sizeof ( qp->recv.iobufs[0] ) ) );
+ qp = zalloc ( total_size );
+ if ( ! qp )
+ return NULL;
+ qp->qkey = qkey;
+ qp->send.qp = qp;
+ qp->send.is_send = 1;
+ qp->send.cq = send_cq;
+ list_add ( &qp->send.list, &send_cq->work_queues );
+ qp->send.num_wqes = num_send_wqes;
+ qp->send.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) );
+ qp->recv.qp = qp;
+ qp->recv.cq = recv_cq;
+ list_add ( &qp->recv.list, &recv_cq->work_queues );
+ qp->recv.num_wqes = num_recv_wqes;
+ qp->recv.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) +
+ ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) ));
+
+ /* Perform device-specific initialisation and get QPN */
+ if ( ( rc = ibdev->op->create_qp ( ibdev, qp ) ) != 0 ) {
+ DBGC ( ibdev, "IBDEV %p could not initialise queue pair: "
+ "%s\n", ibdev, strerror ( rc ) );
+ list_del ( &qp->send.list );
+ list_del ( &qp->recv.list );
+ free ( qp );
+ return NULL;
+ }
+
+ DBGC ( ibdev, "IBDEV %p created queue pair %p (%p) with QPN %#lx\n",
+ ibdev, qp, ib_qp_get_drvdata ( qp ), qp->qpn );
+ DBGC ( ibdev, "IBDEV %p QPN %#lx has %d send entries at [%p,%p)\n",
+ ibdev, qp->qpn, num_send_wqes, qp->send.iobufs,
+ qp->recv.iobufs );
+ DBGC ( ibdev, "IBDEV %p QPN %#lx has %d receive entries at [%p,%p)\n",
+ ibdev, qp->qpn, num_recv_wqes, qp->recv.iobufs,
+ ( ( ( void * ) qp ) + total_size ) );
+ return qp;
+}
+
+/**
+ * Destroy queue pair
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ */
+void ib_destroy_qp ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp ) {
+ DBGC ( ibdev, "IBDEV %p destroying queue pair %#lx\n",
+ ibdev, qp->qpn );
+ ibdev->op->destroy_qp ( ibdev, qp );
+ list_del ( &qp->send.list );
+ list_del ( &qp->recv.list );
+ free ( qp );
+}
+
+/**
+ * Find work queue belonging to completion queue
+ *
+ * @v cq Completion queue
+ * @v qpn Queue pair number
+ * @v is_send Find send work queue (rather than receive)
+ * @ret wq Work queue, or NULL if not found
+ */
+struct ib_work_queue * ib_find_wq ( struct ib_completion_queue *cq,
+ unsigned long qpn, int is_send ) {
+ struct ib_work_queue *wq;
+
+ list_for_each_entry ( wq, &cq->work_queues, list ) {
+ if ( ( wq->qp->qpn == qpn ) && ( wq->is_send == is_send ) )
+ return wq;
+ }
+ return NULL;
+}
+
+/***************************************************************************
+ *
+ * Management datagram operations
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Get port information
+ *
+ * @v ibdev Infiniband device
+ * @v port_info Port information datagram to fill in
+ * @ret rc Return status code
+ */
+static int ib_get_port_info ( struct ib_device *ibdev,
+ struct ib_mad_port_info *port_info ) {
+ struct ib_mad_hdr *hdr = &port_info->mad_hdr;
+ int rc;
+
+ /* Construct MAD */
+ memset ( port_info, 0, sizeof ( *port_info ) );
+ hdr->base_version = IB_MGMT_BASE_VERSION;
+ hdr->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+ hdr->class_version = 1;
+ hdr->method = IB_MGMT_METHOD_GET;
+ hdr->attr_id = htons ( IB_SMP_ATTR_PORT_INFO );
+ hdr->attr_mod = htonl ( ibdev->port );
+
+ if ( ( rc = ib_mad ( ibdev, hdr, sizeof ( *port_info ) ) ) != 0 ) {
+ DBGC ( ibdev, "IBDEV %p could not get port info: %s\n",
+ ibdev, strerror ( rc ) );
+ return rc;
+ }
+ return 0;
+}
+
+/**
+ * Get GUID information
+ *
+ * @v ibdev Infiniband device
+ * @v guid_info GUID information datagram to fill in
+ * @ret rc Return status code
+ */
+static int ib_get_guid_info ( struct ib_device *ibdev,
+ struct ib_mad_guid_info *guid_info ) {
+ struct ib_mad_hdr *hdr = &guid_info->mad_hdr;
+ int rc;
+
+ /* Construct MAD */
+ memset ( guid_info, 0, sizeof ( *guid_info ) );
+ hdr->base_version = IB_MGMT_BASE_VERSION;
+ hdr->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+ hdr->class_version = 1;
+ hdr->method = IB_MGMT_METHOD_GET;
+ hdr->attr_id = htons ( IB_SMP_ATTR_GUID_INFO );
+
+ if ( ( rc = ib_mad ( ibdev, hdr, sizeof ( *guid_info ) ) ) != 0 ) {
+ DBGC ( ibdev, "IBDEV %p could not get GUID info: %s\n",
+ ibdev, strerror ( rc ) );
+ return rc;
+ }
+ return 0;
+}
+
+/**
+ * Get partition key table
+ *
+ * @v ibdev Infiniband device
+ * @v guid_info Partition key table datagram to fill in
+ * @ret rc Return status code
+ */
+static int ib_get_pkey_table ( struct ib_device *ibdev,
+ struct ib_mad_pkey_table *pkey_table ) {
+ struct ib_mad_hdr *hdr = &pkey_table->mad_hdr;
+ int rc;
+
+ /* Construct MAD */
+ memset ( pkey_table, 0, sizeof ( *pkey_table ) );
+ hdr->base_version = IB_MGMT_BASE_VERSION;
+ hdr->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+ hdr->class_version = 1;
+ hdr->method = IB_MGMT_METHOD_GET;
+ hdr->attr_id = htons ( IB_SMP_ATTR_PKEY_TABLE );
+
+ if ( ( rc = ib_mad ( ibdev, hdr, sizeof ( *pkey_table ) ) ) != 0 ) {
+ DBGC ( ibdev, "IBDEV %p could not get pkey table: %s\n",
+ ibdev, strerror ( rc ) );
+ return rc;
+ }
+ return 0;
+}
+
+/**
+ * Wait for link up
+ *
+ * @v ibdev Infiniband device
+ * @ret rc Return status code
+ *
+ * This function shouldn't really exist. Unfortunately, IB links take
+ * a long time to come up, and we can't get various key parameters
+ * e.g. our own IPoIB MAC address without information from the subnet
+ * manager). We should eventually make link-up an asynchronous event.
+ */
+static int ib_wait_for_link ( struct ib_device *ibdev ) {
+ struct ib_mad_port_info port_info;
+ unsigned int retries;
+ int rc;
+
+ printf ( "Waiting for Infiniband link-up..." );
+ for ( retries = 20 ; retries ; retries-- ) {
+ if ( ( rc = ib_get_port_info ( ibdev, &port_info ) ) != 0 )
+ continue;
+ if ( ( ( port_info.port_state__link_speed_supported ) & 0xf )
+ == 4 ) {
+ printf ( "ok\n" );
+ return 0;
+ }
+ printf ( "." );
+ sleep ( 1 );
+ }
+ printf ( "failed\n" );
+ return -ENODEV;
+};
+
+/**
+ * Get MAD parameters
+ *
+ * @v ibdev Infiniband device
+ * @ret rc Return status code
+ */
+static int ib_get_mad_params ( struct ib_device *ibdev ) {
+ union {
+ /* This union exists just to save stack space */
+ struct ib_mad_port_info port_info;
+ struct ib_mad_guid_info guid_info;
+ struct ib_mad_pkey_table pkey_table;
+ } u;
+ int rc;
+
+ /* Port info gives us the first half of the port GID and the SM LID */
+ if ( ( rc = ib_get_port_info ( ibdev, &u.port_info ) ) != 0 )
+ return rc;
+ memcpy ( &ibdev->port_gid.u.bytes[0], u.port_info.gid_prefix, 8 );
+ ibdev->sm_lid = ntohs ( u.port_info.mastersm_lid );
+
+ /* GUID info gives us the second half of the port GID */
+ if ( ( rc = ib_get_guid_info ( ibdev, &u.guid_info ) ) != 0 )
+ return rc;
+ memcpy ( &ibdev->port_gid.u.bytes[8], u.guid_info.gid_local, 8 );
+
+ /* Get partition key */
+ if ( ( rc = ib_get_pkey_table ( ibdev, &u.pkey_table ) ) != 0 )
+ return rc;
+ ibdev->pkey = ntohs ( u.pkey_table.pkey[0][0] );
+
+ DBGC ( ibdev, "IBDEV %p port GID is %08lx:%08lx:%08lx:%08lx\n",
+ ibdev, htonl ( ibdev->port_gid.u.dwords[0] ),
+ htonl ( ibdev->port_gid.u.dwords[1] ),
+ htonl ( ibdev->port_gid.u.dwords[2] ),
+ htonl ( ibdev->port_gid.u.dwords[3] ) );
+
+ return 0;
+}
+
+/***************************************************************************
+ *
+ * Infiniband device creation/destruction
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Allocate Infiniband device
+ *
+ * @v priv_size Size of driver private data area
+ * @ret ibdev Infiniband device, or NULL
+ */
+struct ib_device * alloc_ibdev ( size_t priv_size ) {
+ struct ib_device *ibdev;
+ void *drv_priv;
+ size_t total_len;
+
+ total_len = ( sizeof ( *ibdev ) + priv_size );
+ ibdev = zalloc ( total_len );
+ if ( ibdev ) {
+ drv_priv = ( ( ( void * ) ibdev ) + sizeof ( *ibdev ) );
+ ib_set_drvdata ( ibdev, drv_priv );
+ }
+ return ibdev;
+}
+
+/**
+ * Register Infiniband device
+ *
+ * @v ibdev Infiniband device
+ * @ret rc Return status code
+ */
+int register_ibdev ( struct ib_device *ibdev ) {
+ int rc;
+
+ /* Open link */
+ if ( ( rc = ib_open ( ibdev ) ) != 0 )
+ goto err_open;
+
+ /* Wait for link */
+ if ( ( rc = ib_wait_for_link ( ibdev ) ) != 0 )
+ goto err_wait_for_link;
+
+ /* Get MAD parameters */
+ if ( ( rc = ib_get_mad_params ( ibdev ) ) != 0 )
+ goto err_get_mad_params;
+
+ /* Add IPoIB device */
+ if ( ( rc = ipoib_probe ( ibdev ) ) != 0 ) {
+ DBGC ( ibdev, "IBDEV %p could not add IPoIB device: %s\n",
+ ibdev, strerror ( rc ) );
+ goto err_ipoib_probe;
+ }
+
+ return 0;
+
+ err_ipoib_probe:
+ err_get_mad_params:
+ err_wait_for_link:
+ ib_close ( ibdev );
+ err_open:
+ return rc;
+}
+
+/**
+ * Unregister Infiniband device
+ *
+ * @v ibdev Infiniband device
+ */
+void unregister_ibdev ( struct ib_device *ibdev ) {
+ ipoib_remove ( ibdev );
+ ib_close ( ibdev );
+}
+
+/**
+ * Free Infiniband device
+ *
+ * @v ibdev Infiniband device
+ */
+void free_ibdev ( struct ib_device *ibdev ) {
+ free ( ibdev );
+}
+
diff --git a/gpxe/src/net/iobpad.c b/gpxe/src/net/iobpad.c
new file mode 100644
index 00000000..9961edca
--- /dev/null
+++ b/gpxe/src/net/iobpad.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * I/O buffer padding
+ *
+ */
+
+#include <string.h>
+#include <gpxe/iobuf.h>
+
+/**
+ * Pad I/O buffer
+ *
+ * @v iobuf I/O buffer
+ * @v min_len Minimum length
+ *
+ * This function pads and aligns I/O buffers, for devices that
+ * aren't capable of padding in hardware, or that require specific
+ * alignment in TX buffers. The packet data will end up aligned to a
+ * multiple of @c IOB_ALIGN.
+ *
+ * @c min_len must not exceed @v IOB_ZLEN.
+ */
+void iob_pad ( struct io_buffer *iobuf, size_t min_len ) {
+ void *data;
+ size_t len;
+ size_t headroom;
+ signed int pad_len;
+
+ assert ( min_len <= IOB_ZLEN );
+
+ /* Move packet data to start of I/O buffer. This will both
+ * align the data (since I/O buffers are aligned to
+ * IOB_ALIGN) and give us sufficient space for the
+ * zero-padding
+ */
+ data = iobuf->data;
+ len = iob_len ( iobuf );
+ headroom = iob_headroom ( iobuf );
+ iob_push ( iobuf, headroom );
+ memmove ( iobuf->data, data, len );
+ iob_unput ( iobuf, headroom );
+
+ /* Pad to minimum packet length */
+ pad_len = ( min_len - iob_len ( iobuf ) );
+ if ( pad_len > 0 )
+ memset ( iob_put ( iobuf, pad_len ), 0, pad_len );
+}
diff --git a/gpxe/src/net/ipv4.c b/gpxe/src/net/ipv4.c
new file mode 100644
index 00000000..591293b7
--- /dev/null
+++ b/gpxe/src/net/ipv4.c
@@ -0,0 +1,633 @@
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/list.h>
+#include <gpxe/in.h>
+#include <gpxe/arp.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/ip.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/settings.h>
+
+/** @file
+ *
+ * IPv4 protocol
+ *
+ */
+
+/* Unique IP datagram identification number */
+static uint16_t next_ident = 0;
+
+struct net_protocol ipv4_protocol;
+
+/** List of IPv4 miniroutes */
+struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes );
+
+/** List of fragment reassembly buffers */
+static LIST_HEAD ( frag_buffers );
+
+/**
+ * Add IPv4 minirouting table entry
+ *
+ * @v netdev Network device
+ * @v address IPv4 address
+ * @v netmask Subnet mask
+ * @v gateway Gateway address (or @c INADDR_NONE for no gateway)
+ * @ret miniroute Routing table entry, or NULL
+ */
+static struct ipv4_miniroute * __malloc
+add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address,
+ struct in_addr netmask, struct in_addr gateway ) {
+ struct ipv4_miniroute *miniroute;
+
+ DBG ( "IPv4 add %s", inet_ntoa ( address ) );
+ DBG ( "/%s ", inet_ntoa ( netmask ) );
+ if ( gateway.s_addr != INADDR_NONE )
+ DBG ( "gw %s ", inet_ntoa ( gateway ) );
+ DBG ( "via %s\n", netdev->name );
+
+ /* Allocate and populate miniroute structure */
+ miniroute = malloc ( sizeof ( *miniroute ) );
+ if ( ! miniroute ) {
+ DBG ( "IPv4 could not add miniroute\n" );
+ return NULL;
+ }
+
+ /* Record routing information */
+ miniroute->netdev = netdev_get ( netdev );
+ miniroute->address = address;
+ miniroute->netmask = netmask;
+ miniroute->gateway = gateway;
+
+ /* Add to end of list if we have a gateway, otherwise
+ * to start of list.
+ */
+ if ( gateway.s_addr != INADDR_NONE ) {
+ list_add_tail ( &miniroute->list, &ipv4_miniroutes );
+ } else {
+ list_add ( &miniroute->list, &ipv4_miniroutes );
+ }
+
+ return miniroute;
+}
+
+/**
+ * Delete IPv4 minirouting table entry
+ *
+ * @v miniroute Routing table entry
+ */
+static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
+
+ DBG ( "IPv4 del %s", inet_ntoa ( miniroute->address ) );
+ DBG ( "/%s ", inet_ntoa ( miniroute->netmask ) );
+ if ( miniroute->gateway.s_addr != INADDR_NONE )
+ DBG ( "gw %s ", inet_ntoa ( miniroute->gateway ) );
+ DBG ( "via %s\n", miniroute->netdev->name );
+
+ netdev_put ( miniroute->netdev );
+ list_del ( &miniroute->list );
+ free ( miniroute );
+}
+
+/**
+ * Perform IPv4 routing
+ *
+ * @v dest Final destination address
+ * @ret dest Next hop destination address
+ * @ret miniroute Routing table entry to use, or NULL if no route
+ *
+ * If the route requires use of a gateway, the next hop destination
+ * address will be overwritten with the gateway address.
+ */
+static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) {
+ struct ipv4_miniroute *miniroute;
+ int local;
+ int has_gw;
+
+ /* Never attempt to route the broadcast address */
+ if ( dest->s_addr == INADDR_BROADCAST )
+ return NULL;
+
+ /* Find first usable route in routing table */
+ list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
+ local = ( ( ( dest->s_addr ^ miniroute->address.s_addr )
+ & miniroute->netmask.s_addr ) == 0 );
+ has_gw = ( miniroute->gateway.s_addr != INADDR_NONE );
+ if ( local || has_gw ) {
+ if ( ! local )
+ *dest = miniroute->gateway;
+ return miniroute;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Fragment reassembly counter timeout
+ *
+ * @v timer Retry timer
+ * @v over If asserted, the timer is greater than @c MAX_TIMEOUT
+ */
+static void ipv4_frag_expired ( struct retry_timer *timer __unused,
+ int over ) {
+ if ( over ) {
+ DBG ( "Fragment reassembly timeout" );
+ /* Free the fragment buffer */
+ }
+}
+
+/**
+ * Free fragment buffer
+ *
+ * @v fragbug Fragment buffer
+ */
+static void free_fragbuf ( struct frag_buffer *fragbuf ) {
+ free ( fragbuf );
+}
+
+/**
+ * Fragment reassembler
+ *
+ * @v iobuf I/O buffer, fragment of the datagram
+ * @ret frag_iob Reassembled packet, or NULL
+ */
+static struct io_buffer * ipv4_reassemble ( struct io_buffer * iobuf ) {
+ struct iphdr *iphdr = iobuf->data;
+ struct frag_buffer *fragbuf;
+
+ /**
+ * Check if the fragment belongs to any fragment series
+ */
+ list_for_each_entry ( fragbuf, &frag_buffers, list ) {
+ if ( fragbuf->ident == iphdr->ident &&
+ fragbuf->src.s_addr == iphdr->src.s_addr ) {
+ /**
+ * Check if the packet is the expected fragment
+ *
+ * The offset of the new packet must be equal to the
+ * length of the data accumulated so far (the length of
+ * the reassembled I/O buffer
+ */
+ if ( iob_len ( fragbuf->frag_iob ) ==
+ ( iphdr->frags & IP_MASK_OFFSET ) ) {
+ /**
+ * Append the contents of the fragment to the
+ * reassembled I/O buffer
+ */
+ iob_pull ( iobuf, sizeof ( *iphdr ) );
+ memcpy ( iob_put ( fragbuf->frag_iob,
+ iob_len ( iobuf ) ),
+ iobuf->data, iob_len ( iobuf ) );
+ free_iob ( iobuf );
+
+ /** Check if the fragment series is over */
+ if ( !iphdr->frags & IP_MASK_MOREFRAGS ) {
+ iobuf = fragbuf->frag_iob;
+ free_fragbuf ( fragbuf );
+ return iobuf;
+ }
+
+ } else {
+ /* Discard the fragment series */
+ free_fragbuf ( fragbuf );
+ free_iob ( iobuf );
+ }
+ return NULL;
+ }
+ }
+
+ /** Check if the fragment is the first in the fragment series */
+ if ( iphdr->frags & IP_MASK_MOREFRAGS &&
+ ( ( iphdr->frags & IP_MASK_OFFSET ) == 0 ) ) {
+
+ /** Create a new fragment buffer */
+ fragbuf = ( struct frag_buffer* ) malloc ( sizeof( *fragbuf ) );
+ fragbuf->ident = iphdr->ident;
+ fragbuf->src = iphdr->src;
+
+ /* Set up the reassembly I/O buffer */
+ fragbuf->frag_iob = alloc_iob ( IP_FRAG_IOB_SIZE );
+ iob_pull ( iobuf, sizeof ( *iphdr ) );
+ memcpy ( iob_put ( fragbuf->frag_iob, iob_len ( iobuf ) ),
+ iobuf->data, iob_len ( iobuf ) );
+ free_iob ( iobuf );
+
+ /* Set the reassembly timer */
+ fragbuf->frag_timer.timeout = IP_FRAG_TIMEOUT;
+ fragbuf->frag_timer.expired = ipv4_frag_expired;
+ start_timer ( &fragbuf->frag_timer );
+
+ /* Add the fragment buffer to the list of fragment buffers */
+ list_add ( &fragbuf->list, &frag_buffers );
+ }
+
+ return NULL;
+}
+
+/**
+ * Add IPv4 pseudo-header checksum to existing checksum
+ *
+ * @v iobuf I/O buffer
+ * @v csum Existing checksum
+ * @ret csum Updated checksum
+ */
+static uint16_t ipv4_pshdr_chksum ( struct io_buffer *iobuf, uint16_t csum ) {
+ struct ipv4_pseudo_header pshdr;
+ struct iphdr *iphdr = iobuf->data;
+ size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
+
+ /* Build pseudo-header */
+ pshdr.src = iphdr->src;
+ pshdr.dest = iphdr->dest;
+ pshdr.zero_padding = 0x00;
+ pshdr.protocol = iphdr->protocol;
+ pshdr.len = htons ( iob_len ( iobuf ) - hdrlen );
+
+ /* Update the checksum value */
+ return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
+}
+
+/**
+ * Determine link-layer address
+ *
+ * @v dest IPv4 destination address
+ * @v src IPv4 source address
+ * @v netdev Network device
+ * @v ll_dest Link-layer destination address buffer
+ * @ret rc Return status code
+ */
+static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
+ struct net_device *netdev, uint8_t *ll_dest ) {
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ uint8_t *dest_bytes = ( ( uint8_t * ) &dest );
+
+ if ( dest.s_addr == INADDR_BROADCAST ) {
+ /* Broadcast address */
+ memcpy ( ll_dest, ll_protocol->ll_broadcast,
+ ll_protocol->ll_addr_len );
+ return 0;
+ } else if ( IN_MULTICAST ( dest.s_addr ) ) {
+ /* Special case: IPv4 multicast over Ethernet. This
+ * code may need to be generalised once we find out
+ * what happens for other link layers.
+ */
+ ll_dest[0] = 0x01;
+ ll_dest[1] = 0x00;
+ ll_dest[2] = 0x5e;
+ ll_dest[3] = dest_bytes[1] & 0x7f;
+ ll_dest[4] = dest_bytes[2];
+ ll_dest[5] = dest_bytes[3];
+ return 0;
+ } else {
+ /* Unicast address: resolve via ARP */
+ return arp_resolve ( netdev, &ipv4_protocol, &dest,
+ &src, ll_dest );
+ }
+}
+
+/**
+ * Transmit IP packet
+ *
+ * @v iobuf I/O buffer
+ * @v tcpip Transport-layer protocol
+ * @v st_dest Destination network-layer address
+ * @v netdev Network device to use if no route found, or NULL
+ * @v trans_csum Transport-layer checksum to complete, or NULL
+ * @ret rc Status
+ *
+ * This function expects a transport-layer segment and prepends the IP header
+ */
+static int ipv4_tx ( struct io_buffer *iobuf,
+ struct tcpip_protocol *tcpip_protocol,
+ struct sockaddr_tcpip *st_dest,
+ struct net_device *netdev,
+ uint16_t *trans_csum ) {
+ struct iphdr *iphdr = iob_push ( iobuf, sizeof ( *iphdr ) );
+ struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
+ struct ipv4_miniroute *miniroute;
+ struct in_addr next_hop;
+ uint8_t ll_dest[MAX_LL_ADDR_LEN];
+ int rc;
+
+ /* Fill up the IP header, except source address */
+ memset ( iphdr, 0, sizeof ( *iphdr ) );
+ iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) );
+ iphdr->service = IP_TOS;
+ iphdr->len = htons ( iob_len ( iobuf ) );
+ iphdr->ident = htons ( ++next_ident );
+ iphdr->ttl = IP_TTL;
+ iphdr->protocol = tcpip_protocol->tcpip_proto;
+ iphdr->dest = sin_dest->sin_addr;
+
+ /* Use routing table to identify next hop and transmitting netdev */
+ next_hop = iphdr->dest;
+ if ( ( miniroute = ipv4_route ( &next_hop ) ) ) {
+ iphdr->src = miniroute->address;
+ netdev = miniroute->netdev;
+ }
+ if ( ! netdev ) {
+ DBG ( "IPv4 has no route to %s\n", inet_ntoa ( iphdr->dest ) );
+ rc = -ENETUNREACH;
+ goto err;
+ }
+
+ /* Determine link-layer destination address */
+ if ( ( rc = ipv4_ll_addr ( next_hop, iphdr->src, netdev,
+ ll_dest ) ) != 0 ) {
+ DBG ( "IPv4 has no link-layer address for %s: %s\n",
+ inet_ntoa ( next_hop ), strerror ( rc ) );
+ goto err;
+ }
+
+ /* Fix up checksums */
+ if ( trans_csum )
+ *trans_csum = ipv4_pshdr_chksum ( iobuf, *trans_csum );
+ iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
+
+ /* Print IP4 header for debugging */
+ DBG ( "IPv4 TX %s->", inet_ntoa ( iphdr->src ) );
+ DBG ( "%s len %d proto %d id %04x csum %04x\n",
+ inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ), iphdr->protocol,
+ ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) );
+
+ /* Hand off to link layer */
+ if ( ( rc = net_tx ( iobuf, netdev, &ipv4_protocol, ll_dest ) ) != 0 ) {
+ DBG ( "IPv4 could not transmit packet via %s: %s\n",
+ netdev->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+
+ err:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * Process incoming packets
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v ll_source Link-layer destination source
+ *
+ * This function expects an IP4 network datagram. It processes the headers
+ * and sends it to the transport layer.
+ */
+static int ipv4_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused,
+ const void *ll_source __unused ) {
+ struct iphdr *iphdr = iobuf->data;
+ size_t hdrlen;
+ size_t len;
+ union {
+ struct sockaddr_in sin;
+ struct sockaddr_tcpip st;
+ } src, dest;
+ uint16_t csum;
+ uint16_t pshdr_csum;
+ int rc;
+
+ /* Sanity check the IPv4 header */
+ if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) {
+ DBG ( "IPv4 packet too short at %zd bytes (min %zd bytes)\n",
+ iob_len ( iobuf ), sizeof ( *iphdr ) );
+ goto err;
+ }
+ if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) {
+ DBG ( "IPv4 version %#02x not supported\n", iphdr->verhdrlen );
+ goto err;
+ }
+ hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
+ if ( hdrlen < sizeof ( *iphdr ) ) {
+ DBG ( "IPv4 header too short at %zd bytes (min %zd bytes)\n",
+ hdrlen, sizeof ( *iphdr ) );
+ goto err;
+ }
+ if ( hdrlen > iob_len ( iobuf ) ) {
+ DBG ( "IPv4 header too long at %zd bytes "
+ "(packet is %zd bytes)\n", hdrlen, iob_len ( iobuf ) );
+ goto err;
+ }
+ if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) {
+ DBG ( "IPv4 checksum incorrect (is %04x including checksum "
+ "field, should be 0000)\n", csum );
+ goto err;
+ }
+ len = ntohs ( iphdr->len );
+ if ( len < hdrlen ) {
+ DBG ( "IPv4 length too short at %zd bytes "
+ "(header is %zd bytes)\n", len, hdrlen );
+ goto err;
+ }
+ if ( len > iob_len ( iobuf ) ) {
+ DBG ( "IPv4 length too long at %zd bytes "
+ "(packet is %zd bytes)\n", len, iob_len ( iobuf ) );
+ goto err;
+ }
+
+ /* Print IPv4 header for debugging */
+ DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) );
+ DBG ( "%s len %d proto %d id %04x csum %04x\n",
+ inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol,
+ ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) );
+
+ /* Truncate packet to correct length, calculate pseudo-header
+ * checksum and then strip off the IPv4 header.
+ */
+ iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) );
+ pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM );
+ iob_pull ( iobuf, hdrlen );
+
+ /* Fragment reassembly */
+ if ( ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) ) ||
+ ( ( iphdr->frags & htons ( IP_MASK_OFFSET ) ) != 0 ) ) {
+ /* Pass the fragment to ipv4_reassemble() which either
+ * returns a fully reassembled I/O buffer or NULL.
+ */
+ iobuf = ipv4_reassemble ( iobuf );
+ if ( ! iobuf )
+ return 0;
+ }
+
+ /* Construct socket addresses and hand off to transport layer */
+ memset ( &src, 0, sizeof ( src ) );
+ src.sin.sin_family = AF_INET;
+ src.sin.sin_addr = iphdr->src;
+ memset ( &dest, 0, sizeof ( dest ) );
+ dest.sin.sin_family = AF_INET;
+ dest.sin.sin_addr = iphdr->dest;
+ if ( ( rc = tcpip_rx ( iobuf, iphdr->protocol, &src.st,
+ &dest.st, pshdr_csum ) ) != 0 ) {
+ DBG ( "IPv4 received packet rejected by stack: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+
+ err:
+ free_iob ( iobuf );
+ return -EINVAL;
+}
+
+/**
+ * Check existence of IPv4 address for ARP
+ *
+ * @v netdev Network device
+ * @v net_addr Network-layer address
+ * @ret rc Return status code
+ */
+static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) {
+ const struct in_addr *address = net_addr;
+ struct ipv4_miniroute *miniroute;
+
+ list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
+ if ( ( miniroute->netdev == netdev ) &&
+ ( miniroute->address.s_addr == address->s_addr ) ) {
+ /* Found matching address */
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+/**
+ * Convert IPv4 address to dotted-quad notation
+ *
+ * @v in IP address
+ * @ret string IP address in dotted-quad notation
+ */
+char * inet_ntoa ( struct in_addr in ) {
+ static char buf[16]; /* "xxx.xxx.xxx.xxx" */
+ uint8_t *bytes = ( uint8_t * ) &in;
+
+ sprintf ( buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] );
+ return buf;
+}
+
+/**
+ * Transcribe IP address
+ *
+ * @v net_addr IP address
+ * @ret string IP address in dotted-quad notation
+ *
+ */
+static const char * ipv4_ntoa ( const void *net_addr ) {
+ return inet_ntoa ( * ( ( struct in_addr * ) net_addr ) );
+}
+
+/** IPv4 protocol */
+struct net_protocol ipv4_protocol __net_protocol = {
+ .name = "IP",
+ .net_proto = htons ( ETH_P_IP ),
+ .net_addr_len = sizeof ( struct in_addr ),
+ .rx = ipv4_rx,
+ .ntoa = ipv4_ntoa,
+};
+
+/** IPv4 TCPIP net protocol */
+struct tcpip_net_protocol ipv4_tcpip_protocol __tcpip_net_protocol = {
+ .name = "IPv4",
+ .sa_family = AF_INET,
+ .tx = ipv4_tx,
+};
+
+/** IPv4 ARP protocol */
+struct arp_net_protocol ipv4_arp_protocol __arp_net_protocol = {
+ .net_protocol = &ipv4_protocol,
+ .check = ipv4_arp_check,
+};
+
+/******************************************************************************
+ *
+ * Settings
+ *
+ ******************************************************************************
+ */
+
+/** IPv4 address setting */
+struct setting ip_setting __setting = {
+ .name = "ip",
+ .description = "IPv4 address",
+ .tag = DHCP_EB_YIADDR,
+ .type = &setting_type_ipv4,
+};
+
+/** IPv4 subnet mask setting */
+struct setting netmask_setting __setting = {
+ .name = "netmask",
+ .description = "IPv4 subnet mask",
+ .tag = DHCP_SUBNET_MASK,
+ .type = &setting_type_ipv4,
+};
+
+/** Default gateway setting */
+struct setting gateway_setting __setting = {
+ .name = "gateway",
+ .description = "Default gateway",
+ .tag = DHCP_ROUTERS,
+ .type = &setting_type_ipv4,
+};
+
+/**
+ * Create IPv4 routing table based on configured settings
+ *
+ * @ret rc Return status code
+ */
+static int ipv4_create_routes ( void ) {
+ struct ipv4_miniroute *miniroute;
+ struct ipv4_miniroute *tmp;
+ struct net_device *netdev;
+ struct settings *settings;
+ struct in_addr address = { 0 };
+ struct in_addr netmask = { 0 };
+ struct in_addr gateway = { INADDR_NONE };
+
+ /* Delete all existing routes */
+ list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list )
+ del_ipv4_miniroute ( miniroute );
+
+ /* Create a route for each configured network device */
+ for_each_netdev ( netdev ) {
+ settings = netdev_settings ( netdev );
+ /* Get IPv4 address */
+ address.s_addr = 0;
+ fetch_ipv4_setting ( settings, &ip_setting, &address );
+ if ( ! address.s_addr )
+ continue;
+ /* Calculate default netmask */
+ if ( IN_CLASSA ( ntohl ( address.s_addr ) ) ) {
+ netmask.s_addr = htonl ( IN_CLASSA_NET );
+ } else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) {
+ netmask.s_addr = htonl ( IN_CLASSB_NET );
+ } else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) {
+ netmask.s_addr = htonl ( IN_CLASSC_NET );
+ } else {
+ netmask.s_addr = 0;
+ }
+ /* Override with subnet mask, if present */
+ fetch_ipv4_setting ( settings, &netmask_setting, &netmask );
+ /* Get default gateway, if present */
+ gateway.s_addr = INADDR_NONE;
+ fetch_ipv4_setting ( settings, &gateway_setting, &gateway );
+ /* Configure route */
+ miniroute = add_ipv4_miniroute ( netdev, address,
+ netmask, gateway );
+ if ( ! miniroute )
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/** IPv4 settings applicator */
+struct settings_applicator ipv4_settings_applicator __settings_applicator = {
+ .apply = ipv4_create_routes,
+};
diff --git a/gpxe/src/net/ipv6.c b/gpxe/src/net/ipv6.c
new file mode 100644
index 00000000..3407d538
--- /dev/null
+++ b/gpxe/src/net/ipv6.c
@@ -0,0 +1,380 @@
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <byteswap.h>
+#include <gpxe/in.h>
+#include <gpxe/ip6.h>
+#include <gpxe/ndp.h>
+#include <gpxe/list.h>
+#include <gpxe/icmp6.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/socket.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/if_ether.h>
+
+struct net_protocol ipv6_protocol;
+
+/* Unspecified IP6 address */
+static struct in6_addr ip6_none = {
+ .in6_u.u6_addr32 = { 0,0,0,0 }
+};
+
+/** An IPv6 routing table entry */
+struct ipv6_miniroute {
+ /* List of miniroutes */
+ struct list_head list;
+
+ /* Network device */
+ struct net_device *netdev;
+
+ /* Destination prefix */
+ struct in6_addr prefix;
+ /* Prefix length */
+ int prefix_len;
+ /* IPv6 address of interface */
+ struct in6_addr address;
+ /* Gateway address */
+ struct in6_addr gateway;
+};
+
+/** List of IPv6 miniroutes */
+static LIST_HEAD ( miniroutes );
+
+/**
+ * Add IPv6 minirouting table entry
+ *
+ * @v netdev Network device
+ * @v prefix Destination prefix
+ * @v address Address of the interface
+ * @v gateway Gateway address (or ::0 for no gateway)
+ * @ret miniroute Routing table entry, or NULL
+ */
+static struct ipv6_miniroute * __malloc
+add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr prefix,
+ int prefix_len, struct in6_addr address,
+ struct in6_addr gateway ) {
+ struct ipv6_miniroute *miniroute;
+
+ miniroute = malloc ( sizeof ( *miniroute ) );
+ if ( miniroute ) {
+ /* Record routing information */
+ miniroute->netdev = netdev_get ( netdev );
+ miniroute->prefix = prefix;
+ miniroute->prefix_len = prefix_len;
+ miniroute->address = address;
+ miniroute->gateway = gateway;
+
+ /* Add miniroute to list of miniroutes */
+ if ( !IP6_EQUAL ( gateway, ip6_none ) ) {
+ list_add_tail ( &miniroute->list, &miniroutes );
+ } else {
+ list_add ( &miniroute->list, &miniroutes );
+ }
+ }
+
+ return miniroute;
+}
+
+/**
+ * Delete IPv6 minirouting table entry
+ *
+ * @v miniroute Routing table entry
+ */
+static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
+ netdev_put ( miniroute->netdev );
+ list_del ( &miniroute->list );
+ free ( miniroute );
+}
+
+/**
+ * Add IPv6 interface
+ *
+ * @v netdev Network device
+ * @v prefix Destination prefix
+ * @v address Address of the interface
+ * @v gateway Gateway address (or ::0 for no gateway)
+ */
+int add_ipv6_address ( struct net_device *netdev, struct in6_addr prefix,
+ int prefix_len, struct in6_addr address,
+ struct in6_addr gateway ) {
+ struct ipv6_miniroute *miniroute;
+
+ /* Clear any existing address for this net device */
+ del_ipv6_address ( netdev );
+
+ /* Add new miniroute */
+ miniroute = add_ipv6_miniroute ( netdev, prefix, prefix_len, address,
+ gateway );
+ if ( ! miniroute )
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * Remove IPv6 interface
+ *
+ * @v netdev Network device
+ */
+void del_ipv6_address ( struct net_device *netdev ) {
+ struct ipv6_miniroute *miniroute;
+
+ list_for_each_entry ( miniroute, &miniroutes, list ) {
+ if ( miniroute->netdev == netdev ) {
+ del_ipv6_miniroute ( miniroute );
+ break;
+ }
+ }
+}
+
+/**
+ * Calculate TCPIP checksum
+ *
+ * @v iobuf I/O buffer
+ * @v tcpip TCP/IP protocol
+ *
+ * This function constructs the pseudo header and completes the checksum in the
+ * upper layer header.
+ */
+static uint16_t ipv6_tx_csum ( struct io_buffer *iobuf, uint16_t csum ) {
+ struct ip6_header *ip6hdr = iobuf->data;
+ struct ipv6_pseudo_header pshdr;
+
+ /* Calculate pseudo header */
+ memset ( &pshdr, 0, sizeof ( pshdr ) );
+ pshdr.src = ip6hdr->src;
+ pshdr.dest = ip6hdr->dest;
+ pshdr.len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
+ pshdr.nxt_hdr = ip6hdr->nxt_hdr;
+
+ /* Update checksum value */
+ return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
+}
+
+/**
+ * Dump IP6 header for debugging
+ *
+ * ip6hdr IPv6 header
+ */
+void ipv6_dump ( struct ip6_header *ip6hdr ) {
+ DBG ( "IP6 %p src %s dest %s nxt_hdr %d len %d\n", ip6hdr,
+ inet6_ntoa ( ip6hdr->src ), inet6_ntoa ( ip6hdr->dest ),
+ ip6hdr->nxt_hdr, ntohs ( ip6hdr->payload_len ) );
+}
+
+/**
+ * Transmit IP6 packet
+ *
+ * iobuf I/O buffer
+ * tcpip TCP/IP protocol
+ * st_dest Destination socket address
+ *
+ * This function prepends the IPv6 headers to the payload an transmits it.
+ */
+static int ipv6_tx ( struct io_buffer *iobuf,
+ struct tcpip_protocol *tcpip,
+ struct sockaddr_tcpip *st_dest,
+ struct net_device *netdev,
+ uint16_t *trans_csum ) {
+ struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest;
+ struct in6_addr next_hop;
+ struct ipv6_miniroute *miniroute;
+ uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
+ const uint8_t *ll_dest = ll_dest_buf;
+ int rc;
+
+ /* Construct the IPv6 packet */
+ struct ip6_header *ip6hdr = iob_push ( iobuf, sizeof ( *ip6hdr ) );
+ memset ( ip6hdr, 0, sizeof ( *ip6hdr) );
+ ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );//IP6_VERSION;
+ ip6hdr->payload_len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
+ ip6hdr->nxt_hdr = tcpip->tcpip_proto;
+ ip6hdr->hop_limit = IP6_HOP_LIMIT; // 255
+
+ /* Determine the next hop address and interface
+ *
+ * TODO: Implement the routing table.
+ */
+ next_hop = dest->sin6_addr;
+ list_for_each_entry ( miniroute, &miniroutes, list ) {
+ if ( ( memcmp ( &ip6hdr->dest, &miniroute->prefix,
+ miniroute->prefix_len ) == 0 ) ||
+ ( IP6_EQUAL ( miniroute->gateway, ip6_none ) ) ) {
+ netdev = miniroute->netdev;
+ ip6hdr->src = miniroute->address;
+ if ( ! ( IS_UNSPECIFIED ( miniroute->gateway ) ) ) {
+ next_hop = miniroute->gateway;
+ }
+ break;
+ }
+ }
+ /* No network interface identified */
+ if ( !netdev ) {
+ DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
+ rc = -ENETUNREACH;
+ goto err;
+ }
+
+ /* Complete the transport layer checksum */
+ if ( trans_csum )
+ *trans_csum = ipv6_tx_csum ( iobuf, *trans_csum );
+
+ /* Print IPv6 header */
+ ipv6_dump ( ip6hdr );
+
+ /* Resolve link layer address */
+ if ( next_hop.in6_u.u6_addr8[0] == 0xff ) {
+ ll_dest_buf[0] = 0x33;
+ ll_dest_buf[1] = 0x33;
+ ll_dest_buf[2] = next_hop.in6_u.u6_addr8[12];
+ ll_dest_buf[3] = next_hop.in6_u.u6_addr8[13];
+ ll_dest_buf[4] = next_hop.in6_u.u6_addr8[14];
+ ll_dest_buf[5] = next_hop.in6_u.u6_addr8[15];
+ } else {
+ /* Unicast address needs to be resolved by NDP */
+ if ( ( rc = ndp_resolve ( netdev, &next_hop, &ip6hdr->src,
+ ll_dest_buf ) ) != 0 ) {
+ DBG ( "No entry for %s\n", inet6_ntoa ( next_hop ) );
+ goto err;
+ }
+ }
+
+ /* Transmit packet */
+ return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest );
+
+ err:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * Process next IP6 header
+ *
+ * @v iobuf I/O buffer
+ * @v nxt_hdr Next header number
+ * @v src Source socket address
+ * @v dest Destination socket address
+ *
+ * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
+ */
+static int ipv6_process_nxt_hdr ( struct io_buffer *iobuf, uint8_t nxt_hdr,
+ struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest ) {
+ switch ( nxt_hdr ) {
+ case IP6_HOPBYHOP:
+ case IP6_ROUTING:
+ case IP6_FRAGMENT:
+ case IP6_AUTHENTICATION:
+ case IP6_DEST_OPTS:
+ case IP6_ESP:
+ DBG ( "Function not implemented for header %d\n", nxt_hdr );
+ return -ENOSYS;
+ case IP6_ICMP6:
+ break;
+ case IP6_NO_HEADER:
+ DBG ( "No next header\n" );
+ return 0;
+ }
+ /* Next header is not a IPv6 extension header */
+ return tcpip_rx ( iobuf, nxt_hdr, src, dest, 0 /* fixme */ );
+}
+
+/**
+ * Process incoming IP6 packets
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v ll_source Link-layer source address
+ *
+ * This function processes a IPv6 packet
+ */
+static int ipv6_rx ( struct io_buffer *iobuf,
+ __unused struct net_device *netdev,
+ __unused const void *ll_source ) {
+
+ struct ip6_header *ip6hdr = iobuf->data;
+ union {
+ struct sockaddr_in6 sin6;
+ struct sockaddr_tcpip st;
+ } src, dest;
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) {
+ DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
+ goto drop;
+ }
+
+ /* TODO: Verify checksum */
+
+ /* Print IP6 header for debugging */
+ ipv6_dump ( ip6hdr );
+
+ /* Check header version */
+ if ( ( ip6hdr->ver_traffic_class_flow_label & 0xf0000000 ) != 0x60000000 ) {
+ DBG ( "Invalid protocol version\n" );
+ goto drop;
+ }
+
+ /* Check the payload length */
+ if ( ntohs ( ip6hdr->payload_len ) > iob_len ( iobuf ) ) {
+ DBG ( "Inconsistent packet length (%d bytes)\n",
+ ip6hdr->payload_len );
+ goto drop;
+ }
+
+ /* Ignore the traffic class and flow control values */
+
+ /* Construct socket address */
+ memset ( &src, 0, sizeof ( src ) );
+ src.sin6.sin_family = AF_INET6;
+ src.sin6.sin6_addr = ip6hdr->src;
+ memset ( &dest, 0, sizeof ( dest ) );
+ dest.sin6.sin_family = AF_INET6;
+ dest.sin6.sin6_addr = ip6hdr->dest;
+
+ /* Strip header */
+ iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) -
+ sizeof ( *ip6hdr ) );
+ iob_pull ( iobuf, sizeof ( *ip6hdr ) );
+
+ /* Send it to the transport layer */
+ return ipv6_process_nxt_hdr ( iobuf, ip6hdr->nxt_hdr, &src.st, &dest.st );
+
+ drop:
+ DBG ( "Packet dropped\n" );
+ free_iob ( iobuf );
+ return -1;
+}
+
+/**
+ * Print a IP6 address as xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
+ */
+char * inet6_ntoa ( struct in6_addr in6 ) {
+ static char buf[40];
+ uint16_t *bytes = ( uint16_t* ) &in6;
+ sprintf ( buf, "%x:%x:%x:%x:%x:%x:%x:%x", bytes[0], bytes[1], bytes[2],
+ bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] );
+ return buf;
+}
+
+static const char * ipv6_ntoa ( const void *net_addr ) {
+ return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
+}
+
+/** IPv6 protocol */
+struct net_protocol ipv6_protocol __net_protocol = {
+ .name = "IPv6",
+ .net_proto = htons ( ETH_P_IPV6 ),
+ .net_addr_len = sizeof ( struct in6_addr ),
+ .rx = ipv6_rx,
+ .ntoa = ipv6_ntoa,
+};
+
+/** IPv6 TCPIP net protocol */
+struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
+ .name = "IPv6",
+ .sa_family = AF_INET6,
+ .tx = ipv6_tx,
+};
diff --git a/gpxe/src/net/ndp.c b/gpxe/src/net/ndp.c
new file mode 100644
index 00000000..8bea8b32
--- /dev/null
+++ b/gpxe/src/net/ndp.c
@@ -0,0 +1,180 @@
+#include <stdint.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/ndp.h>
+#include <gpxe/icmp6.h>
+#include <gpxe/ip6.h>
+#include <gpxe/netdevice.h>
+
+/** @file
+ *
+ * Neighbour Discovery Protocol
+ *
+ * This file implements address resolution as specified by the neighbour
+ * discovery protocol in RFC2461. This protocol is part of the IPv6 protocol
+ * family.
+ */
+
+/* A neighbour entry */
+struct ndp_entry {
+ /** Target IP6 address */
+ struct in6_addr in6;
+ /** Link layer protocol */
+ struct ll_protocol *ll_protocol;
+ /** Link-layer address */
+ uint8_t ll_addr[MAX_LL_ADDR_LEN];
+ /** State of the neighbour entry */
+ int state;
+};
+
+/** Number of entries in the neighbour cache table */
+#define NUM_NDP_ENTRIES 4
+
+/** The neighbour cache table */
+static struct ndp_entry ndp_table[NUM_NDP_ENTRIES];
+#define ndp_table_end &ndp_table[NUM_NDP_ENTRIES]
+
+static unsigned int next_new_ndp_entry = 0;
+
+/**
+ * Find entry in the neighbour cache
+ *
+ * @v in6 IP6 address
+ */
+static struct ndp_entry *
+ndp_find_entry ( struct in6_addr *in6 ) {
+ struct ndp_entry *ndp;
+
+ for ( ndp = ndp_table ; ndp < ndp_table_end ; ndp++ ) {
+ if ( IP6_EQUAL ( ( *in6 ), ndp->in6 ) &&
+ ( ndp->state != NDP_STATE_INVALID ) ) {
+ return ndp;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Add NDP entry
+ *
+ * @v netdev Network device
+ * @v in6 IP6 address
+ * @v ll_addr Link-layer address
+ * @v state State of the entry - one of the NDP_STATE_XXX values
+ */
+static void
+add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
+ void *ll_addr, int state ) {
+ struct ndp_entry *ndp;
+ ndp = &ndp_table[next_new_ndp_entry++ % NUM_NDP_ENTRIES];
+
+ /* Fill up entry */
+ ndp->ll_protocol = netdev->ll_protocol;
+ memcpy ( &ndp->in6, &( *in6 ), sizeof ( *in6 ) );
+ if ( ll_addr ) {
+ memcpy ( ndp->ll_addr, ll_addr, netdev->ll_protocol->ll_addr_len );
+ } else {
+ memset ( ndp->ll_addr, 0, netdev->ll_protocol->ll_addr_len );
+ }
+ ndp->state = state;
+ DBG ( "New neighbour cache entry: IP6 %s => %s %s\n",
+ inet6_ntoa ( ndp->in6 ), netdev->ll_protocol->name,
+ netdev->ll_protocol->ntoa ( ndp->ll_addr ) );
+}
+
+/**
+ * Resolve the link-layer address
+ *
+ * @v netdev Network device
+ * @v dest Destination address
+ * @v src Source address
+ * @ret dest_ll_addr Destination link-layer address or NULL
+ * @ret rc Status
+ *
+ * This function looks up the neighbour cache for an entry corresponding to the
+ * destination address. If it finds a valid entry, it fills up dest_ll_addr and
+ * returns 0. Otherwise it sends a neighbour solicitation to the solicited
+ * multicast address.
+ */
+int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
+ struct in6_addr *src, void *dest_ll_addr ) {
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ struct ndp_entry *ndp;
+ int rc;
+
+ ndp = ndp_find_entry ( dest );
+ /* Check if the entry is valid */
+ if ( ndp && ndp->state == NDP_STATE_REACHABLE ) {
+ DBG ( "Neighbour cache hit: IP6 %s => %s %s\n",
+ inet6_ntoa ( *dest ), ll_protocol->name,
+ ll_protocol->ntoa ( ndp->ll_addr ) );
+ memcpy ( dest_ll_addr, ndp->ll_addr, ll_protocol->ll_addr_len );
+ return 0;
+ }
+
+ /* Check if the entry was already created */
+ if ( ndp ) {
+ DBG ( "Awaiting neighbour advertisement\n" );
+ /* For test */
+// ndp->state = NDP_STATE_REACHABLE;
+// memcpy ( ndp->ll_addr, netdev->ll_addr, 6 );
+// assert ( ndp->ll_protocol->ll_addr_len == 6 );
+// icmp6_test_nadvert ( netdev, dest, ndp->ll_addr );
+// assert ( ndp->state == NDP_STATE_REACHABLE );
+ /* Take it out till here */
+ return -ENOENT;
+ }
+ DBG ( "Neighbour cache miss: IP6 %s\n", inet6_ntoa ( *dest ) );
+
+ /* Add entry in the neighbour cache */
+ add_ndp_entry ( netdev, dest, NULL, NDP_STATE_INCOMPLETE );
+
+ /* Send neighbour solicitation */
+ if ( ( rc = icmp6_send_solicit ( netdev, src, dest ) ) != 0 ) {
+ return rc;
+ }
+ return -ENOENT;
+}
+
+/**
+ * Process neighbour advertisement
+ *
+ * @v iobuf I/O buffer
+ * @v st_src Source address
+ * @v st_dest Destination address
+ */
+int ndp_process_advert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src __unused,
+ struct sockaddr_tcpip *st_dest __unused ) {
+ struct neighbour_advert *nadvert = iobuf->data;
+ struct ndp_entry *ndp;
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *nadvert ) ) {
+ DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
+ return -EINVAL;
+ }
+
+ assert ( nadvert->code == 0 );
+ assert ( nadvert->flags & ICMP6_FLAGS_SOLICITED );
+ assert ( nadvert->opt_type == 2 );
+
+ /* Update the neighbour cache, if entry is present */
+ ndp = ndp_find_entry ( &nadvert->target );
+ if ( ndp ) {
+
+ assert ( nadvert->opt_len ==
+ ( ( 2 + ndp->ll_protocol->ll_addr_len ) / 8 ) );
+
+ if ( IP6_EQUAL ( ndp->in6, nadvert->target ) ) {
+ memcpy ( ndp->ll_addr, nadvert->opt_ll_addr,
+ ndp->ll_protocol->ll_addr_len );
+ ndp->state = NDP_STATE_REACHABLE;
+ return 0;
+ }
+ }
+ DBG ( "Unsolicited advertisement (dropping packet)\n" );
+ return 0;
+}
diff --git a/gpxe/src/net/netdev_settings.c b/gpxe/src/net/netdev_settings.c
new file mode 100644
index 00000000..44aca7d8
--- /dev/null
+++ b/gpxe/src/net/netdev_settings.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/settings.h>
+#include <gpxe/netdevice.h>
+
+/** @file
+ *
+ * Network device configuration settings
+ *
+ */
+
+/** Network device named settings */
+struct setting mac_setting __setting = {
+ .name = "mac",
+ .description = "MAC address",
+ .type = &setting_type_hex,
+};
+
+/**
+ * Store value of network device setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+static int netdev_store ( struct settings *settings, struct setting *setting,
+ const void *data, size_t len ) {
+ struct net_device *netdev = container_of ( settings, struct net_device,
+ settings.settings );
+
+ if ( setting_cmp ( setting, &mac_setting ) == 0 ) {
+ if ( len != netdev->ll_protocol->ll_addr_len )
+ return -EINVAL;
+ memcpy ( netdev->ll_addr, data, len );
+ return 0;
+ } else {
+ return simple_settings_store ( settings, setting, data, len );
+ }
+}
+
+/**
+ * Fetch value of network device setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to fetch
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+static int netdev_fetch ( struct settings *settings, struct setting *setting,
+ void *data, size_t len ) {
+ struct net_device *netdev = container_of ( settings, struct net_device,
+ settings.settings );
+
+ if ( setting_cmp ( setting, &mac_setting ) == 0 ) {
+ if ( len > netdev->ll_protocol->ll_addr_len )
+ len = netdev->ll_protocol->ll_addr_len;
+ memcpy ( data, netdev->ll_addr, len );
+ return netdev->ll_protocol->ll_addr_len;
+ } else {
+ return simple_settings_fetch ( settings, setting, data, len );
+ }
+}
+
+/** Network device configuration settings operations */
+struct settings_operations netdev_settings_operations = {
+ .store = netdev_store,
+ .fetch = netdev_fetch,
+};
diff --git a/gpxe/src/net/netdevice.c b/gpxe/src/net/netdevice.c
new file mode 100644
index 00000000..6875b3ba
--- /dev/null
+++ b/gpxe/src/net/netdevice.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <byteswap.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/tables.h>
+#include <gpxe/process.h>
+#include <gpxe/init.h>
+#include <gpxe/device.h>
+#include <gpxe/netdevice.h>
+
+/** @file
+ *
+ * Network device management
+ *
+ */
+
+/** Registered network-layer protocols */
+static struct net_protocol net_protocols[0]
+ __table_start ( struct net_protocol, net_protocols );
+static struct net_protocol net_protocols_end[0]
+ __table_end ( struct net_protocol, net_protocols );
+
+/** List of network devices */
+struct list_head net_devices = LIST_HEAD_INIT ( net_devices );
+
+/**
+ * Transmit raw packet via network device
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ *
+ * Transmits the packet via the specified network device. This
+ * function takes ownership of the I/O buffer.
+ */
+int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) {
+ int rc;
+
+ DBGC ( netdev, "NETDEV %p transmitting %p (%p+%zx)\n",
+ netdev, iobuf, iobuf->data, iob_len ( iobuf ) );
+
+ list_add_tail ( &iobuf->list, &netdev->tx_queue );
+
+ if ( ! ( netdev->state & NETDEV_OPEN ) ) {
+ rc = -ENETUNREACH;
+ goto err;
+ }
+
+ if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 )
+ goto err;
+
+ return 0;
+
+ err:
+ netdev_tx_complete_err ( netdev, iobuf, rc );
+ return rc;
+}
+
+/**
+ * Complete network transmission
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @v rc Packet status code
+ *
+ * The packet must currently be in the network device's TX queue.
+ */
+void netdev_tx_complete_err ( struct net_device *netdev,
+ struct io_buffer *iobuf, int rc ) {
+
+ /* Update statistics counter */
+ if ( rc == 0 ) {
+ netdev->stats.tx_ok++;
+ DBGC ( netdev, "NETDEV %p transmission %p complete\n",
+ netdev, iobuf );
+ } else {
+ netdev->stats.tx_err++;
+ DBGC ( netdev, "NETDEV %p transmission %p failed: %s\n",
+ netdev, iobuf, strerror ( rc ) );
+ }
+
+ /* Catch data corruption as early as possible */
+ assert ( iobuf->list.next != NULL );
+ assert ( iobuf->list.prev != NULL );
+
+ /* Dequeue and free I/O buffer */
+ list_del ( &iobuf->list );
+ free_iob ( iobuf );
+}
+
+/**
+ * Complete network transmission
+ *
+ * @v netdev Network device
+ * @v rc Packet status code
+ *
+ * Completes the oldest outstanding packet in the TX queue.
+ */
+void netdev_tx_complete_next_err ( struct net_device *netdev, int rc ) {
+ struct io_buffer *iobuf;
+
+ list_for_each_entry ( iobuf, &netdev->tx_queue, list ) {
+ netdev_tx_complete_err ( netdev, iobuf, rc );
+ return;
+ }
+}
+
+/**
+ * Flush device's transmit queue
+ *
+ * @v netdev Network device
+ */
+static void netdev_tx_flush ( struct net_device *netdev ) {
+
+ /* Discard any packets in the TX queue */
+ while ( ! list_empty ( &netdev->tx_queue ) ) {
+ netdev_tx_complete_next_err ( netdev, -ECANCELED );
+ }
+}
+
+/**
+ * Add packet to receive queue
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer, or NULL
+ *
+ * The packet is added to the network device's RX queue. This
+ * function takes ownership of the I/O buffer.
+ */
+void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ) {
+
+ DBGC ( netdev, "NETDEV %p received %p (%p+%zx)\n",
+ netdev, iobuf, iobuf->data, iob_len ( iobuf ) );
+
+ /* Enqueue packet */
+ list_add_tail ( &iobuf->list, &netdev->rx_queue );
+
+ /* Update statistics counter */
+ netdev->stats.rx_ok++;
+}
+
+/**
+ * Discard received packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer, or NULL
+ * @v rc Packet status code
+ *
+ * The packet is discarded and an RX error is recorded. This function
+ * takes ownership of the I/O buffer. @c iobuf may be NULL if, for
+ * example, the net device wishes to report an error due to being
+ * unable to allocate an I/O buffer.
+ */
+void netdev_rx_err ( struct net_device *netdev,
+ struct io_buffer *iobuf, int rc ) {
+
+ DBGC ( netdev, "NETDEV %p failed to receive %p: %s\n",
+ netdev, iobuf, strerror ( rc ) );
+
+ /* Discard packet */
+ free_iob ( iobuf );
+
+ /* Update statistics counter */
+ netdev->stats.rx_err++;
+}
+
+/**
+ * Poll for completed and received packets on network device
+ *
+ * @v netdev Network device
+ *
+ * Polls the network device for completed transmissions and received
+ * packets. Any received packets will be added to the RX packet queue
+ * via netdev_rx().
+ */
+void netdev_poll ( struct net_device *netdev ) {
+
+ if ( netdev->state & NETDEV_OPEN )
+ netdev->op->poll ( netdev );
+}
+
+/**
+ * Remove packet from device's receive queue
+ *
+ * @v netdev Network device
+ * @ret iobuf I/O buffer, or NULL
+ *
+ * Removes the first packet from the device's RX queue and returns it.
+ * Ownership of the packet is transferred to the caller.
+ */
+struct io_buffer * netdev_rx_dequeue ( struct net_device *netdev ) {
+ struct io_buffer *iobuf;
+
+ list_for_each_entry ( iobuf, &netdev->rx_queue, list ) {
+ list_del ( &iobuf->list );
+ return iobuf;
+ }
+ return NULL;
+}
+
+/**
+ * Flush device's receive queue
+ *
+ * @v netdev Network device
+ */
+static void netdev_rx_flush ( struct net_device *netdev ) {
+ struct io_buffer *iobuf;
+
+ /* Discard any packets in the RX queue */
+ while ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) {
+ netdev_rx_err ( netdev, iobuf, -ECANCELED );
+ }
+}
+
+/**
+ * Free network device
+ *
+ * @v refcnt Network device reference counter
+ */
+static void free_netdev ( struct refcnt *refcnt ) {
+ struct net_device *netdev =
+ container_of ( refcnt, struct net_device, refcnt );
+
+ netdev_tx_flush ( netdev );
+ netdev_rx_flush ( netdev );
+ free ( netdev );
+}
+
+/**
+ * Allocate network device
+ *
+ * @v priv_size Size of private data area (net_device::priv)
+ * @ret netdev Network device, or NULL
+ *
+ * Allocates space for a network device and its private data area.
+ */
+struct net_device * alloc_netdev ( size_t priv_size ) {
+ struct net_device *netdev;
+ size_t total_len;
+
+ total_len = ( sizeof ( *netdev ) + priv_size );
+ netdev = zalloc ( total_len );
+ if ( netdev ) {
+ netdev->refcnt.free = free_netdev;
+ INIT_LIST_HEAD ( &netdev->tx_queue );
+ INIT_LIST_HEAD ( &netdev->rx_queue );
+ settings_init ( netdev_settings ( netdev ),
+ &netdev_settings_operations, &netdev->refcnt,
+ netdev->name );
+ netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) );
+ }
+ return netdev;
+}
+
+/**
+ * Register network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ *
+ * Gives the network device a name and adds it to the list of network
+ * devices.
+ */
+int register_netdev ( struct net_device *netdev ) {
+ static unsigned int ifindex = 0;
+ int rc;
+
+ /* Create device name */
+ snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
+ ifindex++ );
+
+ /* Register per-netdev configuration settings */
+ if ( ( rc = register_settings ( netdev_settings ( netdev ),
+ NULL ) ) != 0 ) {
+ DBGC ( netdev, "NETDEV %p could not register settings: %s\n",
+ netdev, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Add to device list */
+ netdev_get ( netdev );
+ list_add_tail ( &netdev->list, &net_devices );
+ DBGC ( netdev, "NETDEV %p registered as %s (phys %s hwaddr %s)\n",
+ netdev, netdev->name, netdev->dev->name,
+ netdev_hwaddr ( netdev ) );
+
+ return 0;
+}
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+int netdev_open ( struct net_device *netdev ) {
+ int rc;
+
+ /* Do nothing if device is already open */
+ if ( netdev->state & NETDEV_OPEN )
+ return 0;
+
+ DBGC ( netdev, "NETDEV %p opening\n", netdev );
+
+ /* Open the device */
+ if ( ( rc = netdev->op->open ( netdev ) ) != 0 )
+ return rc;
+
+ /* Mark as opened */
+ netdev->state |= NETDEV_OPEN;
+ return 0;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+void netdev_close ( struct net_device *netdev ) {
+
+ /* Do nothing if device is already closed */
+ if ( ! ( netdev->state & NETDEV_OPEN ) )
+ return;
+
+ DBGC ( netdev, "NETDEV %p closing\n", netdev );
+
+ /* Close the device */
+ netdev->op->close ( netdev );
+
+ /* Flush TX and RX queues */
+ netdev_tx_flush ( netdev );
+ netdev_rx_flush ( netdev );
+
+ /* Mark as closed */
+ netdev->state &= ~NETDEV_OPEN;
+}
+
+/**
+ * Unregister network device
+ *
+ * @v netdev Network device
+ *
+ * Removes the network device from the list of network devices.
+ */
+void unregister_netdev ( struct net_device *netdev ) {
+
+ /* Ensure device is closed */
+ netdev_close ( netdev );
+
+ /* Unregister per-netdev configuration settings */
+ unregister_settings ( netdev_settings ( netdev ) );
+
+ /* Remove from device list */
+ list_del ( &netdev->list );
+ netdev_put ( netdev );
+ DBGC ( netdev, "NETDEV %p unregistered\n", netdev );
+}
+
+/** Enable or disable interrupts
+ *
+ * @v netdev Network device
+ * @v enable Interrupts should be enabled
+ */
+void netdev_irq ( struct net_device *netdev, int enable ) {
+ netdev->op->irq ( netdev, enable );
+}
+
+/**
+ * Get network device by name
+ *
+ * @v name Network device name
+ * @ret netdev Network device, or NULL
+ */
+struct net_device * find_netdev ( const char *name ) {
+ struct net_device *netdev;
+
+ list_for_each_entry ( netdev, &net_devices, list ) {
+ if ( strcmp ( netdev->name, name ) == 0 )
+ return netdev;
+ }
+
+ return NULL;
+}
+
+/**
+ * Get network device by PCI bus:dev.fn address
+ *
+ * @v bus_type Bus type
+ * @v location Bus location
+ * @ret netdev Network device, or NULL
+ */
+struct net_device * find_netdev_by_location ( unsigned int bus_type,
+ unsigned int location ) {
+ struct net_device *netdev;
+
+ list_for_each_entry ( netdev, &net_devices, list ) {
+ if ( ( netdev->dev->desc.bus_type == bus_type ) &&
+ ( netdev->dev->desc.location == location ) )
+ return netdev;
+ }
+
+ return NULL;
+}
+
+/**
+ * Transmit network-layer packet
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v net_protocol Network-layer protocol
+ * @v ll_dest Destination link-layer address
+ * @ret rc Return status code
+ *
+ * Prepends link-layer headers to the I/O buffer and transmits the
+ * packet via the specified network device. This function takes
+ * ownership of the I/O buffer.
+ */
+int net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
+ struct net_protocol *net_protocol, const void *ll_dest ) {
+
+ /* Force a poll on the netdevice to (potentially) clear any
+ * backed-up TX completions. This is needed on some network
+ * devices to avoid excessive losses due to small TX ring
+ * sizes.
+ */
+ netdev_poll ( netdev );
+
+ return netdev->ll_protocol->tx ( iobuf, netdev, net_protocol, ll_dest );
+}
+
+/**
+ * Process received network-layer packet
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v net_proto Network-layer protocol, in network-byte order
+ * @v ll_source Source link-layer address
+ * @ret rc Return status code
+ */
+int net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
+ uint16_t net_proto, const void *ll_source ) {
+ struct net_protocol *net_protocol;
+
+ /* Hand off to network-layer protocol, if any */
+ for ( net_protocol = net_protocols ; net_protocol < net_protocols_end ;
+ net_protocol++ ) {
+ if ( net_protocol->net_proto == net_proto ) {
+ return net_protocol->rx ( iobuf, netdev, ll_source );
+ }
+ }
+ free_iob ( iobuf );
+ return 0;
+}
+
+/**
+ * Single-step the network stack
+ *
+ * @v process Network stack process
+ *
+ * This polls all interfaces for received packets, and processes
+ * packets from the RX queue.
+ */
+static void net_step ( struct process *process __unused ) {
+ struct net_device *netdev;
+ struct io_buffer *iobuf;
+
+ /* Poll and process each network device */
+ list_for_each_entry ( netdev, &net_devices, list ) {
+
+ /* Poll for new packets */
+ netdev_poll ( netdev );
+
+ /* Process at most one received packet. Give priority
+ * to getting packets out of the NIC over processing
+ * the received packets, because we advertise a window
+ * that assumes that we can receive packets from the
+ * NIC faster than they arrive.
+ */
+ if ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) {
+ DBGC ( netdev, "NETDEV %p processing %p (%p+%zx)\n",
+ netdev, iobuf, iobuf->data,
+ iob_len ( iobuf ) );
+ netdev->ll_protocol->rx ( iobuf, netdev );
+ }
+ }
+}
+
+/** Networking stack process */
+struct process net_process __permanent_process = {
+ .step = net_step,
+};
diff --git a/gpxe/src/net/nullnet.c b/gpxe/src/net/nullnet.c
new file mode 100644
index 00000000..7e199ce3
--- /dev/null
+++ b/gpxe/src/net/nullnet.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+
+/** @file
+ *
+ * Null network device
+ *
+ */
+
+static int null_open ( struct net_device *netdev __unused ) {
+ return -ENODEV;
+};
+
+static void null_close ( struct net_device *netdev __unused ) {
+ /* Do nothing */
+};
+
+static int null_transmit ( struct net_device *netdev __unused,
+ struct io_buffer *iobuf __unused ) {
+ return -ENODEV;
+};
+
+static void null_poll ( struct net_device *netdev __unused ) {
+ /* Do nothing */
+}
+
+static void null_irq ( struct net_device *netdev __unused,
+ int enable __unused ) {
+ /* Do nothing */
+}
+
+struct net_device_operations null_netdev_operations = {
+ .open = null_open,
+ .close = null_close,
+ .transmit = null_transmit,
+ .poll = null_poll,
+ .irq = null_irq,
+};
diff --git a/gpxe/src/net/rarp.c b/gpxe/src/net/rarp.c
new file mode 100644
index 00000000..bb5e6ad7
--- /dev/null
+++ b/gpxe/src/net/rarp.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <byteswap.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/rarp.h>
+
+/** @file
+ *
+ * Reverse Address Resolution Protocol
+ *
+ */
+
+/**
+ * Process incoming ARP packets
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v ll_source Link-layer source address
+ * @ret rc Return status code
+ *
+ * This is a dummy method which simply discards RARP packets.
+ */
+static int rarp_rx ( struct io_buffer *iobuf,
+ struct net_device *netdev __unused,
+ const void *ll_source __unused ) {
+ free_iob ( iobuf );
+ return 0;
+}
+
+
+/**
+ * Transcribe RARP address
+ *
+ * @v net_addr RARP address
+ * @ret string "<RARP>"
+ *
+ * This operation is meaningless for the RARP protocol.
+ */
+static const char * rarp_ntoa ( const void *net_addr __unused ) {
+ return "<RARP>";
+}
+
+/** RARP protocol */
+struct net_protocol rarp_protocol __net_protocol = {
+ .name = "RARP",
+ .net_proto = htons ( ETH_P_RARP ),
+ .rx = rarp_rx,
+ .ntoa = rarp_ntoa,
+};
diff --git a/gpxe/src/net/retry.c b/gpxe/src/net/retry.c
new file mode 100644
index 00000000..90b89711
--- /dev/null
+++ b/gpxe/src/net/retry.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <gpxe/timer.h>
+#include <gpxe/list.h>
+#include <gpxe/process.h>
+#include <gpxe/init.h>
+#include <gpxe/retry.h>
+
+/** @file
+ *
+ * Retry timers
+ *
+ * A retry timer is a binary exponential backoff timer. It can be
+ * used to build automatic retransmission into network protocols.
+ *
+ * This implementation of the timer is designed to satisfy RFC 2988
+ * and therefore be usable as a TCP retransmission timer.
+ *
+ *
+ */
+
+/** Default timeout value */
+#define MIN_TIMEOUT ( TICKS_PER_SEC / 4 )
+
+/** Limit after which the timeout will be deemed permanent */
+#define MAX_TIMEOUT ( 10 * TICKS_PER_SEC )
+
+/* The theoretical minimum that the algorithm in stop_timer() can
+ * adjust the timeout back down to is seven ticks, so set the minimum
+ * timeout to at least that value for the sake of consistency.
+ */
+#if MIN_TIMEOUT < 7
+#undef MIN_TIMEOUT
+#define MIN_TIMEOUT 7
+#endif
+
+/** List of running timers */
+static LIST_HEAD ( timers );
+
+/**
+ * Start timer
+ *
+ * @v timer Retry timer
+ *
+ * This starts the timer running with the current timeout value. If
+ * stop_timer() is not called before the timer expires, the timer will
+ * be stopped and the timer's callback function will be called.
+ */
+void start_timer ( struct retry_timer *timer ) {
+ if ( ! timer_running ( timer ) )
+ list_add ( &timer->list, &timers );
+ timer->start = currticks();
+ if ( timer->timeout < MIN_TIMEOUT )
+ timer->timeout = MIN_TIMEOUT;
+ DBG2 ( "Timer %p started at time %ld (expires at %ld)\n",
+ timer, timer->start, ( timer->start + timer->timeout ) );
+}
+
+/**
+ * Start timer with no delay
+ *
+ * @v timer Retry timer
+ *
+ * This starts the timer running with a zero timeout value.
+ */
+void start_timer_nodelay ( struct retry_timer *timer ) {
+ start_timer ( timer );
+ timer->timeout = 0;
+}
+
+/**
+ * Stop timer
+ *
+ * @v timer Retry timer
+ *
+ * This stops the timer and updates the timer's timeout value.
+ */
+void stop_timer ( struct retry_timer *timer ) {
+ unsigned long old_timeout = timer->timeout;
+ unsigned long now = currticks();
+ unsigned long runtime;
+
+ /* If timer was already stopped, do nothing */
+ if ( ! timer_running ( timer ) )
+ return;
+
+ list_del ( &timer->list );
+ runtime = ( now - timer->start );
+ timer->start = 0;
+ DBG2 ( "Timer %p stopped at time %ld (ran for %ld)\n",
+ timer, now, runtime );
+
+ /* Update timer. Variables are:
+ *
+ * r = round-trip time estimate (i.e. runtime)
+ * t = timeout value (i.e. timer->timeout)
+ * s = smoothed round-trip time
+ *
+ * By choice, we set t = 4s, i.e. allow for four times the
+ * normal round-trip time to pass before retransmitting.
+ *
+ * We want to smooth according to s := ( 7 s + r ) / 8
+ *
+ * Since we don't actually store s, this reduces to
+ * t := ( 7 t / 8 ) + ( r / 2 )
+ *
+ */
+ if ( timer->count ) {
+ timer->count--;
+ } else {
+ timer->timeout -= ( timer->timeout >> 3 );
+ timer->timeout += ( runtime >> 1 );
+ if ( timer->timeout != old_timeout ) {
+ DBG ( "Timer %p timeout updated to %ld\n",
+ timer, timer->timeout );
+ }
+ }
+}
+
+/**
+ * Handle expired timer
+ *
+ * @v timer Retry timer
+ */
+static void timer_expired ( struct retry_timer *timer ) {
+ int fail;
+
+ /* Stop timer without performing RTT calculations */
+ DBG2 ( "Timer %p stopped at time %ld on expiry\n",
+ timer, currticks() );
+ list_del ( &timer->list );
+ timer->start = 0;
+ timer->count++;
+
+ /* Back off the timeout value */
+ timer->timeout <<= 1;
+ if ( ( fail = ( timer->timeout > MAX_TIMEOUT ) ) )
+ timer->timeout = MAX_TIMEOUT;
+ DBG ( "Timer %p timeout backed off to %ld\n",
+ timer, timer->timeout );
+
+ /* Call expiry callback */
+ timer->expired ( timer, fail );
+}
+
+/**
+ * Single-step the retry timer list
+ *
+ * @v process Retry timer process
+ */
+static void retry_step ( struct process *process __unused ) {
+ struct retry_timer *timer;
+ struct retry_timer *tmp;
+ unsigned long now = currticks();
+ unsigned long used;
+
+ list_for_each_entry_safe ( timer, tmp, &timers, list ) {
+ used = ( now - timer->start );
+ if ( used >= timer->timeout )
+ timer_expired ( timer );
+ }
+}
+
+/** Retry timer process */
+struct process retry_process __permanent_process = {
+ .step = retry_step,
+};
diff --git a/gpxe/src/net/tcp.c b/gpxe/src/net/tcp.c
new file mode 100644
index 00000000..da8e87b4
--- /dev/null
+++ b/gpxe/src/net/tcp.c
@@ -0,0 +1,1073 @@
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/timer.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/malloc.h>
+#include <gpxe/retry.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/uri.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/tcp.h>
+
+/** @file
+ *
+ * TCP protocol
+ *
+ */
+
+/** A TCP connection */
+struct tcp_connection {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** List of TCP connections */
+ struct list_head list;
+
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+ /** Data transfer interface closed flag */
+ int xfer_closed;
+
+ /** Remote socket address */
+ struct sockaddr_tcpip peer;
+ /** Local port, in network byte order */
+ unsigned int local_port;
+
+ /** Current TCP state */
+ unsigned int tcp_state;
+ /** Previous TCP state
+ *
+ * Maintained only for debug messages
+ */
+ unsigned int prev_tcp_state;
+ /** Current sequence number
+ *
+ * Equivalent to SND.UNA in RFC 793 terminology.
+ */
+ uint32_t snd_seq;
+ /** Unacknowledged sequence count
+ *
+ * Equivalent to (SND.NXT-SND.UNA) in RFC 793 terminology.
+ */
+ uint32_t snd_sent;
+ /** Send window
+ *
+ * Equivalent to SND.WND in RFC 793 terminology
+ */
+ uint32_t snd_win;
+ /** Current acknowledgement number
+ *
+ * Equivalent to RCV.NXT in RFC 793 terminology.
+ */
+ uint32_t rcv_ack;
+ /** Most recent received timestamp
+ *
+ * Equivalent to TS.Recent in RFC 1323 terminology.
+ */
+ uint32_t ts_recent;
+ /** Timestamps enabled */
+ int timestamps;
+
+ /** Transmit queue */
+ struct list_head queue;
+ /** Retransmission timer */
+ struct retry_timer timer;
+};
+
+/**
+ * List of registered TCP connections
+ */
+static LIST_HEAD ( tcp_conns );
+
+/* Forward declarations */
+static struct xfer_interface_operations tcp_xfer_operations;
+static void tcp_expired ( struct retry_timer *timer, int over );
+static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
+ uint32_t win );
+
+/**
+ * Name TCP state
+ *
+ * @v state TCP state
+ * @ret name Name of TCP state
+ */
+static inline __attribute__ (( always_inline )) const char *
+tcp_state ( int state ) {
+ switch ( state ) {
+ case TCP_CLOSED: return "CLOSED";
+ case TCP_LISTEN: return "LISTEN";
+ case TCP_SYN_SENT: return "SYN_SENT";
+ case TCP_SYN_RCVD: return "SYN_RCVD";
+ case TCP_ESTABLISHED: return "ESTABLISHED";
+ case TCP_FIN_WAIT_1: return "FIN_WAIT_1";
+ case TCP_FIN_WAIT_2: return "FIN_WAIT_2";
+ case TCP_CLOSING_OR_LAST_ACK: return "CLOSING/LAST_ACK";
+ case TCP_TIME_WAIT: return "TIME_WAIT";
+ case TCP_CLOSE_WAIT: return "CLOSE_WAIT";
+ default: return "INVALID";
+ }
+}
+
+/**
+ * Dump TCP state transition
+ *
+ * @v tcp TCP connection
+ */
+static inline __attribute__ (( always_inline )) void
+tcp_dump_state ( struct tcp_connection *tcp ) {
+
+ if ( tcp->tcp_state != tcp->prev_tcp_state ) {
+ DBGC ( tcp, "TCP %p transitioned from %s to %s\n", tcp,
+ tcp_state ( tcp->prev_tcp_state ),
+ tcp_state ( tcp->tcp_state ) );
+ }
+ tcp->prev_tcp_state = tcp->tcp_state;
+}
+
+/**
+ * Dump TCP flags
+ *
+ * @v flags TCP flags
+ */
+static inline __attribute__ (( always_inline )) void
+tcp_dump_flags ( struct tcp_connection *tcp, unsigned int flags ) {
+ if ( flags & TCP_RST )
+ DBGC ( tcp, " RST" );
+ if ( flags & TCP_SYN )
+ DBGC ( tcp, " SYN" );
+ if ( flags & TCP_PSH )
+ DBGC ( tcp, " PSH" );
+ if ( flags & TCP_FIN )
+ DBGC ( tcp, " FIN" );
+ if ( flags & TCP_ACK )
+ DBGC ( tcp, " ACK" );
+}
+
+/***************************************************************************
+ *
+ * Open and close
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Bind TCP connection to local port
+ *
+ * @v tcp TCP connection
+ * @v port Local port number, in network-endian order
+ * @ret rc Return status code
+ *
+ * If the port is 0, the connection is assigned an available port
+ * between 1024 and 65535.
+ */
+static int tcp_bind ( struct tcp_connection *tcp, unsigned int port ) {
+ struct tcp_connection *existing;
+ static uint16_t try_port = 1023;
+
+ /* If no port specified, find the first available port */
+ if ( ! port ) {
+ while ( try_port ) {
+ try_port++;
+ if ( try_port < 1024 )
+ continue;
+ if ( tcp_bind ( tcp, htons ( try_port ) ) == 0 )
+ return 0;
+ }
+ DBGC ( tcp, "TCP %p could not bind: no free ports\n", tcp );
+ return -EADDRINUSE;
+ }
+
+ /* Attempt bind to local port */
+ list_for_each_entry ( existing, &tcp_conns, list ) {
+ if ( existing->local_port == port ) {
+ DBGC ( tcp, "TCP %p could not bind: port %d in use\n",
+ tcp, ntohs ( port ) );
+ return -EADDRINUSE;
+ }
+ }
+ tcp->local_port = port;
+
+ DBGC ( tcp, "TCP %p bound to port %d\n", tcp, ntohs ( port ) );
+ return 0;
+}
+
+/**
+ * Open a TCP connection
+ *
+ * @v xfer Data transfer interface
+ * @v peer Peer socket address
+ * @v local Local socket address, or NULL
+ * @ret rc Return status code
+ */
+static int tcp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
+ struct sockaddr *local ) {
+ struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
+ struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
+ struct tcp_connection *tcp;
+ unsigned int bind_port;
+ int rc;
+
+ /* Allocate and initialise structure */
+ tcp = zalloc ( sizeof ( *tcp ) );
+ if ( ! tcp )
+ return -ENOMEM;
+ DBGC ( tcp, "TCP %p allocated\n", tcp );
+ xfer_init ( &tcp->xfer, &tcp_xfer_operations, &tcp->refcnt );
+ tcp->prev_tcp_state = TCP_CLOSED;
+ tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN );
+ tcp_dump_state ( tcp );
+ tcp->snd_seq = random();
+ INIT_LIST_HEAD ( &tcp->queue );
+ tcp->timer.expired = tcp_expired;
+ memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) );
+
+ /* Bind to local port */
+ bind_port = ( st_local ? st_local->st_port : 0 );
+ if ( ( rc = tcp_bind ( tcp, bind_port ) ) != 0 )
+ goto err;
+
+ /* Start timer to initiate SYN */
+ start_timer_nodelay ( &tcp->timer );
+
+ /* Attach parent interface, transfer reference to connection
+ * list and return
+ */
+ xfer_plug_plug ( &tcp->xfer, xfer );
+ list_add ( &tcp->list, &tcp_conns );
+ return 0;
+
+ err:
+ ref_put ( &tcp->refcnt );
+ return rc;
+}
+
+/**
+ * Close TCP connection
+ *
+ * @v tcp TCP connection
+ * @v rc Reason for close
+ *
+ * Closes the data transfer interface. If the TCP state machine is in
+ * a suitable state, the connection will be deleted.
+ */
+static void tcp_close ( struct tcp_connection *tcp, int rc ) {
+ struct io_buffer *iobuf;
+ struct io_buffer *tmp;
+
+ /* Close data transfer interface */
+ xfer_nullify ( &tcp->xfer );
+ xfer_close ( &tcp->xfer, rc );
+ tcp->xfer_closed = 1;
+
+ /* If we are in CLOSED, or have otherwise not yet received a
+ * SYN (i.e. we are in LISTEN or SYN_SENT), just delete the
+ * connection.
+ */
+ if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) {
+
+ /* Transition to CLOSED for the sake of debugging messages */
+ tcp->tcp_state = TCP_CLOSED;
+ tcp_dump_state ( tcp );
+
+ /* Free any unsent I/O buffers */
+ list_for_each_entry_safe ( iobuf, tmp, &tcp->queue, list ) {
+ list_del ( &iobuf->list );
+ free_iob ( iobuf );
+ }
+
+ /* Remove from list and drop reference */
+ stop_timer ( &tcp->timer );
+ list_del ( &tcp->list );
+ ref_put ( &tcp->refcnt );
+ DBGC ( tcp, "TCP %p connection deleted\n", tcp );
+ return;
+ }
+
+ /* If we have not had our SYN acknowledged (i.e. we are in
+ * SYN_RCVD), pretend that it has been acknowledged so that we
+ * can send a FIN without breaking things.
+ */
+ if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
+ tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 );
+
+ /* If we have no data remaining to send, start sending FIN */
+ if ( list_empty ( &tcp->queue ) ) {
+ tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
+ tcp_dump_state ( tcp );
+ }
+}
+
+/***************************************************************************
+ *
+ * Transmit data path
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Calculate transmission window
+ *
+ * @v tcp TCP connection
+ * @ret len Maximum length that can be sent in a single packet
+ */
+static size_t tcp_xmit_win ( struct tcp_connection *tcp ) {
+ size_t len;
+
+ /* Not ready if we're not in a suitable connection state */
+ if ( ! TCP_CAN_SEND_DATA ( tcp->tcp_state ) )
+ return 0;
+
+ /* Length is the minimum of the receiver's window and the path MTU */
+ len = tcp->snd_win;
+ if ( len > TCP_PATH_MTU )
+ len = TCP_PATH_MTU;
+
+ return len;
+}
+
+/**
+ * Process TCP transmit queue
+ *
+ * @v tcp TCP connection
+ * @v max_len Maximum length to process
+ * @v dest I/O buffer to fill with data, or NULL
+ * @v remove Remove data from queue
+ * @ret len Length of data processed
+ *
+ * This processes at most @c max_len bytes from the TCP connection's
+ * transmit queue. Data will be copied into the @c dest I/O buffer
+ * (if provided) and, if @c remove is true, removed from the transmit
+ * queue.
+ */
+static size_t tcp_process_queue ( struct tcp_connection *tcp, size_t max_len,
+ struct io_buffer *dest, int remove ) {
+ struct io_buffer *iobuf;
+ struct io_buffer *tmp;
+ size_t frag_len;
+ size_t len = 0;
+
+ list_for_each_entry_safe ( iobuf, tmp, &tcp->queue, list ) {
+ frag_len = iob_len ( iobuf );
+ if ( frag_len > max_len )
+ frag_len = max_len;
+ if ( dest ) {
+ memcpy ( iob_put ( dest, frag_len ), iobuf->data,
+ frag_len );
+ }
+ if ( remove ) {
+ iob_pull ( iobuf, frag_len );
+ if ( ! iob_len ( iobuf ) ) {
+ list_del ( &iobuf->list );
+ free_iob ( iobuf );
+ }
+ }
+ len += frag_len;
+ max_len -= frag_len;
+ }
+ return len;
+}
+
+/**
+ * Transmit any outstanding data
+ *
+ * @v tcp TCP connection
+ * @v force_send Force sending of packet
+ *
+ * Transmits any outstanding data on the connection.
+ *
+ * Note that even if an error is returned, the retransmission timer
+ * will have been started if necessary, and so the stack will
+ * eventually attempt to retransmit the failed packet.
+ */
+static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
+ struct io_buffer *iobuf;
+ struct tcp_header *tcphdr;
+ struct tcp_mss_option *mssopt;
+ struct tcp_timestamp_padded_option *tsopt;
+ void *payload;
+ unsigned int flags;
+ size_t len = 0;
+ size_t seq_len;
+ size_t app_win;
+ size_t rcv_win;
+
+ /* If retransmission timer is already running, do nothing */
+ if ( timer_running ( &tcp->timer ) )
+ return 0;
+
+ /* Calculate both the actual (payload) and sequence space
+ * lengths that we wish to transmit.
+ */
+ if ( TCP_CAN_SEND_DATA ( tcp->tcp_state ) ) {
+ len = tcp_process_queue ( tcp, tcp_xmit_win ( tcp ),
+ NULL, 0 );
+ }
+ seq_len = len;
+ flags = TCP_FLAGS_SENDING ( tcp->tcp_state );
+ if ( flags & ( TCP_SYN | TCP_FIN ) ) {
+ /* SYN or FIN consume one byte, and we can never send both */
+ assert ( ! ( ( flags & TCP_SYN ) && ( flags & TCP_FIN ) ) );
+ seq_len++;
+ }
+ tcp->snd_sent = seq_len;
+
+ /* If we have nothing to transmit, stop now */
+ if ( ( seq_len == 0 ) && ! force_send )
+ return 0;
+
+ /* If we are transmitting anything that requires
+ * acknowledgement (i.e. consumes sequence space), start the
+ * retransmission timer. Do this before attempting to
+ * allocate the I/O buffer, in case allocation itself fails.
+ */
+ if ( seq_len )
+ start_timer ( &tcp->timer );
+
+ /* Allocate I/O buffer */
+ iobuf = alloc_iob ( len + MAX_HDR_LEN );
+ if ( ! iobuf ) {
+ DBGC ( tcp, "TCP %p could not allocate data buffer\n", tcp );
+ return -ENOMEM;
+ }
+ iob_reserve ( iobuf, MAX_HDR_LEN );
+
+ /* Fill data payload from transmit queue */
+ tcp_process_queue ( tcp, len, iobuf, 0 );
+
+ /* Estimate window size */
+ rcv_win = ( ( freemem * 3 ) / 4 );
+ if ( rcv_win > TCP_MAX_WINDOW_SIZE )
+ rcv_win = TCP_MAX_WINDOW_SIZE;
+ app_win = xfer_window ( &tcp->xfer );
+ if ( rcv_win > app_win )
+ rcv_win = app_win;
+ rcv_win &= ~0x03; /* Keep everything dword-aligned */
+
+ /* Fill up the TCP header */
+ payload = iobuf->data;
+ if ( flags & TCP_SYN ) {
+ mssopt = iob_push ( iobuf, sizeof ( *mssopt ) );
+ mssopt->kind = TCP_OPTION_MSS;
+ mssopt->length = sizeof ( *mssopt );
+ mssopt->mss = htons ( TCP_MSS );
+ }
+ if ( ( flags & TCP_SYN ) || tcp->timestamps ) {
+ tsopt = iob_push ( iobuf, sizeof ( *tsopt ) );
+ memset ( tsopt->nop, TCP_OPTION_NOP, sizeof ( tsopt->nop ) );
+ tsopt->tsopt.kind = TCP_OPTION_TS;
+ tsopt->tsopt.length = sizeof ( tsopt->tsopt );
+ tsopt->tsopt.tsval = ntohl ( currticks() );
+ tsopt->tsopt.tsecr = ntohl ( tcp->ts_recent );
+ }
+ tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) );
+ memset ( tcphdr, 0, sizeof ( *tcphdr ) );
+ tcphdr->src = tcp->local_port;
+ tcphdr->dest = tcp->peer.st_port;
+ tcphdr->seq = htonl ( tcp->snd_seq );
+ tcphdr->ack = htonl ( tcp->rcv_ack );
+ tcphdr->hlen = ( ( payload - iobuf->data ) << 2 );
+ tcphdr->flags = flags;
+ tcphdr->win = htons ( rcv_win );
+ tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) );
+
+ /* Dump header */
+ DBGC ( tcp, "TCP %p TX %d->%d %08lx..%08lx %08lx %4zd",
+ tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
+ ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) + seq_len ),
+ ntohl ( tcphdr->ack ), len );
+ tcp_dump_flags ( tcp, tcphdr->flags );
+ DBGC ( tcp, "\n" );
+
+ /* Transmit packet */
+ return tcpip_tx ( iobuf, &tcp_protocol, &tcp->peer, NULL,
+ &tcphdr->csum );
+}
+
+/**
+ * Retransmission timer expired
+ *
+ * @v timer Retry timer
+ * @v over Failure indicator
+ */
+static void tcp_expired ( struct retry_timer *timer, int over ) {
+ struct tcp_connection *tcp =
+ container_of ( timer, struct tcp_connection, timer );
+ int graceful_close = TCP_CLOSED_GRACEFULLY ( tcp->tcp_state );
+
+ DBGC ( tcp, "TCP %p timer %s in %s\n", tcp,
+ ( over ? "expired" : "fired" ), tcp_state ( tcp->tcp_state ) );
+
+ assert ( ( tcp->tcp_state == TCP_SYN_SENT ) ||
+ ( tcp->tcp_state == TCP_SYN_RCVD ) ||
+ ( tcp->tcp_state == TCP_ESTABLISHED ) ||
+ ( tcp->tcp_state == TCP_FIN_WAIT_1 ) ||
+ ( tcp->tcp_state == TCP_TIME_WAIT ) ||
+ ( tcp->tcp_state == TCP_CLOSE_WAIT ) ||
+ ( tcp->tcp_state == TCP_CLOSING_OR_LAST_ACK ) );
+
+ if ( over || graceful_close ) {
+ /* If we have finally timed out and given up, or if
+ * this is the result of a graceful close, terminate
+ * the connection
+ */
+ tcp->tcp_state = TCP_CLOSED;
+ tcp_dump_state ( tcp );
+ tcp_close ( tcp, -ETIMEDOUT );
+ } else {
+ /* Otherwise, retransmit the packet */
+ tcp_xmit ( tcp, 0 );
+ }
+}
+
+/**
+ * Send RST response to incoming packet
+ *
+ * @v in_tcphdr TCP header of incoming packet
+ * @ret rc Return status code
+ */
+static int tcp_xmit_reset ( struct tcp_connection *tcp,
+ struct sockaddr_tcpip *st_dest,
+ struct tcp_header *in_tcphdr ) {
+ struct io_buffer *iobuf;
+ struct tcp_header *tcphdr;
+
+ /* Allocate space for dataless TX buffer */
+ iobuf = alloc_iob ( MAX_HDR_LEN );
+ if ( ! iobuf ) {
+ DBGC ( tcp, "TCP %p could not allocate data buffer\n", tcp );
+ return -ENOMEM;
+ }
+ iob_reserve ( iobuf, MAX_HDR_LEN );
+
+ /* Construct RST response */
+ tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) );
+ memset ( tcphdr, 0, sizeof ( *tcphdr ) );
+ tcphdr->src = in_tcphdr->dest;
+ tcphdr->dest = in_tcphdr->src;
+ tcphdr->seq = in_tcphdr->ack;
+ tcphdr->ack = in_tcphdr->seq;
+ tcphdr->hlen = ( ( sizeof ( *tcphdr ) / 4 ) << 4 );
+ tcphdr->flags = ( TCP_RST | TCP_ACK );
+ tcphdr->win = htons ( TCP_MAX_WINDOW_SIZE );
+ tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) );
+
+ /* Dump header */
+ DBGC ( tcp, "TCP %p TX %d->%d %08lx..%08lx %08lx %4d",
+ tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ),
+ ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) ),
+ ntohl ( tcphdr->ack ), 0 );
+ tcp_dump_flags ( tcp, tcphdr->flags );
+ DBGC ( tcp, "\n" );
+
+ /* Transmit packet */
+ return tcpip_tx ( iobuf, &tcp_protocol, st_dest,
+ NULL, &tcphdr->csum );
+}
+
+/***************************************************************************
+ *
+ * Receive data path
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Identify TCP connection by local port number
+ *
+ * @v local_port Local port (in network-endian order)
+ * @ret tcp TCP connection, or NULL
+ */
+static struct tcp_connection * tcp_demux ( unsigned int local_port ) {
+ struct tcp_connection *tcp;
+
+ list_for_each_entry ( tcp, &tcp_conns, list ) {
+ if ( tcp->local_port == local_port )
+ return tcp;
+ }
+ return NULL;
+}
+
+/**
+ * Parse TCP received options
+ *
+ * @v tcp TCP connection
+ * @v data Raw options data
+ * @v len Raw options length
+ * @v options Options structure to fill in
+ */
+static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data,
+ size_t len, struct tcp_options *options ) {
+ const void *end = ( data + len );
+ const struct tcp_option *option;
+ unsigned int kind;
+
+ memset ( options, 0, sizeof ( *options ) );
+ while ( data < end ) {
+ option = data;
+ kind = option->kind;
+ if ( kind == TCP_OPTION_END )
+ return;
+ if ( kind == TCP_OPTION_NOP ) {
+ data++;
+ continue;
+ }
+ switch ( kind ) {
+ case TCP_OPTION_MSS:
+ options->mssopt = data;
+ break;
+ case TCP_OPTION_TS:
+ options->tsopt = data;
+ break;
+ default:
+ DBGC ( tcp, "TCP %p received unknown option %d\n",
+ tcp, kind );
+ break;
+ }
+ data += option->length;
+ }
+}
+
+/**
+ * Handle TCP received SYN
+ *
+ * @v tcp TCP connection
+ * @v seq SEQ value (in host-endian order)
+ * @v options TCP options
+ * @ret rc Return status code
+ */
+static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq,
+ struct tcp_options *options ) {
+
+ /* Synchronise sequence numbers on first SYN */
+ if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) {
+ tcp->rcv_ack = seq;
+ if ( options->tsopt )
+ tcp->timestamps = 1;
+ }
+
+ /* Ignore duplicate SYN */
+ if ( ( tcp->rcv_ack - seq ) > 0 )
+ return 0;
+
+ /* Mark SYN as received and start sending ACKs with each packet */
+ tcp->tcp_state |= ( TCP_STATE_SENT ( TCP_ACK ) |
+ TCP_STATE_RCVD ( TCP_SYN ) );
+
+ /* Acknowledge SYN */
+ tcp->rcv_ack++;
+
+ return 0;
+}
+
+/**
+ * Handle TCP received ACK
+ *
+ * @v tcp TCP connection
+ * @v ack ACK value (in host-endian order)
+ * @v win WIN value (in host-endian order)
+ * @ret rc Return status code
+ */
+static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack,
+ uint32_t win ) {
+ size_t ack_len = ( ack - tcp->snd_seq );
+ size_t len;
+ unsigned int acked_flags;
+
+ /* Ignore duplicate or out-of-range ACK */
+ if ( ack_len > tcp->snd_sent ) {
+ DBGC ( tcp, "TCP %p received ACK for [%08lx,%08lx), "
+ "sent only [%08lx,%08lx)\n", tcp, tcp->snd_seq,
+ ( tcp->snd_seq + ack_len ), tcp->snd_seq,
+ ( tcp->snd_seq + tcp->snd_sent ) );
+ return -EINVAL;
+ }
+
+ /* Acknowledge any flags being sent */
+ len = ack_len;
+ acked_flags = ( TCP_FLAGS_SENDING ( tcp->tcp_state ) &
+ ( TCP_SYN | TCP_FIN ) );
+ if ( acked_flags )
+ len--;
+
+ /* Update SEQ and sent counters, and window size */
+ tcp->snd_seq = ack;
+ tcp->snd_sent = 0;
+ tcp->snd_win = win;
+
+ /* Stop the retransmission timer */
+ stop_timer ( &tcp->timer );
+
+ /* Remove any acknowledged data from transmit queue */
+ tcp_process_queue ( tcp, len, NULL, 1 );
+
+ /* Mark SYN/FIN as acknowledged if applicable. */
+ if ( acked_flags )
+ tcp->tcp_state |= TCP_STATE_ACKED ( acked_flags );
+
+ /* Start sending FIN if we've had all possible data ACKed */
+ if ( list_empty ( &tcp->queue ) && tcp->xfer_closed )
+ tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
+
+ return 0;
+}
+
+/**
+ * Handle TCP received data
+ *
+ * @v tcp TCP connection
+ * @v seq SEQ value (in host-endian order)
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ *
+ * This function takes ownership of the I/O buffer.
+ */
+static int tcp_rx_data ( struct tcp_connection *tcp, uint32_t seq,
+ struct io_buffer *iobuf ) {
+ size_t already_rcvd;
+ size_t len;
+ int rc;
+
+ /* Ignore duplicate or out-of-order data */
+ already_rcvd = ( tcp->rcv_ack - seq );
+ len = iob_len ( iobuf );
+ if ( already_rcvd >= len ) {
+ free_iob ( iobuf );
+ return 0;
+ }
+ iob_pull ( iobuf, already_rcvd );
+ len -= already_rcvd;
+
+ /* Deliver data to application */
+ if ( ( rc = xfer_deliver_iob ( &tcp->xfer, iobuf ) ) != 0 )
+ return rc;
+
+ /* Acknowledge new data */
+ tcp->rcv_ack += len;
+ return 0;
+}
+
+/**
+ * Handle TCP received FIN
+ *
+ * @v tcp TCP connection
+ * @v seq SEQ value (in host-endian order)
+ * @ret rc Return status code
+ */
+static int tcp_rx_fin ( struct tcp_connection *tcp, uint32_t seq ) {
+
+ /* Ignore duplicate or out-of-order FIN */
+ if ( ( tcp->rcv_ack - seq ) > 0 )
+ return 0;
+
+ /* Mark FIN as received and acknowledge it */
+ tcp->tcp_state |= TCP_STATE_RCVD ( TCP_FIN );
+ tcp->rcv_ack++;
+
+ /* Close connection */
+ tcp_close ( tcp, 0 );
+
+ return 0;
+}
+
+/**
+ * Handle TCP received RST
+ *
+ * @v tcp TCP connection
+ * @v seq SEQ value (in host-endian order)
+ * @ret rc Return status code
+ */
+static int tcp_rx_rst ( struct tcp_connection *tcp, uint32_t seq ) {
+
+ /* Accept RST only if it falls within the window. If we have
+ * not yet received a SYN, then we have no window to test
+ * against, so fall back to checking that our SYN has been
+ * ACKed.
+ */
+ if ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) {
+ if ( ( tcp->rcv_ack - seq ) > 0 )
+ return 0;
+ } else {
+ if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) )
+ return 0;
+ }
+
+ /* Abort connection */
+ tcp->tcp_state = TCP_CLOSED;
+ tcp_dump_state ( tcp );
+ tcp_close ( tcp, -ECONNRESET );
+
+ return -ECONNRESET;
+}
+
+/**
+ * Process received packet
+ *
+ * @v iobuf I/O buffer
+ * @v st_src Partially-filled source address
+ * @v st_dest Partially-filled destination address
+ * @v pshdr_csum Pseudo-header checksum
+ * @ret rc Return status code
+ */
+static int tcp_rx ( struct io_buffer *iobuf,
+ struct sockaddr_tcpip *st_src,
+ struct sockaddr_tcpip *st_dest __unused,
+ uint16_t pshdr_csum ) {
+ struct tcp_header *tcphdr = iobuf->data;
+ struct tcp_connection *tcp;
+ struct tcp_options options;
+ size_t hlen;
+ uint16_t csum;
+ uint32_t start_seq;
+ uint32_t seq;
+ uint32_t ack;
+ uint32_t win;
+ unsigned int flags;
+ size_t len;
+ int rc;
+
+ /* Sanity check packet */
+ if ( iob_len ( iobuf ) < sizeof ( *tcphdr ) ) {
+ DBG ( "TCP packet too short at %zd bytes (min %zd bytes)\n",
+ iob_len ( iobuf ), sizeof ( *tcphdr ) );
+ rc = -EINVAL;
+ goto discard;
+ }
+ hlen = ( ( tcphdr->hlen & TCP_MASK_HLEN ) / 16 ) * 4;
+ if ( hlen < sizeof ( *tcphdr ) ) {
+ DBG ( "TCP header too short at %zd bytes (min %zd bytes)\n",
+ hlen, sizeof ( *tcphdr ) );
+ rc = -EINVAL;
+ goto discard;
+ }
+ if ( hlen > iob_len ( iobuf ) ) {
+ DBG ( "TCP header too long at %zd bytes (max %zd bytes)\n",
+ hlen, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto discard;
+ }
+ csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, iob_len ( iobuf ));
+ if ( csum != 0 ) {
+ DBG ( "TCP checksum incorrect (is %04x including checksum "
+ "field, should be 0000)\n", csum );
+ rc = -EINVAL;
+ goto discard;
+ }
+
+ /* Parse parameters from header and strip header */
+ tcp = tcp_demux ( tcphdr->dest );
+ start_seq = seq = ntohl ( tcphdr->seq );
+ ack = ntohl ( tcphdr->ack );
+ win = ntohs ( tcphdr->win );
+ flags = tcphdr->flags;
+ tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ),
+ ( hlen - sizeof ( *tcphdr ) ), &options );
+ iob_pull ( iobuf, hlen );
+ len = iob_len ( iobuf );
+
+ /* Dump header */
+ DBGC ( tcp, "TCP %p RX %d<-%d %08lx %08lx..%08lx %4zd",
+ tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ),
+ ntohl ( tcphdr->ack ), ntohl ( tcphdr->seq ),
+ ( ntohl ( tcphdr->seq ) + len +
+ ( ( tcphdr->flags & ( TCP_SYN | TCP_FIN ) ) ? 1 : 0 ) ), len);
+ tcp_dump_flags ( tcp, tcphdr->flags );
+ DBGC ( tcp, "\n" );
+
+ /* If no connection was found, send RST */
+ if ( ! tcp ) {
+ tcp_xmit_reset ( tcp, st_src, tcphdr );
+ rc = -ENOTCONN;
+ goto discard;
+ }
+
+ /* Handle ACK, if present */
+ if ( flags & TCP_ACK ) {
+ if ( ( rc = tcp_rx_ack ( tcp, ack, win ) ) != 0 ) {
+ tcp_xmit_reset ( tcp, st_src, tcphdr );
+ goto discard;
+ }
+ }
+
+ /* Handle SYN, if present */
+ if ( flags & TCP_SYN ) {
+ tcp_rx_syn ( tcp, seq, &options );
+ seq++;
+ }
+
+ /* Handle RST, if present */
+ if ( flags & TCP_RST ) {
+ if ( ( rc = tcp_rx_rst ( tcp, seq ) ) != 0 )
+ goto discard;
+ }
+
+ /* Handle new data, if any */
+ tcp_rx_data ( tcp, seq, iobuf );
+ seq += len;
+
+ /* Handle FIN, if present */
+ if ( flags & TCP_FIN ) {
+ tcp_rx_fin ( tcp, seq );
+ seq++;
+ }
+
+ /* Update timestamp, if present and applicable */
+ if ( ( seq == tcp->rcv_ack ) && options.tsopt )
+ tcp->ts_recent = ntohl ( options.tsopt->tsval );
+
+ /* Dump out any state change as a result of the received packet */
+ tcp_dump_state ( tcp );
+
+ /* Send out any pending data. If peer is expecting an ACK for
+ * this packet then force sending a reply.
+ */
+ tcp_xmit ( tcp, ( start_seq != seq ) );
+
+ /* If this packet was the last we expect to receive, set up
+ * timer to expire and cause the connection to be freed.
+ */
+ if ( TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ) ) {
+ tcp->timer.timeout = ( 2 * TCP_MSL );
+ start_timer ( &tcp->timer );
+ }
+
+ return 0;
+
+ discard:
+ /* Free received packet */
+ free_iob ( iobuf );
+ return rc;
+}
+
+/** TCP protocol */
+struct tcpip_protocol tcp_protocol __tcpip_protocol = {
+ .name = "TCP",
+ .rx = tcp_rx,
+ .tcpip_proto = IP_TCP,
+};
+
+/***************************************************************************
+ *
+ * Data transfer interface
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Close interface
+ *
+ * @v xfer Data transfer interface
+ * @v rc Reason for close
+ */
+static void tcp_xfer_close ( struct xfer_interface *xfer, int rc ) {
+ struct tcp_connection *tcp =
+ container_of ( xfer, struct tcp_connection, xfer );
+
+ /* Close data transfer interface */
+ tcp_close ( tcp, rc );
+
+ /* Transmit FIN, if possible */
+ tcp_xmit ( tcp, 0 );
+}
+
+/**
+ * Check flow control window
+ *
+ * @v xfer Data transfer interface
+ * @ret len Length of window
+ */
+static size_t tcp_xfer_window ( struct xfer_interface *xfer ) {
+ struct tcp_connection *tcp =
+ container_of ( xfer, struct tcp_connection, xfer );
+
+ /* Not ready if data queue is non-empty. This imposes a limit
+ * of only one unACKed packet in the TX queue at any time; we
+ * do this to conserve memory usage.
+ */
+ if ( ! list_empty ( &tcp->queue ) )
+ return 0;
+
+ /* Return TCP window length */
+ return tcp_xmit_win ( tcp );
+}
+
+/**
+ * Deliver datagram as I/O buffer
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf Datagram I/O buffer
+ * @v meta Data transfer metadata, or NULL
+ * @ret rc Return status code
+ */
+static int tcp_xfer_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta __unused ) {
+ struct tcp_connection *tcp =
+ container_of ( xfer, struct tcp_connection, xfer );
+
+ /* Enqueue packet */
+ list_add_tail ( &iobuf->list, &tcp->queue );
+
+ /* Transmit data, if possible */
+ tcp_xmit ( tcp, 0 );
+
+ return 0;
+}
+
+/** TCP data transfer interface operations */
+static struct xfer_interface_operations tcp_xfer_operations = {
+ .close = tcp_xfer_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = tcp_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = tcp_xfer_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/***************************************************************************
+ *
+ * Openers
+ *
+ ***************************************************************************
+ */
+
+/** TCP socket opener */
+struct socket_opener tcp_socket_opener __socket_opener = {
+ .semantics = SOCK_STREAM,
+ .family = AF_INET,
+ .open = tcp_open,
+};
+
+char TCP_SOCK_STREAM[1];
+
+/**
+ * Open TCP URI
+ *
+ * @v xfer Data transfer interface
+ * @v uri URI
+ * @ret rc Return status code
+ */
+static int tcp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) {
+ struct sockaddr_tcpip peer;
+
+ /* Sanity check */
+ if ( ! uri->host )
+ return -EINVAL;
+
+ memset ( &peer, 0, sizeof ( peer ) );
+ peer.st_port = htons ( uri_port ( uri, 0 ) );
+ return xfer_open_named_socket ( xfer, SOCK_STREAM,
+ ( struct sockaddr * ) &peer,
+ uri->host, NULL );
+}
+
+/** TCP URI opener */
+struct uri_opener tcp_uri_opener __uri_opener = {
+ .scheme = "tcp",
+ .open = tcp_open_uri,
+};
+
diff --git a/gpxe/src/net/tcp/ftp.c b/gpxe/src/net/tcp/ftp.c
new file mode 100644
index 00000000..ffb2fbff
--- /dev/null
+++ b/gpxe/src/net/tcp/ftp.c
@@ -0,0 +1,467 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/socket.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/in.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/uri.h>
+#include <gpxe/features.h>
+#include <gpxe/ftp.h>
+
+/** @file
+ *
+ * File transfer protocol
+ *
+ */
+
+FEATURE ( FEATURE_PROTOCOL, "FTP", DHCP_EB_FEATURE_FTP, 1 );
+
+/**
+ * FTP states
+ *
+ * These @b must be sequential, i.e. a successful FTP session must
+ * pass through each of these states in order.
+ */
+enum ftp_state {
+ FTP_CONNECT = 0,
+ FTP_USER,
+ FTP_PASS,
+ FTP_TYPE,
+ FTP_PASV,
+ FTP_RETR,
+ FTP_QUIT,
+ FTP_DONE,
+};
+
+/**
+ * An FTP request
+ *
+ */
+struct ftp_request {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+
+ /** URI being fetched */
+ struct uri *uri;
+ /** FTP control channel interface */
+ struct xfer_interface control;
+ /** FTP data channel interface */
+ struct xfer_interface data;
+
+ /** Current state */
+ enum ftp_state state;
+ /** Buffer to be filled with data received via the control channel */
+ char *recvbuf;
+ /** Remaining size of recvbuf */
+ size_t recvsize;
+ /** FTP status code, as text */
+ char status_text[5];
+ /** Passive-mode parameters, as text */
+ char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */
+};
+
+/**
+ * Free FTP request
+ *
+ * @v refcnt Reference counter
+ */
+static void ftp_free ( struct refcnt *refcnt ) {
+ struct ftp_request *ftp =
+ container_of ( refcnt, struct ftp_request, refcnt );
+
+ DBGC ( ftp, "FTP %p freed\n", ftp );
+
+ uri_put ( ftp->uri );
+ free ( ftp );
+}
+
+/**
+ * Mark FTP operation as complete
+ *
+ * @v ftp FTP request
+ * @v rc Return status code
+ */
+static void ftp_done ( struct ftp_request *ftp, int rc ) {
+
+ DBGC ( ftp, "FTP %p completed (%s)\n", ftp, strerror ( rc ) );
+
+ /* Close all data transfer interfaces */
+ xfer_nullify ( &ftp->xfer );
+ xfer_close ( &ftp->xfer, rc );
+ xfer_nullify ( &ftp->control );
+ xfer_close ( &ftp->control, rc );
+ xfer_nullify ( &ftp->data );
+ xfer_close ( &ftp->data, rc );
+}
+
+/*****************************************************************************
+ *
+ * FTP control channel
+ *
+ */
+
+/**
+ * FTP control channel strings
+ *
+ * These are used as printf() format strings. Since only one of them
+ * (RETR) takes an argument, we always supply that argument to the
+ * snprintf() call.
+ */
+static const char * ftp_strings[] = {
+ [FTP_CONNECT] = "",
+ [FTP_USER] = "USER anonymous\r\n",
+ [FTP_PASS] = "PASS etherboot@etherboot.org\r\n",
+ [FTP_TYPE] = "TYPE I\r\n",
+ [FTP_PASV] = "PASV\r\n",
+ [FTP_RETR] = "RETR %s\r\n",
+ [FTP_QUIT] = "QUIT\r\n",
+ [FTP_DONE] = "",
+};
+
+/**
+ * Handle control channel being closed
+ *
+ * @v control FTP control channel interface
+ * @v rc Reason for close
+ *
+ * When the control channel is closed, the data channel must also be
+ * closed, if it is currently open.
+ */
+static void ftp_control_close ( struct xfer_interface *control, int rc ) {
+ struct ftp_request *ftp =
+ container_of ( control, struct ftp_request, control );
+
+ DBGC ( ftp, "FTP %p control connection closed: %s\n",
+ ftp, strerror ( rc ) );
+
+ /* Complete FTP operation */
+ ftp_done ( ftp, rc );
+}
+
+/**
+ * Parse FTP byte sequence value
+ *
+ * @v text Text string
+ * @v value Value buffer
+ * @v len Length of value buffer
+ *
+ * This parses an FTP byte sequence value (e.g. the "aaa,bbb,ccc,ddd"
+ * form for IP addresses in PORT commands) into a byte sequence. @c
+ * *text will be updated to point beyond the end of the parsed byte
+ * sequence.
+ *
+ * This function is safe in the presence of malformed data, though the
+ * output is undefined.
+ */
+static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) {
+ do {
+ *(value++) = strtoul ( *text, text, 10 );
+ if ( **text )
+ (*text)++;
+ } while ( --len );
+}
+
+/**
+ * Handle an FTP control channel response
+ *
+ * @v ftp FTP request
+ *
+ * This is called once we have received a complete response line.
+ */
+static void ftp_reply ( struct ftp_request *ftp ) {
+ char status_major = ftp->status_text[0];
+ char separator = ftp->status_text[3];
+
+ DBGC ( ftp, "FTP %p received status %s\n", ftp, ftp->status_text );
+
+ /* Ignore malformed lines */
+ if ( separator != ' ' )
+ return;
+
+ /* Ignore "intermediate" responses (1xx codes) */
+ if ( status_major == '1' )
+ return;
+
+ /* Anything other than success (2xx) or, in the case of a
+ * repsonse to a "USER" command, a password prompt (3xx), is a
+ * fatal error.
+ */
+ if ( ! ( ( status_major == '2' ) ||
+ ( ( status_major == '3' ) && ( ftp->state == FTP_USER ) ) ) ){
+ /* Flag protocol error and close connections */
+ ftp_done ( ftp, -EPROTO );
+ }
+
+ /* Open passive connection when we get "PASV" response */
+ if ( ftp->state == FTP_PASV ) {
+ char *ptr = ftp->passive_text;
+ union {
+ struct sockaddr_in sin;
+ struct sockaddr sa;
+ } sa;
+ int rc;
+
+ sa.sin.sin_family = AF_INET;
+ ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_addr,
+ sizeof ( sa.sin.sin_addr ) );
+ ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_port,
+ sizeof ( sa.sin.sin_port ) );
+ if ( ( rc = xfer_open_socket ( &ftp->data, SOCK_STREAM,
+ &sa.sa, NULL ) ) != 0 ) {
+ DBGC ( ftp, "FTP %p could not open data connection\n",
+ ftp );
+ ftp_done ( ftp, rc );
+ return;
+ }
+ }
+
+ /* Move to next state */
+ if ( ftp->state < FTP_DONE )
+ ftp->state++;
+
+ /* Send control string */
+ if ( ftp->state < FTP_DONE ) {
+ DBGC ( ftp, "FTP %p sending ", ftp );
+ DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path );
+ xfer_printf ( &ftp->control, ftp_strings[ftp->state],
+ ftp->uri->path );
+ }
+}
+
+/**
+ * Handle new data arriving on FTP control channel
+ *
+ * @v control FTP control channel interface
+ * @v data New data
+ * @v len Length of new data
+ *
+ * Data is collected until a complete line is received, at which point
+ * its information is passed to ftp_reply().
+ */
+static int ftp_control_deliver_raw ( struct xfer_interface *control,
+ const void *data, size_t len ) {
+ struct ftp_request *ftp =
+ container_of ( control, struct ftp_request, control );
+ char *recvbuf = ftp->recvbuf;
+ size_t recvsize = ftp->recvsize;
+ char c;
+
+ while ( len-- ) {
+ c = * ( ( char * ) data++ );
+ switch ( c ) {
+ case '\r' :
+ case '\n' :
+ /* End of line: call ftp_reply() to handle
+ * completed reply. Avoid calling ftp_reply()
+ * twice if we receive both \r and \n.
+ */
+ if ( recvsize == 0 )
+ ftp_reply ( ftp );
+ /* Start filling up the status code buffer */
+ recvbuf = ftp->status_text;
+ recvsize = sizeof ( ftp->status_text ) - 1;
+ break;
+ case '(' :
+ /* Start filling up the passive parameter buffer */
+ recvbuf = ftp->passive_text;
+ recvsize = sizeof ( ftp->passive_text ) - 1;
+ break;
+ case ')' :
+ /* Stop filling the passive parameter buffer */
+ recvsize = 0;
+ break;
+ default :
+ /* Fill up buffer if applicable */
+ if ( recvsize > 0 ) {
+ *(recvbuf++) = c;
+ recvsize--;
+ }
+ break;
+ }
+ }
+
+ /* Store for next invocation */
+ ftp->recvbuf = recvbuf;
+ ftp->recvsize = recvsize;
+
+ return 0;
+}
+
+/** FTP control channel operations */
+static struct xfer_interface_operations ftp_control_operations = {
+ .close = ftp_control_close,
+ .vredirect = xfer_vopen,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = ftp_control_deliver_raw,
+};
+
+/*****************************************************************************
+ *
+ * FTP data channel
+ *
+ */
+
+/**
+ * Handle FTP data channel being closed
+ *
+ * @v data FTP data channel interface
+ * @v rc Reason for closure
+ *
+ * When the data channel is closed, the control channel should be left
+ * alone; the server will send a completion message via the control
+ * channel which we'll pick up.
+ *
+ * If the data channel is closed due to an error, we abort the request.
+ */
+static void ftp_data_closed ( struct xfer_interface *data, int rc ) {
+ struct ftp_request *ftp =
+ container_of ( data, struct ftp_request, data );
+
+ DBGC ( ftp, "FTP %p data connection closed: %s\n",
+ ftp, strerror ( rc ) );
+
+ /* If there was an error, close control channel and record status */
+ if ( rc )
+ ftp_done ( ftp, rc );
+}
+
+/**
+ * Handle data delivery via FTP data channel
+ *
+ * @v xfer FTP data channel interface
+ * @v iobuf I/O buffer
+ * @v meta Data transfer metadata, or NULL
+ * @ret rc Return status code
+ */
+static int ftp_data_deliver_iob ( struct xfer_interface *data,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta __unused ) {
+ struct ftp_request *ftp =
+ container_of ( data, struct ftp_request, data );
+ int rc;
+
+ if ( ( rc = xfer_deliver_iob ( &ftp->xfer, iobuf ) ) != 0 ) {
+ DBGC ( ftp, "FTP %p failed to deliver data: %s\n",
+ ftp, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/** FTP data channel operations */
+static struct xfer_interface_operations ftp_data_operations = {
+ .close = ftp_data_closed,
+ .vredirect = xfer_vopen,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = ftp_data_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/*****************************************************************************
+ *
+ * Data transfer interface
+ *
+ */
+
+/**
+ * Close FTP data transfer interface
+ *
+ * @v xfer FTP data transfer interface
+ * @v rc Reason for close
+ */
+static void ftp_xfer_closed ( struct xfer_interface *xfer, int rc ) {
+ struct ftp_request *ftp =
+ container_of ( xfer, struct ftp_request, xfer );
+
+ DBGC ( ftp, "FTP %p data transfer interface closed: %s\n",
+ ftp, strerror ( rc ) );
+
+ ftp_done ( ftp, rc );
+}
+
+/** FTP data transfer interface operations */
+static struct xfer_interface_operations ftp_xfer_operations = {
+ .close = ftp_xfer_closed,
+ .vredirect = ignore_xfer_vredirect,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = ignore_xfer_deliver_raw,
+};
+
+/*****************************************************************************
+ *
+ * URI opener
+ *
+ */
+
+/**
+ * Initiate an FTP connection
+ *
+ * @v xfer Data transfer interface
+ * @v uri Uniform Resource Identifier
+ * @ret rc Return status code
+ */
+static int ftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
+ struct ftp_request *ftp;
+ struct sockaddr_tcpip server;
+ int rc;
+
+ /* Sanity checks */
+ if ( ! uri->path )
+ return -EINVAL;
+ if ( ! uri->host )
+ return -EINVAL;
+
+ /* Allocate and populate structure */
+ ftp = zalloc ( sizeof ( *ftp ) );
+ if ( ! ftp )
+ return -ENOMEM;
+ ftp->refcnt.free = ftp_free;
+ xfer_init ( &ftp->xfer, &ftp_xfer_operations, &ftp->refcnt );
+ ftp->uri = uri_get ( uri );
+ xfer_init ( &ftp->control, &ftp_control_operations, &ftp->refcnt );
+ xfer_init ( &ftp->data, &ftp_data_operations, &ftp->refcnt );
+ ftp->recvbuf = ftp->status_text;
+ ftp->recvsize = sizeof ( ftp->status_text ) - 1;
+
+ DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path );
+
+ /* Open control connection */
+ memset ( &server, 0, sizeof ( server ) );
+ server.st_port = htons ( uri_port ( uri, FTP_PORT ) );
+ if ( ( rc = xfer_open_named_socket ( &ftp->control, SOCK_STREAM,
+ ( struct sockaddr * ) &server,
+ uri->host, NULL ) ) != 0 )
+ goto err;
+
+ /* Attach to parent interface, mortalise self, and return */
+ xfer_plug_plug ( &ftp->xfer, xfer );
+ ref_put ( &ftp->refcnt );
+ return 0;
+
+ err:
+ DBGC ( ftp, "FTP %p could not create request: %s\n",
+ ftp, strerror ( rc ) );
+ ftp_done ( ftp, rc );
+ ref_put ( &ftp->refcnt );
+ return rc;
+}
+
+/** FTP URI opener */
+struct uri_opener ftp_uri_opener __uri_opener = {
+ .scheme = "ftp",
+ .open = ftp_open,
+};
diff --git a/gpxe/src/net/tcp/http.c b/gpxe/src/net/tcp/http.c
new file mode 100644
index 00000000..db92e9eb
--- /dev/null
+++ b/gpxe/src/net/tcp/http.c
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Hyper Text Transfer Protocol (HTTP)
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <assert.h>
+#include <gpxe/uri.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/socket.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/process.h>
+#include <gpxe/linebuf.h>
+#include <gpxe/features.h>
+#include <gpxe/http.h>
+
+FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 );
+
+/** HTTP receive state */
+enum http_rx_state {
+ HTTP_RX_RESPONSE = 0,
+ HTTP_RX_HEADER,
+ HTTP_RX_DATA,
+ HTTP_RX_DEAD,
+};
+
+/**
+ * An HTTP request
+ *
+ */
+struct http_request {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+
+ /** URI being fetched */
+ struct uri *uri;
+ /** Transport layer interface */
+ struct xfer_interface socket;
+
+ /** TX process */
+ struct process process;
+
+ /** HTTP response code */
+ unsigned int response;
+ /** HTTP Content-Length */
+ size_t content_length;
+ /** Received length */
+ size_t rx_len;
+ /** RX state */
+ enum http_rx_state rx_state;
+ /** Line buffer for received header lines */
+ struct line_buffer linebuf;
+};
+
+/**
+ * Free HTTP request
+ *
+ * @v refcnt Reference counter
+ */
+static void http_free ( struct refcnt *refcnt ) {
+ struct http_request *http =
+ container_of ( refcnt, struct http_request, refcnt );
+
+ uri_put ( http->uri );
+ empty_line_buffer ( &http->linebuf );
+ free ( http );
+};
+
+/**
+ * Mark HTTP request as complete
+ *
+ * @v http HTTP request
+ * @v rc Return status code
+ */
+static void http_done ( struct http_request *http, int rc ) {
+
+ /* Prevent further processing of any current packet */
+ http->rx_state = HTTP_RX_DEAD;
+
+ /* If we had a Content-Length, and the received content length
+ * isn't correct, flag an error
+ */
+ if ( http->content_length &&
+ ( http->content_length != http->rx_len ) ) {
+ DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n",
+ http, http->rx_len, http->content_length );
+ rc = -EIO;
+ }
+
+ /* Remove process */
+ process_del ( &http->process );
+
+ /* Close all data transfer interfaces */
+ xfer_nullify ( &http->socket );
+ xfer_close ( &http->socket, rc );
+ xfer_nullify ( &http->xfer );
+ xfer_close ( &http->xfer, rc );
+}
+
+/**
+ * Convert HTTP response code to return status code
+ *
+ * @v response HTTP response code
+ * @ret rc Return status code
+ */
+static int http_response_to_rc ( unsigned int response ) {
+ switch ( response ) {
+ case 200:
+ return 0;
+ case 404:
+ return -ENOENT;
+ case 403:
+ return -EPERM;
+ default:
+ return -EIO;
+ }
+}
+
+/**
+ * Handle HTTP response
+ *
+ * @v http HTTP request
+ * @v response HTTP response
+ * @ret rc Return status code
+ */
+static int http_rx_response ( struct http_request *http, char *response ) {
+ char *spc;
+ int rc;
+
+ DBGC ( http, "HTTP %p response \"%s\"\n", http, response );
+
+ /* Check response starts with "HTTP/" */
+ if ( strncmp ( response, "HTTP/", 5 ) != 0 )
+ return -EIO;
+
+ /* Locate and check response code */
+ spc = strchr ( response, ' ' );
+ if ( ! spc )
+ return -EIO;
+ http->response = strtoul ( spc, NULL, 10 );
+ if ( ( rc = http_response_to_rc ( http->response ) ) != 0 )
+ return rc;
+
+ /* Move to received headers */
+ http->rx_state = HTTP_RX_HEADER;
+ return 0;
+}
+
+/**
+ * Handle HTTP Content-Length header
+ *
+ * @v http HTTP request
+ * @v value HTTP header value
+ * @ret rc Return status code
+ */
+static int http_rx_content_length ( struct http_request *http,
+ const char *value ) {
+ char *endp;
+
+ http->content_length = strtoul ( value, &endp, 10 );
+ if ( *endp != '\0' ) {
+ DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n",
+ http, value );
+ return -EIO;
+ }
+
+ /* Use seek() to notify recipient of filesize */
+ xfer_seek ( &http->xfer, http->content_length, SEEK_SET );
+ xfer_seek ( &http->xfer, 0, SEEK_SET );
+
+ return 0;
+}
+
+/** An HTTP header handler */
+struct http_header_handler {
+ /** Name (e.g. "Content-Length") */
+ const char *header;
+ /** Handle received header
+ *
+ * @v http HTTP request
+ * @v value HTTP header value
+ * @ret rc Return status code
+ *
+ * If an error is returned, the download will be aborted.
+ */
+ int ( * rx ) ( struct http_request *http, const char *value );
+};
+
+/** List of HTTP header handlers */
+static struct http_header_handler http_header_handlers[] = {
+ {
+ .header = "Content-Length",
+ .rx = http_rx_content_length,
+ },
+ { NULL, NULL }
+};
+
+/**
+ * Handle HTTP header
+ *
+ * @v http HTTP request
+ * @v header HTTP header
+ * @ret rc Return status code
+ */
+static int http_rx_header ( struct http_request *http, char *header ) {
+ struct http_header_handler *handler;
+ char *separator;
+ char *value;
+ int rc;
+
+ /* An empty header line marks the transition to the data phase */
+ if ( ! header[0] ) {
+ DBGC ( http, "HTTP %p start of data\n", http );
+ empty_line_buffer ( &http->linebuf );
+ http->rx_state = HTTP_RX_DATA;
+ return 0;
+ }
+
+ DBGC ( http, "HTTP %p header \"%s\"\n", http, header );
+
+ /* Split header at the ": " */
+ separator = strstr ( header, ": " );
+ if ( ! separator ) {
+ DBGC ( http, "HTTP %p malformed header\n", http );
+ return -EIO;
+ }
+ *separator = '\0';
+ value = ( separator + 2 );
+
+ /* Hand off to header handler, if one exists */
+ for ( handler = http_header_handlers ; handler->header ; handler++ ) {
+ if ( strcasecmp ( header, handler->header ) == 0 ) {
+ if ( ( rc = handler->rx ( http, value ) ) != 0 )
+ return rc;
+ break;
+ }
+ }
+ return 0;
+}
+
+/** An HTTP line-based data handler */
+struct http_line_handler {
+ /** Handle line
+ *
+ * @v http HTTP request
+ * @v line Line to handle
+ * @ret rc Return status code
+ */
+ int ( * rx ) ( struct http_request *http, char *line );
+};
+
+/** List of HTTP line-based data handlers */
+static struct http_line_handler http_line_handlers[] = {
+ [HTTP_RX_RESPONSE] = { .rx = http_rx_response },
+ [HTTP_RX_HEADER] = { .rx = http_rx_header },
+};
+
+/**
+ * Handle new data arriving via HTTP connection in the data phase
+ *
+ * @v http HTTP request
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int http_rx_data ( struct http_request *http,
+ struct io_buffer *iobuf ) {
+ int rc;
+
+ /* Update received length */
+ http->rx_len += iob_len ( iobuf );
+
+ /* Hand off data buffer */
+ if ( ( rc = xfer_deliver_iob ( &http->xfer, iobuf ) ) != 0 )
+ return rc;
+
+ /* If we have reached the content-length, stop now */
+ if ( http->content_length &&
+ ( http->rx_len >= http->content_length ) ) {
+ http_done ( http, 0 );
+ }
+
+ return 0;
+}
+
+/**
+ * Handle new data arriving via HTTP connection
+ *
+ * @v socket Transport layer interface
+ * @v iobuf I/O buffer
+ * @v meta Data transfer metadata, or NULL
+ * @ret rc Return status code
+ */
+static int http_socket_deliver_iob ( struct xfer_interface *socket,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta __unused ) {
+ struct http_request *http =
+ container_of ( socket, struct http_request, socket );
+ struct http_line_handler *lh;
+ char *line;
+ ssize_t len;
+ int rc = 0;
+
+ while ( iob_len ( iobuf ) ) {
+ switch ( http->rx_state ) {
+ case HTTP_RX_DEAD:
+ /* Do no further processing */
+ goto done;
+ case HTTP_RX_DATA:
+ /* Once we're into the data phase, just fill
+ * the data buffer
+ */
+ rc = http_rx_data ( http, iobuf );
+ iobuf = NULL;
+ goto done;
+ case HTTP_RX_RESPONSE:
+ case HTTP_RX_HEADER:
+ /* In the other phases, buffer and process a
+ * line at a time
+ */
+ len = line_buffer ( &http->linebuf, iobuf->data,
+ iob_len ( iobuf ) );
+ if ( len < 0 ) {
+ rc = len;
+ DBGC ( http, "HTTP %p could not buffer line: "
+ "%s\n", http, strerror ( rc ) );
+ goto done;
+ }
+ iob_pull ( iobuf, len );
+ line = buffered_line ( &http->linebuf );
+ if ( line ) {
+ lh = &http_line_handlers[http->rx_state];
+ if ( ( rc = lh->rx ( http, line ) ) != 0 )
+ goto done;
+ }
+ break;
+ default:
+ assert ( 0 );
+ break;
+ }
+ }
+
+ done:
+ if ( rc )
+ http_done ( http, rc );
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * HTTP process
+ *
+ * @v process Process
+ */
+static void http_step ( struct process *process ) {
+ struct http_request *http =
+ container_of ( process, struct http_request, process );
+ const char *path = http->uri->path;
+ const char *host = http->uri->host;
+ const char *query = http->uri->query;
+ int rc;
+
+ if ( xfer_window ( &http->socket ) ) {
+ process_del ( &http->process );
+ if ( ( rc = xfer_printf ( &http->socket,
+ "GET %s%s%s HTTP/1.1\r\n"
+ "User-Agent: gPXE/" VERSION "\r\n"
+ "Host: %s\r\n"
+ "\r\n",
+ ( path ? path : "/" ),
+ ( query ? "?" : "" ),
+ ( query ? query : "" ),
+ host ) ) != 0 ) {
+ http_done ( http, rc );
+ }
+ }
+}
+
+/**
+ * HTTP connection closed by network stack
+ *
+ * @v socket Transport layer interface
+ * @v rc Reason for close
+ */
+static void http_socket_close ( struct xfer_interface *socket, int rc ) {
+ struct http_request *http =
+ container_of ( socket, struct http_request, socket );
+
+ DBGC ( http, "HTTP %p socket closed: %s\n",
+ http, strerror ( rc ) );
+
+ http_done ( http, rc );
+}
+
+/** HTTP socket operations */
+static struct xfer_interface_operations http_socket_operations = {
+ .close = http_socket_close,
+ .vredirect = xfer_vopen,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = http_socket_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/**
+ * Close HTTP data transfer interface
+ *
+ * @v xfer Data transfer interface
+ * @v rc Reason for close
+ */
+static void http_xfer_close ( struct xfer_interface *xfer, int rc ) {
+ struct http_request *http =
+ container_of ( xfer, struct http_request, xfer );
+
+ DBGC ( http, "HTTP %p interface closed: %s\n",
+ http, strerror ( rc ) );
+
+ http_done ( http, rc );
+}
+
+/** HTTP data transfer interface operations */
+static struct xfer_interface_operations http_xfer_operations = {
+ .close = http_xfer_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = ignore_xfer_deliver_raw,
+};
+
+/**
+ * Initiate an HTTP connection, with optional filter
+ *
+ * @v xfer Data transfer interface
+ * @v uri Uniform Resource Identifier
+ * @v default_port Default port number
+ * @v filter Filter to apply to socket, or NULL
+ * @ret rc Return status code
+ */
+int http_open_filter ( struct xfer_interface *xfer, struct uri *uri,
+ unsigned int default_port,
+ int ( * filter ) ( struct xfer_interface *xfer,
+ struct xfer_interface **next ) ) {
+ struct http_request *http;
+ struct sockaddr_tcpip server;
+ struct xfer_interface *socket;
+ int rc;
+
+ /* Sanity checks */
+ if ( ! uri->host )
+ return -EINVAL;
+
+ /* Allocate and populate HTTP structure */
+ http = zalloc ( sizeof ( *http ) );
+ if ( ! http )
+ return -ENOMEM;
+ http->refcnt.free = http_free;
+ xfer_init ( &http->xfer, &http_xfer_operations, &http->refcnt );
+ http->uri = uri_get ( uri );
+ xfer_init ( &http->socket, &http_socket_operations, &http->refcnt );
+ process_init ( &http->process, http_step, &http->refcnt );
+
+ /* Open socket */
+ memset ( &server, 0, sizeof ( server ) );
+ server.st_port = htons ( uri_port ( http->uri, default_port ) );
+ socket = &http->socket;
+ if ( filter ) {
+ if ( ( rc = filter ( socket, &socket ) ) != 0 )
+ goto err;
+ }
+ if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM,
+ ( struct sockaddr * ) &server,
+ uri->host, NULL ) ) != 0 )
+ goto err;
+
+ /* Attach to parent interface, mortalise self, and return */
+ xfer_plug_plug ( &http->xfer, xfer );
+ ref_put ( &http->refcnt );
+ return 0;
+
+ err:
+ DBGC ( http, "HTTP %p could not create request: %s\n",
+ http, strerror ( rc ) );
+ http_done ( http, rc );
+ ref_put ( &http->refcnt );
+ return rc;
+}
+
+/**
+ * Initiate an HTTP connection
+ *
+ * @v xfer Data transfer interface
+ * @v uri Uniform Resource Identifier
+ * @ret rc Return status code
+ */
+static int http_open ( struct xfer_interface *xfer, struct uri *uri ) {
+ return http_open_filter ( xfer, uri, HTTP_PORT, NULL );
+}
+
+/** HTTP URI opener */
+struct uri_opener http_uri_opener __uri_opener = {
+ .scheme = "http",
+ .open = http_open,
+};
diff --git a/gpxe/src/net/tcp/https.c b/gpxe/src/net/tcp/https.c
new file mode 100644
index 00000000..15ab32ef
--- /dev/null
+++ b/gpxe/src/net/tcp/https.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Secure Hyper Text Transfer Protocol (HTTPS)
+ *
+ */
+
+#include <stddef.h>
+#include <gpxe/open.h>
+#include <gpxe/tls.h>
+#include <gpxe/http.h>
+#include <gpxe/features.h>
+
+FEATURE ( FEATURE_PROTOCOL, "HTTPS", DHCP_EB_FEATURE_HTTPS, 1 );
+
+/**
+ * Initiate an HTTPS connection
+ *
+ * @v xfer Data transfer interface
+ * @v uri Uniform Resource Identifier
+ * @ret rc Return status code
+ */
+static int https_open ( struct xfer_interface *xfer, struct uri *uri ) {
+ return http_open_filter ( xfer, uri, HTTPS_PORT, add_tls );
+}
+
+/** HTTPS URI opener */
+struct uri_opener https_uri_opener __uri_opener = {
+ .scheme = "https",
+ .open = https_open,
+};
diff --git a/gpxe/src/net/tcp/iscsi.c b/gpxe/src/net/tcp/iscsi.c
new file mode 100644
index 00000000..c01ca44b
--- /dev/null
+++ b/gpxe/src/net/tcp/iscsi.c
@@ -0,0 +1,1726 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <gpxe/vsprintf.h>
+#include <gpxe/socket.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/scsi.h>
+#include <gpxe/process.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/settings.h>
+#include <gpxe/features.h>
+#include <gpxe/iscsi.h>
+
+/** @file
+ *
+ * iSCSI protocol
+ *
+ */
+
+FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 );
+
+/** iSCSI initiator name (explicitly specified) */
+static char *iscsi_explicit_initiator_iqn;
+
+/** Default iSCSI initiator name (constructed from hostname) */
+static char *iscsi_default_initiator_iqn;
+
+/** iSCSI username */
+static char *iscsi_username;
+
+/** iSCSI password */
+static char *iscsi_password;
+
+static void iscsi_start_tx ( struct iscsi_session *iscsi );
+static void iscsi_start_login ( struct iscsi_session *iscsi );
+static void iscsi_start_data_out ( struct iscsi_session *iscsi,
+ unsigned int datasn );
+
+/**
+ * Finish receiving PDU data into buffer
+ *
+ * @v iscsi iSCSI session
+ */
+static void iscsi_rx_buffered_data_done ( struct iscsi_session *iscsi ) {
+ free ( iscsi->rx_buffer );
+ iscsi->rx_buffer = NULL;
+}
+
+/**
+ * Free iSCSI session
+ *
+ * @v refcnt Reference counter
+ */
+static void iscsi_free ( struct refcnt *refcnt ) {
+ struct iscsi_session *iscsi =
+ container_of ( refcnt, struct iscsi_session, refcnt );
+
+ free ( iscsi->target_address );
+ free ( iscsi->target_iqn );
+ free ( iscsi->username );
+ free ( iscsi->password );
+ chap_finish ( &iscsi->chap );
+ iscsi_rx_buffered_data_done ( iscsi );
+ free ( iscsi );
+}
+
+/**
+ * Open iSCSI transport-layer connection
+ *
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ */
+static int iscsi_open_connection ( struct iscsi_session *iscsi ) {
+ struct sockaddr_tcpip target;
+ int rc;
+
+ assert ( iscsi->tx_state == ISCSI_TX_IDLE );
+ assert ( iscsi->rx_state == ISCSI_RX_BHS );
+ assert ( iscsi->rx_offset == 0 );
+
+ /* Open socket */
+ memset ( &target, 0, sizeof ( target ) );
+ target.st_port = htons ( iscsi->target_port );
+ if ( ( rc = xfer_open_named_socket ( &iscsi->socket, SOCK_STREAM,
+ ( struct sockaddr * ) &target,
+ iscsi->target_address,
+ NULL ) ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p could not open socket: %s\n",
+ iscsi, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Enter security negotiation phase */
+ iscsi->status = ( ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE |
+ ISCSI_STATUS_STRINGS_SECURITY );
+
+ /* Assign fresh initiator task tag */
+ iscsi->itt++;
+
+ /* Initiate login */
+ iscsi_start_login ( iscsi );
+
+ return 0;
+}
+
+/**
+ * Close iSCSI transport-layer connection
+ *
+ * @v iscsi iSCSI session
+ * @v rc Reason for close
+ *
+ * Closes the transport-layer connection and resets the session state
+ * ready to attempt a fresh login.
+ */
+static void iscsi_close_connection ( struct iscsi_session *iscsi, int rc ) {
+
+ /* Close all data transfer interfaces */
+ xfer_close ( &iscsi->socket, rc );
+
+ /* Clear connection status */
+ iscsi->status = 0;
+
+ /* Reset TX and RX state machines */
+ iscsi->tx_state = ISCSI_TX_IDLE;
+ iscsi->rx_state = ISCSI_RX_BHS;
+ iscsi->rx_offset = 0;
+
+ /* Free any temporary dynamically allocated memory */
+ chap_finish ( &iscsi->chap );
+ iscsi_rx_buffered_data_done ( iscsi );
+}
+
+/**
+ * Mark iSCSI SCSI operation as complete
+ *
+ * @v iscsi iSCSI session
+ * @v rc Return status code
+ *
+ * Note that iscsi_scsi_done() will not close the connection, and must
+ * therefore be called only when the internal state machines are in an
+ * appropriate state, otherwise bad things may happen on the next call
+ * to iscsi_issue(). The general rule is to call iscsi_scsi_done()
+ * only at the end of receiving a PDU; at this point the TX and RX
+ * engines should both be idle.
+ */
+static void iscsi_scsi_done ( struct iscsi_session *iscsi, int rc ) {
+
+ assert ( iscsi->tx_state == ISCSI_TX_IDLE );
+
+ iscsi->command = NULL;
+ iscsi->rc = rc;
+}
+
+/****************************************************************************
+ *
+ * iSCSI SCSI command issuing
+ *
+ */
+
+/**
+ * Build iSCSI SCSI command BHS
+ *
+ * @v iscsi iSCSI session
+ *
+ * We don't currently support bidirectional commands (i.e. with both
+ * Data-In and Data-Out segments); these would require providing code
+ * to generate an AHS, and there doesn't seem to be any need for it at
+ * the moment.
+ */
+static void iscsi_start_command ( struct iscsi_session *iscsi ) {
+ struct iscsi_bhs_scsi_command *command = &iscsi->tx_bhs.scsi_command;
+
+ assert ( ! ( iscsi->command->data_in && iscsi->command->data_out ) );
+
+ /* Construct BHS and initiate transmission */
+ iscsi_start_tx ( iscsi );
+ command->opcode = ISCSI_OPCODE_SCSI_COMMAND;
+ command->flags = ( ISCSI_FLAG_FINAL |
+ ISCSI_COMMAND_ATTR_SIMPLE );
+ if ( iscsi->command->data_in )
+ command->flags |= ISCSI_COMMAND_FLAG_READ;
+ if ( iscsi->command->data_out )
+ command->flags |= ISCSI_COMMAND_FLAG_WRITE;
+ /* lengths left as zero */
+ command->lun = iscsi->lun;
+ command->itt = htonl ( ++iscsi->itt );
+ command->exp_len = htonl ( iscsi->command->data_in_len |
+ iscsi->command->data_out_len );
+ command->cmdsn = htonl ( iscsi->cmdsn );
+ command->expstatsn = htonl ( iscsi->statsn + 1 );
+ memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
+ DBGC ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n",
+ iscsi, SCSI_CDB_DATA ( command->cdb ),
+ ( iscsi->command->data_in ? "in" : "out" ),
+ ( iscsi->command->data_in ?
+ iscsi->command->data_in_len : iscsi->command->data_out_len ));
+}
+
+/**
+ * Receive data segment of an iSCSI SCSI response PDU
+ *
+ * @v iscsi iSCSI session
+ * @v data Received data
+ * @v len Length of received data
+ * @v remaining Data remaining after this data
+ * @ret rc Return status code
+ */
+static int iscsi_rx_scsi_response ( struct iscsi_session *iscsi,
+ const void *data, size_t len,
+ size_t remaining ) {
+ struct iscsi_bhs_scsi_response *response
+ = &iscsi->rx_bhs.scsi_response;
+ int sense_offset;
+
+ /* Capture the sense response code as it floats past, if present */
+ sense_offset = ISCSI_SENSE_RESPONSE_CODE_OFFSET - iscsi->rx_offset;
+ if ( ( sense_offset >= 0 ) && len ) {
+ iscsi->command->sense_response =
+ * ( ( char * ) data + sense_offset );
+ }
+
+ /* Wait for whole SCSI response to arrive */
+ if ( remaining )
+ return 0;
+
+ /* Record SCSI status code */
+ iscsi->command->status = response->status;
+
+ /* Check for errors */
+ if ( response->response != ISCSI_RESPONSE_COMMAND_COMPLETE )
+ return -EIO;
+
+ /* Mark as completed */
+ iscsi_scsi_done ( iscsi, 0 );
+ return 0;
+}
+
+/**
+ * Receive data segment of an iSCSI data-in PDU
+ *
+ * @v iscsi iSCSI session
+ * @v data Received data
+ * @v len Length of received data
+ * @v remaining Data remaining after this data
+ * @ret rc Return status code
+ */
+static int iscsi_rx_data_in ( struct iscsi_session *iscsi,
+ const void *data, size_t len,
+ size_t remaining ) {
+ struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in;
+ unsigned long offset;
+
+ /* Copy data to data-in buffer */
+ offset = ntohl ( data_in->offset ) + iscsi->rx_offset;
+ assert ( iscsi->command != NULL );
+ assert ( iscsi->command->data_in );
+ assert ( ( offset + len ) <= iscsi->command->data_in_len );
+ copy_to_user ( iscsi->command->data_in, offset, data, len );
+
+ /* Wait for whole SCSI response to arrive */
+ if ( remaining )
+ return 0;
+
+ /* Mark as completed if status is present */
+ if ( data_in->flags & ISCSI_DATA_FLAG_STATUS ) {
+ assert ( ( offset + len ) == iscsi->command->data_in_len );
+ assert ( data_in->flags & ISCSI_FLAG_FINAL );
+ iscsi->command->status = data_in->status;
+ /* iSCSI cannot return an error status via a data-in */
+ iscsi_scsi_done ( iscsi, 0 );
+ }
+
+ return 0;
+}
+
+/**
+ * Receive data segment of an iSCSI R2T PDU
+ *
+ * @v iscsi iSCSI session
+ * @v data Received data
+ * @v len Length of received data
+ * @v remaining Data remaining after this data
+ * @ret rc Return status code
+ */
+static int iscsi_rx_r2t ( struct iscsi_session *iscsi,
+ const void *data __unused, size_t len __unused,
+ size_t remaining __unused ) {
+ struct iscsi_bhs_r2t *r2t = &iscsi->rx_bhs.r2t;
+
+ /* Record transfer parameters and trigger first data-out */
+ iscsi->ttt = ntohl ( r2t->ttt );
+ iscsi->transfer_offset = ntohl ( r2t->offset );
+ iscsi->transfer_len = ntohl ( r2t->len );
+ iscsi_start_data_out ( iscsi, 0 );
+
+ return 0;
+}
+
+/**
+ * Build iSCSI data-out BHS
+ *
+ * @v iscsi iSCSI session
+ * @v datasn Data sequence number within the transfer
+ *
+ */
+static void iscsi_start_data_out ( struct iscsi_session *iscsi,
+ unsigned int datasn ) {
+ struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
+ unsigned long offset;
+ unsigned long remaining;
+ unsigned long len;
+
+ /* We always send 512-byte Data-Out PDUs; this removes the
+ * need to worry about the target's MaxRecvDataSegmentLength.
+ */
+ offset = datasn * 512;
+ remaining = iscsi->transfer_len - offset;
+ len = remaining;
+ if ( len > 512 )
+ len = 512;
+
+ /* Construct BHS and initiate transmission */
+ iscsi_start_tx ( iscsi );
+ data_out->opcode = ISCSI_OPCODE_DATA_OUT;
+ if ( len == remaining )
+ data_out->flags = ( ISCSI_FLAG_FINAL );
+ ISCSI_SET_LENGTHS ( data_out->lengths, 0, len );
+ data_out->lun = iscsi->lun;
+ data_out->itt = htonl ( iscsi->itt );
+ data_out->ttt = htonl ( iscsi->ttt );
+ data_out->expstatsn = htonl ( iscsi->statsn + 1 );
+ data_out->datasn = htonl ( datasn );
+ data_out->offset = htonl ( iscsi->transfer_offset + offset );
+ DBGC ( iscsi, "iSCSI %p start data out DataSN %#x len %#lx\n",
+ iscsi, datasn, len );
+}
+
+/**
+ * Complete iSCSI data-out PDU transmission
+ *
+ * @v iscsi iSCSI session
+ *
+ */
+static void iscsi_data_out_done ( struct iscsi_session *iscsi ) {
+ struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
+
+ /* If we haven't reached the end of the sequence, start
+ * sending the next data-out PDU.
+ */
+ if ( ! ( data_out->flags & ISCSI_FLAG_FINAL ) )
+ iscsi_start_data_out ( iscsi, ntohl ( data_out->datasn ) + 1 );
+}
+
+/**
+ * Send iSCSI data-out data segment
+ *
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ */
+static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) {
+ struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
+ struct io_buffer *iobuf;
+ unsigned long offset;
+ size_t len;
+
+ offset = ntohl ( data_out->offset );
+ len = ISCSI_DATA_LEN ( data_out->lengths );
+
+ assert ( iscsi->command != NULL );
+ assert ( iscsi->command->data_out );
+ assert ( ( offset + len ) <= iscsi->command->data_out_len );
+
+ iobuf = xfer_alloc_iob ( &iscsi->socket, len );
+ if ( ! iobuf )
+ return -ENOMEM;
+
+ copy_from_user ( iob_put ( iobuf, len ),
+ iscsi->command->data_out, offset, len );
+
+ return xfer_deliver_iob ( &iscsi->socket, iobuf );
+}
+
+/****************************************************************************
+ *
+ * iSCSI login
+ *
+ */
+
+/**
+ * Build iSCSI login request strings
+ *
+ * @v iscsi iSCSI session
+ *
+ * These are the initial set of strings sent in the first login
+ * request PDU. We want the following settings:
+ *
+ * HeaderDigest=None
+ * DataDigest=None
+ * MaxConnections is irrelevant; we make only one connection anyway
+ * InitialR2T=Yes [1]
+ * ImmediateData is irrelevant; we never send immediate data
+ * MaxRecvDataSegmentLength=8192 (default; we don't care) [3]
+ * MaxBurstLength=262144 (default; we don't care) [3]
+ * FirstBurstLength=262144 (default; we don't care)
+ * DefaultTime2Wait=0 [2]
+ * DefaultTime2Retain=0 [2]
+ * MaxOutstandingR2T=1
+ * DataPDUInOrder=Yes
+ * DataSequenceInOrder=Yes
+ * ErrorRecoveryLevel=0
+ *
+ * [1] InitialR2T has an OR resolution function, so the target may
+ * force us to use it. We therefore simplify our logic by always
+ * using it.
+ *
+ * [2] These ensure that we can safely start a new task once we have
+ * reconnected after a failure, without having to manually tidy up
+ * after the old one.
+ *
+ * [3] We are quite happy to use the RFC-defined default values for
+ * these parameters, but some targets (notably OpenSolaris)
+ * incorrectly assume a default value of zero, so we explicitly
+ * specify the default values.
+ */
+static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
+ void *data, size_t len ) {
+ unsigned int used = 0;
+ unsigned int i;
+
+ if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) {
+ used += ssnprintf ( data + used, len - used,
+ "InitiatorName=%s%c"
+ "TargetName=%s%c"
+ "SessionType=Normal%c"
+ "AuthMethod=CHAP,None%c",
+ iscsi_initiator_iqn(), 0,
+ iscsi->target_iqn, 0, 0, 0 );
+ }
+
+ if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) {
+ used += ssnprintf ( data + used, len - used, "CHAP_A=5%c", 0 );
+ }
+
+ if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) &&
+ iscsi->username ) {
+ used += ssnprintf ( data + used, len - used,
+ "CHAP_N=%s%cCHAP_R=0x",
+ iscsi->username, 0 );
+ for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) {
+ used += ssnprintf ( data + used, len - used, "%02x",
+ iscsi->chap.response[i] );
+ }
+ used += ssnprintf ( data + used, len - used, "%c", 0 );
+ }
+
+ if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) {
+ used += ssnprintf ( data + used, len - used,
+ "HeaderDigest=None%c"
+ "DataDigest=None%c"
+ "InitialR2T=Yes%c"
+ "MaxRecvDataSegmentLength=8192%c"
+ "MaxBurstLength=262144%c"
+ "DefaultTime2Wait=0%c"
+ "DefaultTime2Retain=0%c"
+ "MaxOutstandingR2T=1%c"
+ "DataPDUInOrder=Yes%c"
+ "DataSequenceInOrder=Yes%c"
+ "ErrorRecoveryLevel=0%c",
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+ }
+
+ return used;
+}
+
+/**
+ * Build iSCSI login request BHS
+ *
+ * @v iscsi iSCSI session
+ */
+static void iscsi_start_login ( struct iscsi_session *iscsi ) {
+ struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
+ int len;
+
+ /* Construct BHS and initiate transmission */
+ iscsi_start_tx ( iscsi );
+ request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
+ ISCSI_FLAG_IMMEDIATE );
+ request->flags = ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) |
+ ISCSI_LOGIN_FLAG_TRANSITION );
+ /* version_max and version_min left as zero */
+ len = iscsi_build_login_request_strings ( iscsi, NULL, 0 );
+ ISCSI_SET_LENGTHS ( request->lengths, 0, len );
+ request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
+ IANA_EN_FEN_SYSTEMS );
+ /* isid_iana_qual left as zero */
+ request->tsih = htons ( iscsi->tsih );
+ request->itt = htonl ( iscsi->itt );
+ /* cid left as zero */
+ request->cmdsn = htonl ( iscsi->cmdsn );
+ request->expstatsn = htonl ( iscsi->statsn + 1 );
+}
+
+/**
+ * Complete iSCSI login request PDU transmission
+ *
+ * @v iscsi iSCSI session
+ *
+ */
+static void iscsi_login_request_done ( struct iscsi_session *iscsi ) {
+
+ /* Clear any "strings to send" flags */
+ iscsi->status &= ~ISCSI_STATUS_STRINGS_MASK;
+
+ /* Free any dynamically allocated storage used for login */
+ chap_finish ( &iscsi->chap );
+}
+
+/**
+ * Transmit data segment of an iSCSI login request PDU
+ *
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ *
+ * For login requests, the data segment consists of the login strings.
+ */
+static int iscsi_tx_login_request ( struct iscsi_session *iscsi ) {
+ struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
+ struct io_buffer *iobuf;
+ size_t len;
+
+ len = ISCSI_DATA_LEN ( request->lengths );
+ iobuf = xfer_alloc_iob ( &iscsi->socket, len );
+ if ( ! iobuf )
+ return -ENOMEM;
+ iob_put ( iobuf, len );
+ iscsi_build_login_request_strings ( iscsi, iobuf->data, len );
+ return xfer_deliver_iob ( &iscsi->socket, iobuf );
+}
+
+/**
+ * Handle iSCSI TargetAddress text value
+ *
+ * @v iscsi iSCSI session
+ * @v value TargetAddress value
+ * @ret rc Return status code
+ */
+static int iscsi_handle_targetaddress_value ( struct iscsi_session *iscsi,
+ const char *value ) {
+ char *separator;
+
+ DBGC ( iscsi, "iSCSI %p will redirect to %s\n", iscsi, value );
+
+ /* Replace target address */
+ free ( iscsi->target_address );
+ iscsi->target_address = strdup ( value );
+ if ( ! iscsi->target_address )
+ return -ENOMEM;
+
+ /* Replace target port */
+ iscsi->target_port = htons ( ISCSI_PORT );
+ separator = strchr ( iscsi->target_address, ':' );
+ if ( separator ) {
+ *separator = '\0';
+ iscsi->target_port = strtoul ( ( separator + 1 ), NULL, 0 );
+ }
+
+ return 0;
+}
+
+/**
+ * Handle iSCSI AuthMethod text value
+ *
+ * @v iscsi iSCSI session
+ * @v value AuthMethod value
+ * @ret rc Return status code
+ */
+static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
+ const char *value ) {
+
+ /* If server requests CHAP, send the CHAP_A string */
+ if ( strcmp ( value, "CHAP" ) == 0 ) {
+ DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n",
+ iscsi );
+ iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM;
+ }
+ return 0;
+}
+
+/**
+ * Handle iSCSI CHAP_A text value
+ *
+ * @v iscsi iSCSI session
+ * @v value CHAP_A value
+ * @ret rc Return status code
+ */
+static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
+ const char *value ) {
+ int rc;
+
+ /* We only ever offer "5" (i.e. MD5) as an algorithm, so if
+ * the server responds with anything else it is a protocol
+ * violation.
+ */
+ if ( strcmp ( value, "5" ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p got invalid CHAP algorithm \"%s\"\n",
+ iscsi, value );
+ return -EPROTO;
+ }
+
+ /* Prepare for CHAP with MD5 */
+ if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
+ iscsi, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Handle iSCSI CHAP_I text value
+ *
+ * @v iscsi iSCSI session
+ * @v value CHAP_I value
+ * @ret rc Return status code
+ */
+static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
+ const char *value ) {
+ unsigned int identifier;
+ char *endp;
+
+ /* The CHAP identifier is an integer value */
+ identifier = strtoul ( value, &endp, 0 );
+ if ( *endp != '\0' ) {
+ DBGC ( iscsi, "iSCSI %p saw invalid CHAP identifier \"%s\"\n",
+ iscsi, value );
+ return -EPROTO;
+ }
+
+ /* Identifier and secret are the first two components of the
+ * challenge.
+ */
+ chap_set_identifier ( &iscsi->chap, identifier );
+ if ( iscsi->password ) {
+ chap_update ( &iscsi->chap, iscsi->password,
+ strlen ( iscsi->password ) );
+ }
+
+ return 0;
+}
+
+/**
+ * Handle iSCSI CHAP_C text value
+ *
+ * @v iscsi iSCSI session
+ * @v value CHAP_C value
+ * @ret rc Return status code
+ */
+static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
+ const char *value ) {
+ char buf[3];
+ char *endp;
+ uint8_t byte;
+
+ /* Check and strip leading "0x" */
+ if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
+ DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge \"%s\"\n",
+ iscsi, value );
+ }
+ value += 2;
+
+ /* Process challenge an octet at a time */
+ for ( ; ( value[0] && value[1] ) ; value += 2 ) {
+ memcpy ( buf, value, 2 );
+ buf[2] = 0;
+ byte = strtoul ( buf, &endp, 16 );
+ if ( *endp != '\0' ) {
+ DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge "
+ "byte \"%s\"\n", iscsi, buf );
+ return -EPROTO;
+ }
+ chap_update ( &iscsi->chap, &byte, sizeof ( byte ) );
+ }
+
+ /* Build CHAP response */
+ DBGC ( iscsi, "iSCSI %p sending CHAP response\n", iscsi );
+ chap_respond ( &iscsi->chap );
+ iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;
+
+ return 0;
+}
+
+/** An iSCSI text string that we want to handle */
+struct iscsi_string_type {
+ /** String key
+ *
+ * This is the portion up to and including the "=" sign,
+ * e.g. "InitiatorName=", "CHAP_A=", etc.
+ */
+ const char *key;
+ /** Handle iSCSI string value
+ *
+ * @v iscsi iSCSI session
+ * @v value iSCSI string value
+ * @ret rc Return status code
+ */
+ int ( * handle ) ( struct iscsi_session *iscsi, const char *value );
+};
+
+/** iSCSI text strings that we want to handle */
+static struct iscsi_string_type iscsi_string_types[] = {
+ { "TargetAddress=", iscsi_handle_targetaddress_value },
+ { "AuthMethod=", iscsi_handle_authmethod_value },
+ { "CHAP_A=", iscsi_handle_chap_a_value },
+ { "CHAP_I=", iscsi_handle_chap_i_value },
+ { "CHAP_C=", iscsi_handle_chap_c_value },
+ { NULL, NULL }
+};
+
+/**
+ * Handle iSCSI string
+ *
+ * @v iscsi iSCSI session
+ * @v string iSCSI string (in "key=value" format)
+ * @ret rc Return status code
+ */
+static int iscsi_handle_string ( struct iscsi_session *iscsi,
+ const char *string ) {
+ struct iscsi_string_type *type;
+ size_t key_len;
+ int rc;
+
+ for ( type = iscsi_string_types ; type->key ; type++ ) {
+ key_len = strlen ( type->key );
+ if ( strncmp ( string, type->key, key_len ) != 0 )
+ continue;
+ DBGC ( iscsi, "iSCSI %p handling %s\n", iscsi, string );
+ if ( ( rc = type->handle ( iscsi,
+ ( string + key_len ) ) ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p could not handle %s: %s\n",
+ iscsi, string, strerror ( rc ) );
+ return rc;
+ }
+ return 0;
+ }
+ DBGC ( iscsi, "iSCSI %p ignoring %s\n", iscsi, string );
+ return 0;
+}
+
+/**
+ * Handle iSCSI strings
+ *
+ * @v iscsi iSCSI session
+ * @v string iSCSI string buffer
+ * @v len Length of string buffer
+ * @ret rc Return status code
+ */
+static int iscsi_handle_strings ( struct iscsi_session *iscsi,
+ const char *strings, size_t len ) {
+ size_t string_len;
+ int rc;
+
+ /* Handle each string in turn, taking care not to overrun the
+ * data buffer in case of badly-terminated data.
+ */
+ while ( 1 ) {
+ string_len = ( strnlen ( strings, len ) + 1 );
+ if ( string_len > len )
+ break;
+ if ( ( rc = iscsi_handle_string ( iscsi, strings ) ) != 0 )
+ return rc;
+ strings += string_len;
+ len -= string_len;
+ }
+ return 0;
+}
+
+/**
+ * Receive PDU data into buffer
+ *
+ * @v iscsi iSCSI session
+ * @v data Data to receive
+ * @v len Length of data
+ * @ret rc Return status code
+ *
+ * This can be used when the RX PDU type handler wishes to buffer up
+ * all received data and process the PDU as a single unit. The caller
+ * is repsonsible for calling iscsi_rx_buffered_data_done() after
+ * processing the data.
+ */
+static int iscsi_rx_buffered_data ( struct iscsi_session *iscsi,
+ const void *data, size_t len ) {
+
+ /* Allocate buffer on first call */
+ if ( ! iscsi->rx_buffer ) {
+ iscsi->rx_buffer = malloc ( iscsi->rx_len );
+ if ( ! iscsi->rx_buffer )
+ return -ENOMEM;
+ }
+
+ /* Copy data to buffer */
+ assert ( ( iscsi->rx_offset + len ) <= iscsi->rx_len );
+ memcpy ( ( iscsi->rx_buffer + iscsi->rx_offset ), data, len );
+
+ return 0;
+}
+
+/**
+ * Receive data segment of an iSCSI login response PDU
+ *
+ * @v iscsi iSCSI session
+ * @v data Received data
+ * @v len Length of received data
+ * @v remaining Data remaining after this data
+ * @ret rc Return status code
+ */
+static int iscsi_rx_login_response ( struct iscsi_session *iscsi,
+ const void *data, size_t len,
+ size_t remaining ) {
+ struct iscsi_bhs_login_response *response
+ = &iscsi->rx_bhs.login_response;
+ int rc;
+
+ /* Buffer up the PDU data */
+ if ( ( rc = iscsi_rx_buffered_data ( iscsi, data, len ) ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p could not buffer login response: %s\n",
+ iscsi, strerror ( rc ) );
+ return rc;
+ }
+ if ( remaining )
+ return 0;
+
+ /* Process string data and discard string buffer */
+ if ( ( rc = iscsi_handle_strings ( iscsi, iscsi->rx_buffer,
+ iscsi->rx_len ) ) != 0 )
+ return rc;
+ iscsi_rx_buffered_data_done ( iscsi );
+
+ /* Check for login redirection */
+ if ( response->status_class == ISCSI_STATUS_REDIRECT ) {
+ DBGC ( iscsi, "iSCSI %p redirecting to new server\n", iscsi );
+ iscsi_close_connection ( iscsi, 0 );
+ if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p could not redirect: %s\n ",
+ iscsi, strerror ( rc ) );
+ return rc;
+ }
+ return 0;
+ }
+
+ /* Check for fatal errors */
+ if ( response->status_class != 0 ) {
+ DBGC ( iscsi, "iSCSI login failure: class %02x detail %02x\n",
+ response->status_class, response->status_detail );
+ iscsi->instant_rc = -EPERM;
+ return -EPERM;
+ }
+
+ /* Handle login transitions */
+ if ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) {
+ switch ( response->flags & ISCSI_LOGIN_NSG_MASK ) {
+ case ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION:
+ iscsi->status =
+ ( ISCSI_STATUS_OPERATIONAL_NEGOTIATION_PHASE |
+ ISCSI_STATUS_STRINGS_OPERATIONAL );
+ break;
+ case ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE:
+ iscsi->status = ISCSI_STATUS_FULL_FEATURE_PHASE;
+ break;
+ default:
+ DBGC ( iscsi, "iSCSI %p got invalid response flags "
+ "%02x\n", iscsi, response->flags );
+ return -EIO;
+ }
+ }
+
+ /* Send next login request PDU if we haven't reached the full
+ * feature phase yet.
+ */
+ if ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) !=
+ ISCSI_STATUS_FULL_FEATURE_PHASE ) {
+ iscsi_start_login ( iscsi );
+ return 0;
+ }
+
+ /* Reset retry count */
+ iscsi->retry_count = 0;
+
+ /* Record TSIH for future reference */
+ iscsi->tsih = ntohl ( response->tsih );
+
+ /* Send the actual SCSI command */
+ iscsi_start_command ( iscsi );
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * iSCSI to socket interface
+ *
+ */
+
+/**
+ * Start up a new TX PDU
+ *
+ * @v iscsi iSCSI session
+ *
+ * This initiates the process of sending a new PDU. Only one PDU may
+ * be in transit at any one time.
+ */
+static void iscsi_start_tx ( struct iscsi_session *iscsi ) {
+ assert ( iscsi->tx_state == ISCSI_TX_IDLE );
+
+ /* Initialise TX BHS */
+ memset ( &iscsi->tx_bhs, 0, sizeof ( iscsi->tx_bhs ) );
+
+ /* Flag TX engine to start transmitting */
+ iscsi->tx_state = ISCSI_TX_BHS;
+}
+
+/**
+ * Transmit nothing
+ *
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ */
+static int iscsi_tx_nothing ( struct iscsi_session *iscsi __unused ) {
+ return 0;
+}
+
+/**
+ * Transmit basic header segment of an iSCSI PDU
+ *
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ */
+static int iscsi_tx_bhs ( struct iscsi_session *iscsi ) {
+ return xfer_deliver_raw ( &iscsi->socket, &iscsi->tx_bhs,
+ sizeof ( iscsi->tx_bhs ) );
+}
+
+/**
+ * Transmit data segment of an iSCSI PDU
+ *
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ *
+ * Handle transmission of part of a PDU data segment. iscsi::tx_bhs
+ * will be valid when this is called.
+ */
+static int iscsi_tx_data ( struct iscsi_session *iscsi ) {
+ struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
+
+ switch ( common->opcode & ISCSI_OPCODE_MASK ) {
+ case ISCSI_OPCODE_DATA_OUT:
+ return iscsi_tx_data_out ( iscsi );
+ case ISCSI_OPCODE_LOGIN_REQUEST:
+ return iscsi_tx_login_request ( iscsi );
+ default:
+ /* Nothing to send in other states */
+ return 0;
+ }
+}
+
+/**
+ * Transmit data padding of an iSCSI PDU
+ *
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ *
+ * Handle transmission of any data padding in a PDU data segment.
+ * iscsi::tx_bhs will be valid when this is called.
+ */
+static int iscsi_tx_data_padding ( struct iscsi_session *iscsi ) {
+ static const char pad[] = { '\0', '\0', '\0' };
+ struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
+ size_t pad_len;
+
+ pad_len = ISCSI_DATA_PAD_LEN ( common->lengths );
+ if ( ! pad_len )
+ return 0;
+
+ return xfer_deliver_raw ( &iscsi->socket, pad, pad_len );
+}
+
+/**
+ * Complete iSCSI PDU transmission
+ *
+ * @v iscsi iSCSI session
+ *
+ * Called when a PDU has been completely transmitted and the TX state
+ * machine is about to enter the idle state. iscsi::tx_bhs will be
+ * valid for the just-completed PDU when this is called.
+ */
+static void iscsi_tx_done ( struct iscsi_session *iscsi ) {
+ struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
+
+ switch ( common->opcode & ISCSI_OPCODE_MASK ) {
+ case ISCSI_OPCODE_DATA_OUT:
+ iscsi_data_out_done ( iscsi );
+ case ISCSI_OPCODE_LOGIN_REQUEST:
+ iscsi_login_request_done ( iscsi );
+ default:
+ /* No action */
+ break;
+ }
+}
+
+/**
+ * Transmit iSCSI PDU
+ *
+ * @v iscsi iSCSI session
+ * @v buf Temporary data buffer
+ * @v len Length of temporary data buffer
+ *
+ * Constructs data to be sent for the current TX state
+ */
+static void iscsi_tx_step ( struct process *process ) {
+ struct iscsi_session *iscsi =
+ container_of ( process, struct iscsi_session, process );
+ struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
+ int ( * tx ) ( struct iscsi_session *iscsi );
+ enum iscsi_tx_state next_state;
+ size_t tx_len;
+ int rc;
+
+ /* Select fragment to transmit */
+ while ( 1 ) {
+ switch ( iscsi->tx_state ) {
+ case ISCSI_TX_IDLE:
+ /* Stop processing */
+ return;
+ case ISCSI_TX_BHS:
+ tx = iscsi_tx_bhs;
+ tx_len = sizeof ( iscsi->tx_bhs );
+ next_state = ISCSI_TX_AHS;
+ break;
+ case ISCSI_TX_AHS:
+ tx = iscsi_tx_nothing;
+ tx_len = 0;
+ next_state = ISCSI_TX_DATA;
+ break;
+ case ISCSI_TX_DATA:
+ tx = iscsi_tx_data;
+ tx_len = ISCSI_DATA_LEN ( common->lengths );
+ next_state = ISCSI_TX_DATA_PADDING;
+ break;
+ case ISCSI_TX_DATA_PADDING:
+ tx = iscsi_tx_data_padding;
+ tx_len = ISCSI_DATA_PAD_LEN ( common->lengths );
+ next_state = ISCSI_TX_IDLE;
+ break;
+ default:
+ assert ( 0 );
+ return;
+ }
+
+ /* Check for window availability, if needed */
+ if ( tx_len && ( xfer_window ( &iscsi->socket ) == 0 ) ) {
+ /* Cannot transmit at this point; stop processing */
+ return;
+ }
+
+ /* Transmit data */
+ if ( ( rc = tx ( iscsi ) ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p could not transmit: %s\n",
+ iscsi, strerror ( rc ) );
+ return;
+ }
+
+ /* Move to next state */
+ iscsi->tx_state = next_state;
+ if ( next_state == ISCSI_TX_IDLE )
+ iscsi_tx_done ( iscsi );
+ }
+}
+
+/**
+ * Receive basic header segment of an iSCSI PDU
+ *
+ * @v iscsi iSCSI session
+ * @v data Received data
+ * @v len Length of received data
+ * @v remaining Data remaining after this data
+ * @ret rc Return status code
+ *
+ * This fills in iscsi::rx_bhs with the data from the BHS portion of
+ * the received PDU.
+ */
+static int iscsi_rx_bhs ( struct iscsi_session *iscsi, const void *data,
+ size_t len, size_t remaining __unused ) {
+ memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
+ if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) {
+ DBGC ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n",
+ iscsi, iscsi->rx_bhs.common.opcode,
+ ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
+ }
+ return 0;
+}
+
+/**
+ * Discard portion of an iSCSI PDU.
+ *
+ * @v iscsi iSCSI session
+ * @v data Received data
+ * @v len Length of received data
+ * @v remaining Data remaining after this data
+ * @ret rc Return status code
+ *
+ * This discards data from a portion of a received PDU.
+ */
+static int iscsi_rx_discard ( struct iscsi_session *iscsi __unused,
+ const void *data __unused, size_t len __unused,
+ size_t remaining __unused ) {
+ /* Do nothing */
+ return 0;
+}
+
+/**
+ * Receive data segment of an iSCSI PDU
+ *
+ * @v iscsi iSCSI session
+ * @v data Received data
+ * @v len Length of received data
+ * @v remaining Data remaining after this data
+ * @ret rc Return status code
+ *
+ * Handle processing of part of a PDU data segment. iscsi::rx_bhs
+ * will be valid when this is called.
+ */
+static int iscsi_rx_data ( struct iscsi_session *iscsi, const void *data,
+ size_t len, size_t remaining ) {
+ struct iscsi_bhs_common_response *response
+ = &iscsi->rx_bhs.common_response;
+
+ /* Update cmdsn and statsn */
+ iscsi->cmdsn = ntohl ( response->expcmdsn );
+ iscsi->statsn = ntohl ( response->statsn );
+
+ switch ( response->opcode & ISCSI_OPCODE_MASK ) {
+ case ISCSI_OPCODE_LOGIN_RESPONSE:
+ return iscsi_rx_login_response ( iscsi, data, len, remaining );
+ case ISCSI_OPCODE_SCSI_RESPONSE:
+ return iscsi_rx_scsi_response ( iscsi, data, len, remaining );
+ case ISCSI_OPCODE_DATA_IN:
+ return iscsi_rx_data_in ( iscsi, data, len, remaining );
+ case ISCSI_OPCODE_R2T:
+ return iscsi_rx_r2t ( iscsi, data, len, remaining );
+ default:
+ if ( remaining )
+ return 0;
+ DBGC ( iscsi, "iSCSI %p unknown opcode %02x\n", iscsi,
+ response->opcode );
+ return -EOPNOTSUPP;
+ }
+}
+
+/**
+ * Receive new data
+ *
+ * @v socket Transport layer interface
+ * @v data Received data
+ * @v len Length of received data
+ * @ret rc Return status code
+ *
+ * This handles received PDUs. The receive strategy is to fill in
+ * iscsi::rx_bhs with the contents of the BHS portion of the PDU,
+ * throw away any AHS portion, and then process each part of the data
+ * portion as it arrives. The data processing routine therefore
+ * always has a full copy of the BHS available, even for portions of
+ * the data in different packets to the BHS.
+ */
+static int iscsi_socket_deliver_raw ( struct xfer_interface *socket,
+ const void *data, size_t len ) {
+ struct iscsi_session *iscsi =
+ container_of ( socket, struct iscsi_session, socket );
+ struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
+ int ( * rx ) ( struct iscsi_session *iscsi, const void *data,
+ size_t len, size_t remaining );
+ enum iscsi_rx_state next_state;
+ size_t frag_len;
+ size_t remaining;
+ int rc;
+
+ while ( 1 ) {
+ switch ( iscsi->rx_state ) {
+ case ISCSI_RX_BHS:
+ rx = iscsi_rx_bhs;
+ iscsi->rx_len = sizeof ( iscsi->rx_bhs );
+ next_state = ISCSI_RX_AHS;
+ break;
+ case ISCSI_RX_AHS:
+ rx = iscsi_rx_discard;
+ iscsi->rx_len = 4 * ISCSI_AHS_LEN ( common->lengths );
+ next_state = ISCSI_RX_DATA;
+ break;
+ case ISCSI_RX_DATA:
+ rx = iscsi_rx_data;
+ iscsi->rx_len = ISCSI_DATA_LEN ( common->lengths );
+ next_state = ISCSI_RX_DATA_PADDING;
+ break;
+ case ISCSI_RX_DATA_PADDING:
+ rx = iscsi_rx_discard;
+ iscsi->rx_len = ISCSI_DATA_PAD_LEN ( common->lengths );
+ next_state = ISCSI_RX_BHS;
+ break;
+ default:
+ assert ( 0 );
+ return -EINVAL;
+ }
+
+ frag_len = iscsi->rx_len - iscsi->rx_offset;
+ if ( frag_len > len )
+ frag_len = len;
+ remaining = iscsi->rx_len - iscsi->rx_offset - frag_len;
+ if ( ( rc = rx ( iscsi, data, frag_len, remaining ) ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p could not process received "
+ "data: %s\n", iscsi, strerror ( rc ) );
+ iscsi_close_connection ( iscsi, rc );
+ iscsi_scsi_done ( iscsi, rc );
+ return rc;
+ }
+
+ iscsi->rx_offset += frag_len;
+ data += frag_len;
+ len -= frag_len;
+
+ /* If all the data for this state has not yet been
+ * received, stay in this state for now.
+ */
+ if ( iscsi->rx_offset != iscsi->rx_len )
+ return 0;
+
+ iscsi->rx_state = next_state;
+ iscsi->rx_offset = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * Handle stream connection closure
+ *
+ * @v socket Transport layer interface
+ * @v rc Reason for close
+ *
+ */
+static void iscsi_socket_close ( struct xfer_interface *socket, int rc ) {
+ struct iscsi_session *iscsi =
+ container_of ( socket, struct iscsi_session, socket );
+
+ /* Even a graceful close counts as an error for iSCSI */
+ if ( ! rc )
+ rc = -ECONNRESET;
+
+ /* Close session cleanly */
+ iscsi_close_connection ( iscsi, rc );
+
+ /* Retry connection if within the retry limit, otherwise fail */
+ if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
+ DBGC ( iscsi, "iSCSI %p retrying connection (retry #%d)\n",
+ iscsi, iscsi->retry_count );
+ if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p could not reconnect: %s\n",
+ iscsi, strerror ( rc ) );
+ iscsi_scsi_done ( iscsi, rc );
+ }
+ } else {
+ DBGC ( iscsi, "iSCSI %p retry count exceeded\n", iscsi );
+ iscsi->instant_rc = rc;
+ iscsi_scsi_done ( iscsi, rc );
+ }
+}
+
+/**
+ * Handle redirection event
+ *
+ * @v socket Transport layer interface
+ * @v type Location type
+ * @v args Remaining arguments depend upon location type
+ * @ret rc Return status code
+ */
+static int iscsi_vredirect ( struct xfer_interface *socket, int type,
+ va_list args ) {
+ struct iscsi_session *iscsi =
+ container_of ( socket, struct iscsi_session, socket );
+ va_list tmp;
+ struct sockaddr *peer;
+
+ /* Intercept redirects to a LOCATION_SOCKET and record the IP
+ * address for the iBFT. This is a bit of a hack, but avoids
+ * inventing an ioctl()-style call to retrieve the socket
+ * address from a data-xfer interface.
+ */
+ if ( type == LOCATION_SOCKET ) {
+ va_copy ( tmp, args );
+ ( void ) va_arg ( tmp, int ); /* Discard "semantics" */
+ peer = va_arg ( tmp, struct sockaddr * );
+ memcpy ( &iscsi->target_sockaddr, peer,
+ sizeof ( iscsi->target_sockaddr ) );
+ va_end ( tmp );
+ }
+
+ return xfer_vopen ( socket, type, args );
+}
+
+
+/** iSCSI socket operations */
+static struct xfer_interface_operations iscsi_socket_operations = {
+ .close = iscsi_socket_close,
+ .vredirect = iscsi_vredirect,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = iscsi_socket_deliver_raw,
+};
+
+
+/****************************************************************************
+ *
+ * iSCSI command issuing
+ *
+ */
+
+/**
+ * Issue SCSI command
+ *
+ * @v scsi SCSI device
+ * @v command SCSI command
+ * @ret rc Return status code
+ */
+static int iscsi_command ( struct scsi_device *scsi,
+ struct scsi_command *command ) {
+ struct iscsi_session *iscsi =
+ container_of ( scsi->backend, struct iscsi_session, refcnt );
+ int rc;
+
+ /* Record SCSI command */
+ iscsi->command = command;
+
+ /* Abort immediately if we have a recorded permanent failure */
+ if ( iscsi->instant_rc ) {
+ rc = iscsi->instant_rc;
+ goto done;
+ }
+
+ /* Issue command or open connection as appropriate */
+ if ( iscsi->status ) {
+ iscsi_start_command ( iscsi );
+ } else {
+ if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 )
+ goto done;
+ }
+
+ /* Wait for command to complete */
+ iscsi->rc = -EINPROGRESS;
+ while ( iscsi->rc == -EINPROGRESS )
+ step();
+ rc = iscsi->rc;
+
+ done:
+ iscsi->command = NULL;
+ return rc;
+}
+
+static int iscsi_detached_command ( struct scsi_device *scsi __unused,
+ struct scsi_command *command __unused ) {
+ return -ENODEV;
+}
+
+/**
+ * Shut down iSCSI interface
+ *
+ * @v scsi SCSI device
+ */
+void iscsi_detach ( struct scsi_device *scsi ) {
+ struct iscsi_session *iscsi =
+ container_of ( scsi->backend, struct iscsi_session, refcnt );
+
+ xfer_nullify ( &iscsi->socket );
+ iscsi_close_connection ( iscsi, 0 );
+ process_del ( &iscsi->process );
+ scsi->command = iscsi_detached_command;
+ ref_put ( scsi->backend );
+ scsi->backend = NULL;
+}
+
+/****************************************************************************
+ *
+ * Instantiator
+ *
+ */
+
+/** iSCSI root path components (as per RFC4173) */
+enum iscsi_root_path_component {
+ RP_LITERAL = 0,
+ RP_SERVERNAME,
+ RP_PROTOCOL,
+ RP_PORT,
+ RP_LUN,
+ RP_TARGETNAME,
+ NUM_RP_COMPONENTS
+};
+
+/**
+ * Parse iSCSI LUN
+ *
+ * @v iscsi iSCSI session
+ * @v lun_string LUN string representation (as per RFC4173)
+ * @ret rc Return status code
+ */
+static int iscsi_parse_lun ( struct iscsi_session *iscsi,
+ const char *lun_string ) {
+ char *p = ( char * ) lun_string;
+ union {
+ uint64_t u64;
+ uint16_t u16[4];
+ } lun;
+ int i;
+
+ /* Empty LUN; assume LUN 0 */
+ if ( ! *lun_string )
+ return 0;
+
+ for ( i = 0 ; i < 4 ; i++ ) {
+ lun.u16[i] = strtoul ( p, &p, 16 );
+ if ( *p != '-' )
+ return -EINVAL;
+ p++;
+ }
+ if ( *p )
+ return -EINVAL;
+
+ iscsi->lun = lun.u64;
+ return 0;
+}
+
+/**
+ * Parse iSCSI root path
+ *
+ * @v iscsi iSCSI session
+ * @v root_path iSCSI root path (as per RFC4173)
+ * @ret rc Return status code
+ */
+static int iscsi_parse_root_path ( struct iscsi_session *iscsi,
+ const char *root_path ) {
+ char rp_copy[ strlen ( root_path ) + 1 ];
+ char *rp_comp[NUM_RP_COMPONENTS];
+ char *rp = rp_copy;
+ int i = 0;
+ int rc;
+
+ /* Split root path into component parts */
+ strcpy ( rp_copy, root_path );
+ while ( 1 ) {
+ rp_comp[i++] = rp;
+ if ( i == NUM_RP_COMPONENTS )
+ break;
+ for ( ; *rp != ':' ; rp++ ) {
+ if ( ! *rp ) {
+ DBGC ( iscsi, "iSCSI %p root path \"%s\" "
+ "too short\n", iscsi, root_path );
+ return -EINVAL;
+ }
+ }
+ *(rp++) = '\0';
+ }
+
+ /* Use root path components to configure iSCSI session */
+ iscsi->target_address = strdup ( rp_comp[RP_SERVERNAME] );
+ if ( ! iscsi->target_address )
+ return -ENOMEM;
+ iscsi->target_port = strtoul ( rp_comp[RP_PORT], NULL, 10 );
+ if ( ! iscsi->target_port )
+ iscsi->target_port = ISCSI_PORT;
+ if ( ( rc = iscsi_parse_lun ( iscsi, rp_comp[RP_LUN] ) ) != 0 ) {
+ DBGC ( iscsi, "iSCSI %p invalid LUN \"%s\"\n",
+ iscsi, rp_comp[RP_LUN] );
+ return rc;
+ }
+ iscsi->target_iqn = strdup ( rp_comp[RP_TARGETNAME] );
+ if ( ! iscsi->target_iqn )
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * Set iSCSI authentication details
+ *
+ * @v iscsi iSCSI session
+ * @v username Username, if any
+ * @v password Password, if any
+ * @ret rc Return status code
+ */
+static int iscsi_set_auth ( struct iscsi_session *iscsi,
+ const char *username, const char *password ) {
+
+ if ( username ) {
+ iscsi->username = strdup ( username );
+ if ( ! iscsi->username )
+ return -ENOMEM;
+ }
+
+ if ( password ) {
+ iscsi->password = strdup ( password );
+ if ( ! iscsi->password )
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * Attach iSCSI interface
+ *
+ * @v scsi SCSI device
+ * @v root_path iSCSI root path (as per RFC4173)
+ * @ret rc Return status code
+ */
+int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) {
+ struct iscsi_session *iscsi;
+ int rc;
+
+ /* Allocate and initialise structure */
+ iscsi = zalloc ( sizeof ( *iscsi ) );
+ if ( ! iscsi )
+ return -ENOMEM;
+ iscsi->refcnt.free = iscsi_free;
+ xfer_init ( &iscsi->socket, &iscsi_socket_operations, &iscsi->refcnt );
+ process_init ( &iscsi->process, iscsi_tx_step, &iscsi->refcnt );
+
+ /* Parse root path */
+ if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 )
+ goto err;
+ /* Set fields not specified by root path */
+ if ( ( rc = iscsi_set_auth ( iscsi, iscsi_username,
+ iscsi_password ) ) != 0 )
+ goto err;
+
+ /* Sanity checks */
+ if ( ! iscsi->target_address ) {
+ DBGC ( iscsi, "iSCSI %p does not yet support discovery\n",
+ iscsi );
+ rc = -ENOTSUP;
+ goto err;
+ }
+ if ( ! iscsi->target_iqn ) {
+ DBGC ( iscsi, "iSCSI %p no target address supplied in %s\n",
+ iscsi, root_path );
+ rc = -EINVAL;
+ goto err;
+ }
+
+ /* Attach parent interface, mortalise self, and return */
+ scsi->backend = ref_get ( &iscsi->refcnt );
+ scsi->command = iscsi_command;
+ scsi->lun = iscsi->lun;
+ ref_put ( &iscsi->refcnt );
+ return 0;
+
+ err:
+ ref_put ( &iscsi->refcnt );
+ return rc;
+}
+
+/****************************************************************************
+ *
+ * Settings
+ *
+ */
+
+/** iSCSI initiator IQN setting */
+struct setting initiator_iqn_setting __setting = {
+ .name = "initiator-iqn",
+ .description = "iSCSI initiator name",
+ .tag = DHCP_ISCSI_INITIATOR_IQN,
+ .type = &setting_type_string,
+};
+
+/** An iSCSI string setting */
+struct iscsi_string_setting {
+ /** Setting */
+ struct setting *setting;
+ /** String to update */
+ char **string;
+ /** String prefix */
+ const char *prefix;
+};
+
+/** iSCSI string settings */
+static struct iscsi_string_setting iscsi_string_settings[] = {
+ {
+ .setting = &initiator_iqn_setting,
+ .string = &iscsi_explicit_initiator_iqn,
+ .prefix = "",
+ },
+ {
+ .setting = &username_setting,
+ .string = &iscsi_username,
+ .prefix = "",
+ },
+ {
+ .setting = &password_setting,
+ .string = &iscsi_password,
+ .prefix = "",
+ },
+ {
+ .setting = &hostname_setting,
+ .string = &iscsi_default_initiator_iqn,
+ .prefix = "iqn.2000-09.org.etherboot:",
+ },
+};
+
+/**
+ * Apply iSCSI setting
+ *
+ * @v setting iSCSI string setting
+ * @ret rc Return status code
+ */
+static int apply_iscsi_string_setting ( struct iscsi_string_setting *setting ){
+ size_t prefix_len;
+ int setting_len;
+ size_t len;
+ int check_len;
+ char *p;
+
+ /* Free old string */
+ free ( *setting->string );
+ *setting->string = NULL;
+
+ /* Allocate new string */
+ prefix_len = strlen ( setting->prefix );
+ setting_len = fetch_setting_len ( NULL, setting->setting );
+ if ( setting_len < 0 ) {
+ /* Missing settings are not errors; leave strings as NULL */
+ return 0;
+ }
+ len = ( prefix_len + setting_len + 1 );
+ p = *setting->string = malloc ( len );
+ if ( ! p )
+ return -ENOMEM;
+
+ /* Fill new string */
+ strcpy ( p, setting->prefix );
+ check_len = fetch_string_setting ( NULL, setting->setting,
+ ( p + prefix_len ),
+ ( len - prefix_len ) );
+ assert ( check_len == setting_len );
+
+ return 0;
+}
+
+/**
+ * Apply iSCSI settings
+ *
+ * @ret rc Return status code
+ */
+static int apply_iscsi_settings ( void ) {
+ struct iscsi_string_setting *setting;
+ unsigned int i;
+ int rc;
+
+ for ( i = 0 ; i < ( sizeof ( iscsi_string_settings ) /
+ sizeof ( iscsi_string_settings[0] ) ) ; i++ ) {
+ setting = &iscsi_string_settings[i];
+ if ( ( rc = apply_iscsi_string_setting ( setting ) ) != 0 ) {
+ DBG ( "iSCSI could not apply setting %s\n",
+ setting->setting->name );
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/** iSCSI settings applicator */
+struct settings_applicator iscsi_settings_applicator __settings_applicator = {
+ .apply = apply_iscsi_settings,
+};
+
+/****************************************************************************
+ *
+ * Initiator name
+ *
+ */
+
+/**
+ * Get iSCSI initiator IQN
+ *
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ */
+const char * iscsi_initiator_iqn ( void ) {
+
+ if ( iscsi_explicit_initiator_iqn )
+ return iscsi_explicit_initiator_iqn;
+ if ( iscsi_default_initiator_iqn )
+ return iscsi_default_initiator_iqn;
+ return "iqn.2000-09.org.etherboot:UNKNOWN";
+}
diff --git a/gpxe/src/net/tcpip.c b/gpxe/src/net/tcpip.c
new file mode 100644
index 00000000..1bc8d1a3
--- /dev/null
+++ b/gpxe/src/net/tcpip.c
@@ -0,0 +1,145 @@
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/tables.h>
+#include <gpxe/tcpip.h>
+
+/** @file
+ *
+ * Transport-network layer interface
+ *
+ * This file contains functions and utilities for the
+ * TCP/IP transport-network layer interface
+ */
+
+/** Registered network-layer protocols that support TCP/IP */
+static struct tcpip_net_protocol tcpip_net_protocols[0]
+ __table_start ( struct tcpip_net_protocol, tcpip_net_protocols );
+static struct tcpip_net_protocol tcpip_net_protocols_end[0]
+ __table_end ( struct tcpip_net_protocol, tcpip_net_protocols );
+
+/** Registered transport-layer protocols that support TCP/IP */
+static struct tcpip_protocol tcpip_protocols[0]
+ __table_start ( struct tcpip_protocol, tcpip_protocols );
+static struct tcpip_protocol tcpip_protocols_end[0]
+ __table_end ( struct tcpip_protocol, tcpip_protocols );
+
+/** Process a received TCP/IP packet
+ *
+ * @v iobuf I/O buffer
+ * @v tcpip_proto Transport-layer protocol number
+ * @v st_src Partially-filled source address
+ * @v st_dest Partially-filled destination address
+ * @v pshdr_csum Pseudo-header checksum
+ * @ret rc Return status code
+ *
+ * This function expects a transport-layer segment from the network
+ * layer. The network layer should fill in as much as it can of the
+ * source and destination addresses (i.e. it should fill in the
+ * address family and the network-layer addresses, but leave the ports
+ * and the rest of the structures as zero).
+ */
+int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto,
+ struct sockaddr_tcpip *st_src,
+ struct sockaddr_tcpip *st_dest,
+ uint16_t pshdr_csum ) {
+ struct tcpip_protocol *tcpip;
+
+ /* Hand off packet to the appropriate transport-layer protocol */
+ for ( tcpip = tcpip_protocols; tcpip < tcpip_protocols_end; tcpip++ ) {
+ if ( tcpip->tcpip_proto == tcpip_proto ) {
+ DBG ( "TCP/IP received %s packet\n", tcpip->name );
+ return tcpip->rx ( iobuf, st_src, st_dest, pshdr_csum );
+ }
+ }
+
+ DBG ( "Unrecognised TCP/IP protocol %d\n", tcpip_proto );
+ free_iob ( iobuf );
+ return -EPROTONOSUPPORT;
+}
+
+/** Transmit a TCP/IP packet
+ *
+ * @v iobuf I/O buffer
+ * @v tcpip_protocol Transport-layer protocol
+ * @v st_dest Destination address
+ * @v netdev Network device to use if no route found, or NULL
+ * @v trans_csum Transport-layer checksum to complete, or NULL
+ * @ret rc Return status code
+ */
+int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip_protocol,
+ struct sockaddr_tcpip *st_dest, struct net_device *netdev,
+ uint16_t *trans_csum ) {
+ struct tcpip_net_protocol *tcpip_net;
+
+ /* Hand off packet to the appropriate network-layer protocol */
+ for ( tcpip_net = tcpip_net_protocols ;
+ tcpip_net < tcpip_net_protocols_end ; tcpip_net++ ) {
+ if ( tcpip_net->sa_family == st_dest->st_family ) {
+ DBG ( "TCP/IP sending %s packet\n", tcpip_net->name );
+ return tcpip_net->tx ( iobuf, tcpip_protocol, st_dest,
+ netdev, trans_csum );
+ }
+ }
+
+ DBG ( "Unrecognised TCP/IP address family %d\n", st_dest->st_family );
+ free_iob ( iobuf );
+ return -EAFNOSUPPORT;
+}
+
+/**
+ * Calculate continued TCP/IP checkum
+ *
+ * @v partial Checksum of already-summed data, in network byte order
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret cksum Updated checksum, in network byte order
+ *
+ * Calculates a TCP/IP-style 16-bit checksum over the data block. The
+ * checksum is returned in network byte order.
+ *
+ * This function may be used to add new data to an existing checksum.
+ * The function assumes that both the old data and the new data start
+ * on even byte offsets; if this is not the case then you will need to
+ * byte-swap either the input partial checksum, the output checksum,
+ * or both. Deciding which to swap is left as an exercise for the
+ * interested reader.
+ */
+uint16_t tcpip_continue_chksum ( uint16_t partial, const void *data,
+ size_t len ) {
+ unsigned int cksum = ( ( ~partial ) & 0xffff );
+ unsigned int value;
+ unsigned int i;
+
+ for ( i = 0 ; i < len ; i++ ) {
+ value = * ( ( uint8_t * ) data + i );
+ if ( i & 1 ) {
+ /* Odd bytes: swap on little-endian systems */
+ value = be16_to_cpu ( value );
+ } else {
+ /* Even bytes: swap on big-endian systems */
+ value = le16_to_cpu ( value );
+ }
+ cksum += value;
+ if ( cksum > 0xffff )
+ cksum -= 0xffff;
+ }
+
+ return ( ~cksum );
+}
+
+/**
+ * Calculate TCP/IP checkum
+ *
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret cksum Checksum, in network byte order
+ *
+ * Calculates a TCP/IP-style 16-bit checksum over the data block. The
+ * checksum is returned in network byte order.
+ */
+uint16_t tcpip_chksum ( const void *data, size_t len ) {
+ return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len );
+}
diff --git a/gpxe/src/net/tls.c b/gpxe/src/net/tls.c
new file mode 100644
index 00000000..834686fb
--- /dev/null
+++ b/gpxe/src/net/tls.c
@@ -0,0 +1,1731 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Transport Layer Security Protocol
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/hmac.h>
+#include <gpxe/md5.h>
+#include <gpxe/sha1.h>
+#include <gpxe/aes.h>
+#include <gpxe/rsa.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/filter.h>
+#include <gpxe/tls.h>
+
+static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
+ const void *data, size_t len );
+static void tls_clear_cipher ( struct tls_session *tls,
+ struct tls_cipherspec *cipherspec );
+
+/**
+ * Free TLS session
+ *
+ * @v refcnt Reference counter
+ */
+static void free_tls ( struct refcnt *refcnt ) {
+ struct tls_session *tls =
+ container_of ( refcnt, struct tls_session, refcnt );
+
+ /* Free dynamically-allocated resources */
+ tls_clear_cipher ( tls, &tls->tx_cipherspec );
+ tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
+ tls_clear_cipher ( tls, &tls->rx_cipherspec );
+ tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
+ free ( tls->rsa_mod );
+ free ( tls->rsa_pub_exp );
+ free ( tls->rx_data );
+
+ /* Free TLS structure itself */
+ free ( tls );
+}
+
+/**
+ * Finish with TLS session
+ *
+ * @v tls TLS session
+ * @v rc Status code
+ */
+static void tls_close ( struct tls_session *tls, int rc ) {
+
+ /* Remove process */
+ process_del ( &tls->process );
+
+ /* Close ciphertext and plaintext streams */
+ xfer_nullify ( &tls->cipherstream.xfer );
+ xfer_close ( &tls->cipherstream.xfer, rc );
+ xfer_nullify ( &tls->plainstream.xfer );
+ xfer_close ( &tls->plainstream.xfer, rc );
+}
+
+/******************************************************************************
+ *
+ * Random number generation
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Generate random data
+ *
+ * @v data Buffer to fill
+ * @v len Length of buffer
+ */
+static void tls_generate_random ( void *data, size_t len ) {
+ /* FIXME: Some real random data source would be nice... */
+ memset ( data, 0x01, len );
+}
+
+/**
+ * Update HMAC with a list of ( data, len ) pairs
+ *
+ * @v digest Hash function to use
+ * @v digest_ctx Digest context
+ * @v args ( data, len ) pairs of data, terminated by NULL
+ */
+static void tls_hmac_update_va ( struct crypto_algorithm *digest,
+ void *digest_ctx, va_list args ) {
+ void *data;
+ size_t len;
+
+ while ( ( data = va_arg ( args, void * ) ) ) {
+ len = va_arg ( args, size_t );
+ hmac_update ( digest, digest_ctx, data, len );
+ }
+}
+
+/**
+ * Generate secure pseudo-random data using a single hash function
+ *
+ * @v tls TLS session
+ * @v digest Hash function to use
+ * @v secret Secret
+ * @v secret_len Length of secret
+ * @v out Output buffer
+ * @v out_len Length of output buffer
+ * @v seeds ( data, len ) pairs of seed data, terminated by NULL
+ */
+static void tls_p_hash_va ( struct tls_session *tls,
+ struct crypto_algorithm *digest,
+ void *secret, size_t secret_len,
+ void *out, size_t out_len,
+ va_list seeds ) {
+ uint8_t secret_copy[secret_len];
+ uint8_t digest_ctx[digest->ctxsize];
+ uint8_t digest_ctx_partial[digest->ctxsize];
+ uint8_t a[digest->digestsize];
+ uint8_t out_tmp[digest->digestsize];
+ size_t frag_len = digest->digestsize;
+ va_list tmp;
+
+ /* Copy the secret, in case HMAC modifies it */
+ memcpy ( secret_copy, secret, secret_len );
+ secret = secret_copy;
+ DBGC2 ( tls, "TLS %p %s secret:\n", tls, digest->name );
+ DBGC2_HD ( tls, secret, secret_len );
+
+ /* Calculate A(1) */
+ hmac_init ( digest, digest_ctx, secret, &secret_len );
+ va_copy ( tmp, seeds );
+ tls_hmac_update_va ( digest, digest_ctx, tmp );
+ va_end ( tmp );
+ hmac_final ( digest, digest_ctx, secret, &secret_len, a );
+ DBGC2 ( tls, "TLS %p %s A(1):\n", tls, digest->name );
+ DBGC2_HD ( tls, &a, sizeof ( a ) );
+
+ /* Generate as much data as required */
+ while ( out_len ) {
+ /* Calculate output portion */
+ hmac_init ( digest, digest_ctx, secret, &secret_len );
+ hmac_update ( digest, digest_ctx, a, sizeof ( a ) );
+ memcpy ( digest_ctx_partial, digest_ctx, digest->ctxsize );
+ va_copy ( tmp, seeds );
+ tls_hmac_update_va ( digest, digest_ctx, tmp );
+ va_end ( tmp );
+ hmac_final ( digest, digest_ctx,
+ secret, &secret_len, out_tmp );
+
+ /* Copy output */
+ if ( frag_len > out_len )
+ frag_len = out_len;
+ memcpy ( out, out_tmp, frag_len );
+ DBGC2 ( tls, "TLS %p %s output:\n", tls, digest->name );
+ DBGC2_HD ( tls, out, frag_len );
+
+ /* Calculate A(i) */
+ hmac_final ( digest, digest_ctx_partial,
+ secret, &secret_len, a );
+ DBGC2 ( tls, "TLS %p %s A(n):\n", tls, digest->name );
+ DBGC2_HD ( tls, &a, sizeof ( a ) );
+
+ out += frag_len;
+ out_len -= frag_len;
+ }
+}
+
+/**
+ * Generate secure pseudo-random data
+ *
+ * @v tls TLS session
+ * @v secret Secret
+ * @v secret_len Length of secret
+ * @v out Output buffer
+ * @v out_len Length of output buffer
+ * @v ... ( data, len ) pairs of seed data, terminated by NULL
+ */
+static void tls_prf ( struct tls_session *tls, void *secret, size_t secret_len,
+ void *out, size_t out_len, ... ) {
+ va_list seeds;
+ va_list tmp;
+ size_t subsecret_len;
+ void *md5_secret;
+ void *sha1_secret;
+ uint8_t out_md5[out_len];
+ uint8_t out_sha1[out_len];
+ unsigned int i;
+
+ va_start ( seeds, out_len );
+
+ /* Split secret into two, with an overlap of up to one byte */
+ subsecret_len = ( ( secret_len + 1 ) / 2 );
+ md5_secret = secret;
+ sha1_secret = ( secret + secret_len - subsecret_len );
+
+ /* Calculate MD5 portion */
+ va_copy ( tmp, seeds );
+ tls_p_hash_va ( tls, &md5_algorithm, md5_secret, subsecret_len,
+ out_md5, out_len, seeds );
+ va_end ( tmp );
+
+ /* Calculate SHA1 portion */
+ va_copy ( tmp, seeds );
+ tls_p_hash_va ( tls, &sha1_algorithm, sha1_secret, subsecret_len,
+ out_sha1, out_len, seeds );
+ va_end ( tmp );
+
+ /* XOR the two portions together into the final output buffer */
+ for ( i = 0 ; i < out_len ; i++ ) {
+ *( ( uint8_t * ) out + i ) = ( out_md5[i] ^ out_sha1[i] );
+ }
+
+ va_end ( seeds );
+}
+
+/**
+ * Generate secure pseudo-random data
+ *
+ * @v secret Secret
+ * @v secret_len Length of secret
+ * @v out Output buffer
+ * @v out_len Length of output buffer
+ * @v label String literal label
+ * @v ... ( data, len ) pairs of seed data
+ */
+#define tls_prf_label( tls, secret, secret_len, out, out_len, label, ... ) \
+ tls_prf ( (tls), (secret), (secret_len), (out), (out_len), \
+ label, ( sizeof ( label ) - 1 ), __VA_ARGS__, NULL )
+
+/******************************************************************************
+ *
+ * Secret management
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Generate master secret
+ *
+ * @v tls TLS session
+ *
+ * The pre-master secret and the client and server random values must
+ * already be known.
+ */
+static void tls_generate_master_secret ( struct tls_session *tls ) {
+ DBGC ( tls, "TLS %p pre-master-secret:\n", tls );
+ DBGC_HD ( tls, &tls->pre_master_secret,
+ sizeof ( tls->pre_master_secret ) );
+ DBGC ( tls, "TLS %p client random bytes:\n", tls );
+ DBGC_HD ( tls, &tls->client_random, sizeof ( tls->server_random ) );
+ DBGC ( tls, "TLS %p server random bytes:\n", tls );
+ DBGC_HD ( tls, &tls->server_random, sizeof ( tls->server_random ) );
+
+ tls_prf_label ( tls, tls->pre_master_secret,
+ sizeof ( tls->pre_master_secret ),
+ tls->master_secret, sizeof ( tls->master_secret ),
+ "master secret",
+ tls->client_random, sizeof ( tls->client_random ),
+ tls->server_random, sizeof ( tls->server_random ) );
+
+ DBGC ( tls, "TLS %p generated master secret:\n", tls );
+ DBGC_HD ( tls, &tls->master_secret, sizeof ( tls->master_secret ) );
+}
+
+/**
+ * Generate key material
+ *
+ * @v tls TLS session
+ *
+ * The master secret must already be known.
+ */
+static int tls_generate_keys ( struct tls_session *tls ) {
+ struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending;
+ struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending;
+ size_t hash_size = tx_cipherspec->digest->digestsize;
+ size_t key_size = tx_cipherspec->key_len;
+ size_t iv_size = tx_cipherspec->cipher->blocksize;
+ size_t total = ( 2 * ( hash_size + key_size + iv_size ) );
+ uint8_t key_block[total];
+ uint8_t *key;
+ int rc;
+
+ /* Generate key block */
+ tls_prf_label ( tls, tls->master_secret, sizeof ( tls->master_secret ),
+ key_block, sizeof ( key_block ), "key expansion",
+ tls->server_random, sizeof ( tls->server_random ),
+ tls->client_random, sizeof ( tls->client_random ) );
+
+ /* Split key block into portions */
+ key = key_block;
+
+ /* TX MAC secret */
+ memcpy ( tx_cipherspec->mac_secret, key, hash_size );
+ DBGC ( tls, "TLS %p TX MAC secret:\n", tls );
+ DBGC_HD ( tls, key, hash_size );
+ key += hash_size;
+
+ /* RX MAC secret */
+ memcpy ( rx_cipherspec->mac_secret, key, hash_size );
+ DBGC ( tls, "TLS %p RX MAC secret:\n", tls );
+ DBGC_HD ( tls, key, hash_size );
+ key += hash_size;
+
+ /* TX key */
+ if ( ( rc = cipher_setkey ( tx_cipherspec->cipher,
+ tx_cipherspec->cipher_ctx,
+ key, key_size ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not set TX key: %s\n",
+ tls, strerror ( rc ) );
+ return rc;
+ }
+ DBGC ( tls, "TLS %p TX key:\n", tls );
+ DBGC_HD ( tls, key, key_size );
+ key += key_size;
+
+ /* RX key */
+ if ( ( rc = cipher_setkey ( rx_cipherspec->cipher,
+ rx_cipherspec->cipher_ctx,
+ key, key_size ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not set TX key: %s\n",
+ tls, strerror ( rc ) );
+ return rc;
+ }
+
+ /* FIXME: AES needs to be fixed to not require this */
+ AES_convert_key ( rx_cipherspec->cipher_ctx );
+
+ DBGC ( tls, "TLS %p RX key:\n", tls );
+ DBGC_HD ( tls, key, key_size );
+ key += key_size;
+
+ /* TX initialisation vector */
+ cipher_setiv ( tx_cipherspec->cipher, tx_cipherspec->cipher_ctx, key );
+ DBGC ( tls, "TLS %p TX IV:\n", tls );
+ DBGC_HD ( tls, key, iv_size );
+ key += iv_size;
+
+ /* RX initialisation vector */
+ cipher_setiv ( rx_cipherspec->cipher, rx_cipherspec->cipher_ctx, key );
+ DBGC ( tls, "TLS %p RX IV:\n", tls );
+ DBGC_HD ( tls, key, iv_size );
+ key += iv_size;
+
+ assert ( ( key_block + total ) == key );
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Cipher suite management
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Clear cipher suite
+ *
+ * @v cipherspec TLS cipher specification
+ */
+static void tls_clear_cipher ( struct tls_session *tls __unused,
+ struct tls_cipherspec *cipherspec ) {
+ free ( cipherspec->dynamic );
+ memset ( cipherspec, 0, sizeof ( cipherspec ) );
+ cipherspec->pubkey = &crypto_null;
+ cipherspec->cipher = &crypto_null;
+ cipherspec->digest = &crypto_null;
+}
+
+/**
+ * Set cipher suite
+ *
+ * @v tls TLS session
+ * @v cipherspec TLS cipher specification
+ * @v pubkey Public-key encryption elgorithm
+ * @v cipher Bulk encryption cipher algorithm
+ * @v digest MAC digest algorithm
+ * @v key_len Key length
+ * @ret rc Return status code
+ */
+static int tls_set_cipher ( struct tls_session *tls,
+ struct tls_cipherspec *cipherspec,
+ struct crypto_algorithm *pubkey,
+ struct crypto_algorithm *cipher,
+ struct crypto_algorithm *digest,
+ size_t key_len ) {
+ size_t total;
+ void *dynamic;
+
+ /* Clear out old cipher contents, if any */
+ tls_clear_cipher ( tls, cipherspec );
+
+ /* Allocate dynamic storage */
+ total = ( pubkey->ctxsize + 2 * cipher->ctxsize + digest->digestsize );
+ dynamic = malloc ( total );
+ if ( ! dynamic ) {
+ DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto "
+ "context\n", tls, total );
+ return -ENOMEM;
+ }
+ memset ( dynamic, 0, total );
+
+ /* Assign storage */
+ cipherspec->dynamic = dynamic;
+ cipherspec->pubkey_ctx = dynamic; dynamic += pubkey->ctxsize;
+ cipherspec->cipher_ctx = dynamic; dynamic += cipher->ctxsize;
+ cipherspec->cipher_next_ctx = dynamic; dynamic += cipher->ctxsize;
+ cipherspec->mac_secret = dynamic; dynamic += digest->digestsize;
+ assert ( ( cipherspec->dynamic + total ) == dynamic );
+
+ /* Store parameters */
+ cipherspec->pubkey = pubkey;
+ cipherspec->cipher = cipher;
+ cipherspec->digest = digest;
+ cipherspec->key_len = key_len;
+
+ return 0;
+}
+
+/**
+ * Select next cipher suite
+ *
+ * @v tls TLS session
+ * @v cipher_suite Cipher suite specification
+ * @ret rc Return status code
+ */
+static int tls_select_cipher ( struct tls_session *tls,
+ unsigned int cipher_suite ) {
+ struct crypto_algorithm *pubkey = &crypto_null;
+ struct crypto_algorithm *cipher = &crypto_null;
+ struct crypto_algorithm *digest = &crypto_null;
+ unsigned int key_len = 0;
+ int rc;
+
+ switch ( cipher_suite ) {
+ case htons ( TLS_RSA_WITH_AES_128_CBC_SHA ):
+ key_len = ( 128 / 8 );
+ cipher = &aes_algorithm;
+ digest = &sha1_algorithm;
+ break;
+ case htons ( TLS_RSA_WITH_AES_256_CBC_SHA ):
+ key_len = ( 256 / 8 );
+ cipher = &aes_algorithm;
+ digest = &sha1_algorithm;
+ break;
+ default:
+ DBGC ( tls, "TLS %p does not support cipher %04x\n",
+ tls, ntohs ( cipher_suite ) );
+ return -ENOTSUP;
+ }
+
+ /* Set ciphers */
+ if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending, pubkey,
+ cipher, digest, key_len ) ) != 0 )
+ return rc;
+ if ( ( rc = tls_set_cipher ( tls, &tls->rx_cipherspec_pending, pubkey,
+ cipher, digest, key_len ) ) != 0 )
+ return rc;
+
+ DBGC ( tls, "TLS %p selected %s-%s-%d-%s\n", tls,
+ pubkey->name, cipher->name, ( key_len * 8 ), digest->name );
+
+ return 0;
+}
+
+/**
+ * Activate next cipher suite
+ *
+ * @v tls TLS session
+ * @v pending Pending cipher specification
+ * @v active Active cipher specification to replace
+ * @ret rc Return status code
+ */
+static int tls_change_cipher ( struct tls_session *tls,
+ struct tls_cipherspec *pending,
+ struct tls_cipherspec *active ) {
+
+ /* Sanity check */
+ if ( /* FIXME (when pubkey is not hard-coded to RSA):
+ * ( pending->pubkey == &crypto_null ) || */
+ ( pending->cipher == &crypto_null ) ||
+ ( pending->digest == &crypto_null ) ) {
+ DBGC ( tls, "TLS %p refusing to use null cipher\n", tls );
+ return -ENOTSUP;
+ }
+
+ tls_clear_cipher ( tls, active );
+ memswap ( active, pending, sizeof ( *active ) );
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Handshake verification
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Add handshake record to verification hash
+ *
+ * @v tls TLS session
+ * @v data Handshake record
+ * @v len Length of handshake record
+ */
+static void tls_add_handshake ( struct tls_session *tls,
+ const void *data, size_t len ) {
+
+ digest_update ( &md5_algorithm, tls->handshake_md5_ctx, data, len );
+ digest_update ( &sha1_algorithm, tls->handshake_sha1_ctx, data, len );
+}
+
+/**
+ * Calculate handshake verification hash
+ *
+ * @v tls TLS session
+ * @v out Output buffer
+ *
+ * Calculates the MD5+SHA1 digest over all handshake messages seen so
+ * far.
+ */
+static void tls_verify_handshake ( struct tls_session *tls, void *out ) {
+ struct crypto_algorithm *md5 = &md5_algorithm;
+ struct crypto_algorithm *sha1 = &sha1_algorithm;
+ uint8_t md5_ctx[md5->ctxsize];
+ uint8_t sha1_ctx[sha1->ctxsize];
+ void *md5_digest = out;
+ void *sha1_digest = ( out + md5->digestsize );
+
+ memcpy ( md5_ctx, tls->handshake_md5_ctx, sizeof ( md5_ctx ) );
+ memcpy ( sha1_ctx, tls->handshake_sha1_ctx, sizeof ( sha1_ctx ) );
+ digest_final ( md5, md5_ctx, md5_digest );
+ digest_final ( sha1, sha1_ctx, sha1_digest );
+}
+
+/******************************************************************************
+ *
+ * Record handling
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Transmit Handshake record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_send_handshake ( struct tls_session *tls,
+ void *data, size_t len ) {
+
+ /* Add to handshake digest */
+ tls_add_handshake ( tls, data, len );
+
+ /* Send record */
+ return tls_send_plaintext ( tls, TLS_TYPE_HANDSHAKE, data, len );
+}
+
+/**
+ * Transmit Client Hello record
+ *
+ * @v tls TLS session
+ * @ret rc Return status code
+ */
+static int tls_send_client_hello ( struct tls_session *tls ) {
+ struct {
+ uint32_t type_length;
+ uint16_t version;
+ uint8_t random[32];
+ uint8_t session_id_len;
+ uint16_t cipher_suite_len;
+ uint16_t cipher_suites[2];
+ uint8_t compression_methods_len;
+ uint8_t compression_methods[1];
+ } __attribute__ (( packed )) hello;
+
+ memset ( &hello, 0, sizeof ( hello ) );
+ hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) |
+ htonl ( sizeof ( hello ) -
+ sizeof ( hello.type_length ) ) );
+ hello.version = htons ( TLS_VERSION_TLS_1_0 );
+ memcpy ( &hello.random, tls->client_random, sizeof ( hello.random ) );
+ hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) );
+ hello.cipher_suites[0] = htons ( TLS_RSA_WITH_AES_128_CBC_SHA );
+ hello.cipher_suites[1] = htons ( TLS_RSA_WITH_AES_256_CBC_SHA );
+ hello.compression_methods_len = sizeof ( hello.compression_methods );
+
+ return tls_send_handshake ( tls, &hello, sizeof ( hello ) );
+}
+
+/**
+ * Transmit Client Key Exchange record
+ *
+ * @v tls TLS session
+ * @ret rc Return status code
+ */
+static int tls_send_client_key_exchange ( struct tls_session *tls ) {
+ /* FIXME: Hack alert */
+ RSA_CTX *rsa_ctx;
+ RSA_pub_key_new ( &rsa_ctx, tls->rsa_mod, tls->rsa_mod_len,
+ tls->rsa_pub_exp, tls->rsa_pub_exp_len );
+ struct {
+ uint32_t type_length;
+ uint16_t encrypted_pre_master_secret_len;
+ uint8_t encrypted_pre_master_secret[rsa_ctx->num_octets];
+ } __attribute__ (( packed )) key_xchg;
+
+ memset ( &key_xchg, 0, sizeof ( key_xchg ) );
+ key_xchg.type_length = ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) |
+ htonl ( sizeof ( key_xchg ) -
+ sizeof ( key_xchg.type_length ) ) );
+ key_xchg.encrypted_pre_master_secret_len
+ = htons ( sizeof ( key_xchg.encrypted_pre_master_secret ) );
+
+ /* FIXME: Hack alert */
+ DBGC ( tls, "RSA encrypting plaintext, modulus, exponent:\n" );
+ DBGC_HD ( tls, &tls->pre_master_secret,
+ sizeof ( tls->pre_master_secret ) );
+ DBGC_HD ( tls, tls->rsa_mod, tls->rsa_mod_len );
+ DBGC_HD ( tls, tls->rsa_pub_exp, tls->rsa_pub_exp_len );
+ RSA_encrypt ( rsa_ctx, tls->pre_master_secret,
+ sizeof ( tls->pre_master_secret ),
+ key_xchg.encrypted_pre_master_secret, 0 );
+ DBGC ( tls, "RSA encrypt done. Ciphertext:\n" );
+ DBGC_HD ( tls, &key_xchg.encrypted_pre_master_secret,
+ sizeof ( key_xchg.encrypted_pre_master_secret ) );
+ RSA_free ( rsa_ctx );
+
+
+ return tls_send_handshake ( tls, &key_xchg, sizeof ( key_xchg ) );
+}
+
+/**
+ * Transmit Change Cipher record
+ *
+ * @v tls TLS session
+ * @ret rc Return status code
+ */
+static int tls_send_change_cipher ( struct tls_session *tls ) {
+ static const uint8_t change_cipher[1] = { 1 };
+ return tls_send_plaintext ( tls, TLS_TYPE_CHANGE_CIPHER,
+ change_cipher, sizeof ( change_cipher ) );
+}
+
+/**
+ * Transmit Finished record
+ *
+ * @v tls TLS session
+ * @ret rc Return status code
+ */
+static int tls_send_finished ( struct tls_session *tls ) {
+ struct {
+ uint32_t type_length;
+ uint8_t verify_data[12];
+ } __attribute__ (( packed )) finished;
+ uint8_t digest[MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE];
+
+ memset ( &finished, 0, sizeof ( finished ) );
+ finished.type_length = ( cpu_to_le32 ( TLS_FINISHED ) |
+ htonl ( sizeof ( finished ) -
+ sizeof ( finished.type_length ) ) );
+ tls_verify_handshake ( tls, digest );
+ tls_prf_label ( tls, tls->master_secret, sizeof ( tls->master_secret ),
+ finished.verify_data, sizeof ( finished.verify_data ),
+ "client finished", digest, sizeof ( digest ) );
+
+ return tls_send_handshake ( tls, &finished, sizeof ( finished ) );
+}
+
+/**
+ * Receive new Change Cipher record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_change_cipher ( struct tls_session *tls,
+ void *data, size_t len ) {
+ int rc;
+
+ if ( ( len != 1 ) || ( *( ( uint8_t * ) data ) != 1 ) ) {
+ DBGC ( tls, "TLS %p received invalid Change Cipher\n", tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL;
+ }
+
+ if ( ( rc = tls_change_cipher ( tls, &tls->rx_cipherspec_pending,
+ &tls->rx_cipherspec ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not activate RX cipher: %s\n",
+ tls, strerror ( rc ) );
+ return rc;
+ }
+ tls->rx_seq = ~( ( uint64_t ) 0 );
+
+ return 0;
+}
+
+/**
+ * Receive new Alert record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_alert ( struct tls_session *tls, void *data, size_t len ) {
+ struct {
+ uint8_t level;
+ uint8_t description;
+ char next[0];
+ } __attribute__ (( packed )) *alert = data;
+ void *end = alert->next;
+
+ /* Sanity check */
+ if ( end != ( data + len ) ) {
+ DBGC ( tls, "TLS %p received overlength Alert\n", tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL;
+ }
+
+ switch ( alert->level ) {
+ case TLS_ALERT_WARNING:
+ DBGC ( tls, "TLS %p received warning alert %d\n",
+ tls, alert->description );
+ return 0;
+ case TLS_ALERT_FATAL:
+ DBGC ( tls, "TLS %p received fatal alert %d\n",
+ tls, alert->description );
+ return -EPERM;
+ default:
+ DBGC ( tls, "TLS %p received unknown alert level %d"
+ "(alert %d)\n", tls, alert->level, alert->description );
+ return -EIO;
+ }
+}
+
+/**
+ * Receive new Server Hello record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_server_hello ( struct tls_session *tls,
+ void *data, size_t len ) {
+ struct {
+ uint32_t type_length;
+ uint16_t version;
+ uint8_t random[32];
+ uint8_t session_id_len;
+ char next[0];
+ } __attribute__ (( packed )) *hello_a = data;
+ struct {
+ uint8_t session_id[hello_a->session_id_len];
+ uint16_t cipher_suite;
+ uint8_t compression_method;
+ char next[0];
+ } __attribute__ (( packed )) *hello_b = ( void * ) &hello_a->next;
+ void *end = hello_b->next;
+ int rc;
+
+ /* Sanity check */
+ if ( end != ( data + len ) ) {
+ DBGC ( tls, "TLS %p received overlength Server Hello\n", tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL;
+ }
+
+ /* Check protocol version */
+ if ( ntohs ( hello_a->version ) < TLS_VERSION_TLS_1_0 ) {
+ DBGC ( tls, "TLS %p does not support protocol version %d.%d\n",
+ tls, ( ntohs ( hello_a->version ) >> 8 ),
+ ( ntohs ( hello_a->version ) & 0xff ) );
+ return -ENOTSUP;
+ }
+
+ /* Copy out server random bytes */
+ memcpy ( tls->server_random, hello_a->random,
+ sizeof ( tls->server_random ) );
+
+ /* Select cipher suite */
+ if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 )
+ return rc;
+
+ /* Generate secrets */
+ tls_generate_master_secret ( tls );
+ if ( ( rc = tls_generate_keys ( tls ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Receive new Certificate record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_certificate ( struct tls_session *tls,
+ void *data, size_t len ) {
+ struct {
+ uint32_t type_length;
+ uint8_t length[3];
+ uint8_t first_cert_length[3];
+ uint8_t asn1_start[0];
+ } __attribute__ (( packed )) *certificate = data;
+ uint8_t *cert = certificate->asn1_start;
+ int offset = 0;
+
+ /* FIXME */
+ (void) len;
+
+ if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(cert, &offset, ASN1_EXPLICIT_TAG) ||
+ asn1_skip_obj(cert, &offset, ASN1_INTEGER) ||
+ asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
+ asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
+ asn1_next_obj(cert, &offset, ASN1_BIT_STRING) < 0) {
+ DBGC ( tls, "TLS %p invalid certificate\n", tls );
+ DBGC_HD ( tls, cert + offset, 64 );
+ return -EPERM;
+ }
+
+ offset++;
+
+ if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) {
+ DBGC ( tls, "TLS %p invalid certificate\n", tls );
+ DBGC_HD ( tls, cert + offset, 64 );
+ return -EPERM;
+ }
+
+ tls->rsa_mod_len = asn1_get_int(cert, &offset, &tls->rsa_mod);
+ tls->rsa_pub_exp_len = asn1_get_int(cert, &offset, &tls->rsa_pub_exp);
+
+ DBGC_HD ( tls, tls->rsa_mod, tls->rsa_mod_len );
+ DBGC_HD ( tls, tls->rsa_pub_exp, tls->rsa_pub_exp_len );
+
+ return 0;
+}
+
+/**
+ * Receive new Server Hello Done record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_server_hello_done ( struct tls_session *tls,
+ void *data, size_t len ) {
+ struct {
+ uint32_t type_length;
+ char next[0];
+ } __attribute__ (( packed )) *hello_done = data;
+ void *end = hello_done->next;
+
+ /* Sanity check */
+ if ( end != ( data + len ) ) {
+ DBGC ( tls, "TLS %p received overlength Server Hello Done\n",
+ tls );
+ DBGC_HD ( tls, data, len );
+ return -EINVAL;
+ }
+
+ /* Check that we are ready to send the Client Key Exchange */
+ if ( tls->tx_state != TLS_TX_NONE ) {
+ DBGC ( tls, "TLS %p received Server Hello Done while in "
+ "TX state %d\n", tls, tls->tx_state );
+ return -EIO;
+ }
+
+ /* Start sending the Client Key Exchange */
+ tls->tx_state = TLS_TX_CLIENT_KEY_EXCHANGE;
+
+ return 0;
+}
+
+/**
+ * Receive new Finished record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_finished ( struct tls_session *tls,
+ void *data, size_t len ) {
+
+ /* FIXME: Handle this properly */
+ tls->tx_state = TLS_TX_DATA;
+ ( void ) data;
+ ( void ) len;
+ return 0;
+}
+
+/**
+ * Receive new Handshake record
+ *
+ * @v tls TLS session
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_handshake ( struct tls_session *tls,
+ void *data, size_t len ) {
+ uint8_t *type = data;
+ int rc;
+
+ switch ( *type ) {
+ case TLS_SERVER_HELLO:
+ rc = tls_new_server_hello ( tls, data, len );
+ break;
+ case TLS_CERTIFICATE:
+ rc = tls_new_certificate ( tls, data, len );
+ break;
+ case TLS_SERVER_HELLO_DONE:
+ rc = tls_new_server_hello_done ( tls, data, len );
+ break;
+ case TLS_FINISHED:
+ rc = tls_new_finished ( tls, data, len );
+ break;
+ default:
+ DBGC ( tls, "TLS %p ignoring handshake type %d\n",
+ tls, *type );
+ rc = 0;
+ break;
+ }
+
+ /* Add to handshake digest (except for Hello Requests, which
+ * are explicitly excludede).
+ */
+ if ( *type != TLS_HELLO_REQUEST )
+ tls_add_handshake ( tls, data, len );
+
+ return rc;
+}
+
+/**
+ * Receive new record
+ *
+ * @v tls TLS session
+ * @v type Record type
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_new_record ( struct tls_session *tls,
+ unsigned int type, void *data, size_t len ) {
+
+ switch ( type ) {
+ case TLS_TYPE_CHANGE_CIPHER:
+ return tls_new_change_cipher ( tls, data, len );
+ case TLS_TYPE_ALERT:
+ return tls_new_alert ( tls, data, len );
+ case TLS_TYPE_HANDSHAKE:
+ return tls_new_handshake ( tls, data, len );
+ case TLS_TYPE_DATA:
+ return xfer_deliver_raw ( &tls->plainstream.xfer, data, len );
+ default:
+ /* RFC4346 says that we should just ignore unknown
+ * record types.
+ */
+ DBGC ( tls, "TLS %p ignoring record type %d\n", tls, type );
+ return 0;
+ }
+}
+
+/******************************************************************************
+ *
+ * Record encryption/decryption
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Calculate HMAC
+ *
+ * @v tls TLS session
+ * @v cipherspec Cipher specification
+ * @v seq Sequence number
+ * @v tlshdr TLS header
+ * @v data Data
+ * @v len Length of data
+ * @v mac HMAC to fill in
+ */
+static void tls_hmac ( struct tls_session *tls __unused,
+ struct tls_cipherspec *cipherspec,
+ uint64_t seq, struct tls_header *tlshdr,
+ const void *data, size_t len, void *hmac ) {
+ struct crypto_algorithm *digest = cipherspec->digest;
+ uint8_t digest_ctx[digest->ctxsize];
+
+ hmac_init ( digest, digest_ctx, cipherspec->mac_secret,
+ &digest->digestsize );
+ seq = cpu_to_be64 ( seq );
+ hmac_update ( digest, digest_ctx, &seq, sizeof ( seq ) );
+ hmac_update ( digest, digest_ctx, tlshdr, sizeof ( *tlshdr ) );
+ hmac_update ( digest, digest_ctx, data, len );
+ hmac_final ( digest, digest_ctx, cipherspec->mac_secret,
+ &digest->digestsize, hmac );
+}
+
+/**
+ * Allocate and assemble stream-ciphered record from data and MAC portions
+ *
+ * @v tls TLS session
+ * @ret data Data
+ * @ret len Length of data
+ * @ret digest MAC digest
+ * @ret plaintext_len Length of plaintext record
+ * @ret plaintext Allocated plaintext record
+ */
+static void * __malloc tls_assemble_stream ( struct tls_session *tls,
+ const void *data, size_t len,
+ void *digest, size_t *plaintext_len ) {
+ size_t mac_len = tls->tx_cipherspec.digest->digestsize;
+ void *plaintext;
+ void *content;
+ void *mac;
+
+ /* Calculate stream-ciphered struct length */
+ *plaintext_len = ( len + mac_len );
+
+ /* Allocate stream-ciphered struct */
+ plaintext = malloc ( *plaintext_len );
+ if ( ! plaintext )
+ return NULL;
+ content = plaintext;
+ mac = ( content + len );
+
+ /* Fill in stream-ciphered struct */
+ memcpy ( content, data, len );
+ memcpy ( mac, digest, mac_len );
+
+ return plaintext;
+}
+
+/**
+ * Allocate and assemble block-ciphered record from data and MAC portions
+ *
+ * @v tls TLS session
+ * @ret data Data
+ * @ret len Length of data
+ * @ret digest MAC digest
+ * @ret plaintext_len Length of plaintext record
+ * @ret plaintext Allocated plaintext record
+ */
+static void * tls_assemble_block ( struct tls_session *tls,
+ const void *data, size_t len,
+ void *digest, size_t *plaintext_len ) {
+ size_t blocksize = tls->tx_cipherspec.cipher->blocksize;
+ size_t iv_len = blocksize;
+ size_t mac_len = tls->tx_cipherspec.digest->digestsize;
+ size_t padding_len;
+ void *plaintext;
+ void *iv;
+ void *content;
+ void *mac;
+ void *padding;
+
+ /* FIXME: TLSv1.1 has an explicit IV */
+ iv_len = 0;
+
+ /* Calculate block-ciphered struct length */
+ padding_len = ( ( blocksize - 1 ) & -( iv_len + len + mac_len + 1 ) );
+ *plaintext_len = ( iv_len + len + mac_len + padding_len + 1 );
+
+ /* Allocate block-ciphered struct */
+ plaintext = malloc ( *plaintext_len );
+ if ( ! plaintext )
+ return NULL;
+ iv = plaintext;
+ content = ( iv + iv_len );
+ mac = ( content + len );
+ padding = ( mac + mac_len );
+
+ /* Fill in block-ciphered struct */
+ memset ( iv, 0, iv_len );
+ memcpy ( content, data, len );
+ memcpy ( mac, digest, mac_len );
+ memset ( padding, padding_len, ( padding_len + 1 ) );
+
+ return plaintext;
+}
+
+/**
+ * Send plaintext record
+ *
+ * @v tls TLS session
+ * @v type Record type
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
+ const void *data, size_t len ) {
+ struct tls_header plaintext_tlshdr;
+ struct tls_header *tlshdr;
+ struct tls_cipherspec *cipherspec = &tls->tx_cipherspec;
+ void *plaintext = NULL;
+ size_t plaintext_len;
+ struct io_buffer *ciphertext = NULL;
+ size_t ciphertext_len;
+ size_t mac_len = cipherspec->digest->digestsize;
+ uint8_t mac[mac_len];
+ int rc;
+
+ /* Construct header */
+ plaintext_tlshdr.type = type;
+ plaintext_tlshdr.version = htons ( TLS_VERSION_TLS_1_0 );
+ plaintext_tlshdr.length = htons ( len );
+
+ /* Calculate MAC */
+ tls_hmac ( tls, cipherspec, tls->tx_seq, &plaintext_tlshdr,
+ data, len, mac );
+
+ /* Allocate and assemble plaintext struct */
+ if ( is_stream_cipher ( cipherspec->cipher ) ) {
+ plaintext = tls_assemble_stream ( tls, data, len, mac,
+ &plaintext_len );
+ } else {
+ plaintext = tls_assemble_block ( tls, data, len, mac,
+ &plaintext_len );
+ }
+ if ( ! plaintext ) {
+ DBGC ( tls, "TLS %p could not allocate %zd bytes for "
+ "plaintext\n", tls, plaintext_len );
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ DBGC2 ( tls, "Sending plaintext data:\n" );
+ DBGC2_HD ( tls, plaintext, plaintext_len );
+
+ /* Allocate ciphertext */
+ ciphertext_len = ( sizeof ( *tlshdr ) + plaintext_len );
+ ciphertext = xfer_alloc_iob ( &tls->cipherstream.xfer,
+ ciphertext_len );
+ if ( ! ciphertext ) {
+ DBGC ( tls, "TLS %p could not allocate %zd bytes for "
+ "ciphertext\n", tls, ciphertext_len );
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ /* Assemble ciphertext */
+ tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) );
+ tlshdr->type = type;
+ tlshdr->version = htons ( TLS_VERSION_TLS_1_0 );
+ tlshdr->length = htons ( plaintext_len );
+ memcpy ( cipherspec->cipher_next_ctx, cipherspec->cipher_ctx,
+ cipherspec->cipher->ctxsize );
+ if ( ( rc = cipher_encrypt ( cipherspec->cipher,
+ cipherspec->cipher_next_ctx, plaintext,
+ iob_put ( ciphertext, plaintext_len ),
+ plaintext_len ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not encrypt: %s\n",
+ tls, strerror ( rc ) );
+ DBGC_HD ( tls, plaintext, plaintext_len );
+ goto done;
+ }
+
+ /* Free plaintext as soon as possible to conserve memory */
+ free ( plaintext );
+ plaintext = NULL;
+
+ /* Send ciphertext */
+ rc = xfer_deliver_iob ( &tls->cipherstream.xfer, ciphertext );
+ ciphertext = NULL;
+ if ( rc != 0 ) {
+ DBGC ( tls, "TLS %p could not deliver ciphertext: %s\n",
+ tls, strerror ( rc ) );
+ goto done;
+ }
+
+ /* Update TX state machine to next record */
+ tls->tx_seq += 1;
+ memcpy ( tls->tx_cipherspec.cipher_ctx,
+ tls->tx_cipherspec.cipher_next_ctx,
+ tls->tx_cipherspec.cipher->ctxsize );
+
+ done:
+ free ( plaintext );
+ free_iob ( ciphertext );
+ return rc;
+}
+
+/**
+ * Split stream-ciphered record into data and MAC portions
+ *
+ * @v tls TLS session
+ * @v plaintext Plaintext record
+ * @v plaintext_len Length of record
+ * @ret data Data
+ * @ret len Length of data
+ * @ret digest MAC digest
+ * @ret rc Return status code
+ */
+static int tls_split_stream ( struct tls_session *tls,
+ void *plaintext, size_t plaintext_len,
+ void **data, size_t *len, void **digest ) {
+ void *content;
+ size_t content_len;
+ void *mac;
+ size_t mac_len;
+
+ /* Decompose stream-ciphered data */
+ mac_len = tls->rx_cipherspec.digest->digestsize;
+ if ( plaintext_len < mac_len ) {
+ DBGC ( tls, "TLS %p received underlength record\n", tls );
+ DBGC_HD ( tls, plaintext, plaintext_len );
+ return -EINVAL;
+ }
+ content_len = ( plaintext_len - mac_len );
+ content = plaintext;
+ mac = ( content + content_len );
+
+ /* Fill in return values */
+ *data = content;
+ *len = content_len;
+ *digest = mac;
+
+ return 0;
+}
+
+/**
+ * Split block-ciphered record into data and MAC portions
+ *
+ * @v tls TLS session
+ * @v plaintext Plaintext record
+ * @v plaintext_len Length of record
+ * @ret data Data
+ * @ret len Length of data
+ * @ret digest MAC digest
+ * @ret rc Return status code
+ */
+static int tls_split_block ( struct tls_session *tls,
+ void *plaintext, size_t plaintext_len,
+ void **data, size_t *len,
+ void **digest ) {
+ void *iv;
+ size_t iv_len;
+ void *content;
+ size_t content_len;
+ void *mac;
+ size_t mac_len;
+ void *padding;
+ size_t padding_len;
+ unsigned int i;
+
+ /* Decompose block-ciphered data */
+ if ( plaintext_len < 1 ) {
+ DBGC ( tls, "TLS %p received underlength record\n", tls );
+ DBGC_HD ( tls, plaintext, plaintext_len );
+ return -EINVAL;
+ }
+ iv_len = tls->rx_cipherspec.cipher->blocksize;
+
+ /* FIXME: TLSv1.1 uses an explicit IV */
+ iv_len = 0;
+
+ mac_len = tls->rx_cipherspec.digest->digestsize;
+ padding_len = *( ( uint8_t * ) ( plaintext + plaintext_len - 1 ) );
+ if ( plaintext_len < ( iv_len + mac_len + padding_len + 1 ) ) {
+ DBGC ( tls, "TLS %p received underlength record\n", tls );
+ DBGC_HD ( tls, plaintext, plaintext_len );
+ return -EINVAL;
+ }
+ content_len = ( plaintext_len - iv_len - mac_len - padding_len - 1 );
+ iv = plaintext;
+ content = ( iv + iv_len );
+ mac = ( content + content_len );
+ padding = ( mac + mac_len );
+
+ /* Verify padding bytes */
+ for ( i = 0 ; i < padding_len ; i++ ) {
+ if ( *( ( uint8_t * ) ( padding + i ) ) != padding_len ) {
+ DBGC ( tls, "TLS %p received bad padding\n", tls );
+ DBGC_HD ( tls, plaintext, plaintext_len );
+ return -EINVAL;
+ }
+ }
+
+ /* Fill in return values */
+ *data = content;
+ *len = content_len;
+ *digest = mac;
+
+ return 0;
+}
+
+/**
+ * Receive new ciphertext record
+ *
+ * @v tls TLS session
+ * @v tlshdr Record header
+ * @v ciphertext Ciphertext record
+ * @ret rc Return status code
+ */
+static int tls_new_ciphertext ( struct tls_session *tls,
+ struct tls_header *tlshdr, void *ciphertext ) {
+ struct tls_header plaintext_tlshdr;
+ struct tls_cipherspec *cipherspec = &tls->rx_cipherspec;
+ size_t record_len = ntohs ( tlshdr->length );
+ void *plaintext = NULL;
+ void *data;
+ size_t len;
+ void *mac;
+ size_t mac_len = cipherspec->digest->digestsize;
+ uint8_t verify_mac[mac_len];
+ int rc;
+
+ /* Allocate buffer for plaintext */
+ plaintext = malloc ( record_len );
+ if ( ! plaintext ) {
+ DBGC ( tls, "TLS %p could not allocate %zd bytes for "
+ "decryption buffer\n", tls, record_len );
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ /* Decrypt the record */
+ if ( ( rc = cipher_decrypt ( cipherspec->cipher,
+ cipherspec->cipher_ctx, ciphertext,
+ plaintext, record_len ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not decrypt: %s\n",
+ tls, strerror ( rc ) );
+ DBGC_HD ( tls, ciphertext, record_len );
+ goto done;
+ }
+
+ /* Split record into content and MAC */
+ if ( is_stream_cipher ( cipherspec->cipher ) ) {
+ if ( ( rc = tls_split_stream ( tls, plaintext, record_len,
+ &data, &len, &mac ) ) != 0 )
+ goto done;
+ } else {
+ if ( ( rc = tls_split_block ( tls, plaintext, record_len,
+ &data, &len, &mac ) ) != 0 )
+ goto done;
+ }
+
+ /* Verify MAC */
+ plaintext_tlshdr.type = tlshdr->type;
+ plaintext_tlshdr.version = tlshdr->version;
+ plaintext_tlshdr.length = htons ( len );
+ tls_hmac ( tls, cipherspec, tls->rx_seq, &plaintext_tlshdr,
+ data, len, verify_mac);
+ if ( memcmp ( mac, verify_mac, mac_len ) != 0 ) {
+ DBGC ( tls, "TLS %p failed MAC verification\n", tls );
+ DBGC_HD ( tls, plaintext, record_len );
+ goto done;
+ }
+
+ DBGC2 ( tls, "Received plaintext data:\n" );
+ DBGC2_HD ( tls, data, len );
+
+ /* Process plaintext record */
+ if ( ( rc = tls_new_record ( tls, tlshdr->type, data, len ) ) != 0 )
+ goto done;
+
+ rc = 0;
+ done:
+ free ( plaintext );
+ return rc;
+}
+
+/******************************************************************************
+ *
+ * Plaintext stream operations
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Close interface
+ *
+ * @v xfer Plainstream data transfer interface
+ * @v rc Reason for close
+ */
+static void tls_plainstream_close ( struct xfer_interface *xfer, int rc ) {
+ struct tls_session *tls =
+ container_of ( xfer, struct tls_session, plainstream.xfer );
+
+ tls_close ( tls, rc );
+}
+
+/**
+ * Check flow control window
+ *
+ * @v xfer Plainstream data transfer interface
+ * @ret len Length of window
+ */
+static size_t tls_plainstream_window ( struct xfer_interface *xfer ) {
+ struct tls_session *tls =
+ container_of ( xfer, struct tls_session, plainstream.xfer );
+
+ /* Block window unless we are ready to accept data */
+ if ( tls->tx_state != TLS_TX_DATA )
+ return 0;
+
+ return filter_window ( xfer );
+}
+
+/**
+ * Deliver datagram as raw data
+ *
+ * @v xfer Plainstream data transfer interface
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+static int tls_plainstream_deliver_raw ( struct xfer_interface *xfer,
+ const void *data, size_t len ) {
+ struct tls_session *tls =
+ container_of ( xfer, struct tls_session, plainstream.xfer );
+
+ /* Refuse unless we are ready to accept data */
+ if ( tls->tx_state != TLS_TX_DATA )
+ return -ENOTCONN;
+
+ return tls_send_plaintext ( tls, TLS_TYPE_DATA, data, len );
+}
+
+/** TLS plaintext stream operations */
+static struct xfer_interface_operations tls_plainstream_operations = {
+ .close = tls_plainstream_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = tls_plainstream_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = tls_plainstream_deliver_raw,
+};
+
+/******************************************************************************
+ *
+ * Ciphertext stream operations
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Close interface
+ *
+ * @v xfer Plainstream data transfer interface
+ * @v rc Reason for close
+ */
+static void tls_cipherstream_close ( struct xfer_interface *xfer, int rc ) {
+ struct tls_session *tls =
+ container_of ( xfer, struct tls_session, cipherstream.xfer );
+
+ tls_close ( tls, rc );
+}
+
+/**
+ * Handle received TLS header
+ *
+ * @v tls TLS session
+ * @ret rc Returned status code
+ */
+static int tls_newdata_process_header ( struct tls_session *tls ) {
+ size_t data_len = ntohs ( tls->rx_header.length );
+
+ /* Allocate data buffer now that we know the length */
+ assert ( tls->rx_data == NULL );
+ tls->rx_data = malloc ( data_len );
+ if ( ! tls->rx_data ) {
+ DBGC ( tls, "TLS %p could not allocate %zd bytes "
+ "for receive buffer\n", tls, data_len );
+ return -ENOMEM;
+ }
+
+ /* Move to data state */
+ tls->rx_state = TLS_RX_DATA;
+
+ return 0;
+}
+
+/**
+ * Handle received TLS data payload
+ *
+ * @v tls TLS session
+ * @ret rc Returned status code
+ */
+static int tls_newdata_process_data ( struct tls_session *tls ) {
+ int rc;
+
+ /* Process record */
+ if ( ( rc = tls_new_ciphertext ( tls, &tls->rx_header,
+ tls->rx_data ) ) != 0 )
+ return rc;
+
+ /* Increment RX sequence number */
+ tls->rx_seq += 1;
+
+ /* Free data buffer */
+ free ( tls->rx_data );
+ tls->rx_data = NULL;
+
+ /* Return to header state */
+ tls->rx_state = TLS_RX_HEADER;
+
+ return 0;
+}
+
+/**
+ * Receive new ciphertext
+ *
+ * @v app Stream application
+ * @v data Data received
+ * @v len Length of received data
+ * @ret rc Return status code
+ */
+static int tls_cipherstream_deliver_raw ( struct xfer_interface *xfer,
+ const void *data, size_t len ) {
+ struct tls_session *tls =
+ container_of ( xfer, struct tls_session, cipherstream.xfer );
+ size_t frag_len;
+ void *buf;
+ size_t buf_len;
+ int ( * process ) ( struct tls_session *tls );
+ int rc;
+
+ while ( len ) {
+ /* Select buffer according to current state */
+ switch ( tls->rx_state ) {
+ case TLS_RX_HEADER:
+ buf = &tls->rx_header;
+ buf_len = sizeof ( tls->rx_header );
+ process = tls_newdata_process_header;
+ break;
+ case TLS_RX_DATA:
+ buf = tls->rx_data;
+ buf_len = ntohs ( tls->rx_header.length );
+ process = tls_newdata_process_data;
+ break;
+ default:
+ assert ( 0 );
+ return -EINVAL;
+ }
+
+ /* Copy data portion to buffer */
+ frag_len = ( buf_len - tls->rx_rcvd );
+ if ( frag_len > len )
+ frag_len = len;
+ memcpy ( ( buf + tls->rx_rcvd ), data, frag_len );
+ tls->rx_rcvd += frag_len;
+ data += frag_len;
+ len -= frag_len;
+
+ /* Process data if buffer is now full */
+ if ( tls->rx_rcvd == buf_len ) {
+ if ( ( rc = process ( tls ) ) != 0 ) {
+ tls_close ( tls, rc );
+ return rc;
+ }
+ tls->rx_rcvd = 0;
+ }
+ }
+
+ return 0;
+}
+
+/** TLS ciphertext stream operations */
+static struct xfer_interface_operations tls_cipherstream_operations = {
+ .close = tls_cipherstream_close,
+ .vredirect = xfer_vopen,
+ .window = filter_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = tls_cipherstream_deliver_raw,
+};
+
+/******************************************************************************
+ *
+ * Controlling process
+ *
+ ******************************************************************************
+ */
+
+/**
+ * TLS TX state machine
+ *
+ * @v process TLS process
+ */
+static void tls_step ( struct process *process ) {
+ struct tls_session *tls =
+ container_of ( process, struct tls_session, process );
+ int rc;
+
+ /* Wait for cipherstream to become ready */
+ if ( ! xfer_window ( &tls->cipherstream.xfer ) )
+ return;
+
+ switch ( tls->tx_state ) {
+ case TLS_TX_NONE:
+ /* Nothing to do */
+ break;
+ case TLS_TX_CLIENT_HELLO:
+ /* Send Client Hello */
+ if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not send Client Hello: %s\n",
+ tls, strerror ( rc ) );
+ goto err;
+ }
+ tls->tx_state = TLS_TX_NONE;
+ break;
+ case TLS_TX_CLIENT_KEY_EXCHANGE:
+ /* Send Client Key Exchange */
+ if ( ( rc = tls_send_client_key_exchange ( tls ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could send Client Key Exchange: "
+ "%s\n", tls, strerror ( rc ) );
+ goto err;
+ }
+ tls->tx_state = TLS_TX_CHANGE_CIPHER;
+ break;
+ case TLS_TX_CHANGE_CIPHER:
+ /* Send Change Cipher, and then change the cipher in use */
+ if ( ( rc = tls_send_change_cipher ( tls ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not send Change Cipher: "
+ "%s\n", tls, strerror ( rc ) );
+ goto err;
+ }
+ if ( ( rc = tls_change_cipher ( tls,
+ &tls->tx_cipherspec_pending,
+ &tls->tx_cipherspec )) != 0 ){
+ DBGC ( tls, "TLS %p could not activate TX cipher: "
+ "%s\n", tls, strerror ( rc ) );
+ goto err;
+ }
+ tls->tx_seq = 0;
+ tls->tx_state = TLS_TX_FINISHED;
+ break;
+ case TLS_TX_FINISHED:
+ /* Send Finished */
+ if ( ( rc = tls_send_finished ( tls ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not send Finished: %s\n",
+ tls, strerror ( rc ) );
+ goto err;
+ }
+ tls->tx_state = TLS_TX_NONE;
+ break;
+ case TLS_TX_DATA:
+ /* Nothing to do */
+ break;
+ default:
+ assert ( 0 );
+ }
+
+ return;
+
+ err:
+ tls_close ( tls, rc );
+}
+
+/******************************************************************************
+ *
+ * Instantiator
+ *
+ ******************************************************************************
+ */
+
+int add_tls ( struct xfer_interface *xfer, struct xfer_interface **next ) {
+ struct tls_session *tls;
+
+ /* Allocate and initialise TLS structure */
+ tls = malloc ( sizeof ( *tls ) );
+ if ( ! tls )
+ return -ENOMEM;
+ memset ( tls, 0, sizeof ( *tls ) );
+ tls->refcnt.free = free_tls;
+ filter_init ( &tls->plainstream, &tls_plainstream_operations,
+ &tls->cipherstream, &tls_cipherstream_operations,
+ &tls->refcnt );
+ tls_clear_cipher ( tls, &tls->tx_cipherspec );
+ tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
+ tls_clear_cipher ( tls, &tls->rx_cipherspec );
+ tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
+ *( ( uint32_t * ) tls->client_random ) = 0; /* GMT Unix time */
+ tls_generate_random ( ( tls->client_random + 4 ),
+ ( sizeof ( tls->client_random ) - 4 ) );
+ *( ( uint16_t * ) tls->pre_master_secret )
+ = htons ( TLS_VERSION_TLS_1_0 );
+ tls_generate_random ( ( tls->pre_master_secret + 2 ),
+ ( sizeof ( tls->pre_master_secret ) - 2 ) );
+ digest_init ( &md5_algorithm, tls->handshake_md5_ctx );
+ digest_init ( &sha1_algorithm, tls->handshake_sha1_ctx );
+ tls->tx_state = TLS_TX_CLIENT_HELLO;
+ process_init ( &tls->process, tls_step, &tls->refcnt );
+
+ /* Attach to parent interface, mortalise self, and return */
+ xfer_plug_plug ( &tls->plainstream.xfer, xfer );
+ *next = &tls->cipherstream.xfer;
+ ref_put ( &tls->refcnt );
+ return 0;
+}
+
diff --git a/gpxe/src/net/udp.c b/gpxe/src/net/udp.c
new file mode 100644
index 00000000..89a5b868
--- /dev/null
+++ b/gpxe/src/net/udp.c
@@ -0,0 +1,465 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/uri.h>
+#include <gpxe/udp.h>
+
+/** @file
+ *
+ * UDP protocol
+ */
+
+/**
+ * A UDP connection
+ *
+ */
+struct udp_connection {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** List of UDP connections */
+ struct list_head list;
+
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+
+ /** Remote socket address */
+ struct sockaddr_tcpip peer;
+ /** Local port on which the connection receives packets */
+ unsigned int local_port;
+};
+
+/**
+ * List of registered UDP connections
+ */
+static LIST_HEAD ( udp_conns );
+
+/* Forward declatations */
+static struct xfer_interface_operations udp_xfer_operations;
+struct tcpip_protocol udp_protocol;
+
+/**
+ * Bind UDP connection to local port
+ *
+ * @v udp UDP connection
+ * @v port Local port, in network byte order, or zero
+ * @ret rc Return status code
+ *
+ * Opens the UDP connection and binds to a local port. If no local
+ * port is specified, the first available port will be used.
+ */
+static int udp_bind ( struct udp_connection *udp, unsigned int port ) {
+ struct udp_connection *existing;
+ static uint16_t try_port = 1024;
+
+ /* If no port specified, find the first available port */
+ if ( ! port ) {
+ for ( ; try_port ; try_port++ ) {
+ if ( try_port < 1024 )
+ continue;
+ if ( udp_bind ( udp, htons ( try_port ) ) == 0 )
+ return 0;
+ }
+ return -EADDRINUSE;
+ }
+
+ /* Attempt bind to local port */
+ list_for_each_entry ( existing, &udp_conns, list ) {
+ if ( existing->local_port == port ) {
+ DBGC ( udp, "UDP %p could not bind: port %d in use\n",
+ udp, ntohs ( port ) );
+ return -EADDRINUSE;
+ }
+ }
+ udp->local_port = port;
+
+ /* Add to UDP connection list */
+ DBGC ( udp, "UDP %p bound to port %d\n", udp, ntohs ( port ) );
+
+ return 0;
+}
+
+/**
+ * Open a UDP connection
+ *
+ * @v xfer Data transfer interface
+ * @v peer Peer socket address, or NULL
+ * @v local Local socket address, or NULL
+ * @v promisc Socket is promiscuous
+ * @ret rc Return status code
+ */
+static int udp_open_common ( struct xfer_interface *xfer,
+ struct sockaddr *peer, struct sockaddr *local,
+ int promisc ) {
+ struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
+ struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
+ struct udp_connection *udp;
+ unsigned int bind_port;
+ int rc;
+
+ /* Allocate and initialise structure */
+ udp = zalloc ( sizeof ( *udp ) );
+ if ( ! udp )
+ return -ENOMEM;
+ DBGC ( udp, "UDP %p allocated\n", udp );
+ xfer_init ( &udp->xfer, &udp_xfer_operations, &udp->refcnt );
+ if ( st_peer )
+ memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) );
+
+ /* Bind to local port */
+ if ( ! promisc ) {
+ bind_port = ( st_local ? st_local->st_port : 0 );
+ if ( ( rc = udp_bind ( udp, bind_port ) ) != 0 )
+ goto err;
+ }
+
+ /* Attach parent interface, transfer reference to connection
+ * list and return
+ */
+ xfer_plug_plug ( &udp->xfer, xfer );
+ list_add ( &udp->list, &udp_conns );
+ return 0;
+
+ err:
+ ref_put ( &udp->refcnt );
+ return rc;
+}
+
+/**
+ * Open a UDP connection
+ *
+ * @v xfer Data transfer interface
+ * @v peer Peer socket address
+ * @v local Local socket address, or NULL
+ * @ret rc Return status code
+ */
+int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer,
+ struct sockaddr *local ) {
+ return udp_open_common ( xfer, peer, local, 0 );
+}
+
+/**
+ * Open a promiscuous UDP connection
+ *
+ * @v xfer Data transfer interface
+ * @ret rc Return status code
+ *
+ * Promiscuous UDP connections are required in order to support the
+ * PXE API.
+ */
+int udp_open_promisc ( struct xfer_interface *xfer ) {
+ return udp_open_common ( xfer, NULL, NULL, 1 );
+}
+
+/**
+ * Close a UDP connection
+ *
+ * @v udp UDP connection
+ * @v rc Reason for close
+ */
+static void udp_close ( struct udp_connection *udp, int rc ) {
+
+ /* Close data transfer interface */
+ xfer_nullify ( &udp->xfer );
+ xfer_close ( &udp->xfer, rc );
+
+ /* Remove from list of connections and drop list's reference */
+ list_del ( &udp->list );
+ ref_put ( &udp->refcnt );
+
+ DBGC ( udp, "UDP %p closed\n", udp );
+}
+
+/**
+ * Transmit data via a UDP connection to a specified address
+ *
+ * @v udp UDP connection
+ * @v iobuf I/O buffer
+ * @v src_port Source port, or 0 to use default
+ * @v dest Destination address, or NULL to use default
+ * @v netdev Network device, or NULL to use default
+ * @ret rc Return status code
+ */
+static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
+ unsigned int src_port, struct sockaddr_tcpip *dest,
+ struct net_device *netdev ) {
+ struct udp_header *udphdr;
+ size_t len;
+ int rc;
+
+ /* Check we can accommodate the header */
+ if ( ( rc = iob_ensure_headroom ( iobuf, UDP_MAX_HLEN ) ) != 0 ) {
+ free_iob ( iobuf );
+ return rc;
+ }
+
+ /* Fill in default values if not explicitly provided */
+ if ( ! src_port )
+ src_port = udp->local_port;
+ if ( ! dest )
+ dest = &udp->peer;
+
+ /* Add the UDP header */
+ udphdr = iob_push ( iobuf, sizeof ( *udphdr ) );
+ len = iob_len ( iobuf );
+ udphdr->dest = dest->st_port;
+ udphdr->src = src_port;
+ udphdr->len = htons ( len );
+ udphdr->chksum = 0;
+ udphdr->chksum = tcpip_chksum ( udphdr, len );
+
+ /* Dump debugging information */
+ DBGC ( udp, "UDP %p TX %d->%d len %d\n", udp,
+ ntohs ( udphdr->src ), ntohs ( udphdr->dest ),
+ ntohs ( udphdr->len ) );
+
+ /* Send it to the next layer for processing */
+ if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, dest, netdev,
+ &udphdr->chksum ) ) != 0 ) {
+ DBGC ( udp, "UDP %p could not transmit packet: %s\n",
+ udp, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Identify UDP connection by local port number
+ *
+ * @v local_port Local port (in network-endian order)
+ * @ret udp UDP connection, or NULL
+ */
+static struct udp_connection * udp_demux ( unsigned int local_port ) {
+ struct udp_connection *udp;
+
+ list_for_each_entry ( udp, &udp_conns, list ) {
+ if ( ( udp->local_port == local_port ) ||
+ ( udp->local_port == 0 ) ) {
+ return udp;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Process a received packet
+ *
+ * @v iobuf I/O buffer
+ * @v st_src Partially-filled source address
+ * @v st_dest Partially-filled destination address
+ * @v pshdr_csum Pseudo-header checksum
+ * @ret rc Return status code
+ */
+static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
+ struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
+ struct udp_header *udphdr = iobuf->data;
+ struct udp_connection *udp;
+ struct xfer_metadata meta;
+ size_t ulen;
+ unsigned int csum;
+ int rc = 0;
+
+ /* Sanity check packet */
+ if ( iob_len ( iobuf ) < sizeof ( *udphdr ) ) {
+ DBG ( "UDP packet too short at %zd bytes (min %zd bytes)\n",
+ iob_len ( iobuf ), sizeof ( *udphdr ) );
+
+ rc = -EINVAL;
+ goto done;
+ }
+ ulen = ntohs ( udphdr->len );
+ if ( ulen < sizeof ( *udphdr ) ) {
+ DBG ( "UDP length too short at %zd bytes "
+ "(header is %zd bytes)\n", ulen, sizeof ( *udphdr ) );
+ rc = -EINVAL;
+ goto done;
+ }
+ if ( ulen > iob_len ( iobuf ) ) {
+ DBG ( "UDP length too long at %zd bytes (packet is %zd "
+ "bytes)\n", ulen, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto done;
+ }
+ if ( udphdr->chksum ) {
+ csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, ulen );
+ if ( csum != 0 ) {
+ DBG ( "UDP checksum incorrect (is %04x including "
+ "checksum field, should be 0000)\n", csum );
+ rc = -EINVAL;
+ goto done;
+ }
+ }
+
+ /* Parse parameters from header and strip header */
+ st_src->st_port = udphdr->src;
+ st_dest->st_port = udphdr->dest;
+ udp = udp_demux ( udphdr->dest );
+ iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) );
+ iob_pull ( iobuf, sizeof ( *udphdr ) );
+
+ /* Dump debugging information */
+ DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp,
+ ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen );
+
+ /* Ignore if no matching connection found */
+ if ( ! udp ) {
+ DBG ( "No UDP connection listening on port %d\n",
+ ntohs ( udphdr->dest ) );
+ rc = -ENOTCONN;
+ goto done;
+ }
+
+ /* Pass data to application */
+ memset ( &meta, 0, sizeof ( meta ) );
+ meta.src = ( struct sockaddr * ) st_src;
+ meta.dest = ( struct sockaddr * ) st_dest;
+ rc = xfer_deliver_iob_meta ( &udp->xfer, iobuf, &meta );
+ iobuf = NULL;
+
+ done:
+ free_iob ( iobuf );
+ return rc;
+}
+
+struct tcpip_protocol udp_protocol __tcpip_protocol = {
+ .name = "UDP",
+ .rx = udp_rx,
+ .tcpip_proto = IP_UDP,
+};
+
+/***************************************************************************
+ *
+ * Data transfer interface
+ *
+ ***************************************************************************
+ */
+
+/**
+ * Close interface
+ *
+ * @v xfer Data transfer interface
+ * @v rc Reason for close
+ */
+static void udp_xfer_close ( struct xfer_interface *xfer, int rc ) {
+ struct udp_connection *udp =
+ container_of ( xfer, struct udp_connection, xfer );
+
+ /* Close connection */
+ udp_close ( udp, rc );
+}
+
+/**
+ * Allocate I/O buffer for UDP
+ *
+ * @v xfer Data transfer interface
+ * @v len Payload size
+ * @ret iobuf I/O buffer, or NULL
+ */
+static struct io_buffer * udp_alloc_iob ( struct xfer_interface *xfer,
+ size_t len ) {
+ struct udp_connection *udp =
+ container_of ( xfer, struct udp_connection, xfer );
+ struct io_buffer *iobuf;
+
+ iobuf = alloc_iob ( UDP_MAX_HLEN + len );
+ if ( ! iobuf ) {
+ DBGC ( udp, "UDP %p cannot allocate buffer of length %zd\n",
+ udp, len );
+ return NULL;
+ }
+ iob_reserve ( iobuf, UDP_MAX_HLEN );
+ return iobuf;
+}
+
+/**
+ * Deliver datagram as I/O buffer
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf Datagram I/O buffer
+ * @v meta Data transfer metadata, or NULL
+ * @ret rc Return status code
+ */
+static int udp_xfer_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct udp_connection *udp =
+ container_of ( xfer, struct udp_connection, xfer );
+ struct sockaddr_tcpip *src;
+ struct sockaddr_tcpip *dest = NULL;
+ struct net_device *netdev = NULL;
+ unsigned int src_port = 0;
+
+ /* Apply xfer metadata */
+ if ( meta ) {
+ src = ( struct sockaddr_tcpip * ) meta->src;
+ if ( src )
+ src_port = src->st_port;
+ dest = ( struct sockaddr_tcpip * ) meta->dest;
+ netdev = meta->netdev;
+ }
+
+ /* Transmit data, if possible */
+ udp_tx ( udp, iobuf, src_port, dest, netdev );
+
+ return 0;
+}
+
+/** UDP data transfer interface operations */
+static struct xfer_interface_operations udp_xfer_operations = {
+ .close = udp_xfer_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = unlimited_xfer_window,
+ .alloc_iob = udp_alloc_iob,
+ .deliver_iob = udp_xfer_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/***************************************************************************
+ *
+ * Openers
+ *
+ ***************************************************************************
+ */
+
+/** UDP socket opener */
+struct socket_opener udp_socket_opener __socket_opener = {
+ .semantics = SOCK_DGRAM,
+ .family = AF_INET,
+ .open = udp_open,
+};
+
+char UDP_SOCK_DGRAM[1];
+
+/**
+ * Open UDP URI
+ *
+ * @v xfer Data transfer interface
+ * @v uri URI
+ * @ret rc Return status code
+ */
+static int udp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) {
+ struct sockaddr_tcpip peer;
+
+ /* Sanity check */
+ if ( ! uri->host )
+ return -EINVAL;
+
+ memset ( &peer, 0, sizeof ( peer ) );
+ peer.st_port = htons ( uri_port ( uri, 0 ) );
+ return xfer_open_named_socket ( xfer, SOCK_DGRAM,
+ ( struct sockaddr * ) &peer,
+ uri->host, NULL );
+}
+
+/** UDP URI opener */
+struct uri_opener udp_uri_opener __uri_opener = {
+ .scheme = "udp",
+ .open = udp_open_uri,
+};
diff --git a/gpxe/src/net/udp/dhcp.c b/gpxe/src/net/udp/dhcp.c
new file mode 100644
index 00000000..3961c61d
--- /dev/null
+++ b/gpxe/src/net/udp/dhcp.c
@@ -0,0 +1,825 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/device.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/job.h>
+#include <gpxe/retry.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/ip.h>
+#include <gpxe/uuid.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/timer.h>
+#include <gpxe/settings.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/dhcpopts.h>
+#include <gpxe/dhcppkt.h>
+
+/** @file
+ *
+ * Dynamic Host Configuration Protocol
+ *
+ */
+
+/**
+ * DHCP operation types
+ *
+ * This table maps from DHCP message types (i.e. values of the @c
+ * DHCP_MESSAGE_TYPE option) to values of the "op" field within a DHCP
+ * packet.
+ */
+static const uint8_t dhcp_op[] = {
+ [DHCPDISCOVER] = BOOTP_REQUEST,
+ [DHCPOFFER] = BOOTP_REPLY,
+ [DHCPREQUEST] = BOOTP_REQUEST,
+ [DHCPDECLINE] = BOOTP_REQUEST,
+ [DHCPACK] = BOOTP_REPLY,
+ [DHCPNAK] = BOOTP_REPLY,
+ [DHCPRELEASE] = BOOTP_REQUEST,
+ [DHCPINFORM] = BOOTP_REQUEST,
+};
+
+/** Raw option data for options common to all DHCP requests */
+static uint8_t dhcp_request_options_data[] = {
+ DHCP_MAX_MESSAGE_SIZE, DHCP_WORD ( ETH_MAX_MTU ),
+ DHCP_VENDOR_CLASS_ID,
+ DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':',
+ 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':',
+ 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ),
+ DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( 0 ),
+ DHCP_CLIENT_NDI, DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ),
+ DHCP_PARAMETER_REQUEST_LIST,
+ DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS,
+ DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME,
+ DHCP_ROOT_PATH, DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID,
+ DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME,
+ DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ),
+ DHCP_END
+};
+
+/** Options common to all DHCP requests */
+static struct dhcp_options dhcp_request_options = {
+ .data = dhcp_request_options_data,
+ .max_len = sizeof ( dhcp_request_options_data ),
+ .len = sizeof ( dhcp_request_options_data ),
+};
+
+/** DHCP feature codes */
+static uint8_t dhcp_features[0] __table_start ( uint8_t, dhcp_features );
+static uint8_t dhcp_features_end[0] __table_end ( uint8_t, dhcp_features );
+
+/**
+ * Name a DHCP packet type
+ *
+ * @v msgtype DHCP message type
+ * @ret string DHCP mesasge type name
+ */
+static inline const char * dhcp_msgtype_name ( unsigned int msgtype ) {
+ switch ( msgtype ) {
+ case 0: return "BOOTP"; /* Non-DHCP packet */
+ case DHCPDISCOVER: return "DHCPDISCOVER";
+ case DHCPOFFER: return "DHCPOFFER";
+ case DHCPREQUEST: return "DHCPREQUEST";
+ case DHCPDECLINE: return "DHCPDECLINE";
+ case DHCPACK: return "DHCPACK";
+ case DHCPNAK: return "DHCPNAK";
+ case DHCPRELEASE: return "DHCPRELEASE";
+ case DHCPINFORM: return "DHCPINFORM";
+ default: return "DHCP<invalid>";
+ }
+}
+
+/**
+ * Calculate DHCP transaction ID for a network device
+ *
+ * @v netdev Network device
+ * @ret xid DHCP XID
+ *
+ * Extract the least significant bits of the hardware address for use
+ * as the transaction ID.
+ */
+static uint32_t dhcp_xid ( struct net_device *netdev ) {
+ uint32_t xid;
+
+ memcpy ( &xid, ( netdev->ll_addr + netdev->ll_protocol->ll_addr_len
+ - sizeof ( xid ) ), sizeof ( xid ) );
+ return xid;
+}
+
+/**
+ * Create a DHCP packet
+ *
+ * @v dhcppkt DHCP packet structure to fill in
+ * @v netdev Network device
+ * @v msgtype DHCP message type
+ * @v options Initial options to include (or NULL)
+ * @v data Buffer for DHCP packet
+ * @v max_len Size of DHCP packet buffer
+ * @ret rc Return status code
+ *
+ * Creates a DHCP packet in the specified buffer, and fills out a @c
+ * dhcp_packet structure that can be passed to
+ * set_dhcp_packet_option() or copy_dhcp_packet_options().
+ */
+int create_dhcp_packet ( struct dhcp_packet *dhcppkt,
+ struct net_device *netdev, uint8_t msgtype,
+ struct dhcp_options *options,
+ void *data, size_t max_len ) {
+ struct dhcphdr *dhcphdr = data;
+ size_t options_len;
+ unsigned int hlen;
+ int rc;
+
+ /* Sanity check */
+ options_len = ( options ? options->len : 0 );
+ if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) )
+ return -ENOSPC;
+
+ /* Initialise DHCP packet content */
+ memset ( dhcphdr, 0, max_len );
+ dhcphdr->xid = dhcp_xid ( netdev );
+ dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
+ dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
+ dhcphdr->op = dhcp_op[msgtype];
+ /* If hardware length exceeds the chaddr field length, don't
+ * use the chaddr field. This is as per RFC4390.
+ */
+ hlen = netdev->ll_protocol->ll_addr_len;
+ if ( hlen > sizeof ( dhcphdr->chaddr ) ) {
+ hlen = 0;
+ dhcphdr->flags = htons ( BOOTP_FL_BROADCAST );
+ }
+ dhcphdr->hlen = hlen;
+ memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen );
+ memcpy ( dhcphdr->options, options->data, options_len );
+
+ /* Initialise DHCP packet structure */
+ memset ( dhcppkt, 0, sizeof ( *dhcppkt ) );
+ dhcppkt_init ( dhcppkt, data, max_len );
+
+ /* Set DHCP_MESSAGE_TYPE option */
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE,
+ &msgtype, sizeof ( msgtype ) ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/** DHCP network device descriptor */
+struct dhcp_netdev_desc {
+ /** Bus type ID */
+ uint8_t type;
+ /** Vendor ID */
+ uint16_t vendor;
+ /** Device ID */
+ uint16_t device;
+} __attribute__ (( packed ));
+
+/** DHCP client identifier */
+struct dhcp_client_id {
+ /** Link-layer protocol */
+ uint8_t ll_proto;
+ /** Link-layer address */
+ uint8_t ll_addr[MAX_LL_ADDR_LEN];
+} __attribute__ (( packed ));
+
+/** DHCP client UUID */
+struct dhcp_client_uuid {
+ /** Identifier type */
+ uint8_t type;
+ /** UUID */
+ union uuid uuid;
+} __attribute__ (( packed ));
+
+#define DHCP_CLIENT_UUID_TYPE 0
+
+/**
+ * Create DHCP request packet
+ *
+ * @v dhcppkt DHCP packet structure to fill in
+ * @v netdev Network device
+ * @v dhcpoffer DHCPOFFER packet received from server
+ * @v data Buffer for DHCP packet
+ * @v max_len Size of DHCP packet buffer
+ * @ret rc Return status code
+ */
+int create_dhcp_request ( struct dhcp_packet *dhcppkt,
+ struct net_device *netdev,
+ struct dhcp_packet *dhcpoffer,
+ void *data, size_t max_len ) {
+ struct device_description *desc = &netdev->dev->desc;
+ struct dhcp_netdev_desc dhcp_desc;
+ struct dhcp_client_id client_id;
+ struct dhcp_client_uuid client_uuid;
+ unsigned int msgtype;
+ size_t dhcp_features_len;
+ size_t ll_addr_len;
+ int rc;
+
+ /* Create DHCP packet */
+ msgtype = ( dhcpoffer ? DHCPREQUEST : DHCPDISCOVER );
+ if ( ( rc = create_dhcp_packet ( dhcppkt, netdev, msgtype,
+ &dhcp_request_options, data,
+ max_len ) ) != 0 ) {
+ DBG ( "DHCP could not create DHCP packet: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy any required options from previous server repsonse */
+ if ( dhcpoffer ) {
+ struct in_addr server_id;
+ struct in_addr requested_ip;
+
+ if ( dhcppkt_fetch ( dhcpoffer, DHCP_SERVER_IDENTIFIER,
+ &server_id, sizeof ( server_id ) )
+ != sizeof ( server_id ) ) {
+ DBG ( "DHCP offer missing server identifier\n" );
+ return -EINVAL;
+ }
+ if ( dhcppkt_fetch ( dhcpoffer, DHCP_EB_YIADDR,
+ &requested_ip, sizeof ( requested_ip ) )
+ != sizeof ( requested_ip ) ) {
+ DBG ( "DHCP offer missing IP address\n" );
+ return -EINVAL;
+ }
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
+ &server_id,
+ sizeof ( server_id ) ) ) != 0 ) {
+ DBG ( "DHCP could not set server identifier: %s\n ",
+ strerror ( rc ) );
+ return rc;
+ }
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
+ &requested_ip,
+ sizeof ( requested_ip ) ) ) != 0 ){
+ DBG ( "DHCP could not set requested address: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ }
+
+ /* Add options to identify the feature list */
+ dhcp_features_len = ( dhcp_features_end - dhcp_features );
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features,
+ dhcp_features_len ) ) != 0 ) {
+ DBG ( "DHCP could not set features list option: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Add options to identify the network device */
+ dhcp_desc.type = desc->bus_type;
+ dhcp_desc.vendor = htons ( desc->vendor );
+ dhcp_desc.device = htons ( desc->device );
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc,
+ sizeof ( dhcp_desc ) ) ) != 0 ) {
+ DBG ( "DHCP could not set bus ID option: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Add DHCP client identifier. Required for Infiniband, and
+ * doesn't hurt other link layers.
+ */
+ client_id.ll_proto = ntohs ( netdev->ll_protocol->ll_proto );
+ ll_addr_len = netdev->ll_protocol->ll_addr_len;
+ assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) );
+ memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len );
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_ID, &client_id,
+ ( ll_addr_len + 1 ) ) ) != 0 ) {
+ DBG ( "DHCP could not set client ID: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Add client UUID, if we have one. Required for PXE. */
+ client_uuid.type = DHCP_CLIENT_UUID_TYPE;
+ if ( ( rc = get_uuid ( &client_uuid.uuid ) ) == 0 ) {
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID,
+ &client_uuid,
+ sizeof ( client_uuid ) ) ) != 0 ) {
+ DBG ( "DHCP could not set client UUID: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * DHCP settings
+ *
+ */
+
+/** A DHCP settings block */
+struct dhcp_settings {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** DHCP packet */
+ struct dhcp_packet dhcppkt;
+ /** Setting interface */
+ struct settings settings;
+};
+
+/**
+ * Decrement reference count on DHCP settings block
+ *
+ * @v dhcpset DHCP settings block
+ */
+static inline void dhcpset_put ( struct dhcp_settings *dhcpset ) {
+ ref_put ( &dhcpset->refcnt );
+}
+
+/**
+ * Store value of DHCP setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
+ */
+static int dhcpset_store ( struct settings *settings, struct setting *setting,
+ const void *data, size_t len ) {
+ struct dhcp_settings *dhcpset =
+ container_of ( settings, struct dhcp_settings, settings );
+
+ return dhcppkt_store ( &dhcpset->dhcppkt, setting->tag, data, len );
+}
+
+/**
+ * Fetch value of setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int dhcpset_fetch ( struct settings *settings, struct setting *setting,
+ void *data, size_t len ) {
+ struct dhcp_settings *dhcpset =
+ container_of ( settings, struct dhcp_settings, settings );
+
+ return dhcppkt_fetch ( &dhcpset->dhcppkt, setting->tag, data, len );
+}
+
+/** DHCP settings operations */
+static struct settings_operations dhcpset_settings_operations = {
+ .store = dhcpset_store,
+ .fetch = dhcpset_fetch,
+};
+
+/**
+ * Create DHCP setting block
+ *
+ * @v dhcphdr DHCP packet
+ * @v len Length of DHCP packet
+ * @ret dhcpset DHCP settings block
+ */
+static struct dhcp_settings * dhcpset_create ( const struct dhcphdr *dhcphdr,
+ size_t len ) {
+ struct dhcp_settings *dhcpset;
+ void *data;
+
+ dhcpset = zalloc ( sizeof ( *dhcpset ) + len );
+ if ( dhcpset ) {
+ data = ( ( ( void * ) dhcpset ) + sizeof ( *dhcpset ) );
+ memcpy ( data, dhcphdr, len );
+ dhcppkt_init ( &dhcpset->dhcppkt, data, len );
+ settings_init ( &dhcpset->settings,
+ &dhcpset_settings_operations, &dhcpset->refcnt,
+ DHCP_SETTINGS_NAME );
+ }
+ return dhcpset;
+}
+
+/****************************************************************************
+ *
+ * DHCP to UDP interface
+ *
+ */
+
+/** A DHCP session */
+struct dhcp_session {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** Job control interface */
+ struct job_interface job;
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+
+ /** Network device being configured */
+ struct net_device *netdev;
+
+ /** State of the session
+ *
+ * This is a value for the @c DHCP_MESSAGE_TYPE option
+ * (e.g. @c DHCPDISCOVER).
+ */
+ int state;
+ /** Response obtained from DHCP server */
+ struct dhcp_settings *response;
+ /** Response obtained from ProxyDHCP server */
+ struct dhcp_settings *proxy_response;
+ /** Retransmission timer */
+ struct retry_timer timer;
+ /** Session start time (in ticks) */
+ unsigned long start;
+};
+
+/**
+ * Free DHCP session
+ *
+ * @v refcnt Reference counter
+ */
+static void dhcp_free ( struct refcnt *refcnt ) {
+ struct dhcp_session *dhcp =
+ container_of ( refcnt, struct dhcp_session, refcnt );
+
+ netdev_put ( dhcp->netdev );
+ dhcpset_put ( dhcp->response );
+ dhcpset_put ( dhcp->proxy_response );
+ free ( dhcp );
+}
+
+/**
+ * Mark DHCP session as complete
+ *
+ * @v dhcp DHCP session
+ * @v rc Return status code
+ */
+static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) {
+
+ /* Block futher incoming messages */
+ job_nullify ( &dhcp->job );
+ xfer_nullify ( &dhcp->xfer );
+
+ /* Stop retry timer */
+ stop_timer ( &dhcp->timer );
+
+ /* Free resources and close interfaces */
+ xfer_close ( &dhcp->xfer, rc );
+ job_done ( &dhcp->job, rc );
+}
+
+/**
+ * Register options received via DHCP
+ *
+ * @v dhcp DHCP session
+ * @ret rc Return status code
+ */
+static int dhcp_register_settings ( struct dhcp_session *dhcp ) {
+ struct settings *old_settings;
+ struct settings *settings;
+ struct settings *parent;
+ int rc;
+
+ /* Register ProxyDHCP settings, if present */
+ if ( dhcp->proxy_response ) {
+ settings = &dhcp->proxy_response->settings;
+ settings->name = PROXYDHCP_SETTINGS_NAME;
+ old_settings = find_settings ( settings->name );
+ if ( old_settings )
+ unregister_settings ( old_settings );
+ if ( ( rc = register_settings ( settings, NULL ) ) != 0 )
+ return rc;
+ }
+
+ /* Register DHCP settings */
+ parent = netdev_settings ( dhcp->netdev );
+ settings = &dhcp->response->settings;
+ old_settings = find_child_settings ( parent, settings->name );
+ if ( old_settings )
+ unregister_settings ( old_settings );
+ if ( ( rc = register_settings ( settings, parent ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * Data transfer interface
+ *
+ */
+
+/**
+ * Transmit DHCP request
+ *
+ * @v dhcp DHCP session
+ * @ret rc Return status code
+ */
+static int dhcp_send_request ( struct dhcp_session *dhcp ) {
+ struct xfer_metadata meta = {
+ .netdev = dhcp->netdev,
+ };
+ struct io_buffer *iobuf;
+ struct dhcp_packet *dhcpoffer = NULL;
+ struct dhcp_packet dhcppkt;
+ int rc;
+
+ DBGC ( dhcp, "DHCP %p transmitting %s\n",
+ dhcp, dhcp_msgtype_name ( dhcp->state ) );
+
+ assert ( ( dhcp->state == DHCPDISCOVER ) ||
+ ( dhcp->state == DHCPREQUEST ) );
+
+ /* Start retry timer. Do this first so that failures to
+ * transmit will be retried.
+ */
+ start_timer ( &dhcp->timer );
+
+ /* Allocate buffer for packet */
+ iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN );
+ if ( ! iobuf )
+ return -ENOMEM;
+
+ /* Create DHCP packet in temporary buffer */
+ if ( dhcp->state == DHCPREQUEST ) {
+ assert ( dhcp->response );
+ dhcpoffer = &dhcp->response->dhcppkt;
+ }
+ if ( ( rc = create_dhcp_request ( &dhcppkt, dhcp->netdev,
+ dhcpoffer, iobuf->data,
+ iob_tailroom ( iobuf ) ) ) != 0 ) {
+ DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n",
+ dhcp, strerror ( rc ) );
+ goto done;
+ }
+
+ /* Transmit the packet */
+ iob_put ( iobuf, dhcppkt.len );
+ rc = xfer_deliver_iob_meta ( &dhcp->xfer, iobuf, &meta );
+ iobuf = NULL;
+ if ( rc != 0 ) {
+ DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n",
+ dhcp, strerror ( rc ) );
+ goto done;
+ }
+
+ done:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * Handle DHCP retry timer expiry
+ *
+ * @v timer DHCP retry timer
+ * @v fail Failure indicator
+ */
+static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
+ struct dhcp_session *dhcp =
+ container_of ( timer, struct dhcp_session, timer );
+
+ if ( fail ) {
+ dhcp_finished ( dhcp, -ETIMEDOUT );
+ } else {
+ dhcp_send_request ( dhcp );
+ }
+}
+
+/**
+ * Receive new data
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf I/O buffer
+ * @v data Received data
+ * @v len Length of received data
+ * @ret rc Return status code
+ */
+static int dhcp_deliver_raw ( struct xfer_interface *xfer,
+ const void *data, size_t len ) {
+ struct dhcp_session *dhcp =
+ container_of ( xfer, struct dhcp_session, xfer );
+ struct dhcp_settings *response;
+ struct dhcp_settings **store_response;
+ struct dhcphdr *dhcphdr;
+ uint8_t msgtype = 0;
+ uint8_t priority = 0;
+ uint8_t existing_priority = 0;
+ unsigned long elapsed;
+ int is_proxy;
+ uint8_t ignore_proxy = 0;
+ int rc;
+
+ /* Convert packet into a DHCP settings block */
+ response = dhcpset_create ( data, len );
+ if ( ! response ) {
+ DBGC ( dhcp, "DHCP %p could not store DHCP packet\n", dhcp );
+ return -ENOMEM;
+ }
+ dhcphdr = response->dhcppkt.dhcphdr;
+
+ /* Check for matching transaction ID */
+ if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
+ DBGC ( dhcp, "DHCP %p wrong transaction ID (wanted %08lx, "
+ "got %08lx)\n", dhcp, ntohl ( dhcphdr->xid ),
+ ntohl ( dhcp_xid ( dhcp->netdev ) ) );
+ goto out_discard;
+ };
+
+ /* Determine and verify message type */
+ is_proxy = ( dhcphdr->yiaddr.s_addr == 0 );
+ dhcppkt_fetch ( &response->dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
+ sizeof ( msgtype ) );
+ DBGC ( dhcp, "DHCP %p received %s%s\n", dhcp,
+ ( is_proxy ? "Proxy" : "" ), dhcp_msgtype_name ( msgtype ) );
+ if ( ( ( dhcp->state != DHCPDISCOVER ) || ( msgtype != DHCPOFFER ) ) &&
+ ( ( dhcp->state != DHCPREQUEST ) || ( msgtype != DHCPACK ) ) ) {
+ DBGC ( dhcp, "DHCP %p discarding %s while in %s state\n",
+ dhcp, dhcp_msgtype_name ( msgtype ),
+ dhcp_msgtype_name ( dhcp->state ) );
+ goto out_discard;
+ }
+
+ /* Update stored standard/ProxyDHCP options, if the new
+ * options have equal or higher priority than the
+ * currently-stored options.
+ */
+ store_response = ( is_proxy ? &dhcp->proxy_response : &dhcp->response);
+ if ( *store_response ) {
+ dhcppkt_fetch ( &(*store_response)->dhcppkt, DHCP_EB_PRIORITY,
+ &existing_priority,
+ sizeof ( existing_priority ) );
+ }
+ dhcppkt_fetch ( &response->dhcppkt, DHCP_EB_PRIORITY, &priority,
+ sizeof ( priority ) );
+ if ( priority >= existing_priority ) {
+ dhcpset_put ( *store_response );
+ *store_response = response;
+ } else {
+ dhcpset_put ( response );
+ }
+
+ /* If we don't yet have a standard DHCP response (i.e. one
+ * with an IP address), then just leave the timer running.
+ */
+ if ( ! dhcp->response )
+ goto out;
+
+ /* Handle DHCP response */
+ dhcppkt_fetch ( &dhcp->response->dhcppkt, DHCP_EB_NO_PROXYDHCP,
+ &ignore_proxy, sizeof ( ignore_proxy ) );
+ switch ( dhcp->state ) {
+ case DHCPDISCOVER:
+ /* If we have allowed sufficient time for ProxyDHCP
+ * reponses, then transition to making the DHCPREQUEST.
+ */
+ elapsed = ( currticks() - dhcp->start );
+ if ( ignore_proxy || ( elapsed > PROXYDHCP_WAIT_TIME ) ) {
+ stop_timer ( &dhcp->timer );
+ dhcp->state = DHCPREQUEST;
+ dhcp_send_request ( dhcp );
+ }
+ break;
+ case DHCPREQUEST:
+ /* DHCP finished; register options and exit */
+ if ( ignore_proxy && dhcp->proxy_response ) {
+ dhcpset_put ( dhcp->proxy_response );
+ dhcp->proxy_response = NULL;
+ }
+ if ( ( rc = dhcp_register_settings ( dhcp ) ) != 0 ) {
+ dhcp_finished ( dhcp, rc );
+ break;
+ }
+ dhcp_finished ( dhcp, 0 );
+ break;
+ default:
+ assert ( 0 );
+ }
+
+ out:
+ return 0;
+
+ out_discard:
+ dhcpset_put ( response );
+ return 0;
+}
+
+/** DHCP data transfer interface operations */
+static struct xfer_interface_operations dhcp_xfer_operations = {
+ .close = ignore_xfer_close,
+ .vredirect = xfer_vopen,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = dhcp_deliver_raw,
+};
+
+/****************************************************************************
+ *
+ * Job control interface
+ *
+ */
+
+/**
+ * Handle kill() event received via job control interface
+ *
+ * @v job DHCP job control interface
+ */
+static void dhcp_job_kill ( struct job_interface *job ) {
+ struct dhcp_session *dhcp =
+ container_of ( job, struct dhcp_session, job );
+
+ /* Terminate DHCP session */
+ dhcp_finished ( dhcp, -ECANCELED );
+}
+
+/** DHCP job control interface operations */
+static struct job_interface_operations dhcp_job_operations = {
+ .done = ignore_job_done,
+ .kill = dhcp_job_kill,
+ .progress = ignore_job_progress,
+};
+
+/****************************************************************************
+ *
+ * Instantiator
+ *
+ */
+
+/**
+ * Start DHCP on a network device
+ *
+ * @v job Job control interface
+ * @v netdev Network device
+ * @v register_options DHCP option block registration routine
+ * @ret rc Return status code
+ *
+ * Starts DHCP on the specified network device. If successful, the @c
+ * register_options() routine will be called with the acquired
+ * options.
+ */
+int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
+ static struct sockaddr_in server = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = INADDR_BROADCAST,
+ .sin_port = htons ( BOOTPS_PORT ),
+ };
+ static struct sockaddr_in client = {
+ .sin_family = AF_INET,
+ .sin_port = htons ( BOOTPC_PORT ),
+ };
+ struct dhcp_session *dhcp;
+ int rc;
+
+ /* Allocate and initialise structure */
+ dhcp = zalloc ( sizeof ( *dhcp ) );
+ if ( ! dhcp )
+ return -ENOMEM;
+ dhcp->refcnt.free = dhcp_free;
+ job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
+ xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
+ dhcp->netdev = netdev_get ( netdev );
+ dhcp->timer.expired = dhcp_timer_expired;
+ dhcp->state = DHCPDISCOVER;
+ dhcp->start = currticks();
+
+ /* Instantiate child objects and attach to our interfaces */
+ if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM,
+ ( struct sockaddr * ) &server,
+ ( struct sockaddr * ) &client ) ) != 0 )
+ goto err;
+
+ /* Start timer to initiate initial DHCPREQUEST */
+ start_timer_nodelay ( &dhcp->timer );
+
+ /* Attach parent interface, mortalise self, and return */
+ job_plug_plug ( &dhcp->job, job );
+ ref_put ( &dhcp->refcnt );
+ return 0;
+
+ err:
+ dhcp_finished ( dhcp, rc );
+ ref_put ( &dhcp->refcnt );
+ return rc;
+}
diff --git a/gpxe/src/net/udp/dns.c b/gpxe/src/net/udp/dns.c
new file mode 100644
index 00000000..1bcdbc7e
--- /dev/null
+++ b/gpxe/src/net/udp/dns.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * Portions copyright (C) 2004 Anselm M. Hoffmeister
+ * <stockholm@users.sourceforge.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/resolv.h>
+#include <gpxe/retry.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/settings.h>
+#include <gpxe/features.h>
+#include <gpxe/dns.h>
+
+/** @file
+ *
+ * DNS protocol
+ *
+ */
+
+FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 );
+
+/** The DNS server */
+static struct sockaddr_tcpip nameserver = {
+ .st_port = htons ( DNS_PORT ),
+};
+
+/** A DNS request */
+struct dns_request {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** Name resolution interface */
+ struct resolv_interface resolv;
+ /** Data transfer interface */
+ struct xfer_interface socket;
+ /** Retry timer */
+ struct retry_timer timer;
+
+ /** Socket address to fill in with resolved address */
+ struct sockaddr sa;
+ /** Current query packet */
+ struct dns_query query;
+ /** Location of query info structure within current packet
+ *
+ * The query info structure is located immediately after the
+ * compressed name.
+ */
+ struct dns_query_info *qinfo;
+ /** Recursion counter */
+ unsigned int recursion;
+};
+
+/**
+ * Mark DNS request as complete
+ *
+ * @v dns DNS request
+ * @v rc Return status code
+ */
+static void dns_done ( struct dns_request *dns, int rc ) {
+
+ /* Stop the retry timer */
+ stop_timer ( &dns->timer );
+
+ /* Close data transfer interface */
+ xfer_nullify ( &dns->socket );
+ xfer_close ( &dns->socket, rc );
+
+ /* Mark name resolution as complete */
+ resolv_done ( &dns->resolv, &dns->sa, rc );
+}
+
+/**
+ * Compare DNS reply name against the query name from the original request
+ *
+ * @v dns DNS request
+ * @v reply DNS reply
+ * @v rname Reply name
+ * @ret zero Names match
+ * @ret non-zero Names do not match
+ */
+static int dns_name_cmp ( struct dns_request *dns,
+ const struct dns_header *reply,
+ const char *rname ) {
+ const char *qname = dns->query.payload;
+ int i;
+
+ while ( 1 ) {
+ /* Obtain next section of rname */
+ while ( ( *rname ) & 0xc0 ) {
+ rname = ( ( ( char * ) reply ) +
+ ( ntohs( *((uint16_t *)rname) ) & ~0xc000 ));
+ }
+ /* Check that lengths match */
+ if ( *rname != *qname )
+ return -1;
+ /* If length is zero, we have reached the end */
+ if ( ! *qname )
+ return 0;
+ /* Check that data matches */
+ for ( i = *qname + 1; i > 0 ; i-- ) {
+ if ( *(rname++) != *(qname++) )
+ return -1;
+ }
+ }
+}
+
+/**
+ * Skip over a (possibly compressed) DNS name
+ *
+ * @v name DNS name
+ * @ret name Next DNS name
+ */
+static const char * dns_skip_name ( const char *name ) {
+ while ( 1 ) {
+ if ( ! *name ) {
+ /* End of name */
+ return ( name + 1);
+ }
+ if ( *name & 0xc0 ) {
+ /* Start of a compressed name */
+ return ( name + 2 );
+ }
+ /* Uncompressed name portion */
+ name += *name + 1;
+ }
+}
+
+/**
+ * Find an RR in a reply packet corresponding to our query
+ *
+ * @v dns DNS request
+ * @v reply DNS reply
+ * @ret rr DNS RR, or NULL if not found
+ */
+static union dns_rr_info * dns_find_rr ( struct dns_request *dns,
+ const struct dns_header *reply ) {
+ int i, cmp;
+ const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header );
+ union dns_rr_info *rr_info;
+
+ /* Skip over the questions section */
+ for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) {
+ p = dns_skip_name ( p ) + sizeof ( struct dns_query_info );
+ }
+
+ /* Process the answers section */
+ for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) {
+ cmp = dns_name_cmp ( dns, reply, p );
+ p = dns_skip_name ( p );
+ rr_info = ( ( union dns_rr_info * ) p );
+ if ( cmp == 0 )
+ return rr_info;
+ p += ( sizeof ( rr_info->common ) +
+ ntohs ( rr_info->common.rdlength ) );
+ }
+
+ return NULL;
+}
+
+/**
+ * Convert a standard NUL-terminated string to a DNS name
+ *
+ * @v string Name as a NUL-terminated string
+ * @v buf Buffer in which to place DNS name
+ * @ret next Byte following constructed DNS name
+ *
+ * DNS names consist of "<length>element" pairs.
+ */
+static char * dns_make_name ( const char *string, char *buf ) {
+ char *length_byte = buf++;
+ char c;
+
+ while ( ( c = *(string++) ) ) {
+ if ( c == '.' ) {
+ *length_byte = buf - length_byte - 1;
+ length_byte = buf;
+ }
+ *(buf++) = c;
+ }
+ *length_byte = buf - length_byte - 1;
+ *(buf++) = '\0';
+ return buf;
+}
+
+/**
+ * Convert an uncompressed DNS name to a NUL-terminated string
+ *
+ * @v name DNS name
+ * @ret string NUL-terminated string
+ *
+ * Produce a printable version of a DNS name. Used only for debugging.
+ */
+static inline char * dns_unmake_name ( char *name ) {
+ char *p;
+ unsigned int len;
+
+ p = name;
+ while ( ( len = *p ) ) {
+ *(p++) = '.';
+ p += len;
+ }
+
+ return name + 1;
+}
+
+/**
+ * Decompress a DNS name
+ *
+ * @v reply DNS replay
+ * @v name DNS name
+ * @v buf Buffer into which to decompress DNS name
+ * @ret next Byte following decompressed DNS name
+ */
+static char * dns_decompress_name ( const struct dns_header *reply,
+ const char *name, char *buf ) {
+ int i, len;
+
+ do {
+ /* Obtain next section of name */
+ while ( ( *name ) & 0xc0 ) {
+ name = ( ( char * ) reply +
+ ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) );
+ }
+ /* Copy data */
+ len = *name;
+ for ( i = len + 1 ; i > 0 ; i-- ) {
+ *(buf++) = *(name++);
+ }
+ } while ( len );
+ return buf;
+}
+
+/**
+ * Send next packet in DNS request
+ *
+ * @v dns DNS request
+ */
+static int dns_send_packet ( struct dns_request *dns ) {
+ static unsigned int qid = 0;
+ size_t qlen;
+
+ /* Increment query ID */
+ dns->query.dns.id = htons ( ++qid );
+
+ DBGC ( dns, "DNS %p sending query ID %d\n", dns, qid );
+
+ /* Start retransmission timer */
+ start_timer ( &dns->timer );
+
+ /* Send the data */
+ qlen = ( ( ( void * ) dns->qinfo ) - ( ( void * ) &dns->query )
+ + sizeof ( dns->qinfo ) );
+ return xfer_deliver_raw ( &dns->socket, &dns->query, qlen );
+}
+
+/**
+ * Handle DNS retransmission timer expiry
+ *
+ * @v timer Retry timer
+ * @v fail Failure indicator
+ */
+static void dns_timer_expired ( struct retry_timer *timer, int fail ) {
+ struct dns_request *dns =
+ container_of ( timer, struct dns_request, timer );
+
+ if ( fail ) {
+ dns_done ( dns, -ETIMEDOUT );
+ } else {
+ dns_send_packet ( dns );
+ }
+}
+
+/**
+ * Receive new data
+ *
+ * @v socket UDP socket
+ * @v data DNS reply
+ * @v len Length of DNS reply
+ * @ret rc Return status code
+ */
+static int dns_xfer_deliver_raw ( struct xfer_interface *socket,
+ const void *data, size_t len ) {
+ struct dns_request *dns =
+ container_of ( socket, struct dns_request, socket );
+ const struct dns_header *reply = data;
+ union dns_rr_info *rr_info;
+ struct sockaddr_in *sin;
+ unsigned int qtype = dns->qinfo->qtype;
+
+ /* Sanity check */
+ if ( len < sizeof ( *reply ) ) {
+ DBGC ( dns, "DNS %p received underlength packet length %zd\n",
+ dns, len );
+ return -EINVAL;
+ }
+
+ /* Check reply ID matches query ID */
+ if ( reply->id != dns->query.dns.id ) {
+ DBGC ( dns, "DNS %p received unexpected reply ID %d "
+ "(wanted %d)\n", dns, ntohs ( reply->id ),
+ ntohs ( dns->query.dns.id ) );
+ return -EINVAL;
+ }
+
+ DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id ));
+
+ /* Stop the retry timer. After this point, each code path
+ * must either restart the timer by calling dns_send_packet(),
+ * or mark the DNS operation as complete by calling
+ * dns_done()
+ */
+ stop_timer ( &dns->timer );
+
+ /* Search through response for useful answers. Do this
+ * multiple times, to take advantage of useful nameservers
+ * which send us e.g. the CNAME *and* the A record for the
+ * pointed-to name.
+ */
+ while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) {
+ switch ( rr_info->common.type ) {
+
+ case htons ( DNS_TYPE_A ):
+
+ /* Found the target A record */
+ DBGC ( dns, "DNS %p found address %s\n",
+ dns, inet_ntoa ( rr_info->a.in_addr ) );
+ sin = ( struct sockaddr_in * ) &dns->sa;
+ sin->sin_family = AF_INET;
+ sin->sin_addr = rr_info->a.in_addr;
+
+ /* Mark operation as complete */
+ dns_done ( dns, 0 );
+ return 0;
+
+ case htons ( DNS_TYPE_CNAME ):
+
+ /* Found a CNAME record; update query and recurse */
+ DBGC ( dns, "DNS %p found CNAME\n", dns );
+ dns->qinfo = ( void * ) dns_decompress_name ( reply,
+ rr_info->cname.cname,
+ dns->query.payload );
+ dns->qinfo->qtype = htons ( DNS_TYPE_A );
+ dns->qinfo->qclass = htons ( DNS_CLASS_IN );
+
+ /* Terminate the operation if we recurse too far */
+ if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) {
+ DBGC ( dns, "DNS %p recursion exceeded\n",
+ dns );
+ dns_done ( dns, -ELOOP );
+ return 0;
+ }
+ break;
+
+ default:
+ DBGC ( dns, "DNS %p got unknown record type %d\n",
+ dns, ntohs ( rr_info->common.type ) );
+ break;
+ }
+ }
+
+ /* Determine what to do next based on the type of query we
+ * issued and the reponse we received
+ */
+ switch ( qtype ) {
+
+ case htons ( DNS_TYPE_A ):
+ /* We asked for an A record and got nothing;
+ * try the CNAME.
+ */
+ DBGC ( dns, "DNS %p found no A record; trying CNAME\n", dns );
+ dns->qinfo->qtype = htons ( DNS_TYPE_CNAME );
+ dns_send_packet ( dns );
+ return 0;
+
+ case htons ( DNS_TYPE_CNAME ):
+ /* We asked for a CNAME record. If we got a response
+ * (i.e. if the next A query is already set up), then
+ * issue it, otherwise abort.
+ */
+ if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) {
+ dns_send_packet ( dns );
+ return 0;
+ } else {
+ DBGC ( dns, "DNS %p found no CNAME record\n", dns );
+ dns_done ( dns, -ENXIO );
+ return 0;
+ }
+
+ default:
+ assert ( 0 );
+ dns_done ( dns, -EINVAL );
+ return 0;
+ }
+}
+
+/**
+ * Receive new data
+ *
+ * @v socket UDP socket
+ * @v rc Reason for close
+ */
+static void dns_xfer_close ( struct xfer_interface *socket, int rc ) {
+ struct dns_request *dns =
+ container_of ( socket, struct dns_request, socket );
+
+ if ( ! rc )
+ rc = -ECONNABORTED;
+
+ dns_done ( dns, rc );
+}
+
+/** DNS socket operations */
+static struct xfer_interface_operations dns_socket_operations = {
+ .close = dns_xfer_close,
+ .vredirect = xfer_vopen,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = dns_xfer_deliver_raw,
+};
+
+/**
+ * Resolve name using DNS
+ *
+ * @v resolv Name resolution interface
+ * @v name Name to resolve
+ * @v sa Socket address to fill in
+ * @ret rc Return status code
+ */
+static int dns_resolv ( struct resolv_interface *resolv,
+ const char *name, struct sockaddr *sa ) {
+ struct dns_request *dns;
+ int rc;
+
+ /* Fail immediately if no DNS servers */
+ if ( ! nameserver.st_family ) {
+ DBG ( "DNS not attempting to resolve \"%s\": "
+ "no DNS servers\n", name );
+ return -ENXIO;
+ }
+
+ /* Allocate DNS structure */
+ dns = zalloc ( sizeof ( *dns ) );
+ if ( ! dns )
+ return -ENOMEM;
+ resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt );
+ xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt );
+ dns->timer.expired = dns_timer_expired;
+ memcpy ( &dns->sa, sa, sizeof ( dns->sa ) );
+
+ /* Create query */
+ dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
+ DNS_FLAG_RD );
+ dns->query.dns.qdcount = htons ( 1 );
+ dns->qinfo = ( void * ) dns_make_name ( name, dns->query.payload );
+ dns->qinfo->qtype = htons ( DNS_TYPE_A );
+ dns->qinfo->qclass = htons ( DNS_CLASS_IN );
+
+ /* Open UDP connection */
+ if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
+ ( struct sockaddr * ) &nameserver,
+ NULL ) ) != 0 ) {
+ DBGC ( dns, "DNS %p could not open socket: %s\n",
+ dns, strerror ( rc ) );
+ goto err;
+ }
+
+ /* Send first DNS packet */
+ dns_send_packet ( dns );
+
+ /* Attach parent interface, mortalise self, and return */
+ resolv_plug_plug ( &dns->resolv, resolv );
+ ref_put ( &dns->refcnt );
+ return 0;
+
+ err:
+ ref_put ( &dns->refcnt );
+ return rc;
+}
+
+/** DNS name resolver */
+struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = {
+ .name = "DNS",
+ .resolv = dns_resolv,
+};
+
+/******************************************************************************
+ *
+ * Settings
+ *
+ ******************************************************************************
+ */
+
+/** DNS server setting */
+struct setting dns_setting __setting = {
+ .name = "dns",
+ .description = "DNS server",
+ .tag = DHCP_DNS_SERVERS,
+ .type = &setting_type_ipv4,
+};
+
+/**
+ * Apply nameserver setting
+ *
+ * @ret rc Return status code
+ */
+static int apply_nameserver_setting ( void ) {
+ struct sockaddr_in *sin_nameserver =
+ ( struct sockaddr_in * ) &nameserver;
+ int len;
+
+ if ( ( len = fetch_ipv4_setting ( NULL, &dns_setting,
+ &sin_nameserver->sin_addr ) ) >= 0 ){
+ sin_nameserver->sin_family = AF_INET;
+ DBG ( "DNS using nameserver %s\n",
+ inet_ntoa ( sin_nameserver->sin_addr ) );
+ }
+
+ return 0;
+}
+
+/** Nameserver setting applicator */
+struct settings_applicator nameserver_applicator __settings_applicator = {
+ .apply = apply_nameserver_setting,
+};
diff --git a/gpxe/src/net/udp/tftp.c b/gpxe/src/net/udp/tftp.c
new file mode 100644
index 00000000..e49bcf9f
--- /dev/null
+++ b/gpxe/src/net/udp/tftp.c
@@ -0,0 +1,1149 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <assert.h>
+#include <gpxe/refcnt.h>
+#include <gpxe/xfer.h>
+#include <gpxe/open.h>
+#include <gpxe/uri.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/retry.h>
+#include <gpxe/features.h>
+#include <gpxe/bitmap.h>
+#include <gpxe/settings.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/uri.h>
+#include <gpxe/tftp.h>
+
+/** @file
+ *
+ * TFTP protocol
+ *
+ */
+
+FEATURE ( FEATURE_PROTOCOL, "TFTP", DHCP_EB_FEATURE_TFTP, 1 );
+
+/**
+ * A TFTP request
+ *
+ * This data structure holds the state for an ongoing TFTP transfer.
+ */
+struct tftp_request {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** Data transfer interface */
+ struct xfer_interface xfer;
+
+ /** URI being fetched */
+ struct uri *uri;
+ /** Transport layer interface */
+ struct xfer_interface socket;
+ /** Multicast transport layer interface */
+ struct xfer_interface mc_socket;
+
+ /** Data block size
+ *
+ * This is the "blksize" option negotiated with the TFTP
+ * server. (If the TFTP server does not support TFTP options,
+ * this will default to 512).
+ */
+ unsigned int blksize;
+ /** File size
+ *
+ * This is the value returned in the "tsize" option from the
+ * TFTP server. If the TFTP server does not support the
+ * "tsize" option, this value will be zero.
+ */
+ unsigned long tsize;
+
+ /** Server port
+ *
+ * This is the port to which RRQ packets are sent.
+ */
+ unsigned int port;
+ /** Peer address
+ *
+ * The peer address is determined by the first response
+ * received to the TFTP RRQ.
+ */
+ struct sockaddr_tcpip peer;
+ /** Request flags */
+ unsigned int flags;
+ /** MTFTP timeout count */
+ unsigned int mtftp_timeouts;
+
+ /** Block bitmap */
+ struct bitmap bitmap;
+ /** Maximum known length
+ *
+ * We don't always know the file length in advance. In
+ * particular, if the TFTP server doesn't support the tsize
+ * option, or we are using MTFTP, then we don't know the file
+ * length until we see the end-of-file block (which, in the
+ * case of MTFTP, may not be the last block we see).
+ *
+ * This value is updated whenever we obtain information about
+ * the file length.
+ */
+ size_t filesize;
+ /** Retransmission timer */
+ struct retry_timer timer;
+};
+
+/** TFTP request flags */
+enum {
+ /** Send ACK packets */
+ TFTP_FL_SEND_ACK = 0x0001,
+ /** Request blksize and tsize options */
+ TFTP_FL_RRQ_SIZES = 0x0002,
+ /** Request multicast option */
+ TFTP_FL_RRQ_MULTICAST = 0x0004,
+ /** Perform MTFTP recovery on timeout */
+ TFTP_FL_MTFTP_RECOVERY = 0x0008,
+};
+
+/** Maximum number of MTFTP open requests before falling back to TFTP */
+#define MTFTP_MAX_TIMEOUTS 3
+
+/**
+ * Free TFTP request
+ *
+ * @v refcnt Reference counter
+ */
+static void tftp_free ( struct refcnt *refcnt ) {
+ struct tftp_request *tftp =
+ container_of ( refcnt, struct tftp_request, refcnt );
+
+ uri_put ( tftp->uri );
+ bitmap_free ( &tftp->bitmap );
+ free ( tftp );
+}
+
+/**
+ * Mark TFTP request as complete
+ *
+ * @v tftp TFTP connection
+ * @v rc Return status code
+ */
+static void tftp_done ( struct tftp_request *tftp, int rc ) {
+
+ DBGC ( tftp, "TFTP %p finished with status %d (%s)\n",
+ tftp, rc, strerror ( rc ) );
+
+ /* Stop the retry timer */
+ stop_timer ( &tftp->timer );
+
+ /* Close all data transfer interfaces */
+ xfer_nullify ( &tftp->socket );
+ xfer_close ( &tftp->socket, rc );
+ xfer_nullify ( &tftp->mc_socket );
+ xfer_close ( &tftp->mc_socket, rc );
+ xfer_nullify ( &tftp->xfer );
+ xfer_close ( &tftp->xfer, rc );
+}
+
+/**
+ * Reopen TFTP socket
+ *
+ * @v tftp TFTP connection
+ * @ret rc Return status code
+ */
+static int tftp_reopen ( struct tftp_request *tftp ) {
+ struct sockaddr_tcpip server;
+ int rc;
+
+ /* Close socket */
+ xfer_close ( &tftp->socket, 0 );
+
+ /* Disable ACK sending. */
+ tftp->flags &= ~TFTP_FL_SEND_ACK;
+
+ /* Reset peer address */
+ memset ( &tftp->peer, 0, sizeof ( tftp->peer ) );
+
+ /* Open socket */
+ memset ( &server, 0, sizeof ( server ) );
+ server.st_port = htons ( tftp->port );
+ if ( ( rc = xfer_open_named_socket ( &tftp->socket, SOCK_DGRAM,
+ ( struct sockaddr * ) &server,
+ tftp->uri->host, NULL ) ) != 0 ) {
+ DBGC ( tftp, "TFTP %p could not open socket: %s\n",
+ tftp, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Reopen TFTP multicast socket
+ *
+ * @v tftp TFTP connection
+ * @v local Local socket address
+ * @ret rc Return status code
+ */
+static int tftp_reopen_mc ( struct tftp_request *tftp,
+ struct sockaddr *local ) {
+ int rc;
+
+ /* Close multicast socket */
+ xfer_close ( &tftp->mc_socket, 0 );
+
+ /* Open multicast socket. We never send via this socket, so
+ * use the local address as the peer address (since the peer
+ * address cannot be NULL).
+ */
+ if ( ( rc = xfer_open_socket ( &tftp->mc_socket, SOCK_DGRAM,
+ local, local ) ) != 0 ) {
+ DBGC ( tftp, "TFTP %p could not open multicast "
+ "socket: %s\n", tftp, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Presize TFTP receive buffers and block bitmap
+ *
+ * @v tftp TFTP connection
+ * @v filesize Known minimum file size
+ * @ret rc Return status code
+ */
+static int tftp_presize ( struct tftp_request *tftp, size_t filesize ) {
+ unsigned int num_blocks;
+ int rc;
+
+ /* Do nothing if we are already large enough */
+ if ( filesize <= tftp->filesize )
+ return 0;
+
+ /* Record filesize */
+ tftp->filesize = filesize;
+
+ /* Notify recipient of file size */
+ xfer_seek ( &tftp->xfer, filesize, SEEK_SET );
+ xfer_seek ( &tftp->xfer, 0, SEEK_SET );
+
+ /* Calculate expected number of blocks. Note that files whose
+ * length is an exact multiple of the blocksize will have a
+ * trailing zero-length block, which must be included.
+ */
+ num_blocks = ( ( filesize / tftp->blksize ) + 1 );
+ if ( ( rc = bitmap_resize ( &tftp->bitmap, num_blocks ) ) != 0 ) {
+ DBGC ( tftp, "TFTP %p could not resize bitmap to %d blocks: "
+ "%s\n", tftp, num_blocks, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * TFTP requested blocksize
+ *
+ * This is treated as a global configuration parameter.
+ */
+static unsigned int tftp_request_blksize = TFTP_MAX_BLKSIZE;
+
+/**
+ * Set TFTP request blocksize
+ *
+ * @v blksize Requested block size
+ */
+void tftp_set_request_blksize ( unsigned int blksize ) {
+ if ( blksize < TFTP_DEFAULT_BLKSIZE )
+ blksize = TFTP_DEFAULT_BLKSIZE;
+ tftp_request_blksize = blksize;
+}
+
+/**
+ * MTFTP multicast receive address
+ *
+ * This is treated as a global configuration parameter.
+ */
+static struct sockaddr_in tftp_mtftp_socket = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl ( 0xefff0101 ),
+ .sin_port = htons ( 3001 ),
+};
+
+/**
+ * Set MTFTP multicast address
+ *
+ * @v address Multicast IPv4 address
+ */
+void tftp_set_mtftp_address ( struct in_addr address ) {
+ tftp_mtftp_socket.sin_addr = address;
+}
+
+/**
+ * Set MTFTP multicast port
+ *
+ * @v port Multicast port
+ */
+void tftp_set_mtftp_port ( unsigned int port ) {
+ tftp_mtftp_socket.sin_port = htons ( port );
+}
+
+/**
+ * Transmit RRQ
+ *
+ * @v tftp TFTP connection
+ * @ret rc Return status code
+ */
+static int tftp_send_rrq ( struct tftp_request *tftp ) {
+ struct tftp_rrq *rrq;
+ const char *path = tftp->uri->path;
+ size_t len = ( sizeof ( *rrq ) + strlen ( path ) + 1 /* NUL */
+ + 5 + 1 /* "octet" + NUL */
+ + 7 + 1 + 5 + 1 /* "blksize" + NUL + ddddd + NUL */
+ + 5 + 1 + 1 + 1 /* "tsize" + NUL + "0" + NUL */
+ + 9 + 1 + 1 /* "multicast" + NUL + NUL */ );
+ struct io_buffer *iobuf;
+
+ DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, path );
+
+ /* Allocate buffer */
+ iobuf = xfer_alloc_iob ( &tftp->socket, len );
+ if ( ! iobuf )
+ return -ENOMEM;
+
+ /* Build request */
+ rrq = iob_put ( iobuf, sizeof ( *rrq ) );
+ rrq->opcode = htons ( TFTP_RRQ );
+ iob_put ( iobuf, snprintf ( iobuf->tail, iob_tailroom ( iobuf ),
+ "%s%coctet", path, 0 ) + 1 );
+ if ( tftp->flags & TFTP_FL_RRQ_SIZES ) {
+ iob_put ( iobuf, snprintf ( iobuf->tail,
+ iob_tailroom ( iobuf ),
+ "blksize%c%d%ctsize%c0", 0,
+ tftp_request_blksize, 0, 0 ) + 1 );
+ }
+ if ( tftp->flags & TFTP_FL_RRQ_MULTICAST ) {
+ iob_put ( iobuf, snprintf ( iobuf->tail,
+ iob_tailroom ( iobuf ),
+ "multicast%c", 0 ) + 1 );
+ }
+
+ /* RRQ always goes to the address specified in the initial
+ * xfer_open() call
+ */
+ return xfer_deliver_iob ( &tftp->socket, iobuf );
+}
+
+/**
+ * Transmit ACK
+ *
+ * @v tftp TFTP connection
+ * @ret rc Return status code
+ */
+static int tftp_send_ack ( struct tftp_request *tftp ) {
+ struct tftp_ack *ack;
+ struct io_buffer *iobuf;
+ struct xfer_metadata meta = {
+ .dest = ( struct sockaddr * ) &tftp->peer,
+ };
+ unsigned int block;
+
+ /* Determine next required block number */
+ block = bitmap_first_gap ( &tftp->bitmap );
+ DBGC2 ( tftp, "TFTP %p sending ACK for block %d\n", tftp, block );
+
+ /* Allocate buffer */
+ iobuf = xfer_alloc_iob ( &tftp->socket, sizeof ( *ack ) );
+ if ( ! iobuf )
+ return -ENOMEM;
+
+ /* Build ACK */
+ ack = iob_put ( iobuf, sizeof ( *ack ) );
+ ack->opcode = htons ( TFTP_ACK );
+ ack->block = htons ( block );
+
+ /* ACK always goes to the peer recorded from the RRQ response */
+ return xfer_deliver_iob_meta ( &tftp->socket, iobuf, &meta );
+}
+
+/**
+ * Transmit next relevant packet
+ *
+ * @v tftp TFTP connection
+ * @ret rc Return status code
+ */
+static int tftp_send_packet ( struct tftp_request *tftp ) {
+
+ /* Update retransmission timer */
+ stop_timer ( &tftp->timer );
+ start_timer ( &tftp->timer );
+
+ /* Send RRQ or ACK as appropriate */
+ if ( ! tftp->peer.st_family ) {
+ return tftp_send_rrq ( tftp );
+ } else {
+ if ( tftp->flags & TFTP_FL_SEND_ACK ) {
+ return tftp_send_ack ( tftp );
+ } else {
+ return 0;
+ }
+ }
+}
+
+/**
+ * Handle TFTP retransmission timer expiry
+ *
+ * @v timer Retry timer
+ * @v fail Failure indicator
+ */
+static void tftp_timer_expired ( struct retry_timer *timer, int fail ) {
+ struct tftp_request *tftp =
+ container_of ( timer, struct tftp_request, timer );
+ int rc;
+
+ /* If we are doing MTFTP, attempt the various recovery strategies */
+ if ( tftp->flags & TFTP_FL_MTFTP_RECOVERY ) {
+ if ( tftp->peer.st_family ) {
+ /* If we have received any response from the server,
+ * try resending the RRQ to restart the download.
+ */
+ DBGC ( tftp, "TFTP %p attempting reopen\n", tftp );
+ if ( ( rc = tftp_reopen ( tftp ) ) != 0 )
+ goto err;
+ } else {
+ /* Fall back to plain TFTP after several attempts */
+ tftp->mtftp_timeouts++;
+ DBGC ( tftp, "TFTP %p timeout %d waiting for MTFTP "
+ "open\n", tftp, tftp->mtftp_timeouts );
+
+ if ( tftp->mtftp_timeouts > MTFTP_MAX_TIMEOUTS ) {
+ DBGC ( tftp, "TFTP %p falling back to plain "
+ "TFTP\n", tftp );
+ tftp->flags = TFTP_FL_RRQ_SIZES;
+
+ /* Close multicast socket */
+ xfer_close ( &tftp->mc_socket, 0 );
+
+ /* Reset retry timer */
+ start_timer_nodelay ( &tftp->timer );
+
+ /* The blocksize may change: discard
+ * the block bitmap
+ */
+ bitmap_free ( &tftp->bitmap );
+ memset ( &tftp->bitmap, 0,
+ sizeof ( tftp->bitmap ) );
+
+ /* Reopen on standard TFTP port */
+ tftp->port = TFTP_PORT;
+ if ( ( rc = tftp_reopen ( tftp ) ) != 0 )
+ goto err;
+ }
+ }
+ } else {
+ /* Not doing MTFTP (or have fallen back to plain
+ * TFTP); fail as per normal.
+ */
+ if ( fail ) {
+ rc = -ETIMEDOUT;
+ goto err;
+ }
+ }
+ tftp_send_packet ( tftp );
+ return;
+
+ err:
+ tftp_done ( tftp, rc );
+}
+
+/**
+ * Process TFTP "blksize" option
+ *
+ * @v tftp TFTP connection
+ * @v value Option value
+ * @ret rc Return status code
+ */
+static int tftp_process_blksize ( struct tftp_request *tftp,
+ const char *value ) {
+ char *end;
+
+ tftp->blksize = strtoul ( value, &end, 10 );
+ if ( *end ) {
+ DBGC ( tftp, "TFTP %p got invalid blksize \"%s\"\n",
+ tftp, value );
+ return -EINVAL;
+ }
+ DBGC ( tftp, "TFTP %p blksize=%d\n", tftp, tftp->blksize );
+
+ return 0;
+}
+
+/**
+ * Process TFTP "tsize" option
+ *
+ * @v tftp TFTP connection
+ * @v value Option value
+ * @ret rc Return status code
+ */
+static int tftp_process_tsize ( struct tftp_request *tftp,
+ const char *value ) {
+ char *end;
+
+ tftp->tsize = strtoul ( value, &end, 10 );
+ if ( *end ) {
+ DBGC ( tftp, "TFTP %p got invalid tsize \"%s\"\n",
+ tftp, value );
+ return -EINVAL;
+ }
+ DBGC ( tftp, "TFTP %p tsize=%ld\n", tftp, tftp->tsize );
+
+ return 0;
+}
+
+/**
+ * Process TFTP "multicast" option
+ *
+ * @v tftp TFTP connection
+ * @v value Option value
+ * @ret rc Return status code
+ */
+static int tftp_process_multicast ( struct tftp_request *tftp,
+ const char *value ) {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ } socket;
+ char buf[ strlen ( value ) + 1 ];
+ char *addr;
+ char *port;
+ char *port_end;
+ char *mc;
+ char *mc_end;
+ int rc;
+
+ /* Split value into "addr,port,mc" fields */
+ memcpy ( buf, value, sizeof ( buf ) );
+ addr = buf;
+ port = strchr ( addr, ',' );
+ if ( ! port ) {
+ DBGC ( tftp, "TFTP %p multicast missing port,mc\n", tftp );
+ return -EINVAL;
+ }
+ *(port++) = '\0';
+ mc = strchr ( port, ',' );
+ if ( ! mc ) {
+ DBGC ( tftp, "TFTP %p multicast missing mc\n", tftp );
+ return -EINVAL;
+ }
+ *(mc++) = '\0';
+
+ /* Parse parameters */
+ if ( strtoul ( mc, &mc_end, 0 ) == 0 )
+ tftp->flags &= ~TFTP_FL_SEND_ACK;
+ if ( *mc_end ) {
+ DBGC ( tftp, "TFTP %p multicast invalid mc %s\n", tftp, mc );
+ return -EINVAL;
+ }
+ DBGC ( tftp, "TFTP %p is%s the master client\n",
+ tftp, ( ( tftp->flags & TFTP_FL_SEND_ACK ) ? "" : " not" ) );
+ if ( *addr && *port ) {
+ socket.sin.sin_family = AF_INET;
+ if ( inet_aton ( addr, &socket.sin.sin_addr ) == 0 ) {
+ DBGC ( tftp, "TFTP %p multicast invalid IP address "
+ "%s\n", tftp, addr );
+ return -EINVAL;
+ }
+ DBGC ( tftp, "TFTP %p multicast IP address %s\n",
+ tftp, inet_ntoa ( socket.sin.sin_addr ) );
+ socket.sin.sin_port = htons ( strtoul ( port, &port_end, 0 ) );
+ if ( *port_end ) {
+ DBGC ( tftp, "TFTP %p multicast invalid port %s\n",
+ tftp, port );
+ return -EINVAL;
+ }
+ DBGC ( tftp, "TFTP %p multicast port %d\n",
+ tftp, ntohs ( socket.sin.sin_port ) );
+ if ( ( rc = tftp_reopen_mc ( tftp, &socket.sa ) ) != 0 )
+ return rc;
+ }
+
+ return 0;
+}
+
+/** A TFTP option */
+struct tftp_option {
+ /** Option name */
+ const char *name;
+ /** Option processor
+ *
+ * @v tftp TFTP connection
+ * @v value Option value
+ * @ret rc Return status code
+ */
+ int ( * process ) ( struct tftp_request *tftp, const char *value );
+};
+
+/** Recognised TFTP options */
+static struct tftp_option tftp_options[] = {
+ { "blksize", tftp_process_blksize },
+ { "tsize", tftp_process_tsize },
+ { "multicast", tftp_process_multicast },
+ { NULL, NULL }
+};
+
+/**
+ * Process TFTP option
+ *
+ * @v tftp TFTP connection
+ * @v name Option name
+ * @v value Option value
+ * @ret rc Return status code
+ */
+static int tftp_process_option ( struct tftp_request *tftp,
+ const char *name, const char *value ) {
+ struct tftp_option *option;
+
+ for ( option = tftp_options ; option->name ; option++ ) {
+ if ( strcasecmp ( name, option->name ) == 0 )
+ return option->process ( tftp, value );
+ }
+
+ DBGC ( tftp, "TFTP %p received unknown option \"%s\" = \"%s\"\n",
+ tftp, name, value );
+
+ /* Unknown options should be silently ignored */
+ return 0;
+}
+
+/**
+ * Receive OACK
+ *
+ * @v tftp TFTP connection
+ * @v buf Temporary data buffer
+ * @v len Length of temporary data buffer
+ * @ret rc Return status code
+ */
+static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) {
+ struct tftp_oack *oack = buf;
+ char *end = buf + len;
+ char *name;
+ char *value;
+ int rc = 0;
+
+ /* Sanity check */
+ if ( len < sizeof ( *oack ) ) {
+ DBGC ( tftp, "TFTP %p received underlength OACK packet "
+ "length %zd\n", tftp, len );
+ rc = -EINVAL;
+ goto done;
+ }
+ if ( end[-1] != '\0' ) {
+ DBGC ( tftp, "TFTP %p received OACK missing final NUL\n",
+ tftp );
+ rc = -EINVAL;
+ goto done;
+ }
+
+ /* Process each option in turn */
+ name = oack->data;
+ while ( name < end ) {
+ value = ( name + strlen ( name ) + 1 );
+ if ( value == end ) {
+ DBGC ( tftp, "TFTP %p received OACK missing value "
+ "for option \"%s\"\n", tftp, name );
+ rc = -EINVAL;
+ goto done;
+ }
+ if ( ( rc = tftp_process_option ( tftp, name, value ) ) != 0 )
+ goto done;
+ name = ( value + strlen ( value ) + 1 );
+ }
+
+ /* Process tsize information, if available */
+ if ( tftp->tsize ) {
+ if ( ( rc = tftp_presize ( tftp, tftp->tsize ) ) != 0 )
+ goto done;
+ }
+
+ /* Request next data block */
+ tftp_send_packet ( tftp );
+
+ done:
+ if ( rc )
+ tftp_done ( tftp, rc );
+ return rc;
+}
+
+/**
+ * Receive DATA
+ *
+ * @v tftp TFTP connection
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ *
+ * Takes ownership of I/O buffer.
+ */
+static int tftp_rx_data ( struct tftp_request *tftp,
+ struct io_buffer *iobuf ) {
+ struct tftp_data *data = iobuf->data;
+ struct xfer_metadata meta;
+ int block;
+ off_t offset;
+ size_t data_len;
+ int rc;
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *data ) ) {
+ DBGC ( tftp, "TFTP %p received underlength DATA packet "
+ "length %zd\n", tftp, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto done;
+ }
+
+ /* Extract data */
+ block = ( ntohs ( data->block ) - 1 );
+ offset = ( block * tftp->blksize );
+ iob_pull ( iobuf, sizeof ( *data ) );
+ data_len = iob_len ( iobuf );
+ if ( data_len > tftp->blksize ) {
+ DBGC ( tftp, "TFTP %p received overlength DATA packet "
+ "length %zd\n", tftp, data_len );
+ rc = -EINVAL;
+ goto done;
+ }
+
+ /* Deliver data */
+ memset ( &meta, 0, sizeof ( meta ) );
+ meta.whence = SEEK_SET;
+ meta.offset = offset;
+ rc = xfer_deliver_iob_meta ( &tftp->xfer, iobuf, &meta );
+ iobuf = NULL;
+ if ( rc != 0 ) {
+ DBGC ( tftp, "TFTP %p could not deliver data: %s\n",
+ tftp, strerror ( rc ) );
+ goto done;
+ }
+
+ /* Ensure block bitmap is ready */
+ if ( ( rc = tftp_presize ( tftp, ( offset + data_len ) ) ) != 0 )
+ goto done;
+
+ /* Mark block as received */
+ bitmap_set ( &tftp->bitmap, block );
+
+ /* Acknowledge block */
+ tftp_send_packet ( tftp );
+
+ /* If all blocks have been received, finish. */
+ if ( bitmap_full ( &tftp->bitmap ) )
+ tftp_done ( tftp, 0 );
+
+ done:
+ free_iob ( iobuf );
+ if ( rc )
+ tftp_done ( tftp, rc );
+ return rc;
+}
+
+/** Translation between TFTP errors and internal error numbers */
+static const int tftp_errors[] = {
+ [TFTP_ERR_FILE_NOT_FOUND] = ENOENT,
+ [TFTP_ERR_ACCESS_DENIED] = EACCES,
+ [TFTP_ERR_ILLEGAL_OP] = ENOTSUP,
+};
+
+/**
+ * Receive ERROR
+ *
+ * @v tftp TFTP connection
+ * @v buf Temporary data buffer
+ * @v len Length of temporary data buffer
+ * @ret rc Return status code
+ */
+static int tftp_rx_error ( struct tftp_request *tftp, void *buf, size_t len ) {
+ struct tftp_error *error = buf;
+ unsigned int err;
+ int rc = 0;
+
+ /* Sanity check */
+ if ( len < sizeof ( *error ) ) {
+ DBGC ( tftp, "TFTP %p received underlength ERROR packet "
+ "length %zd\n", tftp, len );
+ return -EINVAL;
+ }
+
+ DBGC ( tftp, "TFTP %p received ERROR packet with code %d, message "
+ "\"%s\"\n", tftp, ntohs ( error->errcode ), error->errmsg );
+
+ /* Determine final operation result */
+ err = ntohs ( error->errcode );
+ if ( err < ( sizeof ( tftp_errors ) / sizeof ( tftp_errors[0] ) ) )
+ rc = -tftp_errors[err];
+ if ( ! rc )
+ rc = -ENOTSUP;
+
+ /* Close TFTP request */
+ tftp_done ( tftp, rc );
+
+ return 0;
+}
+
+/**
+ * Receive new data
+ *
+ * @v tftp TFTP connection
+ * @v iobuf I/O buffer
+ * @v meta Transfer metadata, or NULL
+ * @ret rc Return status code
+ */
+static int tftp_rx ( struct tftp_request *tftp,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct sockaddr_tcpip *st_src;
+ struct tftp_common *common = iobuf->data;
+ size_t len = iob_len ( iobuf );
+ int rc = -EINVAL;
+
+ /* Sanity checks */
+ if ( len < sizeof ( *common ) ) {
+ DBGC ( tftp, "TFTP %p received underlength packet length "
+ "%zd\n", tftp, len );
+ goto done;
+ }
+ if ( ! meta ) {
+ DBGC ( tftp, "TFTP %p received packet without metadata\n",
+ tftp );
+ goto done;
+ }
+ if ( ! meta->src ) {
+ DBGC ( tftp, "TFTP %p received packet without source port\n",
+ tftp );
+ goto done;
+ }
+
+ /* Filter by TID. Set TID on first response received */
+ st_src = ( struct sockaddr_tcpip * ) meta->src;
+ if ( ! tftp->peer.st_family ) {
+ memcpy ( &tftp->peer, st_src, sizeof ( tftp->peer ) );
+ DBGC ( tftp, "TFTP %p using remote port %d\n", tftp,
+ ntohs ( tftp->peer.st_port ) );
+ } else if ( memcmp ( &tftp->peer, st_src,
+ sizeof ( tftp->peer ) ) != 0 ) {
+ DBGC ( tftp, "TFTP %p received packet from wrong source (got "
+ "%d, wanted %d)\n", tftp, ntohs ( st_src->st_port ),
+ ntohs ( tftp->peer.st_port ) );
+ goto done;
+ }
+
+ switch ( common->opcode ) {
+ case htons ( TFTP_OACK ):
+ rc = tftp_rx_oack ( tftp, iobuf->data, len );
+ break;
+ case htons ( TFTP_DATA ):
+ rc = tftp_rx_data ( tftp, iobuf );
+ iobuf = NULL;
+ break;
+ case htons ( TFTP_ERROR ):
+ rc = tftp_rx_error ( tftp, iobuf->data, len );
+ break;
+ default:
+ DBGC ( tftp, "TFTP %p received strange packet type %d\n",
+ tftp, ntohs ( common->opcode ) );
+ break;
+ };
+
+ done:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * Receive new data via socket
+ *
+ * @v socket Transport layer interface
+ * @v iobuf I/O buffer
+ * @v meta Transfer metadata, or NULL
+ * @ret rc Return status code
+ */
+static int tftp_socket_deliver_iob ( struct xfer_interface *socket,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct tftp_request *tftp =
+ container_of ( socket, struct tftp_request, socket );
+
+ /* Enable sending ACKs when we receive a unicast packet. This
+ * covers three cases:
+ *
+ * 1. Standard TFTP; we should always send ACKs, and will
+ * always receive a unicast packet before we need to send the
+ * first ACK.
+ *
+ * 2. RFC2090 multicast TFTP; the only unicast packets we will
+ * receive are the OACKs; enable sending ACKs here (before
+ * processing the OACK) and disable it when processing the
+ * multicast option if we are not the master client.
+ *
+ * 3. MTFTP; receiving a unicast datagram indicates that we
+ * are the "master client" and should send ACKs.
+ */
+ tftp->flags |= TFTP_FL_SEND_ACK;
+
+ return tftp_rx ( tftp, iobuf, meta );
+}
+
+/** TFTP socket operations */
+static struct xfer_interface_operations tftp_socket_operations = {
+ .close = ignore_xfer_close,
+ .vredirect = xfer_vopen,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = tftp_socket_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/**
+ * Receive new data via multicast socket
+ *
+ * @v mc_socket Multicast transport layer interface
+ * @v iobuf I/O buffer
+ * @v meta Transfer metadata, or NULL
+ * @ret rc Return status code
+ */
+static int tftp_mc_socket_deliver_iob ( struct xfer_interface *mc_socket,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ struct tftp_request *tftp =
+ container_of ( mc_socket, struct tftp_request, mc_socket );
+
+ return tftp_rx ( tftp, iobuf, meta );
+}
+
+/** TFTP multicast socket operations */
+static struct xfer_interface_operations tftp_mc_socket_operations = {
+ .close = ignore_xfer_close,
+ .vredirect = xfer_vopen,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = tftp_mc_socket_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/**
+ * Close TFTP data transfer interface
+ *
+ * @v xfer Data transfer interface
+ * @v rc Reason for close
+ */
+static void tftp_xfer_close ( struct xfer_interface *xfer, int rc ) {
+ struct tftp_request *tftp =
+ container_of ( xfer, struct tftp_request, xfer );
+
+ DBGC ( tftp, "TFTP %p interface closed: %s\n",
+ tftp, strerror ( rc ) );
+
+ tftp_done ( tftp, rc );
+}
+
+/** TFTP data transfer interface operations */
+static struct xfer_interface_operations tftp_xfer_operations = {
+ .close = tftp_xfer_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = xfer_deliver_as_raw,
+ .deliver_raw = ignore_xfer_deliver_raw,
+};
+
+/**
+ * Initiate TFTP/TFTM/MTFTP download
+ *
+ * @v xfer Data transfer interface
+ * @v uri Uniform Resource Identifier
+ * @ret rc Return status code
+ */
+static int tftp_core_open ( struct xfer_interface *xfer, struct uri *uri,
+ unsigned int default_port,
+ struct sockaddr *multicast,
+ unsigned int flags ) {
+ struct tftp_request *tftp;
+ int rc;
+
+ /* Sanity checks */
+ if ( ! uri->host )
+ return -EINVAL;
+ if ( ! uri->path )
+ return -EINVAL;
+
+ /* Allocate and populate TFTP structure */
+ tftp = zalloc ( sizeof ( *tftp ) );
+ if ( ! tftp )
+ return -ENOMEM;
+ tftp->refcnt.free = tftp_free;
+ xfer_init ( &tftp->xfer, &tftp_xfer_operations, &tftp->refcnt );
+ tftp->uri = uri_get ( uri );
+ xfer_init ( &tftp->socket, &tftp_socket_operations, &tftp->refcnt );
+ xfer_init ( &tftp->mc_socket, &tftp_mc_socket_operations,
+ &tftp->refcnt );
+ tftp->blksize = TFTP_DEFAULT_BLKSIZE;
+ tftp->flags = flags;
+ tftp->timer.expired = tftp_timer_expired;
+
+ /* Open socket */
+ tftp->port = uri_port ( tftp->uri, default_port );
+ if ( ( rc = tftp_reopen ( tftp ) ) != 0 )
+ goto err;
+
+ /* Open multicast socket */
+ if ( multicast ) {
+ if ( ( rc = tftp_reopen_mc ( tftp, multicast ) ) != 0 )
+ goto err;
+ }
+
+ /* Start timer to initiate RRQ */
+ start_timer_nodelay ( &tftp->timer );
+
+ /* Attach to parent interface, mortalise self, and return */
+ xfer_plug_plug ( &tftp->xfer, xfer );
+ ref_put ( &tftp->refcnt );
+ return 0;
+
+ err:
+ DBGC ( tftp, "TFTP %p could not create request: %s\n",
+ tftp, strerror ( rc ) );
+ tftp_done ( tftp, rc );
+ ref_put ( &tftp->refcnt );
+ return rc;
+}
+
+/**
+ * Initiate TFTP download
+ *
+ * @v xfer Data transfer interface
+ * @v uri Uniform Resource Identifier
+ * @ret rc Return status code
+ */
+static int tftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
+ return tftp_core_open ( xfer, uri, TFTP_PORT, NULL,
+ TFTP_FL_RRQ_SIZES );
+
+}
+
+/** TFTP URI opener */
+struct uri_opener tftp_uri_opener __uri_opener = {
+ .scheme = "tftp",
+ .open = tftp_open,
+};
+
+/**
+ * Initiate TFTM download
+ *
+ * @v xfer Data transfer interface
+ * @v uri Uniform Resource Identifier
+ * @ret rc Return status code
+ */
+static int tftm_open ( struct xfer_interface *xfer, struct uri *uri ) {
+ return tftp_core_open ( xfer, uri, TFTP_PORT, NULL,
+ ( TFTP_FL_RRQ_SIZES |
+ TFTP_FL_RRQ_MULTICAST ) );
+
+}
+
+/** TFTM URI opener */
+struct uri_opener tftm_uri_opener __uri_opener = {
+ .scheme = "tftm",
+ .open = tftm_open,
+};
+
+/**
+ * Initiate MTFTP download
+ *
+ * @v xfer Data transfer interface
+ * @v uri Uniform Resource Identifier
+ * @ret rc Return status code
+ */
+static int mtftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
+ return tftp_core_open ( xfer, uri, MTFTP_PORT,
+ ( struct sockaddr * ) &tftp_mtftp_socket,
+ TFTP_FL_MTFTP_RECOVERY );
+}
+
+/** MTFTP URI opener */
+struct uri_opener mtftp_uri_opener __uri_opener = {
+ .scheme = "mtftp",
+ .open = mtftp_open,
+};
+
+/******************************************************************************
+ *
+ * Settings
+ *
+ ******************************************************************************
+ */
+
+/** TFTP server setting */
+struct setting next_server_setting __setting = {
+ .name = "next-server",
+ .description = "TFTP server",
+ .tag = DHCP_EB_SIADDR,
+ .type = &setting_type_ipv4,
+};
+
+/**
+ * Apply TFTP configuration settings
+ *
+ * @ret rc Return status code
+ */
+static int tftp_apply_settings ( void ) {
+ static struct in_addr tftp_server = { 0 };
+ struct in_addr last_tftp_server;
+ char uri_string[32];
+ struct uri *uri;
+
+ /* Retrieve TFTP server setting */
+ last_tftp_server = tftp_server;
+ fetch_ipv4_setting ( NULL, &next_server_setting, &tftp_server );
+
+ /* If TFTP server setting has changed, set the current working
+ * URI to match. Do it only when the TFTP server has changed
+ * to try to minimise surprises to the user, who probably
+ * won't expect the CWURI to change just because they updated
+ * an unrelated setting and triggered all the settings
+ * applicators.
+ */
+ if ( tftp_server.s_addr != last_tftp_server.s_addr ) {
+ snprintf ( uri_string, sizeof ( uri_string ),
+ "tftp://%s/", inet_ntoa ( tftp_server ) );
+ uri = parse_uri ( uri_string );
+ if ( ! uri )
+ return -ENOMEM;
+ churi ( uri );
+ uri_put ( uri );
+ }
+
+ return 0;
+}
+
+/** TFTP settings applicator */
+struct settings_applicator tftp_settings_applicator __settings_applicator = {
+ .apply = tftp_apply_settings,
+};
diff --git a/gpxe/src/proto/fsp.c b/gpxe/src/proto/fsp.c
new file mode 100644
index 00000000..9a10991d
--- /dev/null
+++ b/gpxe/src/proto/fsp.c
@@ -0,0 +1,243 @@
+ /*********************************************************************\
+ * Copyright (c) 2005 by Radim Kolar (hsn-sendmail.cz) *
+ * *
+ * You may copy or modify this file in any manner you wish, provided *
+ * that this notice is always included, and that you hold the author *
+ * harmless for any loss or damage resulting from the installation or *
+ * use of this software. *
+ * *
+ * This file provides support for FSP v2 protocol written from scratch *
+ * by Radim Kolar, FSP project leader. *
+ * *
+ * ABOUT FSP *
+ * FSP is a lightweight file transfer protocol and is being used for *
+ * booting, Internet firmware updates, embedded devices and in *
+ * wireless applications. FSP is very easy to implement; contact Radim *
+ * Kolar if you need hand optimized assembler FSP stacks for various *
+ * microcontrollers, CPUs or consultations. *
+ * http://fsp.sourceforge.net/ *
+ * *
+ * REVISION HISTORY *
+ * 1.0 2005-03-17 rkolar Initial coding *
+ * 1.1 2005-03-24 rkolar We really need to send CC_BYE to the server *
+ * at end of transfer, because next stage boot *
+ * loader is unable to contact FSP server *
+ * until session timeouts. *
+ * 1.2 2005-03-26 rkolar We need to query filesize in advance, *
+ * because NBI loader do not reads file until *
+ * eof is reached.
+ * REMARKS *
+ * there is no support for selecting port number of fsp server, maybe *
+ * we should parse fsp:// URLs in boot image filename. *
+ * this implementation has filename limit 255 chars. *
+ \*********************************************************************/
+
+#ifdef DOWNLOAD_PROTO_FSP
+
+#define FSP_PORT 21
+
+/* FSP commands */
+#define CC_GET_FILE 0x42
+#define CC_BYE 0x4A
+#define CC_ERR 0x40
+#define CC_STAT 0x4D
+
+/* etherboot limits */
+#define FSP_MAXFILENAME 255
+
+struct fsp_info {
+ in_addr server_ip;
+ uint16_t server_port;
+ uint16_t local_port;
+ const char *filename;
+ int (*fnc)(unsigned char *, unsigned int, unsigned int, int);
+};
+
+struct fsp_header {
+ uint8_t cmd;
+ uint8_t sum;
+ uint16_t key;
+ uint16_t seq;
+ uint16_t len;
+ uint32_t pos;
+} PACKED;
+
+#define FSP_MAXPAYLOAD (ETH_MAX_MTU - \
+ (sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct fsp_header)))
+
+static struct fsp_request {
+ struct iphdr ip;
+ struct udphdr udp;
+ struct fsp_header fsp;
+ unsigned char data[FSP_MAXFILENAME + 1 + 2];
+} request;
+
+struct fsp_reply {
+ struct iphdr ip;
+ struct udphdr udp;
+ struct fsp_header fsp;
+ unsigned char data[FSP_MAXPAYLOAD];
+} PACKED;
+
+
+static int await_fsp(int ival, void *ptr, unsigned short ptype __unused,
+ struct iphdr *ip, struct udphdr *udp)
+{
+ if(!udp)
+ return 0;
+ if (ip->dest.s_addr != arptable[ARP_CLIENT].ipaddr.s_addr)
+ return 0;
+ if (ntohs(udp->dest) != ival)
+ return 0;
+ if (ntohs(udp->len) < 12+sizeof(struct udphdr))
+ return 0;
+ return 1;
+}
+
+static int proto_fsp(struct fsp_info *info)
+{
+ uint32_t filepos;
+ uint32_t filelength=0;
+ int i,retry;
+ uint16_t reqlen;
+ struct fsp_reply *reply;
+ int block=1;
+
+ /* prepare FSP request packet */
+ filepos=0;
+ i=strlen(info->filename);
+ if(i>FSP_MAXFILENAME)
+ {
+ printf("Boot filename is too long.\n");
+ return 0;
+ }
+ strcpy(request.data,info->filename);
+ *(uint16_t *)(request.data+i+1)=htons(FSP_MAXPAYLOAD);
+ request.fsp.len=htons(i+1);
+ reqlen=i+3+12;
+
+ rx_qdrain();
+ retry=0;
+
+ /* main loop */
+ for(;;) {
+ int sum;
+ long timeout;
+
+ /* query filelength if not known */
+ if(filelength == 0)
+ request.fsp.cmd=CC_STAT;
+
+ /* prepare request packet */
+ request.fsp.pos=htonl(filepos);
+ request.fsp.seq=random();
+ request.fsp.sum=0;
+ for(i=0,sum=reqlen;i<reqlen;i++)
+ {
+ sum += ((uint8_t *)&request.fsp)[i];
+ }
+ request.fsp.sum= sum + (sum >> 8);
+ /* send request */
+ if (!udp_transmit(info->server_ip.s_addr, info->local_port,
+ info->server_port, sizeof(request.ip) +
+ sizeof(request.udp) + reqlen, &request))
+ return (0);
+ /* wait for retry */
+#ifdef CONGESTED
+ timeout =
+ rfc2131_sleep_interval(filepos ? TFTP_REXMT : TIMEOUT, retry);
+#else
+ timeout = rfc2131_sleep_interval(TIMEOUT, retry);
+#endif
+ retry++;
+ if (!await_reply(await_fsp, info->local_port, NULL, timeout))
+ continue;
+ reply=(struct fsp_reply *) &nic.packet[ETH_HLEN];
+ /* check received packet */
+ if (reply->fsp.seq != request.fsp.seq)
+ continue;
+ reply->udp.len=ntohs(reply->udp.len)-sizeof(struct udphdr);
+ if(reply->udp.len < ntohs(reply->fsp.len) + 12 )
+ continue;
+ sum=-reply->fsp.sum;
+ for(i=0;i<reply->udp.len;i++)
+ {
+ sum += ((uint8_t *)&(reply->fsp))[i];
+ }
+ sum = (sum + (sum >> 8)) & 0xff;
+ if(sum != reply->fsp.sum)
+ {
+ printf("FSP checksum failed. computed %d, but packet has %d.\n",sum,reply->fsp.sum);
+ continue;
+ }
+ if(reply->fsp.cmd == CC_ERR)
+ {
+ printf("\nFSP error: %s",info->filename);
+ if(reply->fsp.len)
+ printf(" [%s]",reply->data);
+ printf("\n");
+ return 0;
+ }
+ if(reply->fsp.cmd == CC_BYE && filelength == 1)
+ {
+ info->fnc(request.data,block,1,1);
+ return 1;
+ }
+ if(reply->fsp.cmd == CC_STAT)
+ {
+ if(reply->data[8] == 0)
+ {
+ /* file not found, etc. */
+ filelength=0xffffffff;
+ } else
+ {
+ filelength= ntohl(*((uint32_t *)&reply->data[4]));
+ }
+ request.fsp.cmd = CC_GET_FILE;
+ request.fsp.key = reply->fsp.key;
+ retry=0;
+ continue;
+ }
+
+ if(reply->fsp.cmd == CC_GET_FILE)
+ {
+ if(ntohl(reply->fsp.pos) != filepos)
+ continue;
+ request.fsp.key = reply->fsp.key;
+ retry=0;
+ i=ntohs(reply->fsp.len);
+ if(i == 1)
+ {
+ request.fsp.cmd=CC_BYE;
+ request.data[0]=reply->data[0];
+ continue;
+ }
+ /* let last byte alone */
+ if(i >= filelength)
+ i = filelength - 1;
+ if(!info->fnc(reply->data,block++,i,0))
+ return 0;
+ filepos += i;
+ filelength -= i;
+ }
+ }
+
+ return 0;
+}
+
+int url_fsp(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
+{
+ struct fsp_info info;
+ /* Set the defaults */
+ info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
+ info.server_port = FSP_PORT;
+ info.local_port = 1024 + random() & 0xfbff;
+ info.fnc = fnc;
+
+ /* Now parse the url */
+ /* printf("fsp-URI: [%s]\n", name); */
+ /* quick hack for now */
+ info.filename=name;
+ return proto_fsp(&info);
+}
+#endif
diff --git a/gpxe/src/proto/igmp.c b/gpxe/src/proto/igmp.c
new file mode 100644
index 00000000..d711421a
--- /dev/null
+++ b/gpxe/src/proto/igmp.c
@@ -0,0 +1,167 @@
+/*
+ * Eric Biederman wrote this code originally.
+ *
+ */
+
+#if 0
+
+#include <ip.h>
+#include <igmp.h>
+
+static unsigned long last_igmpv1 = 0;
+static struct igmptable_t igmptable[MAX_IGMP];
+
+static long rfc1112_sleep_interval ( long base, int exp ) {
+ unsigned long divisor, tmo;
+
+ if ( exp > BACKOFF_LIMIT )
+ exp = BACKOFF_LIMIT;
+ divisor = RAND_MAX / ( base << exp );
+ tmo = random() / divisor;
+ return tmo;
+}
+
+static void send_igmp_reports ( unsigned long now ) {
+ struct igmp_ip_t igmp;
+ int i;
+
+ for ( i = 0 ; i < MAX_IGMP ; i++ ) {
+ if ( ! igmptable[i].time )
+ continue;
+ if ( now < igmptable[i].time )
+ continue;
+
+ igmp.router_alert[0] = 0x94;
+ igmp.router_alert[1] = 0x04;
+ igmp.router_alert[2] = 0;
+ igmp.router_alert[3] = 0;
+ build_ip_hdr ( igmptable[i].group.s_addr, 1, IP_IGMP,
+ sizeof ( igmp.router_alert ),
+ sizeof ( igmp ), &igmp );
+ igmp.igmp.type = IGMPv2_REPORT;
+ if ( last_igmpv1 &&
+ ( now < last_igmpv1 + IGMPv1_ROUTER_PRESENT_TIMEOUT ) ) {
+ igmp.igmp.type = IGMPv1_REPORT;
+ }
+ igmp.igmp.response_time = 0;
+ igmp.igmp.chksum = 0;
+ igmp.igmp.group.s_addr = igmptable[i].group.s_addr;
+ igmp.igmp.chksum = ipchksum ( &igmp.igmp,
+ sizeof ( igmp.igmp ) );
+ ip_transmit ( sizeof ( igmp ), &igmp );
+ DBG ( "IGMP sent report to %s\n", inet_ntoa ( igmp.igmp.group ) );
+ /* Don't send another igmp report until asked */
+ igmptable[i].time = 0;
+ }
+}
+
+static void process_igmp ( unsigned long now, unsigned short ptype __unused,
+ struct iphdr *ip ) {
+ struct igmp *igmp;
+ int i;
+ unsigned iplen;
+
+ if ( ( ! ip ) || ( ip->protocol != IP_IGMP ) ||
+ ( nic.packetlen < ( sizeof ( struct iphdr ) +
+ sizeof ( struct igmp ) ) ) ) {
+ return;
+ }
+
+ iplen = ( ip->verhdrlen & 0xf ) * 4;
+ igmp = ( struct igmp * ) &nic.packet[ sizeof( struct iphdr ) ];
+ if ( ipchksum ( igmp, ntohs ( ip->len ) - iplen ) != 0 )
+ return;
+
+ if ( ( igmp->type == IGMP_QUERY ) &&
+ ( ip->dest.s_addr == htonl ( GROUP_ALL_HOSTS ) ) ) {
+ unsigned long interval = IGMP_INTERVAL;
+
+ if ( igmp->response_time == 0 ) {
+ last_igmpv1 = now;
+ } else {
+ interval = ( igmp->response_time * TICKS_PER_SEC ) /10;
+ }
+
+ DBG ( "IGMP received query for %s\n", inet_ntoa ( igmp->group ) );
+ for ( i = 0 ; i < MAX_IGMP ; i++ ) {
+ uint32_t group = igmptable[i].group.s_addr;
+ if ( ( group == 0 ) ||
+ ( group == igmp->group.s_addr ) ) {
+ unsigned long time;
+ time = currticks() +
+ rfc1112_sleep_interval ( interval, 0 );
+ if ( time < igmptable[i].time ) {
+ igmptable[i].time = time;
+ }
+ }
+ }
+ }
+ if ( ( ( igmp->type == IGMPv1_REPORT ) ||
+ ( igmp->type == IGMPv2_REPORT ) ) &&
+ ( ip->dest.s_addr == igmp->group.s_addr ) ) {
+ DBG ( "IGMP received report for %s\n",
+ inet_ntoa ( igmp->group ) );
+ for ( i = 0 ; i < MAX_IGMP ; i++ ) {
+ if ( ( igmptable[i].group.s_addr ==
+ igmp->group.s_addr ) &&
+ ( igmptable[i].time != 0 ) ) {
+ igmptable[i].time = 0;
+ }
+ }
+ }
+}
+
+struct background igmp_background __background = {
+ .send = send_igmp_reports,
+ .process = process_igmp,
+};
+
+void leave_group ( int slot ) {
+ /* Be very stupid and always send a leave group message if
+ * I have subscribed. Imperfect but it is standards
+ * compliant, easy and reliable to implement.
+ *
+ * The optimal group leave method is to only send leave when,
+ * we were the last host to respond to a query on this group,
+ * and igmpv1 compatibility is not enabled.
+ */
+ if ( igmptable[slot].group.s_addr ) {
+ struct igmp_ip_t igmp;
+
+ igmp.router_alert[0] = 0x94;
+ igmp.router_alert[1] = 0x04;
+ igmp.router_alert[2] = 0;
+ igmp.router_alert[3] = 0;
+ build_ip_hdr ( htonl ( GROUP_ALL_HOSTS ), 1, IP_IGMP,
+ sizeof ( igmp.router_alert ), sizeof ( igmp ),
+ &igmp);
+ igmp.igmp.type = IGMP_LEAVE;
+ igmp.igmp.response_time = 0;
+ igmp.igmp.chksum = 0;
+ igmp.igmp.group.s_addr = igmptable[slot].group.s_addr;
+ igmp.igmp.chksum = ipchksum ( &igmp.igmp, sizeof ( igmp ) );
+ ip_transmit ( sizeof ( igmp ), &igmp );
+ DBG ( "IGMP left group %s\n", inet_ntoa ( igmp.igmp.group ) );
+ }
+ memset ( &igmptable[slot], 0, sizeof ( igmptable[0] ) );
+}
+
+void join_group ( int slot, unsigned long group ) {
+ /* I have already joined */
+ if ( igmptable[slot].group.s_addr == group )
+ return;
+ if ( igmptable[slot].group.s_addr ) {
+ leave_group ( slot );
+ }
+ /* Only join a group if we are given a multicast ip, this way
+ * code can be given a non-multicast (broadcast or unicast ip)
+ * and still work...
+ */
+ if ( ( group & htonl ( MULTICAST_MASK ) ) ==
+ htonl ( MULTICAST_NETWORK ) ) {
+ igmptable[slot].group.s_addr = group;
+ igmptable[slot].time = currticks();
+ }
+}
+
+#endif
diff --git a/gpxe/src/proto/nfs.c b/gpxe/src/proto/nfs.c
new file mode 100644
index 00000000..943081a2
--- /dev/null
+++ b/gpxe/src/proto/nfs.c
@@ -0,0 +1,616 @@
+#if 0
+
+#include <gpxe/init.h>
+#include <gpxe/in.h>
+
+/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
+ * large portions are copied verbatim) as distributed in OSKit 0.97. A few
+ * changes were necessary to adapt the code to Etherboot and to fix several
+ * inconsistencies. Also the RPC message preparation is done "by hand" to
+ * avoid adding netsprintf() which I find hard to understand and use. */
+
+/* NOTE 2: Etherboot does not care about things beyond the kernel image, so
+ * it loads the kernel image off the boot server (ARP_SERVER) and does not
+ * access the client root disk (root-path in dhcpd.conf), which would use
+ * ARP_ROOTSERVER. The root disk is something the operating system we are
+ * about to load needs to use. This is different from the OSKit 0.97 logic. */
+
+/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
+ * If a symlink is encountered, it is followed as far as possible (recursion
+ * possible, maximum 16 steps). There is no clearing of ".."'s inside the
+ * path, so please DON'T DO THAT. thx. */
+
+#define START_OPORT 700 /* mountd usually insists on secure ports */
+#define OPORT_SWEEP 200 /* make sure we don't leave secure range */
+
+static int oport = START_OPORT;
+static struct sockaddr_in mount_server;
+static struct sockaddr_in nfs_server;
+static unsigned long rpc_id;
+
+/**************************************************************************
+RPC_INIT - set up the ID counter to something fairly random
+**************************************************************************/
+void rpc_init(void)
+{
+ unsigned long t;
+
+ t = currticks();
+ rpc_id = t ^ (t << 8) ^ (t << 16);
+}
+
+/**************************************************************************
+RPC_PRINTERROR - Print a low level RPC error message
+**************************************************************************/
+static void rpc_printerror(struct rpc_t *rpc)
+{
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus) {
+ /* rpc_printerror() is called for any RPC related error,
+ * suppress output if no low level RPC error happened. */
+ DBG("RPC error: (%ld,%ld,%ld)\n", ntohl(rpc->u.reply.rstatus),
+ ntohl(rpc->u.reply.verifier),
+ ntohl(rpc->u.reply.astatus));
+ }
+}
+
+/**************************************************************************
+AWAIT_RPC - Wait for an rpc packet
+**************************************************************************/
+static int await_rpc(int ival, void *ptr,
+ unsigned short ptype __unused, struct iphdr *ip,
+ struct udphdr *udp, struct tcphdr *tcp __unused)
+{
+ struct rpc_t *rpc;
+ if (!udp)
+ return 0;
+ if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
+ return 0;
+ if (ntohs(udp->dest) != ival)
+ return 0;
+ if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + 8)
+ return 0;
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (*(unsigned long *)ptr != ntohl(rpc->u.reply.id))
+ return 0;
+ if (MSG_REPLY != ntohl(rpc->u.reply.type))
+ return 0;
+ return 1;
+}
+
+/**************************************************************************
+RPC_LOOKUP - Lookup RPC Port numbers
+**************************************************************************/
+static int rpc_lookup(struct sockaddr_in *addr, int prog, int ver, int sport)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ int retries;
+ long *p;
+
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_PORTMAP);
+ buf.u.call.vers = htonl(2); /* portmapper is version 2 */
+ buf.u.call.proc = htonl(PORTMAP_GETPORT);
+ p = (long *)buf.u.call.data;
+ *p++ = 0; *p++ = 0; /* auth credential */
+ *p++ = 0; *p++ = 0; /* auth verifier */
+ *p++ = htonl(prog);
+ *p++ = htonl(ver);
+ *p++ = htonl(IP_UDP);
+ *p++ = 0;
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout;
+ udp_transmit(addr->sin_addr.s_addr, sport, addr->sin_port,
+ (char *)p - (char *)&buf, &buf);
+ timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ if (await_reply(await_rpc, sport, &id, timeout)) {
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus) {
+ rpc_printerror(rpc);
+ return 0;
+ } else {
+ return ntohl(rpc->u.reply.data[0]);
+ }
+ }
+ }
+ return 0;
+}
+
+/**************************************************************************
+RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
+**************************************************************************/
+static long *rpc_add_credentials(long *p)
+{
+ int hl;
+
+ /* Here's the executive summary on authentication requirements of the
+ * various NFS server implementations: Linux accepts both AUTH_NONE
+ * and AUTH_UNIX authentication (also accepts an empty hostname field
+ * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
+ * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
+ * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
+ * it (if the BOOTP/DHCP reply didn't give one, just use an empty
+ * hostname). */
+
+ hl = (hostnamelen + 3) & ~3;
+
+ /* Provide an AUTH_UNIX credential. */
+ *p++ = htonl(1); /* AUTH_UNIX */
+ *p++ = htonl(hl+20); /* auth length */
+ *p++ = htonl(0); /* stamp */
+ *p++ = htonl(hostnamelen); /* hostname string */
+ if (hostnamelen & 3) {
+ *(p + hostnamelen / 4) = 0; /* add zero padding */
+ }
+ memcpy(p, hostname, hostnamelen);
+ p += hl / 4;
+ *p++ = 0; /* uid */
+ *p++ = 0; /* gid */
+ *p++ = 0; /* auxiliary gid list */
+
+ /* Provide an AUTH_NONE verifier. */
+ *p++ = 0; /* AUTH_NONE */
+ *p++ = 0; /* auth length */
+
+ return p;
+}
+
+/**************************************************************************
+NFS_PRINTERROR - Print a NFS error message
+**************************************************************************/
+static void nfs_printerror(int err)
+{
+ switch (-err) {
+ case NFSERR_PERM:
+ printf("Not owner\n");
+ break;
+ case NFSERR_NOENT:
+ printf("No such file or directory\n");
+ break;
+ case NFSERR_ACCES:
+ printf("Permission denied\n");
+ break;
+ case NFSERR_ISDIR:
+ printf("Directory given where filename expected\n");
+ break;
+ case NFSERR_INVAL:
+ printf("Invalid filehandle\n");
+ break; // INVAL is not defined in NFSv2, some NFS-servers
+ // seem to use it in answers to v2 nevertheless.
+ case 9998:
+ printf("low-level RPC failure (parameter decoding problem?)\n");
+ break;
+ case 9999:
+ printf("low-level RPC failure (authentication problem?)\n");
+ break;
+ default:
+ printf("Unknown NFS error %d\n", -err);
+ }
+}
+
+/**************************************************************************
+NFS_MOUNT - Mount an NFS Filesystem
+**************************************************************************/
+static int nfs_mount(struct sockaddr_in *server, char *path, char *fh, int sport)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ int retries;
+ long *p;
+ int pathlen = strlen(path);
+
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_MOUNT);
+ buf.u.call.vers = htonl(1); /* mountd is version 1 */
+ buf.u.call.proc = htonl(MOUNT_ADDENTRY);
+ p = rpc_add_credentials((long *)buf.u.call.data);
+ *p++ = htonl(pathlen);
+ if (pathlen & 3) {
+ *(p + pathlen / 4) = 0; /* add zero padding */
+ }
+ memcpy(p, path, pathlen);
+ p += (pathlen + 3) / 4;
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout;
+ udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
+ (char *)p - (char *)&buf, &buf);
+ timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ if (await_reply(await_rpc, sport, &id, timeout)) {
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus || rpc->u.reply.data[0]) {
+ rpc_printerror(rpc);
+ if (rpc->u.reply.rstatus) {
+ /* RPC failed, no verifier, data[0] */
+ return -9999;
+ }
+ if (rpc->u.reply.astatus) {
+ /* RPC couldn't decode parameters */
+ return -9998;
+ }
+ return -ntohl(rpc->u.reply.data[0]);
+ } else {
+ memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+/**************************************************************************
+NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
+**************************************************************************/
+static void nfs_umountall(struct sockaddr_in *server)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ int retries;
+ long *p;
+
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_MOUNT);
+ buf.u.call.vers = htonl(1); /* mountd is version 1 */
+ buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
+ p = rpc_add_credentials((long *)buf.u.call.data);
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ udp_transmit(server->sin_addr.s_addr, oport, server->sin_port,
+ (char *)p - (char *)&buf, &buf);
+ if (await_reply(await_rpc, oport, &id, timeout)) {
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus) {
+ rpc_printerror(rpc);
+ }
+ break;
+ }
+ }
+}
+
+/**************************************************************************
+NFS_RESET - Reset the NFS subsystem
+**************************************************************************/
+static void nfs_reset ( void ) {
+ /* If we have a mount server, call nfs_umountall() */
+ if ( mount_server.sin_addr.s_addr ) {
+ nfs_umountall ( &mount_server );
+ }
+ /* Zero the data structures */
+ memset ( &mount_server, 0, sizeof ( mount_server ) );
+ memset ( &nfs_server, 0, sizeof ( nfs_server ) );
+}
+
+/***************************************************************************
+ * NFS_READLINK (AH 2003-07-14)
+ * This procedure is called when read of the first block fails -
+ * this probably happens when it's a directory or a symlink
+ * In case of successful readlink(), the dirname is manipulated,
+ * so that inside the nfs() function a recursion can be done.
+ **************************************************************************/
+static int nfs_readlink(struct sockaddr_in *server, char *fh __unused,
+ char *path, char *nfh, int sport)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ long *p;
+ int retries;
+ int pathlen = strlen(path);
+
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_NFS);
+ buf.u.call.vers = htonl(2); /* nfsd is version 2 */
+ buf.u.call.proc = htonl(NFS_READLINK);
+ p = rpc_add_credentials((long *)buf.u.call.data);
+ memcpy(p, nfh, NFS_FHSIZE);
+ p += (NFS_FHSIZE / 4);
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
+ (char *)p - (char *)&buf, &buf);
+ if (await_reply(await_rpc, sport, &id, timeout)) {
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus || rpc->u.reply.data[0]) {
+ rpc_printerror(rpc);
+ if (rpc->u.reply.rstatus) {
+ /* RPC failed, no verifier, data[0] */
+ return -9999;
+ }
+ if (rpc->u.reply.astatus) {
+ /* RPC couldn't decode parameters */
+ return -9998;
+ }
+ return -ntohl(rpc->u.reply.data[0]);
+ } else {
+ // It *is* a link.
+ // If it's a relative link, append everything to dirname, filename TOO!
+ retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
+ if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
+ path[pathlen++] = '/';
+ while ( ( retries + pathlen ) > 298 ) {
+ retries--;
+ }
+ if ( retries > 0 ) {
+ memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
+ } else { retries = 0; }
+ path[pathlen + retries] = 0;
+ } else {
+ // Else make it the only path.
+ if ( retries > 298 ) { retries = 298; }
+ memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
+ path[retries] = 0;
+ }
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+/**************************************************************************
+NFS_LOOKUP - Lookup Pathname
+**************************************************************************/
+static int nfs_lookup(struct sockaddr_in *server, char *fh, char *path, char *nfh,
+ int sport)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ long *p;
+ int retries;
+ int pathlen = strlen(path);
+
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_NFS);
+ buf.u.call.vers = htonl(2); /* nfsd is version 2 */
+ buf.u.call.proc = htonl(NFS_LOOKUP);
+ p = rpc_add_credentials((long *)buf.u.call.data);
+ memcpy(p, fh, NFS_FHSIZE);
+ p += (NFS_FHSIZE / 4);
+ *p++ = htonl(pathlen);
+ if (pathlen & 3) {
+ *(p + pathlen / 4) = 0; /* add zero padding */
+ }
+ memcpy(p, path, pathlen);
+ p += (pathlen + 3) / 4;
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
+ (char *)p - (char *)&buf, &buf);
+ if (await_reply(await_rpc, sport, &id, timeout)) {
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus || rpc->u.reply.data[0]) {
+ rpc_printerror(rpc);
+ if (rpc->u.reply.rstatus) {
+ /* RPC failed, no verifier, data[0] */
+ return -9999;
+ }
+ if (rpc->u.reply.astatus) {
+ /* RPC couldn't decode parameters */
+ return -9998;
+ }
+ return -ntohl(rpc->u.reply.data[0]);
+ } else {
+ memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+/**************************************************************************
+NFS_READ - Read File on NFS Server
+**************************************************************************/
+static int nfs_read(struct sockaddr_in *server, char *fh, int offset, int len,
+ int sport)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ int retries;
+ long *p;
+
+ static int tokens=0;
+ /*
+ * Try to implement something similar to a window protocol in
+ * terms of response to losses. On successful receive, increment
+ * the number of tokens by 1 (cap at 256). On failure, halve it.
+ * When the number of tokens is >= 2, use a very short timeout.
+ */
+
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_NFS);
+ buf.u.call.vers = htonl(2); /* nfsd is version 2 */
+ buf.u.call.proc = htonl(NFS_READ);
+ p = rpc_add_credentials((long *)buf.u.call.data);
+ memcpy(p, fh, NFS_FHSIZE);
+ p += NFS_FHSIZE / 4;
+ *p++ = htonl(offset);
+ *p++ = htonl(len);
+ *p++ = 0; /* unused parameter */
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ if (tokens >= 2)
+ timeout = TICKS_PER_SEC/2;
+
+ udp_transmit(server->sin_addr.s_addr, sport, server->sin_port,
+ (char *)p - (char *)&buf, &buf);
+ if (await_reply(await_rpc, sport, &id, timeout)) {
+ if (tokens < 256)
+ tokens++;
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus || rpc->u.reply.data[0]) {
+ rpc_printerror(rpc);
+ if (rpc->u.reply.rstatus) {
+ /* RPC failed, no verifier, data[0] */
+ return -9999;
+ }
+ if (rpc->u.reply.astatus) {
+ /* RPC couldn't decode parameters */
+ return -9998;
+ }
+ return -ntohl(rpc->u.reply.data[0]);
+ } else {
+ return 0;
+ }
+ } else
+ tokens >>= 1;
+ }
+ return -1;
+}
+
+/**************************************************************************
+NFS - Download extended BOOTP data, or kernel image from NFS server
+**************************************************************************/
+static int nfs ( char *url __unused, struct sockaddr_in *server,
+ char *name, struct buffer *buffer ) {
+ static int recursion = 0;
+ int sport;
+ int err, namelen = strlen(name);
+ char dirname[300], *fname;
+ char dirfh[NFS_FHSIZE]; /* file handle of directory */
+ char filefh[NFS_FHSIZE]; /* file handle of kernel image */
+ int rlen, size, offs, len;
+ struct rpc_t *rpc;
+
+ sport = oport++;
+ if (oport > START_OPORT+OPORT_SWEEP) {
+ oport = START_OPORT;
+ }
+
+ mount_server.sin_addr = nfs_server.sin_addr = server->sin_addr;
+ mount_server.sin_port = rpc_lookup(server, PROG_MOUNT, 1, sport);
+ if ( ! mount_server.sin_port ) {
+ DBG ( "Cannot get mount port from %s:%d\n",
+ inet_ntoa ( server->sin_addr ), server->sin_port );
+ return 0;
+ }
+ nfs_server.sin_port = rpc_lookup(server, PROG_NFS, 2, sport);
+ if ( ! mount_server.sin_port ) {
+ DBG ( "Cannot get nfs port from %s:%d\n",
+ inet_ntoa ( server->sin_addr ), server->sin_port );
+ return 0;
+ }
+
+ if ( name != dirname ) {
+ memcpy(dirname, name, namelen + 1);
+ }
+ recursion = 0;
+nfssymlink:
+ if ( recursion > NFS_MAXLINKDEPTH ) {
+ DBG ( "\nRecursion: More than %d symlinks followed. Abort.\n",
+ NFS_MAXLINKDEPTH );
+ return 0;
+ }
+ recursion++;
+ fname = dirname + (namelen - 1);
+ while (fname >= dirname) {
+ if (*fname == '/') {
+ *fname = '\0';
+ fname++;
+ break;
+ }
+ fname--;
+ }
+ if (fname < dirname) {
+ DBG("can't parse file name %s\n", name);
+ return 0;
+ }
+
+ err = nfs_mount(&mount_server, dirname, dirfh, sport);
+ if (err) {
+ DBG("mounting %s: ", dirname);
+ nfs_printerror(err);
+ /* just to be sure... */
+ nfs_reset();
+ return 0;
+ }
+
+ err = nfs_lookup(&nfs_server, dirfh, fname, filefh, sport);
+ if (err) {
+ DBG("looking up %s: ", fname);
+ nfs_printerror(err);
+ nfs_reset();
+ return 0;
+ }
+
+ offs = 0;
+ size = -1; /* will be set properly with the first reply */
+ len = NFS_READ_SIZE; /* first request is always full size */
+ do {
+ err = nfs_read(&nfs_server, filefh, offs, len, sport);
+ if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
+ // An error occured. NFS servers tend to sending
+ // errors 21 / 22 when symlink instead of real file
+ // is requested. So check if it's a symlink!
+ if ( nfs_readlink(&nfs_server, dirfh, dirname,
+ filefh, sport) == 0 ) {
+ printf("\nLoading symlink:%s ..",dirname);
+ goto nfssymlink;
+ }
+ nfs_printerror(err);
+ nfs_reset();
+ return 0;
+ }
+ if (err) {
+ printf("\nError reading at offset %d: ", offs);
+ nfs_printerror(err);
+ nfs_reset();
+ return 0;
+ }
+
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+
+ /* size must be found out early to allow EOF detection */
+ if (size == -1) {
+ size = ntohl(rpc->u.reply.data[6]);
+ }
+ rlen = ntohl(rpc->u.reply.data[18]);
+ if (rlen > len) {
+ rlen = len; /* shouldn't happen... */
+ }
+
+ if ( ! fill_buffer ( buffer, &rpc->u.reply.data[19],
+ offs, rlen ) ) {
+ nfs_reset();
+ return 0;
+ }
+
+ offs += rlen;
+ /* last request is done with matching requested read size */
+ if (size-offs < NFS_READ_SIZE) {
+ len = size-offs;
+ }
+ } while (len != 0);
+ /* len == 0 means that all the file has been read */
+ return 1;
+}
+
+struct protocol nfs_protocol __protocol = {
+ .name = "nfs",
+ .default_port = SUNRPC_PORT,
+ .load = nfs,
+};
+
+#endif
diff --git a/gpxe/src/proto/nmb.c b/gpxe/src/proto/nmb.c
new file mode 100644
index 00000000..e1fc911e
--- /dev/null
+++ b/gpxe/src/proto/nmb.c
@@ -0,0 +1,110 @@
+#if 0
+
+#include "resolv.h"
+#include "string.h"
+#include <gpxe/dns.h>
+#include "nic.h"
+#include "nmb.h"
+
+/*
+ * Convert a standard NUL-terminated string to an NBNS query name.
+ *
+ * Returns a pointer to the character following the constructed NBNS
+ * query name.
+ *
+ */
+static inline char * nbns_make_name ( char *dest, const char *name ) {
+ char nb_name[16];
+ char c;
+ int i;
+ uint16_t *d;
+
+ *(dest++) = 32; /* Length is always 32 */
+
+ /* Name encoding is as follows: pad the name with spaces to
+ * length 15, and add a NUL. Take this 16-byte string, split
+ * it into nibbles and add 0x41 to each nibble to form a byte
+ * of the resulting name string.
+ */
+ memset ( nb_name, ' ', 15 );
+ nb_name[15] = '\0';
+ memcpy ( nb_name, name, strlen ( name ) ); /* Do not copy NUL */
+
+ d = ( uint16_t * ) dest;
+ for ( i = 0 ; i < 16 ; i++ ) {
+ c = nb_name[i];
+ *( d++ ) = htons ( ( ( c | ( c << 4 ) ) & 0x0f0f ) + 0x4141 );
+ }
+ dest = ( char * ) d;
+
+ *(dest++) = 0; /* Terminating 0-length name component */
+ return dest;
+}
+
+/*
+ * Resolve a name using NMB
+ *
+ */
+static int nmb_resolv ( struct in_addr *addr, const char *name ) {
+ struct dns_query query;
+ struct dns_query_info *query_info;
+ struct dns_header *reply;
+ struct dns_rr_info *rr_info;
+ struct dns_rr_info_nb *rr_info_nb;
+ struct sockaddr_in nameserver;
+
+ DBG ( "NMB resolving %s\n", name );
+
+ /* Set up the query data */
+ nameserver.sin_addr.s_addr = INADDR_BROADCAST;
+ nameserver.sin_port = NBNS_UDP_PORT;
+ memset ( &query, 0, sizeof ( query ) );
+ query.dns.id = htons ( 1 );
+ query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
+ DNS_FLAG_RD | DNS_FLAG_BROADCAST );
+ query.dns.qdcount = htons ( 1 );
+ query_info = ( void * ) nbns_make_name ( query.payload, name );
+ query_info->qtype = htons ( DNS_TYPE_NB );
+ query_info->qclass = htons ( DNS_CLASS_IN );
+
+ /* Issue query, wait for reply */
+ reply = dns_query ( &query,
+ ( ( ( char * ) query_info )
+ + sizeof ( *query_info )
+ - ( ( char * ) &query ) ),
+ &nameserver );
+ if ( ! reply ) {
+ DBG ( "NMB got no response via %@ (port %d)\n",
+ nameserver.sin_addr.s_addr,
+ nameserver.sin_port );
+ return 0;
+ }
+
+ /* Search through response for useful answers. */
+ rr_info = dns_find_rr ( &query, reply );
+ if ( ! rr_info ) {
+ DBG ( "NMB got invalid response\n" );
+ return 0;
+ }
+
+ /* Check type of response */
+ if ( ntohs ( rr_info->type ) != DNS_TYPE_NB ) {
+ DBG ( "NMB got answer type %hx (wanted %hx)\n",
+ ntohs ( rr_info->type ), DNS_TYPE_NB );
+ return 0;
+ }
+
+ /* Read response */
+ rr_info_nb = ( struct dns_rr_info_nb * ) rr_info;
+ *addr = rr_info_nb->nb_address;
+ DBG ( "NMB found address %@\n", addr->s_addr );
+
+ return 1;
+}
+
+struct resolver nmb_resolver __resolver = {
+ .name = "NMB",
+ .resolv = nmb_resolv,
+};
+
+#endif
diff --git a/gpxe/src/proto/slam.c b/gpxe/src/proto/slam.c
new file mode 100644
index 00000000..a25c30de
--- /dev/null
+++ b/gpxe/src/proto/slam.c
@@ -0,0 +1,541 @@
+#if 0
+
+/*
+ * IMPORTANT
+ *
+ * This file should be rewritten to avoid the use of a bitmap. Our
+ * buffer routines can cope with being handed blocks in an arbitrary
+ * order, duplicate blocks, etc. This code could be substantially
+ * simplified by taking advantage of these features.
+ *
+ */
+
+#define SLAM_PORT 10000
+#define SLAM_MULTICAST_IP ((239<<24)|(255<<16)|(1<<8)|(1<<0))
+#define SLAM_MULTICAST_PORT 10000
+#define SLAM_LOCAL_PORT 10000
+
+/* Set the timeout intervals to at least 1 second so
+ * on a 100Mbit ethernet can receive 10000 packets
+ * in one second.
+ *
+ * The only case that is likely to trigger all of the nodes
+ * firing a nack packet is a slow server. The odds of this
+ * happening could be reduced being slightly smarter and utilizing
+ * the multicast channels for nacks. But that only improves the odds
+ * it doesn't improve the worst case. So unless this proves to be
+ * a common case having the control data going unicast should increase
+ * the odds of the data not being dropped.
+ *
+ * When doing exponential backoff we increase just the timeout
+ * interval and not the base to optimize for throughput. This is only
+ * expected to happen when the server is down. So having some nodes
+ * pinging immediately should get the transmission restarted quickly after a
+ * server restart. The host nic won't be to baddly swamped because of
+ * the random distribution of the nodes.
+ *
+ */
+#define SLAM_INITIAL_MIN_TIMEOUT (TICKS_PER_SEC/3)
+#define SLAM_INITIAL_TIMEOUT_INTERVAL (TICKS_PER_SEC)
+#define SLAM_BASE_MIN_TIMEOUT (2*TICKS_PER_SEC)
+#define SLAM_BASE_TIMEOUT_INTERVAL (4*TICKS_PER_SEC)
+#define SLAM_BACKOFF_LIMIT 5
+#define SLAM_MAX_RETRIES 20
+
+/*** Packets Formats ***
+ * Data Packet:
+ * transaction
+ * total bytes
+ * block size
+ * packet #
+ * data
+ *
+ * Status Request Packet
+ * transaction
+ * total bytes
+ * block size
+ *
+ * Status Packet
+ * received packets
+ * requested packets
+ * received packets
+ * requested packets
+ * ...
+ * received packets
+ * requested packtes
+ * 0
+ */
+
+#define MAX_HDR (7 + 7 + 7) /* transaction, total size, block size */
+#define MIN_HDR (1 + 1 + 1) /* transactino, total size, block size */
+
+#define MAX_SLAM_REQUEST MAX_HDR
+#define MIN_SLAM_REQUEST MIN_HDR
+
+#define MIN_SLAM_DATA (MIN_HDR + 1)
+
+static struct slam_nack {
+ struct iphdr ip;
+ struct udphdr udp;
+ unsigned char data[ETH_MAX_MTU -
+ (sizeof(struct iphdr) + sizeof(struct udphdr))];
+} nack;
+
+struct slam_state {
+ unsigned char hdr[MAX_HDR];
+ unsigned long hdr_len;
+ unsigned long block_size;
+ unsigned long total_bytes;
+ unsigned long total_packets;
+
+ unsigned long received_packets;
+
+ struct buffer *buffer;
+ unsigned char *image;
+ unsigned char *bitmap;
+} state;
+
+
+static void init_slam_state(void)
+{
+ state.hdr_len = sizeof(state.hdr);
+ memset(state.hdr, 0, state.hdr_len);
+ state.block_size = 0;
+ state.total_packets = 0;
+
+ state.received_packets = 0;
+
+ state.image = 0;
+ state.bitmap = 0;
+}
+
+struct slam_info {
+ struct sockaddr_in server;
+ struct sockaddr_in local;
+ struct sockaddr_in multicast;
+ int sent_nack;
+ struct buffer *buffer;
+};
+
+#define SLAM_TIMEOUT 0
+#define SLAM_REQUEST 1
+#define SLAM_DATA 2
+static int await_slam(int ival __unused, void *ptr,
+ unsigned short ptype __unused, struct iphdr *ip,
+ struct udphdr *udp, struct tcphdr *tcp __unused)
+{
+ struct slam_info *info = ptr;
+ if (!udp) {
+ return 0;
+ }
+ /* I can receive two kinds of packets here, a multicast data packet,
+ * or a unicast request for information
+ */
+ /* Check for a data request packet */
+ if ((ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) &&
+ (ntohs(udp->dest) == info->local.sin_port) &&
+ (nic.packetlen >=
+ ETH_HLEN +
+ sizeof(struct iphdr) +
+ sizeof(struct udphdr) +
+ MIN_SLAM_REQUEST)) {
+ return SLAM_REQUEST;
+ }
+ /* Check for a multicast data packet */
+ if ((ip->dest.s_addr == info->multicast.sin_addr.s_addr) &&
+ (ntohs(udp->dest) == info->multicast.sin_port) &&
+ (nic.packetlen >=
+ ETH_HLEN +
+ sizeof(struct iphdr) +
+ sizeof(struct udphdr) +
+ MIN_SLAM_DATA)) {
+ return SLAM_DATA;
+ }
+#if 0
+ printf("#");
+ printf("dest: %@ port: %d len: %d\n",
+ ip->dest.s_addr, ntohs(udp->dest), nic.packetlen);
+#endif
+ return 0;
+
+}
+
+static int slam_encode(
+ unsigned char **ptr, unsigned char *end, unsigned long value)
+{
+ unsigned char *data = *ptr;
+ int bytes;
+ bytes = sizeof(value);
+ while ((bytes > 0) && ((0xff & (value >> ((bytes -1)<<3))) == 0)) {
+ bytes--;
+ }
+ if (bytes <= 0) {
+ bytes = 1;
+ }
+ if (data + bytes >= end) {
+ return -1;
+ }
+ if ((0xe0 & (value >> ((bytes -1)<<3))) == 0) {
+ /* packed together */
+ *data = (bytes << 5) | (value >> ((bytes -1)<<3));
+ } else {
+ bytes++;
+ *data = (bytes << 5);
+ }
+ bytes--;
+ data++;
+ while(bytes) {
+ *(data++) = 0xff & (value >> ((bytes -1)<<3));
+ bytes--;
+ }
+ *ptr = data;
+ return 0;
+}
+
+static int slam_skip(unsigned char **ptr, unsigned char *end)
+{
+ int bytes;
+ if (*ptr >= end) {
+ return -1;
+ }
+ bytes = ((**ptr) >> 5) & 7;
+ if (bytes == 0) {
+ return -1;
+ }
+ if (*ptr + bytes >= end) {
+ return -1;
+ }
+ (*ptr) += bytes;
+ return 0;
+
+}
+
+static unsigned long slam_decode(unsigned char **ptr, unsigned char *end,
+ int *err)
+{
+ unsigned long value;
+ unsigned bytes;
+ if (*ptr >= end) {
+ *err = -1;
+ }
+ bytes = ((**ptr) >> 5) & 7;
+ if ((bytes == 0) || (bytes > sizeof(unsigned long))) {
+ *err = -1;
+ return 0;
+ }
+ if ((*ptr) + bytes >= end) {
+ *err = -1;
+ }
+ value = (**ptr) & 0x1f;
+ bytes--;
+ (*ptr)++;
+ while(bytes) {
+ value <<= 8;
+ value |= **ptr;
+ (*ptr)++;
+ bytes--;
+ }
+ return value;
+}
+
+
+static long slam_sleep_interval(int exp)
+{
+ long range;
+ long divisor;
+ long interval;
+ range = SLAM_BASE_TIMEOUT_INTERVAL;
+ if (exp < 0) {
+ divisor = RAND_MAX/SLAM_INITIAL_TIMEOUT_INTERVAL;
+ } else {
+ if (exp > SLAM_BACKOFF_LIMIT)
+ exp = SLAM_BACKOFF_LIMIT;
+ divisor = RAND_MAX/(range << exp);
+ }
+ interval = random()/divisor;
+ if (exp < 0) {
+ interval += SLAM_INITIAL_MIN_TIMEOUT;
+ } else {
+ interval += SLAM_BASE_MIN_TIMEOUT;
+ }
+ return interval;
+}
+
+
+static unsigned char *reinit_slam_state(
+ unsigned char *header, unsigned char *end)
+{
+ unsigned long total_bytes;
+ unsigned long block_size;
+
+ unsigned long bitmap_len;
+ unsigned long max_packet_len;
+ unsigned char *data;
+ int err;
+
+#if 0
+ printf("reinit\n");
+#endif
+ data = header;
+
+ state.hdr_len = 0;
+ err = slam_skip(&data, end); /* transaction id */
+ total_bytes = slam_decode(&data, end, &err);
+ block_size = slam_decode(&data, end, &err);
+ if (err) {
+ printf("ALERT: slam size out of range\n");
+ return 0;
+ }
+ state.block_size = block_size;
+ state.total_bytes = total_bytes;
+ state.total_packets = (total_bytes + block_size - 1)/block_size;
+ state.hdr_len = data - header;
+ state.received_packets = 0;
+
+ data = state.hdr;
+ slam_encode(&data, &state.hdr[sizeof(state.hdr)], state.total_packets);
+ max_packet_len = data - state.hdr;
+ memcpy(state.hdr, header, state.hdr_len);
+
+#if 0
+ printf("block_size: %ld\n", block_size);
+ printf("total_bytes: %ld\n", total_bytes);
+ printf("total_packets: %ld\n", state.total_packets);
+ printf("hdr_len: %ld\n", state.hdr_len);
+ printf("max_packet_len: %ld\n", max_packet_len);
+#endif
+
+ if (state.block_size > ETH_MAX_MTU - (
+ sizeof(struct iphdr) + sizeof(struct udphdr) +
+ state.hdr_len + max_packet_len)) {
+ printf("ALERT: slam blocksize to large\n");
+ return 0;
+ }
+ bitmap_len = (state.total_packets + 1 + 7)/8;
+ state.image = phys_to_virt ( state.buffer->addr );
+ /* We don't use the buffer routines properly yet; fake it */
+ state.buffer->fill = total_bytes;
+ state.bitmap = state.image + total_bytes;
+ if ((unsigned long)state.image < 1024*1024) {
+ printf("ALERT: slam filesize to large for available memory\n");
+ return 0;
+ }
+ memset(state.bitmap, 0, bitmap_len);
+
+ return header + state.hdr_len;
+}
+
+static int slam_recv_data(unsigned char *data)
+{
+ unsigned long packet;
+ unsigned long data_len;
+ int err;
+ struct udphdr *udp;
+ udp = (struct udphdr *)&nic.packet[ETH_HLEN + sizeof(struct iphdr)];
+ err = 0;
+ packet = slam_decode(&data, &nic.packet[nic.packetlen], &err);
+ if (err || (packet > state.total_packets)) {
+ printf("ALERT: Invalid packet number\n");
+ return 0;
+ }
+ /* Compute the expected data length */
+ if (packet != state.total_packets -1) {
+ data_len = state.block_size;
+ } else {
+ data_len = state.total_bytes % state.block_size;
+ }
+ /* If the packet size is wrong drop the packet and then continue */
+ if (ntohs(udp->len) != (data_len + (data - (unsigned char*)udp))) {
+ printf("ALERT: udp packet is not the correct size\n");
+ return 1;
+ }
+ if (nic.packetlen < data_len + (data - nic.packet)) {
+ printf("ALERT: Ethernet packet shorter than data_len\n");
+ return 1;
+ }
+ if (data_len > state.block_size) {
+ data_len = state.block_size;
+ }
+ if (((state.bitmap[packet >> 3] >> (packet & 7)) & 1) == 0) {
+ /* Non duplicate packet */
+ state.bitmap[packet >> 3] |= (1 << (packet & 7));
+ memcpy(state.image + (packet*state.block_size), data, data_len);
+ state.received_packets++;
+ } else {
+#ifdef MDEBUG
+ printf("<DUP>\n");
+#endif
+ }
+ return 1;
+}
+
+static void transmit_nack(unsigned char *ptr, struct slam_info *info)
+{
+ int nack_len;
+ /* Ensure the packet is null terminated */
+ *ptr++ = 0;
+ nack_len = ptr - (unsigned char *)&nack;
+ build_udp_hdr(info->server.sin_addr.s_addr, info->local.sin_port,
+ info->server.sin_port, 1, nack_len, &nack);
+ ip_transmit(nack_len, &nack);
+#if defined(MDEBUG) && 0
+ printf("Sent NACK to %@ bytes: %d have:%ld/%ld\n",
+ info->server_ip, nack_len,
+ state.received_packets, state.total_packets);
+#endif
+}
+
+static void slam_send_nack(struct slam_info *info)
+{
+ unsigned char *ptr, *end;
+ /* Either I timed out or I was explicitly
+ * asked for a request packet
+ */
+ ptr = &nack.data[0];
+ /* Reserve space for the trailling null */
+ end = &nack.data[sizeof(nack.data) -1];
+ if (!state.bitmap) {
+ slam_encode(&ptr, end, 0);
+ slam_encode(&ptr, end, 1);
+ }
+ else {
+ /* Walk the bitmap */
+ unsigned long i;
+ unsigned long len;
+ unsigned long max;
+ int value;
+ int last;
+ /* Compute the last bit and store an inverted trailer */
+ max = state.total_packets;
+ value = ((state.bitmap[(max -1) >> 3] >> ((max -1) & 7) ) & 1);
+ value = !value;
+ state.bitmap[max >> 3] &= ~(1 << (max & 7));
+ state.bitmap[max >> 3] |= value << (max & 7);
+
+ len = 0;
+ last = 1; /* Start with the received packets */
+ for(i = 0; i <= max; i++) {
+ value = (state.bitmap[i>>3] >> (i & 7)) & 1;
+ if (value == last) {
+ len++;
+ } else {
+ if (slam_encode(&ptr, end, len))
+ break;
+ last = value;
+ len = 1;
+ }
+ }
+ }
+ info->sent_nack = 1;
+ transmit_nack(ptr, info);
+}
+
+static void slam_send_disconnect(struct slam_info *info)
+{
+ if (info->sent_nack) {
+ /* A disconnect is a packet with just the null terminator */
+ transmit_nack(&nack.data[0], info);
+ }
+ info->sent_nack = 0;
+}
+
+
+static int proto_slam(struct slam_info *info)
+{
+ int retry;
+ long timeout;
+
+ init_slam_state();
+ state.buffer = info->buffer;
+
+ retry = -1;
+ rx_qdrain();
+ /* Arp for my server */
+ if (arptable[ARP_SERVER].ipaddr.s_addr != info->server.sin_addr.s_addr) {
+ arptable[ARP_SERVER].ipaddr.s_addr = info->server.sin_addr.s_addr;
+ memset(arptable[ARP_SERVER].node, 0, ETH_ALEN);
+ }
+ /* If I'm running over multicast join the multicast group */
+ join_group(IGMP_SERVER, info->multicast.sin_addr.s_addr);
+ for(;;) {
+ unsigned char *header;
+ unsigned char *data;
+ int type;
+ header = data = 0;
+
+ timeout = slam_sleep_interval(retry);
+ type = await_reply(await_slam, 0, info, timeout);
+ /* Compute the timeout for next time */
+ if (type == SLAM_TIMEOUT) {
+ /* If I timeouted recompute the next timeout */
+ if (retry++ > SLAM_MAX_RETRIES) {
+ return 0;
+ }
+ } else {
+ retry = 0;
+ }
+ if ((type == SLAM_DATA) || (type == SLAM_REQUEST)) {
+ /* Check the incomming packet and reinit the data
+ * structures if necessary.
+ */
+ header = &nic.packet[ETH_HLEN +
+ sizeof(struct iphdr) + sizeof(struct udphdr)];
+ data = header + state.hdr_len;
+ if (memcmp(state.hdr, header, state.hdr_len) != 0) {
+ /* Something is fishy reset the transaction */
+ data = reinit_slam_state(header, &nic.packet[nic.packetlen]);
+ if (!data) {
+ return 0;
+ }
+ }
+ }
+ if (type == SLAM_DATA) {
+ if (!slam_recv_data(data)) {
+ return 0;
+ }
+ if (state.received_packets == state.total_packets) {
+ /* We are done get out */
+ break;
+ }
+ }
+ if ((type == SLAM_TIMEOUT) || (type == SLAM_REQUEST)) {
+ /* Either I timed out or I was explicitly
+ * asked by a request packet
+ */
+ slam_send_nack(info);
+ }
+ }
+ slam_send_disconnect(info);
+
+ /* Leave the multicast group */
+ leave_group(IGMP_SERVER);
+ /* FIXME don't overwrite myself */
+ /* load file to correct location */
+ return 1;
+}
+
+static int url_slam ( char *url __unused, struct sockaddr_in *server,
+ char *file, struct buffer *buffer ) {
+ struct slam_info info;
+ /* Set the defaults */
+ info.server = *server;
+ info.multicast.sin_addr.s_addr = htonl(SLAM_MULTICAST_IP);
+ info.multicast.sin_port = SLAM_MULTICAST_PORT;
+ info.local.sin_addr.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
+ info.local.sin_port = SLAM_LOCAL_PORT;
+ info.buffer = buffer;
+ info.sent_nack = 0;
+ if (file[0]) {
+ printf("\nBad url\n");
+ return 0;
+ }
+ return proto_slam(&info);
+}
+
+struct protocol slam_protocol __protocol = {
+ .name = "x-slam",
+ .default_port = SLAM_PORT,
+ .load = url_slam,
+};
+
+#endif
diff --git a/gpxe/src/tests/linebuf_test.c b/gpxe/src/tests/linebuf_test.c
new file mode 100644
index 00000000..c3c3b375
--- /dev/null
+++ b/gpxe/src/tests/linebuf_test.c
@@ -0,0 +1,35 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <gpxe/linebuf.h>
+
+static const char data1[] =
+"Hello world\r\n"
+"This is a reasonably nice set of lines\n"
+"with not many different terminators\r\n\r\n"
+"There should be exactly one blank line above\n"
+"and this line should never appear at all since it has no terminator";
+
+void linebuf_test ( void ) {
+ struct line_buffer linebuf;
+ const char *data = data1;
+ size_t len = ( sizeof ( data1 ) - 1 /* be mean; strip the NUL */ );
+ ssize_t frag_len;
+ char *line;
+
+ memset ( &linebuf, 0, sizeof ( linebuf ) );
+ while ( len ) {
+ frag_len = line_buffer ( &linebuf, data, len );
+ if ( frag_len < 0 ) {
+ printf ( "line_buffer() failed: %s\n",
+ strerror ( frag_len ) );
+ return;
+ }
+ data += frag_len;
+ len -= frag_len;
+ if ( ( line = buffered_line ( &linebuf ) ) )
+ printf ( "\"%s\"\n", line );
+ }
+
+ empty_line_buffer ( &linebuf );
+}
diff --git a/gpxe/src/tests/memcpy_test.c b/gpxe/src/tests/memcpy_test.c
new file mode 100644
index 00000000..7626e639
--- /dev/null
+++ b/gpxe/src/tests/memcpy_test.c
@@ -0,0 +1,39 @@
+#include <string.h>
+
+/*
+ * This file exists for testing the compilation of memcpy() with the
+ * various constant-length optimisations.
+ *
+ */
+
+#define __regparm __attribute__ (( regparm(3) ))
+
+void __regparm memcpy_0 ( void *dest, void *src ) { memcpy ( dest, src, 0 ); }
+void __regparm memcpy_1 ( void *dest, void *src ) { memcpy ( dest, src, 1 ); }
+void __regparm memcpy_2 ( void *dest, void *src ) { memcpy ( dest, src, 2 ); }
+void __regparm memcpy_3 ( void *dest, void *src ) { memcpy ( dest, src, 3 ); }
+void __regparm memcpy_4 ( void *dest, void *src ) { memcpy ( dest, src, 4 ); }
+void __regparm memcpy_5 ( void *dest, void *src ) { memcpy ( dest, src, 5 ); }
+void __regparm memcpy_6 ( void *dest, void *src ) { memcpy ( dest, src, 6 ); }
+void __regparm memcpy_7 ( void *dest, void *src ) { memcpy ( dest, src, 7 ); }
+void __regparm memcpy_8 ( void *dest, void *src ) { memcpy ( dest, src, 8 ); }
+void __regparm memcpy_9 ( void *dest, void *src ) { memcpy ( dest, src, 9 ); }
+void __regparm memcpy_10 ( void *dest, void *src ) { memcpy ( dest, src, 10 ); }
+void __regparm memcpy_11 ( void *dest, void *src ) { memcpy ( dest, src, 11 ); }
+void __regparm memcpy_12 ( void *dest, void *src ) { memcpy ( dest, src, 12 ); }
+void __regparm memcpy_13 ( void *dest, void *src ) { memcpy ( dest, src, 13 ); }
+void __regparm memcpy_14 ( void *dest, void *src ) { memcpy ( dest, src, 14 ); }
+void __regparm memcpy_15 ( void *dest, void *src ) { memcpy ( dest, src, 15 ); }
+void __regparm memcpy_16 ( void *dest, void *src ) { memcpy ( dest, src, 16 ); }
+void __regparm memcpy_17 ( void *dest, void *src ) { memcpy ( dest, src, 17 ); }
+void __regparm memcpy_18 ( void *dest, void *src ) { memcpy ( dest, src, 18 ); }
+void __regparm memcpy_19 ( void *dest, void *src ) { memcpy ( dest, src, 19 ); }
+void __regparm memcpy_20 ( void *dest, void *src ) { memcpy ( dest, src, 20 ); }
+void __regparm memcpy_21 ( void *dest, void *src ) { memcpy ( dest, src, 21 ); }
+void __regparm memcpy_22 ( void *dest, void *src ) { memcpy ( dest, src, 22 ); }
+void __regparm memcpy_23 ( void *dest, void *src ) { memcpy ( dest, src, 23 ); }
+void __regparm memcpy_24 ( void *dest, void *src ) { memcpy ( dest, src, 24 ); }
+void __regparm memcpy_25 ( void *dest, void *src ) { memcpy ( dest, src, 25 ); }
+void __regparm memcpy_26 ( void *dest, void *src ) { memcpy ( dest, src, 26 ); }
+void __regparm memcpy_27 ( void *dest, void *src ) { memcpy ( dest, src, 27 ); }
+void __regparm memcpy_28 ( void *dest, void *src ) { memcpy ( dest, src, 28 ); }
diff --git a/gpxe/src/tests/umalloc_test.c b/gpxe/src/tests/umalloc_test.c
new file mode 100644
index 00000000..7a361868
--- /dev/null
+++ b/gpxe/src/tests/umalloc_test.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/umalloc.h>
+#include <gpxe/memmap.h>
+
+void umalloc_test ( void ) {
+ struct memory_map memmap;
+ userptr_t bob;
+ userptr_t fred;
+
+ printf ( "Before allocation:\n" );
+ get_memmap ( &memmap );
+
+ bob = umalloc ( 1234 );
+ bob = urealloc ( bob, 12345 );
+ fred = umalloc ( 999 );
+
+ printf ( "After allocation:\n" );
+ get_memmap ( &memmap );
+
+ ufree ( bob );
+ ufree ( fred );
+
+ printf ( "After freeing:\n" );
+ get_memmap ( &memmap );
+}
diff --git a/gpxe/src/tests/uri_test.c b/gpxe/src/tests/uri_test.c
new file mode 100644
index 00000000..25487603
--- /dev/null
+++ b/gpxe/src/tests/uri_test.c
@@ -0,0 +1,145 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/uri.h>
+
+#define URI_MAX_LEN 1024
+
+struct uri_test {
+ const char *base_uri_string;
+ const char *relative_uri_string;
+ const char *resolved_uri_string;
+};
+
+static struct uri_test uri_tests[] = {
+ { "http://www.fensystems.co.uk", "",
+ "http://www.fensystems.co.uk/" },
+ { "http://etherboot.org/wiki/page1", "page2",
+ "http://etherboot.org/wiki/page2" },
+ { "http://etherboot.org/wiki/page1", "../page3",
+ "http://etherboot.org/page3" },
+ { "tftp://192.168.0.1/", "/tftpboot/vmlinuz",
+ "tftp://192.168.0.1/tftpboot/vmlinuz" },
+#if 0
+ "http://www.etherboot.org/wiki",
+ "mailto:bob@nowhere.com",
+ "ftp://joe:secret@insecure.org:8081/hidden/path/to?what=is#this",
+#endif
+};
+
+static int test_parse_unparse ( const char *uri_string ) {
+ char buf[URI_MAX_LEN];
+ size_t len;
+ struct uri *uri = NULL;
+ int rc;
+
+ /* Parse and unparse URI */
+ uri = parse_uri ( uri_string );
+ if ( ! uri ) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ len = unparse_uri ( buf, sizeof ( buf ), uri );
+
+ /* Compare result */
+ if ( strcmp ( buf, uri_string ) != 0 ) {
+ printf ( "Unparse of \"%s\" produced \"%s\"\n",
+ uri_string, buf );
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rc = 0;
+
+ done:
+ uri_put ( uri );
+ if ( rc ) {
+ printf ( "URI parse-unparse of \"%s\" failed: %s\n",
+ uri_string, strerror ( rc ) );
+ }
+ return rc;
+}
+
+static int test_resolve ( const char *base_uri_string,
+ const char *relative_uri_string,
+ const char *resolved_uri_string ) {
+ struct uri *base_uri = NULL;
+ struct uri *relative_uri = NULL;
+ struct uri *resolved_uri = NULL;
+ char buf[URI_MAX_LEN];
+ size_t len;
+ int rc;
+
+ /* Parse URIs */
+ base_uri = parse_uri ( base_uri_string );
+ if ( ! base_uri ) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ relative_uri = parse_uri ( relative_uri_string );
+ if ( ! relative_uri ) {
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ /* Resolve URI */
+ resolved_uri = resolve_uri ( base_uri, relative_uri );
+ if ( ! resolved_uri ) {
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ /* Compare result */
+ len = unparse_uri ( buf, sizeof ( buf ), resolved_uri );
+ if ( strcmp ( buf, resolved_uri_string ) != 0 ) {
+ printf ( "Resolution of \"%s\"+\"%s\" produced \"%s\"\n",
+ base_uri_string, relative_uri_string, buf );
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rc = 0;
+
+ done:
+ uri_put ( base_uri );
+ uri_put ( relative_uri );
+ uri_put ( resolved_uri );
+ if ( rc ) {
+ printf ( "URI resolution of \"%s\"+\"%s\" failed: %s\n",
+ base_uri_string, relative_uri_string,
+ strerror ( rc ) );
+ }
+ return rc;
+}
+
+int uri_test ( void ) {
+ unsigned int i;
+ struct uri_test *uri_test;
+ int rc;
+ int overall_rc = 0;
+
+ for ( i = 0 ; i < ( sizeof ( uri_tests ) /
+ sizeof ( uri_tests[0] ) ) ; i++ ) {
+ uri_test = &uri_tests[i];
+ rc = test_parse_unparse ( uri_test->base_uri_string );
+ if ( rc != 0 )
+ overall_rc = rc;
+ rc = test_parse_unparse ( uri_test->relative_uri_string );
+ if ( rc != 0 )
+ overall_rc = rc;
+ rc = test_parse_unparse ( uri_test->resolved_uri_string );
+ if ( rc != 0 )
+ overall_rc = rc;
+ rc = test_resolve ( uri_test->base_uri_string,
+ uri_test->relative_uri_string,
+ uri_test->resolved_uri_string );
+ if ( rc != 0 )
+ overall_rc = rc;
+ }
+
+ if ( overall_rc )
+ printf ( "URI tests failed: %s\n", strerror ( overall_rc ) );
+ return overall_rc;
+}
diff --git a/gpxe/src/usr/aoeboot.c b/gpxe/src/usr/aoeboot.c
new file mode 100644
index 00000000..f0e481bd
--- /dev/null
+++ b/gpxe/src/usr/aoeboot.c
@@ -0,0 +1,73 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <byteswap.h>
+#include <gpxe/aoe.h>
+#include <gpxe/ata.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/settings.h>
+#include <gpxe/abft.h>
+#include <int13.h>
+#include <usr/aoeboot.h>
+
+/**
+ * Guess boot network device
+ *
+ * @ret netdev Boot network device
+ */
+static struct net_device * guess_boot_netdev ( void ) {
+ struct net_device *boot_netdev;
+
+ /* Just use the first network device */
+ for_each_netdev ( boot_netdev ) {
+ return boot_netdev;
+ }
+
+ return NULL;
+}
+
+int aoeboot ( const char *root_path ) {
+ struct ata_device ata;
+ struct int13_drive drive;
+ int rc;
+
+ memset ( &ata, 0, sizeof ( ata ) );
+ memset ( &drive, 0, sizeof ( drive ) );
+
+ printf ( "AoE booting from %s\n", root_path );
+
+ /* FIXME: ugly, ugly hack */
+ struct net_device *netdev = guess_boot_netdev();
+
+ if ( ( rc = aoe_attach ( &ata, netdev, root_path ) ) != 0 ) {
+ printf ( "Could not attach AoE device: %s\n",
+ strerror ( rc ) );
+ goto error_attach;
+ }
+ if ( ( rc = init_atadev ( &ata ) ) != 0 ) {
+ printf ( "Could not initialise AoE device: %s\n",
+ strerror ( rc ) );
+ goto error_init;
+ }
+
+ /* FIXME: ugly, ugly hack */
+ struct aoe_session *aoe =
+ container_of ( ata.backend, struct aoe_session, refcnt );
+ abft_fill_data ( aoe );
+
+ drive.blockdev = &ata.blockdev;
+
+ register_int13_drive ( &drive );
+ printf ( "Registered as BIOS drive %#02x\n", drive.drive );
+ printf ( "Booting from BIOS drive %#02x\n", drive.drive );
+ rc = int13_boot ( drive.drive );
+ printf ( "Boot failed\n" );
+
+ printf ( "Unregistering BIOS drive %#02x\n", drive.drive );
+ unregister_int13_drive ( &drive );
+
+ error_init:
+ aoe_detach ( &ata );
+ error_attach:
+ return rc;
+}
diff --git a/gpxe/src/usr/autoboot.c b/gpxe/src/usr/autoboot.c
new file mode 100644
index 00000000..c1a61ec0
--- /dev/null
+++ b/gpxe/src/usr/autoboot.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/settings.h>
+#include <gpxe/image.h>
+#include <gpxe/embedded.h>
+#include <usr/ifmgmt.h>
+#include <usr/route.h>
+#include <usr/dhcpmgmt.h>
+#include <usr/imgmgmt.h>
+#include <usr/iscsiboot.h>
+#include <usr/aoeboot.h>
+#include <usr/autoboot.h>
+
+/** @file
+ *
+ * Automatic booting
+ *
+ */
+
+/**
+ * Identify the boot network device
+ *
+ * @ret netdev Boot network device
+ */
+static struct net_device * find_boot_netdev ( void ) {
+ return NULL;
+}
+
+/**
+ * Boot embedded image
+ *
+ * @ret rc Return status code
+ */
+static int boot_embedded_image ( void ) {
+ struct image *image;
+ int rc;
+
+ image = embedded_image();
+ if ( !image )
+ return ENOENT;
+
+ if ( ( rc = imgload ( image ) ) != 0 ) {
+ printf ( "Could not load embedded image: %s\n",
+ strerror ( rc ) );
+ } else if ( ( rc = imgexec ( image ) ) != 0 ) {
+ printf ( "Could not boot embedded image: %s\n",
+ strerror ( rc ) );
+ }
+ image_put ( image );
+ return rc;
+}
+
+/**
+ * Boot using filename
+ *
+ * @v filename Boot filename
+ * @ret rc Return status code
+ */
+static int boot_filename ( const char *filename ) {
+ struct image *image;
+ int rc;
+
+ image = alloc_image();
+ if ( ! image ) {
+ printf ( "Out of memory\n" );
+ return -ENOMEM;
+ }
+ if ( ( rc = imgfetch ( image, filename,
+ register_and_autoload_image ) ) != 0 ) {
+ printf ( "Could not load %s: %s\n",
+ filename, strerror ( rc ) );
+ goto done;
+ }
+ if ( ( rc = imgexec ( image ) ) != 0 ) {
+ printf ( "Could not boot %s: %s\n",
+ filename, strerror ( rc ) );
+ goto done;
+ }
+
+ done:
+ image_put ( image );
+ return rc;
+}
+
+/**
+ * Boot using root path
+ *
+ * @v root_path Root path
+ * @ret rc Return status code
+ */
+int boot_root_path ( const char *root_path ) {
+
+ /* Quick hack */
+ if ( strncmp ( root_path, "iscsi:", 6 ) == 0 ) {
+ return iscsiboot ( root_path );
+ } else if ( strncmp ( root_path, "aoe:", 4 ) == 0 ) {
+ return aoeboot ( root_path );
+ }
+
+ return -ENOTSUP;
+}
+
+/**
+ * Boot from a network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int netboot ( struct net_device *netdev ) {
+ char buf[256];
+ int rc;
+
+ /* Open device and display device status */
+ if ( ( rc = ifopen ( netdev ) ) != 0 )
+ return rc;
+ ifstat ( netdev );
+
+ /* Configure device via DHCP */
+ if ( ( rc = dhcp ( netdev ) ) != 0 )
+ return rc;
+ route();
+
+ /* Try to boot an embedded image if we have one */
+ rc = boot_embedded_image ();
+ if ( rc != ENOENT )
+ return rc;
+
+ /* Try to download and boot whatever we are given as a filename */
+ fetch_string_setting ( NULL, &filename_setting, buf, sizeof ( buf ) );
+ if ( buf[0] ) {
+ printf ( "Booting from filename \"%s\"\n", buf );
+ return boot_filename ( buf );
+ }
+
+ /* No filename; try the root path */
+ fetch_string_setting ( NULL, &root_path_setting, buf, sizeof ( buf ) );
+ if ( buf[0] ) {
+ printf ( "Booting from root path \"%s\"\n", buf );
+ return boot_root_path ( buf );
+ }
+
+ printf ( "No filename or root path specified\n" );
+ return -ENOENT;
+}
+
+/**
+ * Close all open net devices
+ *
+ * Called before a fresh boot attempt in order to free up memory. We
+ * don't just close the device immediately after the boot fails,
+ * because there may still be TCP connections in the process of
+ * closing.
+ */
+static void close_all_netdevs ( void ) {
+ struct net_device *netdev;
+
+ for_each_netdev ( netdev ) {
+ ifclose ( netdev );
+ }
+}
+
+/**
+ * Boot the system
+ */
+void autoboot ( void ) {
+ struct net_device *boot_netdev;
+ struct net_device *netdev;
+
+ /* If we have an identifable boot device, try that first */
+ close_all_netdevs();
+ if ( ( boot_netdev = find_boot_netdev() ) )
+ netboot ( boot_netdev );
+
+ /* If that fails, try booting from any of the other devices */
+ for_each_netdev ( netdev ) {
+ if ( netdev == boot_netdev )
+ continue;
+ close_all_netdevs();
+ netboot ( netdev );
+ }
+
+ printf ( "No more network devices\n" );
+}
diff --git a/gpxe/src/usr/dhcpmgmt.c b/gpxe/src/usr/dhcpmgmt.c
new file mode 100644
index 00000000..2e429cd6
--- /dev/null
+++ b/gpxe/src/usr/dhcpmgmt.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/dhcp.h>
+#include <gpxe/monojob.h>
+#include <gpxe/process.h>
+#include <usr/ifmgmt.h>
+#include <usr/dhcpmgmt.h>
+
+/** @file
+ *
+ * DHCP management
+ *
+ */
+
+int dhcp ( struct net_device *netdev ) {
+ int rc;
+
+ /* Check we can open the interface first */
+ if ( ( rc = ifopen ( netdev ) ) != 0 )
+ return rc;
+
+ /* Perform DHCP */
+ printf ( "DHCP (%s %s)", netdev->name, netdev_hwaddr ( netdev ) );
+ if ( ( rc = start_dhcp ( &monojob, netdev ) ) == 0 )
+ rc = monojob_wait ( "" );
+
+ return rc;
+}
diff --git a/gpxe/src/usr/ifmgmt.c b/gpxe/src/usr/ifmgmt.c
new file mode 100644
index 00000000..5f4323de
--- /dev/null
+++ b/gpxe/src/usr/ifmgmt.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/device.h>
+#include <usr/ifmgmt.h>
+
+/** @file
+ *
+ * Network interface management
+ *
+ */
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+int ifopen ( struct net_device *netdev ) {
+ int rc;
+
+ if ( ( rc = netdev_open ( netdev ) ) != 0 ) {
+ printf ( "Could not open %s: %s\n",
+ netdev->name, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+void ifclose ( struct net_device *netdev ) {
+ netdev_close ( netdev );
+}
+
+/**
+ * Print status of network device
+ *
+ * @v netdev Network device
+ */
+void ifstat ( struct net_device *netdev ) {
+ printf ( "%s: %s on %s (%s) TX:%d TXE:%d RX:%d RXE:%d\n",
+ netdev->name, netdev_hwaddr ( netdev ), netdev->dev->name,
+ ( ( netdev->state & NETDEV_OPEN ) ? "open" : "closed" ),
+ netdev->stats.tx_ok, netdev->stats.tx_err,
+ netdev->stats.rx_ok, netdev->stats.rx_err );
+}
diff --git a/gpxe/src/usr/imgmgmt.c b/gpxe/src/usr/imgmgmt.c
new file mode 100644
index 00000000..bead4867
--- /dev/null
+++ b/gpxe/src/usr/imgmgmt.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <gpxe/image.h>
+#include <gpxe/downloader.h>
+#include <gpxe/monojob.h>
+#include <gpxe/open.h>
+#include <gpxe/uri.h>
+#include <usr/imgmgmt.h>
+
+/** @file
+ *
+ * Image management
+ *
+ */
+
+/**
+ * Fetch an image
+ *
+ * @v uri_string URI as a string (e.g. "http://www.nowhere.com/vmlinuz")
+ * @v name Name for image, or NULL
+ * @v register_image Image registration routine
+ * @ret rc Return status code
+ */
+int imgfetch ( struct image *image, const char *uri_string,
+ int ( * image_register ) ( struct image *image ) ) {
+ struct uri *uri;
+ int rc;
+
+ if ( ! ( uri = parse_uri ( uri_string ) ) )
+ return -ENOMEM;
+
+ image_set_uri ( image, uri );
+
+ if ( ( rc = create_downloader ( &monojob, image, image_register,
+ LOCATION_URI, uri ) ) == 0 )
+ rc = monojob_wait ( uri_string );
+
+ uri_put ( uri );
+ return rc;
+}
+
+/**
+ * Load an image
+ *
+ * @v image Image
+ * @ret rc Return status code
+ */
+int imgload ( struct image *image ) {
+ int rc;
+
+ /* Try to load image */
+ if ( ( rc = image_autoload ( image ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Execute an image
+ *
+ * @v image Image
+ * @ret rc Return status code
+ */
+int imgexec ( struct image *image ) {
+ return image_exec ( image );
+}
+
+/**
+ * Identify the first loaded image
+ *
+ * @ret image Image, or NULL
+ */
+struct image * imgautoselect ( void ) {
+ struct image *image;
+
+ for_each_image ( image ) {
+ if ( image->flags & IMAGE_LOADED )
+ return image;
+ }
+
+ return NULL;
+}
+
+/**
+ * Display status of an image
+ *
+ * @v image Executable/loadable image
+ */
+void imgstat ( struct image *image ) {
+ printf ( "%s: %zd bytes", image->name, image->len );
+ if ( image->type )
+ printf ( " [%s]", image->type->name );
+ if ( image->flags & IMAGE_LOADED )
+ printf ( " [LOADED]" );
+ if ( image->cmdline )
+ printf ( " \"%s\"", image->cmdline );
+ printf ( "\n" );
+}
+
+/**
+ * Free an image
+ *
+ * @v image Executable/loadable image
+ */
+void imgfree ( struct image *image ) {
+ unregister_image ( image );
+}
diff --git a/gpxe/src/usr/iscsiboot.c b/gpxe/src/usr/iscsiboot.c
new file mode 100644
index 00000000..99edc879
--- /dev/null
+++ b/gpxe/src/usr/iscsiboot.c
@@ -0,0 +1,69 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <gpxe/iscsi.h>
+#include <gpxe/settings.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/ibft.h>
+#include <int13.h>
+#include <usr/iscsiboot.h>
+
+/**
+ * Guess boot network device
+ *
+ * @ret netdev Boot network device
+ */
+static struct net_device * guess_boot_netdev ( void ) {
+ struct net_device *boot_netdev;
+
+ /* Just use the first network device */
+ for_each_netdev ( boot_netdev ) {
+ return boot_netdev;
+ }
+
+ return NULL;
+}
+
+int iscsiboot ( const char *root_path ) {
+ struct scsi_device scsi;
+ struct int13_drive drive;
+ int rc;
+
+ memset ( &scsi, 0, sizeof ( scsi ) );
+ memset ( &drive, 0, sizeof ( drive ) );
+
+ printf ( "iSCSI booting from %s\n", root_path );
+
+ if ( ( rc = iscsi_attach ( &scsi, root_path ) ) != 0 ) {
+ printf ( "Could not attach iSCSI device: %s\n",
+ strerror ( rc ) );
+ goto error_attach;
+ }
+ if ( ( rc = init_scsidev ( &scsi ) ) != 0 ) {
+ printf ( "Could not initialise iSCSI device: %s\n",
+ strerror ( rc ) );
+ goto error_init;
+ }
+
+ drive.blockdev = &scsi.blockdev;
+
+ /* FIXME: ugly, ugly hack */
+ struct net_device *netdev = guess_boot_netdev();
+ struct iscsi_session *iscsi =
+ container_of ( scsi.backend, struct iscsi_session, refcnt );
+ ibft_fill_data ( netdev, iscsi );
+
+ register_int13_drive ( &drive );
+ printf ( "Registered as BIOS drive %#02x\n", drive.drive );
+ printf ( "Booting from BIOS drive %#02x\n", drive.drive );
+ rc = int13_boot ( drive.drive );
+ printf ( "Boot failed\n" );
+
+ printf ( "Unregistering BIOS drive %#02x\n", drive.drive );
+ unregister_int13_drive ( &drive );
+
+ error_init:
+ iscsi_detach ( &scsi );
+ error_attach:
+ return rc;
+}
diff --git a/gpxe/src/usr/route.c b/gpxe/src/usr/route.c
new file mode 100644
index 00000000..f3a49f46
--- /dev/null
+++ b/gpxe/src/usr/route.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/ip.h>
+#include <usr/route.h>
+
+/** @file
+ *
+ * Routing table management
+ *
+ */
+
+void route ( void ) {
+ struct ipv4_miniroute *miniroute;
+
+ list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
+ printf ( "%s: %s/", miniroute->netdev->name,
+ inet_ntoa ( miniroute->address ) );
+ printf ( "%s", inet_ntoa ( miniroute->netmask ) );
+ if ( miniroute->gateway.s_addr != INADDR_NONE )
+ printf ( " gw %s", inet_ntoa ( miniroute->gateway ) );
+ if ( ! ( miniroute->netdev->state & NETDEV_OPEN ) )
+ printf ( " (inaccessible)" );
+ printf ( "\n" );
+ }
+}
diff --git a/gpxe/src/util/.gitignore b/gpxe/src/util/.gitignore
new file mode 100644
index 00000000..98adc2df
--- /dev/null
+++ b/gpxe/src/util/.gitignore
@@ -0,0 +1,4 @@
+nrv2b
+zbin
+hijack
+prototester
diff --git a/gpxe/src/util/Makefile b/gpxe/src/util/Makefile
new file mode 100644
index 00000000..d72661e2
--- /dev/null
+++ b/gpxe/src/util/Makefile
@@ -0,0 +1,22 @@
+BLIB = ../bin/blib.a
+CFLAGS = -Os
+
+all : hijack prototester mucurses_test
+
+hijack : hijack.c
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wall -lpcap -o $@ $<
+
+prototester.o : prototester.c
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wall -o $@ -c $< -idirafter ../include
+
+prototester : prototester.o $(BLIB)
+ $(CC) -o $@ $< -lc $(BLIB)
+
+mucurses_test.o : mucurses_test.c
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wall -o $@ -c $<
+
+mucurses_test : mucurses_test.o $(BLIB)
+ $(CC) -o $@ $< -lc $(BLIB)
+
+clean :
+ rm -f hijack prototester mucurses_test *.o
diff --git a/gpxe/src/util/catrom.pl b/gpxe/src/util/catrom.pl
new file mode 100755
index 00000000..fe37e6b6
--- /dev/null
+++ b/gpxe/src/util/catrom.pl
@@ -0,0 +1,48 @@
+#!/usr/bin/perl -w
+
+use warnings;
+use strict;
+
+use bytes;
+
+use constant MAX_ROM_LEN => 1024*1024;
+use constant PCI_OFF => 0x18;
+use constant INDICATOR_OFF => 0x15;
+
+my $total_len = 0;
+my @romfiles = @ARGV
+ or die "Usage: $0 rom-file-1 rom-file-2 ... > multi-rom-file\n";
+
+while ( my $romfile = shift @romfiles ) {
+ my $last = @romfiles ? 0 : 1;
+
+ open ROM, "<$romfile" or die "Could not open $romfile: $!\n";
+ my $len = read ( ROM, my $romdata, MAX_ROM_LEN )
+ or die "Could not read $romfile: $!\n";
+ close ROM;
+
+ die "$romfile is not a ROM file\n"
+ unless substr ( $romdata, 0, 2 ) eq "\x55\xAA";
+
+ ( my $checklen ) = unpack ( 'C', substr ( $romdata, 2, 1 ) );
+ $checklen *= 512;
+ die "$romfile has incorrect length field $checklen (should be $len)\n"
+ unless $len == $checklen;
+
+ ( my $pci ) = unpack ( 'v', substr ( $romdata, PCI_OFF, 2 ) );
+ die "Invalid PCI offset field in $romfile\n"
+ if $pci >= $len;
+ die "No PCIR signature in $romfile\n"
+ unless substr ( $romdata, $pci, 4 ) eq "PCIR";
+
+ ( my $indicator ) =
+ unpack ( 'C', substr ( $romdata, $pci + INDICATOR_OFF, 1 ) );
+ my $msg = sprintf ( "$romfile: indicator was %02x, ", $indicator );
+ $indicator &= ! ( 1 << 7 );
+ $indicator |= ( $last << 7 );
+ $msg .= sprintf ( "now %02x\n", $indicator );
+ substr ( $romdata, $pci + INDICATOR_OFF, 1 ) = pack ( 'C', $indicator );
+ warn $msg;
+
+ print $romdata;
+}
diff --git a/gpxe/src/util/disrom.pl b/gpxe/src/util/disrom.pl
new file mode 100755
index 00000000..64128698
--- /dev/null
+++ b/gpxe/src/util/disrom.pl
@@ -0,0 +1,114 @@
+#!/usr/bin/perl -w
+#
+# Program to display key information about a boot ROM
+# including PCI and PnP structures
+#
+# GPL, Ken Yap 2001
+#
+
+use bytes;
+
+sub getid ($)
+{
+ my ($offset) = @_;
+
+ return '' if ($offset == 0 or $offset > $len);
+ my ($string) = unpack('Z32', substr($data, $offset, 32));
+ return ($string);
+}
+
+sub dispci
+{
+ my ($pcidata) = substr($data, $pci, 0x18);
+ my ($dummy, $vendorid, $deviceid, $vpd, $pcilen, $pcirev,
+ $class1, $class2, $class3, $imglen, $coderev, $codetype,
+ $indicator) = unpack('a4v4C4v2C2', $pcidata);
+ $imglen *= 512;
+ my $vendorstr = sprintf('%#04x', $vendorid);
+ my $devicestr = sprintf('%#04x', $deviceid);
+ my $coderevstr = sprintf('%#04x', $coderev);
+ print <<EOF;
+PCI structure:
+
+Vital product data: $vpd
+Vendor ID: $vendorstr
+Device ID: $devicestr
+Device base type: $class1
+Device sub type: $class2
+Device interface type: $class3
+Image length: $imglen
+Code revision: $coderevstr
+Code type: $codetype
+Indicator: $indicator
+
+EOF
+}
+
+sub dispnp
+{
+ my ($pnpdata) = substr($data, $pnp, 0x20);
+ my ($dummy1, $pnprev, $pnplen, $nextpnp, $dummy2,
+ $pnpcsum, $deviceid, $mfrid, $productid,
+ $class1, $class2, $class3, $indicator,
+ $bcv, $dv, $bev, $dummy, $sri) = unpack('a4C2vC2a4v2C4v5', $pnpdata);
+ print <<EOF;
+PnP structure:
+
+EOF
+ print 'Vendor: ', &getid($mfrid), "\n";
+ print 'Device: ', &getid($productid), "\n";
+ my $indicatorstr = sprintf('%#02x', $indicator);
+ my $bcvstr = sprintf('%#04x', $bcv);
+ my $dvstr = sprintf('%#04x', $dv);
+ my $bevstr = sprintf('%#04x', $bev);
+ my $sristr = sprintf('%#04x', $sri);
+ my $checksum = unpack('%8C*', $pnpdata);
+ print <<EOF;
+Device base type: $class1
+Device sub type: $class2
+Device interface type: $class3
+Device indicator: $indicatorstr
+Boot connection vector: $bcvstr
+Disconnect vector: $dvstr
+Bootstrap entry vector: $bevstr
+Static resource information vector: $sristr
+Checksum: $checksum
+
+EOF
+}
+
+sub pcipnp
+{
+ ($pci, $pnp) = unpack('v2', substr($data, 0x18, 4));
+ if ($pci >= $len or $pnp >= $len) {
+ print "$file: Not a PCI PnP ROM image\n";
+ return;
+ }
+ if (substr($data, $pci, 4) ne 'PCIR' or substr($data, $pnp, 4) ne '$PnP') {
+ print "$file: No PCI and PNP structures, not a PCI PNP ROM image\n";
+ return;
+ }
+ &dispci();
+ &dispnp();
+}
+
+$file = $#ARGV >= 0 ? $ARGV[0] : '-';
+open(F, "$file") or die "$file: $!\n";
+binmode(F);
+# Handle up to 64kB ROM images
+$len = read(F, $data, 64*1024);
+close(F);
+defined($len) or die "$file: $!\n";
+substr($data, 0, 2) eq "\x55\xAA" or die "$file: Not a boot ROM image\n";
+my ($codelen) = unpack('C', substr($data, 2, 1));
+$codelen *= 512;
+if ($codelen < $len) {
+ my $pad = $len - $codelen;
+ print "Image is $codelen bytes and has $pad bytes of padding following\n";
+ $data = substr($data, 0, $codelen);
+} elsif ($codelen > $len) {
+ print "Image should be $codelen bytes but is truncated to $len bytes\n";}
+&pcipnp();
+($csum) = unpack('%8C*', $data);
+print "ROM checksum: $csum \n";
+exit(0);
diff --git a/gpxe/src/util/dskpad.pl b/gpxe/src/util/dskpad.pl
new file mode 100755
index 00000000..3ae325eb
--- /dev/null
+++ b/gpxe/src/util/dskpad.pl
@@ -0,0 +1,12 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+
+use constant FLOPPYSIZE => 1440 * 1024;
+
+while ( my $filename = shift ) {
+ die "$filename is not a file\n" unless -f $filename;
+ die "$filename is too large\n" unless ( -s $filename <= FLOPPYSIZE );
+ truncate $filename, FLOPPYSIZE or die "Could not truncate: $!\n";
+}
diff --git a/gpxe/src/util/geniso b/gpxe/src/util/geniso
new file mode 100755
index 00000000..7642ed36
--- /dev/null
+++ b/gpxe/src/util/geniso
@@ -0,0 +1,56 @@
+#!/bin/bash
+#
+# Generate a isolinux ISO boot image
+#
+# geniso foo.iso foo.lkrn
+#
+# the ISO image is the first argument so that a list of .lkrn images
+# to include can be specified
+#
+case $# in
+0|1)
+ echo Usage: $0 foo.iso foo.lkrn ...
+ exit 1
+ ;;
+esac
+# This should be the default location of the isolinux.bin file
+isolinux_bin=${ISOLINUX_BIN:-util/isolinux.bin}
+if [ ! -r $isolinux_bin ]
+then
+ echo $0: $isolinux_bin not found, please install, or set ISOLINUX_BIN in arch/i386/config correctly
+ exit 1
+fi
+out=$1
+shift
+dir=bin/iso.dir
+mkdir -p $dir
+cfg=$dir/isolinux.cfg
+cp -p $isolinux_bin $dir
+cat > $cfg <<EOF
+# These default options can be changed in the geniso script
+SAY Etherboot ISO boot image generated by geniso
+TIMEOUT 30
+EOF
+first=
+for f
+do
+ if [ ! -r $f ]
+ then
+ echo $f does not exist, skipping 1>&2
+ continue
+ fi
+ b=$(basename $f)
+ g=${b%.lkrn}
+ g=${g//[^a-z0-9]}.krn
+ case "$first" in
+ "")
+ echo DEFAULT $g
+ ;;
+ esac
+ first=$g
+ echo LABEL $b
+ echo "" KERNEL $g
+ cp -p $f $dir/$g
+done >> $cfg
+mkisofs -l -o $out -c boot.cat -b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table $dir
+rm -fr $dir
diff --git a/gpxe/src/util/genliso b/gpxe/src/util/genliso
new file mode 100755
index 00000000..0a67eb3c
--- /dev/null
+++ b/gpxe/src/util/genliso
@@ -0,0 +1,85 @@
+#!/bin/bash
+#
+# Generate a legacy floppy emulation ISO boot image
+#
+# genliso foo.liso foo.lkrn
+#
+# the ISO image is the first argument so that a list of .lkrn images
+# to include can be specified
+#
+case $0 in
+*genliso)
+ ;;
+*genfdimg)
+ genfdimg=1
+ ;;
+esac
+case $# in
+0|1)
+ echo Usage: $0 foo.liso foo.lkrn ...
+ exit 1
+ ;;
+esac
+case "`mtools -V`" in
+Mtools\ version\ 3.9.9*|Mtools\ version\ 3.9.1[0-9]*|Mtools\ version\ 4.*)
+ ;;
+*)
+ echo Mtools version 3.9.9 or later is required
+ exit 1
+ ;;
+esac
+out=$1
+shift
+dir=bin/liso.dir
+mkdir -p $dir
+case "$genfdimg" in
+1)
+ img=$out
+ ;;
+*)
+ img=$dir/boot.img
+ ;;
+esac
+mformat -f 1440 -C -i $img ::
+cfg=bin/syslinux.cfg
+cat > $cfg <<EOF
+# These default options can be changed in the genliso script
+SAY Etherboot ISO boot image generated by genliso
+TIMEOUT 30
+EOF
+first=
+for f
+do
+ if [ ! -r $f ]
+ then
+ echo $f does not exist, skipping 1>&2
+ continue
+ fi
+ # shorten name for 8.3 filesystem
+ b=$(basename $f)
+ g=${b%.lkrn}
+ g=${g//[^a-z0-9]}
+ g=${g:0:8}.krn
+ case "$first" in
+ "")
+ echo DEFAULT $g
+ ;;
+ esac
+ first=$g
+ echo LABEL $b
+ echo "" KERNEL $g
+ mcopy -m -i $img $f ::$g
+done >> $cfg
+mcopy -i $img $cfg ::syslinux.cfg
+if ! syslinux $img
+then
+ exit 1
+fi
+case "$genfdimg" in
+1)
+ ;;
+*)
+ mkisofs -o $out -c boot.cat -b boot.img $dir
+ ;;
+esac
+rm -fr $dir
diff --git a/gpxe/src/util/get-pci-ids b/gpxe/src/util/get-pci-ids
new file mode 100755
index 00000000..6501a7f7
--- /dev/null
+++ b/gpxe/src/util/get-pci-ids
@@ -0,0 +1,135 @@
+#! /usr/bin/perl -w
+
+# get-pci-ids: extract pci vendor/device ids from linux net drivers
+
+# Copyright (C) 2003 Georg Baum <gbaum@users.sf.net>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+# Known bugs/limitations:
+# - Does not recognize all drivers because some require special cflags.
+# Fails also on some drivers that do belong to other architectures
+# than the one of the machine this script is running on.
+# This is currently not so important because all drivers that have an
+# Etherboot counterpart are recognized.
+
+
+use strict;
+use File::Basename "dirname";
+use POSIX "uname";
+
+# Where to find the kernel sources
+my $kernel_src = "/usr/src/linux";
+
+if($#ARGV >= 0) {
+ $kernel_src = shift;
+}
+
+# Sanity checks
+if($#ARGV >= 0) {
+ print STDERR "Too many arguments.\n";
+ print STDERR "Usage: get-pci-ids [path to kernel sources]\n";
+ print STDERR " /usr/src/linux is assumed if no path is given.\n";
+ exit 1;
+}
+
+unless(-f "$kernel_src/include/linux/version.h") {
+ print STDERR "Could not find $kernel_src/include/linux/version.h.\n";
+ print STDERR "$kernel_src is probably no Linux kernel source tree.\n";
+ exit 1;
+}
+
+# Flags that are needed to preprocess the drivers.
+# Some drivers need optimization
+my $cflags="-D__KERNEL__ -I$kernel_src/include -I$kernel_src/net/inet -O2";
+
+# The C preprocessor. It needs to spit out the preprocessed source on stdout.
+my $cpp="gcc -E";
+
+# List of drivers. We parse every .c file. It does not harm if it does not contain a driver.
+my @drivers = split /\s+/, `find $kernel_src/drivers/net -name '*.c' | sort`;
+
+# Kernel version
+my $version = `grep UTS_RELEASE $kernel_src/include/linux/version.h`;
+chomp $version;
+$version =~ s/\s*#define\s+UTS_RELEASE\s+"(\S+)".*$/$1/g;
+
+# Architecture
+my @uname = uname();
+
+
+# Print header
+print "# PCI vendor/device ids extracted from Linux $version on $uname[4] at " . gmtime() . "\n";
+
+my $driver;
+
+# Process the drivers
+foreach $driver (@drivers) {
+
+ # Preprocess to expand macros
+ my $command = "$cpp $cflags -I" . dirname($driver) . " $driver";
+ open DRIVER, "$command |" or die "Could not execute\n\"$command\".\n";
+
+ # Extract the pci_device_id structure
+ my $found = 0;
+ my $line = "";
+ my @lines;
+ while(<DRIVER>) {
+ if(/^\s*static\s+struct\s+pci_device_id/) {
+ # This file contains a driver. Print the name.
+ $driver =~ s!$kernel_src/drivers/net/!!g;
+ print "\n$driver\n";
+ $found = 1;
+ next;
+ }
+ if($found == 1){
+ if(/\};/ or /{\s*0\s*,?\s*}/) {
+ # End of struct
+ $found = 0;
+ } else {
+ chomp;
+ if(/\}\s*,?\s*\n?$/) {
+ # This line contains a full entry or the last part of it.
+ $_ = $line . $_;
+ $line = "";
+ s/[,\{\};\(\)]//g; # Strip punctuation
+ s/^\s+//g; # Eat whitespace at beginning of line
+ tr[A-Z][a-z]; # Convert to lowercase
+ # Push the vendor and device id to @lines if this line is not empty.
+ # We ignore everything else that might be there
+ my ($vendor_id, $device_id, $remainder) = split /\W+/, $_, 3;
+ push @lines, "$vendor_id $device_id\n" if($vendor_id && $device_id);
+ } else {
+ # This line does contain a partial entry. Remember it.
+ $line .= "$_ ";
+ }
+ }
+ }
+ }
+ close DRIVER; # No "or die", because $cpp fails on some files
+
+ # Now print out the sorted values
+ @lines = sort @lines;
+ my $lastline = "";
+ foreach(@lines) {
+ # Print each vendor/device id combination only once.
+ # Some drivers (e.g. e100) do contain subfamilies
+ print if($_ ne $lastline);
+ $lastline = $_;
+ }
+}
+
+
diff --git a/gpxe/src/util/hijack.c b/gpxe/src/util/hijack.c
new file mode 100644
index 00000000..ed89592b
--- /dev/null
+++ b/gpxe/src/util/hijack.c
@@ -0,0 +1,628 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <signal.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <pcap.h>
+
+#define SNAPLEN 1600
+
+/*
+ * FIXME: is there a way to detect the version of the libpcap library?
+ * Version 0.9 has pcap_inject; version 0.8 doesn't, but both report
+ * their version number as 2.4.
+ */
+#define HAVE_PCAP_INJECT 0
+
+struct hijack {
+ pcap_t *pcap;
+ int fd;
+ int datalink;
+ int filtered;
+ unsigned long rx_count;
+ unsigned long tx_count;
+};
+
+struct hijack_listener {
+ struct sockaddr_un sun;
+ int fd;
+};
+
+struct hijack_options {
+ char interface[IF_NAMESIZE];
+ int daemonise;
+};
+
+static int daemonised = 0;
+
+static int signalled = 0;
+
+static void flag_signalled ( int signal __attribute__ (( unused )) ) {
+ signalled = 1;
+}
+
+#if ! HAVE_PCAP_INJECT
+/**
+ * Substitute for pcap_inject(), if this version of libpcap doesn't
+ * have it. Will almost certainly only work under Linux.
+ *
+ */
+int pcap_inject ( pcap_t *pcap, const void *data, size_t len ) {
+ int fd;
+ char *errbuf = pcap_geterr ( pcap );
+
+ fd = pcap_get_selectable_fd ( pcap );
+ if ( fd < 0 ) {
+ snprintf ( errbuf, PCAP_ERRBUF_SIZE,
+ "could not get file descriptor" );
+ return -1;
+ }
+ if ( write ( fd, data, len ) != len ) {
+ snprintf ( errbuf, PCAP_ERRBUF_SIZE,
+ "could not write data: %s", strerror ( errno ) );
+ return -1;
+ }
+ return len;
+}
+#endif /* ! HAVE_PCAP_INJECT */
+
+/**
+ * Log error message
+ *
+ */
+static __attribute__ (( format ( printf, 2, 3 ) )) void
+logmsg ( int level, const char *format, ... ) {
+ va_list ap;
+
+ va_start ( ap, format );
+ if ( daemonised ) {
+ vsyslog ( ( LOG_DAEMON | level ), format, ap );
+ } else {
+ vfprintf ( stderr, format, ap );
+ }
+ va_end ( ap );
+}
+
+/**
+ * Open pcap device
+ *
+ */
+static int hijack_open ( const char *interface, struct hijack *hijack ) {
+ char errbuf[PCAP_ERRBUF_SIZE];
+
+ /* Open interface via pcap */
+ errbuf[0] = '\0';
+ hijack->pcap = pcap_open_live ( interface, SNAPLEN, 1, 0, errbuf );
+ if ( ! hijack->pcap ) {
+ logmsg ( LOG_ERR, "Failed to open %s: %s\n",
+ interface, errbuf );
+ goto err;
+ }
+ if ( errbuf[0] )
+ logmsg ( LOG_WARNING, "Warning: %s\n", errbuf );
+
+ /* Set capture interface to non-blocking mode */
+ if ( pcap_setnonblock ( hijack->pcap, 1, errbuf ) < 0 ) {
+ logmsg ( LOG_ERR, "Could not make %s non-blocking: %s\n",
+ interface, errbuf );
+ goto err;
+ }
+
+ /* Get file descriptor for select() */
+ hijack->fd = pcap_get_selectable_fd ( hijack->pcap );
+ if ( hijack->fd < 0 ) {
+ logmsg ( LOG_ERR, "Cannot get selectable file descriptor "
+ "for %s\n", interface );
+ goto err;
+ }
+
+ /* Get link layer type */
+ hijack->datalink = pcap_datalink ( hijack->pcap );
+
+ return 0;
+
+ err:
+ if ( hijack->pcap )
+ pcap_close ( hijack->pcap );
+ return -1;
+}
+
+/**
+ * Close pcap device
+ *
+ */
+static void hijack_close ( struct hijack *hijack ) {
+ pcap_close ( hijack->pcap );
+}
+
+/**
+ * Install filter for hijacked connection
+ *
+ */
+static int hijack_install_filter ( struct hijack *hijack,
+ char *filter ) {
+ struct bpf_program program;
+
+ /* Compile filter */
+ if ( pcap_compile ( hijack->pcap, &program, filter, 1, 0 ) < 0 ) {
+ logmsg ( LOG_ERR, "could not compile filter \"%s\": %s\n",
+ filter, pcap_geterr ( hijack->pcap ) );
+ goto err_nofree;
+ }
+
+ /* Install filter */
+ if ( pcap_setfilter ( hijack->pcap, &program ) < 0 ) {
+ logmsg ( LOG_ERR, "could not install filter \"%s\": %s\n",
+ filter, pcap_geterr ( hijack->pcap ) );
+ goto err;
+ }
+
+ logmsg ( LOG_INFO, "using filter \"%s\"\n", filter );
+
+ pcap_freecode ( &program );
+ return 0;
+
+ err:
+ pcap_freecode ( &program );
+ err_nofree:
+ return -1;
+}
+
+/**
+ * Set up filter for hijacked ethernet connection
+ *
+ */
+static int hijack_filter_ethernet ( struct hijack *hijack, const char *buf,
+ size_t len ) {
+ char filter[55]; /* see format string */
+ struct ether_header *ether_header = ( struct ether_header * ) buf;
+ unsigned char *hwaddr = ether_header->ether_shost;
+
+ if ( len < sizeof ( *ether_header ) )
+ return -1;
+
+ snprintf ( filter, sizeof ( filter ), "broadcast or multicast or "
+ "ether host %02x:%02x:%02x:%02x:%02x:%02x", hwaddr[0],
+ hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5] );
+
+ return hijack_install_filter ( hijack, filter );
+}
+
+/**
+ * Set up filter for hijacked connection
+ *
+ */
+static int hijack_filter ( struct hijack *hijack, const char *buf,
+ size_t len ) {
+ switch ( hijack->datalink ) {
+ case DLT_EN10MB:
+ return hijack_filter_ethernet ( hijack, buf, len );
+ default:
+ logmsg ( LOG_ERR, "unsupported protocol %s: cannot filter\n",
+ ( pcap_datalink_val_to_name ( hijack->datalink ) ?
+ pcap_datalink_val_to_name ( hijack->datalink ) :
+ "UNKNOWN" ) );
+ /* Return success so we don't get called again */
+ return 0;
+ }
+}
+
+/**
+ * Forward data from hijacker
+ *
+ */
+static ssize_t forward_from_hijacker ( struct hijack *hijack, int fd ) {
+ char buf[SNAPLEN];
+ ssize_t len;
+
+ /* Read packet from hijacker */
+ len = read ( fd, buf, sizeof ( buf ) );
+ if ( len < 0 ) {
+ logmsg ( LOG_ERR, "read from hijacker failed: %s\n",
+ strerror ( errno ) );
+ return -1;
+ }
+ if ( len == 0 )
+ return 0;
+
+ /* Set up filter if not already in place */
+ if ( ! hijack->filtered ) {
+ if ( hijack_filter ( hijack, buf, len ) == 0 )
+ hijack->filtered = 1;
+ }
+
+ /* Transmit packet to network */
+ if ( pcap_inject ( hijack->pcap, buf, len ) != len ) {
+ logmsg ( LOG_ERR, "write to hijacked port failed: %s\n",
+ pcap_geterr ( hijack->pcap ) );
+ return -1;
+ }
+
+ hijack->tx_count++;
+ return len;
+};
+
+/**
+ * Forward data to hijacker
+ *
+ */
+static ssize_t forward_to_hijacker ( int fd, struct hijack *hijack ) {
+ struct pcap_pkthdr *pkt_header;
+ const unsigned char *pkt_data;
+ ssize_t len;
+
+ /* Receive packet from network */
+ if ( pcap_next_ex ( hijack->pcap, &pkt_header, &pkt_data ) < 0 ) {
+ logmsg ( LOG_ERR, "read from hijacked port failed: %s\n",
+ pcap_geterr ( hijack->pcap ) );
+ return -1;
+ }
+ if ( pkt_header->caplen != pkt_header->len ) {
+ logmsg ( LOG_ERR, "read partial packet (%d of %d bytes)\n",
+ pkt_header->caplen, pkt_header->len );
+ return -1;
+ }
+ if ( pkt_header->caplen == 0 )
+ return 0;
+ len = pkt_header->caplen;
+
+ /* Write packet to hijacker */
+ if ( write ( fd, pkt_data, len ) != len ) {
+ logmsg ( LOG_ERR, "write to hijacker failed: %s\n",
+ strerror ( errno ) );
+ return -1;
+ }
+
+ hijack->rx_count++;
+ return len;
+};
+
+
+/**
+ * Run hijacker
+ *
+ */
+static int run_hijacker ( const char *interface, int fd ) {
+ struct hijack hijack;
+ fd_set fdset;
+ int max_fd;
+ ssize_t len;
+
+ logmsg ( LOG_INFO, "new connection for %s\n", interface );
+
+ /* Open connection to network */
+ memset ( &hijack, 0, sizeof ( hijack ) );
+ if ( hijack_open ( interface, &hijack ) < 0 )
+ goto err;
+
+ /* Do the forwarding */
+ max_fd = ( ( fd > hijack.fd ) ? fd : hijack.fd );
+ while ( 1 ) {
+ /* Wait for available data */
+ FD_ZERO ( &fdset );
+ FD_SET ( fd, &fdset );
+ FD_SET ( hijack.fd, &fdset );
+ if ( select ( ( max_fd + 1 ), &fdset, NULL, NULL, 0 ) < 0 ) {
+ logmsg ( LOG_ERR, "select failed: %s\n",
+ strerror ( errno ) );
+ goto err;
+ }
+ if ( FD_ISSET ( fd, &fdset ) ) {
+ len = forward_from_hijacker ( &hijack, fd );
+ if ( len < 0 )
+ goto err;
+ if ( len == 0 )
+ break;
+ }
+ if ( FD_ISSET ( hijack.fd, &fdset ) ) {
+ len = forward_to_hijacker ( fd, &hijack );
+ if ( len < 0 )
+ goto err;
+ if ( len == 0 )
+ break;
+ }
+ }
+
+ hijack_close ( &hijack );
+ logmsg ( LOG_INFO, "closed connection for %s\n", interface );
+ logmsg ( LOG_INFO, "received %ld packets, sent %ld packets\n",
+ hijack.rx_count, hijack.tx_count );
+
+ return 0;
+
+ err:
+ if ( hijack.pcap )
+ hijack_close ( &hijack );
+ return -1;
+}
+
+/**
+ * Open listener socket
+ *
+ */
+static int open_listener ( const char *interface,
+ struct hijack_listener *listener ) {
+
+ /* Create socket */
+ listener->fd = socket ( PF_UNIX, SOCK_SEQPACKET, 0 );
+ if ( listener->fd < 0 ) {
+ logmsg ( LOG_ERR, "Could not create socket: %s\n",
+ strerror ( errno ) );
+ goto err;
+ }
+
+ /* Bind to local filename */
+ listener->sun.sun_family = AF_UNIX,
+ snprintf ( listener->sun.sun_path, sizeof ( listener->sun.sun_path ),
+ "/var/run/hijack-%s", interface );
+ if ( bind ( listener->fd, ( struct sockaddr * ) &listener->sun,
+ sizeof ( listener->sun ) ) < 0 ) {
+ logmsg ( LOG_ERR, "Could not bind socket to %s: %s\n",
+ listener->sun.sun_path, strerror ( errno ) );
+ goto err;
+ }
+
+ /* Set as a listening socket */
+ if ( listen ( listener->fd, 0 ) < 0 ) {
+ logmsg ( LOG_ERR, "Could not listen to %s: %s\n",
+ listener->sun.sun_path, strerror ( errno ) );
+ goto err;
+ }
+
+ return 0;
+
+ err:
+ if ( listener->fd >= 0 )
+ close ( listener->fd );
+ return -1;
+}
+
+/**
+ * Listen on listener socket
+ *
+ */
+static int listen_for_hijackers ( struct hijack_listener *listener,
+ const char *interface ) {
+ int fd;
+ pid_t child;
+ int rc;
+
+ logmsg ( LOG_INFO, "Listening on %s\n", listener->sun.sun_path );
+
+ while ( ! signalled ) {
+ /* Accept new connection, interruptibly */
+ siginterrupt ( SIGINT, 1 );
+ siginterrupt ( SIGHUP, 1 );
+ fd = accept ( listener->fd, NULL, 0 );
+ siginterrupt ( SIGINT, 0 );
+ siginterrupt ( SIGHUP, 0 );
+ if ( fd < 0 ) {
+ if ( errno == EINTR ) {
+ continue;
+ } else {
+ logmsg ( LOG_ERR, "accept failed: %s\n",
+ strerror ( errno ) );
+ goto err;
+ }
+ }
+
+ /* Fork child process */
+ child = fork();
+ if ( child < 0 ) {
+ logmsg ( LOG_ERR, "fork failed: %s\n",
+ strerror ( errno ) );
+ goto err;
+ }
+ if ( child == 0 ) {
+ /* I am the child; run the hijacker */
+ rc = run_hijacker ( interface, fd );
+ close ( fd );
+ exit ( rc );
+ }
+
+ close ( fd );
+ }
+
+ logmsg ( LOG_INFO, "Stopped listening on %s\n",
+ listener->sun.sun_path );
+ return 0;
+
+ err:
+ if ( fd >= 0 )
+ close ( fd );
+ return -1;
+}
+
+/**
+ * Close listener socket
+ *
+ */
+static void close_listener ( struct hijack_listener *listener ) {
+ close ( listener->fd );
+ unlink ( listener->sun.sun_path );
+}
+
+/**
+ * Print usage
+ *
+ */
+static void usage ( char **argv ) {
+ logmsg ( LOG_ERR,
+ "Usage: %s [options]\n"
+ "\n"
+ "Options:\n"
+ " -h|--help Print this help message\n"
+ " -i|--interface intf Use specified network interface\n"
+ " -n|--nodaemon Run in foreground\n",
+ argv[0] );
+}
+
+/**
+ * Parse command-line options
+ *
+ */
+static int parse_options ( int argc, char **argv,
+ struct hijack_options *options ) {
+ static struct option long_options[] = {
+ { "interface", 1, NULL, 'i' },
+ { "nodaemon", 0, NULL, 'n' },
+ { "help", 0, NULL, 'h' },
+ { },
+ };
+ int c;
+
+ /* Set default options */
+ memset ( options, 0, sizeof ( *options ) );
+ strncpy ( options->interface, "eth0", sizeof ( options->interface ) );
+ options->daemonise = 1;
+
+ /* Parse command-line options */
+ while ( 1 ) {
+ int option_index = 0;
+
+ c = getopt_long ( argc, argv, "i:hn", long_options,
+ &option_index );
+ if ( c < 0 )
+ break;
+
+ switch ( c ) {
+ case 'i':
+ strncpy ( options->interface, optarg,
+ sizeof ( options->interface ) );
+ break;
+ case 'n':
+ options->daemonise = 0;
+ break;
+ case 'h':
+ usage( argv );
+ return -1;
+ case '?':
+ /* Unrecognised option */
+ return -1;
+ default:
+ logmsg ( LOG_ERR, "Unrecognised option '-%c'\n", c );
+ return -1;
+ }
+ }
+
+ /* Check there's nothing left over on the command line */
+ if ( optind != argc ) {
+ usage ( argv );
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Daemonise
+ *
+ */
+static int daemonise ( const char *interface ) {
+ char pidfile[16 + IF_NAMESIZE + 4]; /* "/var/run/hijack-<intf>.pid" */
+ char pid[16];
+ int pidlen;
+ int fd = -1;
+
+ /* Daemonise */
+ if ( daemon ( 0, 0 ) < 0 ) {
+ logmsg ( LOG_ERR, "Could not daemonise: %s\n",
+ strerror ( errno ) );
+ goto err;
+ }
+ daemonised = 1; /* Direct messages to syslog now */
+
+ /* Open pid file */
+ snprintf ( pidfile, sizeof ( pidfile ), "/var/run/hijack-%s.pid",
+ interface );
+ fd = open ( pidfile, ( O_WRONLY | O_CREAT | O_TRUNC ),
+ ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) );
+ if ( fd < 0 ) {
+ logmsg ( LOG_ERR, "Could not open %s for writing: %s\n",
+ pidfile, strerror ( errno ) );
+ goto err;
+ }
+
+ /* Write pid to file */
+ pidlen = snprintf ( pid, sizeof ( pid ), "%d\n", getpid() );
+ if ( write ( fd, pid, pidlen ) != pidlen ) {
+ logmsg ( LOG_ERR, "Could not write %s: %s\n",
+ pidfile, strerror ( errno ) );
+ goto err;
+ }
+
+ close ( fd );
+ return 0;
+
+ err:
+ if ( fd >= 0 )
+ close ( fd );
+ return -1;
+}
+
+int main ( int argc, char **argv ) {
+ struct hijack_options options;
+ struct hijack_listener listener;
+ struct sigaction sa;
+
+ /* Parse command-line options */
+ if ( parse_options ( argc, argv, &options ) < 0 )
+ exit ( 1 );
+
+ /* Set up syslog connection */
+ openlog ( basename ( argv[0] ), LOG_PID, LOG_DAEMON );
+
+ /* Set up listening socket */
+ if ( open_listener ( options.interface, &listener ) < 0 )
+ exit ( 1 );
+
+ /* Daemonise on demand */
+ if ( options.daemonise ) {
+ if ( daemonise ( options.interface ) < 0 )
+ exit ( 1 );
+ }
+
+ /* Avoid creating zombies */
+ memset ( &sa, 0, sizeof ( sa ) );
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_RESTART | SA_NOCLDWAIT;
+ if ( sigaction ( SIGCHLD, &sa, NULL ) < 0 ) {
+ logmsg ( LOG_ERR, "Could not set SIGCHLD handler: %s",
+ strerror ( errno ) );
+ exit ( 1 );
+ }
+
+ /* Set 'signalled' flag on SIGINT or SIGHUP */
+ sa.sa_handler = flag_signalled;
+ sa.sa_flags = SA_RESTART | SA_RESETHAND;
+ if ( sigaction ( SIGINT, &sa, NULL ) < 0 ) {
+ logmsg ( LOG_ERR, "Could not set SIGINT handler: %s",
+ strerror ( errno ) );
+ exit ( 1 );
+ }
+ if ( sigaction ( SIGHUP, &sa, NULL ) < 0 ) {
+ logmsg ( LOG_ERR, "Could not set SIGHUP handler: %s",
+ strerror ( errno ) );
+ exit ( 1 );
+ }
+
+ /* Listen for hijackers */
+ if ( listen_for_hijackers ( &listener, options.interface ) < 0 )
+ exit ( 1 );
+
+ close_listener ( &listener );
+
+ return 0;
+}
diff --git a/gpxe/src/util/makerom.pl b/gpxe/src/util/makerom.pl
new file mode 100755
index 00000000..aed3a569
--- /dev/null
+++ b/gpxe/src/util/makerom.pl
@@ -0,0 +1,226 @@
+#!/usr/bin/perl -w
+
+use Getopt::Std;
+
+use constant MINROMSIZE => 8192;
+use constant MAXROMSIZE => 262144;
+
+use constant PCI_PTR_LOC => 0x18; # from beginning of ROM
+use constant PCI_HDR_SIZE => 0x18;
+use constant PNP_PTR_LOC => 0x1a; # from beginning of ROM
+use constant PNP_HDR_SIZE => 0x20;
+use constant PNP_CHKSUM_OFF => 0x9; # bytes from beginning of PnP header
+use constant PNP_DEVICE_OFF => 0x10; # bytes from beginning of PnP header
+use constant PCI_VEND_ID_OFF => 0x4; # bytes from beginning of PCI header
+use constant PCI_DEV_ID_OFF => 0x6; # bytes from beginning of PCI header
+use constant PCI_SIZE_OFF => 0x10; # bytes from beginning of PCI header
+
+use constant UNDI_PTR_LOC => 0x16; # from beginning of ROM
+use constant UNDI_HDR_SIZE => 0x16;
+use constant UNDI_CHKSUM_OFF => 0x05;
+
+use strict;
+
+use vars qw(%opts);
+
+use bytes;
+
+sub getromsize ($) {
+ my ($romref) = @_;
+ my $i;
+
+ print STDERR "BIOS extension ROM Image did not start with 0x55 0xAA\n"
+ if (substr($$romref, 0, 2) ne "\x55\xaa");
+ my $size = ord(substr($$romref, 2, 1)) * 512;
+ for ($i = MINROMSIZE; $i < MAXROMSIZE and $i < $size; $i *= 2) { }
+ print STDERR "$size is a strange size for a boot ROM\n"
+ if ($size > 0 and $i > $size);
+ return ($size);
+}
+
+sub addident ($) {
+ my ($romref) = @_;
+
+ return (0) unless (my $s = $opts{'i'});
+ # include the terminating NUL byte too
+ $s .= "\x00";
+ my $len = length($s);
+ # Put the identifier in only if the space is blank
+ my $pos = length($$romref) - $len - 2;
+ return (0) if (substr($$romref, $pos, $len) ne ("\xFF" x $len));
+ substr($$romref, $pos, $len) = $s;
+ return ($pos);
+}
+
+sub pcipnpheaders ($$) {
+ my ($romref, $identoffset) = @_;
+ my ($pci_hdr_offset, $pnp_hdr_offset);
+
+ $pci_hdr_offset = unpack('v', substr($$romref, PCI_PTR_LOC, 2));
+ $pnp_hdr_offset = unpack('v', substr($$romref, PNP_PTR_LOC, 2));
+ # Sanity checks
+ if ($pci_hdr_offset < PCI_PTR_LOC + 2
+ or $pci_hdr_offset > length($$romref) - PCI_HDR_SIZE
+ or $pnp_hdr_offset < PNP_PTR_LOC + 2
+ or $pnp_hdr_offset > length($$romref) - PNP_HDR_SIZE
+ or substr($$romref, $pci_hdr_offset, 4) ne 'PCIR'
+ or substr($$romref, $pnp_hdr_offset, 4) ne '$PnP') {
+ $pci_hdr_offset = $pnp_hdr_offset = 0;
+ } else {
+ printf "PCI header at %#x and PnP header at %#x\n",
+ $pci_hdr_offset, $pnp_hdr_offset if $opts{'v'};
+ }
+ if ($pci_hdr_offset > 0) {
+ my ($pci_vendor_id, $pci_device_id);
+ # if no -p option, just report what's there
+ if (!defined($opts{'p'})) {
+ $pci_vendor_id = unpack('v', substr($$romref, $pci_hdr_offset+PCI_VEND_ID_OFF, 2));
+ $pci_device_id = unpack('v', substr($$romref, $pci_hdr_offset+PCI_DEV_ID_OFF, 2));
+ printf "PCI Vendor ID %#x Device ID %#x\n",
+ $pci_vendor_id, $pci_device_id;
+ } else {
+ substr($$romref, $pci_hdr_offset + PCI_SIZE_OFF, 2)
+ = pack('v', length($$romref) / 512);
+ ($pci_vendor_id, $pci_device_id) = split(/,/, $opts{'p'});
+ substr($$romref, $pci_hdr_offset+PCI_VEND_ID_OFF, 2)
+ = pack('v', oct($pci_vendor_id)) if ($pci_vendor_id);
+ substr($$romref, $pci_hdr_offset+PCI_DEV_ID_OFF, 2)
+ = pack('v', oct($pci_device_id)) if ($pci_device_id);
+ }
+ }
+ if ($pnp_hdr_offset > 0) {
+ if (defined($identoffset)) {
+ # Point to device id string at end of ROM image
+ substr($$romref, $pnp_hdr_offset+PNP_DEVICE_OFF, 2)
+ = pack('v', $identoffset);
+ }
+ substr($$romref, $pnp_hdr_offset+PNP_CHKSUM_OFF, 1) = "\x00";
+ my $sum = unpack('%8C*', substr($$romref, $pnp_hdr_offset,
+ PNP_HDR_SIZE));
+ substr($$romref, $pnp_hdr_offset+PNP_CHKSUM_OFF, 1) = chr(256 - $sum);
+ }
+}
+
+sub undiheaders ($) {
+ my ($romref) = @_;
+ my ($undi_hdr_offset);
+
+ $undi_hdr_offset = unpack('v', substr($$romref, UNDI_PTR_LOC, 2));
+ # Sanity checks
+ if ($undi_hdr_offset < UNDI_PTR_LOC + 2
+ or $undi_hdr_offset > length($$romref) - UNDI_HDR_SIZE
+ or substr($$romref, $undi_hdr_offset, 4) ne 'UNDI') {
+ $undi_hdr_offset = 0;
+ } else {
+ printf "UNDI header at %#x\n", $undi_hdr_offset if $opts{'v'};
+ }
+ if ($undi_hdr_offset > 0) {
+ substr($$romref, $undi_hdr_offset+UNDI_CHKSUM_OFF, 1) = "\x00";
+ my $sum = unpack('%8C*', substr($$romref, $undi_hdr_offset,
+ UNDI_HDR_SIZE));
+ substr($$romref, $undi_hdr_offset+UNDI_CHKSUM_OFF, 1) = chr(256 - $sum);
+ }
+}
+
+sub writerom ($$) {
+ my ($filename, $romref) = @_;
+
+ open(R, ">$filename") or die "$filename: $!\n";
+ print R $$romref;
+ close(R);
+}
+
+sub checksum ($) {
+ my ($romref) = @_;
+
+ substr($$romref, 6, 1) = "\x00";
+ my $sum = unpack('%8C*', $$romref);
+ substr($$romref, 6, 1) = chr(256 - $sum);
+ # Double check
+ $sum = unpack('%8C*', $$romref);
+ if ($sum != 0) {
+ print "Checksum fails\n"
+ } elsif ($opts{'v'}) {
+ print "Checksum ok\n";
+ }
+}
+
+sub makerom () {
+ my ($rom, $romsize);
+
+ getopts('3xi:p:s:v', \%opts);
+ $ARGV[0] or die "Usage: $0 [-s romsize] [-i ident] [-p vendorid,deviceid] [-x] [-3] rom-file\n";
+ open(R, $ARGV[0]) or die "$ARGV[0]: $!\n";
+ # Read in the whole ROM in one gulp
+ my $filesize = read(R, $rom, MAXROMSIZE+1);
+ close(R);
+ defined($filesize) and $filesize >= 3 or die "Cannot get first 3 bytes of file\n";
+ print "$filesize bytes read\n" if $opts{'v'};
+ # If PXE image, just fill the length field and write it out
+ if ($opts{'x'}) {
+ substr($rom, 2, 1) = chr((length($rom) + 511) / 512);
+ writerom($ARGV[0], \$rom);
+ return;
+ }
+ # Size specified with -s overrides value in 3rd byte in image
+ # -s 0 means round up to next 512 byte block
+ if (defined($opts{'s'})) {
+ if (($romsize = oct($opts{'s'})) <= 0) {
+ # NB: This roundup trick only works on powers of 2
+ $romsize = ($filesize + 511) & ~511
+ }
+ } else {
+ # Shrink romsize down to the smallest power of two that will do
+ for ($romsize = MAXROMSIZE;
+ $romsize > MINROMSIZE and $romsize >= 2*$filesize;
+ $romsize /= 2) { }
+ }
+ if ($filesize > $romsize) {
+ print STDERR "ROM size of $romsize not big enough for data, ";
+ # NB: This roundup trick only works on powers of 2
+ $romsize = ($filesize + 511) & ~511;
+ print "will use $romsize instead\n"
+ }
+ # Pad with 0xFF to $romsize
+ $rom .= "\xFF" x ($romsize - length($rom));
+ if ($romsize >= 128 * 1024) {
+ print "Warning: ROM size exceeds extension BIOS limit\n";
+ }
+ substr($rom, 2, 1) = chr(($romsize / 512) % 256);
+ print "ROM size is $romsize\n" if $opts{'v'};
+ # set the product string only if we don't have one yet
+ my $pnp_hdr_offset = unpack('v', substr($rom, PNP_PTR_LOC, 2));
+ my $identoffset = substr($rom, $pnp_hdr_offset+PNP_DEVICE_OFF, 2) eq "\0\0" ? addident(\$rom) : undef;
+ pcipnpheaders(\$rom, $identoffset);
+ undiheaders(\$rom);
+ # 3c503 requires last two bytes to be 0x80
+ substr($rom, MINROMSIZE-2, 2) = "\x80\x80"
+ if ($opts{'3'} and $romsize == MINROMSIZE);
+ checksum(\$rom);
+ writerom($ARGV[0], \$rom);
+}
+
+sub modrom () {
+ my ($rom);
+
+ getopts('p:v', \%opts);
+ $ARGV[0] or die "Usage: $0 [-p vendorid,deviceid] rom-file\n";
+ open(R, $ARGV[0]) or die "$ARGV[0]: $!\n";
+ # Read in the whole ROM in one gulp
+ my $filesize = read(R, $rom, MAXROMSIZE+1);
+ close(R);
+ defined($filesize) and $filesize >= 3 or die "Cannot get first 3 bytes of file\n";
+ print "$filesize bytes read\n" if $opts{'v'};
+ pcipnpheaders(\$rom, undef);
+ undiheaders(\$rom);
+ checksum(\$rom);
+ writerom($ARGV[0], \$rom);
+}
+
+# Main routine. See how we were called and behave accordingly
+if ($0 =~ m:modrom(\.pl)?$:) {
+ modrom();
+} else {
+ makerom();
+}
+exit(0);
diff --git a/gpxe/src/util/mkconfig.pl b/gpxe/src/util/mkconfig.pl
new file mode 100755
index 00000000..6a9c2f14
--- /dev/null
+++ b/gpxe/src/util/mkconfig.pl
@@ -0,0 +1,188 @@
+#!/usr/bin/perl -w
+
+use File::Spec::Functions qw ( :ALL );
+use File::stat;
+use strict;
+use warnings;
+
+my $cfgdir = "config";
+my $config_h = shift || "config.h";
+
+# Read in a whole file
+#
+sub read_file {
+ my $file = shift;
+
+ open my $fh, "<$file" or die "Could not open file $file: $!\n";
+ local $/;
+ my $data = <$fh>;
+ close $fh;
+ return $data;
+}
+
+# Write out a whole file
+#
+sub write_file {
+ my $file = shift;
+ my $data = shift;
+
+ open my $fh, ">$file" or die "Could not write $file: $!\n";
+ print $fh $data;
+ close $fh;
+}
+
+# Delete a file
+#
+sub delete_file {
+ my $file = shift;
+
+ unlink $file or die "Could not delete $file: $!\n";
+}
+
+# Get a file modification time
+#
+sub file_mtime {
+ my $file = shift;
+
+ my $stat = stat ( $file ) or die "Could not stat $file: $!\n";
+ return $stat->mtime;
+}
+
+# Read all the .h files in a directory
+#
+sub read_dir {
+ my $dir = shift;
+
+ opendir my $dh, $dir or die "Could not open directory $dir: $!\n";
+ my @entries = grep { /\.h$/ } readdir $dh;
+ closedir $dh;
+ return @entries;
+}
+
+# Get the current configuration by reading the configuration file
+# fragments
+#
+sub current_config {
+ my $dir = shift;
+
+ my $cfg = {};
+ foreach my $file ( read_dir ( $dir ) ) {
+ $cfg->{$file} = read_file ( catfile ( $dir, $file ) );
+ }
+ return $cfg;
+}
+
+# Calculate guard name for a header file
+#
+sub guard {
+ my $name = shift;
+
+ $name =~ s/\W/_/g;
+ return "CONFIG_".( uc $name );
+}
+
+# Calculate preamble for a header file
+#
+sub preamble {
+ my $name = shift;
+ my $master = shift;
+
+ my $guard = guard ( $name );
+ my $preamble = <<"EOF";
+/*
+ * This file is automatically generated from $master. Do not edit this
+ * file; edit $master instead.
+ *
+ */
+
+#ifndef $guard
+#define $guard
+EOF
+ return $preamble;
+}
+
+# Calculate postamble for a header file
+#
+sub postamble {
+ my $name = shift;
+
+ my $guard = guard ( $name );
+ return "\n#endif /* $guard */\n";
+}
+
+# Get the new configuration by splitting config.h file using the
+# @BEGIN/@END tags
+#
+sub new_config {
+ my $file = shift;
+
+ my $cfg = {};
+ my $cursor = "";
+
+ open my $fh, "<$file" or die "Could not open $file: $!\n";
+ while ( <$fh> ) {
+ if ( ( my $newcursor, my $suffix ) = /\@BEGIN\s+(\w+\.h)(.*)$/ ) {
+ die "Missing \"\@END $cursor\" before \"\@BEGIN $1\""
+ ." at $file line $.\n" if $cursor;
+ $cursor = $newcursor;
+ $cfg->{$cursor} = preamble ( $cursor, $file )
+ unless exists $cfg->{$cursor};
+ $cfg->{$cursor} .= "\n/*".$suffix."\n";
+ } elsif ( ( my $prefix, my $oldcursor ) = /^(.*)\@END\s+(\w+\.h)/ ) {
+ die "Missing \"\@BEGIN $oldcursor\" before \"\@END $oldcursor\""
+ ." at $file line $.\n" unless $cursor eq $oldcursor;
+ $cfg->{$cursor} .= $prefix."*/\n";
+ $cursor = "";
+ } else {
+ $cfg->{$cursor} .= $_ if $cursor;
+ }
+ }
+ close $fh;
+ die "Missing \"\@END $cursor\" in $file\n" if $cursor;
+
+ foreach $cursor ( keys %$cfg ) {
+ $cfg->{$cursor} .= postamble ( $cursor );
+ }
+
+ return $cfg;
+}
+
+#############################################################################
+#
+# Main program
+
+# Read in current config file fragments
+#
+my $current = current_config ( $cfgdir );
+
+# Read in config.h and split it into fragments
+#
+my $new = new_config ( $config_h );
+
+# Delete any no-longer-wanted config file fragments
+#
+foreach my $file ( keys %$current ) {
+ unlink catfile ( $cfgdir, $file ) unless exists $new->{$file};
+}
+
+# Write out any modified fragments, and find the oldest timestamp of
+# any unmodified fragments.
+#
+my $oldest = time ();
+foreach my $file ( keys %$new ) {
+ if ( $current->{$file} && $new->{$file} eq $current->{$file} ) {
+ # Unmodified
+ my $time = file_mtime ( catfile ( $cfgdir, $file ) );
+ $oldest = $time if $time < $oldest;
+ } else {
+ write_file ( catfile ( $cfgdir, $file ), $new->{$file} );
+ }
+}
+
+# If we now have fragments that are older than config.h, set the
+# timestamp on config.h to match the oldest fragment, to prevent make
+# from always attempting to rebuild the fragments.
+#
+if ( $oldest < file_mtime ( $config_h ) ) {
+ utime time(), $oldest, $config_h or die "Could not touch $config_h: $!\n";
+}
diff --git a/gpxe/src/util/modrom.pl b/gpxe/src/util/modrom.pl
new file mode 100755
index 00000000..695468c2
--- /dev/null
+++ b/gpxe/src/util/modrom.pl
@@ -0,0 +1,226 @@
+#!/usr/bin/perl -w
+
+use Getopt::Std;
+
+use constant MINROMSIZE => 8192;
+use constant MAXROMSIZE => 262144;
+
+use constant PCI_PTR_LOC => 0x18; # from beginning of ROM
+use constant PCI_HDR_SIZE => 0x18;
+use constant PNP_PTR_LOC => 0x1a; # from beginning of ROM
+use constant PNP_HDR_SIZE => 0x20;
+use constant PNP_CHKSUM_OFF => 0x9; # bytes from beginning of PnP header
+use constant PNP_DEVICE_OFF => 0x10; # bytes from beginning of PnP header
+use constant PCI_VEND_ID_OFF => 0x4; # bytes from beginning of PCI header
+use constant PCI_DEV_ID_OFF => 0x6; # bytes from beginning of PCI header
+use constant PCI_SIZE_OFF => 0x10; # bytes from beginning of PCI header
+
+use constant UNDI_PTR_LOC => 0x16; # from beginning of ROM
+use constant UNDI_HDR_SIZE => 0x16;
+use constant UNDI_CHKSUM_OFF => 0x05;
+
+use strict;
+
+use vars qw(%opts);
+
+use bytes;
+
+sub getromsize ($) {
+ my ($romref) = @_;
+ my $i;
+
+ print STDERR "BIOS extension ROM Image did not start with 0x55 0xAA\n"
+ if (substr($$romref, 0, 2) ne "\x55\xaa");
+ my $size = ord(substr($$romref, 2, 1)) * 512;
+ for ($i = MINROMSIZE; $i < MAXROMSIZE and $i < $size; $i *= 2) { }
+ print STDERR "$size is a strange size for a boot ROM\n"
+ if ($size > 0 and $i > $size);
+ return ($size);
+}
+
+sub addident ($) {
+ my ($romref) = @_;
+
+ return (0) unless (my $s = $opts{'i'});
+ # include the terminating NUL byte too
+ $s .= "\x00";
+ my $len = length($s);
+ # Put the identifier in only if the space is blank
+ my $pos = length($$romref) - $len - 2;
+ return (0) if (substr($$romref, $pos, $len) ne ("\xFF" x $len));
+ substr($$romref, $pos, $len) = $s;
+ return ($pos);
+}
+
+sub pcipnpheaders ($$) {
+ my ($romref, $identoffset) = @_;
+ my ($pci_hdr_offset, $pnp_hdr_offset);
+
+ $pci_hdr_offset = unpack('v', substr($$romref, PCI_PTR_LOC, 2));
+ $pnp_hdr_offset = unpack('v', substr($$romref, PNP_PTR_LOC, 2));
+ # Sanity checks
+ if ($pci_hdr_offset < PCI_PTR_LOC + 2
+ or $pci_hdr_offset > length($$romref) - PCI_HDR_SIZE
+ or $pnp_hdr_offset < PNP_PTR_LOC + 2
+ or $pnp_hdr_offset > length($$romref) - PNP_HDR_SIZE
+ or substr($$romref, $pci_hdr_offset, 4) ne 'PCIR'
+ or substr($$romref, $pnp_hdr_offset, 4) ne '$PnP') {
+ $pci_hdr_offset = $pnp_hdr_offset = 0;
+ } else {
+ printf "PCI header at %#x and PnP header at %#x\n",
+ $pci_hdr_offset, $pnp_hdr_offset;
+ }
+ if ($pci_hdr_offset > 0) {
+ my ($pci_vendor_id, $pci_device_id);
+ # if no -p option, just report what's there
+ if (!defined($opts{'p'})) {
+ $pci_vendor_id = unpack('v', substr($$romref, $pci_hdr_offset+PCI_VEND_ID_OFF, 2));
+ $pci_device_id = unpack('v', substr($$romref, $pci_hdr_offset+PCI_DEV_ID_OFF, 2));
+ printf "PCI Vendor ID %#x Device ID %#x\n",
+ $pci_vendor_id, $pci_device_id;
+ } else {
+ substr($$romref, $pci_hdr_offset + PCI_SIZE_OFF, 2)
+ = pack('v', length($$romref) / 512);
+ ($pci_vendor_id, $pci_device_id) = split(/,/, $opts{'p'});
+ substr($$romref, $pci_hdr_offset+PCI_VEND_ID_OFF, 2)
+ = pack('v', oct($pci_vendor_id)) if ($pci_vendor_id);
+ substr($$romref, $pci_hdr_offset+PCI_DEV_ID_OFF, 2)
+ = pack('v', oct($pci_device_id)) if ($pci_device_id);
+ }
+ }
+ if ($pnp_hdr_offset > 0 and defined($identoffset)) {
+ # Point to device id string at end of ROM image
+ substr($$romref, $pnp_hdr_offset+PNP_DEVICE_OFF, 2)
+ = pack('v', $identoffset);
+ substr($$romref, $pnp_hdr_offset+PNP_CHKSUM_OFF, 1) = "\x00";
+ my $sum = unpack('%8C*', substr($$romref, $pnp_hdr_offset,
+ PNP_HDR_SIZE));
+ substr($$romref, $pnp_hdr_offset+PNP_CHKSUM_OFF, 1) = chr(256 - $sum);
+ }
+}
+
+sub undiheaders ($) {
+ my ($romref) = @_;
+ my ($undi_hdr_offset);
+
+ $undi_hdr_offset = unpack('v', substr($$romref, UNDI_PTR_LOC, 2));
+ # Sanity checks
+ if ($undi_hdr_offset < UNDI_PTR_LOC + 2
+ or $undi_hdr_offset > length($$romref) - UNDI_HDR_SIZE
+ or substr($$romref, $undi_hdr_offset, 4) ne 'UNDI') {
+ $undi_hdr_offset = 0;
+ } else {
+ printf "UNDI header at %#x\n", $undi_hdr_offset;
+ }
+ if ($undi_hdr_offset > 0) {
+ substr($$romref, $undi_hdr_offset+UNDI_CHKSUM_OFF, 1) = "\x00";
+ my $sum = unpack('%8C*', substr($$romref, $undi_hdr_offset,
+ UNDI_HDR_SIZE));
+ substr($$romref, $undi_hdr_offset+UNDI_CHKSUM_OFF, 1) = chr(256 - $sum);
+ }
+}
+
+sub writerom ($$) {
+ my ($filename, $romref) = @_;
+
+ open(R, ">$filename") or die "$filename: $!\n";
+ print R $$romref;
+ close(R);
+}
+
+sub checksum ($) {
+ my ($romref) = @_;
+
+ substr($$romref, 5, 1) = "\x00";
+ my $sum = unpack('%8C*', $$romref);
+ substr($$romref, 5, 1) = chr(256 - $sum);
+ # Double check
+ $sum = unpack('%8C*', $$romref);
+ if ($sum != 0) {
+ print "Checksum fails\n"
+ } elsif ($opts{'v'}) {
+ print "Checksum ok\n";
+ }
+}
+
+sub makerom () {
+ my ($rom, $romsize);
+
+ getopts('3xi:p:s:v', \%opts);
+ $ARGV[0] or die "Usage: $0 [-s romsize] [-i ident] [-p vendorid,deviceid] [-x] [-3] rom-file\n";
+ open(R, $ARGV[0]) or die "$ARGV[0]: $!\n";
+ # Read in the whole ROM in one gulp
+ my $filesize = read(R, $rom, MAXROMSIZE+1);
+ close(R);
+ defined($filesize) and $filesize >= 3 or die "Cannot get first 3 bytes of file\n";
+ print "$filesize bytes read\n" if $opts{'v'};
+ # If PXE image, just fill the length field and write it out
+ if ($opts{'x'}) {
+ substr($rom, 2, 1) = chr((length($rom) + 511) / 512);
+ &writerom($ARGV[0], \$rom);
+ return;
+ }
+ # Size specified with -s overrides value in 3rd byte in image
+ # -s 0 means round up to next 512 byte block
+ if (defined($opts{'s'})) {
+ if (($romsize = oct($opts{'s'})) <= 0) {
+ # NB: This roundup trick only works on powers of 2
+ $romsize = ($filesize + 511) & ~511
+ }
+ } else {
+ $romsize = &getromsize(\$rom);
+ # 0 put there by *loader.S means makerom should pick the size
+ if ($romsize == 0) {
+ # Shrink romsize down to the smallest power of two that will do
+ for ($romsize = MAXROMSIZE;
+ $romsize > MINROMSIZE and $romsize >= 2*$filesize;
+ $romsize /= 2) { }
+ }
+ }
+ if ($filesize > $romsize) {
+ print STDERR "ROM size of $romsize not big enough for data, ";
+ # NB: This roundup trick only works on powers of 2
+ $romsize = ($filesize + 511) & ~511;
+ print "will use $romsize instead\n"
+ }
+ # Pad with 0xFF to $romsize
+ $rom .= "\xFF" x ($romsize - length($rom));
+ if ($romsize >= 128 * 1024) {
+ print "Warning: ROM size exceeds extension BIOS limit\n";
+ }
+ substr($rom, 2, 1) = chr(($romsize / 512) % 256);
+ print "ROM size is $romsize\n" if $opts{'v'};
+ my $identoffset = &addident(\$rom);
+ &pcipnpheaders(\$rom, $identoffset);
+ &undiheaders(\$rom);
+ # 3c503 requires last two bytes to be 0x80
+ substr($rom, MINROMSIZE-2, 2) = "\x80\x80"
+ if ($opts{'3'} and $romsize == MINROMSIZE);
+ &checksum(\$rom);
+ &writerom($ARGV[0], \$rom);
+}
+
+sub modrom () {
+ my ($rom);
+
+ getopts('p:v', \%opts);
+ $ARGV[0] or die "Usage: $0 [-p vendorid,deviceid] rom-file\n";
+ open(R, $ARGV[0]) or die "$ARGV[0]: $!\n";
+ # Read in the whole ROM in one gulp
+ my $filesize = read(R, $rom, MAXROMSIZE+1);
+ close(R);
+ defined($filesize) and $filesize >= 3 or die "Cannot get first 3 bytes of file\n";
+ print "$filesize bytes read\n" if $opts{'v'};
+ &pcipnpheaders(\$rom);
+ &undiheaders(\$rom);
+ &checksum(\$rom);
+ &writerom($ARGV[0], \$rom);
+}
+
+# Main routine. See how we were called and behave accordingly
+if ($0 =~ m:modrom(\.pl)?$:) {
+ &modrom();
+} else {
+ &makerom();
+}
+exit(0);
diff --git a/gpxe/src/util/mucurses_test.c b/gpxe/src/util/mucurses_test.c
new file mode 100644
index 00000000..586562df
--- /dev/null
+++ b/gpxe/src/util/mucurses_test.c
@@ -0,0 +1,63 @@
+#include "../include/curses.h"
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+void get_iscsi_chap_secret( char * );
+void mdelay( int msecs );
+
+int main ( void ) {
+ char secret[16];
+ initscr();
+ echo();
+ werase(stdscr);
+ box( stdscr, '|', '-' );
+ get_iscsi_chap_secret(secret);
+
+ mvwprintw( stdscr, 3, 5, "password is \"%s\"", secret );
+ mdelay(2500);
+
+ stdscr->scr->exit(stdscr->scr);
+
+ return 0;
+}
+
+void get_iscsi_chap_secret( char *sec ) {
+ char *title = "Set new iSCSI CHAP secret",
+ *msg = "Configure the iSCSI access secret",
+ pw1[17], pw2[17];
+ WINDOW *secret;
+
+ secret = newwin( stdscr->height / 2,
+ stdscr->width / 2,
+ stdscr->height / 4,
+ stdscr->width / 4 );
+
+ wborder( secret, '|', '|', '-', '-', '+', '+', '+', '+' );
+ mvwprintw( secret, 1, 2, "%s", title );
+ mvwhline( secret, 2, 1, '-' | secret->attrs, secret->width - 2 );
+ mvwprintw( secret, 4, 2, "%s", msg );
+ mvwprintw( secret, 6, 3, "secret" );
+ mvwprintw( secret, 8, 3, "confirm" );
+ start:
+ mvwhline( secret, 6, 12, '_' | secret->attrs, 16 );
+ mvwhline( secret, 8, 12, '_' | secret->attrs, 16 );
+
+ wmove( secret, 6, 12 );
+ wgetnstr( secret, pw1, 16 );
+ wmove( secret, 8, 12 );
+ wgetnstr( secret, pw2, 16 );
+
+ if ( strcmp( pw1, pw2 ) == 0 ) {
+ strcpy( sec, pw1 );
+ werase( secret );
+ }
+ else {
+ mvwprintw( secret, 10, 3, "Passwords do not match" );
+ goto start;
+ }
+}
+
+void mdelay ( int msecs ) {
+ usleep( msecs * 1000 );
+}
diff --git a/gpxe/src/util/nrv2b.c b/gpxe/src/util/nrv2b.c
new file mode 100644
index 00000000..6bac4cdd
--- /dev/null
+++ b/gpxe/src/util/nrv2b.c
@@ -0,0 +1,1501 @@
+/**************************************************************
+ Form adapted from lzhuf.c
+ written by Haruyasu Yoshizaki 11/20/1988
+ some minor changes 4/6/1989
+ comments translated by Haruhiko Okumura 4/7/1989
+
+ minor beautifications and adjustments for compiling under Linux
+ by Markus Gutschke <gutschk@math.uni-muenster.de>
+ 1997-01-27
+
+ Modifications to allow use as a filter by Ken Yap
+ <ken_yap@users.sourceforge.net>.
+
+ 1997-07-01
+
+ Small mod to cope with running on big-endian machines
+ by Jim Hague <jim.hague@acm.org)
+ 1998-02-06
+
+ Make compression statistics report shorter
+ by Ken Yap <ken_yap@users.sourceforge.net>.
+ 2001-04-25
+
+ Replaced algorithm with nrv2b from ucl the compression
+ library from upx. That code is:
+ Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
+ And is distributed under the terms of the GPL.
+ The conversion was performed
+ by Eric Biederman <ebiederman@lnxi.com>.
+ 20 August 2002
+
+**************************************************************/
+#define UCLPACK_COMPAT 0
+#define NDEBUG 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#ifdef __FreeBSD__
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+#include <limits.h>
+#include <assert.h>
+#if UCLPACK_COMPAT
+#include <netinet/in.h>
+#endif
+
+#ifndef VERBOSE
+#define Fprintf(x)
+#define wterr 0
+#else
+#define Fprintf(x) fprintf x
+#endif
+
+#ifndef MAIN
+extern
+#endif
+FILE *infile, *outfile;
+
+#if defined(ENCODE) || defined(DECODE)
+
+#ifndef ENDIAN
+#define ENDIAN 0
+#endif
+#ifndef BITSIZE
+#define BITSIZE 32
+#endif
+
+static __inline__ void Error(char *message)
+{
+ Fprintf((stderr, "\n%s\n", message));
+ exit(EXIT_FAILURE);
+}
+
+/* These will be a complete waste of time on a lo-endian */
+/* system, but it only gets done once so WTF. */
+static unsigned long i86ul_to_host(unsigned long ul)
+{
+ unsigned long res = 0;
+ int i;
+ union
+ {
+ unsigned char c[4];
+ unsigned long ul;
+ } u;
+
+ u.ul = ul;
+ for (i = 3; i >= 0; i--)
+ res = (res << 8) + u.c[i];
+ return res;
+}
+
+static unsigned long host_to_i86ul(unsigned long ul)
+{
+ int i;
+ union
+ {
+ unsigned char c[4];
+ unsigned long ul;
+ } u;
+
+ for (i = 0; i < 4; i++)
+ {
+ u.c[i] = ul & 0xff;
+ ul >>= 8;
+ }
+ return u.ul;
+}
+#endif
+
+
+
+#if UCLPACK_COMPAT
+/* magic file header for compressed files */
+static const unsigned char magic[8] =
+{ 0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a };
+
+#endif
+
+#ifdef ENCODE
+/********** NRV2B_99 compression **********/
+
+/* Note by limiting the ring buffer I have limited the maximum
+ * offset to 64K. Since etherboot rarely gets that big it
+ * is not a problem and it gives me a firm guarantee
+ * that I will never get a 3 byte string match that is encodes
+ * to more than 9/8 it's original size.
+ * That guaranteee is important to for the inplace decompressor.
+ * There are better ways to do this if a larger offset and buffer
+ * would give better compression.
+ */
+#define N (65536ul) /* size of ring buffer */
+#define THRESHOLD 1 /* lower limit for match length */
+#define F 2048 /* upper limit for match length */
+#define M2_MAX_OFFSET 0xd00
+
+/* note: to use default values pass -1, i.e. initialize
+ * this struct by a memset(x,0xff,sizeof(x)) */
+struct ucl_compress_config
+{
+ int bb_endian;
+ int bb_size;
+ unsigned int max_offset;
+ unsigned int max_match;
+ int s_level;
+ int h_level;
+ int p_level;
+ int c_flags;
+ unsigned int m_size;
+};
+
+struct ucl_compress
+{
+ int init;
+
+ unsigned int look; /* bytes in lookahead buffer */
+
+ unsigned int m_len;
+ unsigned int m_off;
+
+ unsigned int last_m_len;
+ unsigned int last_m_off;
+
+ const unsigned char *bp;
+ const unsigned char *ip;
+ const unsigned char *in;
+ const unsigned char *in_end;
+ unsigned char *out;
+
+ uint64_t bb_b;
+ unsigned bb_k;
+ unsigned bb_c_endian;
+ unsigned bb_c_s;
+ unsigned bb_c_s8;
+ unsigned char *bb_p;
+ unsigned char *bb_op;
+
+ struct ucl_compress_config conf;
+ unsigned int *result;
+
+ unsigned int textsize; /* text size counter */
+ unsigned int codesize; /* code size counter */
+ unsigned int printcount; /* counter for reporting progress every 1K
+ bytes */
+
+
+ /* some stats */
+ unsigned long lit_bytes;
+ unsigned long match_bytes;
+ unsigned long rep_bytes;
+ unsigned long lazy;
+};
+
+
+
+#define getbyte(c) ((c).ip < (c).in_end ? *((c).ip)++ : (-1))
+
+#define UCL_E_OK 0
+#define UCL_E_INVALID_ARGUMENT 1
+#define UCL_E_OUT_OF_MEMORY 2
+#define UCL_E_ERROR 3
+
+/***********************************************************************
+//
+************************************************************************/
+
+#define SWD_HSIZE 16384
+#define SWD_MAX_CHAIN 2048
+#define SWD_BEST_OFF 1
+
+#define HEAD3(b,p) \
+ (((0x9f5f*(((((uint32_t)b[p]<<5)^b[p+1])<<5)^b[p+2]))>>5) & (SWD_HSIZE-1))
+
+#define HEAD2(b,p) (b[p] ^ ((unsigned)b[p+1]<<8))
+#define NIL2 UINT_MAX
+
+struct ucl_swd
+{
+/* public - "built-in" */
+ unsigned int n;
+ unsigned int f;
+ unsigned int threshold;
+
+/* public - configuration */
+ unsigned int max_chain;
+ unsigned int nice_length;
+ int use_best_off;
+ unsigned int lazy_insert;
+
+/* public - output */
+ unsigned int m_len;
+ unsigned int m_off;
+ unsigned int look;
+ int b_char;
+#if defined(SWD_BEST_OFF)
+ unsigned int best_off[ SWD_BEST_OFF ];
+#endif
+
+/* semi public */
+ struct ucl_compress *c;
+ unsigned int m_pos;
+#if defined(SWD_BEST_OFF)
+ unsigned int best_pos[ SWD_BEST_OFF ];
+#endif
+
+/* private */
+ const uint8_t *dict;
+ const uint8_t *dict_end;
+ unsigned int dict_len;
+
+/* private */
+ unsigned int ip; /* input pointer (lookahead) */
+ unsigned int bp; /* buffer pointer */
+ unsigned int rp; /* remove pointer */
+ unsigned int b_size;
+
+ unsigned char *b_wrap;
+
+ unsigned int node_count;
+ unsigned int first_rp;
+
+ unsigned char b [ N + F + F ];
+ unsigned int head3 [ SWD_HSIZE ];
+ unsigned int succ3 [ N + F ];
+ unsigned int best3 [ N + F ];
+ unsigned int llen3 [ SWD_HSIZE ];
+ unsigned int head2 [ 65536U ];
+};
+
+#define s_head3(s,key) s->head3[key]
+
+
+#if !defined( NDEBUG)
+static void assert_match(const struct ucl_swd * swd, unsigned int m_len,
+ unsigned int m_off )
+
+{
+ const struct ucl_compress *c = swd->c;
+ unsigned int d_off;
+
+ assert(m_len >= 2);
+ if (m_off <= (unsigned int) (c->bp - c->in))
+ {
+ assert(c->bp - m_off + m_len < c->ip);
+ assert(memcmp(c->bp, c->bp - m_off, m_len) == 0);
+ }
+ else
+ {
+ assert(swd->dict != NULL);
+ d_off = m_off - (unsigned int) (c->bp - c->in);
+ assert(d_off <= swd->dict_len);
+ if (m_len > d_off)
+ {
+ assert(memcmp(c->bp, swd->dict_end - d_off, d_off) ==
+ 0);
+
+ assert(c->in + m_len - d_off < c->ip);
+ assert(memcmp(c->bp + d_off, c->in, m_len - d_off) ==
+ 0);
+
+ }
+ else
+ {
+ assert(memcmp(c->bp, swd->dict_end - d_off, m_len) ==
+ 0);
+
+ }
+ }
+}
+#else
+# define assert_match(a,b,c) ((void)0)
+#endif
+
+/***********************************************************************
+//
+************************************************************************/
+
+
+static
+void swd_initdict(struct ucl_swd *s, const uint8_t *dict, unsigned int dict_len)
+
+{
+ s->dict = s->dict_end = NULL;
+ s->dict_len = 0;
+
+ if (!dict || dict_len <= 0)
+ return;
+ if (dict_len > s->n)
+ {
+ dict += dict_len - s->n;
+ dict_len = s->n;
+ }
+
+ s->dict = dict;
+ s->dict_len = dict_len;
+ s->dict_end = dict + dict_len;
+ memcpy(s->b,dict,dict_len);
+ s->ip = dict_len;
+}
+
+
+static
+void swd_insertdict(struct ucl_swd *s, unsigned int node, unsigned int len)
+{
+ unsigned int key;
+
+ s->node_count = s->n - len;
+ s->first_rp = node;
+
+ while (len-- > 0)
+ {
+ key = HEAD3(s->b,node);
+ s->succ3[node] = s_head3(s,key);
+ s->head3[key] = (unsigned int)(node);
+ s->best3[node] = (unsigned int)(s->f + 1);
+ s->llen3[key]++;
+ assert(s->llen3[key] <= s->n);
+
+ key = HEAD2(s->b,node);
+ s->head2[key] = (unsigned int)(node);
+
+ node++;
+ }
+}
+
+/***********************************************************************
+//
+************************************************************************/
+
+
+static
+int swd_init(struct ucl_swd *s, const uint8_t *dict, unsigned int dict_len)
+{
+ unsigned int i = 0;
+ int c = 0;
+
+ if (s->n == 0)
+ s->n = N;
+ if (s->f == 0)
+ s->f = F;
+ s->threshold = THRESHOLD;
+ if (s->n > N || s->f > F)
+ return UCL_E_INVALID_ARGUMENT;
+
+ /* defaults */
+ s->max_chain = SWD_MAX_CHAIN;
+ s->nice_length = s->f;
+ s->use_best_off = 0;
+ s->lazy_insert = 0;
+
+ s->b_size = s->n + s->f;
+ if (s->b_size + s->f >= UINT_MAX)
+ return UCL_E_ERROR;
+ s->b_wrap = s->b + s->b_size;
+ s->node_count = s->n;
+
+ memset(s->llen3, 0, sizeof(s->llen3[0]) * SWD_HSIZE);
+ for (i = 0; i < 65536U; i++)
+ s->head2[i] = NIL2;
+
+ s->ip = 0;
+ swd_initdict(s,dict,dict_len);
+ s->bp = s->ip;
+ s->first_rp = s->ip;
+
+ assert(s->ip + s->f <= s->b_size);
+
+ s->look = (unsigned int) (s->c->in_end - s->c->ip);
+ if (s->look > 0)
+ {
+ if (s->look > s->f)
+ s->look = s->f;
+ memcpy(&s->b[s->ip],s->c->ip,s->look);
+ s->c->ip += s->look;
+ s->ip += s->look;
+ }
+ if (s->ip == s->b_size)
+ s->ip = 0;
+
+ if (s->look >= 2 && s->dict_len > 0)
+ swd_insertdict(s,0,s->dict_len);
+
+ s->rp = s->first_rp;
+ if (s->rp >= s->node_count)
+ s->rp -= s->node_count;
+ else
+ s->rp += s->b_size - s->node_count;
+
+ /* unused i */
+ /* unused c */
+ return UCL_E_OK;
+}
+
+
+static
+void swd_exit(struct ucl_swd *s)
+{
+ /* unused s */
+
+}
+
+#define swd_pos2off(s,pos) \
+ (s->bp > (pos) ? s->bp - (pos) : s->b_size - ((pos) - s->bp))
+
+/***********************************************************************
+//
+************************************************************************/
+
+static __inline__
+void swd_getbyte(struct ucl_swd *s)
+{
+ int c;
+
+ if ((c = getbyte(*(s->c))) < 0)
+ {
+ if (s->look > 0)
+ --s->look;
+ }
+ else
+ {
+ s->b[s->ip] = (uint8_t)(c);
+ if (s->ip < s->f)
+ s->b_wrap[s->ip] = (uint8_t)(c);
+ }
+ if (++s->ip == s->b_size)
+ s->ip = 0;
+ if (++s->bp == s->b_size)
+ s->bp = 0;
+ if (++s->rp == s->b_size)
+ s->rp = 0;
+}
+/***********************************************************************
+// remove node from lists
+************************************************************************/
+
+static __inline__
+void swd_remove_node(struct ucl_swd *s, unsigned int node)
+{
+ if (s->node_count == 0)
+ {
+ unsigned int key;
+
+#ifdef UCL_DEBUG
+ if (s->first_rp != UINT_MAX)
+ {
+ if (node != s->first_rp)
+ printf("Remove %5d: %5d %5d %5d %5d %6d %6d\n",
+
+ node, s->rp, s->ip, s->bp, s->first_rp,
+ s->ip - node, s->ip - s->bp);
+ assert(node == s->first_rp);
+ s->first_rp = UINT_MAX;
+ }
+#endif
+
+ key = HEAD3(s->b,node);
+ assert(s->llen3[key] > 0);
+ --s->llen3[key];
+
+ key = HEAD2(s->b,node);
+ assert(s->head2[key] != NIL2);
+ if ((unsigned int) s->head2[key] == node)
+ s->head2[key] = NIL2;
+ }
+ else
+ --s->node_count;
+}
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+
+static
+void swd_accept(struct ucl_swd *s, unsigned int n)
+{
+ assert(n <= s->look);
+
+ if (n > 0) do
+ {
+ unsigned int key;
+
+ swd_remove_node(s,s->rp);
+
+ /* add bp into HEAD3 */
+ key = HEAD3(s->b,s->bp);
+ s->succ3[s->bp] = s_head3(s,key);
+ s->head3[key] = (unsigned int)(s->bp);
+ s->best3[s->bp] = (unsigned int)(s->f + 1);
+ s->llen3[key]++;
+ assert(s->llen3[key] <= s->n);
+
+ /* add bp into HEAD2 */
+ key = HEAD2(s->b,s->bp);
+ s->head2[key] = (unsigned int)(s->bp);
+
+ swd_getbyte(s);
+ } while (--n > 0);
+}
+
+/***********************************************************************
+//
+************************************************************************/
+
+static
+void swd_search(struct ucl_swd *s, unsigned int node, unsigned int cnt)
+{
+ const unsigned char *p1;
+ const unsigned char *p2;
+ const unsigned char *px;
+
+ unsigned int m_len = s->m_len;
+ const unsigned char * b = s->b;
+ const unsigned char * bp = s->b + s->bp;
+ const unsigned char * bx = s->b + s->bp + s->look;
+ unsigned char scan_end1;
+
+ assert(s->m_len > 0);
+
+ scan_end1 = bp[m_len - 1];
+ for ( ; cnt-- > 0; node = s->succ3[node])
+ {
+ p1 = bp;
+ p2 = b + node;
+ px = bx;
+
+ assert(m_len < s->look);
+
+ if (
+ p2[m_len - 1] == scan_end1 &&
+ p2[m_len] == p1[m_len] &&
+ p2[0] == p1[0] &&
+ p2[1] == p1[1])
+ {
+ unsigned int i;
+ assert(memcmp(bp,&b[node],3) == 0);
+
+ p1 += 2; p2 += 2;
+ do {} while (++p1 < px && *p1 == *++p2);
+ i = p1 - bp;
+
+#ifdef UCL_DEBUG
+ if (memcmp(bp,&b[node],i) != 0)
+ printf("%5ld %5ld %02x%02x %02x%02x\n",
+ (long)s->bp, (long) node,
+ bp[0], bp[1], b[node], b[node+1]);
+#endif
+ assert(memcmp(bp,&b[node],i) == 0);
+
+#if defined(SWD_BEST_OFF)
+ if (i < SWD_BEST_OFF)
+ {
+ if (s->best_pos[i] == 0)
+ s->best_pos[i] = node + 1;
+ }
+#endif
+ if (i > m_len)
+ {
+ s->m_len = m_len = i;
+ s->m_pos = node;
+ if (m_len == s->look)
+ return;
+ if (m_len >= s->nice_length)
+ return;
+ if (m_len > (unsigned int) s->best3[node])
+ return;
+ scan_end1 = bp[m_len - 1];
+ }
+ }
+ }
+}
+
+static int swd_search2(struct ucl_swd *s)
+{
+ unsigned int key;
+
+ assert(s->look >= 2);
+ assert(s->m_len > 0);
+
+ key = s->head2[ HEAD2(s->b,s->bp) ];
+ if (key == NIL2)
+ return 0;
+#ifdef UCL_DEBUG
+ if (memcmp(&s->b[s->bp],&s->b[key],2) != 0)
+ printf("%5ld %5ld %02x%02x %02x%02x\n", (long)s->bp, (long)key,
+ s->b[s->bp], s->b[s->bp+1], s->b[key], s->b[key+1]);
+#endif
+ assert(memcmp(&s->b[s->bp],&s->b[key],2) == 0);
+#if defined(SWD_BEST_OFF)
+ if (s->best_pos[2] == 0)
+ s->best_pos[2] = key + 1;
+#endif
+
+ if (s->m_len < 2)
+ {
+ s->m_len = 2;
+ s->m_pos = key;
+ }
+ return 1;
+}
+
+/***********************************************************************
+//
+************************************************************************/
+
+static
+void swd_findbest(struct ucl_swd *s)
+{
+ unsigned int key;
+ unsigned int cnt, node;
+ unsigned int len;
+
+ assert(s->m_len > 0);
+
+ /* get current head, add bp into HEAD3 */
+ key = HEAD3(s->b,s->bp);
+ node = s->succ3[s->bp] = s_head3(s,key);
+ cnt = s->llen3[key]++;
+ assert(s->llen3[key] <= s->n + s->f);
+ if (cnt > s->max_chain && s->max_chain > 0)
+ cnt = s->max_chain;
+ s->head3[key] = (unsigned int)(s->bp);
+
+ s->b_char = s->b[s->bp];
+ len = s->m_len;
+ if (s->m_len >= s->look)
+ {
+ if (s->look == 0)
+ s->b_char = -1;
+ s->m_off = 0;
+ s->best3[s->bp] = (unsigned int)(s->f + 1);
+ }
+ else
+ {
+ if (swd_search2(s))
+ if (s->look >= 3)
+ swd_search(s,node,cnt);
+ if (s->m_len > len)
+ s->m_off = swd_pos2off(s,s->m_pos);
+ s->best3[s->bp] = (unsigned int)(s->m_len);
+
+#if defined(SWD_BEST_OFF)
+ if (s->use_best_off)
+ {
+ int i;
+ for (i = 2; i < SWD_BEST_OFF; i++)
+ if (s->best_pos[i] > 0)
+ s->best_off[i] =
+ swd_pos2off(s,s->best_pos[i]-1);
+
+ else
+ s->best_off[i] = 0;
+ }
+#endif
+ }
+
+ swd_remove_node(s,s->rp);
+
+ /* add bp into HEAD2 */
+ key = HEAD2(s->b,s->bp);
+ s->head2[key] = (unsigned int)(s->bp);
+}
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+static int
+init_match ( struct ucl_compress *c, struct ucl_swd *s,
+ const uint8_t *dict, unsigned int dict_len,
+ uint32_t flags )
+{
+ int r;
+
+ assert(!c->init);
+ c->init = 1;
+
+ s->c = c;
+
+ c->last_m_len = c->last_m_off = 0;
+
+ c->textsize = c->codesize = c->printcount = 0;
+ c->lit_bytes = c->match_bytes = c->rep_bytes = 0;
+ c->lazy = 0;
+
+ r = swd_init(s,dict,dict_len);
+ if (r != UCL_E_OK)
+ {
+ swd_exit(s);
+ return r;
+ }
+
+ s->use_best_off = (flags & 1) ? 1 : 0;
+ return UCL_E_OK;
+}
+
+static int
+find_match ( struct ucl_compress *c, struct ucl_swd *s,
+ unsigned int this_len, unsigned int skip )
+{
+ assert(c->init);
+
+ if (skip > 0)
+ {
+ assert(this_len >= skip);
+ swd_accept(s, this_len - skip);
+ c->textsize += this_len - skip + 1;
+ }
+ else
+ {
+ assert(this_len <= 1);
+ c->textsize += this_len - skip;
+ }
+
+ s->m_len = THRESHOLD;
+#ifdef SWD_BEST_OFF
+ if (s->use_best_off)
+ memset(s->best_pos,0,sizeof(s->best_pos));
+#endif
+ swd_findbest(s);
+ c->m_len = s->m_len;
+ c->m_off = s->m_off;
+
+ swd_getbyte(s);
+
+ if (s->b_char < 0)
+ {
+ c->look = 0;
+ c->m_len = 0;
+ swd_exit(s);
+ }
+ else
+ {
+ c->look = s->look + 1;
+ }
+ c->bp = c->ip - c->look;
+
+#if 0
+ /* brute force match search */
+ if (c->m_len > THRESHOLD && c->m_len + 1 <= c->look)
+ {
+ const uint8_t *ip = c->bp;
+ const uint8_t *m = c->bp - c->m_off;
+ const uint8_t *in = c->in;
+
+ if (ip - in > N)
+ in = ip - N;
+ for (;;)
+ {
+ while (*in != *ip)
+ in++;
+ if (in == ip)
+ break;
+ if (in != m)
+ if (memcmp(in,ip,c->m_len+1) == 0)
+ printf("%p %p %p %5d\n",in,ip,m,c->m_len);
+
+ in++;
+ }
+ }
+#endif
+
+ return UCL_E_OK;
+}
+
+
+static int bbConfig(struct ucl_compress *c, int endian, int bitsize)
+{
+ if (endian != -1)
+ {
+ if (endian != 0)
+ return UCL_E_ERROR;
+ c->bb_c_endian = endian;
+ }
+ if (bitsize != -1)
+ {
+ if (bitsize != 8 && bitsize != 16 && bitsize != 32 && bitsize != 64)
+ return UCL_E_ERROR;
+ c->bb_c_s = bitsize;
+ c->bb_c_s8 = bitsize / 8;
+ }
+ c->bb_b = 0; c->bb_k = 0;
+ c->bb_p = NULL;
+ c->bb_op = NULL;
+ return UCL_E_OK;
+}
+
+static void bbWriteBits(struct ucl_compress *c)
+{
+ uint8_t *p = c->bb_p;
+ uint64_t b = c->bb_b;
+
+ p[0] = (uint8_t)(b >> 0);
+ if (c->bb_c_s >= 16)
+ {
+ p[1] = (uint8_t)(b >> 8);
+ if (c->bb_c_s >= 32)
+ {
+ p[2] = (uint8_t)(b >> 16);
+ p[3] = (uint8_t)(b >> 24);
+ if (c->bb_c_s == 64)
+ {
+ p[4] = (uint8_t)(b >> 32);
+ p[5] = (uint8_t)(b >> 40);
+ p[6] = (uint8_t)(b >> 48);
+ p[7] = (uint8_t)(b >> 56);
+ }
+ }
+ }
+}
+
+
+static void bbPutBit(struct ucl_compress *c, unsigned bit)
+{
+ assert(bit == 0 || bit == 1);
+ assert(c->bb_k <= c->bb_c_s);
+
+ if (c->bb_k < c->bb_c_s)
+ {
+ if (c->bb_k == 0)
+ {
+ assert(c->bb_p == NULL);
+ c->bb_p = c->bb_op;
+ c->bb_op += c->bb_c_s8;
+ }
+ assert(c->bb_p != NULL);
+ assert(c->bb_p + c->bb_c_s8 <= c->bb_op);
+
+ c->bb_b = (c->bb_b << 1) + bit;
+ c->bb_k++;
+ }
+ else
+ {
+ assert(c->bb_p != NULL);
+ assert(c->bb_p + c->bb_c_s8 <= c->bb_op);
+
+ bbWriteBits(c);
+ c->bb_p = c->bb_op;
+ c->bb_op += c->bb_c_s8;
+ c->bb_b = bit;
+ c->bb_k = 1;
+ }
+}
+
+
+static void bbPutByte(struct ucl_compress *c, unsigned b)
+{
+ /**printf("putbyte %p %p %x (%d)\n", op, bb_p, x, bb_k);*/
+ assert(c->bb_p == NULL || c->bb_p + c->bb_c_s8 <= c->bb_op);
+ *c->bb_op++ = (uint8_t)(b);
+}
+
+static void bbFlushBits(struct ucl_compress *c, unsigned filler_bit)
+{
+ if (c->bb_k > 0)
+ {
+ assert(c->bb_k <= c->bb_c_s);
+ while (c->bb_k != c->bb_c_s)
+ bbPutBit(c, filler_bit);
+ bbWriteBits(c);
+ c->bb_k = 0;
+ }
+ c->bb_p = NULL;
+}
+
+
+
+/***********************************************************************
+//
+************************************************************************/
+
+
+static void code_prefix_ss11(struct ucl_compress *c, uint32_t i)
+{
+ if (i >= 2)
+ {
+ uint32_t t = 4;
+ i += 2;
+ do {
+ t <<= 1;
+ } while (i >= t);
+ t >>= 1;
+ do {
+ t >>= 1;
+ bbPutBit(c, (i & t) ? 1 : 0);
+ bbPutBit(c, 0);
+ } while (t > 2);
+ }
+ bbPutBit(c, (unsigned)i & 1);
+ bbPutBit(c, 1);
+}
+
+static void
+code_match(struct ucl_compress *c, unsigned int m_len, const unsigned int m_off)
+
+{
+ while (m_len > c->conf.max_match)
+ {
+ code_match(c, c->conf.max_match - 3, m_off);
+ m_len -= c->conf.max_match - 3;
+ }
+
+ c->match_bytes += m_len;
+ if (m_len > c->result[3])
+ c->result[3] = m_len;
+ if (m_off > c->result[1])
+ c->result[1] = m_off;
+
+ bbPutBit(c, 0);
+
+ if (m_off == c->last_m_off)
+ {
+ bbPutBit(c, 0);
+ bbPutBit(c, 1);
+ }
+ else
+ {
+ code_prefix_ss11(c, 1 + ((m_off - 1) >> 8));
+ bbPutByte(c, (unsigned)m_off - 1);
+ }
+ m_len = m_len - 1 - (m_off > M2_MAX_OFFSET);
+ if (m_len >= 4)
+ {
+ bbPutBit(c,0);
+ bbPutBit(c,0);
+ code_prefix_ss11(c, m_len - 4);
+ }
+ else
+ {
+ bbPutBit(c, m_len > 1);
+ bbPutBit(c, (unsigned)m_len & 1);
+ }
+
+ c->last_m_off = m_off;
+}
+
+static void
+code_run(struct ucl_compress *c, const uint8_t *ii, unsigned int lit)
+{
+ if (lit == 0)
+ return;
+ c->lit_bytes += lit;
+ if (lit > c->result[5])
+ c->result[5] = lit;
+ do {
+ bbPutBit(c, 1);
+ bbPutByte(c, *ii++);
+ } while (--lit > 0);
+}
+
+/***********************************************************************
+//
+************************************************************************/
+
+static int
+len_of_coded_match(struct ucl_compress *c, unsigned int m_len, unsigned int
+ m_off)
+
+{
+ int b;
+ if (m_len < 2 || (m_len == 2 && (m_off > M2_MAX_OFFSET))
+ || m_off > c->conf.max_offset)
+ return -1;
+ assert(m_off > 0);
+
+ m_len = m_len - 2 - (m_off > M2_MAX_OFFSET);
+
+ if (m_off == c->last_m_off)
+ b = 1 + 2;
+ else
+ {
+ b = 1 + 10;
+ m_off = (m_off - 1) >> 8;
+ while (m_off > 0)
+ {
+ b += 2;
+ m_off >>= 1;
+ }
+ }
+
+ b += 2;
+ if (m_len < 3)
+ return b;
+ m_len -= 3;
+
+ do {
+ b += 2;
+ m_len >>= 1;
+ } while (m_len > 0);
+
+ return b;
+}
+
+int ucl_nrv2b_99_compress(
+ const uint8_t *in, unsigned long in_len,
+ uint8_t *out, unsigned long *out_len,
+ unsigned int *result)
+{
+ const uint8_t *ii;
+ unsigned int lit;
+ unsigned int m_len, m_off;
+ struct ucl_compress c_buffer;
+ struct ucl_compress * const c = &c_buffer;
+ struct ucl_swd *swd;
+ unsigned int result_buffer[16];
+ int r;
+
+/* max compression */
+#define SC_TRY_LAZY 2
+#define SC_GOOD_LENGTH F
+#define SC_MAX_LAZY F
+#define SC_NICE_LENGTH F
+#define SC_MAX_CHAIN 4096
+#define SC_FLAGS 1
+#define SC_MAX_OFFSET N
+
+ memset(c, 0, sizeof(*c));
+ c->ip = c->in = in;
+ c->in_end = in + in_len;
+ c->out = out;
+ c->result = result ? result : result_buffer;
+ memset(c->result, 0, 16*sizeof(*c->result));
+ c->result[0] = c->result[2] = c->result[4] = UINT_MAX;
+ result = NULL;
+ memset(&c->conf, 0xff, sizeof(c->conf));
+ r = bbConfig(c, ENDIAN, BITSIZE);
+ if (r == 0)
+ r = bbConfig(c, c->conf.bb_endian, c->conf.bb_size);
+ if (r != 0)
+ return UCL_E_INVALID_ARGUMENT;
+ c->bb_op = out;
+
+ ii = c->ip; /* point to start of literal run */
+ lit = 0;
+
+
+ swd = (struct ucl_swd *) malloc(sizeof(*swd));
+ if (!swd)
+ return UCL_E_OUT_OF_MEMORY;
+
+ swd->f = F;
+ swd->n = N;
+ if (in_len >= 256 && in_len < swd->n)
+ swd->n = in_len;
+ if (swd->f < 8 || swd->n < 256)
+ return UCL_E_INVALID_ARGUMENT;
+
+ r = init_match(c,swd,NULL,0, SC_FLAGS);
+ if (r != UCL_E_OK)
+ {
+ free(swd);
+ return r;
+ }
+ if (SC_MAX_CHAIN > 0)
+ swd->max_chain = SC_MAX_CHAIN;
+ if (SC_NICE_LENGTH > 0)
+ swd->nice_length = SC_NICE_LENGTH;
+ if (c->conf.max_match < swd->nice_length)
+ swd->nice_length = c->conf.max_match;
+
+ c->last_m_off = 1;
+ r = find_match(c,swd,0,0);
+ if (r != UCL_E_OK)
+ return r;
+ while (c->look > 0)
+ {
+ unsigned int ahead;
+ unsigned int max_ahead;
+ int l1, l2;
+
+ c->codesize = c->bb_op - out;
+
+ m_len = c->m_len;
+ m_off = c->m_off;
+
+ assert(c->bp == c->ip - c->look);
+ assert(c->bp >= in);
+ if (lit == 0)
+ ii = c->bp;
+ assert(ii + lit == c->bp);
+ assert(swd->b_char == *(c->bp));
+
+ if (m_len < 2 || (m_len == 2 && (m_off > M2_MAX_OFFSET))
+ || m_off > c->conf.max_offset)
+ {
+ /* a literal */
+ lit++;
+ swd->max_chain = SC_MAX_CHAIN;
+ r = find_match(c,swd,1,0);
+ assert(r == 0);
+ continue;
+ }
+
+ /* a match */
+ assert_match(swd,m_len,m_off);
+
+ /* shall we try a lazy match ? */
+ ahead = 0;
+ if (SC_TRY_LAZY <= 0 || m_len >= SC_MAX_LAZY || m_off ==
+ c->last_m_off)
+
+ {
+ /* no */
+ l1 = 0;
+ max_ahead = 0;
+ }
+ else
+ {
+ /* yes, try a lazy match */
+ l1 = len_of_coded_match(c,m_len,m_off);
+ assert(l1 > 0);
+ max_ahead = SC_TRY_LAZY;
+ if ((m_len - 1) < max_ahead) {
+ max_ahead = m_len -1;
+ }
+ }
+
+ while (ahead < max_ahead && c->look > m_len)
+ {
+ if (m_len >= SC_GOOD_LENGTH)
+ swd->max_chain = SC_MAX_CHAIN >> 2;
+ else
+ swd->max_chain = SC_MAX_CHAIN;
+ r = find_match(c,swd,1,0);
+ ahead++;
+
+ assert(r == 0);
+ assert(c->look > 0);
+ assert(ii + lit + ahead == c->bp);
+
+ if (c->m_len < 2)
+ continue;
+ l2 = len_of_coded_match(c,c->m_len,c->m_off);
+ if (l2 < 0)
+ continue;
+ if (l1 + (int)(ahead + c->m_len - m_len) * 5 > l2 +
+ (int)(ahead) * 9)
+ {
+ c->lazy++;
+ assert_match(swd,c->m_len,c->m_off);
+ lit += ahead;
+ assert(ii + lit == c->bp);
+ goto lazy_match_done;
+ }
+ }
+
+ assert(ii + lit + ahead == c->bp);
+
+ /* 1 - code run */
+ code_run(c,ii,lit);
+ lit = 0;
+
+ /* 2 - code match */
+ code_match(c,m_len,m_off);
+ swd->max_chain = SC_MAX_CHAIN;
+ r = find_match(c,swd,m_len,1+ahead);
+ assert(r == 0);
+
+ lazy_match_done: ;
+ }
+
+ /* store final run */
+ code_run(c,ii,lit);
+
+ /* EOF */
+ bbPutBit(c, 0);
+ code_prefix_ss11(c, 0x1000000U);
+ bbPutByte(c, 0xff);
+
+ bbFlushBits(c, 0);
+
+ assert(c->textsize == in_len);
+ c->codesize = c->bb_op - out;
+ *out_len = c->bb_op - out;
+
+#if 0
+ printf("%7ld %7ld -> %7ld %7ld %7ld %ld (max: %d %d %d)\n",
+ (long) c->textsize, (long) in_len, (long) c->codesize,
+ c->match_bytes, c->lit_bytes, c->lazy,
+ c->result[1], c->result[3], c->result[5]);
+#endif
+ assert(c->lit_bytes + c->match_bytes == in_len);
+
+ swd_exit(swd);
+ free(swd);
+
+ return UCL_E_OK;
+}
+
+
+void Encode(void) /* compression */
+{
+ uint8_t *in, *out;
+ unsigned long in_len, out_len;
+ uint32_t tw;
+ int r;
+ fseek(infile, 0, SEEK_END);
+ in_len = ftell(infile);
+#ifdef VERBOSE
+ if ((signed long)in_len < 0)
+ Fprintf((stderr, "Errno: %d", errno));
+#endif
+#if UCLPACK_COMPAT
+ {
+ uint8_t byte;
+ if (fwrite(magic, sizeof(magic), 1, outfile) != 1)
+ Error("Can't write.");
+ tw = htonl(0); /* flags */
+ if (fwrite(&tw, sizeof(tw), 1, outfile) != 1)
+ Error("Can't write.");
+ byte = 0x2b; /* method */
+ if (fwrite(&byte, sizeof(byte), 1, outfile) != 1)
+ Error("Can't write.");
+ byte = 10; /* level */
+ if (fwrite(&byte, sizeof(byte), 1, outfile) != 1)
+ Error("Can't write.");
+ tw = htonl(256*1024); /* block_size */
+ if (fwrite(&tw, sizeof(tw), 1, outfile) != 1)
+ Error("Can't write.");
+ tw = htonl(in_len);
+ if (fwrite(&tw, sizeof(tw), 1, outfile) != 1)
+ Error("Can't write."); /* output size of text */
+ }
+#else
+ tw = host_to_i86ul(in_len);
+ if (fwrite(&tw, sizeof(tw), 1, outfile) != 1)
+ Error("Can't write."); /* output size of text */
+#endif
+ if (in_len == 0)
+ return;
+ rewind(infile);
+
+ in = malloc(in_len);
+ out_len = in_len + (in_len/8) + 256;
+ out = malloc(out_len);
+ if (!in || !out) {
+ Error("Can't malloc");
+ }
+ if (fread(in, in_len, 1, infile) != 1) {
+ Error("Can't read");
+ }
+ r = ucl_nrv2b_99_compress(in, in_len, out, &out_len, 0 );
+ if (r != UCL_E_OK)
+ Error("Compression failure\n");
+#if UCLPACK_COMPAT
+ tw = htonl(out_len);
+ if (fwrite(&tw, sizeof(tw), 1, outfile) != 1)
+ Error("Can't write."); /* file size of text */
+
+#endif
+ if (fwrite(out, out_len, 1, outfile) != 1) {
+ Error("Write error\n");
+ }
+#if UCLPACK_COMPAT
+ tw = htonl(0); /* EOF marker */
+ if (fwrite(&tw, sizeof(tw), 1, outfile) != 1)
+ Error("Can't write.");
+
+#endif
+
+#ifdef LONG_REPORT
+ Fprintf((stdout, "input size %ld bytes\n", in_len));
+ Fprintf((stdout, "output size %ld bytes\n", out_len));
+ Fprintf((stdout, "input/output %.3f\n", (double)in_len / out_len));
+#else
+ Fprintf((stdout, "input/output = %ld/%ld = %.3f\n", in_len, out_len,
+ (double)in_len / out_len));
+#endif
+
+}
+
+#endif
+
+#ifdef DECODE
+
+#define GETBIT_8(bb, src, ilen) \
+ (((bb = bb & 0x7f ? bb*2 : ((unsigned)src[ilen++]*2+1)) >> 8) & 1)
+
+#define GETBIT_LE16(bb, src, ilen) \
+ (bb*=2,bb&0xffff ? (bb>>16)&1 : (ilen+=2,((bb=(src[ilen-2]+src[ilen-1]*256u)*2+1)>>16)&1))
+
+#define GETBIT_LE32(bb, src, ilen) \
+ (bc > 0 ? ((bb>>--bc)&1) : (bc=31,\
+ bb=*(const uint32_t *)((src)+ilen),ilen+=4,(bb>>31)&1))
+
+#define GETBIT_LE64(bb, src, ilen) \
+ (bc > 0 ? ((bb>>--bc)&1) : (bc=63, \
+ bb=*(const uint64_t *)((src)+ilen),ilen+=8,(bb>>63)&1))
+
+#if ENDIAN == 0 && BITSIZE == 8
+#define GETBIT(bb, src, ilen) GETBIT_8(bb, src, ilen)
+#endif
+#if ENDIAN == 0 && BITSIZE == 16
+#define GETBIT(bb, src, ilen) GETBIT_LE16(bb, src, ilen)
+#endif
+#if ENDIAN == 0 && BITSIZE == 32
+#define GETBIT(bb, src, ilen) GETBIT_LE32(bb, src, ilen)
+#endif
+#if ENDIAN == 0 && BITSIZE == 64
+#define GETBIT(bb, src, ilen) GETBIT_LE64(bb, src, ilen)
+#endif
+#ifndef GETBIT
+#error "Bad Combination of ENDIAN and BITSIZE values specified"
+#endif
+
+#undef SAFE
+
+#ifdef SAFE
+#define FAIL(x,r) if (x) { Error(r); }
+#else
+#define FAIL(x,r)
+#endif
+
+void Decode(void) /* recover */
+{
+ uint32_t tw;
+ uint8_t *src, *dst;
+ unsigned long max_src_len, src_len, dst_len;
+ unsigned long ilen = 0, olen = 0, last_m_off = 1;
+#if BITSIZE <= 32
+ uint32_t bb = 0;
+#elif BITSIZE == 64
+ uint64_t bb = 0;
+#endif
+ unsigned bc = 0;
+#if UCLPACK_COMPAT
+ if (fseek(infile, sizeof(magic) + sizeof(tw) + 1 + 1 + sizeof(tw),
+ SEEK_SET) != 0)
+
+ Error("Seek Error");
+ if (fread(&tw, sizeof(tw), 1, infile) < 1)
+ Error("Can't read"); /* read size of text */
+ dst_len = ntohl(tw);
+ if (fread(&tw, sizeof(tw), 1, infile) < 1)
+ Error("Can't read"); /* read size of file */
+ max_src_len = ntohl(tw);
+#else
+ if (fread(&tw, sizeof(tw), 1, infile) < 1)
+ Error("Can't read"); /* read size of text */
+ dst_len = i86ul_to_host(tw);
+ max_src_len = dst_len + (dst_len/8) + 256;
+#endif
+ if (dst_len == 0)
+ return;
+ dst = malloc(dst_len);
+ if (!dst)
+ Error("Can't malloc");
+ src = malloc(max_src_len);
+ if (!src)
+ Error("Can't malloc");
+ src_len = fread(src, 1, max_src_len, infile);
+ if (src_len <= 0)
+ Error("Can't read");
+
+ for(;;) {
+ unsigned int m_off, m_len;
+ while(GETBIT(bb, src, ilen)) {
+ FAIL(ilen >= src_len, "input overrun");
+ FAIL(olen >= dst_len, "output overrun");
+ dst[olen++] = src[ilen++];
+ }
+ m_off = 1;
+ do {
+ m_off = m_off*2 + GETBIT(bb, src, ilen);
+ FAIL(ilen >= src_len, "input overrun");
+ FAIL(m_off > 0xffffffU +3, "lookbehind overrun");
+ } while (!GETBIT(bb, src, ilen));
+ if (m_off == 2)
+ {
+ m_off = last_m_off;
+ }
+ else
+ {
+ FAIL(ilen >= src_len, "input overrun");
+ m_off = (m_off - 3)*256 + src[ilen++];
+ if (m_off == 0xffffffffU)
+ break;
+ last_m_off = ++m_off;
+ }
+ m_len = GETBIT(bb, src, ilen);
+ m_len = m_len*2 + GETBIT(bb, src, ilen);
+ if (m_len == 0)
+ {
+ m_len++;
+ do {
+ m_len = m_len*2 + GETBIT(bb, src, ilen);
+ FAIL(ilen >= src_len, "input overrun");
+ FAIL(m_len >= dst_len, "output overrun");
+ } while(!GETBIT(bb, src, ilen));
+ m_len += 2;
+ }
+ m_len += (m_off > 0xd00);
+ FAIL(olen + m_len > dst_len, "output overrun");
+ FAIL(m_off > olen, "lookbeind overrun");
+ {
+ const uint8_t *m_pos;
+ m_pos = dst + olen - m_off;
+ dst[olen++] = *m_pos++;
+ do {
+ dst[olen++] = *m_pos++;
+ } while(--m_len > 0);
+ }
+ }
+ FAIL(ilen < src_len, "input not consumed");
+ FAIL(ilen > src_len, "input overrun");
+ assert(ilen == src_len);
+ Fprintf((stderr, "%12ld\n", olen));
+ if (dst_len != olen) {
+ fprintf(stderr, "length != expected length\n");
+ }
+ if (fwrite(dst, olen, 1, outfile) != 1)
+ Error("Write error\n");
+ free(src);
+ free(dst);
+}
+#endif
+
+#ifdef MAIN
+int main(int argc, char *argv[])
+{
+ char *s;
+ FILE *f;
+ int c;
+
+ if (argc == 2) {
+ outfile = stdout;
+ if ((f = tmpfile()) == NULL) {
+ perror("tmpfile");
+ return EXIT_FAILURE;
+ }
+ while ((c = getchar()) != EOF)
+ fputc(c, f);
+ rewind(infile = f);
+ }
+ else if (argc != 4) {
+ Fprintf((stderr, "'nrv2b e file1 file2' encodes file1 into file2.\n"
+ "'nrv2b d file2 file1' decodes file2 into file1.\n"));
+ return EXIT_FAILURE;
+ }
+ if (argc == 4) {
+ if ((s = argv[1], s[1] || strpbrk(s, "DEde") == NULL)
+ || (s = argv[2], (infile = fopen(s, "rb")) == NULL)
+ || (s = argv[3], (outfile = fopen(s, "wb")) == NULL)) {
+ Fprintf((stderr, "??? %s\n", s));
+ return EXIT_FAILURE;
+ }
+ }
+ if (toupper(*argv[1]) == 'E')
+ Encode();
+ else
+ Decode();
+ fclose(infile);
+ fclose(outfile);
+ return EXIT_SUCCESS;
+}
+#endif
diff --git a/gpxe/src/util/parserom.pl b/gpxe/src/util/parserom.pl
new file mode 100644
index 00000000..3d41b544
--- /dev/null
+++ b/gpxe/src/util/parserom.pl
@@ -0,0 +1,64 @@
+#!/usr/bin/perl -w
+#
+# Parse PCI_ROM and ISA_ROM entries from a source file on stdin and
+# output the relevant Makefile variable definitions to stdout
+#
+# Based upon portions of Ken Yap's genrules.pl
+
+use strict;
+use warnings;
+
+die "Syntax: $0 driver_source.c" unless @ARGV == 1;
+my $source = shift;
+open DRV, "<$source" or die "Could not open $source: $!\n";
+
+( my $family, my $driver_name ) = ( $source =~ /^(.*?([^\/]+))\..$/ )
+ or die "Could not parse source file name \"$source\"\n";
+
+my $printed_family;
+
+sub rom {
+ ( my $type, my $image, my $desc, my $vendor, my $device ) = @_;
+ my $ids = $vendor ? "$vendor,$device" : "-";
+ unless ( $printed_family ) {
+ print "\n";
+ print "# NIC\t\n";
+ print "# NIC\tfamily\t$family\n";
+ print "DRIVERS += $driver_name\n";
+ $printed_family = 1;
+ }
+ print "\n";
+ print "# NIC\t$image\t$ids\t$desc\n";
+ print "DRIVER_$image = $driver_name\n";
+ print "ROM_TYPE_$image = $type\n";
+ print "ROM_DESCRIPTION_$image = \"$desc\"\n";
+ print "PCI_VENDOR_$image = 0x$vendor\n" if $vendor;
+ print "PCI_DEVICE_$image = 0x$device\n" if $device;
+ print "ROMS += $image\n";
+ print "ROMS_$driver_name += $image\n";
+}
+
+while ( <DRV> ) {
+ next unless /(PCI|ISA)_ROM\s*\(/;
+
+ if ( /^\s*PCI_ROM\s*\(
+ \s*0x([0-9A-Fa-f]{4})\s*, # PCI vendor
+ \s*0x([0-9A-Fa-f]{4})\s*, # PCI device
+ \s*\"([^\"]*)\"\s*, # Image
+ \s*\"([^\"]*)\"\s* # Description
+ \)/x ) {
+ ( my $vendor, my $device, my $image, my $desc ) = ( lc $1, lc $2, $3, $4 );
+ rom ( "pci", $image, $desc, $vendor, $device );
+ rom ( "pci", lc "${vendor}${device}", $desc, $vendor, $device );
+ } elsif ( /^\s*ISA_ROM\s*\(
+ \s*\"([^\"]*)\"\s*, # Image
+ \s*\"([^\"]*)\"\s* # Description
+ \)/x ) {
+ ( my $image, my $desc ) = ( $1, $2 );
+ rom ( "isa", $image, $desc );
+ } else {
+ warn "Malformed PCI_ROM or ISA_ROM macro on line $. of $source\n";
+ }
+}
+
+close DRV;
diff --git a/gpxe/src/util/sortobjdump.pl b/gpxe/src/util/sortobjdump.pl
new file mode 100755
index 00000000..8ad7314b
--- /dev/null
+++ b/gpxe/src/util/sortobjdump.pl
@@ -0,0 +1,40 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+
+# Sort the symbol table portion of the output of objdump -ht by
+# section, then by symbol value, then by size. Used to enhance the
+# linker maps produced by "make bin/%.map" by also showing the values
+# of all non-global symbols.
+
+my %section_idx = ( "*ABS*" => "." );
+my %lines;
+while ( <> ) {
+ if ( /^\s+(\d+)\s+([\.\*]\S+)\s+[0-9a-fA-F]+\s+[0-9a-fA-F]/ ) {
+ # It's a header line containing a section definition; extract the
+ # section index and store it. Also print the header line.
+ print;
+ ( my $index, my $section ) = ( $1, $2 );
+ $section_idx{$section} = sprintf ( "%02d", $index );
+ } elsif ( /^([0-9a-fA-F]+)\s.*?\s([\.\*]\S+)\s+([0-9a-fA-F]+)\s+(\S+)/ ) {
+ # It's a symbol line - store it in the hash, indexed by
+ # "<section_index>:<value>:<size>:<end_tag>". <end_tag> is "0" if
+ # the symbol name is of the form xxx_end, "1" otherwise; this is
+ # done so that table end markers show up before any other symbols
+ # with the same value.
+ ( my $value, my $section, my $size, my $name ) = ( $1, $2, $3, $4 );
+ die "Unrecognised section \"$section\"\n"
+ unless exists $section_idx{$section};
+ my $section_idx = $section_idx{$section};
+ my $end = ( $name =~ /_end$/ ) ? "0" : "1";
+ my $key = $section_idx.":".$value.":".$size.":".$end;
+ $lines{$key} ||= '';
+ $lines{$key} .= $_;
+ } else {
+ # It's a generic header line: just print it.
+ print;
+ }
+}
+
+print $lines{$_} foreach sort keys %lines;
diff --git a/gpxe/src/util/swapdevids.pl b/gpxe/src/util/swapdevids.pl
new file mode 100755
index 00000000..c6255ae7
--- /dev/null
+++ b/gpxe/src/util/swapdevids.pl
@@ -0,0 +1,49 @@
+#!/usr/bin/perl -w
+#
+# Program to reverse the device identifier IDs in the PCIR and PnP
+# structures in a ROM for old non-compliant BIOSes
+#
+# GPL, Ken Yap 2001
+#
+
+use bytes;
+
+use IO::Seekable;
+
+sub swaplocs ($$$)
+{
+ my ($dataref, $loc1, $loc2) = @_;
+ my ($t);
+
+ $t = substr($$dataref, $loc1, 1);
+ substr($$dataref, $loc1, 1) = substr($$dataref, $loc2, 1);
+ substr($$dataref, $loc2, 1) = $t;
+}
+
+sub printdevids ($$)
+{
+ my ($dataref, $loc) = @_;
+
+ return (sprintf "%02x %02x %02x", unpack('C3', substr($$dataref, $loc, 3)));
+}
+
+$#ARGV >= 0 or die "Usage: $0 romimage\n";
+$file = $ARGV[0];
+open(F, "+<$file") or die "$file: $!\n";
+binmode(F);
+# Handle up to 64kB ROM images
+$len = read(F, $data, 64*1024);
+defined($len) or die "$file: $!\n";
+substr($data, 0, 2) eq "\x55\xAA" or die "$file: Not a boot ROM image\n";
+($pci, $pnp) = unpack('v2', substr($data, 0x18, 4));
+($pci < $len and $pnp < $len) or die "$file: Not a PCI PnP ROM image\n";
+(substr($data, $pci, 4) eq 'PCIR' and substr($data, $pnp, 4) eq '$PnP')
+ or die "$file: No PCI and PNP structures, not a PCI PNP ROM image\n";
+&swaplocs(\$data, $pci+13, $pci+15);
+&swaplocs(\$data, $pnp+18, $pnp+20);
+seek(F, 0, SEEK_SET) or die "$file: Cannot seek to beginning\n";
+print F $data;
+close(F);
+print "PCI devids now: ", &printdevids(\$data, $pci+13), "\n";
+print "PnP devids now: ", &printdevids(\$data, $pnp+18), "\n";
+exit(0);
diff --git a/gpxe/src/util/symcheck.pl b/gpxe/src/util/symcheck.pl
new file mode 100755
index 00000000..8925ca62
--- /dev/null
+++ b/gpxe/src/util/symcheck.pl
@@ -0,0 +1,191 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+
+use constant WARNING_SIZE => 512;
+
+my $symtab = {};
+
+# Scan output of "objdump -w -t bin/blib.a" and build up symbol table
+#
+my $object;
+while ( <> ) {
+ chomp;
+ if ( /^In archive/ ) {
+ # Do nothing
+ } elsif ( /^$/ ) {
+ # Do nothing
+ } elsif ( /^(\S+\.o):\s+file format/ ) {
+ $object = $1;
+ } elsif ( /^SYMBOL TABLE:/ ) {
+ # Do nothing
+ } elsif ( /^([0-9a-fA-F]+)\s(l|g|\s)......\s(\S+)\s+([0-9a-fA-F]+)\s+(\S+)$/ ) {
+ my $value = $1;
+ my $scope = $2;
+ my $section = $3;
+ my $size = $4;
+ my $symbol = $5;
+ $symtab->{$object}->{$symbol} = {
+ global => ( $scope ne "l" ),
+ section => ( $section eq "*UND*" ? undef : $section ),
+ value => ( $value ? hex ( $value ) : 0 ),
+ size => ( $size ? hex ( $size ) : 0 ),
+ };
+ } else {
+ die "Unrecognized line \"$_\"";
+ }
+}
+
+# Add symbols that we know will be generated or required by the linker
+#
+foreach my $object ( keys %$symtab ) {
+ my $obj_symbol = "obj_$object";
+ $obj_symbol =~ s/\.o$//;
+ $obj_symbol =~ s/\W/_/g;
+ $symtab->{LINKER}->{$obj_symbol} = {
+ global => 1,
+ section => undef,
+ value => 0,
+ size => 0,
+ };
+}
+foreach my $link_sym qw ( __prefix _prefix _prefix_load_offset
+ _prefix_size _prefix_progbits_size _prefix_size_pgh
+ __text16 _text16 _text16_load_offset
+ _text16_size _text16_progbits_size _text16_size_pgh
+ __data16 _data16 _data16_load_offset
+ _data16_size _data16_progbits_size _data16_size_pgh
+ __text _text __data _data _textdata_load_offset
+ _textdata_size _textdata_progbits_size
+ __rodata __bss _end
+ _payload_offset _max_align
+ _load_size _load_size_pgh _load_size_sect
+ pci_vendor_id pci_device_id ) {
+ $symtab->{LINKER}->{$link_sym} = {
+ global => 1,
+ section => '*ABS*',
+ value => 0,
+ size => 0,
+ };
+}
+
+# Add symbols that we know will be used by the debug system
+#
+foreach my $debug_sym qw ( dbg_autocolourise dbg_decolourise
+ dbg_hex_dump_da ) {
+ $symtab->{DEBUG}->{$debug_sym} = {
+ global => 1,
+ section => undef,
+ value => 0,
+ size => 0,
+ };
+}
+
+# Build up requires, provides and shares symbol tables for global
+# symbols
+#
+my $globals = {};
+while ( ( my $object, my $symbols ) = each %$symtab ) {
+ while ( ( my $symbol, my $info ) = each %$symbols ) {
+ if ( $info->{global} ) {
+ my $category;
+ if ( ! defined $info->{section} ) {
+ $category = "requires";
+ } elsif ( $info->{section} eq "*COM*" ) {
+ $category = "shares";
+ } else {
+ $category = "provides";
+ }
+ $globals->{$symbol}->{$category}->{$object} = 1;
+ }
+ }
+}
+
+# Check for multiply defined, never-defined and unused global symbols
+#
+my $problems = {};
+while ( ( my $symbol, my $info ) = each %$globals ) {
+ my @provides = keys %{$info->{provides}};
+ my @requires = keys %{$info->{requires}};
+ my @shares = keys %{$info->{shares}};
+
+ if ( ( @provides == 0 ) && ( @shares == 1 ) ) {
+ # A symbol "shared" by just a single file is actually being
+ # provided by that file; it just doesn't have an initialiser.
+ @provides = @shares;
+ @shares = ();
+ }
+
+ if ( ( @requires > 0 ) && ( @provides == 0 ) && ( @shares == 0 ) ) {
+ # No object provides this symbol, but some objects require it.
+ $problems->{$_}->{nonexistent}->{$symbol} = 1 foreach @requires;
+ }
+
+ if ( ( @requires == 0 ) && ( @provides > 0 ) ) {
+ # No object requires this symbol, but some objects provide it.
+ foreach my $provide ( @provides ) {
+ if ( $provide eq "LINKER" ) {
+ # Linker-provided symbols are exempt from this check.
+ } elsif ( $symtab->{$provide}->{$symbol}->{section} =~ /^\.tbl\./ ) {
+ # Linker tables are exempt from this check.
+ } else {
+ $problems->{$provide}->{unused}->{$symbol} = 1;
+ }
+ }
+ }
+
+ if ( ( @shares > 0 ) && ( @provides > 0 ) ) {
+ # A shared symbol is being initialised by an object
+ $problems->{$_}->{shared}->{$symbol} = 1 foreach @provides;
+ }
+
+ if ( @provides > 1 ) {
+ # A non-shared symbol is defined in multiple objects
+ $problems->{$_}->{multiples}->{$symbol} = 1 foreach @provides;
+ }
+}
+
+# Check for excessively large local symbols. Text and rodata symbols
+# are exempt from this check
+#
+while ( ( my $object, my $symbols ) = each %$symtab ) {
+ while ( ( my $symbol, my $info ) = each %$symbols ) {
+ if ( ( ! $info->{global} ) &&
+ ( ( defined $info->{section} ) &&
+ ! ( $info->{section} =~ /^(\.text|\.rodata)/ ) ) &&
+ ( $info->{size} >= WARNING_SIZE ) ) {
+ $problems->{$object}->{large}->{$symbol} = 1;
+ }
+ }
+}
+
+# Print out error messages
+#
+my $errors = 0;
+my $warnings = 0;
+foreach my $object ( sort keys %$problems ) {
+ my @nonexistent = sort keys %{$problems->{$object}->{nonexistent}};
+ my @multiples = sort keys %{$problems->{$object}->{multiples}};
+ my @unused = sort keys %{$problems->{$object}->{unused}};
+ my @shared = sort keys %{$problems->{$object}->{shared}};
+ my @large = sort keys %{$problems->{$object}->{large}};
+
+ print "WARN $object provides unused symbol $_\n" foreach @unused;
+ $warnings += @unused;
+ print "WARN $object has large static symbol $_\n" foreach @large;
+ $warnings += @large;
+ print "ERR $object requires non-existent symbol $_\n" foreach @nonexistent;
+ $errors += @nonexistent;
+ foreach my $symbol ( @multiples ) {
+ my @other_objects = sort grep { $_ ne $object }
+ keys %{$globals->{$symbol}->{provides}};
+ print "ERR $object provides symbol $symbol"
+ ." (also provided by @other_objects)\n";
+ }
+ $errors += @multiples;
+ print "ERR $object misuses shared symbol $_\n" foreach @shared;
+}
+
+print "$errors error(s), $warnings warning(s)\n";
+exit ( $errors ? 1 : 0 );
diff --git a/gpxe/src/util/zbin.c b/gpxe/src/util/zbin.c
new file mode 100644
index 00000000..f47fa36b
--- /dev/null
+++ b/gpxe/src/util/zbin.c
@@ -0,0 +1,325 @@
+#include <stdio.h>
+#include <sys/stat.h>
+
+#define ENCODE
+#define VERBOSE
+#include "nrv2b.c"
+FILE *infile, *outfile;
+
+struct input_file {
+ void *buf;
+ size_t len;
+};
+
+struct output_file {
+ void *buf;
+ size_t len;
+ size_t max_len;
+};
+
+struct zinfo_common {
+ char type[4];
+ char pad[12];
+};
+
+struct zinfo_copy {
+ char type[4];
+ uint32_t offset;
+ uint32_t len;
+ uint32_t align;
+};
+
+struct zinfo_pack {
+ char type[4];
+ uint32_t offset;
+ uint32_t len;
+ uint32_t align;
+};
+
+struct zinfo_subtract {
+ char type[4];
+ uint32_t offset;
+ uint32_t divisor;
+ uint32_t pad;
+};
+
+union zinfo_record {
+ struct zinfo_common common;
+ struct zinfo_copy copy;
+ struct zinfo_pack pack;
+ struct zinfo_subtract subtract;
+};
+
+struct zinfo_file {
+ union zinfo_record *zinfo;
+ unsigned int num_entries;
+};
+
+static int read_file ( const char *filename, void **buf, size_t *len ) {
+ FILE *file;
+ struct stat stat;
+
+ file = fopen ( filename, "r" );
+ if ( ! file ) {
+ fprintf ( stderr, "Could not open %s: %s\n", filename,
+ strerror ( errno ) );
+ goto err;
+ }
+
+ if ( fstat ( fileno ( file ), &stat ) < 0 ) {
+ fprintf ( stderr, "Could not stat %s: %s\n", filename,
+ strerror ( errno ) );
+ goto err;
+ }
+
+ *len = stat.st_size;
+ *buf = malloc ( *len );
+ if ( ! *buf ) {
+ fprintf ( stderr, "Could not malloc() %d bytes for %s: %s\n",
+ *len, filename, strerror ( errno ) );
+ goto err;
+ }
+
+ if ( fread ( *buf, 1, *len, file ) != *len ) {
+ fprintf ( stderr, "Could not read %d bytes from %s: %s\n",
+ *len, filename, strerror ( errno ) );
+ goto err;
+ }
+
+ fclose ( file );
+ return 0;
+
+ err:
+ fclose ( file );
+ return -1;
+}
+
+static int read_input_file ( const char *filename,
+ struct input_file *input ) {
+ return read_file ( filename, &input->buf, &input->len );
+}
+
+static int read_zinfo_file ( const char *filename,
+ struct zinfo_file *zinfo ) {
+ void *buf;
+ size_t len;
+
+ if ( read_file ( filename, &buf, &len ) < 0 )
+ return -1;
+
+ if ( ( len % sizeof ( *(zinfo->zinfo) ) ) != 0 ) {
+ fprintf ( stderr, ".zinfo file %s has invalid length %d\n",
+ filename, len );
+ return -1;
+ }
+
+ zinfo->zinfo = buf;
+ zinfo->num_entries = ( len / sizeof ( *(zinfo->zinfo) ) );
+ return 0;
+}
+
+static int alloc_output_file ( size_t max_len, struct output_file *output ) {
+ output->len = 0;
+ output->max_len = ( max_len );
+ output->buf = malloc ( max_len );
+ if ( ! output->buf ) {
+ fprintf ( stderr, "Could not allocate %d bytes for output\n",
+ max_len );
+ return -1;
+ }
+ memset ( output->buf, 0xff, sizeof ( output->buf ) );
+ return 0;
+}
+
+static int process_zinfo_copy ( struct input_file *input,
+ struct output_file *output,
+ union zinfo_record *zinfo ) {
+ struct zinfo_copy *copy = &zinfo->copy;
+ size_t offset = copy->offset;
+ size_t len = copy->len;
+ unsigned int align = copy->align;
+
+ if ( ( offset + len ) > input->len ) {
+ fprintf ( stderr, "Input buffer overrun on copy\n" );
+ return -1;
+ }
+
+ output->len = ( ( output->len + align - 1 ) & ~( align - 1 ) );
+ if ( ( output->len + len ) > output->max_len ) {
+ fprintf ( stderr, "Output buffer overrun on copy\n" );
+ return -1;
+ }
+
+ memcpy ( ( output->buf + output->len ),
+ ( input->buf + offset ), len );
+ output->len += len;
+ return 0;
+}
+
+static int process_zinfo_pack ( struct input_file *input,
+ struct output_file *output,
+ union zinfo_record *zinfo ) {
+ struct zinfo_pack *pack = &zinfo->pack;
+ size_t offset = pack->offset;
+ size_t len = pack->len;
+ unsigned int align = pack->align;
+ unsigned long packed_len;
+
+ if ( ( offset + len ) > input->len ) {
+ fprintf ( stderr, "Input buffer overrun on pack\n" );
+ return -1;
+ }
+
+ output->len = ( ( output->len + align - 1 ) & ~( align - 1 ) );
+ if ( output->len > output->max_len ) {
+ fprintf ( stderr, "Output buffer overrun on pack\n" );
+ return -1;
+ }
+
+ if ( ucl_nrv2b_99_compress ( ( input->buf + offset ), len,
+ ( output->buf + output->len ),
+ &packed_len, 0 ) != UCL_E_OK ) {
+ fprintf ( stderr, "Compression failure\n" );
+ return -1;
+ }
+
+ output->len += packed_len;
+ if ( output->len > output->max_len ) {
+ fprintf ( stderr, "Output buffer overrun on pack\n" );
+ return -1;
+ }
+
+ return 0;
+}
+
+static int process_zinfo_subtract ( struct input_file *input,
+ struct output_file *output,
+ struct zinfo_subtract *subtract,
+ size_t datasize ) {
+ size_t offset = subtract->offset;
+ void *target;
+ long delta;
+
+ if ( ( offset + datasize ) > output->len ) {
+ fprintf ( stderr, "Subtract at %#zx outside output buffer\n",
+ offset );
+ return -1;
+ }
+
+ target = ( output->buf + offset );
+ delta = ( ( output->len / subtract->divisor ) -
+ ( input->len / subtract->divisor ) );
+
+ switch ( datasize ) {
+ case 1: {
+ uint8_t *byte = target;
+ *byte += delta;
+ break; }
+ case 2: {
+ uint16_t *word = target;
+ *word += delta;
+ break; }
+ case 4: {
+ uint32_t *dword = target;
+ *dword += delta;
+ break; }
+ default:
+ fprintf ( stderr, "Unsupported subtract datasize %d\n",
+ datasize );
+ return -1;
+ }
+ return 0;
+}
+
+static int process_zinfo_subb ( struct input_file *input,
+ struct output_file *output,
+ union zinfo_record *zinfo ) {
+ return process_zinfo_subtract ( input, output, &zinfo->subtract, 1 );
+}
+
+static int process_zinfo_subw ( struct input_file *input,
+ struct output_file *output,
+ union zinfo_record *zinfo ) {
+ return process_zinfo_subtract ( input, output, &zinfo->subtract, 2 );
+}
+
+static int process_zinfo_subl ( struct input_file *input,
+ struct output_file *output,
+ union zinfo_record *zinfo ) {
+ return process_zinfo_subtract ( input, output, &zinfo->subtract, 4 );
+}
+
+struct zinfo_processor {
+ char *type;
+ int ( * process ) ( struct input_file *input,
+ struct output_file *output,
+ union zinfo_record *zinfo );
+};
+
+static struct zinfo_processor zinfo_processors[] = {
+ { "COPY", process_zinfo_copy },
+ { "PACK", process_zinfo_pack },
+ { "SUBB", process_zinfo_subb },
+ { "SUBW", process_zinfo_subw },
+ { "SUBL", process_zinfo_subl },
+};
+
+static int process_zinfo ( struct input_file *input,
+ struct output_file *output,
+ union zinfo_record *zinfo ) {
+ struct zinfo_common *common = &zinfo->common;
+ struct zinfo_processor *processor;
+ char type[ sizeof ( common->type ) + 1 ] = "";
+ unsigned int i;
+
+ strncat ( type, common->type, sizeof ( type ) - 1 );
+ for ( i = 0 ; i < ( sizeof ( zinfo_processors ) /
+ sizeof ( zinfo_processors[0] ) ) ; i++ ) {
+ processor = &zinfo_processors[i];
+ if ( strcmp ( processor->type, type ) == 0 )
+ return processor->process ( input, output, zinfo );
+ }
+
+ fprintf ( stderr, "Unknown zinfo record type \"%s\"\n", &type[0] );
+ return -1;
+}
+
+static int write_output_file ( struct output_file *output ) {
+ if ( fwrite ( output->buf, 1, output->len, stdout ) != output->len ) {
+ fprintf ( stderr, "Could not write %d bytes of output: %s\n",
+ output->len, strerror ( errno ) );
+ return -1;
+ }
+ return 0;
+}
+
+int main ( int argc, char **argv ) {
+ struct input_file input;
+ struct output_file output;
+ struct zinfo_file zinfo;
+ unsigned int i;
+
+ if ( argc != 3 ) {
+ fprintf ( stderr, "Syntax: %s file.bin file.zinfo "
+ "> file.zbin\n", argv[0] );
+ exit ( 1 );
+ }
+
+ if ( read_input_file ( argv[1], &input ) < 0 )
+ exit ( 1 );
+ if ( read_zinfo_file ( argv[2], &zinfo ) < 0 )
+ exit ( 1 );
+ if ( alloc_output_file ( ( input.len * 4 ), &output ) < 0 )
+ exit ( 1 );
+
+ for ( i = 0 ; i < zinfo.num_entries ; i++ ) {
+ if ( process_zinfo ( &input, &output,
+ &zinfo.zinfo[i] ) < 0 )
+ exit ( 1 );
+ }
+
+ if ( write_output_file ( &output ) < 0 )
+ exit ( 1 );
+
+ return 0;
+}